Java 첫걸음: 클래스·접근제어자·OOP 입문
목차 23
Java 첫걸음은 객체지향의 입구다. 클래스(설계도)와 객체(실체), 접근 제어자 4종 매트릭스, 생성자·static, 그리고 Java 타입 시스템(참조 vs 기본·Integer 캐시·String 불변·
==/equals)을 한 바퀴 돈다. 매 회차 1~2문항 꾸준히 나오는 빈출 단원으로, 접근 제어 범위 · Integer 캐시 · String 비교가 단골 함정이다. 39·40강의 C(절차형·수동 메모리·포인터)와 달리 Java는 객체지향·자동 GC·참조로 메모리를 다룬다. 상속·다형성·추상 클래스/인터페이스는 42강에서 본격적으로 이어진다.핵심 암기: 접근 제어자 4종 ★공보기사★(공개 public > 보호 protected > 기본 default > 사적 private) · 클래스 4대 구성 ★클필메생★(클래스·필드·메서드·생성자) · Integer 캐시 -128~127 · String 불변 + 리터럴 풀 ·
==는 주소 / equals는 값
PART A — Java 진입
Java 정의 — 1995·고슬링·WORA·JVM ·Java 개요·
[정의] Java = 1995년 Sun Microsystems의 제임스 고슬링이 만든 객체지향 프로그래밍 언어. 2010년 Oracle이 인수했다. 슬로건은 WORA(Write Once, Run Anywhere) — 한 번 컴파일하면 어디서든 실행.
[흐름] 컴파일·실행 단계
Java 소스 (.java)
↓ javac (컴파일)
바이트코드 (.class) ← 플랫폼 독립
↓ JVM (해석·실행)
실행 결과 ← Windows / Mac / Linux 어디서든
[표] 절차형(C) vs Java
| 항목 | 절차형 언어(C) | Java |
|---|---|---|
| 패러다임 | 절차형 | 객체지향 |
| 메모리 관리 | 수동(직접 할당·해제) | 자동 GC |
| 포인터 | 직접 포인터 | 참조(자동) |
| 플랫폼 | OS 종속 | JVM 독립(WORA) |
| 실행 | 컴파일러 단일(.exe) | 컴파일 + 인터프리트 하이브리드(.class + JVM) |
💡 JVM = 통역사. 한 번 영어로 번역(.class)해두면 각 나라(OS)의 통역사(JVM)가 현지 기계어로 즉시 바꿔준다. JVM의 역할 셋 — 바이트코드 해석 · 메모리 자동 관리(GC) · 플랫폼 추상화(WORA).
🔑 암기 "Java = 1995 고슬링 / Sun→Oracle / WORA / JVM 하이브리드(컴파일+인터프리트) / 객체지향+자동 GC"
⚠️ 함정 Java는 순수 컴파일 언어가 아니다 — javac 컴파일 후 JVM이 바이트코드를 해석하는 하이브리드(38강 컴파일러 vs 인터프리터 회수).
🎯 빈출 Java 정의·WORA·JVM 역할이 정의 문제로 간헐 출제.
Hello World — main 시그니처 ·Java 개요·
[정의] 모든 Java 프로그램의 시작점은 main 메서드. 시그니처 public static void main(String[] args)는 토씨까지 고정이다.
[코드]
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
[표] main 시그니처 5요소
| 키워드 | 의미 |
|---|---|
| public | 어디서든 접근 가능 |
| static | 객체 없이 호출(JVM이 객체 생성 없이 진입) |
| void | 반환값 없음 |
| main | JVM이 자동으로 찾는 시작점 |
| String[] args | 명령행 인자 배열 |
💡 클래스명 = 파일명 일치 필수(public class HelloWorld → HelloWorld.java). java.lang 패키지(String·Integer·Math·System)는 import 없이 자동 사용.
🔑 암기 "public static void main(String[] args) — 토씨까지. static 빠지면 진입 불가, String[]이 String이면 main 인식 안 됨"
🎯 빈출 main 시그니처 정확성·클래스명=파일명.
PART B — 클래스와 객체
클래스 vs 객체 — 붕어빵 틀과 붕어빵 ·클래스·시험 1순위·
[정의] 클래스(Class) = 설계도(붕어빵 틀). 객체(Object) = 클래스로 찍어낸 실체(붕어빵). new가 Heap에 객체를 할당한다.
[표] 셋의 분별
| 용어 | 한 줄 |
|---|---|
| 클래스 | 붕어빵 틀 — 설계도. 메모리에 1회 정의 |
| 객체 | 붕어빵 — 틀로 찍어낸 실체. 데이터 있음 |
| new | 붕어빵 찍는 동작 — Heap에 객체 할당 |
[코드]
Student s1 = new Student(); // 붕어빵 하나 찍기 — Heap에 객체
Student s2 = new Student(); // 또 하나 — 별개 객체
💡 절차형 언어의 구조체는 데이터만 묶고 행동(함수)은 따로였다. Java 클래스는 데이터(필드)와 행동(메서드)을 한 덩어리로 묶는다 — 이게 객체지향의 시작.
🔑 암기 "클래스 = 붕어빵 틀(설계도) / 객체 = 붕어빵(실체) / new = Heap에 찍어내기"
🎯 빈출 클래스 vs 객체(인스턴스) 분별이 매 회차.
★클필메생★ — 클래스 4대 구성 ·클래스·시험 1순위·
[정의] 클래스를 이루는 4대 구성요소. 두음 ★클필메생★ — 클래스 정의 · 필드 · 메서드 · 생성자.
[표] 4대 구성
| 두음 | 구성요소 | 한 줄 |
|---|---|---|
| 클 | 클래스 정의 | class 키워드 + 클래스명(파일명 일치) |
| 필 | 필드(Field) | 멤버 변수 — 객체의 상태. 보통 private |
| 메 | 메서드(Method) | 멤버 함수 — 객체의 행동. 보통 public |
| 생 | 생성자(Constructor) | 객체 생성 시 자동 호출. 클래스명 동일, 반환타입 없음 |
[코드]
public class Student { // 클 — 클래스 정의
private String name; // 필 — 필드(private)
private int age;
public Student(String name, int age) { // 생 — 생성자(클래스명 동일·반환타입 없음)
this.name = name;
this.age = age;
}
public String getName() { return name; } // 메 — 메서드
}
💡 ★클필메생★ = 자동차 4대 부품. 차종(클) + 부품(필) + 움직임(메) + 공장 조립(생).
🔑 암기 "★클필메생★ — 클(class)·필(필드·상태)·메(메서드·행동)·생(생성자)"
⚠️ 함정 생성자 4대 특징 — ① 이름 = 클래스명 ② 반환타입 없음(void도 아님) ③ 기본 생성자 자동 생성(인자 생성자 만들면 사라짐) ④ new와 함께 자동 호출.
🎯 빈출 생성자 이름=클래스명 + 반환타입 없음이 단골.
★공보기사★ — 접근 제어자 4×4 매트릭스 ·접근 제어자·시험 1순위·
[정의] 접근 제어자 4종을 범위 넓음→좁음으로. 두음 ★공보기사★ — 공개 public > 보호 protected > 기본 default(생략) > 사적 private.
[표] 접근 범위 4×4 (시험 1순위)
| ★공보기사★ | 같은 클래스 | 같은 패키지 | 자식(다른 패키지) | 외부 |
|---|---|---|---|---|
| 공 public | O | O | O | O |
| 보 protected | O | O | O | X |
| 기 default(생략) | O | O | X | X |
| 사 private | O | X | X | X |
[코드]
public class Base {
public String pub = "공"; // 공 — 어디서든
protected String prot = "보"; // 보 — 같은 패키지 + 다른 패키지 자식
String def = "기"; // 기 — 키워드 생략, 같은 패키지만
private String priv = "사"; // 사 — 이 클래스 안에서만
}
💡 백화점 출입 권한 4단계 — 공=1층 매장(누구나) · 보=VIP 라운지(VIP+자녀) · 기=직원 휴게실(같은 매장 직원) · 사=오너 집무실(본인만).
🔑 암기 "★공보기사★ 범위 넓음→좁음 · 매트릭스 4×4 · default=키워드 생략(같은 패키지) / protected=거기에 다른 패키지 자식 추가"
⚠️ 함정 default vs protected가 매년 1순위 — default는 같은 패키지까지(다른 패키지 자식 X), protected는 다른 패키지 자식까지 O. private은 같은 클래스 안에서만('같은 패키지' 붙으면 틀린 설명).
🎯 빈출 접근 제어 매트릭스가 매 회차 1문제 무조건.
캡슐화 — private + getter/setter + 검증 ·캡슐화·
[정의] 캡슐화(Encapsulation) = 필드를 private으로 숨기고, public getter/setter로만 접근시키는 것. ★공보기사★의 사(private)를 본격 활용한다.
[코드]
public class Account {
private long balance; // 필드는 private
public long getBalance() { return balance; } // getter — 읽기
public void setBalance(long amount) { // setter + 검증
if (amount < 0) throw new IllegalArgumentException("음수 불가");
this.balance = amount;
}
}
💡 약 캡슐 — 약 가루(private 필드)는 껍질(getter/setter) 안에 감싸지고, 외부는 캡슐을 통해서만 접근한다. 필드를 public으로 열면 a.balance = -1000처럼 음수도 그냥 들어간다. 위 코드는 음수 입력 시 실제로 예외가 던져져 차단되는 것을 확인했다.
🔑 암기 "캡슐화 = private 필드 + public getter/setter + 검증 1줄"
🎯 빈출 캡슐화 정의·이점(데이터 보호·검증·변경 자유)이 간헐. 캡슐화는 OOP 4대 특성 중 하나(42강 ★캡상다추★ 종합).
PART C — 생성자·this·static
생성자 오버로딩 — 기본 생성자 사라짐 함정 ·생성자·시험 1순위·
[정의] 오버로딩(Overloading) = 같은 이름 + 다른 매개변수로 쌓기. 생성자도 매개변수 개수·타입이 다르면 여러 개 만들 수 있다.
[코드]
public class Student {
private String name;
private int age;
public Student() { this("이름없음", 0); } // 기본 → this()
public Student(String name) { this(name, 0); } // 인자 1개
public Student(String name, int age) { // 인자 2개 — 오버로딩
this.name = name;
this.age = age;
}
}
Student s1 = new Student(); // 이름없음 / 0
Student s2 = new Student("민지"); // 민지 / 0
Student s3 = new Student("지훈", 22); // 지훈 / 22
💡 위 세 생성자는 실제로 각각 이름없음/0, 민지/0, 지훈/22를 만들어낸다 — 같은 이름이지만 매개변수가 달라 별개로 인식된다.
🔑 암기 "오버로딩 = 같은 이름·다른 매개변수 · 인자 생성자만 만들면 기본 생성자 자동 생성이 사라짐"
⚠️ 함정 public B(String s) {}만 정의하면 new B()는 컴파일 오류(기본 생성자가 사라짐). 매년 단골.
🎯 빈출 기본 생성자 사라짐·오버로딩 성립 여부.
this 키워드 — 3대 용도 ·생성자·
[정의] this = 현재 객체 자신을 가리키는 참조. 3대 용도 = this.필드(매개변수와 이름 충돌 해소) · this()(다른 생성자 호출, 첫 줄만) · return this(메서드 체이닝).
[표] this 3대 용도
| 용도 | 의미 |
|---|---|
this.필드 |
매개변수와 같은 이름의 멤버 필드 구분 |
this(인자) |
같은 클래스의 다른 생성자 호출(생성자 첫 줄만) |
return this |
자기 자신 반환(메서드 체이닝) |
💡 this()와 super()(부모 생성자 호출)는 둘 다 생성자 첫 줄에 와야 해서 동시 사용이 불가능하다 — 시험 단골 함정. super로 부모를 호출하는 상속은 42강에서 본격적으로 다룬다.
🔑 암기 "this = 현재 객체 · this.필드(충돌 해소) / this()(다른 생성자·첫 줄) / return this(체이닝)"
⚠️ 함정 this()와 super() 동시 사용 불가(둘 다 첫 줄).
🎯 빈출 this.필드로 매개변수 충돌 해소·this() 첫 줄 제약.
static — 칠판(공유) vs 인스턴스(개인 노트) ·static·시험 1순위·
[정의] static = 클래스 변수/메서드. 모든 객체가 공유(칠판)하며 객체 없이 클래스명으로 호출. 인스턴스 멤버는 객체마다 따로(개인 노트).
[코드]
public class Counter {
private static int totalCount = 0; // static — 모든 객체 공유(칠판)
private int myCount = 0; // 인스턴스 — 객체마다 따로(개인 노트)
public void increment() {
totalCount++; // 칠판 +1 — 모든 객체에 영향
myCount++; // 내 노트 +1 — 이 객체만
}
public static int getTotal() { return totalCount; } // 객체 없이 호출
}
// c1 두 번·c2 한 번 increment → totalCount=3(공유) / c1.my=2 · c2.my=1
[표] static vs 인스턴스
| 구분 | static(클래스 변수) | 인스턴스 변수 |
|---|---|---|
| 저장 | Method Area, 1개만 | Heap, 객체마다 |
| 공유 | 모든 객체 공유(칠판) | 객체마다 따로(개인 노트) |
| 호출 | 클래스명.멤버 | 객체참조.멤버 |
💡 두 객체가 increment를 합쳐 3번 부르면 공유 칠판 totalCount는 3, 개인 노트 myCount는 각각 2와 1로 갈린다 — 실행으로 확인했다.
🔑 암기 "static = 칠판(공유·Method Area) / 인스턴스 = 개인 노트(Heap) · 클래스명.메서드() 호출"
⚠️ 함정 static 메서드에서 this 사용 불가(객체와 무관 → this 없음). 인스턴스 필드 직접 접근도 불가 — 시도하면 컴파일 오류.
🎯 빈출 static에서 this·인스턴스 필드 접근이 컴파일 오류로 매 회차.
PART D — Java 메모리와 타입
Java 메모리 3영역 — Stack·Heap·Method Area ·메모리·
[정의] Java 메모리는 3영역. Stack(지역 변수·매개변수·참조 변수) · Heap(객체·인스턴스 필드·배열) · Method Area(클래스 메타정보·static 필드).
[흐름] 영역별 저장 대상
┌──────────────┐
│ Stack │ 지역 변수 · 매개변수 · 참조 변수(주소) ← 함수 끝나면 자동 반납
├──────────────┤
│ Heap │ 객체(new) · 인스턴스 필드 · 배열 ← 자동 GC가 회수
├──────────────┤
│ Method Area │ 클래스 메타정보 · static 필드 ← 클래스 로드 시 1회
└──────────────┘
💡 Stack은 도서관 사물함(자동 반납), Heap은 창고(GC 청소부가 자동 정리). 절차형 언어는 할당·해제를 직접 했지만, Java는 new 한 줄로 Heap에 올리고 GC가 알아서 치운다.
🔑 암기 "객체는 Heap / 참조 변수는 Stack / static은 Method Area · free 없이 GC가 회수"
⚠️ 함정 객체는 Heap(Stack 아님) · static은 Method Area(Heap 아님) · Java에 free 키워드 없음(GC가 처리).
🎯 빈출 영역별 저장 대상 매칭(객체·static 위치).
참조 타입 vs 기본 타입 — 포스트잇 vs 같은 방 열쇠 ·타입·시험 1순위·
[정의] 기본 타입(8종) = 값 자체를 저장(복사하면 독립). 참조 타입(그 외 전부) = 주소를 저장(복사하면 같은 객체 공유).
[표] 두 타입 분별
| 구분 | 기본 타입(8종) | 참조 타입(그 외) |
|---|---|---|
| 종류 | byte·short·int·long·float·double·char·boolean | 클래스·배열·String·Integer |
| 저장 | Stack에 값 자체 | Stack에 주소, Heap에 실체 |
| 전달 | 값 복사(독립) | 주소 복사(공유) |
| 비유 | 포스트잇 복사(각자 독립) | 같은 방 열쇠 복사(같은 객체) |
[코드]
int a = 10; int b = a; b = 99; // 포스트잇 복사 — a는 그대로 10
Student s2 = s1; // 같은 방 열쇠 — s2 통해 바꾸면 s1도 바뀜
💡 b = a로 복사한 뒤 b를 99로 바꿔도 a는 10 그대로다(실행 확인). 배열도 참조 타입 — int[] arr은 Stack에 주소만, 실제 데이터는 Heap에. 포인터가 명시적으로 다루던 주소·역참조를 Java는 명시 없이 자동 처리한다.
🔑 암기 "기본 타입(8종) = 값 복사·독립 / 참조 타입 = 주소 복사·공유 · 배열도 참조 타입"
⚠️ 함정 참조 타입 대입은 객체 복제가 아니라 주소 공유 — 한쪽을 바꾸면 다른 쪽도 바뀐다.
🎯 빈출 기본 타입 8종·값 vs 주소 복사 분별.
final 3종 — 변수·메서드·클래스 ·final·
[정의] final = 잠금. 위치에 따라 잠그는 대상이 다르다 — 변수(재할당 잠금) · 메서드(오버라이딩 잠금) · 클래스(상속 잠금).
[표] final 3종
| final 위치 | 의미 | 예 |
|---|---|---|
| ① 변수 | 재할당 잠금(상수화) | final int MAX = 100; |
| ② 메서드 | 오버라이딩 잠금 | final void run() {} |
| ③ 클래스 | 상속 잠금 | final class String {} |
💡 콘크리트 굳히기 — 변수에 부으면 값이 굳고, 메서드에 부으면 덮어쓰기가 굳고, 클래스에 부으면 상속이 굳는다. String이 final 클래스인 이유 = 보안 + 불변 최적화 + 멀티스레드 안전.
🔑 암기 "final = 변수(재할당 X) / 메서드(오버라이딩 X) / 클래스(상속 X·대표 String)"
⚠️ 함정 final int MAX = 100; 뒤 MAX = 200;은 컴파일 오류(cannot assign a value to final variable). 단 final 변수는 생성자에서 1회 초기화 OK, final 메서드도 호출은 가능(오버라이딩만 차단), final 클래스도 인스턴스 생성 OK(상속만 차단). static final = 상수 패턴(가장 흔한 조합).
🎯 빈출 final 변수 재할당 오류·static final 상수.
PART E — 객체 비교와 핵심 타입
Wrapper와 Integer 캐시 -128~127 ·Wrapper·시험 1순위·
[정의] Wrapper = 기본 타입을 객체로 포장한 클래스(int→Integer, double→Double, char→Character, boolean→Boolean). int↔Integer 자동 변환을 박싱/언박싱이라 한다.
[코드]
Integer a = 100, b = 100;
System.out.println(a == b); // true — Integer 캐시(-128~127), 같은 박스 재활용
Integer c = 200, d = 200;
System.out.println(c == d); // false — 캐시 밖, 매번 새 객체
System.out.println(c.equals(d)); // true — 값 비교
💡 JVM이 -128~127까지 256개의 Integer 박스를 미리 만들어 캐시한다. 이 범위 안이면 같은 박스를 재활용해서 ==도 true, 128부터는 매번 새 박스라 ==는 false. 위 결과(true/false/true)를 실행으로 확인했다 — 항상 equals로 비교하면 함정이 사라진다.
🔑 암기 "Integer 캐시 -128~127만 ==도 true · 그 밖은 false · 비교는 항상 equals"
⚠️ 함정 Integer 200 == 200은 false(캐시 밖). ==로 값이 같을 거라 단정하면 틀린다.
🎯 빈출 Integer 캐시 경계(-128~127)가 매년 1점.
String 불변 + 리터럴 풀 ·String·시험 1순위·
[정의] String은 불변(Immutable) — 한 번 만든 문자열은 못 바꾼다. 리터럴 "hello"는 String Pool에 한 번만 저장돼 공유되고, new String("hello")는 Heap에 별도 객체를 만든다.
[코드]
String s1 = "hello", s2 = "hello";
System.out.println(s1 == s2); // true — 리터럴 풀, 같은 객체
String s3 = new String("hello");
System.out.println(s1 == s3); // false — new는 Heap에 별도 객체
System.out.println(s1.equals(s3)); // true — 값 비교
[표] String vs StringBuilder
| 항목 | String | StringBuilder |
|---|---|---|
| 변경 | 불변(Immutable) | 가변(Mutable) |
+ 연산 |
매번 새 객체(비효율) | append로 같은 객체에 추가 |
| 스레드 안전 | O(불변이라 자동) | X(StringBuffer는 O) |
💡 String = 돌에 새긴 글자(불변), StringBuilder = 공책(가변). String으로 +를 1000번 하면 임시 객체 1000개가 생기니, 반복 추가는 StringBuilder가 압도적으로 빠르다. 위 비교 결과(true/false/true)는 실행으로 확인했다.
🔑 암기 "리터럴은 ==도 true / new String()은 == false · 둘 다 equals면 true · 반복 추가는 StringBuilder"
⚠️ 함정 new String("hello")는 같은 글자라도 ==가 false(Heap 별도 객체).
🎯 빈출 리터럴 vs new String 비교가 매 회차(Integer 캐시와 짝).
== vs equals — 주소 비교 vs 값 비교 ·비교·시험 1순위·
[정의] ==는 주소(참조) 비교, equals는 값 비교. 기본 타입은 ==가 값 비교지만, 참조 타입은 ==가 주소를 본다.
[표] == / equals 결과 (실행 확인)
| 비교 | == |
equals |
|---|---|---|
| int 100, 100 | true(기본 타입은 값) | — |
| Integer 100, 100 | true(캐시) | true |
| Integer 200, 200 | false(캐시 밖) | true |
"hello", "hello"(리터럴) |
true(풀) | true |
"hello", new String("hello") |
false(Heap) | true |
| 사용자 객체 a, b | false(다른 객체) | 오버라이딩 안 했으면 false |
💡 ==도 true가 되는 건 Integer 캐시(-128~127)와 String 리터럴 풀, 두 경우뿐. 나머지는 다 false. 그래서 객체 비교는 항상 equals를 우선하고, null 안전을 위해 리터럴을 앞에 둔다 — "hello".equals(s).
🔑 암기 "==는 주소 / equals는 값 · Integer 캐시 + String 리터럴 풀만 ==도 true"
⚠️ 함정 참조 타입을 ==로 내용 비교하면 대부분 false. 사용자 클래스는 equals를 오버라이딩해야 값 비교가 된다.
🎯 빈출 == vs equals가 매년 단골(Integer·String과 묶여 출제).
배열과 ArrayList — length 속성 vs size() 메서드 ·배열·
[정의] 배열 = 크기 고정 참조 타입. 길이는 arr.length(속성, 괄호 X). 크기 한계를 푸는 게 ArrayList(동적 확장), 길이는 list.size()(메서드, 괄호 O).
[코드]
int[] arr = {10, 20, 30, 40, 50}; // Heap에 할당, 인덱스 0부터
arr[0] = 99;
int len = arr.length; // 속성 — 괄호 없음, len=5
ArrayList<String> list = new ArrayList<>();
list.add("민지"); list.add("지훈"); // 끝에 추가
String first = list.get(0); // 조회 — "민지"
int size = list.size(); // 메서드 — 괄호 있음, size=2
[표] 배열 vs ArrayList
| 항목 | 배열 | ArrayList |
|---|---|---|
| 크기 | 고정 | 동적(자동 확장) |
| 크기 조회 | arr.length(속성) |
list.size()(메서드) |
| 타입 | 기본 타입 OK | 참조 타입만(Wrapper 자동 박싱) |
💡 arr.length는 괄호가 없는 속성, list.size()는 괄호가 있는 메서드 — 이 차이가 단골 함정이다. 위 코드는 arr.length=5, list.size()=2, list.get(0)="민지"를 실행으로 확인했다. 배열 비교 ==는 주소 비교라 내용 비교는 Arrays.equals()를 쓴다.
🔑 암기 "배열 = 고정 + arr.length(속성·괄호 X) / ArrayList = 동적 + list.size()(메서드·괄호 O) · 인덱스 0부터"
⚠️ 함정 arr.length()(배열에 괄호) ❌ · list.length ❌ — 배열은 속성, 리스트·String은 메서드.
🎯 빈출 length 속성 vs size() 메서드 분별.
기출 다지기
[기출 1 출제] Java 접근 제어자에 대한 설명으로 옳지 않은 것은? (옳지 않은 것 고르기)
- ① public — 어떤 클래스에서든 접근 가능
- ② protected — 같은 패키지 + 다른 패키지 자식에서 접근 가능
- ③ default — 같은 패키지에서만 접근 가능
- ④ private — 같은 클래스 + 같은 패키지에서 접근 가능
정답 및 해설 보기
정답: ④
private은 해당 클래스 안에서만 접근 가능하다. 같은 패키지여도 다른 클래스면 접근 불가다.
| 보기 | 판정 | 근거 |
|---|---|---|
| ③ default | 옳음 | 같은 패키지까지만 |
| ④ private | 정답(틀린 설명) | '같은 패키지' 추가가 함정 — 같은 클래스만 O |
★공보기사★ 매트릭스에서 private은 '같은 클래스' 열만 O이고 나머지 전부 X. ④는 '같은 패키지'를 넣어 범위를 넓혀놓은 함정이다.
🔑 암기 private에 '같은 패키지'가 붙으면 틀린 설명 — ★공보기사★ 범위 순서로 즉결.
[기출 2 출제] 다음 코드에서 컴파일 결과로 옳은 것은? (컴파일 성공/실패 판정)
class B {
public B(String s) { } // 인자 생성자만 정의
}
// 호출부
B b = new B(); // ?
- ① 정상 컴파일된다
- ②
new B()에서 컴파일 오류가 난다 - ③ 런타임에 예외가 발생한다
- ④ 경고만 뜨고 실행된다
정답 및 해설 보기
정답: ②
기본 생성자는 생성자를 하나도 정의하지 않았을 때만 컴파일러가 자동으로 넣어준다. 인자 있는 생성자 B(String s)를 정의하는 순간 기본 생성자 자동 생성이 사라지므로, new B()(인자 없는 호출)는 컴파일 오류(constructor B cannot be applied to given types)다.
new B("hello")처럼 인자를 주거나, public B() {}를 따로 추가해야 한다.
🔑 암기 인자 생성자만 만들면 기본 생성자 자동 생성이 사라짐 — new 클래스() 컴파일 오류.
[기출 3 출제] 다음 중 컴파일 오류가 발생하는 줄은? (오류 줄 고르기)
class Calc {
private int x = 10; // ⓐ
public static int add(int a, int b) { return a + b; } // ⓑ
public static int add(int a, double b) { return (int)(a + b); } // ⓒ
public static int multiply() { return this.x * 2; } // ⓓ
}
- ① ⓐ
- ② ⓑ
- ③ ⓒ
- ④ ⓓ
정답 및 해설 보기
정답: ④
static 메서드에서는 this를 쓸 수 없다. static은 객체 없이 호출되니 this(현재 객체)가 존재하지 않는다. ⓓ의 this.x는 컴파일 오류(non-static variable this cannot be referenced from a static context)다.
| 보기 | 판정 | 근거 |
|---|---|---|
| ⓑ·ⓒ | 정상 | 매개변수가 (int,int)와 (int,double)로 달라 오버로딩 성립 |
| ④ ⓓ | 정답(오류) | static 메서드 안에서 this·인스턴스 필드 접근 불가 |
🔑 암기 static 메서드 안에 this·인스턴스 필드 직접 접근 보이면 컴파일 오류.
[기출 4 출제] 다음 코드의 출력 결과는? (코드 실행 결과 예측)
Integer a = 100, b = 100;
Integer c = 200, d = 200;
System.out.println(a == b);
System.out.println(c == d);
System.out.println(a.equals(b));
System.out.println(c.equals(d));
- ① true / true / true / true
- ② true / false / true / true
- ③ false / false / true / true
- ④ true / true / false / false
정답 및 해설 보기
정답: ② true / false / true / true
| 비교 | 결과 | 근거 |
|---|---|---|
| a == b (100) | true | Integer 캐시 -128~127, 같은 박스 재활용 |
| c == d (200) | false | 캐시 밖, 매번 새 객체 |
| equals(둘 다) | true | 값 비교 |
100은 캐시 범위 안이라 ==도 true, 200은 캐시 밖이라 새 객체라서 ==는 false. equals는 값을 보니 둘 다 true. 항상 equals를 쓰면 함정이 없다.
🔑 암기 Integer는 -128~127만 ==도 true. 그 밖은 false. 비교는 무조건 equals.
[기출 5 출제] 다음 코드의 출력 결과는? (코드 실행 결과 예측)
String s1 = "hello", s2 = "hello";
String s3 = new String("hello");
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s1.equals(s3));
- ① true / true / true
- ② true / false / false
- ③ true / false / true
- ④ false / false / true
정답 및 해설 보기
정답: ③ true / false / true
| 비교 | 결과 | 근거 |
|---|---|---|
| s1 == s2 | true | 리터럴 풀 같은 객체 |
| s1 == s3 | false | new String은 Heap에 별도 객체 |
| s1.equals(s3) | true | 값 비교 |
리터럴 "hello"는 풀에서 공유되니 ==도 true, new String("hello")는 Heap에 새 객체라 ==는 false. equals는 값을 보니 true. Integer 캐시 문제와 짝으로 자주 출제된다.
🔑 암기 리터럴은 ==도 true, new String()은 == false — 둘 다 equals면 true.
[기출 6 출제] 다음 중 == 비교 결과가 true인 것은? (옳은 것 고르기)
- ①
Integer x = 200, y = 200;에서x == y - ②
String a = new String("hi"), b = new String("hi");에서a == b - ③
int p = 100, q = 100;에서p == q - ④
String s = "hi";와String t = new String("hi");에서s == t
정답 및 해설 보기
정답: ③
==도 true가 되는 건 기본 타입의 값 비교, Integer 캐시(-128~127), String 리터럴 풀, 세 경우뿐이다.
| 보기 | 결과 | 근거 |
|---|---|---|
| ① Integer 200 | false | 캐시 밖, 새 객체 |
| ② new String 둘 | false | Heap 별도 객체 둘 |
| ③ int 100 | true | 기본 타입은 ==가 값 비교 |
| ④ 리터럴 vs new | false | new String은 Heap 별도 |
🔑 암기 ==가 true = 기본 타입 값 / Integer 캐시 / String 리터럴 풀. 나머지는 equals.
[기출 7 출제] Java의 메모리 영역과 저장 대상의 연결로 옳지 않은 것은? (옳지 않은 것 고르기)
- ① Stack — 지역 변수와 참조 변수(주소)
- ② Heap —
new로 생성한 객체와 인스턴스 필드 - ③ Method Area — 클래스 메타정보와 static 필드
- ④ Heap — static 필드와 클래스 메타정보
정답 및 해설 보기
정답: ④
static 필드와 클래스 메타정보는 Method Area에 저장된다(Heap 아님). ④는 static을 Heap에 놓아 ③과 충돌시킨 함정이다.
| 영역 | 저장 대상 |
|---|---|
| Stack | 지역 변수·매개변수·참조 변수(주소) |
| Heap | 객체(new)·인스턴스 필드·배열 |
| Method Area | 클래스 메타정보·static 필드 |
객체는 Heap, static은 Method Area, Java에는 free가 없고 GC가 회수한다 — 이 세 줄이 시험장 1점이다.
🔑 암기 객체=Heap / static=Method Area / 참조 변수=Stack · free 없이 GC 회수.
[기출 8 출제] 다음 코드에서 컴파일 오류가 나는 줄은? (오류 줄 고르기)
final int MAX = 100; // ⓐ
int total = MAX + 50; // ⓑ
MAX = 200; // ⓒ
System.out.println(MAX); // ⓓ
- ① ⓐ
- ② ⓑ
- ③ ⓒ
- ④ ⓓ
정답 및 해설 보기
정답: ③
final 변수는 한 번 초기화하면 재할당이 잠긴다. ⓒ의 MAX = 200;은 컴파일 오류(cannot assign a value to final variable MAX)다.
| 줄 | 판정 | 근거 |
|---|---|---|
| ⓐ | 정상 | final 변수 선언 + 초기화 |
| ⓑ | 정상 | final 값 읽기는 자유 |
| ⓒ | 정답(오류) | final 변수 재할당 금지 |
| ⓓ | 정상 | 값 출력 |
final 변수는 읽기는 자유지만 다시 대입은 불가. static final로 쓰면 클래스 상수 패턴이 된다.
🔑 암기 final 변수는 재할당 금지(읽기는 자유) · static final = 상수.
한 장 요약
| 주제 | 암기·핵심 | 결정 카드 |
|---|---|---|
| Java 정의 | 1995·고슬링·WORA | Sun→Oracle · JVM 하이브리드 · 객체지향+자동 GC |
| main | public static void main(String[] args) |
토씨 고정 · 클래스명=파일명 |
| 클래스 vs 객체 | 붕어빵 틀 / 붕어빵 | new = Heap에 객체 할당 |
| 클래스 구성 | ★클필메생★ | 클·필(상태)·메(행동)·생(클래스명 동일·반환타입 없음) |
| 접근 제어자 | ★공보기사★ | 공>보>기>사 · default vs protected(자식) 매트릭스 |
| 캡슐화 | private + getter/setter | 검증 1줄 · OOP 4대 중 하나(42강) |
| 생성자 | 오버로딩 | 인자 생성자만 만들면 기본 생성자 사라짐 |
| this | 현재 객체 | this.필드 / this()(첫 줄) / return this · super와 동시 불가 |
| static | 칠판(공유) | Method Area · 클래스명 호출 · this 사용 불가 |
| 메모리 | Stack/Heap/Method Area | 객체=Heap · static=Method Area · free 없음(GC) |
| 타입 | 기본 8종 vs 참조 | 값 복사(독립) vs 주소 복사(공유) · 배열도 참조 |
| final | 변수·메서드·클래스 | 재할당 X / 오버라이딩 X / 상속 X(String) |
| Wrapper | Integer 캐시 | -128~127만 ==도 true |
| String | 불변 + 리터럴 풀 | 리터럴 == true / new String == false |
== / equals |
주소 / 값 | 기본 타입·Integer 캐시·String 리터럴만 == true |
| 배열 / ArrayList | length / size() | 속성(괄호 X) / 메서드(괄호 O) · 인덱스 0부터 |
| 함정 (매년 2~3쌍 출제) | 정답 |
|---|---|
| default vs protected | default=같은 패키지 / protected=+다른 패키지 자식 |
| private '같은 패키지' | 틀린 설명 — 같은 클래스만 |
| 인자 생성자만 정의 | 기본 생성자 사라짐(new 클래스() 오류) |
| static에서 this | 컴파일 오류 |
| Integer 200 == 200 | false(캐시 밖) |
"hi" == new String("hi") |
false(Heap 별도) |
| final 변수 재할당 | 컴파일 오류 |
arr.length() / list.length |
배열=속성·리스트=메서드 |
🎯 합격 한 끗: 41강은 ★공보기사★(접근 제어 4×4 매트릭스) · ★클필메생★(클래스 4대 구성) 두 묶음이 기둥이다. 여기에 Integer 캐시 -128~127 · String 리터럴 풀 · ==는 주소·equals는 값 세 줄을 더하면 4과목 Java 빈출의 절반을 가져온다. 깊이 파기보다 추적 한 글자가 정답을 가르는 영역 — private에 '같은 패키지'가 붙으면 틀린 설명이고, 인자 생성자만 만들면 기본 생성자가 사라지며, static에서 this는 컴파일 오류, Integer는 캐시 안(-128~127)만 ==도 true다. 39·40강의 C(절차형·포인터·수동 메모리)가 여기서 Java(객체지향·참조·자동 GC)로 갈아탔다. 캡슐화는 OOP 4대 특성의 하나로 맛만 봤고, 상속·다형성·추상 클래스/인터페이스는 42강에서 ★캡상다추★로 본격적으로 묶인다.
