본문 바로가기

프로그래머/Java Managed Programming

[개체지향 프로그래밍] 개체 생성 | 가비지 콜렉터(garbage collector) | 생성자(constructor)

개체 생성 시 멤버 데이터의 초기화

C

human_t* adam = (human_t*)malloc(sizeof(human_t));
// adam->name: 쓰레기 값
// adam->age: 쓰레기 값
// adam->sec: 쓰레기 값

printf("%d\n", adam->age);
  • C 구조체의 변수는 선언 시 초기화가 안됨
  • 메모리에 남아있던 쓰레기 값이 그대로 유지됨

Java

Human adam = new Human();
// adam.name: null
// adam.age: 0
// adam.sex: null

System.out.printf("%d", adam.age);
  • Java는 0에 준하는 값으로 초기화해 줌
    • int는 0
    • float은 0.0
    • 참조형은 null로
public class Human{
    public String name;
    public int age = 20;
    public Sex sex;
    //...
}
  • 0이 아닌 다른 값으로 초기화하고 싶다면 선언문에 대입 가능

Java에는 free()가 없다

  • Java는 가비지 컬렉션(GC) 기능을 지원
    • JVM에 내장된 가비지 컬렉터가 알아서 가비지(쓰레기)를 지워줌
    • 가비지란? 더 이상 사용되지 않는 개체(힙 메모리)
    • 프로그래머가 직접 메모리를 해제하지 않는다
    • C#도 가비지 컬렉션을 지원
  • 매니지드(managed) 언어는 보통 자동으로 메모리를 관리해줌

GC의 단점

  1. GC가 메모리를 수집하는 시점을 알 수 없음
  2. 모든 개체의 사용 여부를 판단하는 게 그리 빠른 연산이 아님
  • 이러한 이유로 자원이 한정적인 시스템에는 적합하지 않음
    • 메모리 해제를 프로그래머가 직접 할 경우 이런 문제가 없음
  • 자동 메모리 관리 하에서 발생하는 메모리 누수도 존재
    • 즉, 자동 메모리 관리도 메모리 누수에서 100% 자유로울 수 없음

생성시 올바른 값으로 초기화하기

public class Human{
    public String name;
    public int age;
    public Sex sex;

    public Human(String name, int age, Sex sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    // ...
}

Human adam = new Human("adam", 20, Sex.MALE);
Human fiona = new Human("fiona", 20, Sex.FEMALE);

System.out.printf("Name: %s, Age: %d%s", adam.name, adam.age, NEW_LINE);
System.out.printf("Name: %s, Age: %d%s", fiona.name, fiona.age, NEW_LINE);
  • 생성자라는 특별한 메서드가 이런 일을 담당
    • 개체 생성 시에 자동으로 호출되는 특수한 함수
    • 반환형: 없음(void가 아님!)
    • 함수명: 클래스명과 동일

오버로딩도 가능

public Human(String name, Sex sex){
    this.name = name;
    this.sex = sex;
    // age의 초기값은 그대로 0
}

public Human(String name, int age, Sex sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
}

코드 중복을 피하는 법

public Human(String name, Sex sex){
    this(name, sex == Sex.MALE ? 1 : 5, sex);
}
  • 매개변수 수가 적은 생성자에서 매개변수 수가 많은 생성자를 호출
  • this()를 이용하며 다른 생성자를 호출할 수 ㅗ있음

기본 생성자(default constructor)

public <클래스명>() {...}
  • 프로그래머가 생성자를 하나도 안 만들 경우 자동으로 생기는 생성자
  • 컴파일러가 알아서 매개변수 없는 생성자를 만들어 줌
  • 기본 생성자의 함수 바디(body)는 비어 있음
    • 각 멤버 함수는 이미 0에 준하는 값으로 초기화됐기에 괜찮음
  • 프로그래머가 생성자를 제공하면 기본 생성자는 생기지 않음(중요!!!)

생성자로 초기화를 해야 하는 이유

  1. 개념 상의 문제
  2. 후조건의 문제
    • 생성자도 함수. 따라서 선조건과 후조건이 적용
    • 생성자의 후조건: '개체의 상태는 개체 생성과 동시에 유효하다'
  3. 사용자를 고려 안 한 문제
  • 사용자란?
    • 내가 만든 클래스를 사용하는 코드 혹은 프로그래머
    • 나 자신이 될 수도 남이 될 수도 있음
  • 문제: 사용자의 실수를 유발
    • 클래스에 있는 어떤 멤버 변수를 초기화해야 하는가?
    • 어떤 값으로 초기화해야 하는가?
    • 나중에 멤버 변수가 추가될 때 기존의 초기화 코드들을 업데이트 안 하면?

어떤 멤버 변수를 초기화해야 하는가?

  1. 클래스가 저장되어 있는 파일을 열어서 봐야 함
  2. 어떤 멤버 변수를 초기화해줘야 하는지 모를 수도 있음
  3. 나중에 멤버 변수가 추가되는 것에 대응하기 어려움

어떤 값으로 초기화해줘야 하는가?

  1. 그 분야를 잘 아는 사람만 알 수 있는 내용이 있음
  2. 초기화에 어떤 계산이 필요한 경우 코드가 중복됨

외부 라이브러리를 사용하면 더 심각한 문제

  • .class 파일만 제공됨
  • 클래스 선언이 있는 소스파일을 열어볼 수 없음
  • 뭐든 간에 여태까지 본 문제는 모두 생성자로 해결 가능

결론: 생성자는 개체를 만들어주는 계약이다!

  • 함수 시그내처와 마찬가지
  • 즉, 타 과목에서 올바른 함수 작성법이라 배웠던 내용이 모두 적용
    1. 함수는 블랙박스
    2. 호출자와 함수의 분명한 책임 분리

즉, 외부에서 클래스 내부의 데이터를 알 필요가 없음
이게 바로 데이터 추상화(캡슐화의 일부이기도 함)
이 개념은 생성자 뿐만 아니라 모든 메서드에도 적용