2024/05/09
클래스가 필요한 이유
Java는 클래스와 객체로 이루어져 있다. 클래스와 객체는 너무 많은 내용을 포함하고 있어서 이해하기 쉽지 않다.
클래스와 객체라는 개념이 왜 필요한지 부터 시작해서, 클래스가 어떤 방식으로 발전하면서 만들어졌는지 점진적으로 알아보자.
문제: 학생 정보 출력 프로그램 만들기
두 명의 학생 정보를 출력하는 프로그램을 작성해야 한다. 각 학생은 이름, 나이, 성적을 가진다,.
요구 사항
(1) 첫 번째 학생의 이름은 "학생1", 나이는 15, 성적은 90이다.
(2) 두 번째 학생의 이름은 "학생2", 나이는 16, 성적은 80이다.
(3) 각 학생의 정보를 다음과 같은 형식으로 출력해야 한다. "이름: [이름] 나이: [나이] 성적: [성적]"
(4) 변수를 사용해서 학생 정보를 저장하고 변수를 사용해서 학생 정보를 출력해야 한다.
예시 출력
이름: 학생1 나이: 15 성적: 90
이름: 학생2 나이: 16 성적: 80
ClassStart1 - 변수 사용
package class1;
public class ClassStart1 {
public static void main(String[] args) {
String student1Name = "학생1";
int student1Age = 15;
int student1Order = 90;
String student2Name = "학생1";
int student2Age = 16;
int student2Order = 80;
System.out.println("이름: " + student1Name + " 나이: " + student1Age + " 성적: " + student1Order);
System.out.println("이름: " + student2Name + " 나이: " + student2Age + " 성적: " + student2Order);
}
}
학생 2명이므로 각각 다른 변수를 사용해야 한다.
학생이 늘어날 수록 새로운 변수를 선언해야하고, 출력하는 코드도 또 추가해야한다.
이 문제를 해결하기 위해 우선 배열을 사용해서 풀어보자!
ClassStart2 - 배열 사용
이전 문제에 배열을 적용해서 풀어보자.!
학생이 추가되더라도 코드 변경을 최소화 해보자.!
package class1;
public class ClassStart2 {
public static void main(String[] args) {
String[] studentsNames = {"학생1", "학생2"};
int[] studentAges = {15,10};
int[] studentGrades = {90, 80};
for (int i = 0; i < studentsNames.length; i++) {
System.out.println("이름: " + studentsNames[i] + " 나이: " + studentAges[i] + " 성적: " + studentGrades[i]);
}
}
}
배열을 사용해서 학생이 추가되어도 배열에 학생의 데이터만 추가하면 된다. 변수를 더 추가하지 않아도 되고, 출력 부분의 코드도 그대로 유지할 수 있다.
출력문
학생 추가전
String[] studentsNames = {"학생1", "학생2"};
int[] studentAges = {15,10};
int[] studentGrades = {90, 80};
학생 추가후
String[] studentsNames = {"학생1", "학생2", "학생3"};
int[] studentAges = {15,10,14};
int[] studentGrades = {90, 80, 100};
배열 사용의 한계
배열을 사용해서 코드 변경을 최소화하는데는 성공했지만, 한 학새으이 데이터가 studentNames[], studentAges[], studentGrades[]라는 3개의 배열에 나뉘어져 있다. 데이터를 변경할 때 매우 조심해서 작업해야 한다.
ex) 학생 2의 데이터를 제거하려면 각각의 배열마다 학생2의 요소를 정확하게 찾아서 제거해주어야 한다.
클래스 도입
클래스를 사용해서 학생이라는 개념을 만들고, 학생 별로 이름, 나이, 성적을 관리한다.
package class1;
public class Student {
String name;
int age;
int grade;
}
class 키워드를 사용해서 학생 클래스(Student)를 정의한다.
학생 클래스는 내부에 이름(name), 나이(age), 성정(grade)변수를 가진다.
클래스에 정의한 변수들을 멤버 변수, 또는 필드라 한다.
- 멤버 변수(Member Variable): 이 변수들을 특정 클래스에 소속된 멤버이기 때문에 이렇게 부른다.
- 클래스를 사용하면 Int, String과 같은 타입을 직접 만들 수 있다.
- 사용자가 직접 정의하는 사용자 정의 타입을 만들려면 설계도가 필요하다. 설계도 = 클래스
- 설계도인 클래스를 사용해서 실제 메모리에 만들어진 실체를 객체 또는 인스턴스라 한다.
- 클래스를 통해서 사용자가 원하는 종류의 데이터 타입을 마음껏 정의할 수 있다.
클래스, 객체, 인스턴스
클래스는 설계도이고, 이 설계도를 기반으로 실제 메모리에 만들어진 실체를 객체 또는 인스턴스라 한다.
학생 클래스를 기반으로 학생1, 학생2 객체 또는 인스턴스를 만든다!
1. 변수 선언
Student student1
- Student 타입을 받을 수 있는 변수를 선언한다.
- int는 정수를, String은 문자를 담을 수 있듯이 Student는 Student 타입의 객체(인스턴스)를 받을 수 있다.
2. 객체 생성
student1 = new Student() 코드를 나누어 분석
- 객체를 사용하기 위해선 먼저 설계도인 클래스를 기반으로 객체(인스턴스)를 생성해야 한다.
- new Student() : new는 새로 생성한다는 뜻, new Student()는 Student 클래스 정보를 기반으로 새로운 객체를 생성하라는 뜻이다. 이렇게 하면 메모리에 실제 Student 객체(인스턴스)를 실행한다.
- 객체를 생성할 때는 new 클래스명()을 사용하면 된다. 마지막에 ()도 추가해야 한다.
- Student 클래스는 String name, int age, int grade 멤버 변수를 가지고 있다. 이 변수를 사용하는데 필요한 메모리 공간도 함께 확보해야 한다.
2024/05/10
3. 참조값 보관
- 객체를 생성하면 Java는 메모리 어딘가에 있는 이 객체에 접근할 수 있는 참조값(주소)(x001)을 반환한다.
- x001이라고 표현한 값은 참조값
- new 키워드를 통해 객체가 생성되고 나면 참조값을 반환한다. Student student1에 생성된 객체의 참조값(x001)을 보관한다.
- Student student1 변수는 이제 메모리에 존재하는 실제 Student 객체(인스턴스)의 참조값을 가지고 있다.
- student1 변수는 방금 만든 객체에 접근할 수 있는 참조값을 갖고 있다. 이 변수를 통해서 객체를 접근(참조)할 수 있다. student1 변수를 통해 메모리에 있는 실제 객체를 접근하고 사용할 수 있다.
참조값을 변수에 보관해야 하는 이유
객체를 생성하는 new Student() 코드 자체에는 아무런 이름이 없다.
코드는 단순히 Student 클래스를 기반으로 메모리에 실제 객체를 만드는 것이다. 생성한 객체에 접근할 수 있는 방법이 필요하다.
이런 이유로 객체를 생성할 때 반환되는 참조값을 어딘가에 보관해두어야 한다. Student student1 변수에 참조값(x001)을 저장해두었으므로 저장한 참조값(x001)을 통해서 실제 메모리에 존재하는 객체에 접근할 수 있다.
Student student1 = new Student(); // 1. Student 객체 생성
Student student1 = x001; // 2. new Student()의 결과로 x001 참조값 반환
student1 = x001; // 3. 최종 결과
학생2(student2)까지 생성하면 Student 객체(인스턴스)가 메모리에 2개가 생성된다.
각각의 참조값이 다르므로 서로 구분할 수 있다.
참조값을 확인하고 싶으면 다음과 같이 객체를 담고 있는 변수를 출력한다.
System.out.println(student1);
System.out.println(student2);
출력 결과
class1.Student@7a81197d
class1.Student@2f2c9b19
@앞은 패키지 + 클래스 정보를 뜻한다. @뒤에 16진수는 참조값을 뜻한다.
객체 사용
클래스를 통해 생성한 객체를 사용하려면 먼저 메모리에 존재하는 객체에 접근해야 한다.
package class1;
public class ClassStart3 {
public static void main(String[] args) {
Student student1 = new Student();
Student student2 = new Student();
student1.name = "학생";
student1.age = 15;
student1.grade = 90;
System.out.println("이름 : " + student1.name + " 나이 : " + student1.age + " 성적 : " + student1.grade);
System.out.println(student1);
System.out.println(student2);
}
}
객체 참조 그림
객체에 값 대입
객체가 가지고 있는 멤버 변수(name, age, grade)에 값을 대입하려면 먼저 객체에 접근해야 한다.
객체제 접근하려면 .(점 dot) 키워드를 사용하면 된다. 이 키워드는 변수(student1)에 들어있는 참조값(x001)을 읽어서 메모리에 존재하는 객체에 접근한다.
순서를 풀면 다음과 같다.
student1.name = "학생1" //1. student1 객체의 name 멤버 변수에 값 대입
x001.name = "학생1" //2. 변수에 잇는 참조값을 통해 실제 객체에 접근, 해당 객체의 name 멤버 변수에 값 대입
student1.name = "학생1" 코드 실행 전
student1.name = "학생1" 코드 실행 후
student1.name 코드를 통해 .(dot) 키워드가 사용된다. student1 변수가 가지고 있는 참조값을 통해 실제 객체에 접근한다.
객체에 값 읽기
객체의 값을 읽는 것도 앞서 설명한 내용과 같다. .(점, dot) 키워드를 통해 참조값을 사용해서 객체에 접근한 다음에 원하는 작업을 하면 된다.
// 1. 객체 값 읽기
System.out.println("이름: " + student1.name);
// 2. 변수에 있는 참조값을 통해 실제 객체에 접근하고, name 멤버 변수에 접근한다.
System.out.println("이름: " + x001.name);
//3. 객체의 멤버 변수의 값을 일거옴
System.out.println("이름: " + "학생1");
객체 값 읽기
객체 값 읽기
x001에 있는 Student 인스턴스의 name 멤버 변수는 학생 1이라는 값을 가지고 있다.
클래스, 객체, 인스턴스 정리
클래스 - Class
클래스는 객체를 생성하기 위한 틀 또는 설계도이다.
클래스는 객체가 가져야 할 속성(변수)과 기능(메서드)를 정의한다.
학생이라는 클래스는 속성으로 name, age, grade를 가진다.
- 틀 : 붕어빵 틀 생각. 붕어빵 틀은 붕어빵이 아니다. 이렇게 생긴 붕어빵이 나왔으면 좋겠다고 만드는 틀일뿐이다. 실제 먹을 수 있는 것이 아니다. 실제 먹을 수 있는 팥 붕어빵을 객체 또는 인스턴스라 한다.
- 설계도 : 자동차 설계도를 생각. 자동차 설계도는 자동차가 아니다. 설계도는 실제 존재하는 것이 아니라 개념으로만 있는 것이다.
객체 - Object
객체는 클래스에서 정의한 속성과 기능을 가진 실체이다. 객체는 서로 독립적인 상태를 가진다.
ex) student1은 학생1의 속성을 가지는 객체이고, student2는 학생2의 속성을 가지는 객체이다.
student1과 student2는 같은 클래스에서 만들었지만, 서로 다른 객체이다.
인스턴스 - Instance
특정 클래스로부터 생성된 객체를 의미한다. 객체와 인스턴스라는 용어는 자주 혼용된다. 인스턴스는 주로 객체가 어떤 클래스에 속해 있는지 강조할 때 사용한다. student1 객체는 Student 클래스의 인스턴스다. 라고 표현
객체 vs 인스턴스
인스턴스는 객체보다 좀 더 관계에 초점을 맞춘 단어이다. student1은 Student의 객체이다라고 말하는 대신 student1은 Student의 인스턴스이다. 라고 특정 클래스와의 관계를 명확히 할 때 인스턴스라는 용어를 주로 사용한다.
모든 인스턴스는 객체이지만, 인스턴스라고 부르는 순간은 특정 클래스로부터 그 객체게 생성되었음을 강조하고 싶을 때이다.
둘다 클래스에서 나온 실체라는 핵심 의미는 같기 때문에 보통 둘을 구분하지 않고 사용한다.
2024/05/13
배열 도입 - 시작
학생을 출력하는 부분이 코드로 아쉽다.
System.out.println("이름: " + student1.name + " 나이: " + student1.age + ...);
System.out.println("이름: " + student2.name + " 나이: " + student2,age + ...);
새로운 학생이 추가될 때 마다 출력하는 부분도 함께 추가해야 한다.
배열을 사용하게 되면 특정 타입을 연속한 데이터 구조로 묶어서 편리하게 관리할 수 있다.
package class1;
public class ClassStart4 {
public static void main(String[] args) {
Student student1 = new Student();
student1.name = "학생1";
student1.age = 15;
student1.grade = 90;
Student student2 = new Student();
student2.name = "학생2";
student2.age = 16;
student2.grade = 80;
Student[] students = new Student[2];
students[0] = student1;
students[1] = student2;
System.out.println("이름 : " + students[0].name + " 나이 : " + students[0].age + " 성적 : " + students[0].grade);
System.out.println("이름 : " + students[1].name + " 나이 : " + students[1].age + " 성적 : " + students[1].grade);
}
}
Student 클래스를 기반으로 student1, student2 인스턴스를 생성한 후, 필요한 값을 채워준다.
배열에 참조값 대입
Student를 담을 수 있는 배열을 생성하고, 해당 배열에 student1, student2 인스턴스를 보관
Student[] students = new Student[2];
Student 변수를 2개 보관할 수 있는 사이즈 2의 배열을 만든다.
Student 타입의 변수는 Student 인스턴스의 참조값을 보관한다. Student 배열의 각각의 항목도 Student 타입의 변수일 뿐이다. (Student 타입의 참조값을 보관한다.)
배열에는 아직 참조값을 대입하지 않았기 때문에 참조값이 없다는 의미의 null값으로 초기화 된다.
배열에 객체 보관하기
students[0] = student1;
students[1] = student2;
// java에서 대입은 항상 변수에 들어 있는 값을 복사한다.
students[0] = x001;
students[1] = x002;
Java에서 대입은 항상 변수에 들어 있는 값을 복사한다.
student1, student2에 보관된 참조값을 읽고 복사해서 배열에 대입한다고 표현한다.
배열에 참조값을 대입한 이후 배열
배열은 x001, x002의 참조값을 가진다. 참조값을 가지고 있기 때문에 x001(학생1) x002(학생) Student의 인스턴스에 모두 접근할 수 있다.
주의
배열에는 인스턴스 자체가 들어있는 것이 아니다! 인스턴스의 위치를 가리키는 참조값이 들어있을 뿐이다..! 대입시에 인스턴스가 복사되는 것이 아니라 참조값만 복사된다.
배열에 들어있는 객체 사용
배열에 들어있는 객체를 사용하려면 먼저 배열에 접근하고, 그 다음 객체에 접근하면 된다.
학생1
System.out.println(students[0].name); //배열 접근 시작
System.out.println(x005[0].name); //[0]를 사용해서 x005 배열의 0번 요소에 접근
System.out.println(x001.name); //.(dot)을 사용해서 참조값으로 객체에 접근
System.out.println("학생1");
학생2
System.out.println(students[1].name); //배열 접근 시작
System.out.println(x005[1].name); //[1]를 사용해서 x005 배열의 1번 요소에 접근
System.out.println(x002.name); //.(dot)을 사용해서 참조값으로 객체에 접근
System.out.println("학생2");
배열 도입 - 리펙토링
배열을 사용한 덕분에 for문 도입이 가능해졌다.
package class1;
public class ClassStart5 {
public static void main(String[] args) {
Student student1 = new Student();
student1.name = "학생1";
student1.age = 15;
student1.grade = 90;
Student student2 = new Student();
student2.name = "학생2";
student2.age = 16;
student2.grade = 80;
Student[] students = {student1,student2};
for(Student student: students) {
System.out.println("이름 : " + student.name + " 나이 : " + student.age + " 성적 : " + student.grade);
}
}
}
배열 선언 최적화
Student[] students = new Student[]{student1, student2};
Student[] students = {student1, student2};
향상된 for문
for (int i = 0; i < students.length; i++) {
Student s = students[i];
System.out.println("이름:" + s.name + " 나이:" + s.age + ...);
}
for (Student s : students) {
System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" + s.grade);
}
문제풀이
1번 - 영화 리뷰 관리하기
package class1.PrblemSolve;
public class MovieReviewMain {
public static void main(String[] args) {
MovieReview movieReview1 = new MovieReview("인셉션", "인생은 무한 루프");
MovieReview movieReview2 = new MovieReview("어바웃 타임", "인생 시간 영화!");
MovieReview[] movieReviews = new MovieReview[]{movieReview1, movieReview2};
for(MovieReview movieReview: movieReviews) {
System.out.println("영화 제목 : " + "\"" + movieReview.getTitle() + "\"" + "," + " 리뷰: " + "\"" + movieReview.getContent() + "\"");
}
}
}
2024/05/14
2번 - 상품 주문 시스템 개발
package class1.PrblemSolve;
public class ProductOrderMain {
public static void main(String[] args) {
ProductOrder order1 = new ProductOrder("두부",2000,2);
ProductOrder order2 = new ProductOrder("김치",5000,2);
ProductOrder order3 = new ProductOrder("콜라",1500,2);
ProductOrder[] orders = {order1,order2,order3};
int totalPrice = 0;
for(ProductOrder order : orders) {
totalPrice += order.getPrice() * order.getQuantity();
System.out.println("상품명: " + order.getProductName() + "," + " 가격: " + order.getPrice() + "," + " 수량: " + order.getQuantity());
}
System.out.println("총 결제 금액: " + totalPrice);
}
}
'Backend > Java' 카테고리의 다른 글
[김영한 실전 자바 - 기본편] 생성자 (0) | 2024.05.18 |
---|---|
[김영한 실전 자바 - 기본편] 생성자 (0) | 2024.05.17 |
[Effective Java] 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2024.04.26 |
[Effective Java] 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2024.04.26 |
[Java의 정석] - 컬렉션 프레임웍(Collection Framework) (0) | 2024.04.25 |