클래스와 오브젝트
클래스의 개념
객체(object )는 사용할 수 있는 실체를 의미하며, 클래스는 객체를 만들기 위한 설계도와 같다.
따라서 1개의 정의된 클래스를 이용해 여러 개의 객체를 만들 수 있다.
클래스의 구조
package ...; // ① 패키지
import ...; // ② 임포트
class 클래스명 {...} // ③ 외부 클래스
public class A {
int a = 3; // ① 필드
double abc() {...} // ② 메서드
A() {...} // ③ 생성자
class 클래스명 {...} // ④ 이너 클래스
}
클래스와 오브젝트 구분하기

김동형, ⌜Do it! 자바 완전 정복⌟, 이지스퍼블리싱, 2021, 214쪽
- 클래스를 붕어빵 기계에 비유한다면 객체는 붕어빵 기계로 찍어 낸 붕어빵에 비유할 수 있다.
- 우리는 클래스를 직접 사용할 수 없다. 클래스의 모든 특징(필드)과 기능(메서드)들을 사용하려면 클래스를 이용해 객체를 만든 후 그 객체 속의 필드와 메서드 등 을 사용해야 하는 것이다.
- 클래스의 생성자로 객체를 만드는 과정을
인스턴스화 instantiation라고 하고, 인스턴스화로 만들어진 객체를인스턴스 instance라고 한다.
붕어빵 비유를 코드로 이해하기
// 클래스 (설계도)
public class 붕어빵 {
String 속재료;
int 온도;
}
// 객체 (실제 붕어빵들)
붕어빵 팥붕어빵 = new 붕어빵();
붕어빵 슈크림붕어빵 = new 붕어빵();
붕어빵 피자붕어빵 = new 붕어빵();
클래스는 바로 사용할 수 없고 반드시 객체를 생성해 객체 안에 있는 필드, 메서드 및 이너 클래스를 사용해야 한다.
객체의 생성과 활용
A a = new A();
객체는 new 키워드로 생성할 수 있다.
자바에서는 대부분의 클래스가 이 방법으로 객체를 생성한다.
클래스에 따라 메서드를 사용해 객체를 생성할 때도 있지만, 이때도 해당 메서드 내부에서 new를 사용해 객체를 생성한다.
즉, 모든 클래스가 객체를 new 키워드로 생성한다고 볼 수 있다.
객체 복사: 얕은 복사 vs 깊은 복사
Java에서 객체를 복사할 때, 복사의 깊이에 따라 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)로 나뉩니다.
핵심 차이는 ==객체 안에 있는 또 다른 객체(참조 타입)를 어떻게 처리하느냐==에 있습니다.
얕은 복사 (Shallow Copy)
얕은 복사는 객체를 복사할 때, ==객체의 멤버 변수들 중 참조 변수(주소값)만 복사==하는 방식입니다.
동작 방식
- 새로운 객체를 만들지만, 내부 필드들이 참조하는 실제 객체는 원본과 동일한 것을 가리킵니다
- 기본형(Primitive) 타입: 값 자체가 복사되므로 독립적입니다
- 참조형(Reference) 타입: 주소값만 복사되므로, 원본과 복사본이 같은 내부 객체를 공유합니다
문제점
- 복사본에서 내부 객체의 값을 수정하면, 원본 객체의 값도 함께 변경되는 부작용이 발생합니다
예시 코드
Java의 Object.clone() 메서드는 기본적으로 얕은 복사를 수행합니다.
class Person implements Cloneable {
String name;
int[] scores; // 참조 타입 (배열)
public Person(String name, int[] scores) {
this.name = name;
this.scores = scores;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 기본 clone은 얕은 복사 수행
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
int[] data = {100, 90};
Person original = new Person("홍길동", data);
// 얕은 복사 수행
Person copy = (Person) original.clone();
// 복사본의 배열 값을 변경
copy.scores[0] = 0;
// 결과 확인
System.out.println("원본: " + original.scores[0]); // 0 (원본도 바뀜!)
System.out.println("복사본: " + copy.scores[0]); // 0
}
}
메모리 구조
graph TB
subgraph Stack["스택 메모리"]
Ref1["original"]
Ref2["copy"]
end
subgraph Heap["힙 메모리"]
Obj1["Person 객체 1 name: 홍길동, scores: (주소100)"]
Obj2["Person 객체 2 name: 홍길동, scores: (주소100)"]
Array["int 배열 [0, 90] (주소: 100)"]
end
Ref1 --> Obj1
Ref2 --> Obj2
Obj1 --> Array
Obj2 --> Array
style Stack fill:#ffe0b2,stroke:#f57c00,stroke-width:2px
style Heap fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
style Array fill:#ffccbc,stroke:#d84315,stroke-width:3px
깊은 복사 (Deep Copy)
깊은 복사는 객체를 복사할 때, ==객체 내부의 모든 참조 객체까지 새로 생성하여 값을 복사==하는 방식입니다.
동작 방식
- 원본 객체와 복사본 객체가 메모리상에서 완벽하게 분리됩니다
- 참조형 타입: 단순히 주소만 복사하는 것이 아니라, 참조하고 있는 객체 자체를 새로 만들어서 할당합니다
장점
- 복사본을 아무리 수정해도 원본 객체에는 전혀 영향을 주지 않습니다
구현 방법
Java에서는 깊은 복사를 기본적으로 제공하지 않으므로 직접 구현해야 합니다.
방법 A: clone() 메서드 재정의
@Override
protected Object clone() throws CloneNotSupportedException {
Person copy = (Person) super.clone(); // 1. 껍데기 복사 (얕은 복사)
copy.scores = this.scores.clone(); // 2. 내부 참조 객체도 따로 복사
return copy;
}
방법 B: 복사 생성자 (권장)
public Person(Person original) {
this.name = original.name;
// 배열을 새로 생성하여 값만 복사
this.scores = new int[original.scores.length];
System.arraycopy(original.scores, 0, this.scores, 0, original.scores.length);
}
사용 예시:
public class Main {
public static void main(String[] args) {
int[] data = {100, 90};
Person original = new Person("홍길동", data);
// 깊은 복사 수행 (복사 생성자 사용)
Person copy = new Person(original);
// 복사본의 배열 값을 변경
copy.scores[0] = 0;
// 결과 확인
System.out.println("원본: " + original.scores[0]); // 100 (원본 유지!)
System.out.println("복사본: " + copy.scores[0]); // 0
}
}
메모리 구조
graph TB
subgraph Stack["스택 메모리"]
Ref1["original"]
Ref2["copy"]
end
subgraph Heap["힙 메모리"]
Obj1["Person 객체 1 name: 홍길동 scores: (주소 100)"]
Obj2["Person 객체 2 name: 홍길동 scores: (주소 200)"]
Array1["int 배열 1[100, 90](주소: 100)"]
Array2["int 배열 2[0, 90](주소: 200)"]
end
Ref1 --> Obj1
Ref2 --> Obj2
Obj1 --> Array1
Obj2 --> Array2
style Stack fill:#ffe0b2,stroke:#f57c00,stroke-width:2px
style Heap fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
style Array1 fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
style Array2 fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
비교 정리
| 특징 | 얕은 복사 (Shallow Copy) | 깊은 복사 (Deep Copy) |
|---|---|---|
| 복사 대상 | 객체의 주소값 (Reference) | 실제 데이터 (Value) |
| 메모리 | 원본과 내부 객체 공유 | 원본과 내부 객체 분리 (새로운 메모리 할당) |
| 속도 | 빠름 | 상대적으로 느림 (객체 생성 비용 발생) |
| 원본 영향 | 복사본 수정 시 원본도 변경될 수 있음 | 복사본 수정 시 원본에 영향 없음 |
| 구현 방법 | Object.clone() (기본) |
복사 생성자, 직접 구현, 직렬화 등 |
댓글남기기