- 1_생성자
package kr.co.ioacademy; //iocademy 윤찬식 강사님 // 객체를 생성하는 방법. // 1. 생성자 // a. 생성자 메소드의 이름은 클래스의 이름과 같다. // b. 생성자의 오버로딩은 한계가 있다. class RandomIntGenerator { final int min; final int max; public RandomIntGenerator(int min, int max) { this.min = min; this.max = max; } public RandomIntGenerator(int min) { this.min = min; this.max = Integer.MAX_VALUE; } // 1. 동일한 인자를 받는 다른 형태의 객체 생성 방법을 제공할 수 없다. // public RandomIntGenerator(int max) {} } public class Example1 { public static void main(String[] args) { // 2. 객체를 생성하는 코드를 통해서 어떤 객체가 생성되는지를 절대 알 수 없다. RandomIntGenerator obj1 = new RandomIntGenerator(0, 100); RandomIntGenerator obj2 = new RandomIntGenerator(0); } }
- 2_정적 팩토리 메소드(static factory method)
package kr.co.ioacademy; //iocademy 윤찬식 강사님 // 2. 객체를 생성하는 방법 2. // : 정적 팩토리 메소드(static factory method) // 정의 : 객체를 생성할 때 생성자가 아닌 정적 메소드를 통해 생성하자. // 단점 // 1. 상속을 통한 기능 변경이 불가능하다. (테스트가 힘들다) // 2. 이름을 잘 지어야 한다. class RandomIntGenerator { final int min; final int max; // private 이므로 외부에서 접근이 불가능하다. private RandomIntGenerator(int min, int max) { this.min = min; this.max = max; } // 객체를 생성하는 다양한 정적 메소드를 제공하자. // 장점 1. 정적 팩토리 메소드는 결국 메소드 이므로 이름에 제한이 없다. public static RandomIntGenerator between(int min, int max) { return new RandomIntGenerator(min, max); } public static RandomIntGenerator biggerThan(int min) { return new RandomIntGenerator(min, Integer.MAX_VALUE); } public static RandomIntGenerator smallerThan(int max) { return new RandomIntGenerator(Integer.MIN_VALUE, max); } // 장점 3. 생성자처럼 매번 생성할 필요가 없다. // Integer.valueOf(3), Long.valueOf(3) private static final RandomIntGenerator INSTANCE = new RandomIntGenerator(Integer.MIN_VALUE, Integer.MAX_VALUE); public static RandomIntGenerator getInstance() { return INSTANCE; } } public class Example2 { public static void main(String[] args) { // 장점 2. 라이브러리 사용자들은 객체가 어떤 정책을 가지고 생성되는지 // 이름을 통해 쉽게 이해할 수 있다. RandomIntGenerator obj1 = RandomIntGenerator.between(0, 100); RandomIntGenerator obj2 = RandomIntGenerator.smallerThan(100); RandomIntGenerator obj3 = RandomIntGenerator.biggerThan(0); } }
3_불필요한 객체 생성
: Junit test 결과 testLong2() 는 오토박싱을 통해 객체를 계속 생성한다. -> testLong()에 비해 현저히 느리다.package kr.co.ioacademy; //iocademy 윤찬식 강사님 import org.junit.Test; public class Example3 { public static void main(String[] args) { // 불필요한 객체가 생성된다. String s1 = new String("hello"); String s2 = "hello"; // 같은 가상 머신에서 실행되는 모든 코드가 해당 객체를 재사용한다. // 불변 객체 ( Immutable object ): 불변객체는 생성 후에는 변하지 않는 object 로, 언제든 재사용이 가능 // 불변객체의 불필요한 객체 생성을 막으려면 생성자보다는 static 팩토리 메소드를 사용하는 것이 좋습니다. ( Factory 에서 관리 ) Boolean b1 = new Boolean(true); Boolean b2 = Boolean.TRUE; } @Test public void testLong2() { Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; ++i) sum += i; System.out.println(sum); } @Test public void testLong() { long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; ++i) sum += i; System.out.println(sum); } }
4_싱글톤(Singleton)
package kr.co.ioacademy; //iocademy 윤찬식 강사님 // 싱글톤(Singleton) // 개념 : 하나의 인스턴스만 생성되는 클래스 // 장점 : 어디에서든 동일한 방법으로 동일한 객체를 얻을 수 있다. // 단점 : 테스트 용이성 낮다. // 객체와 객체간의 결합도가 높아진다. // 자바에서 가장 많이 사용하는 싱글톤의 형태 // -> 자바 5 이상에서는 스레드 안전성도 보장된다. class Cursor { // 방법 1. private 생성자 private Cursor() { } // 방법 2. // public static final Cursor INSTANCE1 = new Cursor(); // private static final Cursor INSTANCE = new Cursor(); private static final ThreadLocal<Cursor> INSTANCE = new ThreadLocal<Cursor>() { @Override protected Cursor initialValue() { return new Cursor(); } }; public static Cursor getInstance() { return INSTANCE.get(); } } public class Example4 { public static void main(String[] args) { System.out.println(Cursor.getInstance()); new Thread(new Runnable() { @Override public void run() { System.out.println(Cursor.getInstance()); } }).start(); } }
- 5_enum을 이용한 싱글톤
: Serialize()는 객체를 쓰고, 읽어도 하나의 객체로 유지된다.
: Refelction()는 객체가 생성되지 않는다.package kr.co.ioacademy; //iocademy 윤찬식 강사님 import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import org.junit.Test; // 아래의 싱글톤은 두 가지 경우에 객체가 두개이상 생성될 수 있습니다. // 1. Reflection // 2. 직렬화 //class Cursor implements Serializable { // private Cursor() { // } // // private Object readResolve() { // return INSTANCE; // } // // private static final Cursor INSTANCE = new Cursor(); // // public static Cursor getInstance() { // return INSTANCE; // } //} // 위의 문제점을 해결하기 위해 새롭게 제안된 싱글톤 // 원소가 하나뿐인 enum 을 이용하자. // 리플렉션을 통해 생성하는 것도 하는 것 불가능하고 // 직렬화에 의한 객체 생성도 자동적으로 처리해준다. enum Cursor { INSTANCE; public static Cursor getInstance() { return INSTANCE; } } public class Example5 { @Test public void Serialize() throws Exception { String filename = "cursor.dat"; FileOutputStream fos = new FileOutputStream(filename); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(Cursor.getInstance()); fos.close(); oos.close(); FileInputStream fis = new FileInputStream(filename); ObjectInputStream ois = new ObjectInputStream(fis); Cursor c = (Cursor) ois.readObject(); System.out.println(c); System.out.println(Cursor.getInstance()); } @Test public void Refelction() throws Exception { Constructor<?> con = Cursor.class.getDeclaredConstructors()[0]; con.setAccessible(true); Cursor c = (Cursor) con.newInstance(); System.out.println(c); System.out.println(Cursor.getInstance()); } }
6_지연된 초기화
package kr.co.ioacademy; //iocademy 윤찬식 강사님 // 자바의 싱글톤은 객체가 클래스 로더에 의해 로딩될 때 생성된다. // : 객체의 생성 비용이 크거나, 객체를 사용하지 않을 경우 리소스 낭비가 심하다. // (싱글톤은 가비지 컬렉션의 대상이 아니다.) // 지연된 초기화의 필요성 // 1. 메모리 낭비 제거 // 2. 애플리케이션의 로딩 속도 개선 class Cursor { public static final Cursor INSTANCE = new Cursor(); private Cursor() { System.out.println("Cursor created!"); } public static void foo() { System.out.println("foo"); } } public class Example6 { public static void main(String[] args) { Cursor.foo(); } }
7_지연된 초기화2
: DCLP(Double Checked Locking Pattern)
: IODH(Initialization On Demand Holder) - 클래스가 최초로 초기화 되는 시점package kr.co.ioacademy; //iocademy 윤찬식 강사님 class Cursor { // private static Cursor instance = null; private Cursor() { System.out.println("Cursor"); }; // public static Cursor getInstance() { // if (instance == null) // instance = new Cursor(); // // return instance; // } // 위의 지연된 초기화는 스레드 안전성이 없습니다. // 해결해야 합니다. // 1. 엄격한 동기화 // public synchronized static Cursor getInstance() { // if (instance == null) // instance = new Cursor(); // // return instance; // } // 2. 위의 코드는 계속 동기화를 수행하기 때문에 느립니다. // 생성 시점에만 동기화를 적용하고, 생성 이후에는 동기화를 제거하는 방법. // DCLP(Double Checked Locking Pattern) // public static Cursor getInstance() { // if (instance == null) { // synchronized (Cursor.class) { // if (instance == null) // instance = new Cursor(); // } // } // return instance; // } // 3. IODH(Initialization On Demand Holder) // 클래스가 최초로 초기화 되는 시점 // 1) 클래스 T의 인스턴스가 생성될 때 // 2) 클래스 T의 정적 메소드가 호출되었을 때 // 3) 클래스 T의 정적 필드에 값이 할당 되었을 때 * // 4) 클래스 T의 정적 필드가 상수 필드가 아니고 사용되었을 때 * private static class Singleton { private static final Cursor INSTANCE = new Cursor(); } public static Cursor getInstance() { return Singleton.INSTANCE; } public static void foo() { System.out.println("foo"); } } public class Example7 { public static void main(String[] args) { Cursor.foo(); Cursor.getInstance(); } }
8_정적필드 초기화
package kr.co.ioacademy; //iocademy 윤찬식 강사님 import java.util.Calendar; // 정적 필드는 위에서부터 아래로 차근차근 초기화 합니다. // 사용하기 전에 초기화 할 수 있도록 앞에 배치해야 합니다. class Person { public static final Person INSTANCE = new Person(); private final int weight; public static final int CURRENT_YEAR = Calendar.getInstance().get(Calendar.YEAR); private Person() { weight = CURRENT_YEAR - 1984; } public int weight() { return weight; } } public class Example8 { public static void main(String[] args) { System.out.println(Person.INSTANCE.weight()); System.out.println(Person.INSTANCE.CURRENT_YEAR); } }
9_정적필드 초기화2
: isTeenager()는 메소드 호출 당 Calendar, TimeZone, Date 2개의 객체가 생성된다.package kr.co.ioacademy; //iocademy 윤찬식 강사님 import java.util.Calendar; import java.util.Date; import java.util.TimeZone; import org.junit.Test; class Person { private final Date birthDate; public Person(Date b) { this.birthDate = b; } // 아래 메소드가 호출될 때마다 생성되는 객체는 다음과 같다. // 1. Calendar // 2. TimeZone // 3. Date 2개 public boolean isTeenager() { Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1996, Calendar.JANUARY, 1, 0, 0, 0); Date start = gmtCal.getTime(); gmtCal.set(2005, Calendar.JANUARY, 1, 0, 0, 0); Date end = gmtCal.getTime(); return birthDate.compareTo(start) >= 0 && birthDate.compareTo(end) < 0; } private static final Date START; private static final Date END; static { Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1996, Calendar.JANUARY, 1, 0, 0, 0); START = gmtCal.getTime(); gmtCal.set(2005, Calendar.JANUARY, 1, 0, 0, 0); END = gmtCal.getTime(); } public boolean isTeenager2() { return birthDate.compareTo(START) >= 0 && birthDate.compareTo(END) < 0; } } public class Example9 { @Test public void test_isTeen() { Calendar c = Calendar.getInstance(); c.set(1995, Calendar.MAY, 5); Person p = new Person(c.getTime()); for (int i = 0; i < 100000000; ++i) { p.isTeenager(); } } @Test public void test_isTeen2() { Calendar c = Calendar.getInstance(); c.set(1995, Calendar.MAY, 5); Person p = new Person(c.getTime()); for (int i = 0; i < 100000000; ++i) { p.isTeenager2(); } } }
10_StringBuilder
: + 연산자에 대한 오버로딩은 문자열만 제공.
: 매번 String 인스턴스를 생성하는 방식이라 성능 이슈 발생. 이를 개선하기 위해 JDK 1.5 버전 이후에는 컴파일 단계에서 StringBuilder로 컴파일 되도록 변경되었다. 그래서 JDK 1.5 이후부터는 +를 활용해도 성능상에 큰 이슈는 없다.package kr.co.ioacademy; //iocademy 윤찬식 강사님 // + 연산자에 대한 오버로딩은 문자열만 제공하고 있다. // : '+' 연산자는 피연산자로 문자열이 있을 때만 문자열 연결 연산을 수행합니다. // 문자열이 없으면 그냥 덧셈을 수행합니다. public class Example10 { public static void main(String[] args) { System.out.print("H" + "A"); // System.out.print('H' + 'A'); // 문자를 연결하는 방법 1. StringBuilder sb = new StringBuilder(); sb.append('H').append('A'); System.out.print(sb); // 방법 2. System.out.println("" + 'H' + 'A'); // 방법 3. System.out.printf("%c%c", 'H', 'A'); } }
11_부호확장, 제로 확장
: 부호확장은 상위 비트를 부호에 따라 0이나 으로 채우는 것
: 제로확장은 상위 비트를 0으로 채우는 것package kr.co.ioacademy; //iocademy 윤찬식 강사님 // int, long, byte : signed // char : unsigned // 변환되는 자료형에 상관없이 원래 부호가 있는 타입이면 부호 확장이 일어나고, // char 자료형이면 0의 확장이 일어난다. public class Example11 { public static void main(String[] args) { System.out.println((int)(char)(byte)-1); // int(4byte) -> byte(1byte) -> char(2byte) -> int(4byte) // -1 -> (byte) -1 // ff ff ff ff -> ff // -> (char)(byte) -1 // 부호 확장 // ff -> ff ff // -> (int)(char)(byte) -1 // 제로 확장 // ff ff -> 00 00 ff ff } }
'Programing > Java' 카테고리의 다른 글
Effective Java - 객체 소멸 (0) | 2016.03.04 |
---|---|
Junit 사용하기 (0) | 2016.03.03 |
Java Reference와 GC (0) | 2016.02.29 |
Effective Java - Reference (0) | 2016.02.29 |
Garbage Collection (0) | 2015.11.15 |