출처 : https://noritersand.github.io/java/2013/12/05/java-%EC%9E%90%EB%B0%94-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98-reflection/

관련 문서



리플렉션이란 인스턴스로 클래스의 정보를 분석하기 위한 기법이면서, 자바에서 해당 기법을 구현한 클래스와 패키지의 통칭이다.

변수의 이름을 파라미터로 다루기

public class Test {
    String person1 = "사람1";
    String person2 = "사람2";

    public static void main(String[] args) throws Exception {
        Test test = new Test();
    }
    void getValue(String who) throws Exception {
        System.out.println("show something..");
    }
}


일반적으로는 다음처럼 가장 간단하고 고전적인 방식인, 비교에 의한 분기문으로 구현할 수 있을것이다:



String 타입의 인스턴스 변수인 person1, person2가 선언되어 있는 클래스가 있다. 그리고 그 필드의 값을 파라미터에 따라 선택하여 출력하는
 getValue() 메서드를 구현한다고 하자.

void getValue(String who) throws Exception {
    if (who.equals("person1")) {
        System.out.println(this.person1);
    } else if (who.equals("person2")) {
        System.out.println(this.person2);
    }
}


만약 분기문을 사용하지 않고 Field 클래스를 이용해 변수명을 문자열로 취급하려면 다음처럼 작성한다:

import java.lang.reflect.Field;

public class Test {
    String person1 = "사람1";
    String person2 = "사람2";

    public static void main(String[] args) throws Exception {
        Test test = new Test();
        test.getValue("person2");
    }

    void getValue(String who) throws Exception {
        Class<?> cls = this.getClass();
        Field person = cls.getDeclaredField(who);
        System.out.println(person.getName()); // person2
        System.out.println(person.get(this)); // 사람2
    }
}
Test test = new Test();
Field target = test.getDeclaredField("내가 찾아야 할 녀섴");


인스턴스의 클래스값을 getClass로 가져와 getDeclaredField 메서드를 이용하면 해당 필드에 관한 정보를 가져올 수 있다.

target.get(test);


이 정보는 Field 클래스 타입이며 여기서 getName 혹은 get을 이용하면 프로퍼티의 이름과 값을 구할 수 있다. 여기서 get메서드의 인자값으로는 같은 타입의 인스턴스 객체를 넘겨줘야한다.

setAccessible()

public void setAccessible(boolean flag)

setAccessible()은 필드나 메서드의 접근제어 지시자에 의한 제어를 변경한다.

일반적으로 private 인스턴스 변수나 메서드는 해당 클래스의 외부에서는 접근할 수 없다. 가령 다음처럼 private으로 지정된 some 변수에 접근하려고 하면 예외가 발생할 것이다.

class AccessTest {
    private String some = "yo";
    private String getSome() {
        return this.some;
    }
}

public class MainClass {
    public static void main(String[] args) throws Exception {
      AccessTest accessTest = new AccessTest();

      System.out.println(accessTest.some); // compile error: The field AccessTest.some is not visible

      Field field = accessTest.getClass().getDeclaredField("some");
      System.out.println("field: " + field.get(accessTest));
      /*
       * runtime error:
       *     java.lang.IllegalAccessException:
       *     Class MainClass can not access a member of class AccessTest with modifiers "private"
       */
    }
}

이때 setAccessible(true)를 사용하면 문제 없이 접근할 수 있게 된다:

class AccessTest {
    private String some = "yo";
    private String getSome() {
        return this.some;
    }
}

public class MainClass {
    public static void main(String[] args) throws Exception {
      AccessTest accessTest = new AccessTest();

      Field field = accessTest.getClass().getDeclaredField("some");
      field.setAccessible(true);
      System.out.println("field: " + field.get(accessTest));

//    System.out.println(accessTest.some); // 이것은 여전히 불가능하다.

      Method method = accessTest.getClass().getDeclaredMethod("getSome");
      method.setAccessible(true);
      System.out.println("method: " + method.invoke(accessTest, (Object[]) null));

//    System.out.println(accessTest.getSome()); // compile error: The method getSome() from the type AccessTest is not visible

      method.setAccessible(false);
      System.out.println("method: " + method.invoke(accessTest, (Object[]) null));
      /*
       * setAccessible(false)는 접근제어를 원래 상태로 돌려놓기 때문에 다음과 같은 런타임 에러가 발생할 것이다.
       * runtime error:
       *     java.lang.IllegalAccessException:
       *     Class PrivateVariableAcc can not access a member of class AccessTest with modifiers "private"
       */
    }
}

단, 이 방법은 reflect를 통한 필드나 메서드 접근에 한하며 접근연산자(.)를 통한 방식은 불가능하다.

example

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ReflectionTest {
    @SuppressWarnings("unused")
    private static final Logger log = LoggerFactory.getLogger(ReflectionTest.class);

    @Test
    public void test() throws Exception {
        Object instance = new MyClass();
        Class<?> clazz = instance.getClass();

        // invoke method
        Method method = clazz.getDeclaredMethod("myMethod");
        Assert.assertNotNull(method);
        Assert.assertEquals("finally you found me!", method.invoke(instance));

        // access field
        Field field = clazz.getDeclaredField("myField");
        Assert.assertNotNull(field);
        Assert.assertEquals(0, field.get(instance));
    }
}

class MyClass {
    public int myField = 0;

    public String myMethod() {
        return "finally you found me!";
    }
}




출처 : http://devlsh.tistory.com/entry/Java-Naming-Convention


아주 오래전에 읽었봤었지만 그때는 중요성도 모르고 그저 그런가보다 하고 지나갔던 코딩컨벤션
특히 네이밍이 코딩에서 가장 어려운 일이라는 걸 느껴본 사람이라면 다시 한번 보시길...

http://geosoft.no/development/javastyle.html

가장 중요한 말은 이 모든 규칙들은 가독성을 위해서라면 위반되어도 된다는 말.

  1. 필드는 ‘_’ 접미사를 사용한다.
  2. 변수의 길이는 영역과 비례한다.
  3. 객체명의 의미를 메소드명에 넣지마라.?line.getLength(); // NOT: line.getLineLength
  4. compute, find, initialize 사용
  5. 컬렉션은 복수형으로
  6. 개수를 나타내는 변수는 ’n’ 접두사를 사용한다.
  7. 엔터티 번호에는 ‘i’ 접두사나 ‘No’ 접미사를 사용한다.
  8. 대응하는 단어를 사용한다.?get/set , add/remove , create/destroy , start/stop , insert/delete , increment/decrement , old/new , begin/end , first/last , up/down , min/max , next/previous , old/new , open/close , show/hide , suspend/resume
  9. 함수명은 ‘처리 후 무엇을 리턴하는지’, 프로시저명은 ‘무엇을 처리하는지’를 나타낸다.
  10. 인터페이스 디폴트 구현은 ‘Default’ 접두사를 사용한다.
  11. 팩토리 메소드의 이름은 ‘new’ 접두사에 반환하는 인스턴스의 클래스명으로 한다.?public Point newPoint(...)
  12. 메소드 선언 규칙은 <access> static abstract synchronized <unusual> final native 순서이다.
  13. do-while 사용 자제
  14. 조건이 복잡한 경우 임시 boolean으로 단순화 한다.
  15. 정상적인 경우를 if에 놓고 예외를 else에 둔다.
  16. 조건문과 한줄에 놓지 마라.?디버깅을 위해서
  17. 조건문 안에 실행문이 있어서는 안된다.
  18. 특정 의미를 갖는 숫자는 항상 의미를 나타내는 상수로 바꿔서 사용한다.
  19. 빈 for문은 ‘;’(세미콜론)을 새 줄에 놓는다.?for(<initialization>; <condition>; <update>)? ;
  20. 복잡한 메소드는 주석을 달지 말고 새로 만든다.(self-documenting)
  21. javadoc주석을 제외한 주석은 multi-line 주석이라고 해도 ‘//‘를 사용한다.?/* */주석은 디버깅용으로 언제든 제거해서 사용 할 수 있게 한다.
  22. 컬렉션은 포함하는 유형을 주석으로 뒤에 넣는다.?private Vector points_; // of Point
  23. 모든 public 클래스, 또 publics 클래스내의 public, protected 메소드는 javadoc을 만든다.


Java naming은 아니지만 참고할만한 http://msdn.microsoft.com/en-us/library/xzf533w0(v=VS.71).aspx


Java Naming Conventions

 Identifier Type

 Identifier Type

 Examples

 Packages

 유일 패키지명의 접두어는 항상 소문자로 된 ASCII 문자로 쓰며현재까지의 최상위 도메인명인 com, edu, gov, mil, net, org중에 하나이거나, ISO 표준 3166 (1981)에서 지정한 두글자로 된 영문 국가 코드 중에 하나여야 한다.

패키지명의 나머지 요소들은 조직의 내부 네이밍 규칙에 따라 바뀐다이러한 규칙들에 따르면 어떤 디렉토리명 요소에는 부서명프로젝트명기관명로그인 사용자명 등이 올 수 있다.

 com.sun.eng
 com.apple.quicktime.v2
 edu.cmu.cs.bovik.cheese

 

 Classes

 클래스명은 명사여야 하고각 단어의 첫글자는 대문자로 한다클래스명은 간단해야 하며그 클래스를 잘 설명할 수 있어야 한다. (만약 URL이나 HTML같은 단어처럼 축약형이 더 널리 사용되는 경우가 아니라면이니셜명이나 축약형은 피하고 전체 단어를 사용하라.

 class Raster;
 class ImageSprite;

Interfaces

 인터페이스명은 클래스명과 같은 대소문자 규칙을 적용한다.

 interface RasterDelegate;
 interface Storing;

 Methods

 메소드는 동사여야 하고 첫 글자는 소문자다음의 각 단어의 첫글자는 대문자를 사용한다.

  run();
 runFast();
 getBackground();

 Variables

 변수를 제외하고모든 인스턴스클래스클래스 상수는 첫 글자를 소문자로 시작하는 대소문자 혼합 형태이다내부 단어는 대문자로 시작해야 한다변수명으로 언더스코어("_")나 달러("$") 둘다 허용은 되지만이들 글자들로 시작할 수는 없다.

변수명은 짧지만 의미를 가져야 한다변수명은 기억하기 쉬워야 한다임의의 관찰자가 보더라도 그 사용의도를 알 수 있게 해야 한다임시적인 1회성(throwaway) 변수가 아니라면 한 글자로 된 변수명은 피해야 한다임시변수에 대한 일반적인 이름은 i, j, k, m 가 있고정수를 나타내는 n, 문자를 나타내는c, d, e가 있다.

 int i;
 char c;
 float myWidth;

 

 Constants

 클래스 상수로 선언된 변수명과 ANSI 상수명은 언더스코어("_")로 구분된 대문자 단어를 사용한다. (디버깅을 편하게 하려면 ANSI 상수는 피하는 것이 좋다.)

 static final int MIN_WIDTH = 4;
 static final int MAX_WIDTH = 999;
 static final int GET_THE_CPU = 1;

 




Naming Conventions


General Naming Conventions


1. 패키지 이름은 소문자로만 하세요.
mypackage, com.company.application.ui
패키지 이름은 반드시 소문자로 하세요. Sun사에서도 이러한 코딩 룰을 지키고 있습니다.


2. 변수 이름은 소문자로 시작하시고, 명사로 지어주세요.
line, audioSystem
변수 이름은 반드시 소문자로 시작하세요. 단어가 바뀌는 부분은 대문자로 구분해주시면 됩니다. Sun사에서도 이러한 코딩 룰을 지키고 있습니다.


3. 상수(final 변수) 이름은 대문자로만 쓰세요. 단어가 바뀔 때는 언더바(_)로 구분하시면 됩니다.
MAX_ITERATIONS, COLOR_RED
Sun사에서도 이러한 코딩 룰을 지키고 있습니다.
대부분의 경우에는 상수보다는 메서드를 통해서 제공하는 편이 좀 더 좋은 선택입니다.
int getMaxIterations() // NOT : MAX_ITERATIONS = 25
{
    return 25;
}


4. 메서드 이름은 소문자로 시작하시고, 동사로 지어주세요.
getName(), computeTotalWidth()
Sun사에서도 이러한 코딩 룰을 지키고 있습니다.


5. 이름을 지을 때 되도록 약어(약자)를 모두 대문자로 하면 가독성이 떨어집니다. 첫 문자만 대문자로 하세요.
exportHtmlSource(); // NOT: exportHTMLSource();
openDvdPlayer(); // NOT: openDVDPlayer();
되도록 첫 문자만 대문자로 하는 편이 낫습니다.


6. 일반적인 변수들은 타입과 변수명을 같게 하세요.
void setTopic(Topic topic) // NOT: void setTopic(Topic value)
// NOT: void setTopic(Topic aTopic)
// NOT: void setTopic(Topic t)
void connect(Database database) // NOT: void connect(Database db)
// NOT: void connect(Database oracleDB)

일반적이지 않은 변수들은 다음과 같은 룰을 적용할 수 있습니다.
Point startingPoint, centerPoint;
Name loginName;


7. 사용 범위가 넓은 변수는 이름을 길게 짓고, 사용 범위가 짧은 변수는 이름을 짧게 지으세요.
루프문에 사용되는 변수 같이 사용 범위가 짧은 변수들은 짧은 이름을 쓰세요.
일반적으로 int형 변수는 i, j, k, m, n을 쓰고, char형 변수는 c, d를 씁니다.


8. object의 이름이 암시적으로 어떤 것을 표현하고 있다면 메서드 이름에 그 부분을 쓰지 마세요.
line.getLength(); // NOT: line.getLineLength();
의미가 중복되는 부분을 쓰지 마세요.



Specific Naming Conventions


1. get/set 메서드는 속성(변수)에 대해서 직접적인 접근을 해야할 때 쓰세요.
employee.getName();
employee.setName(name);
matrix.getElement(2, 4);
matrix.setElement(2, 4, value);
Sun사에서도 이러한 코딩 룰을 지키고 있습니다.


2. is 키워드는 boolean 타입의 변수나 boolean 타입을 리턴하는 메서드 앞에 붙이세요.
isSet, isVisible, isFinished, isFound, isOpen
Sun사에서도 이러한 코딩 룰을 지키고 있습니다.
isStatus나 isFlag는 간결하지만 좋지 않은 이름입니다.
boolean 타입의 Setter 메서드는 반드시 다음과 같이 써야합니다. (이때는 get/set이 아니라 is/set으로 매칭이 됩니다.)
void setFound(boolean isFound);
경우에 따라서는 has, can, should 키워드를 쓸 수 있습니다.
boolean hasLicense();
boolean canEvaluate();
boolean shouldAbort = false;


3. 어떤 것을 계산하는 경우에는 compute 키워드를 메서드 앞에 붙일 수 있습니다.
valueSet.computeAverage();
matrix.computeInverse();
어떤 것을 계산할 때 씁니다. 계산 결과가 있을 경우 result라는 키워드를 계산 결과를 저장하는 변수에 쓸 수 있습니다.


4. 어떤 것을 찾을 경우에는 find 키워드를 메서드 앞에 붙일 수 있습니다.
vertex.findNearestVertex();
matrix.findSmallestElement();
node.findShortestPath(Node destinationNode);
어떤 것을 찾을 때 씁니다.


5. object나 어떤 확정적인 작업의 초기화를 할 경우에는 initialize 키워드를 메서드 앞에 붙일 수 있습니다.
printer.initializeFontSet();
절대 init 이라고 쓰지 마세요.


6. JFC(Swing) 변수들은 element 타입(component 타입)을 변수 뒤에 붙일 수 있습니다.
widthScale, nameTextField, leftScrollbar, mainPanel, fileToggle, minLabel, printerDialog
변수 이름이 길어지더라도 타입을 뒤에 적어두면 가독성이 좋아집니다.


7. collection이나 배열 타입의 변수 이름은 복수 형태로 이름을 지으세요.
Collection<Point> points;
int[] values;
collection 타입은 저장되는 object의 복수 형태로 지으시면 되고, 일반 배열 타입은 의미를 부여하며 복수 형태로 이름을 지으시면 됩니다.


8. object의 갯수를 나타낼 경우에는 n 키워드를 변수 앞에 붙일 수 있습니다.
nPoints, nLines
Sun사에서는 이러한 용도로 num 키워드를 사용하고 있습니다. numberOf의 약자로써 표현하였지만 실제로는 number라는 느낌이 강하기 때문에 num이라고 쓰지 마시길 바랍니다. n 키워드 또는 numberOf 키워드를 사용해서 나타내시는 것이 좀 더 좋습니다.


9. 엔티티의 번호를 나타낼 경우에는 No 키워드를 변수 뒤에 붙일 수 있습니다.
tableNo, employeeNo
엔티티 번호를 나타낼 때 사용합니다. 다른 방법으로는 i 키워드를 붙이는 것입니다. iTable, iEmployee 라고 해서 iterator의 역할로써 쓰는 것입니다.


10. Iterator 변수들은 i, j, k 등과 같은 이름을 사용하세요.
for (Iterator i = points.iterator(); i.hasNext(); ){
:
}
for (int i = 0; i < nTables; i++) {
:
}
주로 i를 쓰고, j, k 등은 중첩된 반복문일때 쓰세요.


11. 대응되는 단어를 사용해서 이름을 지으면 좋습니다.
get/set, add/remove, create/destroy, start/stop, insert/delete,
increment/decrement, old/new, begin/end, first/last, up/down, min/max,
next/previous, old/new, open/close, show/hide, suspend/resume, etc.
이렇게 하면 가독성이 좋아집니다.


12. 되도록 약자(축약형)를 쓰지 마세요.
computeAverage(); // NOT: compAvg();
ActionEvent event; // NOT: ActionEvent e;
catch (Exception exception) { // NOT: catch (Exception e) {
되도록 이러한 형태는 피하세요.
cmd instead of command
comp instead of compute
cp instead of copy
e instead of exception
init instead of initialize
pt instead of point
etc.
다음과 같은 많이 쓰이는 경우는 약자를 써도 됩니다.
HypertextMarkupLanguage instead of html
CentralProcessingUnit instead of cpu
PriceEarningRatio instead of pe
etc.


13. boolean 타입 변수 이름은 부정적인 의미로 쓰지 마세요.
bool isError; // NOT: isNoError
bool isFound; // NOT: isNotFound
부정적인 이름을 쓰면 가독성이 떨어집니다. 예를 들어, !isNoError 라고 표기를 하면 어떤 의미인지 파악하는 데 오래 걸립니다.


14. 서로 관련이 있는 상수들은 상수 앞에 공통되는 단어를 붙여주는 것이 좋다.
final int COLOR_RED = 1;
final int COLOR_GREEN = 2;
final int COLOR_BLUE = 3;
다음과 같이 나타낼 수도 있다.
interface Color {
    final int RED = 1;
    final int GREEN = 2;
    final int BLUE = 3;
}
스쿨쥐의 생각 :
JDK 1.5 버전 이상이라면 enum을 사용하기를 권장합니다. 하위 호환성을 위해 enum을 쓸 수 없다면 열거타입을 나타내는 패턴을 적용하기 바랍니다. 자세한 내용은 Effective Java를 참조하시기 바랍니다.


15. 예외 클래스는 클래스 뒤에 Exception 키워드를 붙이세요.
class AccessException extends Exception {
:
}
Sun사에서도 이러한 코딩 룰을 지키고 있습니다.


16. 디폴트 인터페이스는 인터페이스 앞에 Default 키워드를 붙이세요.
class DefaultTableCellRenderer
implements TableCellRenderer {
:
}
Sun사에서도 이러한 코딩 룰을 지키고 있습니다.


17. Singleton 패턴을 구현한 클래스에서 인스턴스를 리턴하는 메서드의 이름은 getInstance 라고 붙이세요.
class UnitManager {
    private final static UnitManager instance_ = new UnitManager();
    private UnitManager() {
        ...
    }
    public static UnitManager getInstance() { // NOT: get() or instance() or unitManager() etc.
        return instance_;
    }
}
Sun사의 JDK는 이러한 관행을 일관적으로 지원하지는 않지만 자바 커뮤니티에서는 공통적으로 사용하고 있고, 또한 패턴이 적용되었다는 것을 쉽게 알려줄 수 있습니다.


18. Factory 패턴을 구현한 클래스에서 인스턴스를 리턴하는 메서드 앞에 new 키워드를 쓸 수 있습니다.
class PointFactory {
    public Point newPoint(...) {
        ...
    }
}
Factory 패턴으로 인스턴스를 제공할 경우 "new클래스명" 형태로 Factory 메서드를 제공할 수 있습니다. new Point() 생성자를 재구성하여 메서드로써 인스턴스를 만들어 준다고 생각하시면 됩니다.


19. 리턴 타입이 void인 메서드(procedures)의 경우에는 "무엇을 처리하는 지"를 이름에 쓰는 것이 좋으며, object 타입이나 일반 자료형과 같이 리턴 타입이 있는 메서드(functions)의 경우에는 "처리 후 무엇을 리턴하는 지"를 이름에 쓰는 것이 좋습니다.
예제 없음
메서드가 무엇을 의도하는지 명확하게 알려주어 가독성을 높혀줍니다.



Files


1. 탭(TAB)이나 페이지 구분(page break) 문자와 같은 특수한 문자들은 사용하지 마세요.
예제 없음
혼자만 쓰면 괜찮지만 여러 사람이 공동 작업할 경우 글꼴이나 탭 설정에 따라서 정렬이 다르게 나와서 가독성을 떨어뜨릴 수 있습니다.
스쿨쥐의 생각 :
이클립스의 경우 영역 지정 후 단축키를 통해(기본 단축키는 ctrl + i) 자신의 설정에 맞게 정렬을 할 수 있다.


2. 여러 라인으로 나눈 문장은 정렬을 잘 하세요.
totalSum = a + b + c +
d + e;
method(param1, param2,
param3);
setText ("Long line split" +
"into two parts.");
for (int tableNo = 0; tableNo < nTables;
tableNo += tableStep) {
    ...
}
일반적으로
    콤마(,) 뒤에서 분리합니다.
    연산자 뒤에서 분리합니다.
    이전 라인의 표현식 시작 부분에 맞춰서 정렬합니다.


Statements


Package and Import Statements


1. package 문은 파일의 첫번째 라인에 적어야 하며, 모든 파일은 특정 패키지에 소속되어야 합니다.
예제 없음
package 문의 위치는 Java 언어 코딩 표준에서 권고하고 있는 것입니다. 모든 파일들을 실제 패키지(디폴트 패키지보다는 이름이 있는 패키지)에 두는 것은 Java 객체 지향 프로그래밍 기법에 도움을 줍니다.


2. import 문은 반드시 package 문 뒤에 나와야 합니다. 또한 기본 패키지부터 정렬해야 하며, 관련 패키지들은 그룹핑하고 빈 라인을 삽입하여 일목요연하게 정리합니다.
import java.io.IOException;
import java.net.URL;

import java.rmi.RmiServer;
import java.rmi.server.Server;

import javax.swing.JPanel;
import javax.swing.event.ActionEvent;

import org.linux.apache.server.SoapServer;
import 문의 위치는 Java 언어 코딩 표준에서 권고하고 있는 것입니다. 많은 import 문들을 정렬해두면 편리하게 import 문들을 검토할 수 있고, 이를 통하여 현재 이 패키지가 어떤 용도로 설계되었는지를 쉽게 파악할 수 있습니다. 또한 import 문을 그룹핑하면 관련된 정보들을 공통의 유닛으로 관리할 수 있기 때문에 복잡도를 줄일 수 있습니다.

스쿨쥐의 생각 :
이클립스에서는 단축키(기본 단축키는 ctrl + shift + o)를 통해 쉽게 할 수 있습니다.


3. import 문을 사용할 때는 와일드 카드 문자(*)를 쓰지 마세요.
import java.util.List; // NOT: import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
와일드 카드 문자를 사용하게 되면 성능상에도 약간 영향이 있으며, 같은 이름의 클래스가 서로 다른 패키지에 있을 경우 문제가 발생할 수 있습니다. 또한 import 문만 보고도 어떠한 작업을 할 것인지 가독성을 높이기 위해서라도 와일드 카드를 쓰지 않는 것이 좋습니다.

스쿨쥐의 생각 :
2번과 마찬가지로 쉽게 할 수 있습니다. (동일 단축키)



Classes and Interfaces

Class 와 Interface 의 선언은 다음과 같은 방식으로 조직화하여 사용합니다.
1. Class/Interface 문서(javadoc 주석)
2. class 나 interface 선언문
3. 클래스 변수(static으로 선언된)들을 public, protected, package (접근제한자가 없는), private 순서대로 나열합니다.
4. 인스턴스 변수들을 public, protected, package (접근제한자가 없는), private 순서대로 나열합니다.
5. 생성자
6. 메소드(메소드에는 특별한 순서가 없습니다)
예제 없음
각각의 클래스/인터페이스 구성요소들의 등장 위치를 지키게되면, 현재 코드 상에 어떤 요소들이 다음에 등장할 것인지 예측할 수 있게되어 가독성이 좋아집니다.



Methods


1. 메서드의 지시자는 다음과 같은 순서로 사용한다.
<access> static abstract synchronized <unusual> final native
public static double square(double a); // NOT: static public double square(double a);
<access> 지시자는 public, protected, private 중 하나이고, <unusual> 부분은 volatile 과 transient 중 하나가 지정됩니다. 여기서 가장 중요한 점은 접근(access) 지시자가 맨 처음에 나타나야 한다는 것입니다. 사용할 수 있는 다양한 지시자들이 있지만, 이는 매우 중요한 사항이기 때문에 반드시 메소드 선언문에 반영되어야 합니다. 기타 지시자들의 순서는 덜 중요하지만 일관된 관례를 따르는 것이 바람직합니다.



Types


1. 형변환은 반드시 명시적으로 해주세요.
floatValue = (int) intValue; // NOT: floatValue = intValue;
형변환을 할 때 데이터가 손실될 경우가 있습니다. 명시적으로 하지 않는다면 이러한 것이 의도한 것인지 아니면 실수로 빠뜨린 것인지 알 수 없습니다.


2. 배열 지시자([])는 변수 이름 뒤가 아니라 타입 뒤에 붙이세요.
int[] a = new int[20]; // NOT: int a[] = new int[20]
배열은 타입의 한 속성이지 변수의 속성이 아니기 때문입니다. 어떤 이유에서인지 Java 에서는 두 가지 모두 문법적으로 허용하고 있습니다.



Variables


1. 변수는 선언된 지점에서 초기화하며, 가능한 사용범위를 최소화하여 선언한다.
예제 없음
이 규칙은 어느 시점에서든 변수가 유효한 값을 가진다는 것을 보장해줍니다. 종종 선언하는 시점에 변수에 유효한 값을 초기화하는 것이 불가능한 경우가 있습니다만, 그러한 경우에도 초기화하지 않은 상태로 내버려두는 것보다는 임의의 값이라도 사용하여 초기화 해두는 것이 좋습니다.
스쿨쥐의 생각 :
보통 숫자 형은 0 또는 0.0, 레퍼런스 형은 null을 주는 것이 일반적이다.


2. 변수는 한가지의 의미가 가져야지 여러 의미로 해석되어서는 안됩니다.
예제 없음
일관되고 유일한 의미를 부여함으로써 가독성을 향상시키고 더불어 부작용까지 예방할 수 있습니다.


3. 클래스 변수(static 변수)는 public으로 지정하지 마세요.
예제 없음
public 변수들은 Java 의 정보은닉과 캡슐화 컨셉에 위배됩니다. 대신 변수를 private 으로 선언하시고 이 변수를 접근할 수 있는 메소드를 사용하게 하십시오. 한 가지 예외적인 상황은 클래스가 데이터 구조로만 사용될 때입니다. 이 경우 클래스는 어떠한 행위(메소드)도 갖지 않고 오로지 데이터를 보관하는, 즉 C++ 에서의 struct 와 동일한 형태로 사용됩니다. 이 경우에는 클래스의 인스턴스 변수들을 public 으로 선언할 수도 있습니다.


4. 배열은 반드시 타입 뒤에서 정의하세요.
double[] vertex; // NOT: double vertex[];
int[] count; // NOT: int count[];
public static void main(String[] arguments)
public double[] computeVertex()
이유는 앞에서 설명한 것과 같습니다.


5. 변수의 생존 기간(사용 범위)를 가능한한 짧게 하세요.
예제 없음
변수의 생존 기간을 짧게 하면 영향을 주거나 부작용이 일어나는 것에 대해서 통제하기가 쉬워집니다.



Loops


1. for() 구문 안에는 반복에 사용되는 문장만 넣으세요.
sum = 0; // NOT: for (i = 0, sum = 0; i < 100; i++)
for (i = 0; i < 100; i++) // sum += value[i];
sum += value[i];
관리하기가 편해지고 가독성을 향상시킬 수 있습니다. 무엇이 반복문을 제어하고 무엇이 반복문 내에서 사용되는지 분명하게 하세요.


2. 반복문에 사용되는 변수는 반복문 바로 앞에서 초기화 하세요.
isDone = false; // NOT : bool isDone = false;
while (!isDone) { // :
: // while (!isDone) {
} // :
// }
설명 없음


3. do-while 문은 되도록 쓰지 마세요.
예제 없음
do .... while 문을 사용한 모든 문장은 while 문을 사용하여 동일하게 바꾸어 작성할 수 있습니다. 그렇게 하면 가독성이 더욱 좋아집니다.


4. 반복문 내에서 continue나 break 문을 되도록 쓰지 마세요.
예제 없음
continue나 break는 꼭 필요할 때만 쓰세요. 너무 많이 쓰면 가독성이 떨어집니다.



Conditionals


1. 복잡한 조건식은 피하세요. 대신 임시로 boolean 변수를 사용하세요.
bool isFinished = (elementNo < 0) || (elementNo > maxElement);
bool isRepeatedEntry = (elementNo == lastElement);
if (isFinished || isRepeatedEntry) {
:
}
// NOT:
if ((elementNo < 0) || (elementNo > maxElement) || elementNo == lastElement) {
:
}
디버깅 할 때도 편해지며, 의미가 더 정확하게 전달되어 가독성이 좋아집니다.


2. if문에서 일반적인 상황은 if 부분에 위치시키고, 예외적인 상황은 else 부분에 위치시키세요.
boolean isOk = readFile(fileName);
if (isOk) {
:
} else {
:
}
일반적인 처리 흐름과 예외상황 처리 흐름을 불명확하지 않게 하십시오. 이 지침은 가독성과 성능 모두에 영향을 미치는 중요한 사항입니다.


3. 조건식에 실행문을 적지 마세요.
InputStream stream = File.open(fileName, "w");
if (stream != null) {
:
}
// NOT:
if (File.open(fileName, "w") != null)) {
:
}
조건식에 실행문을 사용하는 것은 간편하지만 읽기가 매우 어려워집니다. 이 지침은 Java 에 발을 처음으로 들여놓는 개발자들에게 특히 강조하고 싶은 지침입니다.



Miscellaneous


1. 코드 상에 매직 넘버를 쓰지 마세요. 0과 1 이외의 숫자는 상수로 정의해서 쓰는 것이 좋습니다.
private static final int TEAM_SIZE = 11;
:
Player[] players = new Player[TEAM_SIZE]; // NOT: Player[] players = new Player[11];
좀 더 의미를 명확하게 하게 할 수 있으며, 재사용성도 좋아집니다.


2. 실수값 상수는 항상 소수점과 최소 소수점 이하 한자리 숫자를 사용하여 지정해야 합니다.
double total = 0.0; // NOT: double total = 0;
double speed = 3.0e8; // NOT: double speed = 3e8;
double sum;
:
sum = (a + b) * 10.0;
특별한 경우에 정수와 실수가 동일한 값을 갖는다 하더라도 기본적으로 특성이 다르기 때문에 구별하는 것이 좋습니다.
또한 위 마지막 라인의 예제에서 살펴볼 수 있듯이, 값을 할당받은 변수(sum)는 다른 변수의 타입을 알 수 없다고 하더라도 명백하게 실수값을 계산해 낸다는 것을 보장할 수 있습니다.


3. 실수값 상수는 항상 정수부에 숫자를 사용하여 지정해야 합니다.
double total = 0.5; // NOT: double total = .5;
Java 에서 사용하는 숫자와 표현식은 수학식 표기법에서 차용하였으므로, 프로그래머는 가능한 수학적 표기법을 준수하는 것이 바람직합니다. 실제로 .5 (간편한 표기) 보다 0.5 (수학적 표기) 가 더 읽기에 쉽습니다. 이 수식에서 정수 5 가 함께 사용되는 경우에는, .5 와 정수 5 를 혼동하여 잘못된 연산을 초래할 가능성이 높아지게 됩니다.


4. static 변수나 메서드는 인스턴스 변수를 통해서 호출하지 말고, 클래스 이름을 통해서 접근해야 합니다.
Thread.sleep(1000); // NOT: thread.sleep(1000);
특정 인스턴스에 대해서 독립적이라는 것을 강조하기 위해서 사용합니다. 즉, static을 사용하고 있다는 것을 알려주어 가독성을 높여줍니다.




Layout and Comments


Layout


1. 블락 레이아웃은 다음 예제를 따르세요.
while (!done) {
    doSomething();
    done = moreToDo();
}
while (!done)
{
    doSomething();
    done = moreToDo();
}
첫번째나 두번째 방법 중 아무 것이나 써도 됩니다.


2. 클래스와 인터페이스 선언은 다음 예제를 따르세요.
class Rectangle extends Shape
implements Cloneable, Serializable {
...
}
extends 키워드는 클래스나 인터페이스 뒤에 붙이시고, implements 키워드는 다음 라인에 적으세요.


3. 메서드 선언은 다음 예제를 따르세요.
public void someMethod()
throws SomeException
{
...
}
throws 키워드는 다음 라인에 적으세요.


4. if문이나 반복문의 경우 한 줄만 있더라도 블락을 꼭 써주세요.
if(condition) {
    //statement;
}
// NOT:
if(condition)
//statement;
한 줄이라도 블락을 쓰는 쪽이 가독성이 좋습니다. 또한 짧은 구문이라면 한줄로 적으셔도 괜찮습니다.



White Space


1. 논리 단위로 빈 라인을 넣어주세요.
// Create a new identity matrix
Matrix4x4 matrix = new Matrix4x4();

// Precompute angles for efficiency
double cosAngle = Math.cos(angle);
double sinAngle = Math.sin(angle);

// Specify matrix as a rotation transformation
matrix.setElement(1, 1, cosAngle);
matrix.setElement(1, 2, sinAngle);
matrix.setElement(2, 1, -sinAngle);
matrix.setElement(2, 2, cosAngle);

// Apply rotation
transformation.multiply(matrix);
마치 국어에서 문단을 나누듯이 논리 단위가 바뀔 때마다 빈 라인을 넣어주세요.


2. 변수와 데이터 타입을 구분해서 맞추세요.
TextFile file;
int nPoints;
double x, y;

이런 식으로 정렬하면 가독성이 좋아집니다.


3. 구문(statement)는 가독성이 좋게 정렬하세요.
if (a == lowValue) compueSomething();
else if (a == mediumValue) computeSomethingElse();
else if (a == highValue) computeSomethingElseYet();

value = (potential * oilDensity) / constant1 +
(depth * waterDensity) / constant2 +
(zCoordinateValue * gasDensity) / constant3;

minPosition = computeDistance(min, x, y, z);
averagePosition = computeDistance(average, x, y, z);

switch (phase) {
    case PHASE_OIL : text = "Oil"; break;
    case PHASE_WATER : text = "Water"; break;
    case PHASE_GAS : text = "Gas"; break;
}
가독성이 좋아진다면 어느 정도 코딩 룰을 바꾸어 적용해도 괜찮습니다.



Comments


1. 프로젝트 진행시 다음 주석 스타일을 따릅니다.
    package example;

    import java.util.ArrayList;
    import java.util.Collection;

    /**
    * JavaDoc 테스트용 클래스입니다.
    *
    * @author 최영목
    * @since 2008.04.03
    * @version 1.0
    * <pre>
    * 2008.04.03 : 최초 작성
    * 2008.04.04 : 컬렉션 추가
    * </pre>
    */
    public class DocExample {

        private Collection<String> userNames;

        public DocExample(String name) {
            userNames = new ArrayList<String>();
        }

        /**
        * 유저 이름을 삭제하는 메서드
        *
        * @param name 삭제할 유저 이름
        * @return 유저가 존재해서 삭제가 되면 true를 리턴하고, 유저가 존재하지 않으면 false를 리턴한다.
        * @throws Exception 그냥 테스트용으로 했음.
        */
        public boolean removeUserName(String name)
        throws Exception {
            return userNames.remove(name);
        }

        /**
        * 어노테이션과 함께 쓸 때를 위한 예제 메서드
        *
        * @return 그냥 텍스트가 출력됨
        */
        @Override
        public String toString() {
            return "테스트용입니다.";
        }
    }
이클립스의 기능에 의존하여 코드 가독성을 높이기 위해 평소에는 주석을 숨겨두고 필요할 때만 꺼내씁니다. 제너릭 타입은 반드시 명시를 해야합니다.


2. JavaDoc 생성시 package 설명을 위해 각 패키지에 package-info.java 파일을 만듭니다.
    /**
    * 설명입니다.
    */
    package example;
반드시 /** */ 주석이 먼저 와야하며 뒤에 패키지 선언이 와야 합니다. 또한 이 파일은 1.5에서부터 사용가능하며, 이전 버전은 doc 폴더 내의 각 패키지에 package.html 또는 package-info.html 파일을 만들어서 넣어야 합니다.



출처 : http://forum.falinux.com/zbxe/index.php?document_srl=807633&mid=lecture_tip

참고 : http://kimseunghyun76.tistory.com/381

Java API : http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook-java.lang.Thread-


Java에서 어플리케이션을 종료 될때 뭔가를 작업 하고 싶을 경우에 addShutdownHook이란 걸 사용 할 수 있습니다.


addShutdownHook은 VM에서 사라지기 전에 마지막 유언을 할 수 있게 해줍니다.^^;


 Ctrl+C, 또는 kill명령으로 강제 종료가 될때도 실행이 됩니다. 위에서 말했듯 마지막 유언을 남기기 위해....


그럼 샘플 소스입니다.


[샘플 소스]

public class ShutdownHookDemo {

	static class ByeMessage extends Thread {

		public void run() {
			System.out.println("프로그램이 종료 되었습니다.!!!!");
		}
	}

	public static void main(String[] args) {

		// 자바가 종료 될때 실행 시킬 쓰레드를 등록 시킴.
        Runtime.getRuntime().addShutdownHook(new ByeMessage());
        
        try {
	        // 프로그램 시작 메세지
	        System.out.println("프로그램 시작!!!!");
	
	        // 3초간 대기 함.
	        System.out.println("대기 3초!!!");
	        Thread.sleep(3000);
	
	        // 프로그램 종료
	        System.out.println("프로그램 종료!!!");
	        
        } catch(Exception e) {
        	e.printStackTrace();
        }
	}
}


- 익명 (
anonymous) class 사용

public class ShutdownHookDemo {

	public static void main(String[] args) {

		// 자바가 종료 될때 실행 시킬 쓰레드를 등록 시킴.
        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                System.out.println("프로그램이 종료 되었습니다.!!!!");
            }
        });
        
        try {
	        // 프로그램 시작 메세지
	        System.out.println("프로그램 시작!!!!");
	
	        // 3초간 대기 함.
	        System.out.println("대기 3초!!!");
	        Thread.sleep(3000);
	
	        // 프로그램 종료
	        System.out.println("프로그램 종료!!!");
	        
        } catch(Exception e) {
        	e.printStackTrace();
        }
	}
}




[실행]

shutdown_1.PNG 



addShutdownHook에는 쓰레드가 등록 됩니다.


샘플에는 대기를 3초로 하고 이클립스에서 실행 시켜서 정상적으로 종료 하도록 하였지만,


 커멘드창에서 프로그램을 실행 시킨후, 3초가 되기전에 Ctrl+C를 눌러 종료 시키면 


"프로그램이 종료 되었습니다.!!!!"라는 메세지가 뜨고 "프로그램 종료!!!!"라는 메세지는 뜨지 않습니다.




* addShutdownHook 사용 시 주의점

- Shutdown hooks의 개수 : 제한 없다.

- Shutdown hook을 붙여야 하는 시점은 언제든지 붙여도 된다. 
  단순한 이야기 이지만, 셧다운 되기 전에 어떤 인스턴스든지 붙여놔야 겠지.

- 같은 hook을 다시 붙이는 것은 할수 없다."Hook previously rfegisted"라는 IllegalArgumentException이 발생한답니다.

- De-register a Hook : Runtime.removeShutdownHook 메소드를 호출하는 것 만으로, 훅을 제거 할 수 있다. 

  하지만 익명 내부 클래스를 사용해서 셧다운을 대부분 등록하는데, 이 것을 사용할수 있는 어떤 참조도 가질수 없기 때문에, 우리는 익명 내부 클래스를 우리가 제거할지 모르는 훅들에 대해서는 사용하면 안된다. 

removeShutdownHook 메소드에 이 참조를 전달 해야하기 때문이랍니다.

- 동시성을 유지하는 것을 지켜요 : 앞서 말한대로.

- Shutdown Hook의 Reliability(신뢰도) : JVM은 최선을 다해 실행하겠지만, 모두 다 실행된다고 보장 할 수 없다고 한다. 

- Hook들에 의한 시간 소비를 유의해라 : 셧다운 할 때 시간 소비 하지 않는것이 중요하다. 




출처 : http://blog.goooood.net/214


VM이 실행되고 있는 운영체제를 확인하는 java 코드

String osName = System.getProperty("os.name"); 


리턴되는 문자열에 대한 문서는 아래 링크
 http://lopica.sourceforge.net/os.html

OS를 구분하기 위한 코드는 아래 링크를 참고하면 된다.
http://www.mkyong.com/java/how-to-detect-os-in-java-systemgetpropertyosname/


- OSValidator.java

public class OSValidator {
  
    private static String OS = System.getProperty("os.name").toLowerCase();
  
    public static void main(String[] args) {
  
        System.out.println(OS);
  
        if (isWindows()) {
            System.out.println("This is Windows");
        } else if (isMac()) {
            System.out.println("This is Mac");
        } else if (isUnix()) {
            System.out.println("This is Unix or Linux");
        } else if (isSolaris()) {
            System.out.println("This is Solaris");
        } else {
            System.out.println("Your OS is not support!!");
        }
    }
  
    public static boolean isWindows() {
  
        return (OS.indexOf("win") >= 0);
  
    }
  
    public static boolean isMac() {
  
        return (OS.indexOf("mac") >= 0);
  
    }
  
    public static boolean isUnix() {
  
        return (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0 );
  
    }
  
    public static boolean isSolaris() {
  
        return (OS.indexOf("sunos") >= 0);
  
    }
  
} 


ThreadLocal 사용법과 활용

출처 : http://javacan.tistory.com/153

자바 1.2 버전부터 제공되고 있지만 아직 다수의 개발자들이 잘 몰라서 활용을 잘 못하는 기능이 하나 있는데, 그 기능이 바로 쓰레드 단위로 로컬 변수를 할당하는 기능이다. 이 기능은 ThreadLocal 클래스를 통해서 제공되는데, 본 글에서는 ThreadLocal 클래스의 기본적인 사용방법과 활용 방법을 살펴보도록 하겠다.

ThreadLocal이란?

일반 변수의 수명은 특정 코드 블록(예, 메서드 범위, for 블록 범위 등) 범위 내에서만 유효하다.

{
    int a = 10;
    ...
   // 블록 내에서 a 변수 사용 가능
}
// 변수 a는 위 코드 블록이 끝나면 더 이상 유효하지 않다. (즉, 수명을 다한다.)

반면에 ThreadLocal을 이용하면 쓰레드 영역에 변수를 설정할 수 있기 때문에, 특정 쓰레드가 실행하는 모든 코드에서 그 쓰레드에 설정된 변수 값을 사용할 수 있게 된다. 아래 그림은 쓰레드 로컬 변수가 어떻게 동작하는 지를 간단하게 보여주고 있다.


위 그림에서 주목할 점은 동일한 코드를 실행하는 데, 쓰레드1에서 실행할 경우 관련 값이 쓰레드1에 저장되고 쓰레드2에서 실행할 경우 쓰레드2에 저장된다는 점이다.


ThreadLocal의 기본 사용법

ThreadLocal의 사용방법은 너무 쉽다. 단지 다음의 네 가지만 해 주면 된다.
  1. ThreadLocal 객체를 생성한다.
  2. ThreadLocal.set() 메서드를 이용해서 현재 쓰레드의 로컬 변수에 값을 저장한다.
  3. ThreadLocal.get() 메서드를 이용해서 현재 쓰레드의 로컬 변수 값을 읽어온다.
  4. ThreadLocal.remove() 메서드를 이용해서 현재 쓰레드의 로컬 변수 값을 삭제한다.
아래 코드는 ThreadLocal의 기본적인 사용방법을 보여주고 있다.

// 현재 쓰레드와 관련된 로컬 변수를 하나 생성한다.
ThreadLocal<UserInfo> local = new ThreadLocal<UserInfo>();

// 로컬 변수에 값 할당
local.set(currentUser);

// 이후 실행되는 코드는 쓰레드 로컬 변수 값을 사용
UserInfo userInfo = local.get();

위 코드만으로는 ThreadLocal이 어떻게 동작하는 지 잘 이해가 되지 않을테니, 구체적인 예제를 이용해서 ThreadLocal의 동작 방식을 살펴보도록 하겠다. 먼저 ThreadLocal 타입의 static 필드를 갖는 클래스를 하나 작성해보자.

public class Context {
    public static ThreadLocal<Date> local = new ThreadLocal<Date>();
}

이제 Context 클래스를 사용해서 쓰레드 로컬 변수를 설정하고 사용하는 코드를 작성할 차례이다. 아래는 코드의 예이다.

class A {
    public void a() {
        Context.local.set(new Date());
       
        B b = new B();
        b.b();

        Context.local.remove();
    }
}

class B {
    public void b() {
        Date date = Context.local.get();

        C c = new C();
        c.c();
    }
}

class C {
    public void c() {
        Date date = Context.local.get();
    }
}

위 코드를 보면 A, B, C 세 개의 클래스가 존재하는데, A.a() 메서드를 호출하면 다음 그림과 같은 순서로 메서드가 실행된다.


위 그림에서 1~10은 모두 하나의 쓰레드에서 실행된다. ThreadLocal과 관련된 부분을 정리하면 다음과 같다.
  • 2 - A.a() 메서드에서 현재 쓰레드의 로컬 변수에 Date 객체를 저장한다.
  • 4 - B.b() 메서드에서 현재 쓰레드의 로컬 변수에 저장된 Date 객체를 읽어와 사용한다.
  • 6 - C.c() 메서드에서 현재 쓰레드의 로컬 변수에 저장된 Date 객체를 읽어와 사용한다.
  • 9 - A.a() 메서드에서 현재 쓰레드의 로컬 변수를 삭제한다.
위 코드에서 중요한 건 A.a()에서 생성한 Date 객체를 B.b() 메서드나 C.c() 메서드에 파라미터로 전달하지 않는다는 것이다. 즉, 파라미터로 객체를 전달하지 않아도 한 쓰레드로 실행되는 코드가 동일한 객체를 참조할 수 있게 된다.

ThreadLocal의 활용

ThreadLocal은 한 쓰레드에서 실행되는 코드가 동일한 객체를 사용할 수 있도록 해 주기 때문에 쓰레드와 관련된 코드에서 파라미터를 사용하지 않고 객체를 전파하기 위한 용도로 주로 사용되며, 주요 용도는 다음과 같다.

  • 사용자 인증정보 전파 - Spring Security에서는 ThreadLocal을 이용해서 사용자 인증 정보를 전파한다.
  • 트랜잭션 컨텍스트 전파 - 트랜잭션 매니저는 트랜잭션 컨텍스트를 전파하는 데 ThreadLocal을 사용한다.
  • 쓰레드에 안전해야 하는 데이터 보관
이 외에도 쓰레드 기준으로 동작해야 하는 기능을 구현할 때 ThreadLocal을 유용하게 사용할 수 있다.

ThreadLocal 사용시 주의 사항

쓰레드 풀 환경에서 ThreadLocal을 사용하는 경우 ThreadLocal 변수에 보관된 데이터의 사용이 끝나면 반드시 해당 데이터를 삭제해 주어야 한다. 그렇지 않을 경우 재사용되는 쓰레드가 올바르지 않은 데이터를 참조할 수 있다.


참고 : http://stackoverflow.com/questions/160970/how-do-i-invoke-a-java-method-when-given-the-method-name-as-a-string


Dog class:

package com.mypackage.bean;

public class Dog {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void printDog(String name, int age) {
        System.out.println(name + " is " + age + " year(s) old.");
    }
}

ReflectionDemo class:

package com.mypackage.reflection;

import java.lang.reflect.Method;

public class ReflectionDemo {

    public static void main(String[] args) throws Exception {
        String dogClassName = "com.mypackage.bean.Dog";
        Class<?> dogClass = Class.forName(dogClassName); // convert string classname to class
        Object dog = dogClass.newInstance(); // instantiate object of class

        String methodName = "";

        // with single parameter, return void
        methodName = "setName";
        Method setNameMethod = dog.getClass().getMethod(methodName, String.class);
        setNameMethod.invoke(dog, "Mishka"); // pass arg

        // without parameters, return string
        methodName = "getName";
        Method getNameMethod = dog.getClass().getMethod(methodName);
        String name = (String) getNameMethod.invoke(dog); // explicit cast

        // with multiple parameters
        methodName = "printDog";
        Class<?>[] paramTypes = new Class[2];
        paramTypes[0] = String.class;
        paramTypes[1] = int.class;      
        Method printDogMethod = dog.getClass().getMethod(methodName, paramTypes);
        printDogMethod.invoke(dog, name, 3); // pass args       
    }
}

Output: Mishka is 3 year(s) old.



출처 : http://jyates.github.io/2012/11/05/rolling-java-gc-logs.html


If you are running a java process, you probably want to keep track of what the garbage collector is doing. You can access this via jconsole or by logging the gc actions by adding:

-Xloggc:gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps

which logs to the ‘gc.log’ file.

And for simple cases, that will probably work just fine…until your process starts running row more than a few days. The GC log is not rolled automatically, potentially resulting in a log that can easily grow out of control and fill up your filesystem.

Bad news bears!

What you really want to do roll the logs periodically. You could do this manually with a cron job (which means you might missing some elements), or every time you restart the process (but if you don’t restart often, you’re up a creek) or send the log to your own custom logger (which is can be tricky to get right).

All pretty ugly solutions. I sure wish we had something better…

As of Oracle Java 1.6_34 (or 1.7_2 in the latest minor version), we do! GC logs can be automatically rolled at a certain size and retain only a certain number of logs.

To turn on simple log rolling, you only need to add (in addition neccessay gc log arguments mentioned above) to your java command line options:

-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=<number of files> -XX:GCLogFileSize=<size>

where <number of files> is just an integer and <size> is the size of the file (e.g 16K is 16 kilobytes, 128M is 128 megabytes, etc.). Rolled files are appened with .<number>, where earlier numbered files are the older files.

Suppose you ran an java program with the parameters:

$ java -Xloggc:gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=128K

you might see something like the following show up in your directory:

-rw-r--r--   1 jyates  staff    90K Nov  5 18:25:39 2012 gc.log.0
-rw-r--r--   1 jyates  staff   128K Nov  5 18:25:25 2012 gc.log.1
-rw-r--r--   1 jyates  staff   128K Nov  5 18:25:29 2012 gc.log.2
-rw-r--r--   1 jyates  staff   128K Nov  5 18:25:33 2012 gc.log.3
-rw-r--r--   1 jyates  staff   128K Nov  5 18:25:36 2012 gc.log.4

What’s really nice note here is that GC logs beyond the specified number areautomatically deleted, ensuring that you know exactly (+/- a few kilobytes for the occasional heavy load) how many log files you will have.

Pretty cool!

Unfortunately, if you attempt to turn on log rolling and forget to include the number of files or the size, the jvm will not turn on logging and instead tell you:

To enable GC log rotation, use -Xloggc:<filename> -XX:+UseGCLogRotaion -XX:NumberOfGCLogFiles=<num_of_files> -XX:GCLogFileSize=<num_of_size>
where num_of_file > 0 and num_of_size > 0
GC log rotation is turned off

this is wrong!

Double check your other parameters, and try again; you definitely want to use -XX:+UseGCLogFileRotation.

Hopefully this helps you setup your own log rolling. If you have any other JVM/GC tricks, I’d love to hear about them.

Notes:

  1. This is actually a best effort rolling process. If you are doing a lot GC work (e.g. leaning on the ‘garbage collect’ button in jconsole), the log may grow larger. However, as soon as the jvm has a chance it will then roll the log.


==========================================================================================


출처 : https://confluence.atlassian.com/display/CONFKB/Enable+Garbage+Collection+Logging

Enable Garbage Collection Logging

Why Garbage Colllection Logging?

Garbage Collection logs are useful when trying to isolate issues within Java based applications as they will pinpoint blocked, hung or stuck threads that may be causing downstream effects in the application. It is always useful to collect at least 10 minutes of garbage collection logs, but analysis accuracy is often increased by the amount of recent log data to review.

For Linux

Add the following parameters into your Confluence System Properties, in setenv.sh (be sure to enter the path for your <confluence-home> directory)

-Xloggc:<confluence-home>/logs/`date +%F_%H-%M-%S`-gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:-PrintTenuringDistribution -XX:+PrintGCCause
-XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=2M

The parameter -XX:+PrintGCCause was introduced within Java 1.7.0_45 and will not work if you are using a version of Java prior to that version. Please check the version of Java that is being used by your instance of Confluence prior to using this flag.

For Windows

Batch file

 

If you start Confluence using 'start-confluence.bat', add the below code to setenv.bat

 

rem Create a timestamp with date and time, replacing ' ' with '0' and '/' with '-'
set atlassian_timestamp=%DATE:~-4%.%DATE:~4,2%.%DATE:~7,2%_%TIME:~0,2%.%TIME:~3,2%.%TIME:~6,2%
set atlassian_timestamp=%atlassian_timestamp: =0%
set atlassian_timestamp=%atlassian_timestamp:/=-%
set atlassian_logsdir=%~dp0..\logs
 
set CATALINA_OPTS=%CATALINA_OPTS% -Xloggc:"%atlassian_logsdir%\gc-%atlassian_timestamp%.log"
set CATALINA_OPTS=%CATALINA_OPTS% -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=2M -XX:+PrintGCCause
set CATALINA_OPTS=%CATALINA_OPTS% -XX:+PrintGCApplicationStoppedTime -XX:-PrintGCDetails -XX:+PrintGCTimeStamps -XX:-PrintTenuringDistribution

NB: This will create the GC logs in the <confluence-install> directory.

Service

If you start Windows as a service, add the below to the service by following these steps (be sure to enter the path for your <confluence-home> directory)

-Xloggc:<confluence-home>/logs/gc.log
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCCause
-XX:+PrintGCApplicationStoppedTime
-XX:-PrintTenuringDistribution
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=2M

NB: There is no way to include the DATE and TIME variables in the filename when starting as a Windows Service. This means the gc.log file will be overwritten on each startup. If you need to analyse the file, be sure to copy it before restarting the service.





병렬 프로그래밍 - 구성 단위 : http://aroundck.tistory.com/867


스레드에 안전한 Map을 사용하려면 ConcurrentMap 을 사용하고, 스레드에 안전하면서 Iterator 처럼 읽기가 많은 경우 (채팅 Client 목록 등)에는 CopyOnWriteArrayList 를 사용하는게 효과적이다.



이 자료는 "에어콘" 사의 "자바 병렬 프로그래밍" 이라는 도서의 내용을 학습하면서 정리한 내용입니다. 예제로 제시된 소스코드 및 자세한 설명은 책을 참조하세요~

 

05. 구성 단위



5.1. 동기화된 컬랙션 클래스

- 동기화되어 있는 컬렉션 클래스의 대표 주자는 바로 Vector 와 HashTable이다. Collections.synchronizedXxx 메소드를 사용해 이와 비슷하게 동기화되어 있는 몇 가지 클래스가 제공된다. 이 클래스는 모두 public 으로 선언된 모든 메소드를 클래스 내부에 캡슐화해 내부의 값을 한 스레드만 사용할 수 있도록 제어하면서 스레드 안전성을 확보하고 있다.


5.1.1. 동기화된 컬렉션 클래스의 문제점.


- 동기화된 컬렉션 클레스는 스레드 안전성을 확보하고 있기는 하다. 하지만 여러 개의 연산을 묶어 하나의 단일 연산처럼 활용해야 할 필요성이 항상 발생한다. 두 개 이상의 연산을 묶어 사용해야 하는 예는 반복(iteration), 이동(navigation), 없는 경우에만 추가( add if absent ) 등이 있다. 동기화된 컬렉션을 사용하면 따로 락이나 동기화 기법을 사용하지 않는다 해도 이런 대부분의 기능이 모두 스레드 안전하다. 하지만 여러 스레드가 해당 컬렉션 하나를 놓고 동시에 그 내용을 변경하려 한다면 컬렉션 클래스가 상식적으로 올바른 방법으로 동작하지 않을 수도 있다.

- 동기화된 컬렉션 클래스는 대부분 클라이언트 측 락을 사용할 수 있도록 만들어져 있기 때문에, 컬렉션 클래스가 사용하는 락을 함께 사용한다면 새로 추가하는 기능을 컬렉션 클래스에 들어 있는 다른 메소드와 같은 수준으로 동기화시킬 수 있다. 동기화된 컬렉션 클래스는 컬렉션 클래스 자체를 락으로 사용해 내부의 전체 메소드를 동기화시키고 있다.


5.1.2. Iterator 와 ConcurrentModificationException


- Collection 클래스에 들어 있는 값을 차례로 반복시켜 읽어내는 가장 표준적인 방법은 바로 Iterator 를 사용하는 방법이다. ( 이런 방법은 Iterator를 직접 사용하건, 아니면 자바 5.0부터 사용할 수 있는 특별한 문법의 for each 문을 사용하건 동일하다.) Iterator를 사용해 컬렉션 클래스 내부의 값을 차례로 읽어다 사용한다 해도 반복문이 실행되는 동안 다른 스레드가 컬렉션 클래스 내부의 값을 추가하거나 제거하는 등의 변경 작업을 시도할 때 발생할 수 있는 문제를 막아주지는 못한다. 다시 말해 동기화된 컬렉션 클래스에서 만들어낸 Iterator를 사용한다 해도 다른 스레드가 같은 시점에 컬렉션 클래스 내부의 갓을 변경하는 작업을 처리하지는 못하게 만들어져 있고, 대신 즉시 멈춤( fail-fast ) 의 형태로 반응하도록 되어 있다. 즉시 멈춤이란 반복문을 실행하는 도중에 컬렉션 클래스 내부의 값을 변경하는 상황이 포착되면 그 즉시 ConcurrentModificationException 예외를 발생시키고 멈추는 처리 방법이다.

- 컬렉션 클래스는 내부에 값 변경 횟수를 카운트하는 변수를 마련해두고, 반복문이 실행되는 동안 변경 횟수 값이 바뀌면 hasNext 메소드나 next 메소드에서 ConcurrentModificationException을 발생시킨다. 더군다나 변경 횟수를 확인하는 부분이 적절하게 동기화되어 있지 않기 때문에 반복문에서 변경 횟수를 세는 과정에서 스테일 값을 사용하게 될 가능성도 있고, 따라서 변경 작업이 있었다는 것을 모를 수도 있다는 말이다. 이렇게 구현한 모습이 문제가 있기는 하지만 전체적인 성능을 떨어뜨릴 수 있기 때문에 변경 작업이 있었다는 상황을 확인하는 기능에 정확한 동기화 기법을 적용하지 않았다고 볼 수 있다.

- 단일 스레드 환경의 프로그램에서도 ConcurrentModificationException이 발생할 수 있다. 반복문 내부에서 Iterator.remove 등의 메소드를 사용하지 않고 해당하는 컬렉션의 값을 직접 제거하는 등의 작업을 하려 하면 예외 상황이 발생한다.

- for-each 반복문을 사용해 컬렉션 클래스의 값을 차례로 읽어들이는 코드는 컴파일할 때, 자동으로 Iterator를 사용하면서 hasNext 나 ext 메소드를 매번 호출하면서 반복하는 방법으로 변경한다. 따라서 반복문을 실행할 때 ConcurrentModificationException이 발생하지 않도록 미연에 방지하는 방법은 Vector에서 반복문을 사용할 때처럼 반복문 전체를 적절한 락으로 동기화를 시키는 방법밖에 없다.
 
- 반복문을 실행하는 코드 전체를 동시화시키는 방법이 그다지 훌륭한 방법이 아니라고 주장하는 이유는, 컬렉셔넹 엄청나게 많은 수의 값이 들어 있거나 값마다 반복하면서 실행해야 하는 작업이 시간이 많이 소모되는 작업일 수 있는데, 이런 경우에는 컬렉션 클래스 내부의 값을 사용하고자 하는 스레드가 상당히 오랜 시간을 대기 상태에서 기다려야 할 수 있다는 말이다. 또한 반복문에서 락을 잡고 있는 상황에서 또 다른 락을 확보해야 한다면, 데드락(deadlock)이 발생할 가능성도 높아진다. 

- 소모상태(starvation)이나 데드락의 위험이 있는 상태에서 컬렉션 클래스를 오랜 시간 동안 락으로 막아두고 있는 상태라면 전체 애플리케이션의 확장성을 해칠 수도 있다. 반복문에서 락을 오래 잡고 있으면 있을수록, 락을 확보하고자 하는 스레드가 대기 상태에 많이 쌓일 수 있고, 대기 상태에 스레드가 적체되면 될수록 CPU 사용량이 급격하게 증가할 가능성이 높다.

-  반복문을 실행하는 동안 컬렉션 클래스에 들어 있는 내용에 락을 걸어둔 것과 비슷한 효과를 내려면 clone 메소드로 복사본을 만들어 복사본을 대상으로 반복문을 사용할 수 있다. 이렇게 clone 메소드로 복사한 사본은 특정 스레드에 한정되어 있으므로 반복문이 실행되는 동안 다른 스레드에서 컬렉션 사본을 건드리기 어렵기 때문에 ConcurrentModificationException이 발생하지 않는다. ( 물론 최소한 clone 메소드를 실행하는 동안에는 컬렉션의 내용을 변경할 수 없도록 동기화시켜야 한다. ) 

- clone 메소드로 복사본을 만드는 작업에도 시간은 필요하기 마련이다. 따라서 반복문에서 사용할 목적으로 복사본을 만드는 방법도 컬렉션에 들어 있는 항목의 개수, 반복문에서 개별 항목마다 실행해야 할 작업이 얼마나 걸리는지, 컬렉션의 여러 가지 기능에 비해 반복 기능을 얼마나 빈번하게 사용하는지, 그리고 응답성과 실행 속도 등의 여러가지 요구 사항을 충분히 고려해서 적절하게 적용해야 한다.



5.1.3. 숨겨진 Iteraotr


- 컬렉션 클래스의 toString 메소드 소스코드를 들여다 보면 해당 컬렉션 클래스의 tierator 메소드를 호출해 내용으로 보관하고 있는 개별 클래스의 toString 메소드를 호출해 출력할 문자열을 만들어 내도록 되어 있다.

- 개발자는 상태 변수와 상태 변수의 동기화를 맞춰주는 락이 멀리 있을수록 동기화를 맞춰야 한다는 필요성을 잊기 쉽다.

클래스 내부에서 필요한 변수를 모두 캡슐화하면 그 상태를 보존하기가 훨씬 편리한 것처럼, 동기화 기법을 클래스 내부에 캡슐화하면 동기화 정책을 적용하기가 쉽다.


- toString 메소드뿐만 아니라 컬렉션 클래스의 hashCode 메소드나 equals 메소드도 내부적으로 iterator 를 사용한다. containsAll, removeAll, retainAll 등의 메소드, 컬렉션 클래스를 넘겨받는 생성 메소드 등도 모두 내부적으로 iterator 를 사용한다. 이렇게 내부적으로 iterator를 사용하는 모든 메소드에서 ConcurrentModificationException 이 발생할 가능성이 있다.
 




5.2. 병렬 컬렉션.

- 동기화된 컬렉션 클래스는 컬렉션의 내부 변수에 접근하는 통로를 일련화해서 스레드 안전성을 확보했다. 여러 스레드가 한꺼번에 동기화된 컬렉션을 사용하려고 하면 동시 사용성은 상당 부분 손해를 볼 수밖에 없다.

- 병렬 컬렉션은 여러 스레드에서 동시에 사용할 수 있도록 설계되어 있다. HashMap 을 대치하면서 병렬성을 확보한 ConcurrentHashMap 과 List 클래스의 하위 클래스이며 객체 목록을 반복시키며 열람하는 연산의 성능을 최우선으로 구현한  CopyOnWriteArrayList 도 병렬 컬렉션이다. ConcurrentMap 도 병렬 컬렉션인데, 인터페이스를 보면 추가하려는 항목이 기존에 없는 경우에만 새로 추가하는 put-if-absent, replace, conditional remove 연산 등이 정의되어 있다.

기존에 사용하던 동기화 컬렉션 클래스를 병렬 컬렉션으로 교체하는 것만으로도 별다른 위험 요소 없이 전체적인 성능을 상당히 끌어 올릴 수 있다.


- Queue 인터페이스는 작업할 내용을 순서대로 쌓아둘 수 있는 구조이고, ConcurrentLinkedQueue 는 FIFO 방식 Queue 이며, PriorityQueue 는 우선 순위에 따라 큐에 쌓여 있는 항목이 추출되는 순서가 바뀌는 특성을 가지고 있다. Queue 인터페이스에 정의되어 있는 연산은 동기화를 맞추느라 대기 상태에서 기다리는 부분이 없다.
 
- Queue 를 상속받은 BlockingQueue 클래스는 큐에 항목을 추가하거나 뽑아낼 때 상황에 따라 대기할 수 있도록 구현되어 있다. 예를 들어 큐가 비어 있다면 큐에서 항목을 봅아내는 연산은 새로운 항목이 추가될 때까지 대기한다. 반대로 큐에 크기가 지정되어 있는 경우에 큐가 지정한 크기만큼 가득 차 있다면, 큐에 새로운 항목을 추가하는 연산은 큐에 빈 자리가 생길 때까지 대기한다. BlockingQueue 클래스는 프로듀서-컨슈머( producer-consumer ) 패턴을 구현할 때 굉장히 편리하게 사용할 수 잇다.

- ConcurrentSkipListMap  ConcurrentSkipListSet  각각 SortedMap 과 SortedSet 클래스의 병렬성을 높이도록 발전된 형태이다. ( SortedMap 과 SortedSet 은 treeMap 과 treeSet을 synchronizedMap 으로 처리해 동기화시킨 컬렉션과 같다. ) 



5.2.1. ConcurrentHashMap

 
- ConcurrentHashMap 은 HashMap 과 같이 해시를 기반으로 하는 Map이다. 하지만 내부적으로는 이전에 사용하던 것과 전혀 다른 동기화 기법을 채택해 병렬성과 확장성이 훨씬 나아졌다. 이전에는 모든 연산에서 하나의 락을 사용했기 때문에 특정 시점에 하나의 스레드만이 해당 컬렉션을 사용할 수 있었다. 하지만 ConcurrentHashMap 은 락스트라이핑( Lock striping ) 이라 부르는 굉장히 세밀한 동기화 방법을 사용해 여러 스레드에서 공유하는 상태에 훨씬 잘 대응할 수 있다. 

- 값을 읽어가는 연산은 많은 수의 스레드라도 얼마든지 동시에 처리할 수 있고, 읽기 연산과 쓰기 연산도 동시에 처리할 수 있으며, 쓰기 연산은 제한된 개수만큼 동시에 수행할 수 있다. 속도를 보자면 여러 스레드가 동시에 동작하는 환경에서 일반적으로 훨씬 높은 성능 결과를 볼 수 있으며, 이와 함꼐 단일 스레드 환경에서도 성능상의 단점을 찾아볼 수 없다. 

- 다른 병렬 컬렉션 클래스와 비슷하게 ConcurrentHashMap 클래스도 Iterator를 만들어 내는 부분에서 많이 발전했는데, ConcurrentHashMap 이 만들어 낸 iterator 는 ConcurrrentModificationException 을 발생시키지 않는다. 따라서 ConcurrentHashMap의 항목을 대상으로 반복문을 실행하는 경우에는 따로 락을 걸어 동기화해야 할 필요가 없다.

- ConcurrrnetHashMap 에서 만들어 낸 iterator 는 즉시 멈춤(fail-fast) 대신 미약한 일관성 전략을 취한다. 미약한 일관성 전략은 반복문과 동시에 컬렉션의 내용을 변경한다 해도 Iterator 를 만들었던 시점의 상황대로 반복을 계속할 수 있다. 게다가 Iterator를 만든 시점 이후에 변경된 내용을 반영해 동작할 수도 있다.( 이 부분은 반드시 보장되지는 않는다. ) 

- 병렬성 문제때문에 Map의 모든 하위 클래스에서 공통적으로 사용하는 size 메소드나 isEmpty 메소드의 의미가 약간 약해졌다. 예를 들어 size 메소드는 그 결과를 리턴하는 시점에 이미 실제 객체의 수가 바뀌었을 수 있기 때문에 정확히 말하자면 size 메소드의 결과는 정확한 값일 수 없고, 단지 추정 값일 뿐이다.

- 동기화된 Map 에서는 지원하지만 ConcurrentHashMap에서는 지원하지 않는 기능이 있는데, 바로 맵을 독점적으로 사용할 수 있도록 막아버리는 기능이다. HashTable 과 SynchronizedMap 메소드를 사용하면 Map 에 대한 락을 잡아 다른 스레드에서 사용하지 못하도록 막을 수 있다. 

- ConcurrentHashMap 을 사용하면 HashTable 이나 SynchronizedMap 메소드를 사용하는 것에 비해 단점이 있기는 하지만, 훨씬 많은 장점을 얻을 수 있기 때문에 대부분의 경우에는 HashTable 이나 SynchronizedMap 을 사용하던 부분에 ConcurrentHashMap 을 대신 사용하기만 해도 별 문제 없이 많은 장점을 얻을 수 있다. 만약 작업 중인 애플리케이션에서 특정 Map 을 완전히 독점해서 사용하는 경우가 있다면, 그 부분에 ConcurrentHashMap 을 적용할 때는 충분히 신경을 기울여야 한다. 



5.2.2. Map 기반의 또 다른 단일 연산


- ConcurrentHashMap 클래스에는 일반적으로 많이 사용하는 put-if-absent, remove-if-equals, replace-if-equal 과 같이 자주 필요한 몇 가지의 연산이 이미 구현되어 있다.



5.2.3. CopyOnWriteArrayList


- CopyOnWriteArrayList 클래스는 동기화된 List 클래스보다 병렬성을 훨씬 높이고자 만들어졌다. 병렬성이 향상됐고, 특히 List에 들어있는 값을 Iterator로 불러다 사용하려 할 때 List 전체에 락을 걸거나 List 를 복제할 필요가 없다. ( CopyOnWriteArrayList 와 비슷하게 Set인터페이스를 구현하는 CopyOnWriteArraySet 도 있다. )

- '변경할 때마다 복사'하는 컬렉션 클래스는 불변 객체를 외부에 공개하면 여러 스레드가 동시에 사용하려는 환경에서도 별다른 동기화 작업이 필요 없다는 개념을 바탕으로 스레드 안전성을 확보하고 있다. 하지만 컬렉션이라면 항상 내용이 바귀어야 하기 때문에, 컬렉션의 내용이 변경될 때마다 복사본을 새로 만들어 내는 전략을 취한다. 만약 CopyOnWriteArrayList 컬렉션에서 iterator 를 뽑아내 사용한다면 Iterator  를 뽑아내는 시점의 컬렉션 데이터를 기준으로 반복하며, 반복하는 동안 컬렉션에 추가되거나 삭제되는 내용은 반복문과 상관 없는 복사본을 대상으로 반영하기 때문에 동시 사용성에 문제가 없다. 

- 반복문에서 락을 걸어야 할 필요가 있기는 하지만, 반복할 대상 전체를 한번에 거는 대신 개별 항목마다 가시성을 확보하려는 목적으로 잠깐씩 락을 거는 정도면 충분하다.

- 변경할 때마다 복사하는 컬렉션에서 뽑아낸 Iterator를 사용할 때는 ConcurrentModificationException이 발생하지 않으며, 컬렉션에 어떤 변경 작업을 가한다 해도 Iteraotr를 뽑아내던 그 시점에 컬렉션에 들어 있던 데이터를 정확하게 활용할 수 있다.

- 물론 컬렉션의 데이터가 변경될 때마다 복사본을 만들어내기 때문에 성능의 측면에서 손해를 볼 수 있고, 특히나 컬렉션에 많은 양의 자료가 들어 있다면 손실이 클 수 있다. 따라서 변경할 때마다 복사하는 컬렉션은 변경 작업보다 반복문으로 읽어내는 일이 훨씬 빈번한 경우에 효과적이다. 




5.3.. 블로킹 큐와 프로듀서-컨슈머 패턴

- 블로킹 큐(blocking queue)는 put과 take라는 핵심 메소드를 갖고 있고, 더불어 offer 와 poll 이라는 메소드도 갖고 있다. 만약 큐가 가득 차 있다면 put 메소드는 값을 추가할 공간이 생길 때까지 대기한다. 반대로 큐가 비어 있는 상태라면 take 메소드는 뽑아낼 값이 들어올 때까지 대기한다. 큐는 그 크기를 제한할 수도 있고, 제한하지 않을 수도 있다.

- 블로킹 큐는 프로듀서-컨슈머(producer-consumer)패턴을 구현할 때 사용하기에 좋다. 프로듀서-컨슈머 패턴은 '해야 할 일' 목록을 가운데에 두고 작업을 만들어 내는 주체와 작업을 처리하는 주체를 분리시키는 설계 방법이다. 프로듀서-컨슈머 패턴을 사용하면 작업을 만들어 내는 부분과 작업을 처리하는 부분을 완전히 분리할 수 있기 때문에 개발 과정을 좀 더 명확하게 단순화시킬 수 있다.

-  프로듀서-컨슈머 패턴을 적용해 프로그램을 구현할 때 블로킹 큐를 사용하는 경우가 많은데, 예를 들어 프로듀서는 작업을 새로 만들어 큐에 쌓아두고, 컨슈머는 큐에 쌓여 있는 작업을 가져다 처리하는 구조다. 프로듀서는 어떤 컨슈머가 몇 개나 동작하고 있는지에 대해 전혀 신경 쓰지 않을 수 있다. 단지 새로운 작업 내용을 만들어 큐에 쌓아두기만 하면 된다. 반대로 컨슈머 역시 프로듀서에 대해서 뭔가를 알고 있어야 할 필요가 없다. 프로듀서가 몇 개이건, 얼마나 많은 작업을 만들어 내고 있건 상관이 없다. 단지 큐에 쌓여 있는 작업을 가져다 처리하기만 하면 된다. 블로킹 큐를 사용하면 여러 개의 프로듀서와 여러 개의 컨슈머가 작동하는 프로듀서-컨슈머 패턴을 손쉽게 구현할 수 있다. 큐와 함께 스레드 풀을 사용하는 경우가 바로 프로듀서-컨슈머 패턴을 활용하는 가장 흔한 경우이다.

- 프로듀서가 컨슈머가 감당할 수 잇는 것보다 많은 양의 작업을 만들어 내면 해당 애플리케이션의 큐에는 계속해서 작업이 누적되어 결국에는 메모리 오류가 발생하게 된다. 하지만 큐의 크기에 제한을 두면 큐에 빈 공간이 생길 때까지 put 메소드가 대기하기 때문에 프로듀서 코드를 작성하기가 훨씬 간편해진다. 그러면 컨슈머가 작업을 처리하는 속도에 프로듀서가 맞춰야 하며, 컨슈머가 처리하는 양보다 많은 작업을 만들어 낼 수 없다.

-  블로킹 큐에는 offer 메소드가 있는데, offer 메소드는 큐에 값을 넣을 수 없을 때 대기하지 않고 바로 공간이 모자라 추가할 수 없다는 오류를 알려준다. offer 메소드를 잘 활용하면 프로듀서가 작업을 많이 만들어 과부하에 이르는 상태를 좀 더 효과적으로 처리할 수 있다.

블로킹 큐는 애플리케이션이 안정적으로 동작하도록 만들고자 할 때 요긴하게 사용할 수 있는 도구이다. 블로킹 큐를 사용하면 처리할 수 있는 양보다 훨씬 많은 작업이 생겨 부하가 걸리는 상황에서 작업량을 조절해 애플리케이션이 안정적으로 동작하도록 유도할 수 있다.


- 생각하기에는 컨슈머가 항상 밀리지 않고 작업을 마쳐준다고 가정하고, 따라서 작업 큐에 제한을 둘 필요가 없을 것이라고 마음 편하게 넘어갈 수도 있다. 이런 가정을 하는 순간 나중에 프로그램 구조를 뒤집어 엎어야 하는 원인을 하나 남겨두는 것뿐이니 주의하자. 블로킹 큐를 사용해 설계 과정에서부터 프로그램에 자원 관리 기능을 추가하자.

- LinkedBlockingQueue  ArrayBlockingQueue  FIFO 형태의 큐인데, LinkedList 나 ArrayList 에서 동기화된 List 인스턴스를 뽑아 사용하는 것보다 성능이 좋다. PriorityBlockingQueue 클래스는 우선 순위를 기준으로 동작하는 큐이고, FIFO 가 아닌 다른 순서로 큐의 항목을 처리해야 하는 경우에 손쉽게 사용할 수 있다.

- SynchronousQueue 클래스도 BlockingQueue 인터페이스를 구현하는데, 큐에 항목이 쌓이지 않으며, 따라서 큐 내부에 값을 저장할 수 있도록 공간을 할당하지도 않는다. 대신 큐에 값을 추가하려는 스레드나 값을 읽어가려는 스레드의 큐를 관리한다.

- 프로듀서와 컨슈머가 직접 데이터를 주고받을 때까지 대기하기 때문에 프로듀서에서 컨슈머로 데이터가 넘어가는 순간은 굉장히 짧아진다는 특징이 있다. 컨슈머에게 데이터를 직접 넘겨주기 때문에 넘겨준 데이터와 관련되어 컨슈머가 갖고 있는 정보를 프로듀서가 쉽게 넘겨 받을 수도 있다.

- SynchronousQueue는 데이터를 넘겨 받을 수 있는 충분한 개수의 컨슈머가 대기하고 있는 경우에 사용하기 좋다.



5.3.1. 예제 : 데스크탑 검색




5.3.2. 직렬 스레드 한정


- 프로듀서-컨슈머 패턴과 블로킹 큐는 가변 객체(mutable object)를 사용할 때 객체의 소유권을 프로듀서에서 컨슈머로 넘기는 과정에서 직렬 스레드 한정(serial thread confinement)기법을 사용한다. 스레드에 한정된 객체는 특정 스레드 하나만이 소유권을 가질 수 있는데, 객체를 안전한 방법으로 공개하면 객체에 대한 소유권을 이전(transfer)할 수 있다. 이렇게 소유권을 이전하고 나면 이전받은 컨슈머 스레드가 객체에 대한 유일한 소유권을 가지며, 프로듀서 스레드는 이전된 객체에 대한 소유권을 완전히 잃는다. 이렇게 안전한 공개 방법을 사용하면 새로운 소유자로 지정된 스레드는 객체의 상태를 완벽하게 볼 수 있지만 원래 소유권을 갖고 있던 스레드는 전혀 상태를 알 수 없게 되어, 새로운 스레드 내부에 객체가 완전히 한정된다.

- 객체 풀(object pool)은 직렬 스레드 한정 기법을 잘 활용하는 예인데, 풀에서 소유하고 있던 객체를 외부 스레드에게 '빌려주는' 일이 본업이기 때문이다. 풀 내부에 소유하고 있던 객체를 외부에 공개할 떄 적절한 동기화 작업이 되어 있고, 그와 함게 풀에서 객체를 빌려다 사용하는 스레드 역시 빌려온 객체를 외부에 공개하거나 풀에 반납한 이후에 계속해서 사용하는 등의 일을 하지 않는다면 풀 스레드와 사용자 스레드 간에 소유권이 원활하게 이전되는 모습을 볼 수 있다.
 
- 가변 객체의 소유권을 이전해야 할 필요가 있다면, 위에서 설명한 것과 다른 객체 공개 방법을 사용할 수도 있다. 하지만 항상 소유권을 이전받는 스레드는 단 하나여야 한다는 점을 주의해야 한다.



5.3.3. 덱, 작업 가로채기


- Deque(덱) 과 BlockingDeque 은 각각 Queue 와 Blockingqueue 를 상속받은 인터페이스이다. Deque는 앞과 뒤 어느 쪽에도 객체를 쉽게 삽입하거나 제거할 수 있도록 준비된 큐이며, Deque을 상속받은 실제 클래스로는 ArrayDeque LinkedBlockingDeque 이 있다.

- 작업 가로채기(work stealing) 이라는 패턴을 적용할 때에는 덱을 그대로 가져다 사용할 수 있다. 작업 가로채기 패턴에서는 모든 컨슈머가 각자의 덱을 갖는다. 만약 특정 컨슈머가 자신의 덱에 들어 있던 작업을 모두 처리하고 나면 다른 컨슈머의 덱에 쌓여있는 작업 가운데 맨 뒤에 추가된 작업을 가로채 가져올 수 있다. 작업 가로채기 패턴은 그 특성상 컨슈머가 하나의 큐를 바라보면서 서로 작업을 가져가려고 경쟁하지 않기 때문에 일반적인 프로듀서-컨슈머 패턴보다 규모가 큰 시스템을 구현하기에 적당하다. 더군다나 컨슈머가 다른 컨슈머의 큐에서 작업을 가져오려 하는 경우에도 앞이 아닌 맨 뒤의 작업을 가져오기 때문에 맨 앞의 작업을 가져가려는 원래 소유자와 경쟁이 일어나지 않는다.

- 작업 가로채기 패턴은 또한 컨슈머가 프로듀서의 역할도 갖고 있는 경우에 적용하기에 좋은데, 스레드가 작업을 진행하는 도중에 새로 처리해야 할 작업이 생기면 자신의 덱에 새로운 작업을 추가한다. ( 작업을 서로 공유하도록 구성하는 경우에는 다른 작업 스레드의 덱에 추가하기도 한다.) 만약 자신의 덱이 비었다면 다른 작업 스레드의 덱을 살펴보고 밀린 작업이 있다면 가져다 처리해 자신의 덱이 비었다고 쉬는 스레드가 없도록 관리한다. 




5.4. 블로킹 메소드, 인터럽터블 메소드

- 스레드는 여러 가지 원인에 의해 블록 당하거나, 멈춰질 수 있다. 예를 들어 I/O 작업이 끝나기를 기다리는 경우도 있고, 락을 확보하기 위해 기다리는 경우도 있고, Thread.sleep 메소드가 끝나기를 기다리는 경우도 있고, 다른 스레드가 작업 중인 내용의 결과를 확인하기 위해 기다리는 경우도 있다.

- 스레드가 블록되면 동작이 멈춰진 다음 블록된 상태(BLOCKED, WAITING, TIMED_WAITING) 가운데 하나를 갖게 된다. 블로킹 연산은 단순히 실행 시간이 오래 걸리는 일반 연산과는 달리 멈춘 상테에서 특정한 신호를 받아야 계속해서 실행할 수 있는 연산을 말한다.

- 기다리던 외부 신호가 확인되면 스레드의 상태가 다시 RUNNABLE 상태로 넘어가고 다시 시스템 스케줄러를 통해 CPU 를 사용할 수 있게 된다.

- Thread 클래스는 해당 스레드를 중단시킬 수 있도록 interrupt 메소드를 제공하며, 해당 스레드에 인터럽트가 걸려 중단된 상태인지를 확인할 수 있는 메소드도 있다. 모든 스레드에는 인터럽트가 걸린 상태인지를 알려주는 불린 값이 있으며, 외부에서 인터럽트를 걸면 불린 변수에 true 가 설정된다.

-  스레드 A가 스레드 B에 인터럽트를 건다는 것은 스레드 B에게 실행을 멈추라고 '요청'하는 것일 뿐이며, 인터럽트가 걸린 스레드 B는 정상적인 종료 시점 이전에 적절한 때를 잡아 실행 중인 작업을 멈추면 된다.

- 프로그램이 호출하는 메소드 가운데 InterruptedException 이 발생할 수 있는 메소드가 있다면 그 메소드를 호출하는 메소드 역시 블로킹 메소드이다. 따라서  InterruptedException이 발생했을 때 그에 대처할 수 있는 방법을 마련해둬야 한다. 라이브러리 형태의 코드라면 일반적으로 두 가지 방법을 사용할 수 있다.

 1. InterruptedException 을 전달 : 받아낸 InterruptedException 을 그대로 호출한 메소드에게 넘긴다.
 2. 인터럽트를 무시하고 복구 : InterruptedException 을 throw 할 수 없을 수 있는데, 이 경우는 예외를 catch 한 다음, 현재 스레드의 interrupt 메소드를 호출해 인터럽트 상태를 설정해 상위 호출 메소드가 인터럽트 상황이 발생했음을 알 수 있도록 해야 한다.

- InterruptedException을 처리함에 있어서 하지 말아야 할 일이 한 가지 있다. 바로 InterruptedException 을 cath 하고는 무시하고 아무 대응도 하지 않는 일이다. 이렇게 아무런 대응을 하지 않으면 인터럽트가 발생했었다는 증거를 인멸하는 것이며, 호출 스택의 상위 메소드가 인터럽트에 대응해 조치를 취할 수 있는 기회를 주지 않는다.

- 발생한 InterruptedException 을 먹어버리고 더 이상 전파하지 않을 수 있는 경우는 Thread 클래스를 직접 상속하는 경우뿐이며, 이럴 때는 인터럽트에 필요한 대응 조치를 모두 취했다고 간주한다.
 





5.5. 동기화 클래스.

- 상태 정보를 사용해 스레드 간의 작업 흐름을 조절할 수 있도록 만들어진 모든 클래스륻 동기화 클래스( synchronizer ) 라고 한다. 동기화 클래스의 예로는 세마포어(semaphore), 배리어(barrier), 래치(latch) 등이 있다.

- 모든 동기화 클래스는 구조적인 특징을 갖고 있다. 모두 동기화 클래스에 접근하려는 스레드가 어느 경우에 통과하고 어느 경우에는 대기하도록 멈추게 해야 하는지를 결정하는 상태 정보를 갖고 있고, 그 상태를 변경할 수 있는 메소드를 제공하고, 동기화 클래스가 특정 상태에 진입할 때가지 효과적으로 대기할 수 있는 메소드도 제공한다.


5.5.1. 래치


- 래치는 스스로가 터미널(terminal)상태에 이를 때까지의 스레드가 동작하는 과정을 늦출 수 있도록 해주는 동기화 클래스이다. 일종의 관문과 같은 형태로, 래치가 터미널 상태에 이르기 전에는 관문이 닫혀 있고, 어떤 스레드도 통과할 수 없다. 그리고 래치가 터미널 상태에 다다르면 관문이 열리고 모든 스레드가 통과한다. 래치가 한 번 터미널 상태에 다다르면 그 상태를 다시 이전으로 되돌릴 수는 없으며, 따라서 한 번 열린 관문은 계속해서 열린 상태로 유지된다.

- 특정한 단일 동작이 완료되기 이전에는 어떤 기능도 동작하지 않도록 막아야 하는 경우에 요긴하게 사용할 수 있다.
 * 특정 자원을 확보하기 전에는 작업을 시작하지 말아야 하는 경우.
 * 의존성을 갖고 있는 다른 서비스가 시작하기 전에는 특정 서비스가 실행되지 않도록 막아야 하는 경우.
 * 특정 작업에 필요한 모든 객체가 실행할 준비를 갖출 때까지 기다리는 경우.

- CountDownLatch는 하나 또는 둘 이상의 스레드가 여러 개의 이벤트가 일어날 때까지 대기할 수 있도록 되어 있다. 래치의 상태는 양의 정수 값으로 카운터를 초기화하며, 이 값은 대기하는 동안 발생해야 하는 이벤트의 건수를 의미한다.

- CountDownLatch 의 countDown 메소드는 대기하던 이벤트가 발생했을 때 내부에 갖고 있는 이벤트 카운터를 하나 낮춰주고, await 메소드는 래치 내부의 카운터가 0 이 될 때까지 대기하던 이벤트가 모두 발생했을 때까지 대기하도록 하는 메소드이다. 외부 스레드가 awiat 메소드를 호출할 때 래치 내부의 카운터가 0보다 큰 값이었다면, await 메소드는 카운터가 0이 되거나, 대기하던 스레드에 인터럽트가 걸리거나, 대기 시간이 길어 타임아웃이 걸릴 때까지 대기한다.



5.5.2. FutureTask


- FutureTask 가 나타내는 연산 작업은 Callable 인터페이스를 구현하도록 되어 있는데, 시작 전 대기, 시작됨, 종료됨과 같은 세 가지 상태를 가질 수 있다. 종료된 상태는 정상적인 종료, 취소, 예외 상황발생과 같이 연산이 끝나는 모든 종류의 상태를 의미한다. FutureTask 가 한 번 종료됨 상태에 이르고 나면 더 이상 상태가 바뀌는 일은 없다.

- FutureTask 는 Executor 프레임웍에서 비동기적인 작업을 실행하고자 할 때 사용하며, 기타 시간이 많이 필요한 모든 작업이 있을 때 실제 결과가 필요한 시점 이전에 미리 작업을 실행시켜두는 용도로 사용한다. 



5.5.3. 세마포어 ( Semaphore )


- 카운팅 세마포어(counting semaphore)는 특정 자원이나 특정 연산을 동시에 사용하거나 호출할 수 있는 스레드의 수를 제한하고자 할 때 사용한다. 카운팅 세마포어의 이런 기능을 사용하면 자원 풀(pool)이나 컬렉션의 크기에 제한을 두고자 할 때 유용한다.

- Semaphore 클래스는 가상의 퍼밋(permit)을 만들어 내부 상태를 관리하며, Semaphore 를 생성할 때 생성 메소드에 최초로 생성할 퍼밋의 수를 넘겨준다. 외부 스레드는 퍼밋을 요청해 확보( 남은 퍼밋이 있는 경우 )하거나, 이전에 확보한 퍼밋을 반납할 수도 있다. 현재 사용할 수 잇는 남은 퍼밋이 없는 경우, acquire 메소드는 남는 퍼밋이 생기거나, 인터럽트가 걸리거나, 지정한 시간을 넘겨 타임아웃이 걸리기 전까지 대기한다. release 는 확보했던 퍼밋을 다시 세마포어에게 반납하는 기능을 한다.

- 세마포어는 데이터베이스 연결 풀과 같은 자원 풀에서 요긴하게 사용할 수 있다.



5.5.4. 배리어


- 배리어( barrier ) 는 특정 이벤트가 발생할 때까지 여러 개의 스레드를 대기 상태로 잡아둘 수 있다는 측면에서 래치와 비슷하다고 볼 수 있다. 래치와의 차이점은 모든 스레드가 배리어 위치에 동시에 이르러야 관문이 열리고 계속해서 실행할 수 있다는 점이 다르다. 래치는 '이벤트'를 기다리기 위한 동기화 클래스이고, 배리어는 '다른 스레드'를 기다리기 위한 동기화 클래스이다. 

- CyclicBarrier 클래스를 사용하면 여러 스레드가 특정한 배리어 포인트에서 반복적으로 서로 만나는 기능을 모델링할 수 있고, 커다란 문제 하나를 여러 개의 작은 부분 문제로 분리해 반복적으로 병렬 처리하는 알고리즘을 구현하고자 할 때 적용하기 좋다.

- 스레드는 각자 배리어 포인트에 다다르면 await 메소드를 호출하며, await 메소드는 모든 스레드가 배리어 포인트에 도달할 떄까지 대기한다. 모든 스레드가 배리어 포인트에 도달하면 배리어는 모든 스레드를 통과시키며, await 메소드에서 대기하고 있던 스레드는 대기 상태가 모두 풀려 실행되고, 배리어는 다시 초기상태로 돌아가 다음 배리어 포인트를 준비한다. 만약 await 를 호출하고 시간이 너무 오래 지나 타임아웃이 걸리거나 await 메소드에서 대기하던 스레드에 인터럽트가 걸리면 배리어는 깨진 것으로 간주하고, await 에서 대기하던 모든 스레드에 BrokenBarrierException 이 발생한다.

- 배리어가 성공적으로 통과하면 await 메소드는 각 스레드별로 배리어 포인트에 도착한 순서를 알려주며, 다음 배리어 포인트로 반복 작업을 하는 동안 뭔가 특별한 작업을 진행할 일종의 리더를 선출하는 데 이 값을 사용할 수 있다.

- 배리어와 약간 다른 형태로 Exchanger 클래스가 있는데 Exchanger 는 두 개의 스레드가 연결되는 배리어이며, 배리어 포인트에 도달하면 양쪽의 스레드가 서로 갖고 있던 값을 교환한다. Exchanger 클래스는 양쪽 스레드가 서로 대칭되는 작업을 수행할 때 유용하다. 

- Exchanger 객체를 통해 양쪽의 스레드가 각자의 값을 교환하는 과정에서 서로 넘겨지는 객체는 안전한 공개 방법으로 넘겨주기 때문에 동기화 문제를 걱정할 필요가 없다.


=====================================================================================================================================



출처 : http://deepblue28.tistory.com/entry/Java-SynchronizedCollections-vs-ConcurrentCollections
참고 : http://tutorials.jenkov.com/java-util-concurrent/concurrentmap.html
       http://whiteship.me/?p=9191

SynchronizedCollections(동기화된 컬렉션)과 ConcurrentCollections(병렬 컬렉션)

동기화된 컬렉션 클래스는 컬렉션의 내부 변수에 접근하는 통로를 일련화해서 스레드 안전성을 확보했다. 하지만 이렇게 만들다 보니 여러 스레드가 한꺼번에 동기화된 컬렉션을 사용하려고 하면 동시 사용성은 상당 부분 손해를 볼 수 밖에 없다. 하지만 병렬 컬렉션은 여러 스레드에서 동시에 사용할 수 있도록 설계되었다.

ConcurrentMap에는 put-if-absent, replace, condition-remove 등을 정의하고 있다.


기존에 사용하던 동기화 컬렉션 클래스를 병렬 컬렉션으로 교체하는 것만으로도 별다른 위험 요소 없이 전체적인 성능을 상당히 끌어 올릴 수 있다.

<에이콘 - 자바 병렬 프로그래밍(p.137) 발췌>


동기화되지 않은(unsynchronized) 컬렉션

- List: ArrayList, LinkedList

- Map: HashMap

- Set: HashSet

- SortedMap: TreeMap

- SortedSet: TreeSet

- Since JDK 1.2

- 문제점: Thread Safe하지 않다.


동기화된(synchronized) 컬렉션

- Vector, Hashtable, Collections.synchronizedXXX()로 생성된 컬렉션들

- Since JDK 1.2

- 문제점: Thread Safe하나, 두개 이상의 연산을 묶어서 처리해야 할 때 외부에서 동기화 처리를 해줘야 한다. (Iteration, put-if-absent, replace, condition-remove 등)


병렬(concurrent) 컬렉션

- List: CopyOnWriteArrayList

- Map: ConcurrentMap, ConcurrentHashMap

- Set: CopyOnWriteArraySet

- SortedMap: ConcurrentSkipListMap (Since Java 6)

- SortedSet: ConcurrentSkipListSet (Since Java 6)

- Queue 계열:ConcurrentLinkedQueue

- Since Java 5

- 특이사항: Concurrent(병렬/동시성)이란 단어에서 알 수 있듯이 Synchronized 컬렉션과 달리 여러 스레드가 동시에 컬렉션에 접근할 수 있다. ConcurrentHashMap의 경우, lock striping 이라 부르는 세밀한 동기화 기법을 사용하기 때문에 가능하다. 구현 소스를 보면 16개의 락 객체를 배열로 두고 전체 Hash 범위를 1/16로 나누어 락을 담당한다. 최대 16개의 스레드가 경쟁없이 동시에 맵 데이터를 사용할 수 있게 한다. (p.350)

반 대로 단점도 있는데, clear()와 같이 전체 데이터를 독점적으로 사용해야할 경우, 단일 락을 사용할 때보다 동기화 시키기도 어렵고 자원도 많이 소모하게 된다. 또한, size(), isEmpty()같은 연산이 최신값을 반환하지 못할 수도 있다. 하지만 내부 상태를 정확하게 알려주지 못한다는 단점이 그다지 문제되는 경우는 거의 없다.


※ Queue, BlockingQueue 인터페이스는 Java 5에서 추가되었다. (Deque, BlockingDeque는 6에서 추가되었다.)

※ Synchronized 컬렉션은 객체 자체에 락을 걸어 독점하게되고, Concurrent 컬렉션은 객체 자체 독점하기가 쉽지 않은 단점이 있지만, 장점이 훨씬 더 많다. Concurrent 컬렉션은 컬렉션 전체를 독점하기 위해서는 충분히 신경을 기울여야 한다.

※ Hash를 기반으로 하는 컬렉션은 hashCode()의 해시값이 넓고 고르게 분포되지 못하면 한쪽으로 쏠린 해시 테이블을 사용하게 되는데, 최악의 경우는 단순한 Linked List와 거의 동일한 상태가 될 수 있다.


* ConcurrentMap 사용법

ConcurrentMap concurrentMap = new ConcurrentHashMap(); concurrentMap.put("key", "value"); Object value = concurrentMap.get("key");

// key가 존재하지 않으면 map에 저장, return 은 null
// key가 존재하면 map에 저장, return 은 기존에 저장되어 있는 값
concurrentMap.put("key", "value");


// key가 존재하지 않으면 map에 저장, return 은 null
// key가 존재하면 map에 저장하지 않음, return 은 기존에 저장되어 있는 값
concurrentMap.putIfAbsent("key", "value");


* Synchronized Collections 사용법

public class CrunchifySynchronizedListFromArrayList {
    public static void main(String[] args) {
        // ********************** synchronizedList ************************
        ArrayList<String> crunchifyArrayList = new ArrayList<String>();

        // populate the crunchifyArrayList
        crunchifyArrayList.add("eBay");
        crunchifyArrayList.add("Paypal");
        crunchifyArrayList.add("Google");
        crunchifyArrayList.add("Yahoo"); 

        // Returns a synchronized (thread-safe) list backed by the specified
        // list. In order to guarantee serial access, it is critical that all
        // access to the backing list is accomplished through the returned list.
        List<String> synchronizedList = Collections.synchronizedList(crunchifyArrayList);
        System.out.println("synchronizedList conatins : " + synchronizedList); 

        // ********************** synchronizedMap ************************

        Map<String, String> crunchifyMap = new HashMap<String, String>();

        // populate the crunchifyMap
        crunchifyMap.put("1", "eBay");
        crunchifyMap.put("2", "Paypal");
        crunchifyMap.put("3", "Google");
        crunchifyMap.put("4", "Yahoo");
   

        // create a synchronized map
        Map<String, String> synchronizedMap = Collections.synchronizedMap(crunchifyMap);

        System.out.println("synchronizedMap contains : " + synchronizedMap);
    }
}

-------------------------------------------------------------------------------------

import java.util.Collection; 
import java.util.Collections;
import java.util.TreeSet;

public class SynchronizedCollectionEx { 
 //treeSet은 Thread-Safe 하지 않습니다.
 static Collection treeSet = new TreeSet(); 
 //synchronizedSet은 Thread-Safe 합니다. 이 객체를 공유 객체로 사용해야 합니다. 
 static Collection synchronizedSet = Collections.synchronizedCollection(treeSet);
 public static void main(String[] args){
     Thread[] t = new Thread[10000];
     for (int i = 0; i < 10000; i++){
         t[i] = new Thread(new WriterThread());
         t[i].start();
     }
 }
}

class WriterThread implements Runnable {
  public void run() {
    for (int i = 0; i < 100; i++){
      SynchronizedCollectionEx.synchronizedSet.add(new Integer((int)(Math.random() * 10)));
      SynchronizedCollectionEx.synchronizedSet.remove(new Integer((int)(Math.random() * 10)));
    }
  }
}


=====================================================================================================================================

Concurrent List 만들기

출처 : https://okihouse.tistory.com/entry/List-Concurrent-List-%EB%A7%8C%EB%93%A4%EA%B8%B0


보통의 경우 가장 잘 알려진 Concurrent Map 의 경우 ConcurrentHashMap이 있다.

동기화를 가능하게 해주므로 Thread Safe를 지원하게 되는데, 간혹가다 Thread Safe를 지원하는 List를 사용해야 될 경우가 있다.


Thread Safe를 가능하게 하는 여러가지 방법이 아래와 같이 존재한다. 


  • Collections.synchronizedList();
  • CopyOnWriteList
  • concurrent Queue or Deque implementations
  • custom List implementations


보통 가장 많이 쓰는게 아마 CopyOnWriteArrayList 일 것 같다.

하지만 CopyOnWriteArrayList 에는 결점이 하나 있는데 이름 그대로 List를 복사하여 사용한다는 점이다. 

즉, 복사본을 만들어 사용한다는 점인데, 이 경우 Write 시간에 영향을 미칠 수 있다.

결국 CopyOnWriteArrayList 는 읽기속도는 빠를 수 있으나, 쓰기 시간이 지연될 수 있으며 쓰기 속도가 중요한 코드에서는 권장하지 않는다.


몇가지 테스트와 검색을 통해서 알아낸 점은 List를 사용자의 환경에 맞게 구현하여 사용하는게 좋다고 판단하였다.


몇몇 테스트를 직접 하지는 않았고, 실제 테스트한 사람의 결과를 보니 놀라운 점을 발견하였다.



5000 번의 읽기와 5000번의 쓰기를 완료한 시간 (읽기 쓰기 비율은 1:1이며 10개의 Thread로 테스팅 함)

  • ArrayList - 16450 ns( not thread safe)
  • ConcurrentList - 20999 ns <- 사용자가 직접 구현한 Concurrent List
  • Vector -35696 ns
  • CopyOnWriteArrayList - 197032 ns


위 결과로도 알 수 있듯이 CopyOnWriteArrayList 는 효율면에서 떨어지는 게 나타난다.


생각보다 직접 구현하는 편이 효율적인 것 같아서 직접 구현을 해보기로 하였다.

위와같이 List<T> Interface를 상속받아 구현하면 된다. List 에는 꼭 구현해야 되는 Method 들이 있는데 일단 전부 @Override 받아야 한다.

  1. public class ConcurrentList<T> implements List<T>  

전부 구현할 필요는 없을 것 같아서 일부만 구현하였다.

  1. private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();  
  2. private final List<T> list;  
  3.   
  4. public ConcurrentList(List<T> list) {  
  5.     this.list = list;  
  6. }  
  7.   
  8. @Override  
  9. public boolean add(T t) {  
  10.     readWriteLock.writeLock().lock();  
  11.     boolean sucsses;  
  12.     try {  
  13.         sucsses = list.add(t);  
  14.     } finally {  
  15.         readWriteLock.writeLock().unlock();  
  16.     }  
  17.     return sucsses;  
  18. }  
  19.   
  20. @Override  
  21. public T get(int index) {  
  22.     readWriteLock.readLock().lock();  
  23.     try {  
  24.         return list.get(index);  
  25.     } finally {  
  26.         readWriteLock.readLock().unlock();  
  27.     }  
  28. }  
  29.   
  30. @Override  
  31. public int size() {  
  32.     readWriteLock.readLock().lock();  
  33.     try {  
  34.         return list.size();  
  35.     } finally {  
  36.         readWriteLock.readLock().unlock();  
  37.     }  
  38. }  

위와 같이 add, get size 만 구현하였고 나머지는 본인의 취향에 따라서 구현하면 된다. 

구동 방법은 생각외로 간단하다. write lock 으로 Thread Safe 환경을 만들어주고 해당 명령어를 실행시킨 뒤 unlock 한다.


보기에는 마치 transaction 방식처럼 느껴지면서 성능에 영향을 미칠 것 같지만 생각외로 효율이 좋다.


실제 List 코드에서는 아래와 같이 작성해서 사용하면 된다. 

단!! 구현하지 않은 코드를 사용하였다간.... 책임지지 못할 일이 발생한다... 

  1. private List<JedisPool> slaveJedisList = new ConcurrentList<JedisPool>(new ArrayList<JedisPool>());  

Redis 에서 사용할 Jedis Pool 에서 사용할 것이기 때문에 위와 같이 선언하였고 사용자의 Object Type 에 맞게 작성하면 된다.

저렇게 사용하면 문제 없이 사용이 가능하다. 


특히, List 의 기능중에 iterator에서는 아래와 같이 객체를 만들어 사용하라고 권장한다.

이유는 ConcurrentModificationException 를 회피하며, 실제 Origin 객체를 수정하지 않고 복사본으로 사용하기 위함이다. 

  1. public Iterator<T> iterator()  
  2.     {  
  3.         readWriteLock.readLock().lock();  
  4.         try  
  5.         {  
  6.             return new ArrayList<T>( list ).iterator();  
  7.         }  
  8.         finally  
  9.         {  
  10.             readWriteLock.readLock().unlock();  
  11.         }  
  12.     }  


실제 Project 에서 테스트 중인데 현재까지는 만족스러운 결과를 나타내었다.






출처 : http://www.brucalipto.org/java/how-to-create-a-portable-jdk-1-dot-7-on-windows/

JDK6 버전 Portable 다운로드 : http://sourceforge.net/projects/portableapps/files/Java%20Portable/


- JDK 1.7 Portable

As a Java developer sometimes I need a version of Java Development Kit (JDK) that is not the one installed on my PC. Oracle Java installer on Windows does too many things that I cannot control (like changing the JVM used by browsers to run applets). As of this writing Java 7 is at version u45 and you can download it from here. Open the downloaded file with 7-zip (in my case was jdk-7u45-windows-i586.exe) and then open the tools.zip you find inside. Extract everything to a convenient path like C:\jdk-1.7u45. Now it is shell time so open a DOS console (Start->Run…->cmd) and type:

Create a portable JDK 1.7
1
2
> cd C:\jdk-1.7u45
> for /r %x in (*.pack) do C:\jdk-1.7u45\bin\unpack200 "%x" "%x.jar"

Now you are almost done but you need to rename some files:

Look for all files that ends with .pack.jar
1
2
3
4
5
6
7
8
9
10
> dir /B /S *.pack.jar
C:\jdk-1.7u45\jre\lib\charsets.pack.jar
C:\jdk-1.7u45\jre\lib\deploy.pack.jar
C:\jdk-1.7u45\jre\lib\javaws.pack.jar
C:\jdk-1.7u45\jre\lib\jfxrt.pack.jar
C:\jdk-1.7u45\jre\lib\jsse.pack.jar
C:\jdk-1.7u45\jre\lib\plugin.pack.jar
C:\jdk-1.7u45\jre\lib\rt.pack.jar
C:\jdk-1.7u45\jre\lib\ext\localedata.pack.jar
C:\jdk-1.7u45\lib\tools.pack.jar

Rename all these files removing the .pack part (eg.: rename charsets.pack.jar to charsets.jar):

Rename all files that ends with .pack.jar
1
2
3
4
5
6
7
8
9
> ren C:\jdk-1.7u45\jre\lib\charsets.pack.jar charsets.jar
> ren C:\jdk-1.7u45\jre\lib\deploy.pack.jar deploy.jar
> ren C:\jdk-1.7u45\jre\lib\javaws.pack.jar javaws.jar
> ren C:\jdk-1.7u45\jre\lib\jfxrt.pack.jar jfxrt.jar
> ren C:\jdk-1.7u45\jre\lib\jsse.pack.jar jsse.jar
> ren C:\jdk-1.7u45\jre\lib\plugin.pack.jar plugin.jar
> ren C:\jdk-1.7u45\jre\lib\rt.pack.jar rt.jar
> ren C:\jdk-1.7u45\jre\lib\ext\localedata.pack.jar localedata.jar
> ren C:\jdk-1.7u45\lib\tools.pack.jar tools.jar

Finally test you new portable JDK 1.7:

Test portable JDK 1.7
1
2
3
4
> .\bin\java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) Client VM (build 24.45-b08, mixed mode)

I hope this tutorial can help you… if you find errors please report them to me and I will correct as soon as possible.


=========================================================================================================
=========================================================================================================

- JDK 1.8


As a Java developer sometimes I need a version of Java Development Kit (JDK) that is not the one installed on my PC. Oracle Java installer on Windows does too many things that I cannot control (like changing the JVM used by browsers to run applets). As of this writing Java 8 is not yet available for general consumption but you can get an early access release here. Open the downloaded file with 7-zip and then open the tools.zip you find inside. Extract everything to a convenient path like C:\jdk-1.8-ea. Now it is shell time so open a DOS console (Start->Run…->cmd) and type:

Create a portable JDK 1.8
1
2
> cd C:\jdk-1.8-ea
> for /r %x in (*.pack) do C:\jdk-1.8-ea\bin\unpack200 "%x" "%x.jar"

Now you are almost done but you need to rename some files:

Look for all files that ends with .pack.jar
1
2
3
4
5
6
7
8
9
10
> dir /B /S *.pack.jar
C:\jdk-1.8-ea\jre\lib\charsets.pack.jar
C:\jdk-1.8-ea\jre\lib\deploy.pack.jar
C:\jdk-1.8-ea\jre\lib\javaws.pack.jar
C:\jdk-1.8-ea\jre\lib\jsse.pack.jar
C:\jdk-1.8-ea\jre\lib\plugin.pack.jar
C:\jdk-1.8-ea\jre\lib\rt.pack.jar
C:\jdk-1.8-ea\jre\lib\ext\jfxrt.pack.jar
C:\jdk-1.8-ea\jre\lib\ext\localedata.pack.jar
C:\jdk-1.8-ea\lib\tools.pack.jar

Rename all these files removing the .pack part (eg.: rename charsets.pack.jar to charsets.jar):

Rename all files that ends with .pack.jar
1
2
3
4
5
6
7
8
9
> ren C:\jdk-1.8-ea\jre\lib\charsets.pack.jar charsets.jar
> ren C:\jdk-1.8-ea\jre\lib\deploy.pack.jar deploy.jar
> ren C:\jdk-1.8-ea\jre\lib\javaws.pack.jar javaws.jar
> ren C:\jdk-1.8-ea\jre\lib\jsse.pack.jar jsse.jar
> ren C:\jdk-1.8-ea\jre\lib\plugin.pack.jar plugin.jar
> ren C:\jdk-1.8-ea\jre\lib\rt.pack.jar rt.jar
> ren C:\jdk-1.8-ea\jre\lib\ext\jfxrt.pack.jar jfxrt.jar
> ren C:\jdk-1.8-ea\jre\lib\ext\localedata.pack.jar localedata.jar
> ren C:\jdk-1.8-ea\lib\tools.pack.jar tools.jar

Finally test you new portable JDK 1.8:

Test portable JDK 1.8
1
2
3
4
C:\jdk-1.8-ea>.\bin\java -version
java version "1.8.0-ea"
Java(TM) SE Runtime Environment (build 1.8.0-ea-b121)
Java HotSpot(TM) Client VM (build 25.0-b63, mixed mode)

I hope this tutorial can help you… if you find errors please report them to me and I will correct as soon as possible.


참고 : http://json-lib.sourceforge.net/
         http://json-lib.sourceforge.net/snippets.html

출처 : http://redtrain.tistory.com/819


* json-lib library 추가 (Maven)

<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>xom</groupId>
<artifactId>xom</artifactId>
<version>1.2.5</version>
</dependency> 


Introduction


JSON-lib (http://json-lib.sourceforge.net/)는 자바에서 beans, maps, collections, array 그리고 XML을 JSON 으로 변환하기 위해 사용하는 자바 라이브러리이다. 




Download


다음의 경로에서 다운로드 한다.



Dependencies


주의해야할 점은 common-lang 최신 버전(3.3.1)을 사용할 경우 org.apache.commons.lang.exception.NestableRuntimeException 이 발생하게 된다. 위의 경로에서 패키지를 다운 받아 등록하거나 pom.xml에 아래의 Dependency를 추가한다.
  1. <dependency>  
  2.     <groupId>commons-lang</groupId>  
  3.     <artifactId>commons-lang</artifactId>  
  4.     <version>2.6</version>  
  5. </dependency>  
  6. <dependency>  
  7.     <groupId>commons-beanutils</groupId>  
  8.     <artifactId>commons-beanutils</artifactId>  
  9.     <version>1.8.3</version>  
  10. </dependency>  
  11. <dependency>  
  12.     <groupId>commons-collections</groupId>  
  13.     <artifactId>commons-collections</artifactId>  
  14.     <version>3.2.1</version>  
  15. </dependency>  
  16. <dependency>  
  17.     <groupId>commons-logging</groupId>  
  18.     <artifactId>commons-logging</artifactId>  
  19.     <version>1.1.1</version>  
  20. </dependency>  
  21. <dependency>  
  22.     <groupId>net.sf.ezmorph</groupId>  
  23.     <artifactId>ezmorph</artifactId>  
  24.     <version>1.0.6</version>  
  25. </dependency>  



How to use json-lib


Array and Collection to JSON : 

  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. import junit.framework.TestCase;  
  4. import net.sf.json.JSONArray;  
  5.   
  6. public class JSONLibTest extends TestCase {  
  7.   
  8.     public void testArrayCollection() {  
  9.         boolean[] boolArray = new boolean[]{true,false};    
  10.         JSONArray jsonArray = JSONArray.fromObject( boolArray );    
  11.         System.out.println( "boolArray : " + jsonArray );  
  12.           
  13.         List<String> list = new ArrayList<String>();    
  14.         list.add("1");  
  15.         list.add("second");  
  16.         list.add("셋");  
  17.           
  18.         JSONArray jsonArray1 = JSONArray.fromObject( list );    
  19.         System.out.println( "jsonArray1:" + jsonArray1 );  
  20.           
  21.         JSONArray jsonArray2 = JSONArray.fromObject( "['하나','2','third']" );    
  22.         System.out.println( "jsonArray2:" + jsonArray2 );  
  23.     }  
  24. }  
  25.   
  26. //boolArray : [true,false]  
  27. //jsonArray1:["1","second","셋"]  
  28. //jsonArray2:["하나","2","third"]  


Bean and Map to JSON : 

  1. //Sample Bean  
  2. import net.sf.json.JSONFunction;  
  3.   
  4. public class TestBean {  
  5.     private String name = "json";  
  6.     private int num = 1;  
  7.     private char[] options = new char[]{ 'k''o''r' };  
  8.     private String func1 = "function(i) { return this.options[i]; }";  
  9.     private JSONFunction func2 = new JSONFunction(new String[]{"i"},"return this.options[i];");  
  10.       
  11.     public String getName() {  
  12.         return name;  
  13.     }  
  14.     public void setName(String name) {  
  15.         this.name = name;  
  16.     }  
  17.     public int getNum() {  
  18.         return num;  
  19.     }  
  20.     public void setNum(int num) {  
  21.         this.num = num;  
  22.     }  
  23.     public char[] getOptions() {  
  24.         return options;  
  25.     }  
  26.     public void setOptions(char[] options) {  
  27.         this.options = options;  
  28.     }  
  29.     public String getFunc1() {  
  30.         return func1;  
  31.     }  
  32.     public void setFunc1(String func1) {  
  33.         this.func1 = func1;  
  34.     }  
  35.     public JSONFunction getFunc2() {  
  36.         return func2;  
  37.     }  
  38.     public void setFunc2(JSONFunction func2) {  
  39.         this.func2 = func2;  
  40.     }  
  41. }  
  1. import java.util.HashMap;  
  2. import java.util.Map;  
  3.   
  4. import junit.framework.TestCase;  
  5. import net.sf.json.JSONObject;  
  6.   
  7. public class JSONLibTest extends TestCase {  
  8.     public void testBeanMaps() {  
  9.         Map<String, Object> map = new HashMap<String, Object>();    
  10.         map.put( "string""jin" );    
  11.         map.put( "bool", Boolean.TRUE );    
  12.         map.put( "int"new Integer(1) );    
  13.         map.put( "array"new String[]{"a","b"} );    
  14.         map.put( "function""function(i){ return this.arr[i]; }" );    
  15.           
  16.         JSONObject jsonObjectMap = JSONObject.fromObject( map );    
  17.         System.out.println( "jsonObjectMap:" +  jsonObjectMap );  
  18.           
  19.         JSONObject jsonObjectBean = JSONObject.fromObject( new TestBean() );    
  20.         System.out.println( "jsonObjectBean:" +  jsonObjectBean );  
  21.     }  
  22. }  
  23.   
  24. //jsonObjectMap:{"int":1,"string":"jin","bool":true,"function":function(i){ return this.arr[i]; },"array":["a","b"]}  
  25.   
  26. //jsonObjectBean:{"func1":function(i) { return this.options[i]; },"func2":function(i){ return this.options[i]; },"name":"json","num":1,"options":["k","o","r"]}  


JSON to Beans : 

  1. import java.lang.reflect.InvocationTargetException;  
  2. import java.util.List;  
  3.   
  4. import junit.framework.TestCase;  
  5. import net.sf.json.JSONArray;  
  6. import net.sf.json.JSONObject;  
  7.   
  8. import org.apache.commons.beanutils.PropertyUtils;  
  9.   
  10. public class JSONLibTest extends TestCase {  
  11.     @SuppressWarnings("unchecked")  
  12.     public void testJsonToBeans() throws   
  13.             IllegalAccessException, InvocationTargetException, NoSuchMethodException {  
  14.   
  15.         String strJson = "{\"func1\":function(i) { return this.options[i]; },\"func2\":function(i){ return this.options[i]; },\"name\":\"json\",\"num\":1,\"options\":[\"k\",\"o\",\"r\"]}";  
  16.           
  17.         JSONObject jsonObjectJsonToBeans = JSONObject.fromObject( strJson );  
  18.         Object beans = JSONObject.toBean( jsonObjectJsonToBeans );   
  19.           
  20.         assertEquals( jsonObjectJsonToBeans.get( "name" ),   
  21.                         PropertyUtils.getProperty( beans, "name" ) );   
  22.         assertEquals( jsonObjectJsonToBeans.get( "num" ),   
  23.                         PropertyUtils.getProperty( beans, "num" ) );   
  24.         assertEquals( jsonObjectJsonToBeans.get( "func1" ),   
  25.                         PropertyUtils.getProperty( beans, "func1" ) );   
  26.         assertEquals( jsonObjectJsonToBeans.get( "func2" ),   
  27.                         PropertyUtils.getProperty( beans, "func2" ) );   
  28.   
  29.         @SuppressWarnings("deprecation")  
  30.         List<String> expected = JSONArray.toList(jsonObjectJsonToBeans.getJSONArray("options"));  
  31.         assertEquals( expected, (List<String>) PropertyUtils.getProperty(beans, "options") );  
  32.     }     
  33. }  


Json to XML AND XML to JSON : 

XMLSerializer.write()와 XMLSerializer.read() 를 사용하여 XML을 JSON으로 JSON을 XML로 변환할 수 있다.

  1. import junit.framework.TestCase;  
  2. import net.sf.json.JSONArray;  
  3. import net.sf.json.JSONObject;  
  4. import net.sf.json.xml.XMLSerializer;  
  5.   
  6. public class JSONLibTest extends TestCase {  
  7.     public void testXML() {  
  8.         XMLSerializer xmlSerializer = new XMLSerializer();  
  9.   
  10.         String strXml1 = "<?xml version=\"1.0\" encoding=\"utf-8\"?><book>" +   
  11.                         "<title>XML To JSON</title><author>json-lib</author></book>";  
  12.   
  13.         JSONObject jsonObject1 = (JSONObject) xmlSerializer.read(strXml1);    
  14.         System.out.println( jsonObject1 );  
  15.           
  16.         JSONObject jsonObject2 = new JSONObject( true );  
  17.         String strXml2 = xmlSerializer.write( jsonObject2 );  
  18.         System.out.println( strXml2 );  
  19.   
  20.         JSONArray jsonObject3 = JSONArray.fromObject("[1,2,3]");    
  21.         String strXml3 = xmlSerializer.write( jsonObject3 );  
  22.         System.out.println( strXml3 );  
  23.           
  24.         System.out.println( "jsonObject1(json to xml):"+ xmlSerializer.read(strXml1) );  
  25.   
  26.         System.out.println( "jsonObject2(json to xml):"+ xmlSerializer.read(strXml2) );  
  27.           
  28.         System.out.println( "jsonObject3(json to xml):"+ xmlSerializer.read(strXml3) );  
  29.     }     
  30. }  
  31.   
  32. //jsonObject1(xml to json):{"title":"XML To JSON","author":"json-lib"}  
  33. //jsonObject2(xml to json):<?xml version="1.0" encoding="UTF-8"?>  
  34. //<o null="true"/>  
  35.   
  36. //jsonObject3(xml to json):<?xml version="1.0" encoding="UTF-8"?>  
  37. //<a><e type="number">1</e><e type="number">2</e><e type="number">3</e></a>  
  38.   
  39. //jsonObject1(json to xml):{"title":"XML To JSON","author":"json-lib"}  
  40. //jsonObject2(json to xml):null  
  41. //jsonObject3(json to xml):["1","2","3"]  


그런데 위의 소스를 돌려보면 java.lang.NoClassDefFoundError: nu/xom/Element 를 던진다.

json-lib 사이트에서 살펴보면 이와 관련된 언급이 없는 것 같은데...


라이브러리를 하나 추가해야 한다.

XOM(XML Object Model) 라이브러리를 추가해야한다. 여기서 다운로드 할 수 있다.




위의 코드를 돌려보면서 발생할 수 있는 Exception은 다음과 같다.

  • java.lang.NoClassDefFoundError
    • 위의 의존성 패키지에서 누락된게 있는지 확인해본다.
  • org.apache.commons.lang.exception.NestableRuntimeException
    • 최신버전 (commons-lang3-3.1.jar) 사용시에 오류 발생
  • java.lang.NoClassDefFoundError: nu/xom/Element
    • XML을 사용할 때 라이브러리를 추가한다.


+ Recent posts