[김영한의 실전 자바 기본편] - 기본형과 참조형

jkky98·2024년 4월 25일
0

Java

목록 보기
13/51

기본형과 참조형

우리가 현재까지 공부한 선언문에 쓰이는 int, String, double...과 같은 형태에서 소문자로 시작하는 int, double같은 경우는 기본형 클래스이다. 반면 String이나 우리가 커스텀으로 만드는 클래스같은 경우는 맨 앞에 대문자가 붙는 참조형 클래스이다. 특수하게 String은 참조형이지만 기본형처럼 쓰인다.

이 둘의 차이점은 한 가지이다.

Account ac1 = new Account();
int intValue1 = 10;

ac1(참조형 클래스로 생성된 객체의 주소를 가짐)는 실제로 "f43fdk2yoe"와 같은 메모리 주소(참조형의 특징)가 담긴다. 반면 intValue1에는 말 그대로 10이 담긴다. 특수한 경우에 해당하는 String은 실제로는 참조형이지만 기본형처럼 사용가능하다. 이전에 설명했던 "주소"개념을 가지는 것이 참조형이며, 그렇지 않고 본체를 그대로 가지는 것이 기본형인 것이다.

기본형들을 담은 배열은 참조형이다.(배열 자체가 참조형 클래스에 해당하기 때문이다.) 정리하면 극 소수의 기본형 빼고는 모두 참조형으로 이해하면 좋다.

활용 :: 메서드 리펙토링

참조형의 원리는 강의서 여러번 강조되었다. "주소 보관"이라는 개념을 잘 알지 못한다면 아래의 코드 전개가 헷갈릴 수 있다.

package class1.ex;

public class ProductOrderMain {
    public static void main(String[] args) {
        ProductOrder po1 = new ProductOrder();
        ProductOrder po2 = new ProductOrder();
        ProductOrder po3 = new ProductOrder();

        po1.productName = "아잔틱";
        po1.price = 500000;
        po1.quantity = 2;

        po2.productName = "릴리화이트";
        po2.price = 27000;
        po2.quantity = 1;

        po3.productName = "카푸치노";
        po3.price = 50000;
        po3.quantity = 2;

        ProductOrder[] pos = {po1, po2, po3};

        int sum = 0;

        for (int i=0; i < pos.length; i++) {
            System.out.println("상품명: " + pos[i].productName + ", 가격: " + pos[i].price + ", 수량: "+pos[i].quantity);
            sum += (pos[i].price * pos[i].quantity);
        }
        System.out.println("총 결제 금액: " + sum);
    }
}
package class1.ref;

public class AccountMethod1 {
    public static void main(String[] args) {
        Account ac1 = new Account();
        initAccount(ac1, "Poul", 800000, 84);

        Account ac2 = new Account();
        initAccount(ac2, "Kael", 490000, 119);

        Account[] acs = {ac1, ac2};

        for (Account ac : acs) {
            printAccount(ac);
        }
    }
    static void initAccount (Account ac, String name, int money, int stock) {
        ac.name = name;
        ac.money = money;
        ac.stock = stock;

    }
    static void printAccount(Account ac) {
        System.out.println("이름 : " + ac.name + " 현금 : " + ac.money + " 주식 수 : "+ac.stock);
    }
}

첫번째 스크립트에서 우리는 계속 객체 내의 멤버변수에 값을 일일이 부여했고 System.out.println()을 통해 콘솔에 표기하는 코드를 적었다. 값을 부여하는 부분과 println()을 수행하는 부분을 메서드로 따로 빼어낼 수 있을 것이다. 필요한 변수를 메서드 파라미터로 받아 기능을 수행할 수 있다. 바로 아래의 스크립트에서는 이러한 설계로 코드를 짜보았다.

아래는 name, money, stock을 멤버변수로 가지는 Account클래스이다. initAccount()는 초기값을 설정하는 커스텀 메서드로 객체와 인자들을 받아 객체 내의 멤버변수를 구성하는 메서드이며 printAccount()는 sout을 바로 처리해주는 기능이다.

초기화

멤버변수와 지역변수의 차이를 알아보자. 지역변수는 메서드등 코드블록 안에서만 쓰이는 변수이며 멤버변수는 클래스 내에 생성된 변수이다. 멤버변수는 자동초기화, 지역변수는 수동초기화의 특성을 가진다.

자동초기화의 멤버변수는 int의 경우 0으로, 참조형의 경우엔 null, boolean의 경우 false로 자동 초기화 된다.

package class1.ref;

public class InitCatch {
    int v1; // 멤버변수 -> 0으로 초기화 됨.
    int v2 = 20; // 멤버변수 -> 20으로 초기화 됨.

    String str1; // 멤버변수 -> null로 초기화
    String str2 = "HI!"; //멤버변수 -> HI!로 초기화

    Account ac1; // 멤버변수 -> null로 초기화
}

------------------------------------------------------------------
package class1.ref;

public class InitCatchMain {
    public static void main(String[] args) {
        InitCatch check = new InitCatch();
        System.out.println(check.v1);
        System.out.println(check.v2);
        System.out.println(check.str1);
        System.out.println(check.str2);
        System.out.println(check.ac1);
    }
}

다음의 클래스 안에는 5개의 멤버변수가 존재한다. 의도대로 v1, v2는 int에 대해 초기화 변수를 직접 지정해준 경우와 아닌 경우(자동 초기화 유도), str또한 초기화 변수를 지정, ac1은 위에서 만들어 둔 Account클래스에 의한 객체를 생성하지않고 선언만 했다.

아래의 실행 메서드인 main에서 이 클래스의 멤버변수들을 모두 sout해보았을 때

다음과 같은 결과가 나타난다. 초기화를 지정해주었을 경우 말고 int의 경우는 0, String의 경우는 null, Account의 경우도 null이다. 실습에선 나타내지 않았지만 boolean 변수또한 선언하고 초기화 변수를 지정해주지 않았다면 false로 자동 초기화된다.

GC (Garbage Collection)

변수에 객체를 할당할 경우, 변수는 참조값을 받아 참조값을 통해 객체의 정보가 담긴 메모리와 연결된다. 객체는 사용될 때 메모리에서 자신의 존재가 의미가 있다. 만약 아무도 사용하지 않은 상태에서 자리만 차지하고있다면 이는 낭비에 해당한다. 그러므로 자신이 전혀 활용되지 않는 시점에는 JVM이 메모리에서 존재를 지워버린다.

  1. 여러 변수들이 존재하고 해당 변수들이 객체의 주소를 할당받은 상태이다.
  2. 이 변수들이 역할을 끝내고 사라진다. -> 객체입장에서 자신의 주소를 가진 변수들이 사라진다.(존재의 의미가 사라진다.)
  3. JVM GC가 작동하여 객체를 메모리에서 지운다.

C와 같은 더 과거의 프로그래밍 언어의 경우 이러한 메모리제거를 직접 구현했어야 했고, 만약 이것을 놓쳐 메모리에 계속해서 객체들이 쌓이게 되면 메모리 부족현상으로 버그가 발생했다. JVM는 이것을 자동적으로 처리해준다.

NullPointerException

NullPointerException은 개발중 가장 많이 만나는 에러중 하나이다.
위의 멤버변수 초기화 개념에서 참조형의 경우 default는 null이다. 변수에 참조값이 아닌 null이 들어왔을 경우에 만약 메서드를 사용하던, 멤버변수를 호출하는 등의 행동을 하면 NullPointerException이 터진다.

Student student1 = null;
student1.study();

이 상황에서 일어나는 것이 NullPointerException이다. 참조형 객체의 자동 초기화가 null이기 때문에 이러한 상황이 빈번하게 일어난다.

profile
자바집사의 거북이 수련법

0개의 댓글