

public class Settings1 {
private static Settings1 instance;
private Settings1() { }
public static Settings1 getInstance() {
if (instance == null) {
instance = new Settings1();
}
return instance;
}
}
1. 생성자를 private으로 만든 이유
2. getInstance() 메소드를 static으로 선언한 이유?
3. getInstance()가 멀티쓰레드 환경에서 안전하지 않은 이유?
if (instance == null) {
instance = new Settings1();
}
/**
* synchronized 사용해서 동기화 처리
*/
public class Settings2 {
private static Settings2 instance;
private Settings2() { }
public static synchronized Settings2 getInstance() {
if (instance == null) {
instance = new Settings2();
}
return instance;
}
}
1. 자바의 동기화 블럭 처리 방법은?
자바에서 동기화를 위해 synchronized 키워드를 사용한다. 동기화는 멀티쓰레드 환경에서 공유 자원의 상태를 보호하기 위해 사용된다.
여기서는 메소드 동기화 방법을 사용하였다.
public static synchronized Settings2 getInstance() {
if (instance == null) {
instance = new Settings2();
}
2. getInstance() 메소드 동기화 시 사용하는 락(lock)은 인스턴스의 락인가 클래스의 락인가? 그 이유는?
1) 사용하는 락은 "클래스의 락"이다.
2) 이유
private static final Settings INSTANCE = new Settings();
private Settings() {}
public static Settings getInstance() {
return INSTANCE;
}
1) 이른 초기화(Eager Initialization)가 단점이 될 수 있는 이유
이른 초기화 방식은 클래스가 로드되는 시점에서 싱글톤 인스턴스를 생성한다. 이러한 방식에는 몇 가지 단점이 존재한다.
리소스 낭비
싱글톤 인스턴스가 사용되지 않더라도 클래스가 로드되기만 하면 객체가 생성된다.
예를 들어, 인스턴스가 무겁거나 초기화 과정에서 많은 리소스를 사용하는 경우, 사용되지 않을 가능성이 있는 객체를 미리 생성하는 것은 비효율적이다.
유연성 부족
이른 초기화 방식은 싱글톤 인스턴스를 초기화할 때 외부적인 조건(환경 변수, 설정 파일 등)에 따라 동적으로 초기화 로직을 변경하기 어렵다. 예를 들어, 인스턴스 생성 시점에서 설정값에 따라 초기화가 달라져야 하는 경우 문제가 될 수 있다.
초기화 시점의 제어 부족
클래스가 로드될 때 즉시 초기화되므로, 초기화 타이밍을 더 세밀히 제어해야 하는 요구사항에 대응하기 어렵다.
특히, 특정 조건이 충족되기 전에는 객체를 생성하지 않아야 하는 경우 적합하지 않다.
2) 생성자에서 Checked 예외를 던질 경우 이 코드를 변경하는 방법
싱글톤 패턴에서 private 생성자가 Checked 예외를 던진다면, 이 코드를 변경하려면 Lazy Initialization 방식으로 전환해야 한다. 이 방식에서는 인스턴스가 필요할 때만 초기화되며, 초기화 과정에서 예외 처리가 가능하다.
public class Settings {
private static Settings instance;
private Settings() throws Exception {
// Checked 예외를 던질 수 있는 초기화 코드
if (/* 특정 조건 */) {
throw new Exception("Initialization failed");
}
}
public static Settings getInstance() throws Exception {
if (instance == null) {
synchronized (Settings.class) { // Thread-safe 처리
if (instance == null) {
instance = new Settings();
}
}
}
return instance;
}
}
getInstance() 메서드가 호출될 때 인스턴스가 생성된다. 이를 통해 필요하지 않은 경우 인스턴스를 생성하지 않는다.
생성자에서 Checked 예외를 던질 수 있으므로, getInstance() 메서드가 예외를 던지도록 선언(throws Exception)합니다.
멀티스레드 환경에서 싱글톤이 안전하게 초기화될 수 있도록 synchronized 블록과 이중 체크(Double-Checked Locking)를 사용한다.
/**
* double checked locking
*/
public class Settings3 {
private static volatile Settings3 instance;
private Settings3() { }
public static Settings3 getInstance() {
if (instance == null) {
synchronized (Settings3.class) {
if (instance == null) {
instance = new Settings3();
}
}
}
return instance;
}
1. double check locking이라고 부르는 이유?
2. instacne 변수는 어떻게 정의해야 하는가? 그 이유는?
/**
* static inner 클래스 홀더
*/
public class Settings4 {
private Settings4() { }
private static class Settings4Holder {
private static final Settings4 INSTANCE = new Settings4();
}
public static Settings4 getInstance() {
return Settings4Holder.INSTANCE;
}
}
1. 이 방법은 static final를 썼는데도 왜 지연 초기화 (lazy initialization)라고 볼 수 있는가?
Settings settings = Settings.getInstance();
Constructor<Settings> declaredConstructor = Settings.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Settings settings1 = declaredConstructor.newInstance();
System.out.println(settings == settings1);
1. 리플렉션에 대해 설명하세요.
2. setAccessible(true)를 사용하는 이유는?
public class Settings implements Serializable
...
Settings settings = Settings.getInstance();
Settings settings1 = null;
try (ObjectOutput out = new ObjectOutputStream(new FileOutputStream("settings.obj"))) {
out.writeObject(settings);
}
try (ObjectInput in = new ObjectInputStream(new FileInputStream("settings.obj"))) {
settings1 = (Settings) in.readObject();
}
System.out.println(settings == settings1);
1. 자바의 직렬화 & 역직렬화에 대해 설명하세요.
직렬화&역직렬화 : 객체를 바이트 스트림으로 변환(직렬화)하여 저장하거나 전송하고, 다시 객체로 복원(역직렬화)하는 과정이다.
2. Serializable란 무엇이며 왜 쓰는가?
Serializable은 객체를 직렬화하여 바이트 스트림으로 변환하거나, 이를 다시 역직렬화하여 객체로 복원할 수 있도록 하는 인터페이스이다.
객체를 저장, 전송, 복원하기 위해 사용되며, 네트워크 통신과 데이터 유지 등에 유용하다.
3. try-resource 블럭에 대해 설명하세요.
자원을 사용하는 코드를 간결하고 안전하게 작성하기 위한 기능으로, 자원 자동 해제를 지원한다.
/**
* Enum을 사용해서 싱글톤 만들기
*/
public enum Settings5 {
INSTANCE;
}
1. enum 타입의 인스턴스를 리팩토링을 만들 수 있는가?
enum 타입은 본질적으로 상수 집합이며, 자바에서 싱글톤 패턴을 구현할 때 매우 적합한 방식으로 간주된다.
그러나 리팩토링하여 새로운 인스턴스를 생성하거나 기존의 INSTANCE를 변경할 수 없다.
2. enum으로 싱글톤 타입을 구현할 때의 단점은?
enum은 Java에서 기본적으로 상수 집합의 용도로 설계되었기 때문에, 일반 클래스와 달리 상속이 불가능하다.
싱글톤 확장이나 복잡한 초기화 로직 구현에는 제약이 존재한다.
일부 프레임워크(특히 구버전)에서는 enum 타입의 싱글톤 사용을 지원하지 않을 수 있다.
복잡한 초기화 로직이 필요한 경우, enum 생성자에서는 제한적인 초기화만 가능하다.
ex) 초기화 과정에서 Checked 예외를 처리할 수 없다.
3. 직렬화 & 역직렬화 시에 별도로 구현해야 하는 메소드가 있는가?
필요없다!
enum은 직렬화 시 자동으로 단일 인스턴스를 보장하므로, 별도로 readResolve() 메서드를 구현할 필요가 없다.
이는 ObjectOutputStream과 ObjectInputStream이 enum의 직렬화와 역직렬화를 자동으로 처리하기 때문이다.
싱글톤 패턴은 애플리케이션 전체에서 하나의 인스턴스를 공유해야 할 때 유용하다. 이는 자원 절약, 상태 공유, 관리 간소화를 위해 실무에서 여러 방식으로 활용된다.
singleton이다.@Component
public class MyService {
public void performAction() {
System.out.println("Action performed");
}
}
@Component로 등록된 빈은 기본적으로 싱글톤 스코프로 관리된다.java.lang.Runtime 클래스java.lang.Runtime 클래스는 JVM의 런타임 환경을 제어할 수 있는 유일한 싱글톤 클래스다.Runtime.getRuntime() 메서드를 통해 인스턴스를 가져옴.public class Main {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
System.out.println("Available Processors: " + runtime.availableProcessors());
System.out.println("Free Memory: " + runtime.freeMemory());
try {
runtime.exec("notepad");
} catch (IOException e) {
e.printStackTrace();
}
}
}
runtime.exit()).Runtime 객체를 공유.예제:
public class ConfigurationBuilder {
private static final ConfigurationBuilder INSTANCE = new ConfigurationBuilder();
private ConfigurationBuilder() { }
public static ConfigurationBuilder getInstance() {
return INSTANCE;
}
public Configuration build() {
// 복잡한 설정 로직
return new Configuration();
}
}
예제:
public class DatabaseFacade {
private static final DatabaseFacade INSTANCE = new DatabaseFacade();
private DatabaseFacade() { }
public static DatabaseFacade getInstance() {
return INSTANCE;
}
public void executeQuery(String query) {
// Query 실행 로직
}
}
예제:
public class ShapeFactory {
private static final ShapeFactory INSTANCE = new ShapeFactory();
private ShapeFactory() { }
public static ShapeFactory getInstance() {
return INSTANCE;
}
public Shape createCircle() {
return new Circle();
}
}
Stateless 설계로 유지하는 것이 안전. 관련 이유)
만약 싱글톤이 "상태를 가지는 필드"(예: 사용자 입력값, 계산 결과 등)를 가지고 있다면, 여러 스레드가 동시에 같은 객체를 사용할 때 문제가 생길 수 있다.
테스트 어려움
메모리 누수