Item 5, 6, 7, 8, 9
- 정적 유틸리티로 사용하는 경우
public class SpellChecker{
private final Lexicon dictionary = ...;
private SpellChecker() {}
public static boolean isValid(String word) { ... }
public static List<String> suggestions (String typo) { ... }
}- 싱글턴을 잘못 사용하는 경우
public class SpellChecker{
private final Lexicon dictionary = ...;
private SpellChecker(...) {}
public static SpellChecker INSTANCE = new SpellChecker(...);
public boolean isValid(String word) { ... }
public List<String> suggestions (String typo) { ... }
}- 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식
- Constructor Injection
- Method(Setter) Injection
- Field Injection
public class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary){
this.dictionary = Objects.requireNonNull(dictionary)
}
public boolean isValid(String word) { ... }
public List<String> suggestions(String typo) { ... }
}public class SpellChecker {
private Lexicon dictionary;
public SpellChecker(){ ... }
public void setDictionary(Lexicon dictionary){
this.dictionary = dictionary;
}
public boolean isValid(String word) { ... }
public List<String> suggestions(String typo) { ... }
}public class SpellChecker {
@Inject private Lexicon dictionary;
public SpellChecker(){ ... }
public boolean isValid(String word) { ... }
public List<String> suggestions(String typo) { ... }
}- 상위 클래스에서 객체를 생성하는 인터페이스를 정의하고, 하위 클래스에서 인스턴스를 생성하도록 하는 방식
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
* @return a result
*/
T get();
}Mosaic create(Supplier<? extends Tile> tileFactory) {
Tile tile = tileFactory.get();
.
.
.
}String s = new String("bikini") // 따라 하지 말 것!
String s = "bikini"- new String()을 할 경우엔 새로운 인스턴스를 만든다.
- "bikini"의 경우 하나의 인스턴스를 사용한다.
출처 : http://www.journaldev.com/797/what-is-java-string-pool
public Boolean(String s) {
this(parseBoolean(s));
} public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(String s) {
return toBoolean(s) ? TRUE : FALSE;
}public class UsingKeySet {
public static void main(String[] args) {
Map<String, Integer> menu = new HashMap<>();
menu.put("Burger", 8);
menu.put("Pizza", 9);
Set<String> names1 = menu.keySet();
Set<String> names2 = menu.keySet();
names1.remove("Burger");
System.out.println(names2.size()); // 1
System.out.println(menu.size()); // 1
}
}private static long sum(){
Long sum = 0L;
for (long i = 0; i <= Integer.MAX_VALUE; i++)
sum += i; // 값을 더 할때 마다 Long 객체를 만든다.
return sum;
}public class RomanNumerals {
private 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 isRomanNumeralFast(String s) {
return ROMAN.matcher(s).matches();
}
}public Object pop() {
if (size == 0)
throw new EmptyStackException();
retrun elements[--size];
}public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;
retrun result;
}new LinkedHashMap<String, String>() {
@Override
protected boolean removeEldestEntry(Map.Entry<String,String> eldest) {
retrun condition; //조건을 만족하면 가장 오래된 기록 제거
}
}- System.gc(), System.Finalization() : 가능성 🆙 하지만 여전히 보장 ❌
- Runtime.runFinalizersOnExit() : 보장 ⭕ 하지만 결함이 존재
- finalizer에서 동작해야 하는 작업을 수행하지 못해서 OutOfMemoryError를 발생시킬 수 있음
- try-with-resources : 12ns
- finalizer : 550ns (50배나 느린 성능)
- cleaner : 66ns (5배 느린 성능)
- A클래스를 공격하려는 B 클래스가 A클래스를 상속받는다.
- B클래스는 인스턴스 생성 혹은 직렬화 과정에서 예외가 발생하면, finalizer 가 수행
- 자신을 정적 인스턴스에 할당하며 가비지 컬렉터가 수집 못하게 막음
- 이렇게 만들어진 객체로 허용되지 않았을 작업을 수행
- final class가 아닐 경우엔 finalize를 final로 선언해서 방지
자원의 소유자가 close 메서드를 호출하지 않는 것에 대비한 안전망 역할
ex) FileInputStream, FileOutputStream, ThreadPoolExecutor
일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체
네이티브 객체의 경우 일반적인 객체가 아니라 GC가 그 존재를 몰라서 발생
자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어지기도 한다.
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
}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();
}
}/**
* @author Josh Bloch
* @since 1.7
*/
public interface AutoCloseable {
void close() throws Exception;
}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);
}
}static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine(); //첫 번째 오류 발생!
} finally {
br.close(); //두 번째 오류 발생!
}
}두 번째로 발생한 오류만 보임
public class MyResource implements AutoCloseable {
public void doSomething() {
System.out.println("Do something");
throw new FirstError();
}
@Override
public void close() {
System.out.println("Close My Resource");
throw new SecondError();
}
}public class TryWithResourceError {
public static void main(String[] args) {
try (MyResource myResource = new MyResource()){
myResource.doSomething();
}
}
}Exception in thread "main" item09.error.FirstError
at item09.error.MyResource.doSomething(MyResource.java:7)
at item09.error.TryWithResourceError.main(TryWithResourceError.java:6)
Suppressed: item09.error.SecondError
at item09.error.MyResource.close(MyResource.java:13)
at item09.error.TryWithResourceError.main(TryWithResourceError.java:5)