• 1_생성자
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    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()에 비해 현저히 느리다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    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)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    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()는 객체가 생성되지 않는다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    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_지연된 초기화

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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) - 클래스가 최초로 초기화 되는 시점

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    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_정적필드 초기화

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    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개의 객체가 생성된다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    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 이후부터는 +를 활용해도 성능상에 큰 이슈는 없다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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으로 채우는 것  


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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

+ Recent posts