개발 학습 실습실

이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.

기초개념 › Java

참조

변수에 객체를 담는다는 것은 무슨 뜻인가

Card a = new Card();에서 변수 a에 들어 있는 것은 Card 객체 자체가 아니라, 그 객체를 가리키는 값입니다. 이 값을 참조라고 부릅니다. 그래서 Card b = a;처럼 변수를 다른 변수에 담으면 객체는 복사되지 않고 가리키는 값만 복사되어, a와 b가 같은 객체를 가리키게 됩니다. 한쪽으로 객체의 내용을 바꾸면 다른 쪽으로 봐도 바뀌어 있습니다. int 같은 기본 타입 변수는 다릅니다. 값 자체가 들어 있어서 int b = a;는 값을 복사합니다. 이 실습은 b에 담는 것을 a에서 복사한 참조, new로 새로 만든 객체, 새로 만든 뒤 owner만 옮겨 담은 객체, int 값으로 바꿔 가며 같은 줄을 실행해, b를 바꾼 뒤 a가 어떻게 되는지와 만들어진 Card 객체가 몇 개인지를 봅니다.

실행으로 대조 · JDK 21에서 네 가지 조합을 javax.tools로 컴파일하고 실행해 대조

이 실습에 나오는 개념

참조
참조는 객체를 가리키는 값이다. new로 객체를 만들면 그 객체를 가리키는 참조가 생기고, 클래스나 인터페이스 타입의 변수에는 객체 자체가 아니라 이 참조(또는 아무 객체도 가리키지 않는다는 null)가 담긴다. 같은 객체를 가리키는 참조는 여러 개일 수 있어서, Card b = a;처럼 대입하면 객체가 복사되지 않고 참조만 복사되어 a와 b가 같은 객체를 가리킨다. 이때 한 변수로 객체의 상태를 바꾸면 다른 변수로 읽어도 바뀌어 있다. 두 참조를 ==로 비교하면 객체의 내용이 아니라, 둘 다 null이거나 같은 객체를 가리키는지를 따진다.
클래스와 객체
클래스는 어떤 필드(데이터)와 메서드(동작)를 가질지 적어 둔 설계도이고, 객체는 그 설계도로 new를 해서 메모리(힙)에 실제로 만든 것이다. 객체를 인스턴스라고도 부른다. 클래스 하나로 객체를 여러 개 만들 수 있고, 객체마다 필드 값을 따로 가진다. 변수에는 객체 자체가 아니라 객체가 있는 곳을 가리키는 참조가 담긴다.
기본형과 참조형
자바의 타입은 기본형과 참조형 두 가지로 나뉜다. 기본형은 byte, short, int, long, float, double, char, boolean의 여덟 가지이며, 변수가 값 자체를 담는다. 참조형은 클래스, 인터페이스, 배열처럼 그 밖의 모든 타입이며, 변수가 객체 자체가 아니라 객체를 가리키는 참조를 담는다. 그래서 기본형 변수를 대입하면 값이 복사되어 두 변수가 서로 영향을 주지 않지만, 참조형 변수를 대입하면 참조가 복사되어 두 변수가 같은 객체를 가리키게 된다. 참조형 변수는 아무 객체도 가리키지 않는다는 뜻의 null을 담을 수 있고, 기본형 변수는 null을 담을 수 없다.
동일성과 동등성
동일성은 두 참조가 메모리상 같은 객체를 가리키는지를 뜻하며 == 연산자로 비교한다. 동등성은 두 객체가 논리적으로 같은 값을 가지는지를 뜻하며 equals() 메서드로 비교한다. 문자열이나 값 객체를 비교할 때 ==를 쓰면 내용이 같아도 false가 나올 수 있다.

이렇게 바꿔 보세요

처음 설정으로 실행하면

  1. Card a = new Card(); 줄이 실행됩니다. new Card()가 Card 객체를 하나 만들고(그림의 객체 A), 변수 a에는 그 객체를 가리키는 값이 담깁니다. 이 값을 참조라고 부릅니다. a 안에 객체가 들어 있는 것이 아니라, a가 객체 A를 가리키고 있습니다.
  2. a.owner = "김철수"; 줄이 실행됩니다. a가 가리키는 객체 A를 찾아가, 그 객체의 owner에 김철수를 담습니다.
  3. Card b = a; 줄이 실행됩니다. a 안에 들어 있는 것은 "객체 A를 가리키는 값"이고, 이 값이 b에 복사됩니다. 객체 A가 복사되는 것이 아닙니다. 새 객체는 만들어지지 않았고 객체는 여전히 1개입니다. a와 b가 같은 객체 A를 가리킵니다.
  4. b.owner = "이영희"; 줄이 실행됩니다. b가 가리키는 객체를 찾아가 owner에 이영희를 담습니다. b가 가리키는 객체는 객체 A이고, a가 가리키는 객체도 객체 A입니다. 그래서 객체 A의 owner가 김철수에서 이영희로 바뀝니다.
  5. System.out.println(a.owner); 줄이 실행됩니다. a가 가리키는 객체 A의 owner를 읽어 이영희를 출력합니다. a로는 아무것도 바꾸지 않았는데 바뀌어 있는 것은, b로 바꾼 객체가 a가 가리키는 바로 그 객체이기 때문입니다.
  6. System.out.println(a == b); 줄이 실행됩니다. 두 변수에 ==를 쓰면 들어 있는 값이 같은 객체를 가리키는지를 비교합니다. a와 b는 모두 객체 A를 가리키므로 true를 출력합니다. owner의 글을 비교하는 것이 아닙니다.
  7. main이 끝납니다. 이 실행에서 new Card()가 1번 실행되어 Card 객체가 1개 만들어졌습니다. 변수에 값을 담는 줄이 객체를 늘린 것은 아니고, 이 코드에서 객체는 new Card()로만 만들어졌습니다.

실행 결과

코드

class Card {
    String owner;
}

public class Main {
    public static void main(String[] args) {
        Card a = new Card();
        a.owner = "김철수";
        // b에는 a가 가리키는 그 객체가 담긴다. 객체는 새로 생기지 않는다
        Card b = a;
        b.owner = "이영희";
        System.out.println(a.owner);
        System.out.println(a == b);
    }
}

이 모형의 전제

실행 환경은 JDK 21.0.12.1(javac 21)입니다. 네 가지 조합을 모두, 화면의 코드와 같은 소스를 javax.tools로 컴파일해 새 클래스 로더로 읽어 Main.main을 실행하고, 표준 출력을 잡은 결과와 대조했습니다. 대조할 때 Probe의 소스에는 Card 객체를 세는 줄(static 변수와 생성자의 증가, 그리고 몇 줄의 출력)만 더했고, 그 줄을 빼면 화면의 코드와 줄 단위로 같은 글임을 조합마다 확인했습니다. 대조한 것은 두 출력 줄, 만들어진 Card 객체의 수, 그리고 단계 문장이 기대는 사실입니다. 사실은 Card b = ...; 줄을 실행한 직후(new Card()를 담은 조합은 owner를 옮겨 담은 직후)의 객체 수, a == b의 값, 두 변수가 가리키는 객체의 owner 값과 그 두 값이 같은지입니다. 문서는 Java Language Specification(Java SE 21)을 직접 열어 확인했습니다. 객체를 가리키는 참조 값이 있다는 것, 두 변수가 같은 객체를 가리키면 한 변수로 바꾼 상태를 다른 변수로 읽을 수 있다는 것, int 값은 대입하면 복사되어 둘이 따로라는 것은 4.2절과 4.3.1절(예제 4.3.1-2)을, 기본 타입 변수는 그 기본 타입의 값을 담고 클래스 타입 변수는 null이나 그 클래스의 객체에 대한 참조를 담는다는 것은 4.12.1절과 4.12.2절을, 참조에 쓴 ==가 둘 다 null이거나 같은 객체를 가리킬 때 true라는 것은 15.21.3절을 따랐습니다. int 변수의 ==가 값을 비교한다는 것(15.21.1절)은 문서를 열어 확인하지 않았고, 이 실행의 결과로만 확인했습니다. 화면은 owner의 글 김철수와 이영희를 객체 안에 들어 있는 값처럼 그렸지만, owner도 String 객체를 가리키는 참조입니다. 이 실습은 그 차이를 다루지 않고, 문자열 상수가 같은 객체를 가리키게 되는 일도 다루지 않았습니다. 객체의 내용이 같은지를 묻는 equals는 "세부 동작 실습: ==와 equals"에서 다룹니다. 변수와 객체가 메모리의 어디에 놓이는지는 이 실습이 다루거나 확인한 것이 아닙니다. 이 실습에서 본 것은 변수에 담긴 값이 객체를 가리키고, 대입하면 그 값이 복사된다는 동작입니다.

앞 실습: 클래스와 객체 · 다음 실습: null

이 실습에 나오는 개념

이 실습에서 보는 것

설정

코드


      
    

그림

실행 비교

이렇게 바꿔 보세요