관리 메뉴

나구리의 개발공부기록

클래스와 데이터, 프로젝트 환경 구성, 클래스가 필요한 이유, 클래스 도입, 객체 사용, 클래스, 객체, 인스턴스 정리, 배열 도입, 문제와 풀이 본문

인프런 - 실전 자바 로드맵/실전 자바 - 기본편

클래스와 데이터, 프로젝트 환경 구성, 클래스가 필요한 이유, 클래스 도입, 객체 사용, 클래스, 객체, 인스턴스 정리, 배열 도입, 문제와 풀이

소소한나구리 2024. 12. 20. 18:12

출처 : 인프런 - 김영한의 실전 자바 - 기본편 (유료) / 김영한님  
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용


 

1. 프로젝트 환경 구성

1) 프로젝트 생성

(1) 프로젝트 생성

  • Name: java-basic
  • build system: IntelliJ
  • JDK: 자바 버전 17 이상

2. 클래스가 필요한 이유

1) 학생 정보 출력 프로그램 만들기

(1) 클래스가 필요한 이유

  • 자바는 클래스와 객체로 이루어져 있는데, 해당 내용은 너무 많은 내용을 포함하고 있어 한번에 이해하기가 쉽지 않음
  • 점진적으로 이해해보기위해 문제를 풀어보면서 이해해보기

(2) 예시 출력

  • 더보기
    이름: 학생1 나이: 15 성적: 90
    이름: 학생2 나이: 16 성적: 80

(3) ClassStart1 - 변수 사용

  • 아래의 코드는 학생이 늘어날 때마다 변수를 추가로 선언해야 하고 출력하는 코드를 반복적으로 추가해야함
  • 이런 문제는 배열을 사용하면 해결할 수 있음
package class1;

public class ClassStart1 {
    public static void main(String[] args) {

        String student1Name = "학생1";
        int student1Age = 15;
        int student1Grade = 90;

        String student2Name = "학생2";
        int student2Age = 16;
        int student2Grade = 80;

        System.out.println("이름: " + student1Name + " 나이: " + student1Age + " 성적: " + student1Grade);
        System.out.println("이름: " + student2Name + " 나이: " + student2Age + " 성적: " + student2Grade);
    }
}

 

(4) ClassStart2 - 배열을 활용

  • 배열을 사용하면 반복문을 통해 출력문의 코드를 줄일 수 있음
package class1;

public class ClassStart2 {
    public static void main(String[] args) {
        String[] studentNames = {"학생1", "학생2"};
        int[] studentAges = {15, 16};
        int[] studentGrades = {90, 80};

        for (int i = 0; i < studentNames.length; i++) {
            System.out.println("이름: " + studentNames[i] + " 나이: " + studentAges[i] + " 성적: " + studentGrades[i]);
        }
    }
}

 

(5) 배열 사용의 한계

  • 배열을 사용해서 코드 변경을 최소화하는데에는 성공했으나, 한 학생의 데이터가 3개의 배열에 나누어져 있어 데이터를 변경할 때 매우 조심해서 작업해야함
  • 예를 들어 학생 2의 데이터를 제거하려면 각각의 배열마다 학생 2의 요소를 정확하게 찾아서 제거해주어야 하는데 이 배열의 인덱스 순서를 항상 정확하게 맞추어야 하는등 신경써야할 문제가 많고 수정 과정에서 실수할 가능성이 매우 높아짐
  • 이 코드는 컴퓨터가 볼 때는 아무 문제가 없지만 사람이 관리하게는 좋은 코드가 아님
  • 지금처럼 이름 나이 성적을 각각 따로 나누어서 관리하는 것은 사람이 관리하기 좋은 방식이 아니며 사람이 관리하기 좋은 방식은 학생이라는 개념을 하나로 묶는 것임
  • 그리고 각각의 학생 별로 본인의 이름, 나이, 성적을 관리하는 것임

3. 클래스 도입

1) 클래스 도입으로 문제 해결

(1) Student 클래스

  • class 키워드를 사용해서 학생 클래스를 정의
  • 학생 클래스는 내부에 이름, 나이, 성적 변수를 가지며 이렇게 클래스에 정의한 변수들을 멤버 변수, 또는 필드라고함
  • 멤버 변수(Member Variable): 특정 클래스에 소속된 멤버
  • 필드(Field): 데이터 항목을 가리키는 전통적인 용어로 데이터베이스, 엑셀 등에서 데이터 각각의 항목을 필드라고함
  • 자바에서는 멤버 변수, 필드는 같은 뜻이며 클래스에 소속된 변수를 뜻함
  • 클래스는 관례상 대문자로 시작하고 낙타 표기법을 사용함
package class1;

public class Student {
    String name;
    int age;
    int grade;
}

 

(2) ClassStart3

  • Student1과 student2를 통해서 각 필드에 접근하여 값들을 출력
package class1;

public class ClassStart3 {
    public static void main(String[] args) {
        Student student1;
        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;

        System.out.println("이름: " + student1.name + " 나이: " + student1.age + " 성적: " + student1.grade);
        System.out.println("이름: " + student2.name + " 나이: " + student2.age + " 성적: " + student2.grade);
    }
}

 

(3) 클래스와 사용자 정의 타입

  • 타입은 데이터의 종류나 형태를 나타냄
  • int는 정수타입, String은 문자타입처럼 학생(Student)라는 사용자 정의 타입을 만든 것이며 클래스를 사용하여 타입을 직접 만들 수 있음
  • 사용자가 직접 정의하는 사용자 정의 타입을 만들려면 설계도가 필요한데 이 설계도가 클래스
  • 그리고 설계도인 클래스를 사용하여 실제 메모리에 만들어진 실체를 객체 또는 인스턴스라고 함
  • 즉 클래스를 통해 사용자가 원하는 종류의 데이터 타입을 마음껏 정의할 수 있음

** 용어정리: 클래스, 객체 인스턴스

  • 클래스는 설계도
  • 해당 설계도(클래스)를 기반으로 실제 메모리에 만들어진 실체를 객체 또는 인스턴스라 하며 같은의미로 사용됨(미묘한 뉘앙스 차이가 존재하는데 뒤에서 설명함)
  • 위 예제는 학생(Student) 클래스를 기반으로 학생1(Student1), 학생2(Student2) 객체 또는 인스턴스를 만든 것임

2) 코드 분석

(1) 변수 선언

  • Student 타입을 받을 수 있는 변수를 선언
  • int는 정수를 String을 문자를 담을 수 있듯이 Student는 Student 타입의 객체(인스턴스)를 받을 수 있음
Student student1

 

(2) 객체 생성

  • 객체를 사용하려면 먼저 설계도인 클래스를 기반으로 객체(인스턴스)를 생성해야 함
  • new Student(): new는 새로 생성한다는 뜻이며 new Student()는 Student 클래스 정보를 기반으로 새로운 객체를 생성하라는 뜻으로 이렇게 하면 메모리에 실제 Student 객체(인스턴스)를 생성함
  • 객체를 생성할 때는 new 클래스명() 을 사용하면 되고 마지막에 ()로 꼭 추가해야함
  • Student 클래스는 String name, int age, int grade 멤버 변수를 가지고 있으므로 이 변수를 사용하는데 필요한 메모리 공간도 함께 확보함
student1 = new Student();

 

(3) 참조값 보관

  • 객체를 생성하면 자바는 메모리 어딘가에 있는 이 객체에 접근할 수 있는 참조값(주소)을 반환함
  • 아래에서 x001이라고 표현한 것이 참조값인데 실제로 x001처럼 표현되는 것은 아니고 이해를 돕기위한 예시표현임
  • new 키워드를 통해 객체가 생성되고 나면 참조값을 반환하며 앞서 선언한 변수인 Student student1에 생성된 객체의 참조값(x001)을 보관함
  • Student student1 변수는 메모리에 존재하는 실제 Student 객체(인스턴스)의 참조값을 가지고 있으므로 이 변수를 통해서 객체를 접근(참조)할 수 있음
  • 즉, student1 변수를 통해 메모리에 있는 실제 객체를 접근하고 사용할 수 있음
student1 = x001;(참조값)

 

(4) 참조값을 변수에 보관해야 하는 이유

  • 객체를 생성하는 new Student() 코드 자체에는 아무런 이름이 없고 단지 Student 클래스를 기반으로 메모리에 실제 객체를 만드는 것이므로 생성한 객체에 접근할 수 있는 방법이 필요함
  • 이런 이유로 객체를 생성할 때 반환되는 참조값을 어딘가에 보관해야하므로 변수에 참조값을 저장하고 이를 통해서 실제 메모리제 존재하는 객체에 접근할 수 있는 것임
  • 이후에 학생2까지 생성하게 되면 Student 객체가 메모리에 2개 생성되고 각각 참조값이 다르므로 서로 구분할 수 있음
  • 참조값을 확인하고 싶으면 아래처럼 객체를 담고 있는 변수를 출력해보면 됨
Student student1 = new Student(); //1. Student 객체 생성
Student student1 = x001; //2. new Student()의 결과로 x001 참조값 반환
student1 = x001; //3. 최종 결과

// 참조값 출력
System.out.println("student1 = " + student1);
System.out.println("student2 = " + student2);

/* 출력 결과
student1 = class1.Student@30f39991
student2 = class1.Student@452b3a41
*/

4. 객체 사용

1) 객체 사용

(1) 객체에 접근

  • 클래스를 통해 생성한 객체를 사용하려면 메모리에 존재하는 객체에 접근해야하는데 .(점, dot)을 사용하여 접근할 수 있음
// 객체 값 대입
student1.name = "학생1";
student1.age = 15;
student1.grade = 90;

// 객체 값 사용
System.out.println("이름: " + student1.name + " 나이: " + student1.age + " 성적: " + student1.grade);

 

(2) 객체 값 대입

  • 객체가 가지고 있는 멤버 변수에 값을 대입하기 위해 .(점, dot) 키워드를 사용하여 객체에 접근하면 되는데, 이 키워드는 변수에 들어있는 참조값을 읽어서 메모리에 존재하는 객체에 접근함
  • student1. 이라고 하면 student1 변수가 가지고 있는 참조값을 통해 실제 객체에 접근하고 student1은 x001이라는 참조값을 가지고 있으므로 x001 위치에 있는 Student 객체에 접근함
  • 즉, student1.name = "학생1" 일 때 .(점, dot)으로 student1 변수가 가지고 있는 참조값(x001)을 통해서 실제 객체에 접근하고 그 객체의 필드인 name에 "학생1" 데이터가 저장됨

(3) 객체 값 읽기

  • 객체의 값을 읽는 것도 앞서 설명한 내용과 동일하며 .(점, dot) 키워드를 통해 참조값을 사용해서 객체에 접근한 다음 원하는 작업을 하면 됨
  • println(student1.name)일 때 .(점, dot)으로 student1 변수가 가지고 있는 참조값을 통해서 실제 객체에 접근하고 그 객체의 필드인 name이 가지고 있는 값을 읽어서 출력이 됨

5. 클래스, 객체, 인스턴스 정리

1) 정리

(1) 클래스 - Class

  • 객체를 생성하기 위한 틀 또는 설계도이며 객체가 가져야할 속성(변수)과 기능(메서드)를 정의함
  • 학생이라는 클래스는 속성으로 name, age, grade를 가지도록 작성할 수 있었음
  • 기능(메서드)에 대한 내용은 뒤에서 설명할 예정
  • 예를 들어 붕어빵 틀을 생각해보면 붕어빵을 만들기 위한 틀일 뿐이며 이 틀을 클래스에 비유하기도하며, 자동차 설계도를 생각해보면 실제 존재하는 것이 아니라 개념만으로 있는 것으로 이런 설계도를 클래스에 비유하기도 함
  • 그리고 틀로 찍어내어 실제 먹을 수 있는 붕어빵, 설계도를 통해 생산한 실제 존재하는 자동차를 객체 또는 인스턴스라 함

(2) 객체 - Object

  • 클래스에서 정의한 속성과 기능을 가진 실체이며 객체는 서로 독립적인 상태를 가짐
  • 예제 코드에서 Student라는 같은 클래스에서 만들어졌지만 student1은 학생1의 속성을 가지는 객체이고 student2는 학생2의 속성을 가지는 객체로 서로 다른 객체임

(3) 인스턴스 - Instance

  • 특정 클래스로부터 생성된 객체를 의미로 객체와 인스턴스라는 용어는 자주 혼용되어 사용됨
  • 인스턴스는 주로 객체가 어떤 클래스에 속해 있는지 강조할 때 사용하는데 예를 들어 student1 객체는 Student 클래스의 인스턴스라도 표현할때 주로 사용함

(4) 객체 vs 인스턴스

  • 둘다 클래스에서 나온 실체라는 의미에서 비슷하게 사용되지만 용어상 인스턴스는 객체보다 좀 더 관계에 초점을 맞춘 단어라고 보면됨
  • 보통 student1은 Student의 객체라고 말하는 대신 student1은 Student1 인스턴스라고 말하는 것 처럼 특정 클래스와의 관계를 명확히 할 때 인스턴스라는 용어를 주로 사용함
  • 모든 인스턴스는 객체이지만 인스턴스라고 부르는 순간은 특정 클래스로부터 그 객체가 생성되었음을 강조하고 싶을 때임
  • 하지만 둘다 클래스에서 나온 실체라는 핵심 의미는 같기 때문에 보통 둘을 구분하지 않고 사용함(대부분 구분하지 않고 사용한다고 함)

6. 배열 도입

1) 시작

(1) 클래스에 배열도입

  • 클래스와 객체 덕분에 학생 데이터를 마치 실제 학생이 있고 그 안에 학생의 정보가 있는 것 처럼 구조적으로 이해하기 쉽게 변경 되었으므로 각각의 학생 별로 객체를 생성하고 해당 객체에 학생의 데이터를 관리하면 됨
  • 그러나 코드를 보면 아쉬운 부분이 있는데 학생을 출력하는 부분을 계속 추가해 주어야한다는 점임
  • 배열을 사용하면 특정 타입을 연속한 데이터 구조로 묶어서 편리하게 관리할 수 있으므로 Student 클래스를 사용한 변수들도 Student 타입이기 때문에 학생도 배열을 사용해서 하나의 데이터 구조로 묶어서 관리할 수 있음

(2) ClassStart4

  • Student 클래스를 기반으로 student1, student2 인스턴스를 생성하고 필요한 값을 채움
  • Student를 담을 수 있는 배열을 생성하고 해당 배열에 student1, student2 인스턴스를 보관
  • Student[] students = new Student[2]
    - Student 타입의 변수를 2개 보관할 수 있는 사이즈 2의 배열을 생성하며 이 배열은Student 타입의 참조값을 보관할 수 있음
    - 최초 생성시에는 참조값을 대입하지 않았기 때문에 null 값으로 초기화가 됨
  • student[0] = student1
    - 자바에서 대입은 항상 변수에 들어있는 값을 복사(중요함)하므로 student1에 들어있는 참조값을 복사하여 배열에 저장함
    - student1에 보관된 참조값을 읽고 복사해서 배열에 대입한다고 표현함
  • student[1] = student2도 실행하면 배열은 x001, x002의 참조값을 가지고 있기 때문에 x001(학생1), x002(학생2) Student 인스턴스에 모두 접근할 수 있음
  • 배열에 들어있는 객체를 사용할 때는 먼저 배열에 접근하고 그 다음에 객체에 접근하면 됨(출력 문의 코드를 보면 이해가 쉬움)
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 = "학생1";
        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);
    }
}

 

** 자바에서 대입은 항상 변수에 들어 있는 값을 복사해서 전달함

  • 자바에서 변수의 대입은 모두 변수에 들어있는 값을 복사해서 전달함
  • 오른쪽 변수인 student1, student2에는 참조값이 들어있는데 이 값을 복사해서 왼쪽에 있는 배열에 전달했으므로 기존 student1, student2에 들어있던 참조값은 당연히 그대로 유지됨

** 주의

  • 변수에는 인스턴스 자체가 들어있는 것이 아니라 인스턴스의 위치를 가리키는 참조값이 들어있을 뿐임
  • 따라서 대입시에 인스턴스가 복사되는 것이 아니라 참조값이 복사되는 것임

2) 리펙토링

(1) ClassStart5

  • 배열을 사용한 덕분에 출력에서 for문을 도입할 수 있게 되었음
  • new Student[] {student1, student2}를 통해서 배열을 생성할 때 값을 입력할 수 있으며 Student[] students = {student1, student2}처럼 더욱 최적화 할 수 있음
  • 일반 for문으로 출력할 수 있지만 향상된 for문(Enhanced For Loop)으로 배열의 요소를 하나씩 꺼낼 수 있음(인텔리제이에서는 iter라고 치면 됨)
package class1;

public class ClassStart5 {
    public static void main(String[] args) {
        // 객체 생성 코드 동일

        Student[] students = new Student[]{student1, student2};
        
        // 일반 for문
        for (int i = 0; i < students.length; i++) {
            System.out.println("이름: " + students[i].name + " 나이: " + students[i].age + " 성적: " + students[i].grade);
        }
        
        // 향상된 for문 사용 - IntelliJ 에서 iter 를 입력하면 됨
        for (Student student : students) {
            System.out.println("이름: " + student.name + " 나이: " + student.age + " 성적: " + student.grade);
        }
    }
}

7. 문제와 풀이

1) 영화 리뷰 관리하기1

(1) 문제 설명 및 요구사항

  • 영화 리뷰 정보를 관리를 위해 영화 리뷰 정보를 담을 수 있는 MovieReview 클래스를 생성
  • 1. MovieReview 클래스에는 영화 제목 - title, 리뷰 내용 - review 멤버 변수를 포함해야 함
  • 2. MovieReviewMain 클래스 안에 main() 메서드를 포함하여 영화 리뷰 정보를 선언하고 출력
  • 배열 없이 출력

(2) 예시 코드 구조 및 출력 예시

더보기
public class MovieReview {
    String title;
    String review;
}
public class MovieReviewMain {
    public static void main(String[] args) {
        // 영화 리뷰 정보 선언
        // 영화 리뷰 정보 출력
    }
}

 

영화 제목: 인셉션, 리뷰: 인생은 무한 루프

영화 제목: 어바웃 타임, 리뷰: 인생 시간 영화!

 

(3) 답안

더보기
package class1.ex;

public class MovieReview {
    String title;
    String review;
}
package class1.ex;

public class MovieReviewMain {
    public static void main(String[] args) {
        MovieReview movieReview1 = new MovieReview();
        movieReview1.title = "인셉션";
        movieReview1.review = "인생은 무한 루프";

        MovieReview movieReview2 = new MovieReview();
        movieReview2.title = "어바웃 타임";
        movieReview2.review = "인생 시간 영화!";

        System.out.println("영화 제목: " + movieReview1.title + ", 리뷰: " + movieReview1.review);
        System.out.println("영화 제목: " + movieReview2.title + ", 리뷰: " + movieReview2.review);
    }
}

 

영화 제목: 인셉션, 리뷰: 인생은 무한 루프
영화 제목: 어바웃 타임, 리뷰: 인생 시간 영화!

2) 영화 리뷰 관리하기2

(1) 요구사항

  • 기존 문제에 배열을 도입하여 출력
  • 리뷰를 출력할 때 배열과 for문을 사용하여 출력문을 한번(한줄)만 사용하여 출력

(2) 답안

더보기
package class1.ex;

public class MovieReviewMain1 {
    public static void main(String[] args) {
        MovieReview movieReview1 = new MovieReview();
        movieReview1.title = "인셉션";
        movieReview1.review = "인생은 무한 루프";

        MovieReview movieReview2 = new MovieReview();
        movieReview2.title = "어바웃 타임";
        movieReview2.review = "인생 시간 영화!";

        MovieReview[] movieReviews = {movieReview1, movieReview2};
        for (MovieReview movieReview : movieReviews) {
            System.out.println("영화 제목: " + movieReview.title + ", 리뷰: " + movieReview.review);
        }
    }
}

 

출력 결과는 동일

3) 상품 주문 시스템 개발

(1) 문제 설명 및 요구사항

  • 온라인 상점의 주문 관리 시스템을 생성
  • 상품 주문정보를 담을 수 있는 ProductOrder 클래스를 생성
  • 1. ProductOrder 클래스에는 상품명 - productName, 가격 - price, 주문 수량 - quantity 라는 멤버 변수를 포함해야 함
  • 2. ProductOrderMain클래스 안에 main() 메서드를 포함하여 여러 상품의 주문 정보를 배열로 관리하여 해당 정보들을 출력하고 최종 결제 금액을 계산하여 출력

(2) 예시 코드 구조 및 출력 예시

더보기
public class ProductOrder {
    String productName;
    int price;
    int quantity;
}
public class ProductOrderMain {
    public static void main(String[] args) {
        // 여러 상품의 주문 정보를 담는 배열 생성
        // 상품 주문 정보를 `ProductOrder` 타입의 변수로 받아 저장
        // 상품 주문 정보와 최종 금액 출력
    }
}


상품명: 두부, 가격: 2000, 수량: 2

상품명: 김치, 가격: 5000, 수량: 1

상품명: 콜라, 가격: 1500, 수량: 2

총 결제 금액: 12000

 

(3) 정답

더보기
package class1.ex;

public class ProductOrderMain {
    public static void main(String[] args) {
        ProductOrder[] productOrders = new ProductOrder[]{new ProductOrder(), new ProductOrder(), new ProductOrder()};

        productOrders[0].productName = "두부";
        productOrders[0].price = 2000;
        productOrders[0].quantity = 2;

        productOrders[1].productName = "김치";
        productOrders[1].price = 5000;
        productOrders[1].quantity = 1;

        productOrders[2].productName = "콜라";
        productOrders[2].price = 1500;
        productOrders[2].quantity = 2;

        int totalPrice = 0;

        for (ProductOrder productOrder : productOrders) {
            System.out.println("상품명: " + productOrder.productName + ", 가격: " + productOrder.price + ", 수량: " + productOrder.quantity);
            totalPrice += productOrder.price * productOrder.quantity;
        }
        System.out.println("총 결제 금액: " + totalPrice);
    }
}

 

상품명: 두부, 가격: 2000, 수량: 2
상품명: 김치, 가격: 5000, 수량: 1
상품명: 콜라, 가격: 1500, 수량: 2
총 결제 금액: 12000