Java static · 예외 · String · 참조 E06

static 은 하나뿐이고, 예외는 catch 로 뛰고 finally 는 무조건이며, == 는 주소이고 참조 복사는 같은 집이다

왜 이 장을 하나
상속 밖의 Java 함정 넷이다. static 은 클래스에 하나라 모든 객체가 공유하고, 예외가 나면 그 줄에서 catch 로 뛰고 finally 는 언제나 돈다. == 는 주소 비교라 new 한 문자열끼리는 false 이고, 배열 · 객체를 대입하면 같은 것을 가리킨다. 17문항이 이 넷과 문자열 연결 · 재귀 · 람다 · enum 으로 나뉜다.
다루는 것static · 예외 흐름 · == 와 equals · 참조 복사 · 문자열 연결 · 재귀 · 람다 · enum
문항기출 17 · 예상 4 · 훈련 20
끝나면== 를 보면 주소인지 값인지 먼저 묻는다

01왜 이 장인가

왜 이걸 하나 — Java 34문항 중 17문항이 이 장이고, 2024년 3회 · 2025년 2회에는 3문항씩 나왔다.

11회분 220문항 중 이 장의 기출은 17문항이다. 11회 중 10회에 나왔고(2023년 3회만 빠짐 — 그 회차의 컴파일 오류 문제가 여기 있다), 2025년 이후 람다 · enum · 제네릭 같은 새 문법이 섞이기 시작했다. 훈련 Java 20문항이 붙는다.

몇 문항무엇을 적나보기
static · 싱글톤3공유되는 값 · 컴파일 오류 줄2023-1회 1번 · 2023-3회 12번 · 2024-1회 1번
예외 흐름3catch 선택 · finally · 람다 안의 throw2024-3회 16번 · 2025-1회 5번 · 2025-2회 9번
== · equals · 참조5문자열 비교 · 배열 참조 · 객체 배열 교환2023-2회 8번 · 2024-2회 1번 · 2024-3회 1번 · 2025-2회 5번 · 15번
문자열 · 배열 처리3split · charAt · 재귀 역순 중복 제거 · 연결 순서2024-2회 17번 · 20번 · 2026-1회 17번
재귀 · enum3음수까지 내려가는 재귀 · 이분 재귀 · enum values2025-1회 16번 · 2025-3회 5번 · 2026-2회 14번

→ == 와 참조가 5문항으로 가장 많다. new 한 것끼리 == 는 false(2024년 2회 1번 NNN), 리터럴끼리는 true(2023년 2회 8번), 배열 · 객체는 대입하면 같은 집(2025년 2회 5번 · 15번) — 세 문장이 이 다섯을 다 푼다.

E01 진단의 14번(compute(5) → 1)이 이 장이다. 종료 조건이 num <= 1 이라 음수까지 내려가는 재귀였고, 「종료 조건에서 무엇을 돌려주나」를 안 보면 답이 바뀐다. 실무에서는 안 쓰는 꼴이지만 시험은 이런 자리를 고른다.

문항 수는 이 노트의 문제 데이터에서 ch=E06 인 것을 센 값이다(2026-09-14). 기출 코드는 grandlife.co.kr 복원에서 옮겼고, 17문항 전부 JDK 17 로 실행하거나 컴파일해 정답을 확인했다. 컴파일 오류 문제(2023-3회 12번)는 javac 가 7번 줄에서 오류를 내는 것을 확인했다.

02한눈에

왜 이걸 하나 — 시험 전날 규칙 9개를 다시 본다.

static클래스에 하나. 모든 객체가 공유하고 클래스명.변수 로도 접근. static 메서드에서 인스턴스 변수를 직접 쓰면 컴파일 오류. 싱글톤은 static 인스턴스 하나를 getInstance 로 돌려준다.
예외 흐름예외가 난 줄에서 멈추고 try 의 나머지는 건너뛴다. catch 는 위에서부터 맞는 첫 것(자식 예외를 위에). finally 는 return 이 있어도 무조건. 예외 전에 찍힌 출력은 남는다.
== 와 equals== 는 주소(같은 객체인가), equals 는 내용. 문자열 리터럴 "A" == "A" 는 같은 상수라 true, new String("A") == new String("A") 는 false. 내용 비교는 equals.
참조 복사int[] b = a; 는 같은 배열. b[0] = 9 면 a[0] 도 9. a.clone() 은 새 배열. 객체도 같다 — 메서드 안에서 words[0] = label 은 밖에 반영, label = "Z" 는 지역 참조만 바뀜.
문자열 연결+ 는 왼쪽부터. 숫자 + 숫자는 덧셈, 문자열이 한 번 끼면 그 뒤는 전부 연결. 9 + 2 + "2" + "3" = "1123". char + int 는 숫자.
String 메서드length() · charAt(i) · substring(a, b)(b 미포함) · indexOf · split(",") → 배열 · equals · toUpperCase · StringBuilder append · reverse.
재귀종료 조건이 돌려주는 값을 그대로 쓴다(num <= 1 이면 음수도 반환). 두 갈래 재귀는 표로 아래부터. 출력이 재귀 호출 앞이면 들어가며, 뒤면 되감으며 찍힌다.
람다 · 인터페이스value -> value + 9 는 함수형 인터페이스의 추상 메서드를 즉석 구현. 람다 안에서 던진 예외는 부른 쪽의 catch 로 간다.
enum정해진 상수 목록. values() 는 전체 배열(선언 순서), ordinal() 은 0부터 순번, name() 은 이름 문자열. 생성자로 값을 붙일 수 있다.

03밑바닥

왜 이걸 하나 — 규칙 9개는 「무엇이 하나이고 무엇이 여럿인가」로 묶인다. static 은 하나, 참조가 가리키는 집도 하나, 문자열 리터럴도 하나.

3-1. static 은 클래스에 하나라 모두가 같은 값을 본다

static int count 는 객체마다가 아니라 클래스에 하나다. 2023년 1회 1번에서 StaticData.count = value 로 10 을 넣고, StaticData.count++ 는 10 을 찍은 뒤 11 이 되며, item.count 는 같은 static 이라 11 이다. 지역변수 value 는 10 그대로, 인스턴스 필드 item.value 는 20 → 네 줄이 10 · 11 · 10 · 20.

static 메서드는 객체 없이 도는 것이라 그 안에서 인스턴스 필드를 직접 쓸 수 없다. 2023년 3회 12번의 public static String getName() { return name; } 이 7번 줄에서 컴파일 오류인 이유다(non-static variable name cannot be referenced from a static context). 싱글톤(2024년 1회 1번)은 static 인스턴스 하나를 getInstance 로 돌려주므로 a 와 b 가 같은 객체라 add 네 번이 전부 한 value 에 쌓여 4 다.

3-2. 예외는 뛰고, finally 는 언제나

try 안에서 예외가 나면 그 줄에서 멈추고 아래 catch 중 맞는 첫 것으로 뛴다. 2025년 1회 5번은 number / divisor 에서 ArithmeticException 이 나 첫 catch 의 출력1 을 찍고, finally 의 출력5 를 찍는다 — 출력2 · 출력4 는 안 찍힌다. catch 는 위에서부터 보므로 자식 예외를 위에 둔다.

2024년 3회 16번은 fail() 이 NullPointerException 을 던져 첫 catch 에서 +1, finally 에서 +100 → 101. Exception catch 는 첫 것이 잡았으니 건너뛴다. 2025년 2회 9번은 람다 안의 throw 가 run 의 catch 로 잡혀 7, 두 번째 람다는 3 + 9 = 12, 합 19 — 람다도 결국 메서드 호출이라 예외는 부른 쪽으로 올라간다.

3-3. == 는 같은 집인가를 묻는다

Java 의 참조 변수는 집 주소를 든 쪽지다. == 는 두 쪽지에 같은 주소가 적혔는지를 묻고, equals 는 그 집의 내용이 같은지를 묻는다. 2024년 2회 1번은 new 로 만든 두 KOREA 가 다른 집이라 N, c 는 a 의 쪽지를 복사한 것이라 같은 집인데 b == c 를 물어 N, 세 번째도 새 집이라 N → NNN.

리터럴은 다르다. "Programming" 을 두 변수에 적으면 상수 풀의 같은 집이라 == 도 true 이고, new 로 만든 셋째와는 false, equals 는 셋 다 true(2023년 2회 8번, true false true true). 2024년 3회 1번은 equals 로 인접한 두 단어를 비교해 같으면 O, 다르면 A 를 찍어 OOAAA 다.

배열과 객체도 쪽지다. 2025년 2회 5번의 change 는 words[0] 을 바꿔 원본 배열의 첫 칸이 B 가 되지만, label = "Z" 는 지역 쪽지만 바꾼 것이라 밖의 label 은 B 그대로 → BB. 2025년 2회 15번은 배열 칸의 쪽지를 바꾼 뒤 boxes[1].value = boxes[0].value 로 b 의 값을 3 으로 만들어 a · b · c 가 1 · 3 · 3, 출력 1a3b3.

3-4. 문자열 연결은 왼쪽부터이고, 재귀는 종료 조건이 답을 정한다

+ 는 왼쪽부터 계산한다. 숫자끼리는 더하고, 문자열이 한 번 끼면 그 뒤로는 전부 이어 붙인다. 2026년 1회 17번의 9 + 2 + "2" + "3" 은 11 → "112" → "1123" 이고, a + b + "결과" + a + b 는 8결과53(예상 1회 8번)이다. 괄호가 없으면 왼쪽부터라는 것만 지키면 된다.

2024년 2회 17번은 재귀로 문자열을 뒤에서부터 보며 처음 나온 글자만 남긴다(되감으며 앞에 붙이므로 dcba). 2025년 1회 16번은 배열을 반씩 나눠 가운데 값을 더하는 이분 재귀라 표를 아래부터 채워 20 이다. 2026년 2회 14번은 num <= 1 에서 num 을 그대로 돌려주므로 compute(−1) = −1 이 위로 올라와 1 이 된다 — 종료 조건이 0 이 아니라는 것을 놓치면 답이 바뀐다.

enum(2025년 3회 5번)은 values() 가 선언 순서의 배열이고 Word.B.name() 은 "B" 라 길이 1, values()[1] 은 B, getText 는 AB 다. 새 문법이 나와도 배열 첨자와 문자열 길이라는 옛 규칙으로 풀린다.

통과 기준은 넷이다. 첫째, static 이 하나라는 것을 두 객체 · 두 참조로 설명한다. 둘째, 예외가 난 줄부터 catch · finally 의 실행 순서를 적는다. 셋째, == 와 equals 를 「같은 집인가 · 같은 내용인가」로 가르고, 리터럴 · new · 대입 복사의 셋을 안다. 넷째, 문자열 연결 순서와 재귀의 종료 조건 반환값을 그대로 쓴다. 기출 17문항이 이 넷 안에 있다.
메서드 안에서 매개변수를 바꾸면 밖의 변수도 바뀐다.쪽지를 복사해 넘기는 것이라 label = "Z" 는 지역 쪽지만 바꾼다. 다만 쪽지가 가리키는 집의 내용(words[0] = label)을 바꾸면 밖에서도 보인다. 2025년 2회 5번의 BB 가 이 둘의 차이다.
재귀의 종료 조건은 언제나 0 을 돌려준다.코드에 적힌 것을 돌려준다. if (num <= 1) return num; 은 −1 이 오면 −1 을 돌려주고, 2026년 2회 14번의 답 1 은 그 −1 이 더해진 결과다.

04기출

왜 이걸 하나 — 코드를 보면서 쪽지와 집을 그리는 연습이다. == 가 보이면 그림부터 그린다.

변수는 쪽지, new 는 새 집으로 그린다. 예외 문제는 try 안의 줄에 번호를 붙이고 어느 번호에서 뛰는지 표시한다. 정답을 연 뒤 그림과 다른 자리가 규칙 9개 중 빠진 것이다.

2023-1회 · 1번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

class StaticData { public int value = 20; static int count = 0; } public class Main { public static void main(String[] args) { int value = 10; StaticData.count = value; StaticData item = new StaticData(); System.out.println(StaticData.count++); System.out.println(item.count); System.out.println(value); System.out.print(item.value); } }
정답10
11
10
20

— 네 줄: 10 / 11 / 10 / 20. static 인 count 는 클래스에 하나뿐이라 어디서 고치든 같은 칸이고, count++ 는 찍은 뒤에 올린다.

원리static 이 붙은 필드는 객체마다 따로 있지 않고 클래스에 딱 하나 있다. StaticData.count(클래스 이름으로)와 item.count(객체 이름으로)는 표기만 다르고 같은 칸이다. static 이 없는 value 는 객체마다 하나씩 생기며 new 할 때 20 으로 채워진다. main 의 지역 변수 value 는 이름만 같은 다른 칸이다. 후위 count++ 는 "지금 값을 쓰고 나서 1 올린다"라 println 에는 올리기 전 값이 찍힌다.

따라가기

  1. value(지역) = 10, StaticData.count = 10
  2. item 생성 → item.value = 20
  3. println(count++) → 10 을 찍고 count 는 11
  4. println(item.count) → 같은 칸 → 11
  5. println(value) → 지역 변수 → 10, print(item.value) → 20

헷갈리는 자리 — 첫 줄을 11 로 적는 것(++count 였다면 11). item.count 를 "객체 것이니 0"으로 보는 것 — static 은 객체 것이 아니라 클래스 것 하나다. 마지막 줄은 println 이 아니라 print 라 줄바꿈이 없다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2023-2회 · 8번Java

다음 Java 프로그램의 실행 결과를 줄마다 쓰시오.

public class Main { public static void main(String[] args) { String first = "Programming"; String second = "Programming"; String third = new String("Programming"); System.out.println(first == second); System.out.println(first == third); System.out.println(first.equals(third)); System.out.println(second.equals(third)); } }
정답true
false
true
true

— true / false / true / true. == 는 같은 객체인지, equals 는 글자 내용이 같은지를 묻는다.

원리 — 자바에서 문자열 변수는 글자 자체가 아니라 글자 덩어리가 놓인 자리(참조)를 든다. "Programming" 처럼 따옴표로 쓴 글자는 같은 글자면 하나만 만들어 공유한다(문자열 풀). 그래서 first 와 second 는 같은 덩어리를 가리키고 == 가 true 다. new String(…) 은 "무조건 새 덩어리를 만들어라"이므로 third 는 다른 자리에 있고, first 와 == 하면 false 다. 내용을 견주려면 equals 를 써야 하고 그것은 세 변수 모두 true 다.

따라가기

  1. first == second → 같은 풀의 덩어리 → true
  2. first == third → new 로 만든 다른 덩어리 → false
  3. first.equals(third) → 글자 같음 → true
  4. second.equals(third) → true

헷갈리는 자리 — 둘째 줄을 true 로 적는 것. 글자가 같아도 new 는 새 객체다. 반대로 첫째 줄을 false 로 적는 것 — 따옴표 글자는 공유된다. == 는 "같은 집인가", equals 는 "같은 내용인가"로 나눠 외운다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2023-3회 · 12번Java

다음 Java 코드에서 컴파일 오류가 발생하는 줄 번호를 쓰시오.

1 class Person { 2 private String name; 3 Person(String value) { 4 name = value; 5 } 6 public static String getName() { 7 return name; 8 } 9 public void print() { 10 System.out.println(name); 11 } 12 }
정답7번 줄

— 7번 줄. static 메서드 안에서 인스턴스 필드 name 을 읽으려 해서 컴파일이 막힌다.

원리 — 인스턴스 필드(name)는 객체가 만들어져야 생기는 칸이다. static 메서드는 객체 없이 클래스 이름만으로 불릴 수 있는 메서드라, 그 안에는 "어느 객체의 name 인가"를 가리킬 this 가 없다. 그래서 static 메서드에서 인스턴스 필드나 인스턴스 메서드를 그냥 쓰면 컴파일러가 막는다. 반대로 인스턴스 메서드 print() 는 this 가 있으니 10번 줄은 문제없다. private 은 같은 클래스 안이라 상관없다.

따라가기

  1. 2번 줄: name 은 인스턴스 필드(static 아님)
  2. 6번 줄: getName 은 static → this 없음
  3. 7번 줄: return name → "non-static field cannot be referenced from a static context" → 7번 줄

헷갈리는 자리 — 6번 줄(선언부)을 적는 것. 선언 자체는 오류가 아니고 그 안에서 name 을 쓴 줄이 오류다. 2번 줄의 private 을 의심하는 것 — 같은 클래스 안이라 문제없다. 고치려면 static 을 빼거나 name 을 static 으로 만들면 된다.

복원 코드를 JDK 17 로 컴파일해 그 줄에서 오류가 나는 것을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-1회 · 1번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

class Counter { private static final Counter INSTANCE = new Counter(); private int value = 0; private Counter() { } static Counter getInstance() { return INSTANCE; } void add() { value++; } int getValue() { return value; } } public class Main { public static void main(String[] args) { Counter a = Counter.getInstance(); Counter b = Counter.getInstance(); a.add(); b.add(); a.add(); b.add(); System.out.print(a.getValue()); } }
정답4

— 4. getInstance 가 늘 같은 객체 하나를 돌려주므로 a 와 b 는 같은 Counter 이고, add 네 번이 전부 그 한 칸에 쌓인다.

원리 — 이 클래스는 싱글톤 꼴이다. 생성자가 private 이라 밖에서 new Counter() 를 못 하고, 클래스 안에서 static final INSTANCE 로 딱 한 번 만든 객체를 getInstance() 가 돌려준다. 그래서 몇 번을 받아 와도 같은 객체다. a 와 b 는 이름만 둘이지 가리키는 것은 하나라, a.add() 도 b.add() 도 같은 value 를 올린다.

따라가기

  1. a = INSTANCE, b = INSTANCE (같은 객체)
  2. add 네 번 → value 0 → 1 → 2 → 3 → 4
  3. a.getValue() → 4

헷갈리는 자리 — a 와 b 를 다른 객체로 보고 a 의 add 두 번만 세어 2 를 적는 것. private 생성자 + static 인스턴스 + getInstance 의 세 줄이 보이면 "객체는 하나"다. 디자인 패턴 장의 싱글톤이 코드로 나온 것이다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-2회 · 1번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { public static void main(String[] args) { String a = new String("KOREA"); String b = new String("KOREA"); String c = a; System.out.print(a == b ? "Y" : "N"); System.out.print(b == c ? "Y" : "N"); System.out.print(a == new String("KOREA") ? "Y" : "N"); } }
정답NNN

— NNN. new 로 만든 문자열은 전부 다른 객체라 == 가 모두 false 이고, c 는 a 와 같지만 비교 상대가 b 다.

원리new String("KOREA") 는 글자가 같아도 매번 새 덩어리를 만든다. == 는 두 변수가 같은 덩어리를 가리키는지만 본다. String c = a 는 a 가 가리키는 덩어리를 c 도 가리키게 한 것이라 a == c 는 true 겠지만, 코드가 묻는 것은 b == c 라서 false 다. 삼항 연산자는 참이면 "Y", 거짓이면 "N" 을 찍는다.

따라가기

  1. a == b → 서로 다른 new → N
  2. b == c → c 는 a 와 같은 덩어리, b 는 다른 덩어리 → N
  3. a == new String("KOREA") → 방금 만든 또 다른 덩어리 → N

헷갈리는 자리 — 둘째를 Y 로 적는 것. c 가 a 와 같다는 것에 눈이 가지만 비교 상대는 b 다. 문자열 풀은 따옴표 글자(literal)에만 적용되고 new 에는 적용되지 않는다는 것이 2023년 2회 8번과 짝이 되는 규칙이다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-2회 · 17번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { static String uniqueReverse(String value, int index) { if (index < 0) return ""; char current = value.charAt(index); String rest = uniqueReverse(value, index - 1); return rest.indexOf(current) >= 0 ? rest : current + rest; } public static void main(String[] args) { String value = "abacad"; System.out.print(uniqueReverse(value, value.length() - 1)); } }
정답dcba

— dcba. 재귀가 앞 글자부터 돌아오면서 아직 없는 글자만 앞에 붙이므로, 중복을 뺀 뒤집힌 문자열이 된다.

원리 — 이 재귀는 index 를 줄이며 끝까지 내려간 뒤(index < 0 이면 빈 문자열) 돌아오면서 결과를 만든다. 돌아오는 순서는 index 0 → 1 → 2 → … 라 앞 글자부터 처리되고, 각 단계에서 current + rest 로 "새 글자를 앞에" 붙인다. 앞에 붙이니 뒤집힌다. 단 rest.indexOf(current) >= 0, 즉 이미 그 글자가 있으면 붙이지 않고 rest 를 그대로 넘긴다. 그래서 "중복 없는 역순"이다.

따라가기 — abacad, index 5 부터 내려가 0 부터 돌아온다.

  1. index 0 'a': rest "" → "a"
  2. index 1 'b': "b" + "a" → "ba"
  3. index 2 'a': 이미 있음 → "ba"
  4. index 3 'c': → "cba", index 4 'a': 있음 → "cba"
  5. index 5 'd': → dcba

헷갈리는 자리 — 내려가는 순서(뒤 글자부터)로 붙여 "dacaba" 류를 적는 것. 재귀의 결과는 돌아올 때 만들어진다. 중복을 안 거르면 "dacaba" 가 뒤집힌 전체가 되고, 앞이 아니라 뒤에 붙였다면(rest + current) "abcd" 가 된다 — 붙이는 방향과 거름 조건 두 가지를 본다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-2회 · 20번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { public static void main(String[] args) { String value = "S/W/ENGINEER"; String[] words = value.split("/"); System.out.print(words[0]); } }
정답S

— S. split("/") 이 문자열을 / 마다 잘라 배열로 만들고, 그 첫 조각이 S 다.

원리split(구분자) 는 구분자를 기준으로 문자열을 잘라 String 배열을 돌려준다. 구분자 자체는 결과에 들어가지 않는다. "S/W/ENGINEER" 를 / 로 자르면 "S", "W", "ENGINEER" 세 조각이고, 배열 첨자는 0 부터이므로 words[0] 은 첫 조각이다. 조각 수는 구분자 수 + 1 이다.

따라가기

  1. words = ["S", "W", "ENGINEER"]
  2. words[0] → S

헷갈리는 자리 — words[0] 을 "첫 번째"가 아니라 "0번째 = 없음"으로 보거나, 구분자 / 가 조각에 포함된다고 보는 것. 자바 split 은 정규식을 받으므로 "." 이나 "|" 로 자를 때는 "\\." 처럼 이스케이프가 필요하다 — / 는 특수문자가 아니라 그대로 쓴다. words.length 는 3 이고 words[3] 을 읽으면 ArrayIndexOutOfBoundsException 이 난다는 것도 같은 문제 꼴에서 자주 묻는다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 1번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { static String[] words = new String[3]; static void printWords(String[] values, int size) { for (int i = 1; i < size; i++) { System.out.print(values[i - 1].equals(values[i]) ? "O" : "N"); } for (String value : values) { System.out.print(value); } } public static void main(String[] args) { words[0] = "A"; words[1] = "A"; words[2] = new String("A"); printWords(words, 3); } }
정답OOAAA

— OOAAA. 이웃한 문자열을 equals 로 견주면 내용이 같아 두 번 다 O 이고, 이어서 세 원소 A 를 찍는다.

원리equals 는 글자 내용을 견주는 메서드라 new String("A") 로 만든 다른 객체여도 "A" 와 같다고 본다. 만약 == 였다면 셋째 원소는 다른 객체라 N 이 나왔을 것이다 — 이 문제는 그 차이를 반대로 찌른다. 첫 반복은 i = 1 부터 size − 1 까지 "앞 원소와 지금 원소"를 견주므로 두 번 돈다. 둘째 반복(향상된 for)은 배열의 원소를 순서대로 하나씩 꺼내 찍는다. 둘 다 print 라 한 줄이다.

따라가기

  1. words = ["A", "A", new "A"]
  2. i = 1: "A".equals("A") → O, i = 2: "A".equals(new "A") → O
  3. for 원소: A A A → 합쳐서 OOAAA

헷갈리는 자리 — 셋째 원소가 new 라서 둘째 비교를 N 으로 적는 것. equals 는 객체가 달라도 내용이 같으면 true 다. 첫 반복이 i = 0 이 아니라 1 부터라 비교가 두 번(원소 수 − 1)이라는 것도 놓치기 쉽다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 16번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { public static void main(String[] args) { int sum = 0; try { fail(); } catch (NullPointerException e) { sum += 1; } catch (Exception e) { sum += 10; } finally { sum += 100; } System.out.print(sum); } static void fail() throws Exception { throw new NullPointerException(); } }
정답101

— 101. 던져진 NullPointerException 은 첫 catch 에 잡혀 1 이 더해지고, finally 는 항상 돌아 100 이 더해진다.

원리 — try 안에서 예외가 나면 그 아래 줄은 건너뛰고 catch 들을 위에서부터 훑어 "예외 종류에 맞는 첫 catch" 하나만 실행한다. NullPointerException 은 Exception 의 자식이라 둘째 catch(Exception)에도 맞지만, 이미 첫 catch 에 잡혔으니 둘째는 안 돈다. finally 는 예외가 났든 안 났든, 잡혔든 안 잡혔든 마지막에 반드시 돈다. fail() 의 throws Exception 은 "이 메서드가 예외를 던질 수 있다"는 선언일 뿐 무엇을 던지는지는 몸통이 정한다.

따라가기

  1. fail() → NullPointerException 던짐
  2. catch (NullPointerException) 에 잡힘 → sum = 1
  3. catch (Exception) 은 건너뜀
  4. finally → sum = 101 → 101

헷갈리는 자리 — 둘째 catch 도 맞으니 10 을 더해 111 로 적는 것. catch 는 하나만 잡는다. finally 를 빼고 1 로 적는 것 — finally 는 언제나 돈다. catch 순서가 반대(Exception 이 먼저)였다면 컴파일 오류다 — 넓은 것이 위에 오면 아래는 닿을 수 없기 때문이다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2025-1회 · 5번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { public static void main(String[] args) { int number = 5; int divisor = 0; try { System.out.print(number / divisor); } catch (ArithmeticException e) { System.out.print("출력1"); } catch (ArrayIndexOutOfBoundsException e) { System.out.print("출력2"); } catch (Exception e) { System.out.print("출력4"); } finally { System.out.print("출력5"); } } }
정답출력1출력5

— 출력1출력5. 5 를 0 으로 나눠 ArithmeticException 이 나고, 그것을 잡는 첫 catch 와 finally 만 돈다.

원리 — 정수를 0 으로 나누면 자바는 ArithmeticException 을 던진다(실수 나눗셈이면 Infinity 가 되어 예외가 없다). 예외가 나는 순간 System.out.print(number / divisor) 는 아무것도 찍지 못하고 catch 로 넘어간다. catch 는 위에서부터 종류가 맞는 첫 하나만 돈다 — ArithmeticException 은 첫 catch 에 정확히 맞고, 셋째 catch(Exception)도 맞지만 이미 잡혔다. finally 는 마지막에 반드시 돈다. 출력2 · 출력4 는 이 흐름에서 닿을 수 없다.

따라가기

  1. 5 / 0 → ArithmeticException, print 는 실행되지 않음
  2. catch (ArithmeticException) → 출력1
  3. finally → 출력5. 한 줄에 이어 출력1출력5

헷갈리는 자리 — 출력4 까지 적는 것(Exception 도 맞으니까) — catch 는 하나만. 출력1 과 출력5 사이에 줄바꿈이나 공백을 넣는 것 — 전부 print 라 붙는다. 출력3 이 코드에 없는 것은 미끼가 아니라 그냥 없는 것이다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2025-1회 · 16번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { static int search(int[] data, int start, int end) { if (start >= end) return 0; int middle = (start + end) / 2; return data[middle] + Math.max( search(data, start, middle), search(data, middle + 1, end) ); } public static void main(String[] args) { int[] data = { 3, 5, 8, 12, 17 }; System.out.println(search(data, 0, data.length - 1)); } }
정답20

— 20. 가운데 값을 더하고 왼쪽 · 오른쪽 반쪽 중 큰 쪽을 택하는 재귀라, 8 + max(8, 12) = 20 이다.

원리 — search(start, end) 는 구간의 가운데 첨자 middle 을 잡고 data[middle] 에, 왼쪽 구간(start~middle)과 오른쪽 구간(middle + 1~end)의 결과 중 큰 것을 더한다. start >= end 면 0 으로 멈춘다. 즉 "가운데를 하나 더하고, 반쪽 중 큰 길로 계속 간다"는 뜻이다. 가운데 첨자는 (start + end) / 2 의 정수 나눗셈이라 (0 + 4) / 2 = 2, (0 + 2) / 2 = 1, (0 + 1) / 2 = 0 이다. 재귀는 작은 구간부터 값을 정해 올라오면 된다.

따라가기 — data = [3, 5, 8, 12, 17].

  1. search(0, 4): middle 2 → 8 + max(search(0, 2), search(3, 4))
  2. search(0, 2): middle 1 → 5 + max(search(0, 1), search(2, 2) = 0)
  3. search(0, 1): middle 0 → 3 + max(0, 0) = 3 → search(0, 2) = 5 + 3 = 8
  4. search(3, 4): middle 3 → 12 + max(0, 0) = 12
  5. 8 + max(8, 12) = 20

헷갈리는 자리 — 오른쪽 구간을 middle 부터 시작한다고 보고 8 을 두 번 더하는 것 — 오른쪽은 middle + 1 부터다. search(2, 2) 처럼 start == end 인 구간이 0 이라는 것(원소가 하나 있어도 0)을 놓치면 8 이 더 붙는다. 조건이 start > end 였다면 답이 달라진다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2025-2회 · 5번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { static void change(String[] words, String label) { words[0] = label; label = "Z"; } public static void main(String[] args) { String[] words = { "A" }; String label = "B"; change(words, label); System.out.print(words[0] + label); } }
정답BB

— BB. 배열은 주소가 넘어가 words[0] 이 B 로 바뀌지만, String 변수 label 은 복사본이라 함수 안의 "Z" 대입이 main 에 안 보인다.

원리 — 자바는 값을 넘긴다. 배열 변수 words 의 "값"은 배열이 놓인 자리(참조)라, 함수 안의 words 도 같은 배열을 가리켜 words[0] = label 이 원본 배열을 고친다. label 도 참조를 복사해 받지만 label = "Z" 는 "함수 안의 label 이 다른 문자열을 가리키게" 할 뿐, main 의 label 이 가리키는 곳은 그대로다. 문자열은 바꿀 수 없는(불변) 객체라 내용을 고치는 길도 없다. "칸 안의 값을 고치면 밖에 보이고, 변수 자체를 새것으로 바꾸면 안 보인다"가 규칙이다.

따라가기

  1. change(words, "B"): words[0] = "B" → 원본 배열 바뀜
  2. label = "Z" → 함수 안 변수만 바뀜
  3. main: words[0] "B" + label "B" → BB

헷갈리는 자리 — BZ 로 적는 것. 매개변수에 새 객체를 대입한 것은 호출한 쪽에 안 돌아간다. AB 로 적는 것 — 배열 칸 대입은 원본에 닿는다. 배열 · 객체의 칸 고치기(보임)와 변수 갈아끼우기(안 보임)를 나눈다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2025-2회 · 9번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { interface Operation { int apply(int value) throws Exception; } static int run(Operation operation) { try { return operation.apply(3); } catch (Exception e) { return 7; } } public static void main(String[] args) { Operation first = value -> { if (value > 2) { throw new Exception(); } return value * 2; }; System.out.print(run(first) + run(value -> value + 9)); } }
정답19

— 19. 첫 람다는 3 > 2 라 예외를 던져 catch 가 7 을 돌려주고, 둘째 람다는 3 + 9 = 12 다.

원리 — 람다 value -> {…} 는 인터페이스 Operation 의 유일한 메서드 apply 를 그 자리에서 구현한 것이다. run 은 apply(3) 을 try 안에서 부르고, 예외가 나면 catch 에서 7 을 돌려준다. 첫 람다는 value 가 2 보다 크면 throw new Exception() 이라 3 을 넣으면 예외다 — return 까지 못 간다. 둘째 람다 value -> value + 9 는 한 줄이라 중괄호 없이 값이 곧 결과다. 두 run 의 결과를 더한다.

따라가기

  1. run(first): apply(3) → 3 > 2 → 예외 → catch → 7
  2. run(둘째): apply(3) → 3 + 9 = 12
  3. 7 + 12 = 19

헷갈리는 자리 — 첫 람다를 3 × 2 = 6 으로 계산해 18 을 적는 것. 조건이 참이라 return 줄은 안 돈다. 또 throws Exception 이 인터페이스에 있어야 람다 안에서 checked 예외를 던질 수 있다는 것 — 없으면 컴파일 오류라, 빈칸 문제로도 나올 수 있는 자리다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2025-2회 · 15번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { static class Box { int value; Box(int value) { this.value = value; } } public static void main(String[] args) { Box a = new Box(1); Box b = new Box(2); Box c = new Box(3); Box[] boxes = { a, b, c }; Box temp = boxes[0]; boxes[0] = boxes[2]; boxes[2] = temp; boxes[1].value = boxes[0].value; System.out.print(a.value + "a" + b.value + "b" + c.value); } }
정답1a3b3

— 1a3b3. 배열 칸을 맞바꿔도 객체 a · b · c 자체는 그대로이고, boxes[1].value = … 만 객체 b 의 값을 3 으로 고친다.

원리 — 배열 boxes 에는 객체가 들어 있는 것이 아니라 객체를 가리키는 참조가 들어 있다. boxes[0]boxes[2] 를 맞바꾸는 것은 참조의 자리만 바꾸는 것이라 a · b · c 어느 객체의 value 도 안 변한다. 반면 boxes[1].value = boxes[0].value 는 boxes[1] 이 가리키는 객체(b)의 칸을 고치는 것이라 b.value 가 바뀐다. 마지막 출력은 배열이 아니라 변수 a · b · c 로 찍으므로 객체의 값을 본다. "참조를 옮기는 것"과 "객체의 칸을 고치는 것"을 나눈다.

따라가기

  1. boxes = [a, b, c] (값 1, 2, 3)
  2. temp = a; boxes[0] = c; boxes[2] = a → boxes = [c, b, a]
  3. boxes[1].value = boxes[0].value → b.value = c.value = 3
  4. a.value 1, b.value 3, c.value 3 → 1a3b3

헷갈리는 자리 — 배열을 맞바꿨으니 a 와 c 의 값도 바뀌었다고 보고 3a3b1 로 적는 것. 참조 자리 바꾸기는 객체를 안 건드린다. boxes[1] 이 b 라는 것을 놓치고 다른 객체를 고치는 것 — 자리 1 은 교환에 끼지 않았다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2025-3회 · 5번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

enum Word { A("A"), B("AB"), C("ABC"); private final String text; Word(String text) { this.text = text; } public String getText() { return text; } } public class Main { public static void main(String[] args) { Word w = Word.values()[Word.B.name().length()]; System.out.print(w.getText()); } }
정답AB

— AB. Word.B.name() 은 "B" 라 길이 1 이고, values()[1] 은 둘째 상수 B 라 그 text "AB" 가 나온다.

원리 — enum 은 정해진 상수들의 목록이고, 각 상수는 생성자를 통해 자기 값(text)을 가진다. name() 은 상수의 이름 자체("B")를 문자열로 돌려주고, getText() 는 우리가 넣은 값("AB")을 돌려준다 — 둘은 다르다. values() 는 상수를 선언 순서대로 담은 배열이라 [0] A, [1] B, [2] C 다. 그러니 values()[Word.B.name().length()] 는 values()[1] = B 이고, 그 text 가 답이다.

따라가기

  1. Word.B.name() → "B", length() → 1
  2. values()[1] → B
  3. B.getText() → AB

헷갈리는 자리 — name() 을 text("AB")로 읽어 길이 2 → values()[2] = C → "ABC" 를 적는 것. name 은 상수 이름이다. 첨자가 0 부터라는 것을 잊고 values()[1] 을 A 로 보는 것도 있다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2026-1회 · 17번Java

다음 Java 프로그램을 실행했을 때 출력되는 값을 쓰시오.

public class Main { public static void main(String[] args) { int x1 = 9; int x2 = 2; String x3 = "3"; System.out.println(x1 + x2 + "2" + x3); } }
정답1123

— 1123. 왼쪽부터 계산해 9 + 2 = 11 이 먼저 정수로 더해지고, 그다음부터는 문자열 붙이기라 "11" + "2" + "3" 이다.

원리+ 는 두 쪽이 모두 숫자면 덧셈이고, 한쪽이라도 문자열이면 붙이기다. 그리고 왼쪽에서 오른쪽으로 하나씩 계산한다. x1 + x2 는 둘 다 int 라 11(덧셈), 그 결과 11 과 "2" 는 한쪽이 문자열이라 "112"(붙이기), 이어서 "112" + "3" → "1123". 문자열이 한 번 끼어든 뒤부터는 되돌아가지 않는다. 괄호가 없으니 순서를 바꿔 읽을 여지가 없다.

따라가기

  1. 9 + 2 → 11 (정수)
  2. 11 + "2" → "112"
  3. "112" + "3" → 1123

헷갈리는 자리 — 전부 문자열로 보고 9223 을 적는 것(첫 덧셈은 정수). 전부 숫자로 보고 16 을 적는 것("2" 와 "3" 은 문자열). "2" + x1 + x2 였다면 "292" 가 되듯 순서가 답을 바꾼다. 괄호로 x1 + (x2 + "2") 라고 묶으면 안쪽이 먼저 붙기라 "9223" 이 된다 — 괄호 유무도 본다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2026-2회 · 14번Java

다음 Java 프로그램의 실행 결과를 쓰시오.

public class Main { static int compute(int num) { if (num <= 1) { return num; } return compute(num - 1) + compute(num - 3); } public static void main(String[] args) { System.out.print(compute(5)); } }
정답1

— 1. 규칙이 n − 1 과 n − 3 이고 멈춤 조건이 1 이하일 때 그 수 자체라, 음수 −1 이 섞여 값이 작아진다.

원리 — 재귀는 작은 입력부터 표로 만들면 안전하다. 멈춤 조건 num <= 1 은 "1 이하이면 그 수를 그대로 돌려준다"라, compute(0) = 0, compute(−1) = −1 처럼 음수도 값이 된다. 이것을 0 으로 잘못 두면 답이 어긋난다. 규칙은 두 개 앞이 아니라 "하나 앞 + 셋 앞"이라 피보나치와 다르다.

따라가기 — c(n) = c(n − 1) + c(n − 3).

  1. c(1) = 1, c(0) = 0, c(−1) = −1
  2. c(2) = c(1) + c(−1) = 0
  3. c(3) = c(2) + c(0) = 0
  4. c(4) = c(3) + c(1) = 1
  5. c(5) = c(4) + c(2) = 1 + 0 = 1

헷갈리는 자리 — c(−1) 을 0 으로 두면 c(2) = 1, c(3) = 1, c(4) = 2, c(5) = 3 이 된다. 피보나치로 풀어 5 를 적는 것도 흔하다. 2023년 3회 14번(같은 규칙으로 compute(7) = 2)과 짝이다.

복원 코드를 JDK 17 로 컴파일 · 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

예상 1회 · 5번Java빈출 원리: 예외처리 순서

다음 코드의 출력을 쓰시오.

int[] arr = {1, 2, 3}; try { System.out.print(arr[0]); System.out.print(arr[3]); System.out.print(arr[1]); } catch (ArrayIndexOutOfBoundsException e) { System.out.print("E"); } finally { System.out.print("F"); } System.out.print("G");
정답1EFG

— 1EFG. arr[3] 에서 예외가 나 그 뒤 줄은 건너뛰고 catch → finally → 다음 문장 순으로 간다.

원리 — try 블록은 예외가 나는 순간 거기서 멈추고 나머지 줄은 실행하지 않는다. arr 은 3칸(0 · 1 · 2)이라 arr[3] 은 범위 밖이고 ArrayIndexOutOfBoundsException 이 던져진다. 그래서 arr[0] 의 1 은 찍혔지만 arr[1] 의 2 는 영영 찍히지 않는다. 예외는 타입이 맞는 catch 로 잡혀 E 를 찍고, finally 는 예외가 났든 안 났든 반드시 실행돼 F 를 찍는다. 예외가 처리됐으니 프로그램은 계속 진행해 G 를 찍는다.

따라가기

  1. arr[0] → 1
  2. arr[3] → 예외 발생 → arr[1] 건너뜀
  3. catch → E → finally → F
  4. try 밖 → G. 출력 1EFG

헷갈리는 자리 — 예외 뒤의 arr[1] 도 찍혀 12EFG 로 적는 것. 예외가 난 줄부터 try 의 나머지는 실행되지 않는다. 그리고 catch 가 없거나 타입이 안 맞았다면 finally 는 F 를 찍고 예외가 밖으로 나가 G 는 안 찍힌다 — "잡혔는가"가 뒤 문장의 운명을 정한다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

예상 1회 · 8번Java빈출 원리: 문자열 연결 순서

다음 코드의 출력을 쓰시오.

int a = 5, b = 3; System.out.println(a + b + "결과" + a + b);
정답8결과53

— 8결과53. + 는 왼쪽부터 계산되어 문자열을 만나기 전까지는 덧셈, 만난 뒤로는 이어 붙이기다.

원리 — 자바의 + 는 두 피연산자 중 하나라도 String 이면 문자열 연결이고, 둘 다 숫자면 덧셈이다. 그리고 같은 우선순위라 왼쪽에서 오른쪽으로 하나씩 계산한다. a + b 는 5 + 3 = 8(숫자). 8 + "결과" 는 "8결과"(문자열). "8결과" + a 는 "8결과5", 거기에 + b 는 "8결과53". 한 번 문자열이 되면 그 뒤의 숫자는 전부 붙는다. 같은 식이라도 괄호를 (a + b) 로 뒤에 두면 "8결과8" 이 된다.

따라가기

  1. 5 + 3 → 8
  2. 8 + "결과" → "8결과"
  3. "8결과" + 5 → "8결과5" → + 3 → 8결과53

헷갈리는 자리 — 뒤의 a + b 도 더해 "8결과8" 로 적는 것. 문자열 뒤에서는 더하기가 없다. println 이라 줄바꿈이 붙지만 답에는 안 적는다. 이 유형은 문자열 위치만 바꿔 여러 번 나온다 — 왼쪽부터 한 칸씩 계산하면 된다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

예상 2회 · 1번Java빈출 원리: static 공유

다음 코드의 출력을 쓰시오.

class Counter { static int total = 0; int num; Counter() { total++; num = total; } } public class Main { public static void main(String[] args) { Counter a = new Counter(); Counter b = new Counter(); Counter c = new Counter(); System.out.print(Counter.total + "" + a.num + b.num + c.num); } }
정답3123

— 3123. static 변수 total 은 클래스에 하나뿐이라 객체를 만들 때마다 1 씩 올라 3 이고, 각 객체의 num 은 만들어질 때의 total 값 1 · 2 · 3 을 갖는다.

원리 — static 필드는 객체마다 있는 것이 아니라 클래스 전체에 하나만 있다. 생성자가 total++ 를 하면 그 하나가 커진다. 반면 num 은 인스턴스 필드라 객체마다 따로 있고, 생성자에서 num = total 로 그 순간의 값을 복사해 둔다. a 는 total 이 1 일 때, b 는 2, c 는 3 일 때 만들어졌다. Counter.total + "" 은 숫자를 문자열로 바꾸는 관용구라 그 뒤의 + 는 전부 이어 붙이기가 되어 "3" + "1" + "2" + "3" 이다.

따라가기

  1. a: total 1, a.num 1 · b: total 2, b.num 2 · c: total 3, c.num 3
  2. Counter.total + "" → "3"
  3. "3" + 1 + 2 + 3 → 3123

헷갈리는 자리 — num 도 static 처럼 보고 3333 으로 적는 것. num 은 객체별 값이다. 그리고 + "" 가 없었다면 3 + 1 + 2 + 3 = 9 로 숫자 덧셈이 되었을 것이다 — 빈 문자열의 자리가 결과를 바꾼다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

예상 2회 · 5번Java빈출 원리: 배열 참조 공유 vs 복사

다음 코드의 출력을 쓰시오.

int[] a = {1, 2, 3}; int[] b = a; b[0] = 9; int[] c = a.clone(); c[1] = 8; System.out.print(a[0] + "" + a[1] + a[2]);
정답923

— 923. b 는 a 와 같은 배열을 가리켜 b[0] = 9 가 a 에 반영되고, c 는 복사본이라 c[1] = 8 은 a 에 영향이 없다.

원리 — 배열 변수는 배열 객체의 "주소"를 담는다. int[] b = a 는 주소를 복사한 것이라 a 와 b 는 같은 배열이다 — b[0] 을 바꾸면 a[0] 도 바뀐다. a.clone() 은 배열을 새로 만들어 값을 복사한 것이라 c 는 다른 배열이다 — c[1] 을 바꿔도 a 는 그대로다. 출력은 a[0] + "" 로 문자열을 만든 뒤 붙이므로 9 · 2 · 3 이 이어진다. 참조 복사와 값 복사의 차이가 이 문제 전부다.

따라가기

  1. b = a → 같은 배열. b[0] = 9 → a = [9 2 3]
  2. c = a.clone() → 새 배열 [9 2 3]. c[1] = 8 → c 만 [9 8 3]
  3. a → 923

헷갈리는 자리 — clone 도 같은 배열이라 보고 983 으로 적거나, = 도 복사라 보고 123 으로 적는 것. "= 는 같은 것을 가리키고, clone 은 새로 만든다". 2차원 배열의 clone 은 바깥 배열만 복사(얕은 복사)라 안쪽 행은 공유된다는 것이 다음 단계 함정이다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

05틀리는 자리

왜 이걸 하나 — 이 장은 줄바꿈과 순서에서 깎인다.

자리새는 꼴적는 법기출
println4줄 출력을 한 줄로println 은 줄바꿈, print 는 이어짐. 문제가 「줄마다」라 하면 줄을 나눈다2023-1회 1번 · 2023-2회 8번
== 리터럴리터럴 == 를 false 로같은 리터럴은 같은 상수 → true. new 끼리만 false2023-2회 8번 · 2024-2회 1번
catch 순서Exception catch 도 실행된다고 본다맞는 첫 catch 하나만2024-3회 16번 · 2025-1회 5번
finally예외가 안 났으니 finally 를 뺀다finally 는 언제나2025-1회 5번
연결 순서"2" 뒤의 3 을 더한다문자열이 끼면 그 뒤는 전부 연결2026-1회 17번 · 예상 1회 8번
종료 조건음수 호출을 0 으로코드가 돌려주는 값 그대로2026-2회 14번
줄 번호「7」만 적거나 다른 줄문제가 「줄 번호」를 물으면 숫자, 정답표 표기가 「7번 줄」이면 그대로2023-3회 12번
참조 교환배열 칸을 바꾸면 a · b · c 변수도 바뀐다고 본다변수는 그대로, 배열 칸의 쪽지만 바뀐다. 값을 바꾼 줄이 어느 객체인지 그림으로2025-2회 15번

Java 출력 문제는 print 인지 println 인지가 줄 수를 정한다. 코드에서 println 을 세어 답의 줄 수와 맞춘다.

06훈련

왜 이걸 하나 — 규칙 9개를 짧은 코드 20개로 돌린다. Java 훈련의 3분의 2가 여기 있다.

훈련 20문항은 static 카운터, == 와 equals, StringBuilder, 정수 나눗셈, 예외 흐름, 배열 clone, switch, 문자열 연결, charAt · substring · split, 중첩 루프, 증감, 비트, 최댓값 탐색, 재귀 피보나치 · 역순, 2차원 배열, char + int, catch 선택이다. 정답은 전부 JDK 17 로 실행해 확인했다.

훈련 Java · 4번Javastatic 카운터
class C { static int cnt = 0; C() { cnt++; } } public class Main { public static void main(String[] args) { new C(); new C(); new C(); System.out.print(C.cnt); } }
정답3

— 3. static 변수 cnt 는 클래스에 하나라 객체를 셋 만들면 생성자가 세 번 돌아 3 이 된다.

원리 — 인스턴스 필드는 객체마다 따로지만 static 필드는 클래스에 단 하나다. 생성자 C() 가 불릴 때마다 그 하나의 cnt 를 1 올린다. new C() 세 번이면 cnt 는 3 이다. 객체를 변수에 담지 않아도 생성자는 실행된다. 읽을 때는 객체가 아니라 클래스 이름으로 C.cnt 라고 접근한다 — "만든 객체 수 세기"는 static 의 대표 용도다.

따라가기

  1. new C() → cnt 1 · new C() → 2 · new C() → 3
  2. C.cnt → 3

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 6번JavaString == vs equals
String a = "Java"; String b = "Java"; String c = new String("Java"); System.out.print((a == b) + " " + (a == c) + " " + a.equals(c));
정답true false true

— true false true. 리터럴 둘은 같은 객체를 공유하고, new 는 새 객체를 만들며, equals 는 내용을 비교한다.

원리 — == 는 "같은 객체인가(주소)", equals 는 "내용이 같은가"다. 문자열 리터럴 "Java" 는 문자열 풀(pool)에 하나만 만들어져 a 와 b 가 같은 객체를 가리키므로 a == b 는 true. new String("Java") 는 풀과 별개로 힙에 새 객체를 만들어 a == c 는 false. 하지만 글자는 같으니 a.equals(c) 는 true. 문자열 비교는 항상 equals 로 해야 한다는 교훈이 담긴 문제다.

따라가기

  1. a == b → 같은 풀 객체 → true
  2. a == c → new 로 만든 다른 객체 → false
  3. a.equals(c) → 내용 같음 → true

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 7번JavaStringBuilder 체인
StringBuilder sb = new StringBuilder("abc"); sb.append("de").reverse(); System.out.print(sb);
정답edcba

— edcba. "abc" 에 "de" 를 붙여 "abcde" 를 만든 뒤 뒤집었다.

원리 — StringBuilder 는 String 과 달리 자기 자신을 고치는(가변) 객체다. append 는 뒤에 붙이고 reverse 는 순서를 뒤집는데, 둘 다 자기 자신을 돌려주므로 점으로 이어 부를 수 있다(메서드 체이닝). sb.append("de") 로 sb 가 "abcde" 가 되고, 이어서 .reverse() 로 "edcba" 가 된다. print(sb) 는 sb.toString() 을 찍는다. String 이었다면 매번 새 객체가 생겨 원본은 안 바뀐다 — 그것이 StringBuilder 를 쓰는 이유다.

따라가기

  1. "abc".append("de") → "abcde"
  2. .reverse() → "edcba"
  3. 출력 edcba

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 8번Java정수 나눗셈 + 삼항
int a = 7, b = 2; System.out.print(a / b + " " + a % b + " " + (a > b ? a - b : b - a));
정답3 1 5

— 3 1 5. 정수 나눗셈 몫 3, 나머지 1, 삼항 연산으로 7 − 2 = 5 다.

원리 — int 끼리의 / 는 소수점을 버린 몫이라 7 / 2 = 3 이고, % 는 나머지라 1 이다. 삼항 연산자는 a > b 가 참이면 a − b, 거짓이면 b − a 를 고르는데 7 > 2 참이라 5 다. 괄호가 없으면 + 가 삼항보다 먼저 묶여 엉뚱한 결과가 되므로 괄호로 감쌌다. 문자열 " " 와 번갈아 + 하므로 각 값은 문자열로 바뀌어 붙는다.

따라가기

  1. 7 / 2 → 3, 7 % 2 → 1
  2. 7 > 2 ? 7 − 2 : … → 5
  3. 출력 3 1 5

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 9번Java예외 흐름 완주
try { int[] x = new int[2]; x[2] = 1; System.out.print("T"); } catch (ArrayIndexOutOfBoundsException e) { System.out.print("C"); } finally { System.out.print("F"); } System.out.print("END");
정답CFEND

— CFEND. x[2] 에서 예외가 나 T 는 안 찍히고 catch → finally → 뒤 문장 순서다.

원리 — new int[2] 는 0 · 1 두 칸이라 x[2] 는 범위 밖이고 ArrayIndexOutOfBoundsException 이 난다. 예외가 난 줄에서 try 는 중단되어 "T" 는 실행되지 않는다. 타입이 맞는 catch 가 잡아 C, finally 는 항상 실행되어 F. 예외가 처리됐으니 프로그램은 계속 진행해 END 를 찍는다. 예외가 없었다면 T F END 였을 것이다.

따라가기

  1. x[2] = 1 → 예외 → "T" 건너뜀
  2. catch → C → finally → F
  3. 계속 → END. 출력 CFEND

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 10번Java배열 참조 vs clone
int[] a = {1, 2, 3}; int[] b = a; b[0] = 9; int[] c = a.clone(); c[2] = 7; System.out.print(a[0] + "" + a[1] + a[2]);
정답923

— 923. b 는 a 와 같은 배열이라 b[0] = 9 가 a 를 바꾸고, c 는 clone 이라 c[2] = 7 은 a 를 안 바꾼다.

원리 — 배열 변수에는 배열의 주소가 들어 있다. int[] b = a 는 주소를 복사해 두 변수가 한 배열을 가리킨다 — 어느 쪽으로 고쳐도 같은 배열이다. a.clone() 은 값이 같은 새 배열을 만들어 돌려주므로 c 는 별개다. 그래서 a 는 [9 2 3] 이고 c 는 [9 2 7] 이다. a[0] + "" 로 먼저 문자열을 만들어 붙이므로 923 이다. P-2-05 와 같은 유형이고 바뀐 칸만 다르다.

따라가기

  1. b[0] = 9 → a = [9 2 3]
  2. c = clone → c[2] = 7 → c 만 변화
  3. a → 923

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 11번Javaswitch fall-through (Java판)
int score = 75; switch (score / 10) { case 9: System.out.print("A"); case 8: System.out.print("B"); case 7: System.out.print("C"); case 6: System.out.print("D"); break; default: System.out.print("F"); }
정답CD

— CD. 75 / 10 = 7 이라 case 7 로 들어가 break 를 만날 때까지 C · D 가 찍힌다.

원리 — switch 는 값이 맞는 case 로 점프한 뒤 break 까지 아래로 흘러내린다(fall-through). score / 10 은 정수 나눗셈이라 7 이다. case 7 의 C 를 찍고, break 가 없어 case 6 의 D 까지 찍은 뒤 거기 break 로 나간다. case 9 · 8 은 위에 있어 안 거치고 default 는 break 뒤라 안 닿는다. 점수대별 등급 코드에서 break 를 빠뜨렸을 때의 전형이다.

따라가기

  1. 75 / 10 = 7 → case 7 → C
  2. break 없음 → case 6 → D → break
  3. 출력 CD

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 12번Java문자열 연결 순서
System.out.print(1 + 2 + "a" + 1 + 2);
정답3a12

— 3a12. 문자열 "a" 앞은 덧셈, 뒤는 이어 붙이기다.

원리 — + 는 왼쪽부터 차례로 계산되고, 문자열이 끼어드는 순간부터 연결 연산이 된다. 1 + 2 는 둘 다 숫자라 3. 3 + "a" 는 "3a". "3a" + 1 은 "3a1", + 2 는 "3a12". 앞의 1 + 2 는 합쳐지고 뒤의 1 · 2 는 따로 붙는 비대칭이 답의 모양이다. 뒤도 더하고 싶으면 (1 + 2) 로 괄호를 쳐야 한다. 문자열이 맨 앞에 오면 "a" + 1 + 2 → "a12" 처럼 전부 붙는다.

따라가기

  1. 1 + 2 → 3
  2. 3 + "a" → "3a"
  3. "3a" + 1 + 2 → 3a12

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 13번JavacharAt + 문자 연산
String s = "korea"; char c = s.charAt(1); System.out.print((char)(c - 32) + "" + (char)(s.charAt(0) + 1));
정답Ol

— Ol. 'o' − 32 = 'O', 'k' + 1 = 'l' 이다.

원리 — char 는 정수(유니코드 번호)로 계산된다. 'o' 는 111 이고 32 를 빼면 79 = 'O'(대소문자 차이 32). 'k' 는 107 이고 1 을 더하면 108 = 'l'. 그런데 char 끼리 산술을 하면 결과는 int 가 되므로 글자로 찍으려면 (char) 로 되돌려야 한다 — 캐스팅이 없으면 79 와 108 이 숫자로 찍힌다. 사이의 + "" 는 두 char 를 숫자로 더하지 않고 문자열로 붙이기 위한 것이다.

따라가기

  1. c = 'o' → (char)(111 − 32) → O
  2. s.charAt(0) = 'k' → (char)(107 + 1) → l
  3. 출력 Ol

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 14번Javasubstring / indexOf / length
String s = "information"; System.out.print(s.substring(2, 6) + " " + s.indexOf('o') + " " + s.length());
정답form 3 11

— form 3 11. substring(2, 6) 은 첨자 2 부터 6 앞까지, indexOf('o') 는 첫 o 의 첨자, length 는 글자 수다.

원리 — substring(a, b) 는 a 번째부터 b 번째 "앞"까지, 곧 b − a 글자를 잘라 낸다. "information" 에 번호를 붙이면 i(0) n(1) f(2) o(3) r(4) m(5) a(6) … 이라 2~5 는 f o r m. indexOf('o') 는 처음 나오는 o 의 첨자 3 이다(뒤의 o 는 상관없다). length() 는 11 이다. 자바 문자열 첨자는 0 부터, 끝 번호는 미포함 — 이 둘만 지키면 된다.

따라가기

  1. substring(2, 6) → 첨자 2 · 3 · 4 · 5 → form
  2. indexOf('o') → 3
  3. length() → 11. 출력 form 3 11

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 15번Java중첩 루프 횟수 세기
int cnt = 0; for (int i = 1; i <= 3; i++) for (int j = i; j <= 3; j++) cnt++; System.out.print(cnt);
정답6

— 6. i = 1 일 때 3번, 2 일 때 2번, 3 일 때 1번 도는 삼각형 반복이다.

원리 — 안쪽 반복의 시작이 j = i 라 바깥이 진행될수록 안쪽 횟수가 줄어든다. i = 1 이면 j 는 1 · 2 · 3, i = 2 면 2 · 3, i = 3 이면 3 만. 합은 3 + 2 + 1 = 6 이다. 일반화하면 n(n+1)/2 로 3 × 4 / 2 = 6. 안쪽 시작이 j = 1 이었다면 3 × 3 = 9, j = i + 1 이었다면 2 + 1 + 0 = 3 이다 — 안쪽 시작값 하나로 답이 갈린다.

따라가기

  1. i=1 → j 1 · 2 · 3 → 3회
  2. i=2 → j 2 · 3 → 2회 · i=3 → j 3 → 1회
  3. 6

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 20번Javai++ + ++i 복합
int i = 5; int r = i++ + ++i; System.out.print(r + " " + i);
정답12 7

— 12 7. i++ 는 5 를 내고 i 를 6 으로, ++i 는 i 를 7 로 만들고 7 을 낸다 — 5 + 7 = 12.

원리 — 식은 왼쪽부터 평가된다. i++ 는 "현재 값 5 를 쓰고 나서" i 를 6 으로 올린다. 다음 ++i 는 "먼저 7 로 올리고" 7 을 쓴다. 그래서 r = 5 + 7 = 12 이고 i 는 두 번 올라 7 이다. 후위 · 전위가 한 식에 섞이면 "값을 언제 읽나"를 순서대로 적어 가는 것이 유일한 방법이다.

따라가기

  1. i++ → 값 5, i = 6
  2. ++i → i = 7, 값 7
  3. r = 12, i = 7 → 출력 12 7

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 21번Java비트 연산 4종
int n = 5; System.out.print((n << 2) + " " + (n >> 1) + " " + (n & 3) + " " + (n ^ 3));
정답20 2 1 6

— 20 2 1 6. 5 = 101 을 왼쪽 2칸(×4), 오른쪽 1칸(÷2), 3 = 011 과 AND · XOR 한 값이다.

원리 — << k 는 2ᵏ 곱, >> k 는 2ᵏ 로 나눈 몫이다. 5 << 2 = 20, 5 >> 1 = 2. & 는 자리별 AND — 101 & 011 = 001 = 1. ^ 는 자리별 XOR(다르면 1) — 101 ^ 011 = 110 = 6. 2진수로 세로로 놓고 자리별로 보면 틀리지 않는다. n & 3 은 "4 로 나눈 나머지"와 같다는 것도 알아 두면 좋다.

따라가기

  1. 5 << 2 = 20, 5 >> 1 = 2
  2. 101 & 011 = 001 = 1, 101 ^ 011 = 110 = 6
  3. 출력 20 2 1 6

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 22번Java최댓값과 위치 탐색
int[] a = {4, 9, 2, 7}; int max = a[0], idx = 0; for (int i = 1; i < a.length; i++) if (a[i] > max) { max = a[i]; idx = i; } System.out.print(max + " " + idx);
정답9 1

— 9 1. 최댓값 9 와 그 위치(첨자) 1 이다.

원리 — 최댓값을 찾을 때 값만 갱신하지 않고 "어디서 찾았나"도 함께 저장하는 형이다. max 와 idx 를 첫 원소로 시작하고, 더 큰 값을 만나면 둘을 함께 갱신한다. 4 → 9(갱신, idx 1) → 2 → 7 순으로 보면 9 가 최대이고 첨자 1 이다. 같은 최댓값이 두 번 나오면 > 라 첫 번째 위치가 남고, >= 였다면 마지막 위치가 남는다 — 부등호 하나가 답을 바꾸는 변형 포인트다.

따라가기

  1. max 4, idx 0 → 9 > 4 → max 9, idx 1
  2. 2 · 7 → 갱신 없음
  3. 출력 9 1

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 23번Java재귀 피보나치
static int fib(int n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); } // main: System.out.print(fib(6));
정답8

— 8. fib(0) = 0, fib(1) = 1 로 시작하는 피보나치의 6번째 값이다.

원리 — fib(n) = fib(n−1) + fib(n−2), 바닥은 n <= 1 일 때 n 자신(0 또는 1)이다. 앞에서부터 표로 채우면 fib(0) 0, fib(1) 1, fib(2) 1, fib(3) 2, fib(4) 3, fib(5) 5, fib(6) 8. C 버전(T-C-10)은 f(1) = f(2) = 1 로 시작해 f(7) = 13 이었다 — 바닥 정의가 다르면 같은 n 이라도 값이 한 칸 밀린다. 코드의 바닥 조건을 반드시 읽는다.

따라가기

  1. 0 1 1 2 3 5 8
  2. fib(6) → 8

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 24번Java재귀로 문자열 역순 출력
static void rev(String s, int i) { if (i < 0) return; System.out.print(s.charAt(i)); rev(s, i - 1); } // main: rev("JAVA", 3);
정답AVAJ

— AVAJ. 첨자 3 부터 0 까지 글자를 하나씩 찍으며 재귀로 내려가 문자열이 뒤집힌다.

원리 — rev(s, i) 는 s 의 i 번 글자를 찍은 뒤 rev(s, i − 1) 을 부른다. 찍는 것이 재귀 호출 "앞"에 있으므로 호출 순서대로 찍힌다 — i = 3(A) → 2(V) → 1(A) → 0(J) → −1 에서 return. 만약 print 가 재귀 호출 "뒤"에 있었다면 되돌아오며 찍혀 JAVA 순서가 된다. 재귀에서 출력 위치(호출 앞 · 뒤)가 순서를 정한다.

따라가기

  1. i=3 A · i=2 V · i=1 A · i=0 J
  2. i=−1 → return
  3. 출력 AVAJ

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 25번Java2차원 배열 대각선 합
int[][] a = {{1,2,3},{4,5,6},{7,8,9}}; int s = 0; for (int i = 0; i < 3; i++) s += a[i][i]; System.out.print(s);
정답15

— 15. 대각선 원소 1 + 5 + 9 다.

원리 — a[i][i] 는 행 번호와 열 번호가 같은 칸, 곧 왼쪽 위에서 오른쪽 아래로 가는 주대각선이다. i = 0 → a[0][0] = 1, i = 1 → a[1][1] = 5, i = 2 → a[2][2] = 9. 합 15. 반대 대각선은 a[i][2 − i] 로 3 + 5 + 7 = 15 가 된다. 2차원 배열 문제는 표를 그리고 첨자가 가리키는 칸에 동그라미를 치면 끝난다. 반복 범위가 3 인 것은 행 · 열이 3 이기 때문이다.

따라가기

  1. a[0][0] 1 · a[1][1] 5 · a[2][2] 9
  2. 15

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 26번Javachar + int의 정체
char c = 'A'; System.out.print(c + 1); System.out.print(" "); System.out.print((char)(c + 1));
정답66 B

— 66 B. char + int 는 int 가 되어 66 이 찍히고, (char) 로 되돌리면 B 다.

원리 — 'A' 는 유니코드 65 다. c + 1 은 char 와 int 의 연산이라 결과가 int 66 이고, print 는 그것을 숫자로 찍는다. (char)(c + 1) 은 66 을 다시 문자로 해석해 'B' 다. "char 에 산술을 하면 int, 글자로 보려면 캐스팅" — 자바 문자 문제의 기본 규칙이다. 가운데 print(" ") 로 공백이 하나 들어간다.

따라가기

  1. c + 1 → 65 + 1 → int 66
  2. (char)(66) → B
  3. 출력 66 B

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 28번Javacatch 선택 규칙
try { Object o = null; o.toString(); } catch (NullPointerException e) { System.out.print("N"); } catch (Exception e) { System.out.print("E"); } finally { System.out.print("F"); }
정답NF

— NF. null 에 메서드를 부르면 NullPointerException 이고, 그것을 첫 catch 가 잡은 뒤 finally 가 돈다.

원리 — o 는 null 이라 가리키는 객체가 없다. o.toString() 은 없는 객체의 메서드를 부르는 것이라 NullPointerException 이 던져진다. catch 는 위에서부터 타입을 맞춰 보고 처음 맞는 하나만 실행한다 — NullPointerException 이 먼저 있어 N 이 찍히고, 아래의 Exception 은 부모 타입이라 잡을 수 있었지만 이미 잡혔으니 실행되지 않는다. finally 로 F. 순서를 바꿔 Exception 을 위에 두면 컴파일 오류가 난다(아래 catch 가 닿을 수 없어서).

따라가기

  1. null.toString() → NullPointerException
  2. 첫 catch 일치 → N (둘째 catch 는 건너뜀)
  3. finally → F. 출력 NF

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

훈련 Java · 29번Javasplit + 배열 접근
String s = "red,blue,green"; String[] arr = s.split(","); System.out.print(arr.length + arr[1] + arr[2].charAt(0));
정답3blueg

— 3blueg. 쉼표로 쪼개 3 조각이고, 두 번째 조각 blue, 세 번째 조각의 첫 글자 g 다.

원리 — split(",") 은 쉼표를 경계로 잘라 문자열 배열을 돌려준다 — ["red", "blue", "green"], 길이 3. arr[1] 은 두 번째 "blue", arr[2].charAt(0) 은 "green" 의 첫 글자 'g' 다. 출력식은 int + String 이라 3 이 먼저 문자열 "3" 이 되고, 그 뒤에 "blue" 와 'g' 가 붙는다. 첫 항이 숫자 3 이고 뒤가 문자열이라 덧셈이 아니라 연결이다.

따라가기

  1. split → 3조각 → 3
  2. arr[1] → blue, arr[2].charAt(0) → g
  3. 3 + "blue" + 'g' → 3blueg

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 JDK 17 실행 결과와 대조했다.

07백지 재현

왜 이걸 하나 — 아래를 안 보고 적을 수 있어야 이 장이 끝난다.