• Exception
    package kr.co.ioacademy;
    // 자바의 예외 3가지
    // 1. 점검 지정 예외     : 복구가 가능한 상황
    // 2. 무점검 지정 예외    : 복구가 불가능한 상황
    //  1) 실행 시점 예외(Runtime Exception) - 사용자
    //  2) 오류(Error) - JVM
    
    // API 사용자에게 점검 지정 예외를 준다는 것은 그 상태를 복구할 권한을 준다는 것이다.
    // (무시할 수 있지만 무시하면 안된다)
    
    // 실행 시점 예외와 오류의 동작 방식은 동일하다.
    // -> 둘다 catch 할 필요가 없다. 처리해서도 안된다.
    
    
    class Engine {}
    
    class Car {
    
      private static Class<Engine> engineClass = Engine.class;
    //  private Engine engine = engineClass.newInstance();
    //  // 2. 인스턴스 필드의 초기화에서 발생하는 예외는 생성자에서 처리해야 한다.
    //  public Car() throws Exception {
    //  }
    
      private Engine engine = newEngine();
      private static Engine newEngine() {
        try {
          return engineClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
          e.printStackTrace();
        }
        return null;
      }
    
      /*
      // 1. 인스턴스 필드가 생성자 호출보다 먼저 일어난다.
      private Car instance = new Car();
      public Car() throws Exception {
        throw new Exception("Failed to create");
      }
      */
    }
    
    public class Example9 {
      public static void main(String[] args) {
        try {
          Car car = new Car();
          System.out.println("Succeed");
        } catch (Exception e) {
          System.out.println("Failed");
        }
      }
    }

  • Assertion
    : jdk 1.4부터 도입
    : Assertion은 불리언 식(expression)을 포함하고 있는 문장으로서, 프로그래머는 그 문장이 실행될 경우 불리언 식이 참이라고 단언할 수 있다. (JSR, Java Specification Request) 
    : Assertion은 개발자가 참이라고 가정하는 상태를 명시하기 위해서 사용된다. (예를 들어, 어떤 메소드가 파라미터로 양수만 입력받아야 한다고 확신한다면, Assertion을 사용하여 그 사실을(즉, 파라미터가 양수라는 것을) 명시할 수 있다.)

    // jdk 1.4
    // javac -source 1.4 AssertionTest.java 
    // JRE 클래스가 Assertion을 포함하는지 여부에 상관없이 기본적으로 Assertion 기능을 사용하지 않는다.
    // java -ea AssertionTest
    
    
    // 문법
    // assert [boolean 식];
    // assert [boolean 식]:[표현식];
    
    // 사용해선 안 되는 경우
    // public 메소드의 파라미터를 검사하는 경우
    // 올바른 수행을 위해 필요한 작업을 수행하는 경우
    // ex) assert checkName(); -> boolean checked = checkName();
    // 							  assert checked;
    
    public class AssertionTest {
    	// Assert 기능 강제적으로 사용하도록 유도
    	static { 
    		boolean assertsEnabled = false;
    		assert assertsEnabled = true;        if (!assertsEnabled)
    			throw new RuntimeException("Assertion 기능이 사용가능해야 합니다!");
    	}    
    
    	/*    
        // post-condition check
    	Object getObj(Object obj) {
    		assert obj != null : "obj cannot be null";
    
    		return obj;
    	}
     
        // pre-condition check
    	void register(MemberInfo member) {
    		assert Member != null:"MemberInfo cannot be null";
    		assert member.getId() != null && !member.getId().equals("");
    
    	        ...
    	}
    	 */
    
    	public static void main(String[] args) {
    		char operator = '%';                  // assumed either '+', '-', '*', '/' only
    		int operand1 = 5, operand2 = 6, result = 0;
    		switch (operator) {
    			case '+': result = operand1 + operand2; break;
    			case '-': result = operand1 - operand2; break;
    			case '*': result = operand1 * operand2; break;
    			case '/': result = operand1 / operand2; break;
    			default: assert false : "Unknown operator: " + operator;  // not plausible here
    		}
    		System.out.println(operand1 + " " + operator + " " + operand2 + " = " + result);
    	}
    }
    
    참고 : http://javacan.tistory.com/entry/79, https://www3.ntu.edu.sg/home/ehchua/programming/java/J5a_ExceptionAssert.html


'Programing > Java' 카테고리의 다른 글

Effective Java - Annotation  (0) 2016.03.08
Effective Java - Reflection  (0) 2016.03.08
Effective Java - 스레드(2)  (0) 2016.03.07
Effective Java - 스레드(1)  (0) 2016.03.07
Effective Java - 불변 객체  (0) 2016.03.07
  • Anotation
    - Java Annotation?
     : 어노테이션은 어노테이션된 요소들의 행동으로부터 프로그램의 행위를 추출하여, 필요하다면 컴파일러나 VM이 상호 의존적인 코드를 생성하는, 프로그램 요소와 메타 태그에 관계된 매커니즘.
     : 사전적으로는 "주석"이라는 의미를 가지고 있으며, 의미대로 자바 코드에 주석처럼 달아 특수한 의미를 부여해준다. 이 특별한 의미는 컴파일 타임 또는 런타임에 해석 될 수 있다.


    - .java부터 .class까지

  •  : .java => Parser => Type checker => [Annotation Checker] => Class File writer => .class


    - 어노테이션 규칙

     : @interface + 어노테이션 이름

     : 어노테이션 소스코드 내부의 메소드 선언은 매개변수를 지닐 수 없다.

     : 어노테이션 소스코드 내부의 메소드 선언은 clauses를 throw 할 수 없다.

     : 메소드의 반환 타입은 primitives, String, Class, enum과 primitives배열, String배열, Class배열, enum배열 중 하나이다.


    - 분류

     1. marker ; @AnnotationTypeName
      : 멤버 변수가 없으며, 단순히 표식으로서 사용되는 어노테이션이다. 컴파일러에게 어떤 의미를 전달한다.

     2. single-element ; @AnnotationTypeName(single-element)
      : 멤버로 단일변수만을 갖는 어노테이션이다. 단일변수 밖에 없기 때문에 (값)만을 명시하여 데이터를 전달할 수 있다.

     3. normal, full-value, multi-value ; @AnnotationName(element=value, element=value, ...)
      : 멤버로 둘 이상의 변수를 갖는 어노테이션으로, 데이터를 (값=쌍)의 형태로 전달한다.


    Built-in Annotation

    == java.lang.annotation (meta-annotation)

    @Retention

     : Retention은 어노테이션이 얼마나 오랫동안 유지되는지에 대해, JVM이 어떻게 사용자 어노테이션을 다루어야 하는지를 서술합니다.

     * SOURCE - 어노테이션이 컴파일 타임시 버려진다는 것을 의미합니다. retention정책이 source로 정의되어 있으면, 클래스 파일은 어노테이션을 지니지 못합니다.

     * CLASS - 어노테이션이 생성된 클래스 파일에서 나타날 것이라는 것을 의미합니다. 그러나 런타임시에는 이 어노테이션을 이용하지 못합니다.

     * RUNTIME- 이는 런타임시 JVM에서 어노테이션의 이용이 가능하다는 것을 의미합니다. 이러한 어노테이션을 읽는 사용자 로직을 가짐으로써 런타임시 무언가를 할 수 있습니다.


    @Target

     : Target은 어디에 어노테이션을 넣을 수 있는지를 서술합니다. field, method, class가 정의된 곳에 어노테이션을 넣을 수 있습니다.

     * TYPE - class, interface, enumeration에 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * METHOD - method 선언에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * PARAMETER - parameter 선언에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * PACKAGE - package 선언에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * FIELD - field 선언에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * LOCAL_VARIABLE - 지역 변수 선언에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * CONSTRUCTOR - 생성자에만 어노테이션을 적용할 수 있다는 것을 의미합니다.

     * ANNOTATION_TYPE - 어노테이션 타입에만 어노테이션을 적용할 수 있다는 것을 의미합니다.


    @Inherited

     - 기본적으로 어노테이션은 상속되지 않습니다. 따라서 상속을 원한다면, 어노테이션을 Inherited 해야 합니다. Inherited 어노테이션이 사용자 어노테이션에 놓여야 하며, 이는 클래스에만 효과가 있습니다. (즉, 상속받는 클래스도 같은 어노테이션 사용)


    @Documented

     - 어노테이션이 기본으로 javadoc 및 유사한 툴에 의해 문서화 되는 것을 나타냅니다. 이러한 형태는 타입의 선언에 주석을 달기 위해 사용합니다. 어노테이션은 클라이언트에 의해 어노테이션된 요소의 사용에 영향을 미치게 됩니다. 타입의 선언에 Documented 어노테이션을 붙인 경우, 그 어노테이션은 어노테이션된 요소의 공개 API중 한 부분이 됩니다.


    == java.lang

    @Override
     : 현재 메소드가 수퍼클래스의 메소드를 오버라이드한 메소드임을 컴파일러에게 명시한다. 만일 수퍼 클래스에 해당하는 메소드가 없으면 컴파일러가 인지하고 에러를 발생시켜 준다.

    @Deprecated
     : 마커 오노테이션으로 차후 버전에 지원되지 않을 수 있기 때문에 더 이상 사용되지 말아야할 메소드를 나타낸다. 특이하게 더이상 사용되지 말아야할 메소드와 같은 라인상에 놓여져야 한다.

    @SuppressWarnings
     : 의미대로 경고를 제거하는 어노테이션이다. Object형을 엘리먼트로 하는 컬렉션을 사용하면, 컴파일러 경고가 발생하는데 이 어노테이션을 사용하여 프로그래머의 의도적인 Object형 사용임을 알려 경고를 제거할 수 있다.

    출처: http://www-01.ibm.com/support/knowledgecenter/SSQ2R2_9.1.0/org.eclipse.jdt.doc.user/tasks/task-suppress_warnings.htm?lang=ko
    Java 5.0부터 java.lang.SuppressWarning 어노테이션을 사용하여 컴파일 단위의 서브세트와 관련된 컴파일 경고를 사용하지 않도록 설정할 수 있습니다.

    @SuppressWarning("unused") public void foo() { String s; }

    어노테이션이 없으면 컴파일러에서 로컬 변수 s를 사용할 수 없습니다. 어노테이션을 사용하여 컴파일러는 이 경고를 foo 메소드에 대해 로컬에서 무시합니다. 이러한 경우 동일한 컴파일 단위 또는 동일한 프로젝트의 다른 위치에 경고를 보관할 수 있습니다.

    SuppressWarnings 어노테이션 내부에서 사용할 수 있는 토큰 목록은 다음과 같습니다.

    • all 모든 경고를 억제합니다.
    • boxing boxing/unboxing 오퍼레이션과 관련된 경고를 억제합니다.
    • cast 캐스트 오퍼레이션과 관련된 경고를 억제합니다.
    • dep-ann 권장되지 않는 어노테이션과 관련된 경고를 억제합니다.
    • deprecation 권장되지 않는 기능과 관련된 경고를 억제합니다.
    • fallthrough switch 문에서 누락된 break 문과 관련된 경고를 억제합니다.
    • finally 리턴되지 않는 마지막 블록과 관련된 경고를 억제합니다.
    • hiding 변수를 숨기는 로컬과 관련된 경고를 억제합니다.
    • incomplete-switch switch 문에서 누락된 항목과 관련된 경고를 억제합니다(enum case).
    • javadoc javadoc 경고와 관련된 경고를 억제합니다.
    • nls 비nls 문자열 리터럴과 관련된 경고를 억제합니다.
    • null 널(null) 분석과 관련된 경고를 억제합니다.
    • rawtypes 원시 유형 사용법과 관련된 경고를 억제합니다.
    • resource 닫기 가능 유형의 자원 사용에 관련된 경고 억제
    • restriction 올바르지 않거나 금지된 참조 사용법과 관련된 경고를 억제합니다.
    • serial 직렬화 가능 클래스에 대한 누락된 serialVersionUID 필드와 관련된 경고를 억제합니다.
    • static-access 잘못된 정적 액세스와 관련된 경고를 억제합니다.
    • static-method static으로 선언될 수 있는 메소드와 관련된 경고를 억제합니다.
    • super 수퍼 호출을 사용하지 않는 메소드 겹쳐쓰기와 관련된 경고를 억제합니다.
    • synthetic-access 내부 클래스로부터의 최적화되지 않은 액세스와 관련된 경고를 억제합니다.
    • sync-override 동기화된 메소드를 오버라이드하는 경우 누락된 동기화로 인한 경고 억제
    • unchecked 미확인 오퍼레이션과 관련된 경고를 억제합니다.
    • unqualified-field-access 규정되지 않은 필드 액세스와 관련된 경고를 억제합니다.
    • unused 사용하지 않은 코드 및 불필요한 코드와 관련된 경고를 억제합니다.

    참고: http://netpyoung.tistory.com/100, http://hiddenviewer.tistory.com/88, https://en.wikibooks.org/wiki/Java_Programming/Annotations/Meta-Annotations

    - Custom Annotation

     : 클래스와 같이 어노테이션을 임의로 정의하여 사용할 수 있다. 어노테이션은 interface 키워드 앞에 @를 붙여 표시한다.
     : @interface InProgress{}

    - 예제

    package kr.co.ioacademy; // ioacademy 윤찬식 강사님
    // Simple JUnit4
    // 어노테이션(Annotation) ; 주석
    // Comment : 비공식적이고 임시적이다.
    // 정의 : 자바의 각 요소(클래스, 메소드, 필드, 매개변수) 가 가질 수 있는
    //        주석 리소스
    
    // 목적
    // 1. 컴파일러에게 추가적인 정보 전달
    // 2. 컴파일 할 때와 설치시의 작업 지정
    // 3. 실행할 때 별도의 처리를 수행
    
    // 기본 어노테이션
    // 1. @Override
    // 2. @Deprecated
    // 3. @SuppressWarnings
    
    // 1. 어노테이션을 만드는 방법.
    
    import java.lang.annotation.*;
    import java.lang.reflect.Method;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @interface Test {
      boolean enabled() default true;
    }
    
    public class Example7 {
      @Test
      public void testA() {
        System.out.println("testA : do something");
      }
    
      @Test(enabled = true)
      public void testB() {
        System.out.println("testB : do something");
        throw new RuntimeException("failed to run");
      }
    
      @Test(enabled = true)
      public void testC() {
        System.out.println("testC : do something");
      }
    
      public static void main(String[] args) {
        System.out.println("Testing....");
    
        int count = 0;
        int passed = 0;
        int failed = 0;
        int ignore = 0;
    
        Class obj = Example7.class;
        for (Method method : obj.getDeclaredMethods()) {
    
          if (method.isAnnotationPresent(Test.class)) {
            ++count;
            Annotation annotation = method.getAnnotation(Test.class);
            Test test = (Test) annotation;
    
            if (test.enabled()) {
              try {
                method.invoke(obj.newInstance());
                ++passed;
                System.out.printf("Test '%s' - passed\n", method.getName());
              } catch (Throwable e) {
                ++failed;
                System.out.printf("Test '%s' - failed : %s\n", method.getName(),
                    e.getCause());
              }
            } else {
              ++ignore;
              System.out.printf("Test '%s' ignored\n", method.getName());
            }
          }
        } // for
    
        System.out.printf("Result - Total:%d, Passed:%d, Failed:%d, Ignore:%d\n",
            count, passed, failed, ignore);
      }
    
    }
    




'Programing > Java' 카테고리의 다른 글

Effective Java - Exception  (0) 2016.03.09
Effective Java - Reflection  (0) 2016.03.08
Effective Java - 스레드(2)  (0) 2016.03.07
Effective Java - 스레드(1)  (0) 2016.03.07
Effective Java - 불변 객체  (0) 2016.03.07
  • 1_Class 정보
     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    // Reflection(Introspection)
    // 개념 : Class 이름만으로도 클래스의 정보(필드, 메소드)를 찾거나
    //       객체를 생성할 수 있는 기능.
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    public class Example1 {
      public static void main(String[] args) throws Exception {
        // 1. Class 얻기
        // a. 객체를 통해서 얻는 방법.
        // Class personClass = new Person().getClass();
    
        // b. Class type으로 얻어내는 방법.
        // Class personClass = Person.class;
    
        // c. 문자열로 얻어내는 방법.
        Class personClass = Class.forName("kr.co.ioacademy.Person"); // 패키지 정보까지 정확히 넣어야된다.
    
        // 2. Class 이름
        System.out.println(personClass.getName());       // kr.co.ioacademy.Person
        System.out.println(personClass.getSimpleName()); // Person
        System.out.println(personClass.getCanonicalName());
    
        int mods = personClass.getModifiers();
    
        // 3. Class 속성
        System.out.println("public : " + Modifier.isPublic(mods));
        System.out.println("final : " + Modifier.isFinal(mods));
        System.out.println("abstract : " + Modifier.isAbstract(mods));
    
        // 4. Method 속성
        Method[] methods = personClass.getMethods();
        for (Method m : methods) {
          System.out.println(m.getName());
          System.out.println(m.getParameterCount());
          Class[] params = m.getParameterTypes();
          for (Class p : params) {
            System.out.println("\t" + p.getName());
          }
        }
      }
    }
    
    final class Person {
      private String name;
      private int age;
    
      public Person() {
        this.name = "";
        this.age = 0;
      }
    
      public Person(String name, int age) {
        this.name = name;
        this.age = age;
      }
    
      @Override
      public String toString() {
        return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
      }
    }
    

  • 2_객체 read / write
    : public field vs. private field
     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.lang.reflect.Field;
    
    // 객체의 필드를 읽거나 쓰는 방법.
    public class Example2 {
      public static void main(String[] args) throws Exception {
        Point point = new Point();
    
        Class pointClass = point.getClass();
    
        // public field read / write
        Field xField = pointClass.getField("x");
        Field yField = pointClass.getField("y");
    
        System.out.println(xField.get(point) + ", " + yField.get(point));
    
        xField.setInt(point, 10);
        // xField.set(point, 10);
        yField.set(point, 20);
        System.out.println(xField.get(point) + ", " + yField.get(point));
    
        Field[] fields = pointClass.getDeclaredFields();
        for (Field f : fields)
          System.out.println(f.getName());
    
    
        // private field read / write
        Field zField = pointClass.getDeclaredField("z");
        zField.setAccessible(true);
        zField.set(point, 100);
    
        System.out.println(zField.get(point));
      }
    }
    
    class Point {
      public int x;
      public int y;
    
      private int z;
      public int z() {
        return z;
      }
    }
    


  • 3_객체 동적 생성
     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.lang.reflect.Constructor;
    
    // Reflection 을 통해서 객체를 생성하는 방법. - 동적 생성
    // Spring - DI(Dependency Injection)
    // iOS
    public class Example3 {
      public static void main(String[] args) throws Exception {
        Class personClass = Person.class;
    
        // 1. 기본 생성자를 통한 객체 생성
        Person person = (Person) personClass.newInstance();
        System.out.println(person);
    
        // 2. 사용자 정의 생성자를 통한 객체 생성
        Class[] paramTypes = {
          String.class, int.class
        };
    
        // Constructor constructor = personClass.getConstructor(paramTypes);
        Constructor constructor = personClass.getDeclaredConstructor(paramTypes);
        System.out.println(constructor);
    
        Object[] cargs = {
            "Tom", 42
        };
    
        constructor.setAccessible(true);
        Person person2 = (Person) constructor.newInstance(cargs);
        System.out.println(person2);
      }
    }
    
    class Person {
      private String name;
      private int age;
    
      public Person() {
        this.name = "";
        this.age = 0;
      }
    
      private Person(String name, int age) {
        this.name = name;
        this.age = age;
      }
    
      @Override
      public String toString() {
        return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
      }
    }
    

  • 3_Refilection 활용
    - 1_Key-Value Coding

     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.lang.reflect.Field;
    
    // Reflection 활용 1.
    // Key-Value Coding : setter / getter
    // Objective-C 기본으로 제공하고 있는 기능.
    public class Example4 {
      public static void main(String[] args) {
        Person person = new Person();
    
        String input = "name";
        String value = "Tom";
    
        person.setValue(input, value);
        person.setValue("phone", "010-1111-2222");
        person.setValue("age", 200);
    
        System.out.println(person.getValue("name"));
        System.out.println(person.getValue("phone"));
        System.out.println(person.getValue("age"));
    
    
        // 일반적인 getter / setter 사용
        /*  
        if (input.equals("name"))
          person.setName(value);
        else if (input.equals("phone"))
          person.setPhone(value);
        */
      }
    }
    
    class Person {
      private String name;
      private String phone;
      private int age;
    
      public Object getValue(String key) {
        Class clazz = this.getClass();
        Field field;
    
        try {
          field = clazz.getDeclaredField(key);
          return field.get(this);
        } catch (NoSuchFieldException | IllegalAccessException e) {
          e.printStackTrace();
        }
    
        return null;
      }
    
      // Reflection을 사용하지 않고 key-value coding
      // but, field가 많아진다면??
      /*  
      public void setValue(String key, Object value) {
      switch (key) {
        case "name":
          this.name = (String) value;
          break;
        case "phone":
          this.phone = (String) value;
          break;
        case "age":
          this.age = (int) value;
          break;
       }
     }*/
      
      public void setValue(String key, Object value) {
        Class clazz = this.getClass(); // Person.class
        try {
          Field filed = clazz.getDeclaredField(key);
          filed.set(this, value);
        } catch (NoSuchFieldException | IllegalAccessException e) {
          e.printStackTrace();
        }
      }
    
      @Override
      public String toString() {
        return "Person{" +
            "name='" + name + '\'' +
            ", phone='" + phone + '\'' +
            '}';
      }
    }
    

    - 2_팩토리
     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import org.hamcrest.Factory;
    
    // Reflection 활용 2.
    // 팩토리
    // 개념 : 객체를 생성하는 객체
    // 장점 : 객체 생성에 관한 코드를 한곳에 모아서 중앙집중적으로 관리하는 것이 가능하다.
    // 단점 : 도형의 종류가 늘어남에 따라 팩토리의 코드는 수정되어야만 한다.
    //        OCP(Open Closed Principle) 를 만족할 수 없다.
    
    abstract class Shape {
    	abstract void print();
    }
    class Rect extends Shape {
    	@Override
    	void print() {
    		System.out.println("Rect");
    	}
    }
    class Circle extends Shape {
    	@Override
    	void print() {
    		System.out.println("Circle");
    	}
    }
    class Triangle extends Shape {
    	@Override
    	void print() {
    		System.out.println("Triangle");
    	}
    }
    
    class ShapeFactory {
      public Shape createShape(String name) {
        Class clazz = null;
        try {
          clazz = Class.forName(name);
          return (Shape) clazz.newInstance();
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
          e.printStackTrace();
        }
    
        return null;
      }
    
    //  public Shape createShape(String name) {
    //    if (name.equals("Rect"))
    //      return new Rect();
    //    else if (name.equals("Circle"))
    //      return new Circle();
    //    else if (name.equals("Triangle"))
    //      return new Triangle();
    //
    //    return null;
    //  }
    }
    
    public class Example5 {
    	public static void main(String args[]) {
    		ShapeFactory factory = new ShapeFactory();
    		Object r = factory.createShape("kr.co.ioacademy.Rect");
    		Object c = factory.createShape("kr.co.ioacademy.Circle");
    		Object t = factory.createShape("kr.co.ioacademy.Triangle");
    		
    		if(r instanceof Rect)
    			((Rect)r).print();
    		if(c instanceof Circle)
    			((Circle)c).print();
    		if(t instanceof Triangle)
    			((Triangle)t).print();
    	}
    }
    

  • 4_Reflection 성능 고찰

    package kr.co.ioacademy; //iocademy 윤찬식 강사님 // Reflection 성능. // 1. Refelction 은 염려할만큼의 성능 저하는 없다. // 2. 잘 설계된 Reflection은 객체 지향의 철학을 어긋나지 않으면서, // 더 좋은 코드를 만들어 낼 수 있다. public class Example6 { public static void doRegular() throws Exception { long start = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { Point p = new Point(); p.print(); } System.out.println(System.currentTimeMillis() - start); } public static void doReflection() throws Exception { long start = System.currentTimeMillis(); Class clazz = Class.forName("kr.co.ioacademy.Point"); for (int i = 0; i < 1000000; i++) { Point p = (Point) clazz.newInstance(); p.print(); } System.out.println(System.currentTimeMillis() - start); } public static void main(String[] args) throws Exception { doRegular(); doReflection(); } } class Point { private int x; private int y; public void print() { } }



  • Reflection에 대한 오해와 진실
    (출처: https://kmongcom.wordpress.com/2014/03/15/%EC%9E%90%EB%B0%94-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EC%97%90-%EB%8C%80%ED%95%9C-%EC%98%A4%ED%95%B4%EC%99%80-%EC%A7%84%EC%8B%A4/)

    사용처

    현장에서 자바 개발자들이 Reflection을 직접 사용하는 것은 극히 드문 일이다. 그것은 Reflection이 적용될 수 있고 또한 적용되어야 할 곳은 라이브러리 클래스, 공통 컴포넌트 클래스, 그리고 프레임워크와 같이 Reflection을 통해 얻는 이득(재사용성, 확장성, 생산성, 유연성 등)이 극대화될 수 있는 곳이어야 하기 때문이다.

    1. Java Serialization

     
    객체를 직렬화(Serialization) 해야 할 경우 Serializable 인터페이스를 구현한다. 그리고, 직렬화된 객체를 읽기 위해서는 java.io.ObjectInputStream 클래스의 readObject() 메서드를 이용한다. 필요에 따라 Serializable 인터페이스를 구현한 클래스가 readObject() 메서드를 구현할 수도 있다. 이때, java.io.ObjectInputStream 클래스의 readObject() 메서드는 내부적으로 Reflection을 이용하여 직렬화된 객체의 readObject() 메서드를 호출한다.

    a4

    그림 4. Reflection을 사용한 Serialization

    2. Apache Commons BeanUtils library

    Struts 프레임워크를 적용한 프로젝트에서 개발한 경험이 있다면, Apache Commons 프로젝트의 BeanUtils 라이브러리 사용을 고민해 본 경험 또한 있을 것이다. Struts 프레임워크는 HttpRequest 객체의 파라미터를 이용하여 ActionForm 클래스의 객체를 생성한다. 이 ActionForm 클래스의 객체를 생성하는 곳에서도 Reflection이 적용되었다.

    Struts를 이용할 경우, 가장 성가신 부분은 ActionForm 클래스의 객체를 대응하는 VO 클래스의 객체로 변환하는 작업이다. 이 작업은 서비스 레이어를 Struts에 종속되지 않게 하기 위해 또는, 레이어 분리를 위해 반드시 수행되어야 한다. 만일 지금까지 ActionForm 객체를 서비스 레이어로 바로 넘겼다면 다시 한번 생각해 보라. VO 클래스 사용에 따른 레이어의 분리와, VO를 사용하지 않음으로써 얻는 개발 생산성 증가, 둘 중 어느 한가지를 택한 것인지.

     이때, 사용할 수 있는 것이 Commons BeanUtils 라이브러리이다. Beanutils.copyProperties(Object dest, Object orig)를 이용하여 간단히 ActionForm 객체를 VO 객체로 변환할 수 있다. 규칙은 ActionForm 클래스의 인스턴스 변수명과 VO 클래스의 인스턴수 변수명이 같아야 한다는 것이다. 이 규칙을 따른다면, Reflection의 마술이 여러분을 위한 모든 작업을 수행해 줄 것이다.

    이슈

    1. Reflection을 사용한 코드는 느리다

    개발자들 사이에 공공연히 진실로 받아들여지는 이 말은 사실이 아니다. 적절히 사용한 Reflection은 오히려 성능을 향상시킬 수 있으며, 또한 많은 이득을 제공한다. 뿐만 아니라, 성능만을 고려한 구현이 객체 지향의 설계 원칙들을 역행한다면, 오히려 이는 더욱 나쁜 결과를 낳게 된다.

    2. Reflection을 이용하여 개발한 프로그램은 에러가 발생하기 쉽고 디버깅이 어렵다

    Reflection은 컴파일 시 타입 체킹을 할 수 없다. 따라서, 런타임시 잘못된 파라미터로 인해 런타임 에러가 발생하기 쉽다. 이는 사실이다. 하지만, 적절히 사용된 런타임 에러 메시지를 이용해 충분히 디버깅이 쉬운 환경으로 만들 수 있다.

    3. Reflection을 사용한 코드는 복잡하다

    Reflection을 사용한 코드는 일반적인 객체 생성, 메서드 호출 코드에 비교하면 복잡한 것이 사실이다. 하지만 클래스의 타입을 비교하여 객체를 생성하는 코드의 경우, 대량의 if/else문을 사용하는 것보다 Reflection을 이용하여 재사용 가능한 컴포넌트로 만든다면, 오히려 코드를 단순화한다.

    4. 성능(Performance) vs. 유연성(Flexibility)

    앞서 말한 바와 같이 “Reflection을 사용한 코드는 느리다”는 사실은 사실이 아니다. 이 말은 Reflection을 사용할 경우 성능이 떨어지지 않는다라는 얘기가 아니다. 오히려, 성능이 떨어진다는 결과가 다수 존재한다. 아래는 Dennis Sosnoski(5. Java Programming dynamics, Part 2: Introducing reflection)가 측정한 Reflection에 관한 성능 결과이다. 그림에서 알 수 있듯이, Reflection을 사용할 경우, 직접(Direct) 또는 참조(Reference)의 경우에 비해 2 ~ 4배 정도 느리다.


    a5

    그림 5. 필드 변수 Access 시간

    a6

    그림 6. 메서드 호출 시간

    이 결과를 통해 알 수 있는 사실은 “Reflection에 따른 성능 저하”가 아니라 “성능 측정 결과, Reflection을 사용한 
    지금 이 경우에는 성능이 저하되는 것을 검증했다”라는 것이다. 최적화 또는 성능 개선(Optimization)시 유의해야 할 점은 반드시 최적화 이전과 이후의 성능을 측정하여, 성능개선이 가시적으로 보일 때에만 적용해야 한다는 것이다. 만일 최적화가 필요하다고 느낀다면, 아래 규칙을 따르라.

    a7

    그림 7. 최적화 규칙

    Reflection과 관련된 성능에 관한 논쟁은 오해에서 비롯된 것이다. 이것은 JDK 초기 버전(1.3.0 이전 버전)의 경우 Reflection의 성능이 현저히 떨어졌다. 하지만 이후의 JDK 버전에서는, Reflection의 중요성을 인식한 Sun의 지속적인 노력으로 성능이 개선되고 있으며, 앞으로도 개선의 여지가 남아 있다. 뿐만 아니라 잘 적용한 Reflection은 많은 이득을 제공한다.

    Reflection을 통해 얻을 수 있는 가장 큰 이득은 시스템 유연성(Flexibility)이다. “그림 3″에서 보는 바와 같이 Mouse 컨트롤러는 미래의 어떤 마우스와도 동작할 수 있다. 이처럼 성능보다 유연성이 더 중요한 상황이 많다

    물론 Reflection 적용에 따른 가시적인 성능 저하를 확신한다면, 다른 대안을 생각해 볼 수 있다. 대안에는, Reflection 대신 interface를 통한 메서드 호출, 코드 자동 생성(Code Generation), 또는 최악의 경우 하드 코딩이 있다.

    5. Compile vs. Run-time Type Checking

     자바의 경우 컴파일 단계에 강력한 Type Checking을 지원한다. 아래와 같이 두 개의 클래스가 틀릴 경우, 컴파일이 에러가 발생한다.

    a8

    그림 8. 컴파일 에러 예

    Reflection은 실제 클래스 없이 클래스의 이름 또는 메서드의 이름만을 이용한다. 따라서, 아래와 같이, 개발 단계 또는 컴파일 단계에는 Type Checking을 하지 않는다.

    a9

    그림 9. Checked Exception 예

    대신, 실행시 발생할 수 있는 Exception을 처리하기 위한 try/catch문을 추가해야 한다.

    자바가 제공하는 Exception의 종류에는 Checked Exception, Run-time Exception 그리고 error가 있다. 이중 Checked Exception의 경우, 위와 같이 컴파일 단계에 catch하거나 throw해야만 컴파일 오류가 발생하지 않는다. 이는 Checked Exception은 예외 상황에서 프로그램적으로 복구할 수 있는 방법이 존재하는 경우를 위한 Exception이기 때문이다.

    하지만 Reflection 사용에 따른 Exception으로부터 복구할 수 있는 상황이란 거의 없다. 예를 들어, 위의 Class Not Found Exception이 발생한다면, 이는 클라이언트가 잘못된 클래스 명을 넘겨주었거나 또는 해당 클래스가 없을 2가지 경우다. 이는 모두 프로그램 에러 상황으로 오히려 Run-time Exception에 가깝다.

     결국, Reflection을 사용함으로써, 개발단계에서 조기에 발견될 수도 있었을 프로그램 에러들이 런타임시에 발생하게 되는 것이다. 이처럼 에러 상황이 늦게 발견하면 디버깅이 어려워지게 된다. 이와 같은 경우를 위해서, 런타임시 디버깅을 위해 상세한 에러 메시징 기능을 포함하는 것이 좋다.

    Drug heals the pain, Overdose kills the gain

    Reflection의 사용에 관한 사실들은 사실 사실이 아니다. 성능, 디버깅, 그리고 복잡성과 관련된 내용들은 잘못 사용된 예에서 파생한 오해들이다.

    Reflection은 염려할 만큼의 성능저하를 가져오지 않는다. 대량의 if/else문이나 switch문 대신, 잘 설계된 Reflection은 객체지향 철학을 어기지 않으면서도 더 좋은 성능을 발휘할 수도 있다.

    또한 디버깅이 어려운 것은 컴파일 단계에 처리할 수 있는 오류들이 실행 단계에 발생하기 때문이 아니다. 더 근본적인 이유는 Reflection을 사용할 경우에 발생할 런타임 에러 메시지를 최대한 상세하게 그리고 친절하게 표시하도록 Exception 전략을 설계하지 못했기 때문이다. 잘 설계된 Exception 처리 전략은 Reflection 뿐만 아니라 시스템의 전체적인 디버깅을 쉽게 만든다.

    Reflection의 복잡성은 사실 개발자 개개인의 초점에 맞추었을 때의 얘기다. 하지만, Reflection의 사용은 개발자의 관점이 아닌 아키텍트 중심으로 설계되어야 한다. Reflection이 가장 유용한 곳이 바로 시스템의 아키텍처를 이루는 컴포넌트들이기 때문이다. 오히려 잘 설계된 Reflection이 제공하는 서비스는 개발을 더욱 단순화 한다.

    하지만 지나친 사용은 화를 부를 수 있다. Reflection을 적절히 사용했고 많은 이득이 따른다 하더라도, 더 간단한 해결책이 존재한다면, Reflection을 사용하지 말 것을 권한다. 단순한 해결책은 언제 어느 경우에나 최상의 선택이다.

    Reflection의 사용은 양날의 검과 같다. 잘 사용한다면, 이름을 불러주었을 때, 여러분의 꽃이 되어 줄 것이다.


'Programing > Java' 카테고리의 다른 글

Effective Java - Exception  (0) 2016.03.09
Effective Java - Annotation  (0) 2016.03.08
Effective Java - 스레드(2)  (0) 2016.03.07
Effective Java - 스레드(1)  (0) 2016.03.07
Effective Java - 불변 객체  (0) 2016.03.07
  • Atomic
    : 원자적 연산은 '원자적인'방법 즉, 중간에 어떠한 방해도 받지 않고 어셈블리어 명령어 하나로 실행할수 있다. 
    : 메모리에 접근하지 않거나 한번 접근하는 어셈블리어 명령은 원자적이다.
    : inc나 dec같이 메모리에서 데이터를 읽고, 이를 갱신하고, 갱신한 값을 메모리에 다시 쓰는 읽기/수정/쓰기 어셈블리어 명령은 읽기와 쓰기 사이에 다른 프로세서가 메모리버스를 점유하지 않는 한 원자적이다. 유니프로세서 시스템에서는 같은 프로세서에서 메모리 접근이 일어나므로 메모리버스를 훔치는일은 발생하지 않는다.

    ∴ 윈도우 프로그램에서 동기화
     : 커널 모드 동기화 vs. 유저모드 동기화
     : 유저모드는 Atomic Operation 과 CRITICAL_SECTION 으로 구분
     : 커널 모드는 세마포어와 뮤텍스로 구분된다. 뮤텍스(두 프로세스간)는 세마포어 기법(멀티 프로세스간)의 일부분으로 포함되는 개념이다.

    : java 1.5 부터 java.util.concurrent.atomic에 Atomic 관련 타입들 존재
    : 속도는 perf3 > perf1 > perf2
    : Atomic이 무조건 좋다는 법은 없다. -> 적절하게 사용하면 된다.

    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    // 1 억 만들기
    
    import org.junit.Test;
    
    import java.util.concurrent.atomic.AtomicLong;
    
    // value += 2; -> Atomic 하지 않다.
    // 1. value 를 메모리부터 로드
    // 2. 2를 더하고
    // 3. 메모리에 저장
    
    public class Example5 {
        private static final int THREAD_COUNT = 2;
        // 1. Atomic 사용
        private static AtomicLong value1 = new AtomicLong(0);
        // private static volatile long value = 0;
    
        private final static Runnable TASK = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000000 / THREAD_COUNT; i++)
                    value1.addAndGet(2);
                    // value += 2;
            }
        };
    
        @Test 
        public void perf1() throws InterruptedException {
            Thread[] threads = new Thread[THREAD_COUNT];
            for (int i = 0; i < THREAD_COUNT; i++)
                threads[i] = new Thread(TASK);
    
            for (Thread t : threads)
                t.start();
    
            boolean alive = true;
            while (alive) {
                for (Thread t : threads) {
                    t.join(10);
                    if (alive = t.isAlive())
                        break;
                }
            }
    
            System.out.println("value : " + value1.get());
        }
        
        // 2. volatile 사용 - for문 연산 자체에 동기화
        private static volatile long value2 = 0;
        private final static Runnable TASK2 = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50000000 / THREAD_COUNT; i++)
                    synchronized (Example5.class) {
                        value2 += 2;
                    }
            }
        };
        @Test
        public void perf2() throws InterruptedException {
            Thread[] threads = new Thread[THREAD_COUNT];
            for (int i = 0; i < THREAD_COUNT; i++)
                threads[i] = new Thread(TASK2);
    
            for (Thread t : threads)
                t.start();
    
            boolean alive = true;
            while (alive) {
                for (Thread t : threads) {
                    t.join(10);
                    if (alive = t.isAlive())
                        break;
                }
            }
    
            System.out.println("value : " + value2);
        }
    
        // 3. volatile 사용 - for문 연산 종료 후 동기화
        private static volatile long value3 = 0;
        private final static Runnable TASK3 = new Runnable() {
            @Override
            public void run() {
                long local_sum = 0;
                for (int i = 0; i < 50000000 / THREAD_COUNT; i++)
                    local_sum += 2;
    
                synchronized (Example5.class) {
                    value3 += local_sum;
                }
            }
        };
        @Test
        public void perf3() throws InterruptedException {
            Thread[] threads = new Thread[THREAD_COUNT];
            for (int i = 0; i < THREAD_COUNT; i++)
                threads[i] = new Thread(TASK3);
    
            for (Thread t : threads)
                t.start();
    
            boolean alive = true;
            while (alive) {
                for (Thread t : threads) {
                    t.join(10);
                    if (alive = t.isAlive())
                        break;
                }
            }
    
            System.out.println("value : " + value3);
        }
    }
    

  • 가짜 공유 (False Sharing)
    멀티 코어 CPU에서 발생할 수 있는 문제다. 멀티 코어 CPU에서는 데이터를 word 단위로 읽어오는 대신 메모리 I/O 효율성을 위해서 cache-line로 읽어오는데, 이때 문제가 생길 수 있다. 두개의 메모리 연산이 동일한 캐쉬라인에서 실행될 경우, CPU<->Memory 버스 사이에서 하드웨어적인 락이 걸리는데, 이때 하드웨어적인 병목현상이 발생한다.
    : 멀티코어에서는 A스레드와 B스레드에서 인접 메모리를 접근할때, 캐시에 있던 내용을 메모리에 반영하려 시도. 인접 메모리를 읽고 있는 상태이기에 병행 수행시 데이터의 유효성을 조금이라도 높이기 위해 메모리에 반영하는 과정에서 속도 저하가 발생하는 것.
    캐시 라인이라 함은 지역성에 근거해 인접한 데이터를 미리 읽어옴으로써 속도향상을 노리는 것

    지역성(locality)은 아래 추정에 근거합니다.
    1. 지금 읽힌 데이터는 이후에도 자주 사용될 가능성이 높다.
    2. 지금 읽힌 데이터와 인접한 데이터는 이어서 사용될 가능성이 높다.

    패딩(padding)를 통한 해결 -> 메모리를 손해보더라도 속도에서 이득을 보자
    : p1, p2, p3, p4, p5, p6(64byte)를 선언하여 캐쉬라인에 대한 패딩 확보 (annotation으로도 존재한다.)

    public final class X {
        public volatile int f1;
        public volatile int f2;
    }
    




    참고 : http://www.smallake.kr/?p=2271, http://elky.tistory.com/318, http://daniel.mitterdorfer.name/articles/2014/false-sharing/

    package kr.co.ioacademy; //iocademy 윤찬식 강사님 import org.junit.Test; import sun.misc.Contended; class C { public final static int NUM_THREADS = 8; public final static long ITERATIONS = 50L * 1000L * 1000L; } // 가짜 공유 문제(False Sharing) // 개념 : 두 스레드간의 공유가 발생하지 않음에도 캐시라인의 공유에 의해서 // 서로의 캐시를 무효화함으로 성능이 저하되는 문제. public class Example6 { @Test public void runTest() throws InterruptedException { Thread[] threads = new Thread[C.NUM_THREADS]; for (int i = 0; i < threads.length; i++) { threads[i] = new Task(i); } for (Thread t : threads) { t.start(); } for (Thread t : threads) { t.join(); } } } class Task extends Thread { static class Long { public volatile long value = 0L; public long p1, p2, p3, p4, p5, p6; // 64byte padding } private static Long[] longs = new Long[C.NUM_THREADS]; static { for (int i = 0; i < longs.length; i++) longs[i] = new Long(); } private int index; public Task(int index) { this.index = index; } @Override public void run() { long i = C.ITERATIONS + 1; while (0 != --i) { longs[index].value = i; } } }


'Programing > Java' 카테고리의 다른 글

Effective Java - Annotation  (0) 2016.03.08
Effective Java - Reflection  (0) 2016.03.08
Effective Java - 스레드(1)  (0) 2016.03.07
Effective Java - 불변 객체  (0) 2016.03.07
Effective Java - 객체 비교, 복제  (0) 2016.03.04
  • Thread 설계
    - Step 1. Single Thread Model
     AutoCloseable Interface

     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    // C99, C11(thread) - Android M
    // C++11/14
    // Java 8, 9
    
    public class Example3 {
        /*
       private static void handleRequest(Socket connection) {
          OutputStream os = null;
          try {
              os = connection.getOutputStream();
              os.write("Hello World".getBytes());
              os.flush();
          } catch (IOException e) {
              e.printStackTrace();
          } finally {
              if (os != null)
                  try {
                      os.close();
                  } catch (IOException e) {
    
                  }
          }
      }
      */
    
        // Try with Resource : Java 7 - (C# using block)
        // 1. Connection, Socket, Stream 비 메모리 자원을 해지하는
        //   명시적인 종료 메소드를 호출해야 하는 것은 코드를 어지럽힌다.
        // 2. try 블록에 존재하는 자원을 자동으로 회수해준다.
        // 3. AutoCloseable Interface
        private static void handleRequest(Socket connection) {
            try (OutputStream os = connection.getOutputStream();
                 InputStream is = connection.getInputStream()) {
                os.write("Hello World".getBytes());
                os.flush();
            } catch (IOException e) {
            }
        }
    
        
        // Step 1. Single Thread Model
        // 1) 한번에 하나의 요청을 처리하는 것이 가능하다.
        // 2) 단일 스레드에서 IO 작업 등으로 대기하는 경우, 하드웨어 자원을
        //    효과적으로 사용할 수 없다.
        public static void main(String[] args) throws IOException {
            ServerSocket socket = new ServerSocket(8080);
    
            while (true) {
                Socket connection = socket.accept();
                handleRequest(connection);
            }
        }
    

    - Step 2. Thread per Connection Model
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    // C99, C11(thread) - Android M
    // C++11/14
    // Java 8, 9
    
    public class Example3 {
        // Try with Resource : Java 7 - (C# using block)
        // 1. Connection, Socket, Stream 비 메모리 자원을 해지하는
        //   명시적인 종료 메소드를 호출해야 하는 것은 코드를 어지럽힌다.
        // 2. try 블록에 존재하는 자원을 자동으로 회수해준다.
        // 3. AutoCloseable Interface
        private static void handleRequest(Socket connection) {
            try (OutputStream os = connection.getOutputStream();
                 InputStream is = connection.getInputStream()) {
                os.write("Hello World".getBytes());
                os.flush();
            } catch (IOException e) {
            }
        }
        
    
        
        // Step 2. Thread per Connection Model
        // 순차적인 실행 방법보다 훨씬 더 많은 작업을 수행하는 것이 가능하다.
    
        // 문제점
        // 1. 엄청나게 많은 스레드가 생성된다.
        // 2. Thread 를 만들고 제거하는 작업에도 자원이 소모된다.
        // : 클라이언트의 요청이 작은 단위로 일어나는 경우에는 더욱더 문제가 심하다.
        // 3. 자원 낭비가 심하다.
        //  1) 실행 중인 스레드는 메모리를 많이 소모한다.
        //     Java의 스택은 두 개이다.(Java, Native)
        //  2) 프로세서의 개수 보다 많은 스레드가 생성되면 대부분의 스레드는
        //     대기 상태에 있다.
        // 4. 컨텍스트 스위칭의 비용이 크다.
        // 5. OS 에서 생성할 수 있는 스레드의 개수는 제한되어 있다.
        public static void main(String[] args) throws IOException {
            ServerSocket socket = new ServerSocket(8080);
            while (true) {
                final Socket connection = socket.accept();
                Runnable task = new Runnable() {
                    @Override
                    public void run() {
                        handleRequest(connection);
                    }
                };
    
                new Thread(task).start();
            }
        }
    

    - Step 3. Pool-based Model
     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    // C99, C11(thread) - Android M
    // C++11/14
    // Java 8, 9
    
    public class Example3 {
        // Try with Resource : Java 7 - (C# using block)
        // 1. Connection, Socket, Stream 비 메모리 자원을 해지하는
        //   명시적인 종료 메소드를 호출해야 하는 것은 코드를 어지럽힌다.
        // 2. try 블록에 존재하는 자원을 자동으로 회수해준다.
        // 3. AutoCloseable Interface
        private static void handleRequest(Socket connection) {
            try (OutputStream os = connection.getOutputStream();
                 InputStream is = connection.getInputStream()) {
                os.write("Hello World".getBytes());
                os.flush();
            } catch (IOException e) {
            }
        }
        
        // Step 3. Pool-based Model
        // 순차적으로 문제를 해결하는 방법은 응답 속도와 전체적인 성능이 떨어지는
        // 문제점이 있고 작업별로 스레드를 만들어 내는 방법은 자원 관리 측면에서
        // 문제점이 있다
    
        // 해결책)
        // 1) 스레드의 생성을 제한해야 한다 -> Thread pool
        // 2) 생산자 & 소비자 모델
    
        public static final int NTHREADS = 100;
        public static final Executor exec = Executors.newFixedThreadPool(NTHREADS);
        // 1) newFixedThreadPool
        // 작업이 등록되면 제한된 개수까지 스레드를 생성해서 해당 작업을 처리한다.
        // 생성 이후에 더 이상 생성하지 않고 스레드 수를 유지한다.
    
        // 2) newCachedThreadPool
        // 스레드 수가 처리할 작업보다 많아서 쉬는 스레드가 발생하면,
        // 스레드를 종료한다.
    
        // 3) newSingleThreadPool
        // 단일 스레드로 처리한다. 등록된 작업은 다양한 정책에 의해서 수행될 수 있다.
        // (LIFO, FIFO, 우선순위)
        public static void main(String[] args) throws IOException {
            ServerSocket socket = new ServerSocket(8080);
    
            while (true) {
                Socket connection = socket.accept();
                Runnable task = new Runnable() {
                    @Override
                    public void run() {
                        handleRequest(connection);
                    }
                };
    
                exec.execute(task);
            }
    
        }
    }
    

  • 스레드의 중단 및 종료 처리
    - 1_동기화
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    // 스레드의 중단 및 종료 처리
    // 1. 스레드를 만들고 시작하는 것은 쉬운 일이다.
    // 2. 스레드를 안전하고 빠르게 멈추게 하는 것은 어렵다.
    // 3. 자바의 스레드를 멈추는 기능이 폐기되었다.
    //   Thread.stop() / Thread.suspend()
    
    // 4. 스레드를 강제로 종료하면 공유되어 있는 여러 가지 상태가
    //    망가질 수 있다.
    // 5. 스레드를 멈춰달라는 요청이 오면 진행 중이던 모든 작업을 정리한 후
    //    스스로 종료하도록 만들어야 한다.
    
    
    import java.util.concurrent.TimeUnit;
    
    
    /*public class Example4 {
        private static boolean stopRequested = false;
        // 문제점 : main 스레드가 변경한 stopRequested 의 새로운 값을 background
        //        thread 가 관측할 수 없다.
        public static void main(String[] args) throws InterruptedException {
            Thread backgroundThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    long i = 0;
                    while (!stopRequested) {
                        i++;
                        //System.out.println("value - "  + i);
                    }
                }
            });
    
            backgroundThread.start();
    
            TimeUnit.SECONDS.sleep(3);
            stopRequested = true;
        }
    
    }
    */
    
    // 문제점 : main 스레드가 변경한 stopRequested 의 새로운 값을 background
    //        thread 가 관측할 수 없다.
    
    // 해결책 1. 동기화(synchronization)
    // 1) 상호 배제 : 다른 스레드가 변경 중인 객체의 상태를 관측할 수 없도록 해준다. - 락
    // 2) 동기화는 동기화 메소드 또는 동기화 블록에 의해 진입한 스레드가 동일한 락의 보호 아래
    //    이루어진 모든 변경을 관측할 수 있다.
    
    // 문제점 : 순환문의 각 단계마다 동기화를 실행하는 비용이 크다.
    
    
    public class Example4 {
        private static synchronized void stop() { stopRequested = true; }
        private static synchronized boolean isStopRequested() {
            return stopRequested;
        }
    
        private static boolean stopRequested = false;
    
        public static void main(String[] args) throws InterruptedException {
            Thread backgroundThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    long i = 0;
                    while (!isStopRequested()) {
                        i++;
                        System.out.println("value - "  + i);
                    }
                }
            });
    
            backgroundThread.start();
    
            TimeUnit.SECONDS.sleep(3);
            stop();
            stopRequested = true;
        }
    }
    

  • - 2_cv 제한자 (const-volatile)
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    // 스레드의 중단 및 종료 처리
    // 1. 스레드를 만들고 시작하는 것은 쉬운 일이다.
    // 2. 스레드를 안전하고 빠르게 멈추게 하는 것은 어렵다.
    // 3. 자바의 스레드를 멈추는 기능이 폐기되었다.
    //   Thread.stop() / Thread.suspend()
    
    // 4. 스레드를 강제로 종료하면 공유되어 있는 여러 가지 상태가
    //    망가질 수 있다.
    // 5. 스레드를 멈춰달라는 요청이 오면 진행 중이던 모든 작업을 정리한 후
    //    스스로 종료하도록 만들어야 한다.
    
    import java.util.concurrent.TimeUnit;
    
    // cv 제한자(const-volatile) : C, C++, C# - Debug / [Release]
    
    // 해결책 2. volatile
    // 정의 : 컴파일러가 최적화를 하지 말라는 지시어 (내부에 cache하여 사용하지 않도록 막음.)
    //   멀티 스레드 상에서 공유되는 변수, 하드웨어와 연결된 변수 사용시
    //   되도록 volatile 붙이자.
    // 효과 : 어떤 스레드건 가장 최근에 기록된 값을 읽을 수 있다.
    public class Example4 {
        private static volatile boolean stopRequested = false;
    
        public static void main(String[] args) throws InterruptedException {
            Thread backgroundThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    long i = 0;
                    while (!stopRequested) {
                        i++;
                        System.out.println("value - "  + i);
                    }
                }
            });
    
            backgroundThread.start();
    
            TimeUnit.SECONDS.sleep(3);
            stopRequested = true;
        }
    }
    


'Programing > Java' 카테고리의 다른 글

Effective Java - Reflection  (0) 2016.03.08
Effective Java - 스레드(2)  (0) 2016.03.07
Effective Java - 불변 객체  (0) 2016.03.07
Effective Java - 객체 비교, 복제  (0) 2016.03.04
Effective Java - 객체 소멸  (0) 2016.03.04
  • 1_불변 객체(Immutable Object)
    : String은 이미 불변 객체로 정의돼있다.

     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    // Java 의 클래스 = 레퍼런스 타입
    // : 객체는 힙에 생성된다.
    
    // Immutable Object(불변 객체)
    // 장점
    // 1. 생성자의 방어 복사 및 접근 메소드의 방어 복사가 필요없다.
    // 2. 병렬 프로그래밍을 작성할 때, 동기화 없이 객체를 공유 가능하다.
    //   "특별한 이유가 없다면 객체를 불변 객체로 설계해야 한다."
    //    : Effective Java, Effective Objective-C
    
    // 단점
    // 객체가 가지는 값마다 새로운 객체가 필요하다.
    // String s += "xxx";   // "Helloxxx"
    //  : 내용이 동일한 객체는 공유되는 메커니즘을 제공해야 한다.(Flyweight)
    //    - static factory method
    
    // 불변 클래스를 만드는 방법
    // 1. 객체를 변경하는 setter 를 제공하지 않습니다.
    // 2. 모든 필드를 final
    // 3. 가변 객체 참조 필드를 사용자가 얻을 수 없도록 해야 한다 (private)
    // 4. 상속 금지 (final class, final method, 생성자를 private 으로 정의하고 public static factory method를 제공)
    
    public class Example1 {
        public static void main(String[] args) {
            Point pos = new Point(10, 20);
    
            // Integer i;
    
            Rect r = new Rect(pos);
            // pos.setY(100);    // 공격!
    
            pos = r.getPosition();
            // pos.setX(-9999);
    
            String s = r.getName();
            s = "xxx";
    
            System.out.println(r);
        }
    }
    
    // String, Integer, Long ... : Immutable Object
    
    class Rect {
        // 캡슐화, 정보 은닉
        private final Point position;
        private String name;
    
        public Rect(Point position) {
            this.position = position.clone();
            this.name = "Tom";
        }
    
        public String getName() {
            return name;
        }
    
        public Point getPosition() {
            return position.clone();
        }
    
        @Override
        public String toString() {
            return "Rect{" +
                    "position=" + position +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    
    class Point implements Cloneable {
        final int x;
        final int y;
    
        @Override
        public Point clone() {
            try {
                return (Point) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return null;
        }
    
    
        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    
        public int getX() {
            return x;
        }
    
        public int getY() {
            return y;
        }
        /*
        public void setX(int x) {
            this.x = x;
        }
    
        public void setY(int y) {
            this.y = y;
        }
        */
    
        @Override
        public String toString() {
            return "Point{" +
                    "x=" + x +
                    ", y=" + y +
                    '}';
        }
    }
    

  • 2_불변 객체
    : final로 선언된 배열은 변하지 않지만, 배열 안의 데이터가 변할수 있다. -> VALUES 는 변하지 않지만 VALUE 안에 데이터가 변할수가 있다.

     
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.Collections;
    
    public class Example2 {
        /*
        private static final Integer[] VALUES =
                { 1, 2, 3, 4, 5 };
    
        // 해결 방법 1. 방어 복사본
        public static Integer[] values() {
            return VALUES.clone();
        }
        */
    
        // 해결 방법 2. 수정불가 컬렉션 사용 - UnsupportedOperationException
        private static final Integer[] PRIVATE_VALUES = {1, 2, 3, 4, 5};
        public static final Collection<Integer> VALUES =
                Collections.unmodifiableCollection(Arrays.asList(PRIVATE_VALUES));
    
        public static void main(String[] args) {
            Collection<Integer> arr = Example2.VALUES;
            arr.add(10);
    
            for (Integer e : VALUES) {
                System.out.println(e);
            }
        }
    }
    


'Programing > Java' 카테고리의 다른 글

Effective Java - 스레드(2)  (0) 2016.03.07
Effective Java - 스레드(1)  (0) 2016.03.07
Effective Java - 객체 비교, 복제  (0) 2016.03.04
Effective Java - 객체 소멸  (0) 2016.03.04
Junit 사용하기  (0) 2016.03.03
  • 객체 비교
    - 1_equals 재정의

    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.util.Objects;
    
    // 1. Object.equals()를 재정의하지 않을 경우
    // 모든 객체는 오직 자기 자신과 동일하다.
    
    // 2. 객체 동일성이 아닌 논리적 동일성의 개념을 제공하기 위해서는
    // equals()를 재정의해야 한다.
    
    class Point {
      private int mX;
      private int mY;
    
      public Point(int x, int y) {
        this.mX = x;
        this.mY = y;
      }
    
      @Override
      public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + mX;
        result = prime * result + mY;
        return result;
      }
    
      @Override
      public boolean equals(Object obj) {
        // 1. 자기 자신인지 검사 - 성능
        if (this == obj) return true;
        // 2. null 인지 체크 (모든 객체는 null과 동치 관계가 있지 않다.)
        if (obj == null) return false;
        // 3. 인자의 자료형이 정확한지 검사.
        if (!(obj instanceof Point)) return false;
        // 4. 자료형 변환
        Point p = (Point) obj;
    
        // 5. 중요 필드 점검
        return mX == p.mX && mY == p.mY;
      }
    }
    
    
    class Unit {
      private Point position;
      private Point start;
    
      // 1. 객체에 대한 참조는 null이 될 수 있다.
      // 필드가 많아지면 Objects.equal을 고려하자.(Google Guava / 1.7)
    
    
      @Override
      public boolean equals(Object obj) {
        if (obj == this) return true;
        if (obj == null) return false;
        if (!(obj instanceof Unit)) return false;
    
        Unit p = (Unit) obj;
        // if (position == null) {
        // if (p.position != null) return false;
        // } else if (!position.equals(p.position)) return false;
        // return true;
    
        return Objects.equals(position, p.position) && Objects.equals(start, p.start);
      }
    }
    
    public class Example5 {
      public static void main(String[] args) {
        Point p1 = new Point(10, 20);
        Point p2 = new Point(10, 20);
    
        if (p1.equals(p2)) {
          System.out.println("Same");
        } else {
          System.out.println("Not Same");
        }
      }
    
    }
    


    - 2_BigDecimal, 배열 비교
    : 컴퓨터는 태생적으로 부동소수점을 정확히 표현할 수 없다.(2진수로 표현하기 때문에...)
    : float, double은 == 로 비교하면 안된다. -> BigDecimal 사용
    : 배열의 내용 비교 -> Arrays.equals()

    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.math.BigDecimal;
    import java.util.Arrays;
    
    // 1. float이나 double은 ==으로 비교하면 안된다.
    // 2. 정밀한 연산을 필요로 한다면 BigDecimal 을 사용해야 한다.
    public class Example6 {
      public static void main(String[] args) {
    
        double value1 = 2.0 - 1.1;
        double value2 = 0.9;
    
        // if (value1 == value2) {
        if (Math.abs(value1 - value2) < 0.001) {
          System.out.println("same");
        } else {
          System.out.println("not same");
        }
    
        BigDecimal v = new BigDecimal(2.0).subtract(new BigDecimal(1.1));
        System.out.println(v);;
    
        // 주의 사항 : BigDecimal(String)의 생성자를 사용해야 한다.
        BigDecimal v2 = new BigDecimal("2.0").subtract(new BigDecimal("1.1"));
        System.out.println(v2);;
    
        //-------------------------------------------------
        // 3. 배열 내용을 비교하려면 Object.equals() 가 아닌 Arrays.equals()
        //    사용해야 한다.
        int[] arr1 = new int[20];
        int[] arr2 = new int[20];
    
        System.out.println(arr1.equals(arr2));
    
        System.out.println(Arrays.equals(arr1, arr2));
      }
    }

  • - 3_hashCode
     : Google Guava를 사용하여 toString 재정의
    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Objects;
    
    import com.google.common.base.MoreObjects;
    
    
    // 핵심 : 같은 객체는 동일한 해시 코드 값을 가져야 한다.
    // 즉 equals를 재정의한 클래스는 반드시 hashCode도 재정의해야 한다.
    // 그래야 HashMap, HashSet, HashTable 등 hash 기반 컬렉션과 함께 사용하면 오동작 하지 않는다.
    
    public class Example7 {
      public static void main(String[] args) {
        Map<Person, String> m = new HashMap<>();
    
        m.put(new Person("Tom", 42), "Google");
    
        System.out.println(m.get(new Person("Tom", 42)));
    
        // toString()을 잘 만들어 놓으면 편리하다.
        System.out.println(new Person("IU", 42));
      }
    }
    
    
    class Person {
      private String name;
      private int age;
    
      public Person(String name, int age) {
        this.name = name;
        this.age = age;
      }
    
      @Override
      public String toString() {
        return MoreObjects.toStringHelper(this).add("name", name).add("age", age).toString();
      }
    
      @Override
      public int hashCode() {
        // equals 에 이용된 중요 필드를 이용해서 hash를 생성하면 된다.
        return Objects.hash(name, age);
      }
    
      @Override
      public boolean equals(Object o) {
        if (o == this) return true;
        if (o instanceof Person) {
          Person p = (Person) o;
          return Objects.equals(name, p.name) && age == p.age;
        }
    
        return false;
      }
    }
    
    


  • 객체 복제
    : 인자 전달 방식
     -> Call-by-Value vs. Call-by-Reference

    : 객체 복사 방식
     -> 얕은 복사(Shallow Copy) vs. 깊은 복사(Deep Copy)

    class Num {
    	public int num;
    	
    	public Num(int num) {
    		this.num = num;
    	}
    }
    
    public class CallBy {
    	
    	// 값을 복사해서 인자 전달
    	// value는 객체에 대한 레퍼런스 값, 프리미티브 타입의 값
    	public void callByValue(int a, int b) { 
    		System.out.println("callByValue in method: " + a + " " + b);
    		int swap = a;
    		a = b;
    		b = swap;
    		System.out.println("callByValue out method: " + a + " " + b);
    		
    	}
    	
    	// 해당 객체를 참조하는 객체를 복사해서 인자 전달(얕은 복사)
    	// 해당 객체의 주소값을 직접 넘기는 것이 아니라 객체를 가리키고 있는 또 다른 주소값을 만들어서 넘긴다
    	void callByReference(Num a, Num b) {
    		System.out.println("callByReference in method: " + a.num + " " + b.num);
    		Num swap = a;
    		a = b;
    		b = swap;
    		System.out.println("callByReference out method: " + a.num + " " + b.num);
    	}
    	
    	// 객체의 멤버 필드값에 대한 복사가 필요(깊은 복사)
    	void callByReference2(Num a, Num b) {
    		System.out.println("callByReference2 in method: " + a.num + " " + b.num);
    		int swap = a.num;
    		a.num = b.num;
    		b.num = swap;
    		System.out.println("callByReference2 out method: " + a.num + " " + b.num);
    	}
    	
    	public static void main(String[] args) {
    		CallBy call = new CallBy();
    		
    		int a = 5;
    		int b = 10;
    		
    		Num n1 = new Num(5);
    		Num n2 = new Num(10);
    		
    		call.callByValue(a, b);
    		System.out.println("callByValue main method: " + a + " " + b + "\n");
    		//callByValue in method: 5 10
    		//callByValue out method: 10 5
    		//callByValue main method: 5 10
    		
    		call.callByReference(n1, n2);
    		System.out.println("callByReference main method: " + n1.num + " " + n2.num + "\n");
    		//callByReference in method: 5 10
    		//callByReference out method: 10 5
    		//callByReference main method: 5 10
    		
    		call.callByReference2(n1, n2);
    		System.out.println("callByReference2 main method: " + n1.num + " " + n2.num);
    		//callByReference2 in method: 5 10
    		//callByReference2 out method: 10 5
    		//callByReference2 main method: 10 5
    		
    	}
    }
    


    - 1_clone, Cloneable
     : 객체에 대한 깊은 복사
     : 
    상속을 해주기 위한 클래스를 설계할 때, 잘 동작하는 protected clone 메소드를 그 클래스에 두지 않는다면 서브 클래스에서 Cloneable 인터페이스를 제대로 구현할 수 없다.

    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import com.google.common.base.MoreObjects;
    
    class Point implements Cloneable {
      private int x;
      private int y;
    
      public Point(int x, int y) {
        this.x = x;
        this.y = y;
      }
    
      public int getX() {
        return x;
      }
    
      public void setX(int x) {
        this.x = x;
      }
    
      public int getY() {
        return y;
      }
    
      public void setY(int y) {
        this.y = y;
      }
    
      @Override
      public String toString() {
        return "(" + x + ", " + y + ")";
      }
    
      @Override
      public Point clone() {
        try {
          return (Point) super.clone();
        } catch (CloneNotSupportedException e) {
          e.printStackTrace();
        }
    
        return null;
      }
    }
    
    // 객체 복제 하기
    // 1. clone() 함수를 오버라이드 한다. (protected -> public)
    // : 오버라이딩할 메소드는 부모의 접근 제한자와 같거나 접근하기 더 쉬워야 한다.
    
    // 2. Cloneable 인터페이스를 구현해야 한다.
    // : 어떤 객체가 복제를 허용한다는 사실을 알리는데 쓰이는 용도이다.
    
    
    // 객체가 Cloneable 인터페이스를 구현하고 있으면, Object.clone() 은
    // 객체가 가지고 있는 모든 멤버를 복사한다.
    class Unit implements Cloneable {
      private String name;
      private int age;
    
      private Point position;
      // 중요 : 변경 가능 객체에 대한 참조를 가지고 있으면 문제가 발생한다.
    
      public Unit(String name, int age, Point pos) {
        this.name = name;
        this.age = age;
        this.position = pos;
      }
    
      public void setPos(int x, int y) {
        position.setX(x);
        position.setY(y);
      }
    
      
      // public Object clone() {
      // 공변 반환형 : 재정의 메소드의 리턴 타입은 재정의 되는 메소드의
      // 리턴 타입의 하위 클래스가 될 수 있다.(1.5)
      //  @Override
      //  public Unit clone() {
      //    try {
      //      return (Unit) super.clone();
      //    } catch (CloneNotSupportedException e) {
      //      e.printStackTrace();
      //    }
      //
      //    return null;
      //  }
    
      @Override
      public Unit clone() {
        try {
          // 1. 전체 복사 후
          Unit result = (Unit) super.clone();
    
          // 2. 변경 가능 객체 복제
          result.position = position.clone();
          return result;
    
        } catch (CloneNotSupportedException e) {
          e.printStackTrace();
        }
    
        return null;
      }
    
      @Override
      public String toString() {
        return MoreObjects.toStringHelper(this).add("name", name).add("age", age)
            .add("pos", position).toString();
      }
    }
    
    
    public class Example8 {
      public static void main(String[] args) {
        Unit p1 = new Unit("Tom", 42, new Point(0, 0));
    
        // Unit p2 = (Unit) p.clone();
        Unit p2 = p1.clone();
    
        p2.setPos(10, 20); // !!!
    
        System.out.println(p1);
        System.out.println(p2);
      }
    }
    


    - 2_생성자 방어 복사
     : 접근자를 이용하여 클라이언트가 값을 변경할 수 있다. -> 캡슐화가 깨짐
     : 객체를 복사할 때 객체의 참조를 리턴하는 것이 아니라 객체의 복사본을 리턴한다. pos.setX(9999)로 x값을 변경해도 복사한 객체의 값을 바꾸므로 기존의 객체의 값은 그대로 유지된다.

    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import com.google.common.base.MoreObjects;
    
    public class Example9 {
      public static void main(String[] args) {
        
        // 1. 생성자 방어 복사가 필요하다.
        //  : 인자의 유효성을 검사하기 전에 복사하고 나서, 
        //    원본이 아닌 복사본의 유효성을 검사해야 한다.
        Point pos = new Point(100, 200);
        // Unit unit = new Unit(pos);
        
        
        String name = "Tom";
        Unit unit = new Unit(pos, name);
        
        pos.setX(9999);
        
        // 2. 접근자 메소드에서도 참조를 방어 복사하여 리턴해야 한다.
        pos = unit.position();
        pos.setX(9999);
    
        System.out.println(unit);
      }
    }
    
    
    class Unit {
      private Point position;
      private String name;
      
      public Unit(Point pos, String name) {
        position = pos.clone();
        // position = pos;
        this.name = name;
      }
    
      public String name() {
        return name;
      }
      
      public Point position() {
        return position.clone();
      }
    
      @Override
      public String toString() {
        return MoreObjects.toStringHelper(this)
            .add("pos", position).add("name", name).toString();
      }
    
    }
    
    
    class Point implements Cloneable {
      private int x;
      private int y;
    
      public Point(int x, int y) {
        this.x = x;
        this.y = y;
      }
    
      public int getX() {
        return x;
      }
    
      public void setX(int x) {
        this.x = x;
      }
    
      public int getY() {
        return y;
      }
    
      public void setY(int y) {
        this.y = y;
      }
    
      @Override
      public String toString() {
        return "(" + x + ", " + y + ")";
      }
    
      @Override
      public Point clone() {
        try {
          return (Point) super.clone();
        } catch (CloneNotSupportedException e) {
          e.printStackTrace();
        }
    
        return null;
      }
    }
    


'Programing > Java' 카테고리의 다른 글

Effective Java - 스레드(1)  (0) 2016.03.07
Effective Java - 불변 객체  (0) 2016.03.07
Effective Java - 객체 소멸  (0) 2016.03.04
Junit 사용하기  (0) 2016.03.03
Effective Java - 객체 생성  (0) 2016.03.03
  • 1_명시적 자원 해지

    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.awt.Image;
    import java.io.IOException;
    import java.net.URL;
    
    import javax.imageio.ImageIO;
    
    // 핵심 : 객체 내부에서 비 메모리 자원을 사용한다면 명시적 종료 메소드를 제공해야 한다.
    // 이유
    // 1. 가비지 컬렉션은 메모리만 수집한다.
    // 2. 가비지 컬렉션이 수거하지 않는 비 메모리 자원에 대해서는
    // 프로그래머가 관리 해야 한다.
    
    // 3. finalize() - 종료자
    // 개념 : 더 이상 참조할 수 없는 객체의 메모리 공간을 회수 할 때 GC에 의해서 호출되는 메소드
    // 문제 : finalize()를 비 메모리 자원을 정리하는 용도로 사용하면 안된다.
    // 1. 즉시 실행된다는 보장이 없다.
    // 예) 종료자 안에서 파일 닫기. - JVM은 종료자를 천천히 실행하므로,
    // 열린 상태의 파일이 많이 남아 있을 수 있다.
    // 한번에 열 수 있는 파일의 개수에 제한이 있으므로 오류가 날 수 있다.
    // - 종료자의 실행 시점은 JVM의 구현에 의존한다.
    
    // 2. 반드시 실행된다는 보장도 없다.
    // - 자바 명세에는 종료자가 즉시 실행되어야 한다는 문구도 없지만,
    // 종료자가 반드시 실행되어야 한다는 문구도 없다.
    // 즉 종료자가 실행되지 않은 객체가 남은 상태로 프로그램이 종료할 수도 있다.
    // (동기화 객체 같은 것을 절대 종료자를 통해 반납하면 안된다)
    
    
    class WebPhoto {
      Image image;
    
      // 명시적인 종료 메소드 - OutputStream, InputStream, Socket 등
      public void release() {
        if (image != null) {
          image.flush();
        }
      }
    
      public WebPhoto(String imageUrl) {
        URL url;
        try {
          url = new URL(imageUrl);
          image = ImageIO.read(url);
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
    
    
    public class Example1 {
      public static void main(String[] args) throws InterruptedException {
        // WebPhoto가 더 이상 사용되지 않는다면, 객체 내부에서 사용하고 있는
        // 자원에 대해서 정리가 필요하다.
        WebPhoto photo = new WebPhoto("http://cfs7.tistory.com/image/14/tistory/2008/08/29/04/53/48b702410053a");
    
        // photo = null;
        // photo.release();
    
        // System.gc();
      }
    }
    

  • 2_finalize

    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.awt.Image;
    import java.io.IOException;
    import java.net.URL;
    
    import javax.imageio.ImageIO;
    
    // finalize()의 용도
    // : 명시적 종료 메소드 호출을 잊은 경우의 안정망을 제공할 수 있다.
    
    // 1. 그런 자원을 발견한 경우 반드시 경고 메세지를 남겨야 한다. (클라이언트 코드에 버그가 있는 것이므로)
    // 2. 부모의 종료자를 명시적으로 호출해야 한다.
    
    class WebPhoto {
      Image image;
    
      /*
      @Override
      public void finalize() {
        if (image != null) {
          System.err.println("Explicit termination method 'release' is not called");
          release();
        }
      }
      */
      
      @Override
      public void finalize() throws Throwable {
        try {
          if (image != null) {
            System.err.println("Explicit termination method 'release' is not called");
            release();
          }
        } finally {
          super.finalize();
        }
      }
      
      // 명시적인 종료 메소드 - OutputStream, InputStream, Socket 등
      public void release() {
        if (image != null) {
          image.flush();
        }
      }
    
      public WebPhoto(String imageUrl) {
        URL url;
        try {
          url = new URL(imageUrl);
          image = ImageIO.read(url);
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
    
    
    public class Example2 {
      public static void main(String[] args) throws InterruptedException {
        WebPhoto photo = new WebPhoto("https://t1.daumcdn.net/cfile/tistory/2761F44856D7F79F2A");
        // System.gc();
      }
    }
    

  • 3_finalize2

    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    import java.awt.Image;
    import java.io.IOException;
    import java.net.URL;
    
    import javax.imageio.ImageIO;
    
    // 명시적 자원 해지가 필요한 클래스는 결국 중복된 코드를 작성해야 한다.
    
    // 종료자의 역활을 일반화한 클래스
    final class CloseGuard {
      public static CloseGuard get() {
        return new CloseGuard();
      }
    
      private CloseGuard() {}
    
      private Throwable site;
    
      public void open(String closer) {
        if (closer == null) throw new NullPointerException("closer == null");
    
        String message = "Explicit termination method '" + closer + "' not called";
        site = new Throwable(message);
      }
    
      public void close() {
        site = null;
      }
    
      public void warnIfOpen() {
        if (site == null) return;
    
        System.err.println(site.toString());
      }
    }
    
    
    class WebPhoto {
      private Image image;
    
      // Surface.java
      private final CloseGuard mCloseGuard = CloseGuard.get();
    
      @Override
      public void finalize() throws Throwable {
        try {
          if (mCloseGuard != null) mCloseGuard.warnIfOpen();
          release();
    
        } finally {
          super.finalize();
        }
      }
    
      public void release() {
        if (image != null) {
          image.flush();
        }
    
        if (mCloseGuard != null) mCloseGuard.close();
      }
    
      public WebPhoto(String imageUrl) {
        URL url;
        try {
          url = new URL(imageUrl);
          image = ImageIO.read(url);
    
          mCloseGuard.open("release");
    
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
    
    
    public class Example3 {
      public static void main(String[] args) {
        WebPhoto photo = new WebPhoto("https://t1.daumcdn.net/cfile/tistory/2761F44856D7F79F2A");
        // System.gc();
      }
    }
    

  • 4_finalize3
    : Finalizer Guardian Idiom(종료자 보호 패턴)
    Image를 상속 받은 클래스(MyImage)의 객체 생성 -> 상위 클래스 Image 객체도 생성 -> MyImage 객체가 GC에 의해 해지 당할 때 하 상위 클래스(Image)의 guardian이란 멤버 변수도 해지 대상이 되고, 여기서 finalize() 호출

    package kr.co.ioacademy; //iocademy 윤찬식 강사님
    
    
    class Image {
    
      public void release() {
        System.out.println("Image 자원 해지");
      }
    
    //  @Override
    //  protected void finalize() throws Throwable {
    //    try {
    //      System.out.println("Image finalize!");
    //      release();
    //    } finally {
    //      super.finalize();
    //    }
    //  }
    
      // 하위 클래스가 부모 클래스의 finalize()를 잊는 문제를
      // 방지하는 방법. - (종료자 보호 패턴)Finalizer Guardian Idiom
      @SuppressWarnings("unused")
      private final Object guardian = new Object() {
        @Override
        protected void finalize() throws Throwable {
          release();
        }
      };
    }
    
    class MyImage extends Image {
    
      @Override
      protected void finalize() throws Throwable {
        System.out.println("MyImage finalize!");
        // 잊었다.!!!
        // super.finalize();
      }
    }
    
    
    public class Example4 {
      public static void main(String[] args) throws InterruptedException {
        MyImage image = new MyImage();
        image = null;
    
        System.gc();
    
        Thread.sleep(10000);
      }
    }
    


'Programing > Java' 카테고리의 다른 글

Effective Java - 불변 객체  (0) 2016.03.07
Effective Java - 객체 비교, 복제  (0) 2016.03.04
Junit 사용하기  (0) 2016.03.03
Effective Java - 객체 생성  (0) 2016.03.03
Java Reference와 GC  (0) 2016.02.29
출처: http://blog.woniper.net/271

junit을 java에서 사용하기 위해서는 2가지 라이브러리가 필요하다.

junit.jar와 hamcrest-core.jar가 필요하다. 다운은 https://github.com/junit-team/junit/wiki/Download-and-Install 여기서 받을 수 있고, junit 공식 사이트는 http://junit.org/


junit 사용법을 설명하기 전에 Printer 예제를 만들어보았다. 

Printer 예제는 git소스를 참고하고 설명은 하지 않겠다. 사실 junit 사용법만 익히기 위해서 Printer 예제는 볼 필요없다. 아래 설명한 어노테이션만 잘 숙지하면 된다.

아래는 Printer를 테스트하는 테스트 코드이다.

  • @Before : @Test 메소드가 있는 클래스는 @Test 메소드가 실행 될 때마다 객체가 생성되고 실행된다. 쉽게 말해서 @Test 메소드가 2개라면 2번 실행하기 위해 객체 생성을 2번한다는 말이다. @Before 메소드는 @Test 메소드가 실행되기 전에 반드시 실행되게한다. @Test 메소드가 마다 공통으로 사용하는 데이터를 @Before 메소드에서 실행하는 것인데, 여기서 공통으로 사용하는 데이터를 fixture라고 한다.
  • @BeforeClass : @Before에서 설명 했듯이 @Test 메소드 실행 할 때마다 객체를 생성한다고 설명했는데, 이때 생성되는 객체가 최초에 한번만 실행되는 어노테이션이다.
  • @After : @Test 메소드가 실행된 후 실행된다. 
  • @AfterClass : 객체가 최초 한번 생성 된 후 실행된다.
  • @Test : @Test 메소드는 테스트를 수행하는 메소드다. 아래 예제에 expected가 붙은 @Test가 있는데 이는 예외 상황을 테스트 하기 위한 기능이다.
  • @Ignore : 테스트를 실행하지 않게 한다. @Test가 붙어 있지만, @Ignore가 붙어 있는 메소드라면 테스트 대상 메소드가 아니다.
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
package net.woniper.se.junit.test;
 
import net.woniper.se.junit.BlackPrint;
import net.woniper.se.junit.NotSupportImagePrint;
import net.woniper.se.junit.Printer;
import org.junit.*;
 
public class PrintTest {
 
    Printer printer;
 
    @Before
    public void setUp() throws Exception {
        System.out.println("setUp");
        printer = new Printer();
    }
 
    @BeforeClass
    public static void beforeClass() {
        System.out.println("beforeClass");
    }
 
    @Test
    public void testColorPrint() throws Exception {
        printer.btnPrint();
        Assert.assertEquals("COLOR", printer.getKind());
    }
 
    @Test
    public void testBlackPrint() throws Exception {
        printer.setPrint(new BlackPrint());
        Assert.assertEquals("BLACK", printer.getKind());
    }
 
    @Test
    @Ignore
    public void testIgnore() throws Exception {
        System.out.println("testIgnore");
    }
 
    @Test(expected = NotSupportImagePrint.class)
    public void testImagePrint() throws Exception {
        printer.setPrint(new BlackPrint());
        printer.imagePrint();
    }
 
    @After
    public void after() {
        System.out.println("after");
        printer.off();
    }
 
    @AfterClass
    public static void afterClass() {
        System.out.println("afterClass");
    }
}


'Programing > Java' 카테고리의 다른 글

Effective Java - 객체 비교, 복제  (0) 2016.03.04
Effective Java - 객체 소멸  (0) 2016.03.04
Effective Java - 객체 생성  (0) 2016.03.03
Java Reference와 GC  (0) 2016.02.29
Effective Java - Reference  (0) 2016.02.29
  • 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

+ Recent posts