• 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
Effective Java - 객체 생성  (0) 2016.03.03
Java Reference와 GC  (0) 2016.02.29
Effective Java - Reference  (0) 2016.02.29
Garbage Collection  (0) 2015.11.15

+ Recent posts