자바의 기초, 클래스의 기본

이병관·2025년 1월 24일
0

클래스가 없을 때의 코드: “날것” 데이터 관리의 어려움

먼저 클래스가 없는 첫 번째 예제를 살펴볼게요:

package class1;

public class classStart1 {
    public static void main(String[] args) {
        String studentName = "학생1";
        int studentAge = 15;
        int studentGrade = 90;

        String studentName2 = "학생1";
        int studentAge2 = 16;
        int studentGrade2 = 80;

        System.out.println("이름:" + studentName + " 나이: " + studentAge + " 성적: " + studentGrade);
        System.out.println("이름:" + studentName2 + " 나이: " + studentAge2 + " 성적: " + studentGrade2);
    }
}

문제점 1. 변수가 중복되고 관리가 번거롭다

  • 학생 한 명을 추가할 때마다 새로운 변수(예: studentName3, studentAge3 …)를 계속 만들어야 합니다.
  • 만약 학생이 100명이 된다면? studentName100, studentAge100 … 이름 짓기도 힘들고, 변수 생성해놓고 실수라도 하면 골치 아프겠죠.

문제점 2. 관련 정보가 흩어져 있다

  • 학생의 이름, 나이, 성적은 모두 서로 밀접하게 관련된 정보인데, ‘하나’로 묶어서 다루기 어렵습니다.
  • 다른 프로그램(메서드)에서 이 정보들을 동시에 전달하거나 변경하려면 매번 세 변수를 별도로 넘겨줘야 하니 불편합니다.

배열로 개선된 코드: 조금 더 나은 접근

package class1;

public class classStart2 {
    public static void main(String[] args) {
        String[] studentNames = {"학생1", "학생2", "학생3", "학생4"};
        int[] studentAge = {15, 16, 17, 18};
        int[] studentGrade = {90, 80, 70, 60};

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

달라진 점

  • 반복문을 활용해서 같은 형태의 처리를 간편하게 할 수 있게 됐습니다.
  • 학생을 추가하려면 배열의 크기를 늘리거나 새로운 배열을 생성하기만 하면 됩니다.

여전히 아쉬운 점

  • 여전히 studentNames, studentAge, studentGrade 세 가지 배열을 따로 관리해야 해요.
  • ‘한 명의 학생’에 관한 정보를 다룰 때, 이름 배열의 i번째, 나이 배열의 i번째, 성적 배열의 i번째를 모두 하나로 묶어줘야 합니다.
  • 배열의 순서를 잘못 맞추면(예: 이름은 2번 인덱스, 나이는 3번 인덱스…) 데이터가 꼬이기 쉬워요.

클래스의 등장: “학생”이라는 객체의 탄생

자, 이제 클래스를 도입해봅시다!

학생 클래스 정의

package class1;

public class Student {
    String name;
    int age;
    int grade;
}
  • Student라는 클래스는 말 그대로 ‘학생’이라는 개념을 코드로 표현(모델링)한 겁니다.
  • 이름, 나이, 성적이라는 속성(필드)을 가지고 있죠.

클래스 사용 예시

package class1;

public class ClassStart3 {
    public static void main(String[] args) {
        Student student1;
        student1 = new Student();
        student1.name = "학생1";
        student1.age = 18;
        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);
    }
}

어떤 점이 좋아졌을까?

  1. 한 덩어리로 묶인 데이터

    • student1 객체에는 name, age, grade가 모두 들어있습니다.
    • 추가적인 정보(예: major, studentId)가 필요해져도, Student 클래스에 필드를 추가만 해주면 됩니다.
  2. 확장성 증가

    • 만약 ‘학생이 시험을 본다’ 같은 행동(메서드)을 추가하고 싶다면, Student 클래스에 해당 메서드를 넣으면 됩니다.
    • 예: public void takeExam() { … }
    • 이렇게 데이터(필드)와 행동(메서드)를 한 곳에 둘 수 있으니, 객체지향 프로그래밍(OOP)의 장점을 살릴 수 있습니다.
  3. 코드의 가독성

    • “학생”이라는 하나의 개념이 코드에서도 “Student”라는 클래스로 명확히 드러납니다.
    • 배열 여러 개를 가지고 인덱스 맞추는 것보다 훨씬 직관적입니다.
  4. 유지보수에 유리

    • 학생 관련 로직이 꼭 필요한 부분에서만 Student를 생성해서 사용하면 됩니다.
    • 코드가 길어져도, 클래스별로 기능을 나누어 깔끔하게 유지보수할 수 있어요.
    • 예를 들어, 수업(ClassRoom) 객체나 과목(Subject) 객체가 생겨도, “학생”이라는 객체를 바로 재활용할 수 있죠!

“대체 왜 클래스가 중요한가요?”

1. ‘설계도(Blueprint)’로서의 역할

  • 클래스는 객체를 만들 수 있는 설계도 역할을 합니다.
  • 수백 명의 학생이 있어도, 다 같은 속성(name, age, grade)을 공유하니까 어디서든 간단하게 new Student()로 만들 수 있습니다.

2. 재사용성 극대화

  • 한 번 만든 Student 클래스를 다른 프로젝트나 다른 부분에서도 재사용 가능.
  • “학생”에 대한 공통 속성이나 기능이 달라져도 클래스를 수정하면 그 클래스를 쓰는 모든 곳이 자동으로 반영됩니다.

3. 객체지향 프로그래밍(OOP)의 핵심

  • 추상화(Abstraction): Student는 실제 “학생”이라는 현실 개념을 코드로 추상화한 것.
  • 캡슐화(Encapsulation): 객체 내부 정보(필드)와 행위(메서드)를 하나의 덩어리로 묶어서, 객체 외부에서는 필요한 인터페이스만 볼 수 있게 함.
  • 상속(Inheritance), 다형성(Polymorphism) 등 다른 OOP 개념들과 결합해 더 강력한 구조 설계가 가능해짐.

클래스 없이 코딩하기와 클래스 쓰기의 차이

  • 클래스 없이 변수를 나열하거나 배열로만 관리하면 작은 프로그램에서는 괜찮을 수 있지만, 기능이 늘어나고 관리할 데이터가 많아질수록 어려워집니다.
  • 클래스를 사용하면 코드 구조가 명확해지고, 재사용성이 높아지며, 유지보수 또한 수월해집니다.
  • OOP 철학의 핵심이라 할 수 있는 “현실 세계의 사물(객체)을 코드로 옮긴다”는 사고방식을 자연스럽게 배울 수 있습니다.

한마디로
클래스는 “코드를 효율적으로 조직하고, 유지보수성을 높이고, 재사용까지 가능한 멋진 방법”을 제공하는 기초 중의 기초입니다!

코드를 짤 때마다 “이건 어떤 클래스(객체)로 나타낼 수 있을까?”를 고민해보는 습관을 들이면, 어느 순간 더 이상 엉켜있는 배열과 변수들로 고생하지 않게 될 거예요.

자바에서의 대입: “변수에 들어있는 값(Value)을 복사해서 전달한다”

자바에서는 “변수에 들어있는 값을 복사해서 전달한다”라는 말이 자주 등장합니다. 이게 무슨 의미인지 간단히 살펴봅시다.

1. 자바에서 변수는 두 가지 종류가 있다

  • 기본형(Primitive type) 변수
    예: int, double, boolean
    → 실제 숫자(값)나 논리값이 변수 안에 직접 저장됩니다.

  • 참조형(Reference type) 변수
    예: String, Student, Array 등 (클래스로 만든 모든 객체 포함)
    → 객체 그 자체가 아니라, “객체가 메모리에 위치한 주소(참조)”를 참조하는 값이 저장됩니다.

2. “값을 복사해서 전달한다”의 의미

Student student1 = new Student(); 

위 코드에서 student1은 “Student 객체에 대한 참조값”을 갖고 있습니다.

만약

Student student2 = student1;

라고 하면, student1이 갖고 있던 “참조값”을 student2복사해서 전달합니다. 결국 student1student2는 같은 객체를 가리키게 되죠.

  • 중요한 점: 객체 자체가 복사되는 게 아니라, 객체를 가리키는 “참조값”만 복사됩니다.
  • 기본형 변수도 마찬가지입니다. 예를 들어 int a = 10; int b = a;라면, a 안의 정수 10b로 복사될 뿐입니다.

예제 코드로 살펴보기

package class1;

public class ClassStart5 {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.name = "학생1";
        student1.age = 18;
        student1.grade = 90;

        Student student2 = new Student();
        student2.name = "학생2";
        student2.age = 16;
        student2.grade = 80;

        Student[] students = {student1, student2};

        // 첫 번째 방법: 일반 for문
        /*
        for(int i = 0; i < students.length; i++) {
            System.out.println("이름: " + students[i].name + 
                               " 나이: " + students[i].age + 
                               " 성적: " + students[i].grade);
        }
        */

        // 두 번째 방법: 일반 for문에서 임시 변수 사용
        /*
        for(int i = 0; i < students.length; i++) {
            Student s = students[i];
            System.out.println("이름: " + s.name + 
                               " 나이: " + s.age + 
                               " 성적: " + s.grade);
        }
        */

        // 세 번째 방법: 향상된 for문
        for (Student s : students) {
            System.out.println("이름: " + s.name + 
                               " 나이: " + s.age + 
                               " 성적: " + s.grade);
        }
    }
}

코드 설명

  1. student1, student2를 생성하고, 각각의 필드를 초기화합니다.
  2. students라는 Student 배열에 student1, student2의 참조를 담습니다.
    • Student[] students = {student1, student2};
    • 여기서 배열의 각 요소는 Student 객체를 직접 담고 있는 게 아니라, 객체의 참조값을 담고 있어요.
  3. for (Student s : students) 구문에서 s는 배열 students의 각 요소(참조값)을 복사받아 사용합니다.
    • students[0] 안에는 student1의 참조값이, students[1] 안에는 student2의 참조값이 들어있습니다.
    • 따라서 sstudents[i]의 참조값을 받아서 실제 객체(student1 혹은 student2)에 접근하게 되죠.
  4. 결과적으로 System.out.println(...)을 통해 각 학생의 정보를 출력합니다.

추가적인 내용: for문 세 가지 방식 비교

  1. 전통적인 for문

    for(int i = 0; i < students.length; i++) {
        System.out.println(students[i].name);
    }
    • 인덱스를 직접 사용해야 하며, i의 범위를 지정해야 합니다.
  2. 전통적인 for문 + 임시 변수 사용

    for(int i = 0; i < students.length; i++) {
        Student s = students[i];
        System.out.println(s.name);
    }
    • 반복문 안에서 students[i]를 매번 타이핑할 필요 없이, 한 번 Student s로 받아서 사용합니다.
  3. 향상된 for문(Enhanced for)

    for (Student s : students) {
        System.out.println(s.name);
    }
    • 배열이나 컬렉션을 순회할 때 인덱스 없이 자동으로 각 요소를 꺼내 사용합니다.
    • 코드가 간결해지고, 실수로 인덱스를 벗어나지 않게 됩니다.

핵심 요약

  1. 자바에서의 대입은 변수 안에 있는 (기본형이면 실제 값, 참조형이면 객체의 주소(참조))을 복사해서 전달한다는 것.
  2. 객체(참조형) 대입 시에는 객체 자체가 복사되는 게 아니라, 객체를 가리키는 참조(주소)가 복사된다.
  3. 배열(Student[] students)에 객체를 넣었을 때도, 실제로는 배열이 객체의 주소들을 저장한다.
  4. 반복문(for 혹은 향상된 for)을 통해 배열 요소에 접근할 때도, 결국 참조값을 가져와서 실제 객체에 접근하는 구조다.

한 줄 정리:
자바에서 “= 연산자(대입)”가 일어날 때는 항상 변수에 들어있는 ‘값’을 복사해 간다.

  • 기본형이면 ‘실제 값(숫자, 논리값 등)’이 복사되고,
  • 참조형이면 ‘객체의 주소(참조값)’가 복사된다.

이 개념을 이해하면, 자바에서 객체를 다룰 때 “내가 지금 새 객체를 만들었나?” “기존 객체에 대한 주소만 복사했나?”를 구분할 수 있게 되고, 예상치 못한 변경(예: “왜 학생1을 바꿨는데 학생2도 바뀌지?” 같은 오류)을 줄일 수 있습니다!

profile
뜨겁고 매콤하고 화끈하게

0개의 댓글