Java 5부터 등장한 제너릭을 이제서야 정리해보려고 한다. 사실 만들어져 있는 제너릭 API들은 많이 사용해왔지만 직접 제너릭 클래스를 작성하는 일은 아무래도 일반 클래스를 작성하는 것보다 빈도가 낮다보니, 제너릭에 관해 최근에서야 알게 된 것들이 있기도 하다.

나는 이미 자바의 버전이 6일 때 배우기 시작해서 제너릭 전과 후의 차이를 극적으로 느끼지는 못했지만 가끔 레거시 코드 중에 제너릭을 쓰지 않고 컬렉션을 사용하는 코드를 보면 조금 힘들긴 하다(물론 제너릭이 늘 옳은 것만은 아니라고 생각한다).

1. 제너릭을 왜 쓰는 걸까요?

2. 제너릭 타입

3. Parameterized Type

4. 제너릭 메서드

5. Bounded Type Parameter

- Class & Interfaces.

6. 제너릭 타입 추론

7. 제너릭으론 할 수 없는 것들

8. 그렇게 중요하진 않지만 흥미로운 사실 - Multiple Extended(Implemented) Generic Interface.

9. assignable - Foo<T>, Bar extends Foo<T>, Foo assignable Bar. @Autowired

10. 제너릭 리플렉션

면접 질문 중에 가장 당황했던 질문이 아니었나 싶다.


"StackOverflow가 발생했을 때 어떻게 해결하실건가요?"


너무 OutOfMemoryError에 대해서만 대비를 했던 탓인지 카운터를 맞았다. 답변도 OutOfMemoryError에 대한 해결 방법으로 해버렸다. 그러자 면접관께서 "그건 Heap 메모리에서구요, StackOverflow는 어떻게 될까요?"하고 내가 잘못 이해했다고 생각을 하셨는지 다시금 바로 잡아주셨지만.. 머리가 하얘졌더랬다.


면접이 끝나고, 정신이 좀 돌아오니 그제야 StackOverflowError는 호출 스택의 깊이가 너무 깊어질 때 발생한다는 게 생각났다. 웬만해선 질문을 받아도 알면 대답하고, 모르면 모른다고 대답을 했을텐데, 말그대로 정말 당황해서 어버버 했었던 것 같다.


먼저 Java API 문서의 StackOverflowError 클래스의 설명에는 "Thrown when a stack overflow occurs because an application recurses too deeply"로 되어있다.  그럼 StackOverflowError가 발생했을 때 어떻게 해야할까? Heap 영역과 마찬가지로 Stack 영역의 메모리를 늘려주면 되는걸까? 복구는 가능할까?


일단 에러 상황을 만들기 위해 무한 재귀호출을 하도록 아래와 같이 작성했다.



그리고 JVM Option에서 -Xss10K를 추가해서 실행해보았다. 근데 아래와 같은 메시지를 출력하면서 실행되지 않는다.


Error: Could not create the Java Virtual Machine.

Error: A fatal exception has occurred. Program will exit.


The stack size specified is too small, Specify at least 160k


160K의 기준이 있나 싶어 1.8 버전의 JVM 명세를 찾아봤으나 딱히 160K에 대한 기준은 없은 것으로 봐서는 JVM 구현에서 결정하는 사항인 듯 하다. 검색을 하다보니 CASSANDRA 이슈에서 Stack 사이즈와 관련된 내용이 있었는데 Java 6의 경우는 -Xss128K도 되는데 Java 7부터는 160K의 하한이 생긴 모양이다. (https://issues.apache.org/jira/browse/CASSANDRA-4275)


다시 스택 크기를 일단 최소로 잡아서 160K로 조정해서 실행해보았다. 751까지 출력된 후 StackOverflowError를 발생시킨다. 일부러 재귀구조로 프로그램을 작성하지 않는 이상 이 정도의 깊이까지 호출하는 경우가 있을지는 의문이긴하다.


스택 크기를 320K로 조정했을 때는 2615까지, 480K일때는 4476, 640K일 때는 6345로 출력되었다. 메서드 구현에 따라 스택 메모리를 소모하는 정도는 달라질 수 있을 것이고, 또한 스택 크기를 지정할 때 스레드 스택 프레임을 위해 기본으로 할당되는 영역까지 고려되어야 할 것 같다. Stack 메모리는 스레드별로 할당되므로 요청을 스레드별로 처리하는 웹 애플리케이션의 경우에는 Heap과 Stack의 크기가 세심하게 설정되어야 할 것 같지만 아직은 경험이 없으니 이에 대한 방법은 더 공부를 해봐야겠다.



그럼 StackOverflowError가 발생했을 때 일반적인 Exception과 마찬가지로 예외 처리가 가능할까? 일단 Error도 try-catch로 Error를 잡을 수는 있지만, 이미 비정상적인 조건에서 발생하게 되므로, 에러가 발생하기 전까지, 혹은 그 후에 처리가 정상적으로 되었는지 확신하기 어렵다. 이 경우에는 Error를 잡는 것보다, Error가 발생하는 원인을 찾아 해결하는 쪽이 더 바람직하다고 본다.



※ 참고 자료

- https://docs.oracle.com/javase/7/docs/api/java/lang/StackOverflowError.html

- https://docs.oracle.com/javase/7/docs/api/java/lang/Error.html

- https://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf

https://issues.apache.org/jira/browse/CASSANDRA-4275

- http://stackoverflow.com/questions/20658264/when-does-stackoverflowerror-occur

자바 애너테이션 기반의 HTTP 클라이언트 라이브러리.


안드로이드 관련 프로젝트를 진행하면서 안드로이드 애플리케이션이나 SDK에서 안드로이드 SDK를 제외하고 가장 많이 공통적으로 쓰였던 부분은 HTTP Client가 아니었나 싶다.

HTTP 통신은 비교적 손이 많이 가는 편이고, 핵심 코드에 비해 주변코드가 많이 작성된다. DB를 프로그래밍 할 때와 비슷하게 주변코드는 공통적으로 처리되는 부분이 많고, 핵심 코드에 집중해서 HTTP 클라이언트를 작성하고 싶다는 생각을 항상 하고 있어서, 이를 애너테이션 기반으로 만들어보면 어떨까 하는 생각에 구상하게 되었다.


프로젝트 네이밍은 자바의 애너테이션 기호인 @을 우리말로 읽을 때 '골뱅이'라고 많이 읽는 것에서 '골뱅'으로 하고, 영어로 쓰면 'gol bang'이므로, 의미는 없지만 '#!' 을 해쉬뱅(Hash bang)으로 읽으니 'Gol!' 으로 축약하였다. (하이고 의미없다, 그치만 개인적으로는 마음에 든다 하하;)


Gol!의 목표는 HTTP 통신을 간편하게 핵심코드만으로 처리할 수 있도록 하는 것이다. 즉 핵심 로직에만 집중할 수 있도록 하여 생산성을 향상시키는데 있다. 고려해야하는 것들은 HTTP 프로토콜, 비동기 요청의 처리 및 비동기 요청 시의 콜백 처리 등이다.

Gol!의 기본적인 개념은 메서드와 HTTP 요청을 1:1로 매핑하는 것이고, 메서드 호출이 곧 HTTP 요청이며, 메서드 파라미터는 쿼리스트링이나 HTTP 메시지 본문이 되며, 리턴 타입이 응답 메시지이다. (비동기의 경우에는 리턴에 대한 처리를 콜백에서 하도록 해야한다.) 이 외에 요청에 관한 데이터(헤더, 요청 URL, 메서드 등)은 애너테이션을 통해 메타 데이터로 제공한다. 애너테이션의 경우 리터럴 상수만 인자로 받을 수 있으므로, 동적으로 요청에 대한 정보를 변경할 수 없다. 따라서 메서드 파라미터 애너테이션을 통해 요청에 대한 정보를 동적으로 제공할 수 있도록 설계하여야 한다.

개별 요청에 대한 정보는 메서드 및 메서드 애너테이션으로 제공하고, 서버에 대한 공통적인 설정은 인터페이스에서 할 수 있도록한다.


기본적으로는 위와 같은 모습으로 메서드가 작성이 되고, 자바의 동적 프록시를 통해 HTTP 요청을 공통으로 처리할 것이므로, 위 요청 메서드는 인터페이스 내에 작성하도록 한다. 따라서 Gol! 사용시에는 요청에 대한 데이터만 인터페이스로 제공하고, 요청에 대한 세부적인 로우레벨의 코드 구현은 최대한 배제할 수 있도록 한다.


예상되는 가장 큰 이슈는 메모리와 속도 이슈가 아닐까싶다. 애너테이션은 리플렉션을 통해 실제 값을 얻을 수 있으므로 여기에서 한번 속도 및 메모리 이슈가 발생하고, 사용자의 구현을 단순하게 하게 위해서 동적 프록시를 적용하게 되므로 또 한번 속도 및 메모리 이슈가 발생한다. 또한 사용자 구현은 단순화되지만, 라이브러리 자체의 복잡도가 높아져서 설계를 잘해야할 듯 하다.. ㅜㅜ


조금씩이라도 부지런하게 작성하고, 개선해봐야겠다. :)

힙을 구현하기는 귀찮아서 '혹시나' 싶어서 찾아보니 '역시나' 고맙게도 자바에 PriorityQueue가 있네요.


먼저 PriortyQueue는 java.util 패키지에 있고, 여러 형태의 생성자가 오버로딩되어 있는데,

주요한 두가지만 알아보면 아래와 같습니다.


  • PriortyQueue<E>()
  • PriorityQueue<E>(int initialCapacity, Comparator<? super E> comparator)


둘을 비교하자면 전자는 기본 생성자를 이용해서 심플하게 생성하는 방법이고,

후자는 초기 큐 내부의 배열 크기와 Comparator 객체를 전달하면서 객체를 생성하는 방법입니다.

(어째서인지 initialCapacity 없이 Comparator만 전달하는 생성자는 없더군요.)


※ initialCapacity?

큐 내부에서 사용하는 배열의 초기 크기를 결정하는 값입니다.

기본적으로 11의 크기를 사용하고 임의로 전달할 경우에는 그 크기를 사용하는데 1보다 작은 경우

IllegalArgumentException 예외가 발생합니다. 전달 인자가 잘못되었단 얘기죠.

배열의 크기는 initialCapacity로 고정되는 것이 아니라, 요소의 수가 배열의 크기에 도달하면

크기를 늘립니다. 재밌는 것은 배열의 크기가 커질 때 규칙을 보면 배열의 크기가 64 미만일 때는 2씩 커지고,

그 이후로는 이전 크기의 1/2 씩 증가하도록 되어있습니다.

왜 이런 룰이 적용되었는지는 잘 모르겠네요.

큐 내의 배열의 최대크기는 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

이렇게 정의되어 있으니, 값으로 보면 2147483639 까지입니다. 왠만한 경우엔 다 들어갈 수 있겠죠?

다만, 힙 메모리가 부족해서 최대 크기가 되기 전에 OutOfMemoryError가 먼저 발생하겠네요.



큐에서 중요한 두 메서드, 큐에 넣고(add), 큐에서 빼는(poll) 메서드에 대해 간단하게 설명하겠습니다.

덤으로 가장 우선순위가 높은 요소를 확인하는 peek 메서드도요.

  • boolean add(E e) : 큐에 요소를 추가합니다. 반환값은 성공 여부입니다.
  • E poll() : 큐에서 우선순위가 가장 높은 요소를 빼냅니다. 즉 반환 후에 큐에서 삭제됩니다.
  • E peek() : poll과 달리 큐에서 삭제하지 않고 가장 우선순위가 높은 요소를 얻습니다.



1. PriorityQueue()

기본 생성자를 이용하는 경우는 제너릭 타입 E의 compareTo 메서드를 이용하여 정렬에 사용합니다.

compareTo 메서드는 그냥 compareTo메서드를 만들어 쓰는 것이 아니라, 

Comparable 인터페이스를 구현해서 만듭니다.


만약 Comparable 인터페이스를 구현하지 않은 클래스를 큐의 엘리먼트로 사용하는 경우에는

java.lang.ClassCastException을 만나게 될 겁니다. PriorityQueue 내부에서 Comparable 타입으로 캐스팅해서

compareTo 메서드를 호출하기 때문입니다.


기본 생성자를 이용하여 큐의 객체를 생성하는 경우를 소스코드로 작성해볼까요.


먼저 요소로 사용할 Element 클래스를 작성해 보겠습니다.

class Element implements Comparable<Element>{
	private int num; // 정렬의 기준이 될 값
	
	public Element(int num){
		this.num = num;
	}
	
	public int getNum(){
		return num;
	}

	@Override // Comparable 인터페이스의 compareTo 메서드 구현
	public int compareTo(Element o) {
		return num <= o.num ? -1 : 1;
	}
}


compareTo 메서드의 기능은 현재 객체와 다른 Element 객체를 비교하여 우선순위를 판단하는 것입니다.

우선순위의 판단은 반환값으로 하는데 음수인 경우에는 현재 객체가 우선임을 의미하고,

양수인 경우에는 대상 객체(위의 소스코드에서는 Element o)가 우선임을 의미합니다.

간편하게 return num - o.num; 을 해도 됩니다.


다음으로 PriorityQueue를 사용해 볼 클래스 PriorityQueueTest 를 작성해 보겠습니다.

public class PriorityQueueTest {
	public static void main(String[] args) {
		PriorityQueue<Element> q =
				new PriorityQueue<Element>();
		
		Random random = new Random(System.nanoTime());
		
		// 0~49의 난수를 생성하여 큐에 넣습니다.
		for(int i = 0 ; i < 10 ; i++){
			q.add(new Element(random.nextInt(50)));
		}
		
		// 큐에서 값을 빼면서 정렬이 되었는지 출력해봅니다.
		int size = q.size();
		for(int i = 0 ; i<size ; i++){
			System.out.println(q.poll().getNum());
		}
	}
}

[출력결과]

4

14

19

19

30

33

42

42

46

49



2. PriorityQueue(int initialCapacity, Comparator<? super E> comparator)

복잡해 보이는 생성자지만 간단합니다. 초기 배열 크기와 Comparator를 구현한 객체를 전달해 주면 되는데,

Comparator<? super E> 가 조금 거슬리는 분도 있을 거라고 생각합니다.

Java 5 부터 제너릭 개념이 들어가면서 컬렉션 관련 API에는 제너릭이 들어가도록 모두 바뀌었습니다.

제너릭이라고 말하는게 꺽쇠 괄호 < > 를 이용해 타입을 지정해 주는 것을 말합니다.


제너릭에 관해서는 다른 글에서 얘기하기로 하고, 위의 <? super E>가 의미하는 것만 알아보면

Comparator 선언 시, 혹은 구현시에 Comparator<타입> 의 형태로 적어줘야 합니다.

이때 ? super E 가 의미하는 게 위에 들어가는 타입은 E를 상속받은 하위 클래스여야 한다는 의미가 됩니다.

풀어보면 어떤 타입(?)의 부모(super)가 E여야 한다. 라고 볼 수 있겠네요.


복잡하게 생각할 것 없이 여기서는 "타입"의 위치에 Element를 적어주도록 합시다.


Comparator 인터페이스를 구현하는 방법은 여러가지가 있겠지만 여기서는 그냥 익명 이너 클래스로 구현하여

전달하도록 하겠습니다. Element 클래스는 위의 클래스 그대로 사용합니다.


class PriorityQueueTest {
	public static void main(String[] args) {
		PriorityQueue<Element> q =
			new PriorityQueue<Element>(50, new Comparator<Element>() {
				@Override
				// Comparator 인터페이스의 compare 메서드를 구현합니다.
				// 여기서는 이전과 반대로 내림차순 정렬을 하도록 했습니다.
				public int compare(Element o1, Element o2) {
					return o2.getNum() - o1.getNum(); 
				}
			});
		
		Random random = new Random(System.nanoTime());
		
		// 0~49의 난수를 생성하여 큐에 넣습니다.
		for(int i = 0 ; i < 10 ; i++){
			q.add(new Element(random.nextInt(50)));
		}
		
		// 큐에서 값을 빼면서 정렬이 되었는지 출력해봅니다.
		int size = q.size();
		for(int i = 0 ; i<size ; i++){
			System.out.println(q.poll().getNum());
		}
	}
}

[출력 결과]

42

36

33

28

26

18

14

4

3

0


내림차순으로 정렬이 되었습니다. 여기서 조금 의아한 점을 찾으신 분도 있을 것 같습니다.

이미 Element는 Comparable 인터페이스를 구현해서 오름차순으로 정렬하도록 되어있는데,

내림차순으로 정렬되었기 때문입니다. 그 의문점의 결과는 위의 출력결과가 말해줍니다.


즉 compareTo 메서드보다 compare 메서드가 우선한다는 말이지요.

(그냥 내부적으로는 if(comparator != null){ ... } 이렇게 되어있어서 문장상에서 우선하게끔 되어있습니다.)



자바의 PriorityQueue 클래스를 사용해 봤습니다.

Comparator나 Comparable인터페이스에 대한 개념이 확실하게 잡힌다면 크게 무리 없이 사용할 수 있을 것 같습니다.

XMLStreamReader 클래스의 next 메서드는 이벤트를 반환하고, 반환된 이벤트에 따라 맞는 처리를 하게 된다.


StAX의 XMLEvent 클래스에는 XML의 이벤트가 상수로 정의되어 있는데, 아래와 같다.

  • XMLEvent.START_ELEMENT
  • XMLEvent.END_ELEMENT
  • XMLEvent.PROCESSING_INSTRUCTION
  • XMLEvent.CHARACTERS
  • XMLEvent.COMMENT
  • XMLEvent.SPACE
  • XMLEvent.START_DOCUMENT
  • XMLEvent.END_DOCUMENT
  • XMLEvent.ENTITY_REFERENCE
  • XMLEvent.ATTRIBUTE
  • XMLEvent.DTD
  • XMLEvent.CDATA
  • XMLEvent.NAMESPACE
  • XMLEvent.NOTATION_DECLARATION
  • XMLEvent.ENTITY_DECLARATION
참고로 이는 XMLStreamConstants에 동일하게 정의되어 있다.

또한 각각의 이벤트에 따라 사용할 수 있는 메서드가 정해져있는데,
이벤트에 맞지 않는 메서드를 사용하면 예외가 발생한다.

 이벤트

유효한 메서드 
 모든 이벤트에 적용가능

 getProperty(), hasNext(), require(), close(), getNamespaceURI(),
 isStartElement(), isEndElement(), isCharacters(),isWhiteSpace(), 

 getNamespaceContext(), getEventType(),getLocation(), hasText(), 

 hasName()

 START_ELEMENT

 next(), getName(), getLocalName(), hasName(), getPrefix(), 

 getAttributeXXX(), isAttributeSpecified(),getNamespaceXXX(), 

 getElementText(), nextTag()

 ATTRIBUTE  next(), nextTag() getAttributeXXX(), isAttributeSpecified()
 NAMESPACE  next(), nextTag() getNamespaceXXX()

 END_ELEMENT

 next(), getName(), getLocalName(), hasName(), getPrefix(), 

 getNamespaceXXX(), nextTag()

 CHARACTERS  next(), getTextXXX(), nextTag()
 CDATA  next(), getTextXXX(), nextTag()
 COMMENT  next(), getTextXXX(), nextTag()
 SPACE  next(), getTextXXX(), nextTag()
 START_DOCUMENT  next(), getEncoding(), getVersion(), isStandalone(), standaloneSet(),
 getCharacterEncodingScheme(), nextTag()

 END_DOCUMENT

 close()
 PROCESSING_INSTRUCTION  next(), getPITarget(), getPIData(), nextTag()
 ENTITY_REFERENCE  next(), getLocalName(), getText(), nextTag()
 DTD  next(), getText(), nextTag()



http://tutorials.jenkov.com/java-xml/stax.html에서 가져옴


The StAX Java API for XML processing is designed for parsing XML streams, just like the SAX API's. The main differences between the StAX and SAX API's are:

  • StAX is a "pull" API. SAX is a "push" API.
  • StAX can do both XML reading and writing. SAX can only do XML reading.

It is pretty obvious what the difference between a "read + write" capable API vs. a "read" capable API is. But the difference between a "pull" and a "push" style API is less obvious, so I'll talk a little about that. For a more feature-by-feature type comparison of SAX and StAX, see the text SAX vs. StAX.

NOTE: This text uses SVG (Scalable Vector Graphics) diagrams. If you are using Internet Explorer you will need the Adobe SVG Plugin do display these diagrams. Firefox 3.0.5+ users and Google Chrome users should have no problems.


"Pull" vs. "Push" Style API

SAX is a push style API. This means that the SAX parser iterates through the XML and calls methods on the handler object provided by you. For instance, when the SAX parser encounters the beginning of an XML element, it calls the startElement on your handler object. It "pushes" the information from the XML into your object. Hence the name "push" style API. This is also referred to as an "event driven" API. Your handler object is notified with event-calls when something interesting is found in the XML document ("interesting" = elements, texts, comments etc.).

The SAX parser push style parsing is illustrated here:

StAX is a pull style API. This means that you have to move the StAX parser from item to item in the XML file yourself, just like you do with a standard Iterator or JDBC ResultSet. You can then access the XML information via the StAX parser for each such "item" encountered in the XML file ("item" = elements, texts, comments etc.).

The StAX parser pull style parsing is illustrated here:

In fact, StAX has two different reader API's. One that looks most like using an Iterator and one that looks most like using a ResultSet. These are called the "iterator" and "cursor" readers.

So, what is the difference between these two readers?

The iterator reader returns an XML event object from it's nextEvent() calls. From this event object you can see what type of event you had encountered (element, text, comment etc.). This event element is immutable, and can be parsed around to other parts of your application. You can also hang on to earlier event objects when iterating to the next event. As you can see, this works very much like how you use an ordinary Iterator when iterating over a collection. Here, you are just iterating over XML events. Here's a sketch:

XMLEventReader reader = ...;

while(reader.hasNext()){
    XMLEvent event = reader.nextEvent();

    if(event.getEventType() == XMLEvent.START_ELEMENT){
        StartElement startElement = event.asStartElement();
        System.out.println(startElement.getName().getLocalPart());
    }
    //... more event types handled here...
}

The cursor reader does not return events from it's next() call. Rather this call moves the cursor to the next "event" in the XML. You can then call methods directly on the cursor to obtain more information about the current event. This is very similar to how you iterate the records of a JDBCResultSet, and call methods like getString() or getLong() to get values from the current record pointed to by the ResultSet. Here is a sketch:

XMLStreamReader streamReader = ...;

while(streamReader.hasNext()){
    int eventType = streamReader.next();

    if(eventType == XMLStreamReader.START_ELEMENT){
        System.out.println(streamReader.getLocalName());
    }

    //... more event types handled here...
}

So, one of the main differences is, that you can hang on to earlier XML event objects when using the iterator style API. You cannot do this when using the cursor style API. Once you move the cursor to the next event in the XML stream, you have no information about the previous event. This speaks in favour of using the iterator style API.

However, the cursor style API is said to be more memory-efficient than the iterator style API. So, if your application needs absolute top-performance, use the cursor style API.

Both of these two StAX API's will be covered in more detail in later texts. See the table of contents in the right side of this page.


Java StAX Implementation

At the time of writing (Java 6) only the StAX interfaces are bundled with the JDK. There is no StAX implementation built into Java. But, there is a standard implementation which can be found here:

http://stax.codehaus.org/


기본 표현


  • .    줄바꿈 문자를 제외한 임의의 문자 하나를 대체
  • \    이스케이프 문자
  • []    집합
  • [\b]    역스페이스
  • \f    페이지 넘김(form feed) 문자
  • \n    줄바꿈(line feed) 문자
  • \r    캐리지 리턴
  • \t    탭
  • \v    수직 탭
  • \d    숫자 하나([0-9])와 동일
  • \D    숫자를 제외한 문자([^0-9])와 동일
  • \w    대소문자와 밑줄을 포함하는 모든 영숫자(=[a-zA-Z0-9_])
  • \W    영숫자나, 밑줄이 아닌 모든 문자(=[^a-zA-Z0-9_])
  • \s    모든 공백 문자(=[\f\n\r\t\v])
  • \S    공백 문자가 아닌 모든 문자(=[^\f\n\r\t\v])
  • *    0-n번 반복 (greedy)
  • ?    0-1번 (greedy)
  • +    1-n번 반복 (greedy)
  • *?    0-n번 반복 (lazy)
  • +?    1-n번 반복 (lazy)
  • {숫자}    숫자 만큼 반복
  • {숫자1, 숫자2}    최소 숫자1번 반복하고 숫자2번까지 반복
  • {숫자,}    최소 숫자만큼 반복 (greedy)
  • {숫자,}?    최소 숫자만큼 반복 (lazy)
  • ()    하위 표현식
  • |    OR
  • \숫자    역참조, 숫자는 하위 표현식의 인덱스, 0은 전체.
  • $숫자    치환시의 역참조
  • \E    \L혹은 \U 변환의 끝을 나타냄
  • \l    다음에 오는 글자를 소문자로 치환
  • \L    \E를 만날 때까지 모든 문자를 소문자로 치환
  • \u    다음에 오는 글자를 대문자로 치환
  • \U    \E를 만날 때까지 모든 문자를 대문자로 치환




위치 찾기


  • \b    단어 경계 지정
  • ^표현    문자열 시작 문자 지정
  • $표현    문자열 끝 문자 지정
  • (?m)    줄바꿈문자를 공백문자에 포함되도록 한다.




POSIX


  • [:alnum:]    모든 영숫자([a-zA-Z0-9)
  • [:alpha:]    모든 영문자([a-zA-Z])
  • [:blank:]    빈칸이나 탭 문자와 같음
  • [:cntrl:]    아스키 제어문자(아스키 0-31, 127)
  • [:digit:]    모든 한 자리 숫자([0-9])
  • [:graph:]    [:print:]와 동일하나 빈칸(space)은 제외
  • [:lower:]    모든 소문자([a-z])
  • [:print:]    출력가능한 모든 문자
  • [:punct:]    [:alnum:]이나 [:cntrl:]가 포함되지 않은 모든 문자
  • [:space:]    빈칸을 포함한 모든 공백 문자([\f\n\r\t\v])
  • [:upper:]    모든 대문자([A-Z])
  • [:xdigit:]    모든 16진수 숫자([a-fA-F0-9])





전방탐색과 후방탐색


  • (?=)    긍정형 전방 탐색
  • (?!)    부정형 전방 탐색
  • (?<=)    긍정형 후방 탐색
  • (?<!)    부정형 후방 탐색




조건 사용하기


  • (?(backreference)true)    역참조가 있을 때 true를 함께 일치시킴
  • (?(backreference)true|false)    역참조가 있을 때 true를 아니면 false를 일치시킴
  • (?(?=식)true|false)    전방탐색을 이용한 조건
  • (?(?<=식)true|false)    후방탐색을 이용한 조건




자바에서 정규표현식 사용


※ JRE 1.4부터 정규표현식을 지원하므로 유의하여 사용한다.

※ 조건은 지원하지 않음

※ \E, \l, \L, \u, \U를 이용한 대소문자 변환은 지원하지 않음

※ \b를 이용한 백스페이스 일치를 지원하지 않음

※ \z는 지원하지 않음


java.util.regex.Matcher 클래스

  • find() - 문자열에서 패턴과 일치하는 부분이 있는지 찾는다.
  • lookingAt() - 문자열이 주어진 패턴으로 시작하는지 일치시켜 본다.
  • matches() - 문자열 전체를 주어진 패턴과 일치시켜본다.
  • replaceAll() - 일치하는 부분을 모두 치환한다.
  • replaceFirst() - 처음 일치하는 부분만 치환한다.
  • group() - 하위 표현식을 반환한다.


java.util.regex.Pattern 클래스

  • compile() - 정규 표현식을 패턴으로 컴파일한다.
  • flag() - 패턴에 설정되어 있는 일치 플래그를 반환한다.
  • matches() - Matcher 클래스의 matches와 동일함
  • pattern() - 만들었던 패턴에서 정규 표현식 부분을 문자열로 반환한다.
  • split() - 문자열을 하위 문자열로 나눈다.



import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexTest{     public static void main(String[] args){         Pattern pattern = Pattern.compile("정규표현식");         Matcher matcher = pattern.matcher("대상 문자열");         if(matcher.find()){             // 처리         }     } }




'밤을 지새다' 카테고리의 다른 글

Linux Kernel Map  (0) 2013.03.24
C# ListBox Double Click 이벤트  (0) 2012.06.08
네이트온 광고와 팝업 없애기  (3) 2012.05.16
파일의 확장자를 보는 방법  (0) 2012.05.16
Gmail 실행파일 첨부 문제  (0) 2012.05.16

자주 보게 되는 예외를 정리해 봤습니다. 이유 모를 예외나 모르는 예외가 있을 때 댓글로 달아주세요.


1. NullPointerException
Null 객체에 대해 접근했을 때 발생한다.
ex) String str = null;
str.equals("foo");


2. IllegalArgumentException
잘못된 인자값을 전달했을때 발생한다.


3. ArithmeticException
산술적인 오류가 있을 때 발생한다.
ex) int i = 1 / 0; // 0으로 나눌 수 없으므로 예외 발생.


4. ClassCastException
잘못된 형변환을 할 때 발생한다.
ex) Object o = "foo";
char[] str = (char[]) o;


'밤을 지새다 > Java' 카테고리의 다른 글

Java StAX: XMLInputFactory  (0) 2012.04.30
Java StAX  (0) 2012.04.30
자바. 얼마나 알고 사용하고 계신가요?  (0) 2011.08.25
객체를 바이트 배열로, 바이트 배열을 객체로  (0) 2011.08.22
메서드 체인화 기법  (0) 2011.08.17
1. JDK 설치
자바개발을 위해서는 JDK를 설치해야합니다.
JDK는 Java Development Kit의 약자로 뜻 그대로 자바 개발 도구입니다. JDK에는 개발을 위한 컴파일러나 인터프리터가 포함되어있습니다. JRE라는 것도 들어보셨을건데 JRE는 자바로 작성한 프로그램을 실행하기 위한 환경으로 Java Runtime Environment의 약자입니다.

소개는 간단히 하고 JDK를 설치해 봅시다. java는 오라클에서 배포하고 있고 사용은 무료입니다.

☞ 다운로드 링크

다운로드 링크를 따라가시면 아래와 같이 나올텐데요, SE버전 링크를 걸어두었습니다. SE는 Standartd Edition이구요. 자바 표준 버전입니다. 기본적인 자바개발을 위한 라이브러리가 포함되어 있습니다. 여러가지 버전이 있는데 그중에 EE버전은 Enterprise Edition으로 웹 애플리케이션 서버 개발시 주로 이용합니다.
라이선스에 동의해 주시면 바로 다운로드 받을 수 있습니다. 
자신의 운영체제에 맞는 버전을 받으시면 됩니다.



JDK 설치파일 실행 모습입니다. 환영한다네요!
 
 

기본 설치 옵션입니다. JDK를 설치하면 JRE도 함께 설치가 됩니다.
데모, 샘플을 이용해 공부할 생각이 있으시다면 Demos and Samples를 클릭하셔서 Install를 선택하시면 됩니다.
설치 경로는 디폴트로 Program files아래에 java 폴더로 설치가 됩니다. 그 아래에 버전별로 구분이 되구요
윈도우 7에서 32bit를 사용하실 경우 Program Files(x86) 폴더에 설치가 됩니다.
바꾸셔도 상관은 없습니다. 다만 환경변수(PATH)설치할 때 지정해준 경로로 잡아주시면 됩니다.
 
 

JDK는 이렇게 설치가 되구요. 사양에 따라서 1분 내외로 설치가 완료됩니다.
 
 

다음은 JRE를 설치합니다. 저는 JRE도 디폴트 경로에 설치했습니다.
 

큼지막하게 자바 자랑하고 있네요. 30십억의 장치에서 자바를 쓰고 있답니다.
오라클이 선마이크로시스템즈를 인수하고는 이제 오라클 로고가 딱!
 

완료 되었습니다. [Product Registration Information] 버튼은 제품 등록에 관한 정보를 확인할 수 있는 페이지입니다.
가볍게 [Finish] 버튼을 눌러 설치를 완료합니다.

 

2. 환경변수 설정
환경변수는 콘솔(커맨드)환경에서 자바 컴파일러나 인터프리터를 실행할 시에 실행파일의 전체 경로를 쓰지 않아도 되게끔 해주는 변수입니다.
현재있는 경로에서 어떤 명령어를 실행했을 때 그 경로에 해당 파일이 존재하지 않는다면 환경변수의 경로들을 하나씩 검사하여 그 안에 있으면 실행한다는 얘기입니다.
즉 컴파일을 위해서 C:\Program Files\java\jdk 1.6.02\bin\javac Hello.java 이렇게 다 치지 않고 어디서든 소스 파일이 있는 곳에서 javac hello.java 이렇게 하시면 됩니다. 


아래는 Window 7 환경 기준으로 설명했구요. XP를 쓰시는 분은 [더보기]를 클릭하셔서 시스템 속성에 들어가는 방법을 확인해 주세요.
 
 

윈도우키 + E 를 눌러 탐색기를 실행하고 좌측에 있는 컴퓨터에서 오른쪽 버튼을 눌러 속성(B)를 선택합니다.

 


시스템 설정창이 나오면 좌측에서 "고급 시스템 설정"을 클릭합니다.

 

고급 시스템 설정을 누르면 시스템 속성 창이 뜨는데 여기서 [고급] 탭으로 이동하셔서
[환경변수(N)...] 버튼을 눌러줍니다.


위쪽에 있는 목록은 현재 로그온된 사용자에게만 적용되는 내용이고
아래쪽에 있는 목록은 현재 시스템을 사용하는 전체에게 적용되는 내용입니다.
하나의 계정으로 쓰시는 경우에는 어느쪽이든 상관없지만 전체 사용자에게 적용하시려면
아래쪽에 있는 Path를 편집하시면 됩니다.


Path 편집을 누르면 기존에 있던 Path 값이 있습니다. 기존의 내용을 지우지 마시고 제일 끝에 가셔서
세미콜론(;, 경로를 구분하는 역할을 합니다)을 삽입하시고 JDK를 설치한 경로\bin을 입력하고 확인을 눌러주세요.
다시 시스템 속성 창으로 돌아오면 확인을 눌러 속성을 저장하고 나옵니다.


패스가 잘 저장되었는지 확인해 봅시다. 윈도우 + R 키를 눌러 실행창을 띄우고 cmd (커맨드 콘솔)을 입력하고 엔터!


콘솔창이 나타나면 java 라고 입력하고 엔터.
java 실행 시 옵션에 대한 내용이 나타나면 설정이 제대로 된겁니다.



아직 에디트 플러스나 기타 텍스트 에디터로 자바 프로그래밍을 하시는 분들도 많으시지만
아무래도 이클립스나 J빌더를 사용해서 프로그래밍을 하는 쪽에 한표를 주고 싶습니다.
"자바를 하려면 콘솔에서 자바를 컴파일하고 실행할 줄 알아야 한다"라시는 분들도 계시지만 참고 정도로 알면 된다고 생각하구요, 무얼하든 마찬가지겠지만 개발에 있어서도 환경이 잘 갖춰져 있는게 개발에 더 도움이 되지 않을까 싶습니다. 

콘솔에서 자바를 사용해보시고 싶으신 분은 "자바 콘솔 명령어" 키워드로 검색하시면 원하시는 결과를 얻을 수 있습니다.

Object to byte array and byte array to Object in Java

원출처 : http://scr4tchp4d.blogspot.com/2008/07/object-to-byte-array-and-byte-array-to.html

Here how to convert an Object to a byte array, and vice-versa.
요기에 객체를 바이트 배열로, 그 반대로 바꾸는 방법이 있습니다!
역시 구글링이랄까.
 
public byte[] toByteArray (Object obj)
{
  byte[] bytes = null;
  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  try {
    ObjectOutputStream oos = new ObjectOutputStream(bos); 
    oos.writeObject(obj);
    oos.flush(); 
    oos.close(); 
    bos.close();
    bytes = bos.toByteArray ();
  }
  catch (IOException ex) {
    //TODO: Handle the exception
  }
  return bytes;
}
    
public Object toObject (byte[] bytes)
{
  Object obj = null;
  try {
    ByteArrayInputStream bis = new ByteArrayInputStream (bytes);
    ObjectInputStream ois = new ObjectInputStream (bis);
    obj = ois.readObject();
  }
  catch (IOException ex) {
    //TODO: Handle the exception
  }
  catch (ClassNotFoundException ex) {
    //TODO: Handle the exception
  }
  return obj;
}

'밤을 지새다 > Java' 카테고리의 다른 글

Java StAX: XMLInputFactory  (0) 2012.04.30
Java StAX  (0) 2012.04.30
Exceptions  (0) 2011.09.05
자바. 얼마나 알고 사용하고 계신가요?  (0) 2011.08.25
메서드 체인화 기법  (0) 2011.08.17

+ Recent posts