• m부터 n까지의 소수를 구하는 문제 


    #define N 1000000
    #include <iostream>
    using namespace std;
    
    bool prime[N+1]; // true 소수 x, false 소수 o
    
    int main()
    {
    	prime[0] = prime[1] = true;
    	for (int i = 2; i*i <= N; i++) {
    		if (prime[i] == false) {
    			for (int j = i*i; j <= N; j+=i) {
    				prime[j] = true;
    			}
    		}
    	}
    	
    	int m, n;
    	cin >> m >> n;
    	cin.sync_with_stdio(false);
    
    	for (int i = m; i <= n; i++) {
    		if (prime[i] == false)
    			// cout << i << endl;
    			// cout은 stdio와의 동기화 때문에 느림 -> sync_with_stdio를 false
    			// endl을 하게되면 매번 출력 버퍼를 비움 -> '\n'으로 개행
    			cout << i << "\n";
    	}
    	return 0;
    }
    


'Algorithm' 카테고리의 다른 글

Dynamic Programming  (0) 2016.04.04
알고리즘 - 보간 탐색  (0) 2015.11.14
알고리즘 - 이진 탐색  (0) 2015.11.14
알고리즘 - 순차 탐색  (1) 2015.11.14
알고리즘 - 위상 정렬  (0) 2015.11.14

[현장] 전설의 개발자 제프 딘, 한국 개발자를 만나다

최근 알파고와 이세돌 9단의 대국에 많은 관심이 쏠리고 있습니다. 데미스 하사비스 구글 딥마인드 최고경영자부터 에릭 슈미트 알파벳 회장까지 한국에 방문해 인간과 인공지능과의 대결에 관심을 보이고 있는데요. 지난 월요일 알파고 인기 못지 않게 유명한 구글 직원이 한국에 방문했습니다. 바로 제프 딘이라는 구글 시니어 펠로우입니다.

제프 딘은 일반인에게는 조금 생소하지만 IT 업계에서는 널리 알려진 ‘전설’같은 인물입니다. 제프 딘은 1999년 중반 구글에 합류했는데요. 구글의 대부분의 제품의 기본이 되는 구글의 크롤링, 인덱싱 및 쿼리 서빙 시스템을 비롯해 구글의 주요 초기 광고 모델과 애드센스 시스템을 공동 설계하고 구현하는 데 기여한 인물입니다. 뿐만 아니라 ‘스패너’, ‘빅테이블’, ‘맵리듀스’ 등을 개발한 엔지니어로 현재는 머신러닝을 위한 대용량 분산 시스템에 대해 연구하고 있습니다.

제프 딘은 3월7일 구글 캠퍼스에서 머신러닝에 대한 강연을 하고 한국 엔지니어들과 1시간 가량 직접 소통하는 시간을 가졌습니다. 이날 행사에는 500명이 넘는 한국 개발자가 등록 신청서를 제출했다고 합니다. 구글코리아는 사전 신청서와 질문 내용을 확인하고 200여명의 개발자만 추려 현장에 참석할 수 있는 기회를 제공했습니다. 강의는 2시부터 시작했지만, 이미 1시30분터 구글캠퍼스 강연장에는 사람이 꽉 찼습니다.  2시가 되자 발 디딜 틈이 없었습니다. 실제 참여자는 210명이 넘었다고 하네요.

jeff_dean_01

▲행사는 사전 등록해서 뽑힌 사람만 참여할 수 있었습니다

jeff_dean_02

▲행사는 사전 등록해서 뽑힌 사람만 참여할 수 있었습니다

이날 행사는 유튜브에 생중계되기도 했는데요. 영상은 현재 무료로 공개됐습니다. 2시 정각 드디어 제프 딘이 등장했습니다. 여기저기서 휴대폰을 들고 사진을 찍는 모습을 볼 수 있었습니다. 마치 연예인을 본 것처럼 말이죠. 제프 딘은 ‘텐서플로’의 로고가 들어간 티셔츠를 입고 등장해 눈길을 끌었습니다. 텐서플로는 최근 구글이 공개한 오픈소스 머신러닝 기술이죠.

jeff_dean_03

▲제프 딘 구글 개발자를 소개하는 권순선 구글 APAC 개발자 플랫폼 총괄

jeff_dean_04

▲제프 딘 구글 시니어 펠로우

이날 발표 주제는 크게 2가지였습니다. 딥 뉴런 러닝이 무엇인지 이야기하고, 텐서플로를 소개했죠. 머신러닝에 대한 간략한 개론을 알고 싶은 분이라면 이번 강의가 도움이 되실 겁니다.

제프 딘 발표 영상 다시보기

현장에서 따로 통역이 지원되지 않았습니다. 현재 공개된 유튜브 영상에서도 자막이 지원되지는 않습니다. 1시간 가량 강연이 끝나고 3시부터는 약 30분간 질의응답 시간을 가졌는데요. 사전 질문을 미리 받아 답변하고, 나머지는 즉석에서 질문과 답변을 주고받았습니다. 현장에서 질문하려는 사람은 굉장히 많았는데요. 한 번에 10여명의 참가자가 동시에 손을 드는 풍경이 이어졌습니다. 참가자들 상당수는 이 질의응답 시간에서 좋은 정보들을 받았다고 후기를 남겼습니다. 제프 딘은 강연이 끝난뒤 추가 질문이 있는 10여명의 개발자들과 일일이 대화를 이어갔습니다.

jeff_dean_00

다음과 제프딘과 한국 개발자들 사이에서 진행된 일부 질의응답 내용입니다.

질문 : 인공지능과 사람의 지능과의 가장 큰 차이점은 무엇인가 ?

답변 : 인간의 지능이 인공지능보다 더 나은 점은 현상을 관찰하고 세상을 이해하는 능력이다. 인간은 다양한 상황을 관찰을 통해 이해한다. 예를 들어, 사람은 한번도 보지 못한 물건을 집어도 무엇을 해야 할지 이전의 경험과 정보를 기반으로 행동할 수 있다. 높은 수준의 언어를 이해하는 것도 비슷한 맥락이다. 컴퓨터는 이러한 능력이 부족하다. 하지만 우리는 열심히 이러한 문제를 해결하고 있는 중이다.

질문 : 알파고와 이세돌 9단의 대결에서 누가 이길 것 같은가?

답변 : 모두가 이번 대국에 관심이 많은 것 같다. 잘 모르겠다. 알파고는 지난번 유럽 챔피언 바둑 기사를 이겼다. 그 후 5개월 동안 알파고도 많이 배웠을 것이다. 알파고는 경험을 통해 배우지 않나. 승패의 핵심은 5개월 동안 알파고가 충분히 배웠는가인데, 잘 모르겠다.(웃음)

질문 : 현재 ‘CTNK’나 ‘카페’ 등 머신러닝와 관련된 오픈소스 라이브러리가 많다. 이와 비교했을 때 텐서플로만의 장점은 무엇인가?

답변 : 현재 다른 종류의 오픈소스 머신러닝 기술이 있고, 서로 다른 특징을 가지고 있다. 구글도 다른 머신러닝 기술의 장점을 가져오려고 많이 노력하고 있다. 예를 들어 자동화를 어떻게 할 것인지 등을 고민했다. 텐서플로의 장점은 유연하다는 것이다. 연구 영역에서는 더욱 그렇다. 또한 연구환경 뿐만 아니라 기업에서 실제 기술에 배포해서 쓸 수 있도록 다양한 지원을 해주고 있다. 데이터센터나 모바일 앱에 적용할 수 있다. 실제로 구글은 안드로이드 앱에 텐서플로를 적용해 활용하고 있다.

질문 : 당신과 같이 좋은 개발자가 되려면 매일 무엇을 해야 할까?

답변 : 아침을 먹어라. (웃음) 내가 좋아하는 것은 내가 알지 못하는 다른 분야 전문가와 만나고 이야기하는 것이다. 다른 전문가와 함께 이야기 하면 혼자 내놓을 수 있는 결과보다 훨씬 다른 결과를 만들 수 있다. 다른 사람의 전문분야에서 무엇인가를 배울 수 있다고 본다. 그러한 경험이 다음에 무엇인가 시작할 때 도움을 받을 수 있다.

질문 : 작은 회사에서는 양질의 데이터를 대량으로 얻기 힘들다. 대학에서도 그렇다. 많은 컴퓨팅 자원을 활용하기도 힘들다. 작은 회사나 그룹에서 머신러닝을 어떻게 연구해야 하는지 조언해 줄 수 있나?

답변 : 큰 회사는 많은 데이터를 접근할 수 있다는 장점을 가진다. 작은 회사라면 외부에 공개된 데이터들도 일단 풀고 싶은 문제를 해결하기 위해 모델을 만들 수 있을 것이다. 똑같지는 않지만 비슷한 모델을 만들 수 있다. 그리고 조금씩 뜯어고치면서 더 나은 훈련을 할 수 있을 거라고 본다. 구글이 향후 새로운 데이터를 공개할 것인지는 여기서 언급할 수 없다. ‘이미지넷’ 데이터는 이미 외부에 공개했다. 이 데이터는 컴퓨터 비전 분야에 영향을 주었다. 컴퓨팅 자원 문제는 클라우드를 이용하면서 해결할 수 있을 거라고 본다.

질문 : 엔지니어로 살아오면서 저질렀던 가장 큰 실수는 무엇인가? 또 그 실수는 어떻게 해결했는가?

답변 : 아주 많다. ‘빅테이블’이라고 있다. 2004-2005년께 만든 시스템이다. 그 시스템에는 분산 트랜잭션(distributed transaction)과 관련된 도구가 없다. 많은 팀이 그 기능을 원했다. 이 기능이 없어서 제대로 동작 안 되는 부분이 있었다. 그래서 빅테이블 핵심 기술 안에 분산 트랜잭션 기능이 있었어야 하는거 아닌가라는 생각이 들었다. 향후 ‘스패너’를 만들어 보안하기도 했다.

질문 : 만약에 게임을 스스로 할 수 있는 AI를 만든다고 치자. 그 AI를 만드는 개발자는 해당 게임을 아주 전문적으로 잘 해야 하는가? 비슷한 질문으로, 알파고 연구자들은 바둑을 잘 두는가?

답변 : 질문을 조금 확장해서 이야기하자면, 당신이 만약에 특정 문제를 머신러닝으로 풀려고 한다면, 당신은 해당 문제와 관련한 분야에 전문성이 있어야 한다. 또한 관련 전문가가 함께 일한다면 도움을 받을 수 있다. 하지만 전문가가 없는 경우가 대부분이다. 예를 들어, 의학 이미지를 연구한다고 했을 때 엑스레이 사진을 해석할 수 있는 전문가가 옆에 있으면 좋을 것이다. 그렇다고 모두가 엑스레이 전문가가 될 필요는 없다. 알파고 연구원들 중에는 바둑을 둘 수 있는 사람이 몇 명 있는 걸로 알고 있다. 또한 바둑을 잘 모르는 연구원도 있다.

질문 : 당신은 그동안 많은 문제를 해결했다. 문제 해결을 보통 어떻게 하는가? 구글 검색도 하는가? 아니면 동료에게 물어보는가?

답변 : 둘 다 맞는것 같다. 검색엔진은 유용하지 않나. (웃음) 나도 검색을 한다. 동료와 토론하는 것도 아주 유용하다. 동료와 토론할 수 있는 문화는 구글 환경에서 좋아하는 부분이다. 구글에는 서로 배경이 다른 다양한 사람이 있다. 가끔씩 아주 놀라울 정도로 넓고 다양한 지식을 가진 사람이 있다. 그런 사람들에게 질문을 하고 배우기도 한다.

질문 : 딥러닝은 현재 음성, 비디오, 언어 분야에 적용돼 활용되고 있다. 앞으로 구글은 어떤 애플리케이션에 딥러닝을 적용할 예정인가? 딥러닝의 수준을 어떻게 평가하는가?

답변: 로봇이나 헬스케어쪽에 딥러닝이 더 적용되지 않을까 싶다. 머신러닝을 적용하기에 유용한 분야다. 딥러닝의 수준을 파악할 때 필요한것은 기계가 이전에 풀어본적 있는 비슷한 문제를 다시 풀 수 있느냐다. 새로운 데이터를 적용해서 풀 수 있는지도 중요하다.

질문 : ‘제프 딘의 29가지 진실‘에서 진짜 내용은 얼마나 있는가?

답변 : 제프딘의 진실은 사실 동료가 만우절 장난으로 만든거다. 그게 여기저기 퍼졌다. 굉장히 나를 칭찬하는 것처럼 이야기했는데, 조금 민망하다. (웃음)

질문 : 가장 좋아하는 언어는 무엇인가?

답변 : 나는 C++와 애증 관계에 있다. (웃음) C++는 아주 유용하지만 동시에 최근 몇 년간 복잡해졌다. C++11, C++14가 나오지 않았나. 새로운 기능이 나오기도 했다. 보통 C++로 작업한다. 하지만 구글 동료들이 만든 ‘고’ 언어에 대해서도 관심이 많다. 실제로 고로 프로그래밍을 해본 적은 별로 없다. 하지만 고는 간단하고 로우 레벨 시스템을 개발하는 데 도움이 된다고 본다.

질문 : 텐서플로 티셔츠는 어떻게 얻을 수 있는가?

답변 : 오늘은 하나밖에 안 가져왔다. (웃음)

3월7일 행사 참가자 중 상당수는 향후 업무에 딥러닝을 적용할 계획을 가지고 있었습니다. 머신러닝 연구 때 사용할 수 있는 데이터에 대해 관심을 보이기도 했습니다. 고등학생, 대학생, 게임회사 개발자, 스타트업 CEO 등 다양한 출신의 개발자를 볼 수 있었습니다. 이들의 소감을 들어볼까요.

올해 18살인 이승우 학생은 “머신러닝 스터디 모임에 참여하면서 제프 딘 행사도 있다는 것을 알고 오고 싶었다”라며 “한국에도 데이터세트가 API를 통해서라도 개방돼 머신러닝 연구원들에게 많은 길이 열렸으면 좋지 않을까 생각했다”라고 밝혔습니다. 딥러닝 전문 기업을 운영하는 김선우 딥바이오 CEO는 “제프 딘은 최근에 딥러닝 분야에서 여러 발표를 하던 분이라 딥러닝 공부하는 사람으로서 그 존재를 이미 알고 있었다”라며 “데이터가 많아질수록 큰 규모의 모델과 더 많은 계산이 필요하고 이로 인해 좋은 결과를 낼 수 있다는 말이 인상적이었다”라고 소회를 밝혔습니다.

외국인이자 텀블벅에서 일하고 있는 비욘 개발자는 “제프 딘은 가장 뛰어난 컴퓨터과학자 중 한명으로 널리 알려져 있다”라며 “텐서플로우에 대한 소개와 딥러닝을 구글에서 어떻게 활용하는지 이야기하는 부분이 기억난다”라고 말하더군요. 또한 차창호 프리랜스 개발자는 “AI 엔지니어는 아니지만 최근 트렌드가 되니깐 계속 관련 논문이나 글을 찾아보고 있다”라며 “이번 발표는 아주 새로운 이야기를 하는건 아니지만 제프 딘을 통해 직접 설명을 들을 수 있다는 점이 의미가 있었다”라고 말했습니다. 서재원 서강대 대학생은 “제프 딘은 텐서플로우를 만든 사람으로 알고 있다”라며 “실제 발표를 듣고 만나니 상당히 대단한 분이라고 생각이 들더라”라고 말했고요. 길소연 대학원생은 “향후 업무에서 머신러닝을 도입할 예정”이라며 “작은 회사에서 데이터들을 어떻게 얻고 활용해야 하는지, 또 학생으로서 딥러닝 공부는 어떻게 해야되는지 대한 답을 얻을 수 있었다”라고 소감을 밝혔습니다.

이번 행사를 총괄한 권순선 구글 APAC 개발자 플랫폼 총괄은 “오늘 이렇게나 많은 질문이 나올지 몰랐다”라며 “참여 개발자들의 열정과 관심에 놀랐다”라고 행사에 만족감을 표시했습니다.

출처: https://www.bloter.net/archives/251561


'Others' 카테고리의 다른 글

OOP - S.O.L.I.D  (0) 2016.03.22
Bluetooth Low Energy  (0) 2016.01.26
코드 하이라이트 사용법  (0) 2015.10.26
  • 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

+ Recent posts