JAVA – 자주하는 실수 10가지

원문

이 글은 자바 개발자들이 자주하는 실수 10가지에 대한 글입니다.

1. Array 객체를 ArrayList로 변환하기

아래와 같이 자주들 이렇게 사용하고는 합니다:

List<String> list = Arrays.asList(arr);

하지만! Arrays.asList()메서드는 Arrays클래스 내부에 있는 private static class 객체를 리턴합니다. java.util.ArrayList class가 아니라는 말이죠.
java.util.Arrays.ArrayList 클래스는 set(), get(), contains() 메서드를 가지며 대신 그외 element를 추가하기 위한 아무 메서드도 지원하지 않습니다. 그래서 리스트의 사이즈는 항상 일정합니다.

그럼 어떻게 사용해야 할까요?

ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));

ArrayList 생성자는 Collection type의 객체를 받을 수 있고, 이는 java.util.Arrays.ArrayList가 됩니다.

2. 만약 Array 객체에서 특정 값을 찾는다면?

개발자들은 아래와 같이 자주 사용하고는 합니다.

Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);

위 코드는 정상적인 작동을 합니다. 하지만 구지 List를 Set으로 변환 할 필요는 없습니다. 이는 추가적인 시간 비용이 들기 때문에 아래와 같이 간단하게 해결 할 수 있습니다.

Arrays.asList(arr).contains(targetValue);

또는

for(String s: arr){
    if(s.equals(targetValue))
        return true;
}
return false;

첫 번째 방법이 두번째 방법보다 읽기가 편하겠죠?

3. 루프 상태에서 List의 특정 객체 지우기

아래의 코드를 돌리다면 어떠한 일들이 벌어질까요? 한번 생각해봅시다.

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
for (int i = 0; i < list.size(); i++) {
    list.remove(i);
}
System.out.println(list);

결과는 처참합니다.

[b, d]

위 코드는 심각한 문제를 가지고 있습니다. 특정 객체를 지우게 되면, 리스트의 사이즈는 줄어들게 되고, 삭제할 타겟의 인덱스 또한 변하게 됩니다. 그래서 만약 루프 안에서 인덱스를 사용하여 여러개의 Element를 지우게 한다면 정상적으로 작동하지 않게 됩니다.

아마 알지도 모르겠지만 대안은 iterator를 사용하면 됩니다. 그리고 foreach문이 있겠죠. 자바에서 foreach문은 iterator처럼 작동하지만 사실 iterator가 아닙니다.

아래의 코드를 보도록 하죠.

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
 
for (String s : list) {
    if (s.equals("a"))
        list.remove(s);
}

위 코드는 ConcurrentModificationException를 발생시킵니다.

하지만 아래의 코드는 정상적으로 작동합니다.

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
    String s = iter.next();
 
    if (s.equals("a")) {
        iter.remove();
    }
}

.next() 메서드는 .remove() 메서드가 호출되기 전에 호출되어야 합니다. foreach문에서 ConcurrentModificationException 문제의 원인은 Compiler.remove() 호출 후에 .next()가 불리도록 만들기 때문입니다.

자세한 내용은 소스코드를 참조해주세요.

4. Hashtable vs HashMap

알고리즘 관례에서, Hashtable은 데이터 구조의 이름이라고 한다고 하네요. 하지만 자바에서는 데이터 구조 이름은 HashMap이라고 합니다.

여기서 중요한 차이점 하나는 Hashtable은 동시성을 가진다는 것과 HashMap는 그렇지 않다는 것입니다.

그래서 Hashtable보다 HashMap의 사용을 많이 합니다.

HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap
Top 10 questions about Map

5. Collection의 Row Type을 사용해라!?

자바에서, raw type과 unbounded wildcard type은 같이 사용하기 쉽습니다. Set이 raw type이고, Set이 unbounded wildcard type입니다.

파라미터로 raw type의 List객체를 사용하는 코드를 생각해봅시다.

public static void add(List list, Object o){
    list.add(o);
}
public static void main(String[] args){
    List<String> list = new ArrayList<String>();
    add(list, 10);
    String s = list.get(0);
}

이 코드는 아래와 같이 예외를 발생하게 하는데,

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at ...

여기서 **Set,Set,그리고 **Set는 몇가지 차이점이 있습니다.

자세한건 Raw type vs. Unbounded wildcardType Erasure를 참조해주세요.

6. 접근 제어

개발자들은 자주 public 클래스 필드를 사용합니다. 필드에 대한 접근이 다이렉트로 이루어지기 때문에 매우 쉽기는 하지만 클래스 디자인 측면에서는 좋지 않습니다. 접근 제어에 대해서는 적으면 적을 수록 좋은 디자인입니다.

자세한 내용은 아래의 링크를 참조해주세요.
public, default, protected, and private

7. ArrayList vs. LinkedList

ArrayListLinkedList의 차이점을 잘 모르는 개발자들은 주로 ArrayList를 사용하고는 합니다. 그 이유는 매우 친숙하기 때문이죠. 그렇지만, 여기에는 아주 큰 퍼포먼스 차이가 있습니다.

LinkedList의 경우 추가와 삭제가 빈번하게 사용될때 사용되어야 하며, 또한 random access 동작이 많지 않습니다.

좀더 상세한 정보를 얻기 위해서 ArrayList vs. LinkedList을 참조해주세요.

8. Mutable vs. Immutable

Immutable 객체는 단순함, 안전함, 기타 등등의 여러가지의 혜택을 받습니다. 하지만 제한된 값을 떼어낼때, 객체들에 많은 변화가 필요할때 가비지 컬렉션이 자주 일어나게 되는데요, 이럴대는 발란스를 맞춰 선택을 해야합니다. 내가 mutable을 사용할지 아니면 immutable을 사용할지에 대해서 말입니다.

일반적으로, mutable객체는 많은 객체의 생상을 피하기 위해 사용되는데요, 아주 오래된 예로 문자열의 결합을 말할 수 있을 있을거 같습니다. 만약 immutable string을 사용한다면, 아무래도 많은 객체 생성과 그에 합당한 가비지 컬렉션이 일어나겠죠. 이는 시간과 cpu 전력을 낭비하는 일입니다. 이럴때는 간단하면 mutable 객체(e.g. StringBuilder)를 사용하면 됩니다.

String result="";
for(String s: arr){
    result = result + s;
}

mutable객체가 매력적으로 보일때는 이런 상황입니다. 예로, 메서드 호출로 하나의 값이 아닌 여러개의 값을 얻고자 할때, 그리고 정렬이나, 필터링 등이 될수 있습니다. 물론 제대로 사용하지 않으면 많은 비용을 그만큼 지불하게 되겠죠.

자세한 내용은 아래의 링크를 참조해 주세요.
from dasblinkenlight’s answer on stack overflow

Why String is Immutable?

9. Super와 Sub의 생성자

Implicit-super-constructor-is-undefined-for-default-constructor

기본 슈퍼 생성자는 정의되어 있지 않아 에러가 발생한다. 자바에서 만약 생성자를 생성하지 않으면, 컴파일러는 인자를 받지 않는 기본생성자를 기본으로 생성한다.

만약 슈퍼 클래스에 생성자가 정의되어 있다면, 위와 같은 경우 Super(String s), 컴파일러는 파라미터가 없는 생성자를 만들지 않을 것이다.

그 파라미터가 있거나 또는 없는 서브 클래스의 생성자는 파라미터가 없는 슈퍼 생성자를 호출한다.
컴파일러는 서브 클래스에 super() 생성자를 추가할려고 할것이고, 그렇지만 그 슈퍼의 기본 생성자는 정의 되어 있지 않다. 그래서 컴파일러는 에러를 표시할 것이다.

이 문제를 해결하기 위해서는 간단하게 Super()생성자를 생성하는것이다.

public Super(){
    System.out.println("Super");
}

또는 슈퍼 생성자를 지우고 서브 생성자를 생성하는 것이다.

Constructor of Super and Sub

10. “”or 생성자?

문자열은 아래와 같이 두가지 방식으로 생성될 수 있다.

//1. use double quotes
String x = "abc";
//2. use constructor
String y = new String("abc");

어떤 차이점이 있을까?

아래의 코드로 빠르게 확인 할 수 있다.

String a = "abcd";
String b = "abcd";
System.out.println(a == b);  // True
System.out.println(a.equals(b)); // True
 
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d);  // False
System.out.println(c.equals(d)); // True

좀 더 자세한 내용을 알기 위해서는 아래의 링크로 확인 할 수 있다.
Create Java String Using ” ” or Constructor?.

내일을 향해
위 목록들은 GitHub, Stack Overflow의 open source project의 분석으로 통한 질문으로, 구글 인기 검색이다. 탑 10에 대한 정확한 평가, 수치는 없지만 공통적으로 많이 언급이 되었던 부분이다.

이상

AWS – 이미지 업로드하기 위한 플러그인 설치 및 기타 등등

너무 방대하다 보니 자료가 찾기 힘들더라.
그래서 이와 같이 정리를 한다.

AWS Toolkit for Eclipse
내가 설명한 내용은 간단하게 이클립스 플러그인 설치와 간단한 사용법에 대해서 서술한다.

이클립스가 설치되었다면 이제 플로그인을 설치해보자.
1. Help > Install New Software 선택
2. “Work with”필드에 http://aws.amazon.com/eclipse 입력
3. 조금 기다려보면 AWS Toolkit for Eclipse 항목들이 활성화 되는데 필요한 항목을 선택 후 Next 선택

이렇게 하면 플러그인에 대한 설치가 끝이 난다.

AWS 관련 자료 링크
http://www.slideshare.net/awskorea

Java > ClassNotFoundException VS NoClassDefFoundError 뭐가 다를까?

ClassNotFoundException 같은 경우는 class 로더가 필요한 class를 class path에서 찾지 못할 때 발생한다.

일반적으로 class path에 클래스를 추가할 경우 따로 채크를 해주어야 한다.

NoClassDefFoundError 같은 경우는 디버거 또는 이유를 찾기에는 좀더 복잡하다. 그리고 이 에러는 컴파일러로부터 class 런타임 시에 class file을 찾지 못했을 때 발생된다.

아래와 같이 확인 할 수 있다.

Test1.java
[sourcecode language=”java”]
public class Test1 {
}
[/sourcecode]

Test.java
[sourcecode language=”java”]
public class Test {
public static void main(String[] args) {
Test1 = new Test1();
}
}
[/sourcecode]

컴파일 후에 Test1.class파일을 지우고 Test파일을 실행 해보자.

Exception in thread “main” java.lang.NoClassDefFoundError: Test
at Test1.main(Test1.java:5)
Caused by: java.lang.ClassNotFoundException: Test
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
… 1 more

위와 같은 에러를 확인 할 수 있다.

reference

JAVA > How to check the byte code class version.

ref

On Linux, Mac OS X or Windows with Cygwin installed, the file(1) command knows the class version. Extract a class from a jar and use file to identify it:

리눅스나 맥에서는 간단하게 아래와 입력하시면 됩니다. Window의 경우 Cygwin이라는 툴이 별도로 필요하며 사용방법은 아래와 같습니다.

$ jar xf log4j-1.2.15.jar
$ file ./org/apache/log4j/Appender.class
./org/apache/log4j/Appender.class: compiled Java class data, version 45.3
A different class version, for example:

$ file ~/bin/classes/P.class
/home/dave/bin/classes/P.class: compiled Java class data, version 50.0

The class version major number corresponds to the following Java JDK versions:

클래스 버전에서 나온 수치는 아래와 같이 매칭됩니다.

  • 46 = Java 1.2
  • 47 = Java 1.3
  • 48 = Java 1.4
  • 49 = Java 5
  • 50 = Java 6
  • 51 = Java 7

Info – Charcter Set, Encoding 그리고 변환

원문

문자셋 (charset, Character Set)

하나의 언어권에서 사용하는 언어를 표현하기 위한 모든 문자(활자)의 모임을 문자셋(charater set)이라고 한다.
다시 말하면 우리가 얘기하는 언어를 책으로 출판할 때 필요한 문자(활자)를 모두 모은 것이라고 생각하면 된다. 추가적으로 부호와 공백 등과 같은 특수 문자도 문자셋에 포함된다.
영어의 경우 알파벳 대소문자와 특수 문자 등으로 간단하게 문자셋을 구성할 수 있지만 한글의 경우 출판에서 가,나,다 등으로 출판함으로 훨씬 다양한 문자셋을 가지고, 또한 한자를 병행해서 사용함으로 문자셋의 범위는 더욱 넓어진다.
추상적인 글자 셋은 여러 개의 인코딩을 가질 수 있다. MIME 문자셋은 IANA에서 정의하며 인터넷 및 XML 파일에서 사용한다.

인코딩 (encoding)

인코딩은 문자셋을 컴퓨터가 이해할 수 있는 바이트와 매핑하는 규칙이다.
예를 들면 ASCII Code에서 A,B,C 등은 문자셋이고 A는 코드 65, B는 코드 66 등 바이트 순서와 매핑한 것이 인코딩이다. 따라서 문자셋을 어떻게 매핑하느냐에 따라 하나의 문자셋이 다양한 인코딩을 가질 수 있다.

유니코드 표
위키 유니코드 표

헥사에서 스트링으로.. 스트링에서 헥사로 변환하기

Objective-C
[sourcecode language=”ObjC”]
// Objective-C equivalents would be
utf8 = [s UTF8String];
[NSString initWithUTF8String:decodedHexString];

// To make an NSString with the hexadecimal representation of a character string:
NSMutableString hex = [NSMutableString string];
while ( *utf8 ) [hex appendFormat:@"%02X" , *utf8++ & 0x00FF];

// You will have to make your own decodeHex function. Just pull two characters out of the string and, if they are valid, add a byte to the result.
[/sourcecode]

java
[sourcecode language=”java”]
utf8 = s.getBytes(ENCODING_UTF8);
new String(decodedHexString, ENCODING_UTF8);
[/sourcecode]
원문

java – google app engine for xmpp

출처 : http://blog.softwaregeeks.org/archives/530
위 글이 원본이며, 본인 보기 좋게 바꾸었습니다. :)

XMPP

(eXtensible Messaging and Presence Protocol)
사용 사례
– Google Talk
– Facebook

XMPP 참고 자료

* XMPP 샘플

inbound service address
xmpp_message /_ah/xmpp/message/chat/
xmpp_presence /_ah/xmpp/presence/available/
/_ah/xmpp/presence/unavailable/
/_ah/xmpp/presence/probe/
xmpp_subscribe /_ah/xmpp/subscription/subscribe/
/_ah/xmpp/subscription/subscribed/
/_ah/xmpp/subscription/unsubscribe/
/_ah/xmpp/subscription/unsubscribed/

위 테이블은 /war/WEB-INF/web.xml에 들어갈 매핑 URL로 매핑이 되었을 때 해당 서블릿으로 요청을 하게 됩니다.

사용자 등록, 삭제 처리

/war/WEB-INF/web.xml 파일에 서블릿 추가.
[sourcecode language=”xml”]
<servlet>
<servlet-name>SubscriptionServlet</servlet-name>
<servlet-class>com.ezcocoa.xmpp.SubscriptionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SubscriptionServlet</servlet-name>
<url-pattern>/_ah/xmpp/subscription/subscribe/</url-pattern>
</servlet-mapping>
[/sourcecode]

SubscriptionServlet.java
[sourcecode language=”java”]
@SuppressWarnings("serial")
public class SubscriptionServlet extends HttpServlet {

XMPPService xmppService = XMPPServiceFactory.getXMPPService();

public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
doPost(request,response);
}

public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
Subscription subscription = xmppService.parseSubscription(request);
String from = subscription.getFromJid().getId();
String to = subscription.getToJid().getId();

System.out.println("From : " + from);
System.out.println("To : " + to);
}
}
[/sourcecode]

구글앱엔진에서 XMPP를 쉽게 처리 하기 위해 XMPPService라는 인터페이스를 제공하며, XMPPServiceFactory를 사용하여 실제 객체를 얻어 사용합니다.

이렇게 해서 하나의 서블릿이 완성되었고, 테스트를 하기 위해 자체 웹서버를 동작시켜 생성된 URL로 접속하시면 테스트가 가능합니다.
[code]
http://localhost:8888/_ah/admin/xmpp
[/code]

java – decompile

자바 디컴파일일 하는 방법을 소개하도록 하겠습니다.

디컴파일을 위해 3가지 Tool 이 필요합니다.
1. AJXMLPrinter2 (Prints XML document from binary XML file – with correct namespace handing & attribute formatting)
위 라이브러리는 바이너리로 된 XML 파일을 사람이 인식할 수 있는 파일로 출력해줍니다.
사용법
[code]java -jar AXMLPrinter2.jar file.xml > output[/code]
2. Undx
위 라이브러리는 .dex 파일을 jar로 변환해주는 역할을 합니다.
사용법
[code]$ java -DASDKLoc={Location of dexdump} -jar undx.jar -f {apkname.apk}[/code]

3. JAD (Java Decompiler)
위 라이브러리는 class파일을 java파일로 변환해주는 역할을 합니다.
사용법
[code]jad -sjava java *[/code]

! decompile 해봅시다.
ex) Android로 예로 들자면 안드로이드는 빌드 후 .apk를 생성하게 됩니다.
사실 .apk파일이나 .jar나 보시면 다 압축(파일의 모음)파일입니다.

압축을 풀고 계층 구조를 보면 아래와 같습니다.
.
|____AndroidManifest.xml
|____Animation.apk
|____classes.dex
|____META-INF
| |____CERT.RSA
| |____CERT.SF
| |____MANIFEST.MF
|____res
| |____anim
| | |____in_from_right.xml
| |____drawable-hdpi
| | |____icon.png
| |____drawable-ldpi
| | |____icon.png
| |____drawable-mdpi
| | |____icon.png
| |____layout
| | |____first_view.xml
| | |____main.xml
| | |____second_view.xml
|____resources.arsc

위 파일들에서 필요한 파일은 classes.dex 파일과 Androidmanifest.xml파일입니다.
classes.dex파일은 파일명 그대로 class파일 모음입니다.
.dex파일은 undx 라이브러리를 이용하여 푸시면 되고
Androidmanifest.xml 바이너리 파일은 AXMLPrinter2 라이브러리를 이용하시면 됩니다.

좀 더 자세한 내용을 남기고 싶었으나 시간 관계로 이만 쓰도록 하겠습니다.
막히는 부분이나 좀더 자세한 설명을 원하시는 경우 글 남겨주세요.

Java – pattern

[sourcecode language=”java”]
Pattern p = Pattern.compile("^[0-9a_zA-Z]{1,3}$");
Matcher m = p.matcher("Z0");
boolean b = m.matches();

if (b) System.out.println("ok");
else System.out.println("oh no!!");
[/sourcecode]