[Java 재정립 02] 객체의 본질 : 메모리 구조와 설계의 관점
글 정보
| 카테고리 | java/study/basic |
|---|---|
| 작성일 | 2025-11-21 |
| 게시 여부 | true |
| seriesName | Java 재정립 |
| seriesOrder | 2 |
| 제목 | [Java 재정립 02] 객체의 본질 : 메모리 구조와 설계의 관점 |
객체?
- OOP에서 SW를 구성하는 단위
- 결국 프로그램이란 이 객체들의 집합체 + 관계
- 객체들의 관계란 결국 설계(=디자인 패턴)를 의미함
- 객체는 고급어 수준에서만 존재하는 개념이다
- 하드웨어는 해당 개념을 알지 못하며, 함수 + 스택 정도만 알고 있음
- 객체는 상태와 행동을 가진다.
- 단순한 데이터 집합이 아닌, 상태(데이터)와 그 데이터를 조작하는 행동(메서드)이 묶인 캡슐화된 단위이다.
항, 식, 구문
항
- 가장 기본이 되는 구성 요소
- 보통 연산의 대상이 되는 피연산자나 독립적인 변수, 상수 등을 의미
x, y, 10등
식
- 값을 만들어내는 코드의 단위
- 항들이 모여서 하나의 결과값(Value)을 반환하면 그것을 '식'이라함
x + y, function()등
구문
- 프로그램이 실행해야 할 완전한 하나의 명령
int x = 10;System.out.println("Hello World!")- 고급어의 실행 단위임
// 예제
int x = 10;
int y = 5;
// 1. 항 (Term): x, y, 10, 5 등 (가장 작은 단위)
// 2. 식 (Expression): x + y (계산되어 15라는 '값'을 남김)
// 3. 구문 (Statement): int sum = x + y; (세미콜론으로 종결되는 실행 단위)
클래스, 객체, 인스턴스
- 클래스 : 객체를 구현하기 위한 문법, 형식
- 객체 : 단위이고, 추상적임
- 인스턴스 : 메모리(Heap)에 실체화된 것
- 변수(참조자)를 사용하는 이유는 메모리 사용을 위한 인스턴스가 존재하는 메모리 주소를 가리키는 리모컨 같은 역할
- 변수는 Stack에 존재함
클래스 문법
- 구성요소(변수, 함수)를 멤버라 지칭
- 멤버
- 필드 = 변수
- 메서드 = 함수
- 선언과 정의가 공존한다.
- C언어와 같은 대부분 언어에선 구분하나, 자바는 선언과 정의가 동시에 되어야함
- 클래스 위에는 항상 패키지가 존재한다.
사용자와 작성자를 구분하라
- 클래스를 만들고, 객체를 다룰때 사용자와 작성자를 구분
- 우리는 대부분 작성자이자 사용자이기에 사용자를 고려하지 않음
- 하지만 실무에선 아님
- 사용자가 다를 가능성이 클 뿐더러, 내가 작성한 코드도 조금 있으면 잊혀짐
- 기억력을 믿지 말고, 사용자에게 친절하게 작성하라
"내가 작성한 코드도 한 달뒤엔 남이 짠 코드와 같다."
필드 선언시 초깃값 정의
- JVM Memory 기준
- Stack
- 컴파일타임에 이미 결정된다.
- 지역변수, 자동변수 등
- Heap
- 주로 클래스를 인스턴스화 하며, new 연산자를 사용
- 런타임에 유동적으로 사용됨
생성자
- 명시적 호출이 없으며 new 연산자를 사용함
- 객체의 초기화를 위해 문법적으로 들어가 있는 함수임
- 주의사항 : 필드 선언시 멤버에 대입한 초깃값과 생성자 초기화 값이 충돌할 경우, 생성자에 기술한 코드가 우선임
public class InitTest {
// 1. 필드 명시적 초기화
int value = 10;
public InitTest() {
// 2. 생성자에서 초기화 (이것이 덮어씌움)
this.value = 20;
}
public static void main(String[] args) {
InitTest t = new InitTest();
System.out.println(t.value); // 결과: 20
}
}
- 객체가 생성되는 시점(new)에 자동으로 호출된다.
- 또한 어떠한 생성자도 클래스에 없는경우, 컴파일러가 자동으로 빈 생성자를 추가해준다.
- 생성자를 작성할 때, 항상 클래스 자체와 관련있는 코드만 기술할것을 권장함
- 하나의 인스턴스에 여러 참조자가 존재할 수 있다.
- 이 경우 사이드 이펙트를 고려하라.
- 생성자를 private로 지정하는 경우, 생성자 사용을 제한하는 것이다.
참조자
TestClass testClass = new TestClass();라는 구문이 있을때,testClass를 참조자 라고 한다.- 클래스 형식에 대한 변수 선언은 모두가 참조자이다.
- 참조자는 인스턴스를 가리키는 레퍼런스이다. 인스턴스 자체가 아니다.
지시자
- 네 가지 지시자가 존재한다.
- public
- protected
- 외부접근 차단, 같은 패키지(또는 파생 클래스)만 허용
- default
- 같은 패키지만 허용
- private
- 접근을 제어하는 이유
- 사용자를 고려해서 개발해야하기 때문
- 기본적으로 private로 정하고, 쓰임에 따라 차차 넓은 범위로 지시자를 확장하는 방식을 사용하기
왜 private를 쓰는걸까?
- public으로만 코딩하면 편하긴 하나, 제작자가 의도하지 않은 방향으로 사용자가 클래스를 사용할 수 있게됨
- 객체 내부 구현을 숨기고, 허용된 메서드만 사용하게 만듬
- 기본적으로 SW 개발은 생산성, 유지보수성을 따짐
- 생산성 -> 얼마나 빠르게 개발이 가능한가?
- 유지보수성 -> 얼마나 용이하게 수정이 가능한가?
- 위 두가지를 충족하기 위해 우린 프레임워크를 사용한다.
- private를 사용하고 getter, setter를 쓰는 이유
- OOP에서 객체 제작 관점, 사용 관점 두 가지를 분리하여 생각하기에 좋다.
this
C++의 포인터와 거의 같다고 봐도 무방하다.
-
- 하나의 클래스가 있다고 가정하자
- 우린 해당 클래스를 사용하여 인스턴스를 런타임(동적)에 할당한다.
- = 객체 생성
- 그렇게 생성된 인스턴스는 메모리에 적재된다.
- 인스턴스가 존재하는 Heap 메모리 구역 하나하나엔 이름이 없음
- 우린 그 구역을 지칭하기 위한 참조자를 사용함
- 왜냐하면 인스턴스는 컴파일 타임이 아닌 런타임이 생성되기 때문임
- 현재 정해져있지 않고, 미래에 생성된다.
- 우린 미래에 인스턴스가 어느 메모리 공간에 할당될지 알 수 없다.
- 그래서 그 어디에 대한 정보를 기술한게
this이다. - 인스턴스가 여러개라면
this도 각각 존재한다. - this는 method에서 유효하다
- 각 스택마다
this값이 정의되어 들어간다. - JVM이 정의해준다! 개발자는 신경쓰지 않아도 되는 포인트
- JVM은 메서드를 컴파일할 때, 첫 번째 파라미터로 몰래 객체 주소를 넘겨준다.
- 이것이 바로
this이다. - this는
static메소드에선 사용할 수 없게 되어있다. - Heap이 아닌 Static 메모리 영역을 사용하기 때문
- 이는 객체 생성없이 호출되는 이유로,
this의 정보를 넘겨받을 수 없기 때문
this의 이해를 돕기 위한 JVM 간략 설명
- OS 유저 모드에서는 JVM도 결국 하나의 프로세스임
- 그렇기에 프로세스는 메모리를 점유하며, JVM 내부에도 메모리 공간들이 있음
- Stack
- Heap
- Method
추가 조사 예정
- Big endian System
- 심볼릭 상수
- 자바의 Call by Reference vs Call by Value