출처: http://stackoverflow.com/questions/10047802/public-static-final-or-private-static-final-with-getter


In Java, it's taught that variables should be kept private to enable better encapsulation, but what about static constants? This:

public static final int FOO = 5;

Would be equivalent in result to this:

private static final int FOO = 5;
...
public static getFoo() { return FOO; }

But which is better practice?


There's one reason to not use a constant directly in your code.

Assume FOO may change later on (but still stay constant), say to public static final int FOO = 10;. Shouldn't break anything as long as nobody's stupid enough to hardcode the value directly right?

No. The Java compiler will inline constants such as Foo above into the calling code, i.e.someFunc(FooClass.FOO); becomes someFunc(5);. Now if you recompile your library but not the calling code you can end up in surprising situations. That's avoided if you use a function - the JIT will still optimize it just fine, so no real performance hit there.


요약: 만약 어떤 코드가 caller와 callee로 되어 있을 때 callee의 상수가 public static final로 되어있을 경우, 그냥 그대로 사용할 경우 문제가 발생할 수 있다.

예를 들어 public static final int Foo = 5라고 정의가 되어있을 때 만약 someFunc(FooClass.Foo)라고 caller에서 사용한다면, JIT에서는 SomeFunc(5)로 치환해서 컴파일을 하게 된다. 그렇게 때문에 나중에 callee의 코드를 public static final int Foo = 10으로 고치게 되버리면 caller를 재컴파일하지 않으면 문제가 발생하게 된다. 이런 상황을 방지하려면, getter를 사용해야 한다.

non-static이나 non-final인 경우 이런 문제가 발생하지 않는다


아래는 추가 자료

출처: http://stackoverflow.com/questions/5173372/java-static-final-values-replaced-in-code-when-compiling


==fileA.java==
class A
{  
   
public static final int SIZE = 100;
}  

Then in another file i use this value

==fileB.java==  
import A;
class b
{
     
Object[] temp = new Object[A.SIZE];
}

When this gets compiled does SIZE get replaced with the value 100, so that if i were to down the road replace the FileA.jar but not FileB.jar would the object array get the new value or would it have been hardcoded to 100 because thats the value when it was originally built?


Another route to proving that the behavior is to looking at the generated bytecode. When the constant is "small" (presumably < 128):

public B();
 
Code:
   
0:   aload_0
   
1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   
4:   aload_0
   
5:   bipush  42
   
7:   anewarray       #3; //class java/lang/Object
   
10:  putfield        #12; //Field temp:[Ljava/lang/Object;
   
13:  return

}

(I used 42 instead of 100 so it stands out more). In this case, it is clearly substituted in the byte code. But, say the constant is "bigger." Then you get byte code that looks like this:

public B();
 
Code:
   
0:   aload_0
   
1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   
4:   aload_0
   
5:   ldc     #12; //int 86753098
   
7:   anewarray       #3; //class java/lang/Object
   
10:  putfield        #13; //Field temp:[Ljava/lang/Object;
   
13:  return

When it is bigger, the opcode "ldc" is used, which according to the JVM documentation "an unsigned byte that must be a valid index into the runtime constant pool of the current class".

In either case, the constant is embedded into B. I imagine, since that in opcodes you can only access the current classes runtime constant pool, that this the decision to write the constant into the class file is independent of implementation (but I don't know that for a fact).

  

출처: http://stackoverflow.com/questions/8302894/what-is-the-difference-between-runnable-jar-library-handling-options





  • Extract required libraries into JAR
  • Package required libraries into JAR
  • Copy required libraries into sub folder next to JAR

  1. Extract required libraries into JAR - Extracts the actual .class files from the libraries your app uses and puts those .class files inside the runnable JAR. So, the runnable JAR will not only contain the .class files of your application, but also the .class files of all the libraries your application uses.

  2. Package required libraries into JAR - Puts the actual JAR files of the libraries into your runnable JAR. Normally, a JAR file within a JAR file cannot be loaded by the JVM. But Eclipse adds special classes to the runnable JAR to make this possible.

  3. Copy required libraries into sub folder next to JAR - Keeps the library JARs completely separate from the runnable JAR, so the runnable JAR will only contain the .class files of your application.

Option #2 is convenient because it packages everything neatly into a single JAR, and keeps the library JARs separated from your application's .class files.

However, a downside to packaging everything inside of a single JAR (options #1 and #2) is that, if you update your application, then the user will have to download more data to update the application. If the JARs are kept separate, then the user would only have to download the JAR that contains your application code, instead of a single, massive JAR that contains your application code and all the library code.

  

PriorityQueue를 적재적소에 사용하자

Posted by epicdev Archive : 2012. 3. 27. 21:07

데이터가 정렬된 상태로 컨테이너에 들어있어야 할 경우에는 PriorityQueue를 자주 사용한다.

PriorityQueue는 Comparable 인터페이스를 구현하는 클래스(boolean compareTo를 구현하여 priority를 매김)를 데이터로 갖거나

생성자에서 따로 Comparator를 받아서 어떻게 priority를 매길지 정할 수 있다.

나같은 경우에는 평소에 데이터들의 순차적 access가 필요 할 때 PrioirtyQueue를 자주 사용한다.

하지만 얼마전에 PriorityQueue를 남용한 잘못을 저질렀다.


PriorityQueue가 사용되기에 적절한 곳은 (내 생각)

1. 컨테이너 안의 데이터가 priority에 따라 사용 순서가 결정

2. 컨테이너의 내용이 자주 바뀜 (poll과 add가 교차적으로 빈번하게 발생함)

3. 데이터를 재사용 할 일이 별로 없음


그런데 나같은 경우는 1번의 경우만 생각하고 2, 3번의 경우는 생각하지 않아서 문제가 발생했다.

어떤 raw 데이터를 통째로 읽어와서 PriorityQueue에 저장을 해놓고, PriorityQueue에서 데이터를 순차적으로 뽑아서, 데이터들 마다 "어떤 처리"를 한 다음 다시 파일로 쓰는 작업이었다.

여기서 나는 2번째 내용을 위반하였다.

나는 그냥 raw 데이터를 읽어오는 족족 PriorityQueue에다가 넣었는데, 이는 "오로지" 나중에 순차적으로 데이터를 뽑아 쓰려고 이렇게 하였다 (1번 이유).

그런데 이 경우 2번의 경우처럼 poll과 add가 교차적으로 빈번하게 발생하지 않는다.

즉, add가 한꺼번에 연속적으로 전부 발생하고나서, poll을 계속 하면서 데이터들을 처리한다.

따라서 이 경우에는 그냥 ArrayList에다가 데이터를 전부 add한 다음 그냥 Collections.sort로 ArrayList를 정렬해서 사용하면 그만이다.

(add와 poll의 교차수행이 빈번할 경우에 ArrayList보다 PriorityQueue가 좋은 점: ArrayList를 사용할 경우 데이터를 add하게되면 ArrayList의 "정렬됨"이라는 상태가 깨지기 때문에, valid한 ordered ArrayList를 유지하려면, add를 할때마다 매번 정렬을 해주어야 한다 (혹은, poll을 요청하기 전까지 add만 하다가 poll 요청이 들어오면 정렬을 해도 된다. 하지만 교차수행이 빈번한 경우에는 매번 정렬을 해야 할 수도 있다). 물론 ordered ArrayList는 정렬된 상태이므로, ArrayList를 traverse한 다음 적절한 위치에다가 add를 해주는 것도 가능하나, 이 또한 ArrayList의 특성상 비효율적일 수가 있다.)


사실 위의 경우에서는 PriorityQueue를 사용하거나 ArrayList와 Collections.sort를 사용하거나 별다른 차이가 없다.

왜냐하면 "재사용"이 없기 때문이다. 하지만 나는 재사용이 필요하였다 (3번 조건 위반).

만약 PriorityQueue의 데이터를 하나씩 뽑아와서 while (!pq.isEmpty()) 처리한다음 파일로 써버리면

PriorityQueue에 남아있는 내용이 없기 때문에, 만약 raw 데이터의 사용이 다시 필요하다면, 파일에서 또 읽어들여야 했다.

(PriorityQueue는 정렬된 list 상태가 아니라 heap 상태라서, iterator로 PriorityQueue를 traverse하게 되면 priority순으로 데이터가 traverse되지 않는다.

priority순으로 PriorityQueue의 데이터를 access하는 방법은 오로지 poll밖에 없다!)

문제는 이 파일이 용량이 꽤나 커서 읽어들이는데 10초정도의 시간이 소요된다는 것이었다.


따라서 내가 겪은 케이스에는 PriorityQueue를 사용하는것 보다 그냥 ArrayList와 Collections.sort를 사용하는 것이 낫다.


결론

1. 도구는 적재적소에 사용해야 한다.

2. 떄론 없어보이는 도구일지라도, 있어보이는 도구보다 나을때도 있다 (특히나 있어보이는 도구가 오로지 특정 상황에서만 최고의 성능을 발휘할 때).

  

Java에서 File IO할 때의 try-catch-finally 스타일

Posted by epicdev Archive : 2012. 3. 27. 19:54

Java에서 File IO를 할 때에 필연적으로 사용해야하는 것이 try와 catch와 finally이다.

그런데 이런 try, catch, finally 들로 코드를 도배하다보면 정말 UGLY한 코드가 나오기가 쉽다.

아래의 코드가 일반적으로 가장 널리 사용되는 스타일이다.


이 코드는 소위 말하면 정말 UGLY하다고 할 수가 있다.

가독성도 떨어지고 뭔가 불필요하게 try와 catch가 들어있는것 처럼 보인다. (실상은 그렇지 않다. 다 필요하다.)

이처럼 불필요하게 "보이는" try와 catch를 없애려고 아래의 코드처럼 할 수도 있다.


이렇게 하고나면 맨 처음 코드에서 catch가 반복되는 것을 해결 할 수 있어 보인다.

물론 이렇게 하면 해결은 되지만, Java에서의 Exception을 처리할 때의 원칙(Exception들을 catch문 하나에서 한꺼번에 처리하지 않는다)에 위배된다.

즉, out.close에서 발생하는 Exception과 new FileOutputStream에서 발생하는 Exception 모두 하나의 catch문에서 처리가 되어버린다.

이를 해결하기 위해서 또한 아래처럼 코드를 짤 수도 있다.


이렇게 코드를 짜게되면 함수가 IOException을 throw하게 된다. 또한, finally 블록에서 out의 null 체크도 없어졌다.

(new FileOutPutStream에서 Exception이 발생하면 곧바로 IOException을 throw하면서 그 다음 line을 실행하지 않으므로 finally 블록에서의 out은 무조건 null이 아니다)

하지만 나같은 경우는 Exception처리를 외부로 유보하는 것을 좋아하지 않으므로 (이런 사람들이 많을것이라 본다), 개인적으로는 비추천이다.


그래서 이제 최종적으로 내가 "알고 있는 한" 가장 BEAUTIFUL한 코드를 살펴보도록 하겠다.


이 코드를 보면 closeQuietly라는 함수를 finally 블록에서 호출하고 있다.

closeQuietly라는 함수는 Closeable의 varargs 타입을 파라미터로 받아서, 받은 closeable들을 모두 닫아버린다.

이렇게 stream들을 닫는 함수를 따로 만듦으로써 코드가 훨씬 깔끔해졌다.


물론 위의 4가지 스타일 모두 사용가능한 스타일이다.

필요한 상황마다 적재적소에 사용 할 수 있다. 네 번째 스타일의 경우에는 때로는 "닭 잡는데 소 잡는 칼을 쓰는 격"이 될 수도 있다.

이 내용에 대한 프로그래머들의 의견은 http://stackoverflow.com/questions/2699209/java-io-ugly-try-finally-block에서 확인 할 수 있다.

위의 링크를 참조하자면, 4번째 스타일이 가장 낫다는 것이 보편적인 생각인 것 같다.



  

Java에 Pair가 없는 이유 (추측)

Posted by epicdev Archive : 2012. 3. 27. 04:00

출처:  http://stackoverflow.com/questions/156275/what-is-the-equivalent-of-the-c-pairl-r-in-java 

요약: Pair라는 것은 어떠한 자료의 추상적인 형태만을 나타내는 것이지, 실제데이터의 "의미"를 전달하는 명칭이 아니므로

(예를 들면 Pair의 first나 second가 의미하는것은 실제 데이터와 관계가 없다라는 것. 코딩할 때 변수명을 a, b 같은 것들로 사용하지 말라는 것과 같은 맥락)

Pair를 사용하는 것보다, 실제 데이터의 "의미"를 전달 할 수 있는 Entry나 Position이나 Range 등을 사용하는 것이 올바르기 때문이다.


In this thread on comp.lang.java.help, Hunter Gratzner gives some arguments against the presence of a Pair construct in Java. The main argument is that a class Pair doesn't convey any semantics about the relationship between the two values (how do you know what "first" and "second" mean ?).

A better practice is to write a very simple class, like the one Mike proposed, for each application you would have made of the Pair class. Map.Entry is an example of a pair that carry its meaning in its name.

To sum up, in my opinion it is better to have a class Position(x,y), a class Range(begin,end)and a class Entry(key,value) rather than a generic Pair(first,second) that doesn't tell me anything about what it's supposed to do.

  
Java에서 파일에다가 출력을 할 때 PrintWriter와 BufferedWriter를 이용할 수 있다.
(Writer를 상속하는 클래스는 BufferedWriter, CharArrayWriter, FilterWriter, OutputStreamWriter, PipedWriter, PrintWriter, StringWriter가 있지만 대표적인 것만 다루기로 하겠다.)

우선 PrintWriter의 경우는  print, println, printf 처럼 다양한 출력함수를 제공함으로써 파일출력을 편하게 해준다.
BufferedWriter의 경우는 버퍼를 사용하여 통해 좀 더 효율적인 파일쓰기를 지원한다.

이 두가지 방법을 마치 둘 중에 하나를 선택해야한다는 흑백논리로 생각하는 사람들이 있는데 이는 큰 오산이다.
Java의 파일 IO 클래스들은  데코레이터 패턴을 사용한다.
즉, 원시 클래스에다가 원하는 기능을 가지는 클래스를 가지고 입맛대로 꾸며서 사용할 수 있다는 것이다.

위의 코드처럼 PrintWriter를 만들어서 사용하면 PrintWriter의 장점과 BufferedWriter의 장점을 모두 이용할 수 있다.
  

Java heap의 세가지 영역

Posted by epicdev Archive : 2012. 2. 11. 05:02

Understanding Java Memory Structure

One strength of the java platform is that it shields the developer from the complexity of memory allocation and garbage collection.  We all know that java stores objects in a Heap memory.  Internally Java partitions the heap memory space logically into three areas called generations.

  1. The Young Generation
  2. The Tenured Generation
  3. The Permanent Generation

Out if these three partitions, only the young and tenured generation spaces are used for allocating memory to objects. The permanent generation space is used for holding the data needed by the virtual machine to describe objects that do not have equivalence at the Java language level.

This partitioning is done in order to improve the performance of garbage collection as performing garbage collection on the entire heap will be very expensive. Instead the architects of java decided to partition the heap space into three parts.

The Young Generation

The young generation is where space is allocated for newly created objects.  In most applications, most of the objects created are used and referenced only for a very short span of time (high infant mortality rate), for example an iterator instance is discarded as soon as the loop is complete.

The purpose of having a separate space of young generation is to maximize promptness (the time between when an object becomes dead and when the memory becomes available) and to reduce the overhead of scanning the entire heap during every garbage collection process.

Garbage collection in the young generation happens when the space in this generation fills up and is called the minor collection. Minor collections can be optimized assuming a high infant mortality rate.  It is well-tuned in the sense that the young generation is large enough (and thus the period between minor collections long enough) that the minor collection can take advantage of the high infant mortality rate. The time required for garbage collection is directly proportional to the number of live objects.  A young generation full of dead objects is collected very quickly. Some surviving objects are moved to a tenured generation.

The Tenured Generation

Objects which survive the young generation are eventually moved into the tenured generation. Garbage collection in the tenured generation happens when the space in the tenured generation fills up and is called the major collection. Since the tenured generation usually have a large number of alive objects, the time required for garbage collecting the tenured generation is much higher than that for the young generation. Hence, garbage collection of the tenured generation is done at a much lesser rate than the young generation.

The Permanent Generation

The permanent generation is not used for allocating objects. Instead it is used for holding the data needed by the virtual machine to describe objects that do not have equivalence at the Java language level. For example objects describing classes and methods are stored in the permanent generation.


아래의 pdf파일은  Fillip Hanik이라는 개발자의 Inside the Java Virtual Machine: Memory Management and Troubleshooting이라는 제목의 슬라이드로 위의 내용을 이해하는데 도움이 된다.

출처:  http://www.springsource.com/files/uploads/all/pdf_files/news_event/Inside_the_JVM.pdf


  

Java에서 중첩 루프 한번에 탈출 하는법

Posted by epicdev Archive : 2012. 1. 26. 22:22
중첩 루프를 돌면서 어떠한 조건이 만족하면 탈출하는식의 코드를 자주 코딩을 하게 된다.
이런 유형은 대개 아래와 같다.

일반적으로 이런 경우에는 아래와 같은 방법으로 코딩을 하게 된다.
혹은 중첩 루프를 함수로 만들어서 if 절 안의 break를 return으로 바꿔서 한번에 루프를 탈출하는 방법을 쓰기도 한다.
C++의 경우 goto를 사용하면 더 간단하게 이 문제를 해결할 수 있다.
물론 goto는 무조건 사용하지말라고 배웠다면 이러한 방법이 꺼려지겠지만,
거의 유일하게 goto를 써도 욕을 먹지 않는 경우가 바로 아래의 경우이다.
flag에 대한 설명을 주저리 주저리 할 필요도 없고, 코드의 가독성 또한 훨씬 높아진다.
하지만 Java에서는 goto문이 없다. 즉, C++에서처럼 goto를 사용해서 중첩 루프를 탈출 할 수 없다는 것이다.
하지만 Java에서는 이와 비슷한 다른 문법적 장치가 있다.
Java에서는 위와 같이 코딩을 하면 중첩 루프를 한번에 탈출할 수 있다.
왜 이런지 이해가 되지 않는다면 아래처럼 named block을 사용했다고 생각하면 된다.
혹은
  

Encapsulate Downcast (다운캐스트의 캡슐화)

Posted by epicdev Archive : 2012. 1. 25. 00:26
문제: 메소드의 리턴 값을 호출하는 쪽에서 다운캐스트하지 않으면 안 된다.

해법: 다운캐스트를 메소드 내부에서 하자.


  

Encapsulate Collection (컬렉션의 캡슐화)

Posted by epicdev Archive : 2012. 1. 25. 00:22
문제: 변경가능한 컬렉션을 반환하는 getter 메소드가 있기 때문에 외부에서 컬렉션을 직접 조작한다.


리팩토링입문
카테고리 컴퓨터/IT > 프로그래밍/언어
지은이 히로시 유키 (한빛미디어, 2007년)
상세보기


  
 «이전 1 2 3 4 5  다음»