
Java에서 Object의 존재 이유는 무엇인가
- 모든 객체를 담을 수 있는 존재
- Object는 자바의 모든 객체의 부모이기 때문에, Object는 모든 객체를 담을 수 있음.
- 이는 어떤 객체가 넘겨질지 모르는 상황에서 Object를 사용하면 아무거나 담을 수 있게 됨.
- 다형성을 제공해줌
- Object는 toString()등 여러 함수를 제공해줌
- OCP 원칙을 지킴
- println은 출력할 때 실제로 그 객체의 toString()을 호출하게 됨.
- Open: 새로운 클래스를 추가하고, toString() 을 오버라이딩해서 기능을 확장할 수 있음.
- Closed: 새로운 클래스를 추가해도 Object 와 toString() 을 사용하는 클라이언트 코드인 println 는 변경하지 않아도 된다.
클래스를 불변으로 설계하는 이유
- 캐시 안정성: 캐시는 데이터가 업데이트 되거나 하면 메로리를 다녀 와야하는데, 값이 불변이라면 메모리를 다녀올 필요가 없어 캐시의 일관성이 보장되며, 이에 관한 오류가 방지됨
- 멀티 스레드 안정성: 멀티 스레드는 값이 공유됨에 따라 발생할 수 있는 동기화 작업에서 문제가 발생할 수 있는데, 값이 불변이기 때문에 스레드간 동시성 문제가 발생하지 않음.
- 엔티티 값 타입: 값이 변하면 안되는 객체에 대해서 불변으로 정의하여 값이 바뀌어서 발생할 수 있는 문제를 줄일 수 있음.
- 참조 실수 방지: 실수로 다른데서 해당 객체를 잘못 참조해서 값을 바꿀려고 시도할 때 불변으로 선언되어 있어 이러한 실수를 방지할 수 있음.
불변 객체의 값을 변경하고 싶을 경우 어떻게 구현하면 되는가
- 변경 된 값을 가진 새로운 객체를 생성해서 리턴해 주면 됨.
- 예를 들어 String 도 불변 객체이며, String에 concat과 같은 함수를 사용하면 새로운 String 객체를 생성해서 반환함.
- 이런식으로 구현할 경우 불변의 이점은 있지만 객체가 하나 더 생성되게 되므로 메모리 효율이 떨어질 수도 있다는 단점이 존재하긴 함
다음 코드는 결과값은?
String a = "hello";
String b = "hello";
System.out.println(a == b);
- 정답은 true
- 자바에서 String 객체는 상수 풀(string pool)이라는 특별한 메커니즘을 사용하여 관리됨.
- 상수 풀은 프로그램이 실행되는 동안 동일한 문자열 상수를 여러 번 사용하더라도 메모리에서 하나의 객체만 생성되도록 보장하는 방법
- 즉, 동일한 문자열을 반복해서 사용하더라도 같은 메모리 위치를 참조하게 됩니다.
상수 풀 매커니즘이 존재하는 이유는 무엇이며 자바에서 어떤 객체들이 상수풀 매커니즘을 갖고 있는가?
- 불변 객체는 값이 변경되지 않기 때문에 같은 값의 객체를 여러 번 생성할 필요가 없어, 상수 풀 매커니즘을 통해 캐싱하여 최적화함.
- 상수풀 매커니즘을 사용하는 객체
- String
- Integer, Long(-128 ~ 127)
- Character(0~127): NUL(0), 0(48), A(65), a(97) 등이 포함
- String a = new String("hello"); 이런식으로 사용하는 경우 새로운 객체를 만들며 상수 풀을 사용하지 않음.
String에 대한 변경점이 많을 경우 무엇을 고려해야 하는지 설명하시오
- String은 불변객체라 변경해야할 일이 많을 경우에는 새로운 객체를 계속 생성해줘야 하기 때문에 성능적 관점에서 좋지 않을 수 있음.
- 따라서, 이 경우 StringBuilder의 사용을 고려할 수 있으며, StringBuilder는 String을 변경 가능한 형태로 다룰 수 있음.
- 혹은, 멀티스레드 환경에서 사용하고자 할 경우에는 동기화된 클래스인 StringBuffer를 사용하면 좋을 수 있음.
- 하지만, 대부분의 경우 String을 사용하더라도 자바가 자동으로 StringbBuilder를 사용하여 최적화 하는 기술이 존재
//기본 String 사용시
String result = "Hello" + " " + "World";
//자바가 자동으로 최적화한 코드
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
String result = sb.toString();
- 위와 같이 자바는 String 사용에 대해서 자동으로 최적화 해서 성능을 향상시킴
- 하지만, 이는 Java Compiler가 하는 부분이므로 컴파일 단계에서 할 수 있어야 하는 문제라, 컴파일 단계에서 판단이 되지 않는 경우에는 할 수가 없음.
- 예를 들어, for문의 경우 몇 번 돌아야 하는지는 동적으로 결정되며 이는 런타임 시 알 수 있으므로 컴파일 시점에는 알 수가 없어 이 때는 자바가 자동으로 최적화 할 수 없음.
- 따라서, for문 내에서 돌아가는 코드의 경우 직접 StringBuilder를 통해 최적화 해야함.
Wrapper 클래스의 장단점은?
- 장점
- Primitive 타입에 비해 객체지향의 장점을 살릴 수 있음.
- null인 것을 표현하고 싶을 때 null 값을 사용할 수 있음.
- 불변 객체
- 컬렉션 클래스에서 사용 가능
- 단점
- 객체이므로 Primitive 타입에 비해 메모리 사용양이 많고 성능이 떨어질 수 있음.
- 불변 객체이므로 값을 변경할 수 없어 값 변경시 새로운 객체를 생성해야 함
- null 값을 가질 수 있기 때문에 NullPointerException이 발생할 수 있음.
Random 함수 사용시 seed를 동일한 값으로 사용하는 케이스는 어떤 케이스일까?
- 일반적으로 Random 함수는 무작위한 값을 위해 사용하지만, 테스트 케이스에서 동일한 Input에 대해 동일한 Output이 나와야 하는 경우가 있음.
- 이런 테스트나 디버깅용으로 seed를 같은 값으로 테스트 해 동일한 결과가 나오는지 테스트해 볼 수 있음.
Enum이 String Constants에 비해 무엇이 더 나은가?
//String Constans 예제
String grade = ClassGrade.BASIC; // 올바른 값
// grade = ClassGrade.BAISIC; // 컴파일 오류 발생
grade = "PLATINUM"; // 존재하지 않는 값 사용 가능, 런타임 논리 오류 발생 가능
//ENUM 예제
public enum ClassGrade{
BASIC, PLATINUM
}
ClassGrade grade = ClassGrade.BASIC; // 올바른 값
// grade = "PLATINUM"; // 컴파일 오류 발생
// grade = ClassGrade.BAISIC; // 컴파일 오류 발생
- String Constants는 컴파일시 문제를 찾을 수 있다는 것 까진 해결해줌
- 하지만 String value에 얘기치 못한 값이 들어가거나, 값으로 설정한 String도 결국 오타가 날 수 있다는 문제가 있음.
- 반면에 ENUM은 이러한 문제를 해결하면서, 클래스처럼 동작하므로 메소드 추가와 같은 부가 기능도 가능함.
- 관련된 상수를 그룹화하여 응집도가 높아지고 가독성이 좋아짐
Type Safe Enum Pattern 의 기존 방식과 자바에서 제공하는 enum 타입을 비교하시오
//type safer enum pattern 기존 방식
public class ClassGrade {
public static final ClassGrade BASIC = new ClassGrade();
public static final ClassGrade GOLD = new ClassGrade();
public static final ClassGrade DIAMOND = new ClassGrade();
// private 생성자 추가
private ClassGrade() {}
}
//자바 enum type
public enum Grade {
BASIC(10), GOLD(20), DIAMOND(30);
}
- 기존 방식은 복잡한 코드를 작성해야 함
- 기존 방식은 private으로 생성자를 꼭 선언해주어 ClassGrade PLATINUM 이런식으로 자기 맘대로 객체를 생성할 수 없게 막아야함.
- 아래 자바 enum type은 위와 완전 동일한 코드
- enum 타입은 객체 생성도 막혀 있음.
- enum은 아래와 같이 부가 기능을 추가해서 사용 가능.
public enum Grade {
BASIC(10), GOLD(20), DIAMOND(30);
private final int discountPercent;
Grade(int discountPercent) {
this.discountPercent = discountPercent;
}
public int getDiscountPercent() {
return discountPercent;
}
// 추가: 할인 계산 메서드
public int discount(int price) {
return price * discountPercent / 100;
}
}
GMT, UTC, DST에 대해 설명하시오
- GMT (그리니치 평균시, Greenwich Mean Time)
- 처음 세계 시간을 만들 때 영국 런던에 있는 그리니치 천문대를 기준으로 했다. 태양이 그리니치 천문대를 통과할 때를 정오로 한다.
- UTC(협정 세계시, Universal Time Coordinated)
- 역사적으로 GMT가 국제적인 시간 표준으로 사용되었고, UTC가 나중에 이를 대체하기 위해 도입되었다.
- 일반적으로 UTC와 GMT는 차이가 거의 없으나 매우 정밀한 시간 측정과 국제적인 표준에 관해서는 UTC가 선호됨.
- DST(일광 절약 시간, Daylight Saving Time)
- 보통 3월에서 10월은 태양이 일찍 뜨고, 나머지는 태양이 상대적으로 늦게 뜬다.
- 시간도 여기에 맞추어 1시간 앞당기거 나 늦추는 제도를 일광 절약 시간제 또는 썸머타임이라 한다.
- 일광 절약 시간은 국가나 지역에 따라 적용 여부와 시작 및 종료 날짜가 다르다.
- 이로 인해 날짜와 시간 계산 시 1시간의 오차가 발생할 수 있으며, 이를 정확히 계산하는 것은 복잡 하다.
- 줄여서 DST는 각 나라마다 다르지만 보통 3월 중순 ~ 11월 초 정도까지 시행된다.
- 참고로 대한민국에서는 1988년 이후로는 시행하지 않는다