생각보다 자바를 사용하면서 자바의 업데이트 히스토리에 대해 모르는 것들이 많고 개념도 좀 아리송해서 검색하다 보니 좋은 글이 나와 포스팅합니다.
읽다 보니 저는 상당히 JDK 1.4적인 코딩만 해 왔었네요.
물론 하위 호환성이라는 미명 아래에 말이죠. (뭐 틀린 건 아니죠)
그래도 앞으로 나아가야 하는 직업이니 상상력을 좀 더 펼쳐 보아야겠어요. ㅎㅎ
실은 JDK 7의 빌드 일정이 나온 상황에서 무진장 늦은 포스팅 이랍니다 으흐흐..
출처 : http://asisis.tistory.com/267
제목 : JDK 5.0, 6.0 강화된 스팩
강화된 루프문, AUTOBOXING / UNBOXING
- for-each 루프문
- for-each 루프문은 컬렉션과 배열을 위해 만들어졌다.
- 컬렉션 사용시 Iterator를 생성하거나 커운터 변수의 시작과 끝 상태를 계산할 필요 없이 컬렉션을 반복할 수 있게 해준다.
- for-each 루프문은 기존의 for문보다 사용이 간단하고 사용자의 코드를 보다 읽기 쉽게 만들어 준다.
- for-each 루프문을 컬렉션에서 사용하기 위해서는 되도록 제너릭(Generics)을 이용하는 것이 좋다.
- 형식
- for(데이터형 접근변수이름: 배열이나 컬렉션 변수이름) {
- 반복적으로 수행해야 할 문장
- }
- 언제 for-each 루프문을 사용하가?
모든 경우에 for-each 루프문을 사용할 수는 없으며, 다음과 같은 경우에는 for-each 루프문을 사용할 수 없다.
- for-each 루프문을 필터링 작업 시에는 유용하지 않다.-즉, 컬렉션을 순회하면서 엘리먼트를 삭제 할 때 for-each 루프문을 사용할 수 없다. 다음과 같은 expurgate 메소드에서의 경우를 살펴보면, expurgate 메소드는 특정 엘리먼트를 삭제하기 위해 iterator을 사용해야 하는데, for-each 루프문에서 iterator를 사용할 수 없다.
- static void expurgate(Collection<String> c) {
- for(Iterator<String> i = c.iterator(); i.hasNext();)
- if(i.next().length() == 4)
- i.remove();
- }
- 리스트 또는 배열에 존재하는 엘리먼트를 다른 엘리먼트로 교체하고자 할 경우 for-each문은 유용하지 않다.
- 여러 개의 컬렉션을 동시에 접근해야 할 경우에 for-each문은 유용하지 않다.
> boxing과 unboxing 소개
(1) boxing이란?
컬렉션에는 int와 같은 프리미티브 타입의 데이터를 넣을 수 없고, 객체만 담을 수 있기 때문에 프리미티브 데이터 값을 적절한 래퍼 클래스로 감싸는 작업을 수행해야 했다.
이러한 작업을 boxing이라고 한다.
-> Autoboxing은 int와 같은 프리미티브 타입의 데이터를 Integer와 같은 래퍼 클래스로 자동으로 변환하는 것을 의미한다.
(2) unbosing이란?
컬레션에서 데이터를 꺼낼 경우에는 Integer와 같은 객체 타입으로 꺼내고 , int 타입의 데이터가 필요할 경우에는 다시 intValue 메소드를 이용하여 Integer 객체에서 int형태로 변화하는 작업을 수행해야 한다.
이 작업을 Unboxing이라고 한다.
-> Unboxing(JDK5.0)은 Integer와 같은 레퍼 클래스로 표현된 객체를 자동으로 int와 같은 프리미티브 타입의 데이터로 변환하는 것을 의미한다.
(3) Autoboxing과 Unboxing 사용시 유의사항
- get 또는 set 오퍼레이션을 수행될 때마다 boxing/unboxing 작업이 수행되기 때문에 수행속도가 늦어진다.
- 따라서, 가끔 사용할 경우에는 충분히 빠른 속도를 내지만, 수행속도가 중요한 루프 안에서 autoboxing과 unboxing은 사용하지 말아야 한다.
(4) Autoboxing과 Unboxing을 언제 사용하는가?
사용될 경우: 레퍼런스 타입과 프리미티브 타입간 "임피던스 불일치"가 발생할 경우에만 사용한다.
40038_Smith_kor.pdf: 관계형 데이터베이스에서 J2EE 애플리케이션의 영향 이해하기
사용되지 않아야 할 경우: 과학적 계산 또는 수행속도가 중요한 코드에서는 사용하지 않는다.
05차시. static import
> static imports
> 비정형 인자(VARARGS; Variable Arguments)
- 인터페이스에 정의되어 있는 모든 상수는 묵시적으로 public, static, final입니다.
- 인터페이스 멤버는 transient, synchronized와 같은 키워드를 사용할 수 없다.
- 메소드 선언시 private, protected를 사용할 수 없다.
Abstract 키워드 사용
이전 버전의 자바 플랫폼에서는 인터페이스 선언 시 또는 인터페이스 내에 메소드를 선언할 때 abstract을 사용했습니다.
하지만, 인터페이스 선언시 또는 인터페이스 내에 메소드를 선언할 때 abstract 라는 키워드를 사용하지 마십시요.
인터페이스 구현의 의미
> 인터페이스는 어떤 행동을 수행한다라는 일종의 협약서의 내용을 정의하고 있다.
> 인터페이스를 구현하는 클래스는 인터페이스에 정의되어 있는 협약서를 준수해야 한다. 즉, 클래스가 인터페이스를 구현한다는 것은 협약서에 사인한다는 의미입니다.
> 인터페이스를 구현하는 클래스를 선언할 때 "implements"라는 키워드를 사용한다.
> 클래스는 하나 이상의 인터페이스를 구현할 수 있다.
> 클래스는 인터페이스에 선언되어 있는 모든 메소드를 구현해야 한다. 만약 하나라도 구현하지 않을 경우 이 클래스는 추상 클래스(Abstract Class)로 정의되어야 한다.
> 클래스가 인터페이스와 클래스를 모두 상속받을 경우에, extends 절 다음에 implements절을 정의해야 합니다.
인터페이스를 타입으로 사용
> 인터페이스 이름을 데이터 타입 이름이 사용되는 모든 장소에서 사용할 수 있다.
--> 상수 인터페이스 안티패턴(Constant Interface Antipattern): Effective Java Programming Language Guide (저자: Joshua Bloch)
static import: 기존의 import문은 클래스를 대상으로 한다면 static import 구문은 정적 메소드와 필드로 확장한 것이다. 즉, 상속을 사용하지 않으면서 static 멤버가 정의되어 있는 클래스 이름을 지정하지 않고서도 static 멤버를 현재 클래스의 멤버 및 메소드 처럼 사용할 수 있도록 한다.
- import static 사용하고자_하는_static_멤버이름
static import문을 언제 사용할 것인가?
-> static import는 static 멤버가 하나 이상의 클래스에서 자주 사용될 경우에 유용하다.
static import를 너무 자주 사용할 경우의 문제점
-> 코드의 가독성과 유지보수가 어렵다
-> static 멤버가 어떤 클래스에 정의되어 있는지에 대해 알수없기 때문 : Full Path import문을 작성하면 해결가능
-> 특히 와일드 카드를 사용하는 경우는 더 알 수가 없다.
- 비정형인자(VARARGS: Variable-Length Arguments)
배경
여러 개의 인스턴스를 한 메소드에 아규먼트로 전달해야 하는데, 컴파일 시점에 몇 개의 인스턴스를 전달해야 하는지 모를 경우 -> 배열 또는 컬렉션 이용
주어진 수의 합을 구하는 sum() 메소드를 만들 경우
** 메타 데이터(Metadata)
- 메타데이터(Metadata) 소개
J2SE 5.0 이전에 제공되었던 주석 메커니즘: transient(필드는 직렬화되지 말아야 한다.), @deprecated javadoc 태그(이 메소드는 더 이상 사용되지 않는다.)
JSR-175 Metadata에서 '@' 표시는 주석(Annotation)을 의미한다.
썬에서 메타 데이터라는 명칭을 제시했지만, 프로그램 주석 기능(Program Annotation Facility)라는 용어도 많이 사용되고 있다.
Metadata: 데이터의 데이터, JSR175의 목적은 자바 언어에서 메타데이터의 기능을 제공하는 것임
Annotation: 클래스, 메소드, 필드, 파라미터, 변수, 생성자 함수, 패키지를 꾸미기 위해 사용된 특별한 종류의 자바 구성 요소 중 하나임, JSR-175에서 메타데이터를 제공하기 위해 선택한 수단임
Attribute: 주석(Annotation)에서 명시된 특정 메타데이터 아이템, 가끔 주석(Annotation)과 바꾸어 사용되기도 함.
- 주석(Annotation)
주석타입(Annotation Type)정의
1) 정의방법
-> 주석 타입 선언은 인터페이스를 선언하는 방법과 유사하다.
-> 주석 타입 선언 시에는 interface 키워드 앞에 @ 기호를 붙입니다.
메소드 선언시 중의해야 할 점
- 메소드는 파라메터를 가질 수 없다.
- 메소드는 throws 절을 가질 수 없다.
- 메소드의 리턴 타입은 프리미티브(Primitive), String, Class, 열거형(Enum), 주석, 앞에서 열거한 타입들의 배열만 사용할 수 있다.
- 메소드는 기본값(Default Value)를 가질 수 있다.
2) 정의 형식
@interface 주석이름 {
멤버1 [default 디폴트값];
멤버2 [default 디폴트값];
...
}
3) 정의 예
디폴트 값을 사용하지 않는 경우
@interface ExamAnnotation {
String str();
int val();
}
디폴트 값을 사용하는 경우
@interface ExamAnnotation {
String str(); default "테스트 디폴트 값";
int val() default 1000;
}
-
마커 주석 타입(Marker Annotation Type)
멤버가 없는 주석 타입을 마커 주석 타입이라고 한다.
- 하나의 멤버만 가지고 있는 주석
-
주석 보류 정책(Annotation Retention Policy)
주석 보류 정책은 주석을 어떤 시점에 무시하고 처리하지 않을 지를 결정한다.
java.lang.annotation.RetentionPolicy
SOURCE: 주석은 소스 파일에서만 사용 가능. 즉, 컴파일 시점에는 주석을 무시하고 처리하지 않는다.
CLASS: 주석은 컴파일을 하는 동안 .class 파일에 저장된다. 하지만 실행되는 동안 JVM이 사용할 수는 없다.(기본)
RUNTIME: 주석은 컴파일 되는 동안 .class파일에 저장되고 실행 중 JVM에 의해 사용 가능하다.
- 주석 정보 얻기
- 빌트인 주석(Built-in Annotation)
java.lang.annotation 패키지
@Documented
툴에게 " 이 주석은 문서화되어져야 한다" 는 것을 알려주기 위해 사용되는 마커 인터페이스(Marker Interface)이다.
다른 주석 선언(Annotation Declaration)을 주석하기 위해서만 사용된다.
@Inherited
다른 주석 선언(Annotation Declaration)에서만 사용이 가능한 마커 주석(Marker Annotation)이다.
클래스 선언에 사용된 주석에만 영향을 준다.
@Inherited는 슈퍼 클래스에서 사용된 주석이 서브 클래스로 상속되도록 한다.
@Retention
주석을 어떤 시점에 무시하고 처리하지 않을 지를 결정하고자 할 때 사용된다.
다른 주석의 주석으로만 사용된다.
@Target
주석이 어떤 타입의 선언에 적용할 수 있도록 할 것인지를 지정하기 위해 사용된다.
다른 주석의 주석으로만 사용한다.
@Target은 하나의 아규먼트를 가진다. 이 아규먼트의 값은 ElementType 열거형에 지정된 상수 중 하나이어야 한다.
ANNOTATION_TYPE : 주석 타입(Annotation Type)선언
CONSTRUCTOR : 생성자 함수 선언
FIELD: 필드 선언(enum 상수 포함)
LOCAL_VARIABLE: 로컬 변수 선언
METHOD: 메소드 선언
PACKAGE: 파라메터 선언
TYPE: 클래스, 인터페이스 (주석타입(AnnotationType)포함), enum선언
java.lang 패키지
@Deprecated
마커 주석이다.
이 주석이 사용된 선언은 이제 더 이상 사용이 안되고 더 새로운 형태로 대체되었다.라는 것을 나타낸다.
@Override
메소드에서만 사용 가능한 마커 주석이다.
@Override으로 주석 처리된 메소드는 반드시 오버라이딩되어야 한다.
오버라이딩 하지 않을 경우 컴파일 에러가 발생한다.
@SuppressWarnings
컴파일러에 의해 발생하는 하나 이상의 경고를 감추고자 할 때 사용된다.
** JVM
- 메소드 인라이닝(Method Inlining)
, "즉시 처리하는"이라는 뜻이다.
, 메소드 호출이 있을 때, 통상적으로 메소드 호출은, 메소드 본문이 위치한 주소로 분기하여 명령을 수행한 다음, 다시 함수를 부른 위치로 되돌아오는 구조를 취한다.
, "인라인으로 처리한다" 는 말은 컴파일 과정에서 메소드 본문 자체를 메소드를 부른 위치에 삽압하는 것을 의미한다.
, 따라서 수행되는 명령의 내용 자체에는 영향을 미치지 않지만, 메소드 호출시에는 레지스터를 세이브하고, 인자를 전달하고, 리턴 주소를 스택에 넣는 등 무시할 수만은 없는 오버해드가 따른다.
, 이를 인라인으로 처리함으로써, 이러한 오버헤드를 제거하여 수행성능 향상을 가져올 수 있다. 특히, 길이가 짧고 자주 호출되는 메소드일수록 인라인으로 처리하면 성능향상이 더 크게 된다.
- SSA(Static Single Assignment): SSA는 모든 변수에 값이 단 한번 할당되도록 하는 IR로 80년대 IBM에서 개발되었다.
실행엔진(Execution Engine)
(2) 런타임 데이터 영역(Runtime Data Area)
프로그램 카운터 레지스터
-> JVM은 멀티 스레드 환경을 지원한다.
-> 각각의 스레드는 자신의 프로그램 카운터(Program Counter:PC) 레지스터를 가진다.
-> 현재 실행중인 메소드가 네이티브가 아닐 경우, PC 레지스터는 현재 실행중인 JVM 명령어의 주소를 가지고 있다.
-> 현재 실행중인 메소드가 네이티브일 경우, PC 레지스터의 값은 정의되 않는다.
자바 & 네이티브 스택 영역(Java and Native Stack Area)
-> 각각의 실행 중인 스레드는 자신의 스택 영역을 가지고 있다.
-> 스레드가 메소드를 호출할 때마다 프레임을 생성해 스택에 저장한다.
-> 프레임은 메소드 호출시 필요한 리턴 주소, 리턴 값, 아규먼트, 로컬 변수를 저장할 공간을 가지고 있다.
힙 메모리(HEAP Memory)
-> JVM은 힙을 제공하고 있고 힙은 모든 JVM 스레드에 의해 공유된다.
-> 힙은 런타임에 모든 클래스 인스턴스와 배열이 할당되는 데이터 영역이다.
-> 힙은 JVM이 구동될 때 생성되어진다.
-> 객체를 위한 힙 저장 공간은 가비지 컬렉터에 의해 회수된다.
-> 가비지 컬렉터는 힙 메모리에서 사용되지 않는 객체를 감지하고, 메모리를 회수하고, 프로그램에서 메모리를 다시 사용할 수 있도록 만들어 주는 역할을 담당한다.
메소드 영역(Method Area)
-> 메소드 영역은 논리적으로 힙과 연관되어 있다.
-> 메소드 영역은 모든 JVM 스레드 간 공유가 된다.
-> 메소드 영역에는 클래스 구조가 저장된다.
-> 메소드 영역은 VM 구동 시 생성되어 진다.
힙 이외의 메모리(Non HEAP Memory)
-> 힙 이외의 메모리는 JVM 구현에 따라 다르다.
-> 예를 들어, 핫스팟 컴파일러는 자주 실행되는 코드를 저장하기 위해 코드 캐쉬(Code Cache)라는 메모리 영역을 사용한다.
(3) 실행코드(Execution Code)
어플리케이션 코드의 구성 요소
클래스 파일
-> 클래스 파일은 컴파일된 소스 코드이다.
-> 클래스 파일은 데이터 구조와 메소드 실행 코드 정보를 가지고 있다.
-> 메소드 실행 코드는 바이트 코드 형식으로 되어 있다.
-> 하드웨어 프로세스는 바이트 코드를 바로 실행시킬 수 없다.
-> 바이트 코드를 네이티브 코드로 변환시키는 작업은 실행 엔진(Execution Engine)의 몫이다.
네이티브 라이브러리 (Native Libarary)
-> 네이티브 라이브러리는 하드웨어 프로세서가 바로 실행시킬 수 있는 포맷으로 되어 있다.
-> 실행 엔진(Execution Engine)은 실행을 위해 필요한 네이티브 라이브러리 함수의 포인터 정보를 하드웨어 프로세서에게 전달해 주는 역할만 수행하면 된다.
(4) 실행코드 인터페이스(Execution Code Interface)
클래스 로더 서브시스템(Class Loader Subsystem)
-> 클래스 로더 서브시스템은 클래스를 로딩하고 검증하고, 어플리케이션을 초기화하는 역할을 수행한다.
-> 클래스가 로딩되면, 클래스 로더는 클래스 파일을 해석하고(Interprete), 런타임 데이터 영역에 존재하는 힙(Heap)과 힙 이외의 영역(Non-Heap Area)에 데이터 구조를 생성한다.
네이티브 인터페이스(Native Interface)
-> 네이티브 인터페이스는 JVM이 네이티브 라이브러리를 액세스할 때 필요한 인터페이스와 네이티브 라이브러리가 런타임 데이터 영역에 있는 데이터 구조를 액세스 하기 위해 필요한 인터페이스를 정의하고 있다.
가비지 컬렉션(Garbage Collection)
-> JVM에서 모든 객체는 힙 메모리 영역에 생성 및 저장된다.
-> 가비지 컬렉션은 프로그램에서 사용하지 않는 메모리를 회수하고, 프로그램에서 메모리를 다시 사용할 수 있도록 만들어 주는 과정을 의미한다.
-> JDK 5.0 핫스팟 VM에서 가비지 컬렉터는 엄격한 세대간 복사 컬렉터(Accurate Generational Copying Collector)로, 매무 큰 힙 영역에 대해서도 잘 동작한다.
심화: 자바가 처음 개발되었을 때 사용했던 가비지 컬렉션 소개
자바가 처음 개발되었을 때, JDK는 Mark-and-sweep Garbage Collector가 탑재되었다.
Mark-and-sweep Garbage Collector는 다음 두 단계로 수행된다.
1단계 Mark: 쓰레기 개체를 인식한다.
2단계 Sweep: 쓰레기 개체의 메모리를 회수한다.
쓰레기 개체들은 현재 어플리케이션 스택 프레임으로부터의 참조를 돌아다님으로써 확인한다.
Mark and sweep은 세계를 멈추는(Stop-the-world) GC 기법이다. 즉, 모든 어플리케이션 스레드는 GC가 완료되거나 높은 우선순위의 스레드가 GC 스레드를 방해할 때까지 정지한다.
Garbage Collector가 다른 스레드에 의해 방해되면, Garbage Collector는 다시 사직해야 하고, 별다른 소득 없이 어플리케이션이 GC를 계속 시도하는 문제(Thrashing)를 이야기할 수 있다.
다른 문제는 많은 어플리케이션들이 "세계를 멈추는"식을 견딜 수 없다는 점이다. 리얼타임에 가까운 해동을 필요로 하거나 많은 수의 트랜잭션 기반의 클라이언트를 서비스하는 어플리케이션의 경우는 특히 그렇다.
JDK 5.0 핫스팟 VM이 지원하는 다른 가비지 컬렉터들
1) Serial Collector: 자바 핫스팟 클라이언트 VM의 디폴트 컬렉터이다.
2) Incremental Collector(Tranin Collector): 대부분 Concurrent Collection으로 대체되어 사용된다.
3) Parallel Collector: 자바 핫스팟 서버 VM의 디폴트 컬렉터로, 작업 처리량(Throghput)을 극대화시키는데 초점을 맞추고 있다.
4) Mostly Concurrent Collector: 어플리케이션의 중지시간을 최소화하는데 초점을 맞추고 있다.
Mark: 쓰레기 개체를 인식, Sweep: 쓰레기 개체의 메모리 회수, Compact: 사용하지 않는 메모리 공간 압축
JVM 최적화를 위해 수행해야 할 작업 단계
1) 컴파일러를 정확히 선택한다.
- 클라이언트 어플리케이션일 경우 클라이언트 핫스팟 컴파일러를 선택한다.
- 서버 어플리케이션일 경우에는 서버 핫스팟 컴파일러를 선택한다.
- 서버 어플리케이션은 서버 클래스 머신에서 실행되야 한다.
- 서버 클래스 머신은 최소 CPU 2개, RAM 2GB인 머신을 말한다.
2) 가비지 컬렉터를 선택한다. (컬렉터 종류는 위 참조)
서버 컴파일러가 실행중인 서버 클래스 머신의 작업 처리량 및 속도를 극대화하기 위해서는 Parallel Gabage Collector를 선택한다.
서버 컴파일러가 실행중인 서버 클래스 머신의 중지시간(Pause Time)을 최대한 줄이기 위해서는 Mostly Concurrent Collector를 선택한다. (머신의 CPU의 개수는 최소한 2개 이상이어야 한다)
3) 선택한 가비지 컬렉터 힙 영역에 대한 환경 구성을 한다.
- Parallel Gabage Collector에서 디폴트로 사용하는 -XX:+UseAdaptivesSizePolicy 옵션을 사용할 경우, 힙 메모리는 가비지 컬렉터가 자동으로 채택된다.
- 힙 메모리를 수동으로 제어하기 위해 사용되는 java 툴 커맨드 라인 옵션
-Xmsn: 구세대의 초기 크기를 조정할 수 있다. 예) -Xms6144k, -Xms6m *k는 Kbytes, m은 Mbytes를 의미한다.
-Xmx: 매개 변수를 사용해서 구세대의 최대 크기를 조정할 수 있다. 예) -Xmx81920k, -Xmx80m
-Xmnn: 신세대의 고정 사이즈를 조정할 수 있다.
-XX: MaxheapFreeRatio=n : 최대 힙 메모리 사용량과 최소 힙 메모리 사용량에 따라 힙 메모리의 사이즈가 적절하게 늘었다 줄었다 한다. 이때 최대로 늘어날 수 있는 메모리 사이즈를 조정할 수 있다. 디폴트는 70이다.
-XX: MinheapFreeRatio=n : 이때 최소로 늘어날 수 있는 메모리 사이즈를 조정할 수 있다. 디폴트는 40이다.
-XX:NewRatio=n: 신/구 세대 사이즈 비율을 조정할 수 있다.
-XX:NewSize : 신세대 사이즈를 조정할 수 있다.
-XX:MaxNewSize 신세대 사이즈를 조정할 수 있다.
-XX:PermSize: 영구 세대(Permanent Generation)의 사이즈를 조정할 수 있다.
-XX:SurvivorRatio=n 에덴과 생존 공간의 사이즈의 비율을 조정할 수 있다.
-XX:+PrintGC : 가비지 컬렉션 정보
** JDK5.0에서는 두개의 실행엔진을 제공하고 있다. (Java HotSopt Client Complier, Java HotSpot Server Compiler)입니다.
10차시. 자바 디버거를 이용한 어플리케이션 디버깅 하기
A. 디버깅을 위해 필요한 정보 수집
1) 어플리케이션을 호스트하는 환경에 대한 정보 수집
2) 어플리케이션의 정지(Failure)재발 가능성에 대한 정보를 수집
3) 어플리케이션의 중지(Failure)에 의해 나타난 증상에 대한 정보를 수집
4) 에러 기록, 스택 정보, 코어 덤프(Core Dump), 로그 파일에 대한 정보 수집
B. 소스 라인과 바이트 코드 명령어간 연관성
1) JVM은 자바 바이트 코드 명령어를 참조하여 자바 프로그램을 실행시킨다.
2) 소스를 단계별로 실행시킬 때 디버거를 이용하여 브레이크포인트를 설정할 때는 소스 코드를 참조하여 설정한다.
3) 디버거 사용법을 잘 이해하기 위해서는 자바 프로그래밍 언어와 바이트 코드 생성에 대한 정보를 이해해야 할 필요가 있다.
4) javap -c 클래스이름(확장자 제외)명령어는 소스 코드에 정의되어 있는 메소드와 연관되어 있는 바이트 코드를 보여준다.
5) 디버깅을 위해 step이란 명령어를 이용하여 프로그램을 단계별로 실행시킬 때, 단계별로 실행되는 명령어는 바이트 코드 명령어이다.
'IT > JAVA' 카테고리의 다른 글
이클립스 설정: 대용량 JSP 작업속도 향상 팁 (0) | 2009.11.27 |
---|---|
iBATIS의 namespace 특성(무시되는 경우) (0) | 2009.05.22 |
심볼릭 링크의 경로를 톰켓(웹)으로 서비스하는 방법 (0) | 2009.02.23 |
받은 파라미터를 모두 input으로 셋팅 예제 (1) | 2008.12.10 |
Java VS .net - 트렌드인가 아니면 잠재능력인가? (5) | 2006.08.09 |