다음 코드의 출력을 예측하라.
static String test() {
StringBuilder sb = new StringBuilder();
try {
sb.append("A");
throw new RuntimeException();
} catch (RuntimeException e) {
sb.append("B");
return sb.toString();
} finally {
sb.append("C");
System.out.println("finally: " + sb);
}
}
System.out.println("반환: " + test());// 출력:
// finally: ABC
// 반환: AB
//
// 해설: catch에서 return sb.toString()이 평가되어 "AB"가 반환값으로 확정된다.
// 그 뒤 finally가 실행되어 sb에 "C"가 붙지만(sb는 참조이므로 객체 자체는 변함),
// 이미 확정된 반환값은 "AB"라는 별도의 String이므로 영향을 받지 않는다.MemberException(abstract, RuntimeException 상속, String code 필드)을 부모로, MemberNotFoundException(long id)(코드 "M404")과 DuplicateEmailException(String email)(코드 "M409")을 만들어라.
두 예외를 던지고 catch (MemberException e) 하나로 잡아 code: message 형식으로 출력하라.
abstract class MemberException extends RuntimeException {
private final String code;
protected MemberException(String code, String message) {
super(message);
this.code = code;
}
String getCode() { return code; }
}
class MemberNotFoundException extends MemberException {
MemberNotFoundException(long id) { super("M404", "회원 없음: id=" + id); }
}
class DuplicateEmailException extends MemberException {
DuplicateEmailException(String email) { super("M409", "이미 가입된 이메일: " + email); }
}
public class Main {
public static void main(String[] args) {
Runnable[] actions = {
() -> { throw new MemberNotFoundException(42); },
() -> { throw new DuplicateEmailException("[email protected]"); }
};
for (Runnable r : actions) {
try {
r.run();
} catch (MemberException e) {
System.out.println(e.getCode() + ": " + e.getMessage());
}
}
// 출력:
// M404: 회원 없음: id=42
// M409: 이미 가입된 이메일: [email protected]
}
}Connection과 Statement라는 두 AutoCloseable 클래스를 만들어 열림/닫힘 시 이름을 출력하게 하고, try-with-resources로 Connection → Statement 순서로 열어 본문에서 "쿼리 실행"을 출력하라. 전체 출력 순서를 확인하고, 왜 그 순서인지 한 줄로 설명하라.
class Connection implements AutoCloseable {
Connection() { System.out.println("Connection 열림"); }
@Override public void close() { System.out.println("Connection 닫힘"); }
}
class Statement implements AutoCloseable {
Statement(Connection c) { System.out.println("Statement 열림"); }
@Override public void close() { System.out.println("Statement 닫힘"); }
}
public class Main {
public static void main(String[] args) {
try (Connection conn = new Connection();
Statement stmt = new Statement(conn)) {
System.out.println("쿼리 실행");
}
// 출력:
// Connection 열림
// Statement 열림
// 쿼리 실행
// Statement 닫힘
// Connection 닫힘
//
// 이유: Statement는 Connection에 의존하므로 의존하는 쪽(나중에 연 것)을 먼저 닫아야
// 닫힌 커넥션 위에서 Statement를 정리하는 상황을 피할 수 있다. 선언 역순.
}
}