기능적으로 동일한 객체를 새로 만드는 대신 객체 하나를 재사용하는 것이 적절합니다.
🥺 문자열 객체
문자열 객체 를 예로 들어봅시다.
String name = "name";
String name1 = "name";
String name2 = new String("name"); // 불필요한 객체 예시
System.out.println(name == name1); // true
System.out.println(name1 == name2); // false
System.out.println(name1.equals(name2)); // true
저렇게 new 를 써서 객체를 생성하면 다른 객체가 생성되어 같지 않습니다.
하지만 new 를 쓰지 않고 객체를 생성 JVM 내부에서 동일한 리터럴값을 재사용하기 때문에 동일한 결과가 나올 것입니다.
🤨 static 팩토리 메서드
Boolean 클래스의 valueOf() 메서드를 예로 들었습니다.
Boolean 클래스에는 객체를 미리 생성하여 재사용하였음을 볼 수 있습니다.
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
Boolean result1 = Boolean.valueOf(true);
Boolean result2 = Boolean.valueOf("true");
Boolean result3 = new Boolean("true"); // deprecated 되었습니다..
Boolean result4 = new Boolean(true);
System.out.println(result1 == result2); // true
System.out.println(result2 == result3); // false
System.out.println(result3 == result1); // false
System.out.println(result4 == result3); // false
🥶 무거운 객체 ( 새로 생성하기에 비용이 많이 들고 성능에 영향이 주는 경우 )
정규표현식을 예로 들고 있습니다. 객체를 미리 컴파일 해놓고 사용하는 것이 바랍하다고 나옵니다.
static boolean isRomanNumeral(String s) {
return s.matches("^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
}
위으 코드를 보면 string의 객체를 받아 matches() 메서드 사용합니다. matches() 메서드는 Pattern 이라는 객체를 매번 생성하여 string 값을 컴파일 해주는 기능인데요, Pattern 객체를 매번 생성하는데 비용이 비싸므로 코드를 수정해야합니다.
Pattern 객체를 미리 생성하여 재사용을 합시다.
private static final Pattern PATTERN = Pattern.compile("^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
static boolean isRomanNumeral(String s) {
return PATTERN.matcher(s).matches();
}
하지만 이 코드도 문제가 있다고 봅니다. 만약에 isRomanNumeral() 을 사용하지 않는다고 하면 생성 된 Pattern 객체 는 불필요하므로 메모리상에 저장공간을 잡아먹게 된다.
지연 초기화 (lazy initialization) 를 사용하여 최적화를 할 수 있지만 추천하지 않는다고 합니다.
😭 오토 박싱 언박싱을 주의 하자
오토박싱은 primitive type 을 reference type 으로 자동 변환 되는것을 말합니다.
반대로 언박싱은 오토박싱의 반대겠죠??
박싱이 되면 내부적으로 추가 연산 작업이 일어납니다.
연산시간을 비교하며 알아보겠습니다.
👻 오토 박싱 연산
public static void main(String[] args) {
long t = System.currentTimeMillis();
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println("실행 시간: " + (System.currentTimeMillis() - t) + " ms");
}
// 실행 시간 : 3377 ms
👻 동일 타입 연산
public static void main(String[] args) {
long t = System.currentTimeMillis();
long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println("실행 시간: " + (System.currentTimeMillis() - t) + " ms") ;
}
// 실행 시간 : 715 ms
하지만 꼭 재사용하는 말은 아닙니다. 객체를 재사용하는데 오류가 있거나 성능에 저하된 경우 보안성에 문제가 있으면 객체를 재생성 해서 문제점을 해결해야겠죠??
'Book > Effective Java' 카테고리의 다른 글
[ITEM 10] equals는 일반 규약을 지켜 재정의하라 (0) | 2022.07.10 |
---|---|
[Book] ITEM 9) try-finally 대신 try-with-resource 를 사용하라 (0) | 2022.03.19 |
[Book] ITEM 8) finalizer 와 cleaner는 피하라 (0) | 2022.03.07 |
[Book] ITEM 7) 더이상 쓰지 않는 객체 레퍼런스는 없애자 (0) | 2022.03.04 |
[Book] ITEM 5) 리소스를 엮을 때는 의존성 주입을 선호하라 (0) | 2022.03.03 |
[Book] ITEM 4) private 생성자로 noninstantiability를 강제할 것 (0) | 2022.03.01 |
[Book] ITEM 3) private 생성자 또는 enum 타입을 사용해서 싱글톤으로 만들것 (0) | 2022.03.01 |
[Book] ITEM 2) 생성자의 매개변수가 많다면 빌더를 고려하라 (0) | 2022.02.26 |