
아이템 6) 불필요한 객체 생성을 피하라
String s = new String("apple"); // 따라 하지 말 것!
String s = "apple"; // 개선된 코드
ex) 주어진 문자열이 유효한 로마 숫자인지를 확인하는 메서드 작성
package step2;
// item6 : 불필요한 객체 생성을 피하라
public class item6_1 {
// 성능을 훨씬 더 끌어올릴 수 있다
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})$");
}
}
package step2;
// item6 : 불필요한 객체 생성을 피하라
import java.util.regex.Pattern;
public class item6_2 {
// 값비싼 객체를 재사용해 성능을 개선한다.
public static final Pattern ROMAN = 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 ROMAN.matcher(s).matches();
}
}
이렇게 개선하면 isRomanNumeral이 빈번하게 호출되는 상황에서 성능을 끌어올릴 수있다.
Boxing Type 대신 Primitive Type을 권장한다.
package step2;
// item6 : 불필요한 객체 생성을 피하라
public class item6_3 {
// Boxing Type
private static long sum1(){
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
return sum;
}
// Primitive Type
private static long sum2(){
long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
return sum;
}
}
아이템 7) 다 쓴 객체 참조를 해제하라
package step2;
// item7 : 다 쓴 객체 참조를 해제하라
import java.util.EmptyStackException;
public class Java_7 {
// 제대로 구현한 pop 메서드
public Object pop(){
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
}
하지만 모든 객체를 다 사용하자마자 일일히 null 처리 시 지저분해질 수 있다.
객체 참조를 null처리하는 일은 예외적인 경우여야 한다.
핵심정리
- 메모리 누수는 겉으로 잘 드러나지 않아 시스템에 수년간 잠복하는 사례가 있다.
- 누수는 리뷰코드나 힙 프로파일러 같은 디버깅 도구를 동원해야만 발견되기도 한다.
- 예방법을 익혀두는게 중요하다.
item 8) finalizer와 cleaner 사용을 피하라
Java의 2가지 객체 소멸자
- finalizer
- cleaner
finalizer의 특징
- 예측불가
- 상황에 따라 위험할 수 있어 일반적으로 불필요하다.
cleaner의 특징
- finalizer의 대안으로 finalizer보다는 덜 위험하다.
- 예측불가
- 느리고, 일반적으로 불필요하다.
finalizer와 cleaner가 할 수 없는 작업과 문제
- 제때 실행되어야 하는 작업
- 상태를 영구적으로 수정하는 작업
- 심각한 성능 문제
finalizer를 사용한 클래스는 finalizer 공격에 노출되어 심각한 보안문제를 일으킬 수 있다.
- 객체 생성을 막으려면 생성자에서 예외를 던지는 것만으로도 충분하지만 finalizer가 있다면 그렇지 않다.
- 해결방안 : final이 아닌 클래스를 finalizer 공격으로부터 방어하려면 아무 일도 하지 않는 finalizer 메서드를 만들고 final로 선언한다.
finalizer와 cleaner를 대신해줄 방법 : AutoCloseable
- AutoCloseable를 구현하고 크라이언트에서 인스턴스를 다 사용하고 나면 close 메서드를 호출하면 된다.
핵심정리
- cleaner(자바 8까지는 finalizer)는 안전망 역할이나 중요하지 않은 네이티브 자원 회수용으로만 사용
- 사용 시 불확실성과 성능저하에 주의해야 한다.
item9) try-finally 보다는 try-with-resources를 사용하라
자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야하는 자원이 많다. ex) InputStream, OutputStream, java.sql.Connection 등
자원이 제대로 닫힘을 보장하는 수단으로 try-finally가 쓰이지만 자원을 회수하는 최선의 방법이 아니다.
package step2;
// item9 : try-finally 보다는 try-with-resources를 사용하라
import java.io.*;
public class Java_9_1 {
// 자원이 둘 이상이면 try-finally 방식은 지저분하다.
static void copy(String src, String dst) throws IOException {
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dst);
try {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
out.close();
}
} finally {
in.close();
}
}
}
package step2;
// item9 : try-finally 보다는 try-with-resources를 사용하라
import java.io.*;
public class Java_9_2 {
// try-with-resources 적용
static void copy(String src, String dst) throws IOException {
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst)) {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
}
}
}
훨씬 짧고 읽기 수월하며 추후 문제를 찾기에도 좋다.
핵심정리
- 꼭 회수해야하는 자원을 다룰 때는 try-with-resources를 사용
- 코드는 더 짧고 분명해지고 만들어지는 예외 정보도 훨씬 유용하다.
- finally가 필요한 상황이면 try-with-resources를 사용
** 이미지출처) Google yes24