protected void finalize() throws Throwable { }
@Overrideprotected void finalize() { }
protected void finalize() throws Throwable { }
@Overrideprotected void finalize() { }
public class StackTest {
// 저장공간 풀 레퍼런스 변수
private Object[] elements;
private int pointer = 0;
public StackTest() {
// 저장공간 풀 생성
elements = new Object[10];
}
public void push(Object o) {
elements[pointer++] = o;
}
public Object pop() {
return elements[--pointer];
}
}
public Map weakMap = new java.util.WeakHashMap();
/*
key 값을 "" 리터럴 스트링으로 생성하거나 기본자료형으로 생성하면
해당 값은 상수풀에 등록이 되어 있게 되고 gc 에 의해 지워지지 않기 때문에
WeakHashMap 의 만기참조시 삭제하는 전략을 사용할 수 없다
*/
// 외부에서 키를 관리한다고 가정한 변수
// private static String key = "a";
private static String key = new String("k"); // or // Integer key = new Integer(1);
public static void main(String[] args) throws InterruptedException {
WeakHashMapTest test = new WeakHashMapTest();
test.weakMap.put(key, 123);
test.someMethod();
key = null;
test.someMethod();
}
public void someMethod() throws InterruptedException {
for(int i =0 ; i<3; i++) {
Thread.sleep(500);
System.gc();
System.out.println(weakMap);
}
}
public class CallBackTest {
public static void main(String[] args) {
SomeClass sc = new SomeClass();
SomeClass.CB callback = new SomeClass.CB() {
@Override
public void cbMethod() {
System.out.println("i'm callback function !");
}
};
sc.someMethod(callback);
// 명시적 제거(callback = null;)를 하지 않을 경우 메모리 누수 발생
// 따라서 callback 을 담을 WeakHashMap 을 생성하고 callback 을 담는다. 이때 키에 해당하는 값은 변수에 담고
// someMethod의 사용이 끝나면 키를 제거한다
}
}
class SomeClass {
interface CB {
void cbMethod();
}
public void someMethod(CB callback) {
callback.cbMethod();
}
}
public static void main(String[] args) {
String a = new String("a"); // heap 에 매번 새롭게 생성
String b = "a"; // string constant pool 에 생성
System.out.println(a==b); // false
System.out.println(a.intern() == b); // true
System.out.println(a == b); // false
String c = a.intern();
System.out.println( a == c); // false
System.out.println( b == c); // true
System.out.println("----------------");
}
public class PersonClient {
public static void main(String[] args) {
Person p = new Person();
// 이렇게 하면 Calendar 객체가 1000 번 생성된다
for(int i=0; i< 1000; i++)
p.someMethod();
}
}
class Person {
public void someMethod() {
// getInstance 라는 메소드명 때문에 singleton 으로 오해할 수 있으나 호출시마다 매번 새로운 객체를 생성해 반환한다.
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
System.out.println(cal.get(Calendar.SECOND));
}
}
private static Calendar cal;
static {
cal = Calendar.getInstance();
}
/**
* 만들고 보니 date 객체도 계속 생성되긴 하지만
* cal 객체는 class 초기화시 한번만 생성된다.
* 다만 improveMethod 가 호출되지 않는다면 불필요한 cal 객체가 생긴샘이다.
* 이런 점을 보완하기 위해 improveMethod가 처음으로 호출될때만 객체를 생성시키는
* 초기화 지연 (lazy initialization) 기법을 사용할 수 있지만 구현이 복잡해지고 성능이 크게 향상되지도 않는다.
*/
public void improveMethod() {
cal.setTime(new Date());
System.out.println(cal.get(Calendar.SECOND));
}
public class UtilityClass {
/**
* 선언된 생성자가 없을때 기본생성자가 생성되기 때문에
* 기본 생성자가 자동 생성되지 못하도록 하여 객체 생성 방지
*
* private 선언으로 외부에서 접근 하지 못하도록
* reflection 으로 생성자를 실행 시키는 것을 방지하고
* 클래스 안에서 실수로 생성자를 호출하면 바로 알 수 있게 하기 위해
* Exception 을 throw 하도록 구현
*
*/
private UtilityClass() {
throw new AssertionError();
}
}
public class SingletonByPublicStaticVariable {위 코드는 클래스가 로드될때 초기화를 통하여 INSTANCE 변수에 객체가 생성되어 할당된다.
public static final SingletonByPublicStaticVariable INSTANCE = new SingletonByPublicStaticVariable();
private SingletonByPublicStaticVariable() {}
public void test() {
System.out.println("singleton by public static final variable");
}
}
public class SingletonByStaticBlock {static 초기화 블럭은 클래스가 로딩 될 때 최초 한번만 실행된다.
private static final SingletonByStaticBlock INSTANCE;
static {
INSTANCE = new SingletonByStaticBlock();
}
public static SingletonByStaticBlock getInstance() {
return INSTANCE;
}
public void test() {
System.out.println("singleton by static block");
}
}
public class SingletonByFactory {
private static final SingletonByFactory INSTANCE = new SingletonByFactory();
private SingletonByFactory() {
}
public static SingletonByFactory getInstance() {
return INSTANCE;
}
public void test() {
System.out.println("singleton by factory method");
}
}
public class SingletonByLazyInit {위 코드에 나타난 대로 객체는 getInstance 메소드 실행되고 객체가 생성 된 적이 없어야만 객체가 생성된다.
private static SingletonByLazyInit INSTANCE;
private SingletonByLazyInit() {}
public static SingletonByLazyInit getInstance() {
if(INSTANCE == null)
INSTANCE = new SingletonByLazyInit();
return INSTANCE;
}
public void test() {
System.out.println("singleton by lazy initialization");
}
}
public class SingletonByLazyInitThreadSafe {
private static SingletonByLazyInitThreadSafe INSTANCE;
private SingletonByLazyInitThreadSafe() {
}
public static synchronized SingletonByLazyInitThreadSafe getInstance() {
if(INSTANCE == null)
INSTANCE = new SingletonByLazyInitThreadSafe();
return INSTANCE;
}
public void test() {
System.out.println("singleton by thread safe initialize ");
}
}
public enum SingletonByEnum {
INSTANCE;
public void test() {
System.out.println("singleton by enum");
}
}
public class SingletonByIdiom {
private SingletonByIdiom() {
}
private static class Singleton {
private static final SingletonByIdiom INSTANCE = new SingletonByIdiom();
}
public static SingletonByIdiom getInstance() {
return Singleton.INSTANCE;
}
public void test() {
System.out.println("singleton by idiom");
}
}
public class Telescoping {
// 필수
private final int year;
private final int month;
private final int day;
// 선택
private final int hour;
private final int minute;
private final int second;
public Telescoping(int year, int month, int day) {
this(year, month, day, 0);
}
public Telescoping(int year, int month, int day, int hour) {
this(year, month, day, hour, 0, 0);
}
public Telescoping(int year, int month, int day, int hour, int minute) {
this(year, month, day, hour, minute, 0);
}
/**
* 인자가 많아 질수록 인자의 순서를 혼동하기 쉽고 사용하기 어려워짐
* @param year
* @param month
* @param day
* @param hour
* @param minute
* @param second
*/
public Telescoping(int year, int month, int day, int hour, int minute, int second) {
this.year = year;
this.month = month;
this.day = day;
this.hour = hour;
this.minute = minute;
this.second = second;
}
}
public class JavaBean {
// 필수 - final 선언 하지 못함
private int year;
private int month;
private int day;
// 선택 - final 선언 하지 못함
private int hour;
private int minute;
private int second;
public JavaBean() {
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}
public int getMinute() {
return minute;
}
public void setMinute(int minute) {
this.minute = minute;
}
public int getSecond() {
return second;
}
public void setSecond(int second) {
this.second = second;
}
public static void main(String[] args) {
/*
가독성은 향상되나 객체 생성을 1회에 끝낼 수 없고
멤버변수를 setter 로 추후에 셋팅이 가능해 일관성이 훼손될 수 있다
*/
JavaBean j = new JavaBean();
j.setYear(2017);
j.setMonth(4);
// ...
j.setSecond(17);
// 일관성 훼손 가능
// j.setYear(2018);
}
}
public class SomePerson {
// 필수인자 final 선언가능
private final String name;
private final Date birthday;
// 선택적인자 final 선언가능
private final String address;
private final int height;
private final String socialStanding;
private final Boolean isCyber;
/**
* builder 를 통해서 만 객체 생성할 수 있도록 private 선언
* 빌더 객체에서 실제 객체로 인자 복사
* @param builder
*/
private SomePerson(Builder builder) {
this.name = builder.name;
this.birthday = builder.birthday;
this.address = builder.address;
this.height = builder.height;
this.socialStanding = builder.socialStanding;
this.isCyber = builder.isCyber;
}
public static class Builder {
private String name;
private Date birthday;
private String address;
private int height;
private String socialStanding;
private Boolean isCyber;
/**
* 필수 인자 값은 생성자로 설정
* @param name
* @param birthday
*/
public Builder(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public Builder address(String address) {
this.address = address;
return this; // builder 객체 자신을 return
}
public Builder height(int height) {
this.height = height;
return this; // builder 객체 자신을 return
}
public Builder socialStanding(String socialStanding) {
this.socialStanding = socialStanding;
return this;
}
public Builder nomalPeople(String address, int height) {
this.address = address;
this.height = height;
return this;
}
public Builder rankPeople(String socialStanding, String address, int height) {
this.socialStanding = socialStanding;
this.address = address;
this.height = height;
return this;
}
public Builder cyberPeople(String address, int height) {
this.address = address;
this.height = height;
this.isCyber = Boolean.TRUE;
return this;
}
public Builder realPeople(String address, int height) {
this.address = address;
this.height = height;
this.isCyber = Boolean.FALSE;
return this;
}
// 최종적으로 변경 불가능 객체 생성
public SomePerson build() {
return new SomePerson(this);
}
}
}
public class MakePersonMain {
/**
* 점층적 생성자 패턴과 비교해봤을때
* 빌더패턴으로 객체를 생성시 인자의 이름이 있으므로 인자를 혼동하지 않는다. (자바빈 패턴 장점)
* 또한 빌더패턴으로 생성된 객체는 변경불가능한 객체로 생성이 가능하다. (점층적 생성자 패턴 장점)
* 그리고 빌더 패턴이 갖는 또 한가지의 장점은 선택적 인자를 필요한 만큼 포괄적으로 설정이 가능하며
* 여러 성격의 객체를 만들수 있다.
* 하나의 빌더로 여러 객체를 생성할 수 있다.
* @param args
*/
public static void main(String[] args) {
// 기본생성 (필수인자)
SomePerson person = new SomePerson.Builder("홍길동", new Date()).build();
// 선택적 인자 포함 생성
SomePerson personExt = new SomePerson.Builder("홍길동2", new Date())
.address("조선...").height(160).build();
// 일반인 - 선택적 인자 포괄 생성
SomePerson personExt2 = new SomePerson.Builder("홍길동", new Date())
.nomalPeople("한양", 170).build();
// 양반 - 객체 생성시 명시적으로 객체의 특성을 나타내도록 생성할 수 있다.
SomePerson nobility = new SomePerson.Builder("황희", new Date())
.rankPeople("영의정", "한양", 166).build();
// 하나의 빌더로 여러 객체 생성 가능, 어떤 필드의 값은 자동으로 채움(isCyber 필드와 같은)
SomePerson.Builder builder = new SomePerson.Builder("메트릭스-네오", new Date());
SomePerson cyberMan = builder.cyberPeople("뉴욕", 195).build();
SomePerson realMan = builder.realPeople("지하세계", 185).build();
}
}
일반 생성자로 객체를 생성할때 발생할 수 있는 문제점
1. class 의 생성자는 동일한 signature 별로 하나의 생성자만 가질수 있다.
(signature : 매개변수의 구성을 뜻하며 자료형, 갯수, 순서에 따라 달라진다.)
2.다른 초기화 기능이나 목적을 가진 객체를 생성하기 위해 생성자 매개변수의 순서를 바꾸는 방법으로 생성자를 overloading 한다.
3. 이렇게 만들어진 생성자를 사용하는 코드를 작성하려는 사람은 코드나 api 설명을 참조하지 않고서는 생성자가 만드려는 객체가 어떤 초기화 기능을 하는지 파악하지 못한다.
아래와 같은 class 가 있을때
public class Person {
private int age;
private int year;
private String name;
// 일반 생일자
public Person(int age, int year, String name) {
this.age = age;
this.year = year;
this.name = name;
}
// 빠른 생일자
public Person(String name, int age, int year) {
this.name = name;
this.age = age + 1;
this.year = year;
}
}
빠른 생일자 객체와 일반 생일자 객체를 생성하려고 할때 아래와 같은 코드로 생성할 수 있다.
class PersonMain {
public static void main(String[] args) {
Person man = new Person(21, 1990, "홍길동");
Person earlyBirthMan = new Person(“빠른 홍길동", 20, 1991);
}
}
하지만 Person class 코드를 보지 않고서는 빠른생일자와 일반생일자를 구별하여 객체를 생성할 수 없다.
(장점 1) 반면 정적 팩터리 메서드는 생성자의 class 명이 아닌 메소드 명을 명명할수 있다.
팩터리 메서드의 이름으로 생성하는 객체의 특성을 나타낼수도 있다.
또 같은 signaure 를 가진 생성자는 오로지 하나 이지만 팩터리 메서드로는 메서드명만 달리하면 그 제한이 없다.
따라서 같은 시그너처를 갖는 생성자를 여러개 정의할 필요가 있을때와
생성자마다 생성할 객체의 특성을 나타낼 필요가 있을때
정적 팩터리 메서드로 생성자를 대신하여 객체를 생성하는 것이 좋다.
아래는 정적 팩토리 메서드를 사용한 수정한 코드이다.
class ImprovedPerson {
private int age;
private int year;
private String name;
private ImprovedPerson(int age, int year, String name){
this.age = age;
this.year = year;
this.name = name;
}
// 일반생일자
public static ImprovedPerson getPerson(int age, int year, String name) {
return new ImprovedPerson(age, year, name);
}
// 빠른생일자
public static ImprovedPerson getEarlyBirthPerson(String name, int age, int year) {
return new ImprovedPerson(age + 1, year, name);
}
}
class PersonMain {(장점 2) 또한 생성자와는 달리 호출할 때마다 새로운 객체를 생성할 필요가 없다.
public static void main(String[] args) {
ImprovedPerson man = ImprovedPerson.getPerson(21, 1990, "홍길동");
ImprovedPerson earlyBirthMan = ImprovedPerson.getEarlyBirthPerson(20, 1991, "빠른 홍길동");
}
}
싱글턴 뿐만 아니라 몇몇의 만들어진 객체를 pool 형태로 캐시 해놓고 재사용도 가능하다.
class Sun {
private static Sun sun = new Sun();
// 생성자로 객체 생성 불가능하도록
private Sun(){}
// 항상 같은 객체 반환
public static Sun getInstance() {
return sun;
}
}
class Star {
// Sun class 에서 extends 하기 때문에 private 선언 할 수 없음
Star(){}
public static Star getSun() {
return Sun.getInstance();
}
}
class Sun extends Star {
private static Sun sun = new Sun();
// 생성자로 객체 생성 불가능하도록
private Sun(){
}
// 항상 같은 객체 반환
public static Sun getInstance() {
return sun;
}
}
interface Star {)
void hello();
}
public class StarBuilder {
private StarBuilder(){}
public static Star getSun() {
return Sun.getInstance();
}
public static Star getMoon() {
return Moon.getInstance();
}
}
class Sun implements Star {
private static Sun sun = new Sun();
// 생성자로 객체 생성 불가능하도록
private Sun() {
}
// 항상 같은 객체 반환
public static Sun getInstance() {
return sun;
}
@Override
public void hello() {
System.out.println("hi sun");
}
}
class Moon implements Star {
private static Moon moon = new Moon();
// 생성자로 객체 생성 불가능하도록
private Moon() {
}
// 항상 같은 객체 반환
public static Moon getInstance() {
return moon;
}
@Override
public void hello() {
System.out.println("hi moon");
}
}
public class Client {
public static void main(String[] args) {
Star s = StarBuilder.getSun();
Star m = StarBuilder.getMoon();
s.hello();
m.hello();
}
}
public static <E> Set<E> newSetFromMap(Map<E, Boolean> var0) {
return new Collections.SetFromMap(var0);
}
private static class SetFromMap<E> extends AbstractSet<E> implements Set<E>, Serializable {
private final Map<E, Boolean> m;
private transient Set<E> s;
private static final long serialVersionUID = 2454657854757543876L;
SetFromMap(Map<E, Boolean> var1) {
if(!var1.isEmpty()) {
throw new IllegalArgumentException("Map is non-empty");
} else {
this.m = var1;
this.s = var1.keySet();
}
}
………………
}
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> var0) {
Enum[] var1 = getUniverse(var0);
if(var1 == null) {
throw new ClassCastException(var0 + " not an enum");
} else {
return (EnumSet)(var1.length <= 64?new RegularEnumSet(var0, var1):new JumboEnumSet(var0, var1));
}
}
public interface Map<K,V> {
Map<String, Integer> m = new HashMap<String, Integer>();이렇게 일반적인 생성시 Map 의 형인자 자료형을 HashMap 생성시에도 작성해줘야 한다.
public static <K, V> HashMap<K, V> newHashMap() {
return new HashMap();
}
Map<String, List<List<List<String>>>> crazyVariable = Maps.newHashMap();
Map<String, List<List<List<String>>>> crazyVariable = new HashMap<>();
1. preferences... (settings...) Appearance & Behavior > Appearance - Window Options ✓ Show memory indicator Editor &...