2012년 10월 22일 월요일

Java 에서 Timestamp 찍어보기

new Timestamp(System.currentTimeMillis());
 
이걸 System.out.println() 으로 찍거나.. 하면 된다.
Log4J 같은 라이브러리를 많이 쓰는 요즘이지만, 아쉬울 때 쓰면 될 듯. 

2012년 8월 6일 월요일

Java 에서 객체의 초기화 (2)

>> Java에서 객체의 초기화(1) 에서 이어짐.


 
생성자 (Constructors)

객체 초기화의 중심요소는 '생성자'이다. 자바에서, 생성자는 메서드와 비슷하나, 메서드는 아니다.생성자는 메서드처럼 파라미터를 셋팅할 수 있고, 코드로 된 몸체를 가진다. 그러나 생성자는 return type 이 없어 메서드와는 다르다. 개발자는 메서드와 마찬가지로 생성자에 특정 접근권한을 줄 수 있지만, public, protected, package 접근자가 붙은 생성자는 서브클래스에 상속되지 않는다. (생성자의 access level 은 단순히 생성자 호출가능 여부가 아니라 객체의 인스턴스화를 결정한다.)


생성자의 기초 (Constructor basics)

소스파일에서 생성자는 클래스의 이름과 같은 이름을 가진 메서드 선언(declaration)처럼 보이지만, return type 이 없다. CoffeCup 클래스에서 생성자의 선언은 다음과 같다 :


// In source packet in file init/ex2/CoffeeCup.java   
 
class CoffeeCup {
    // Constructor looks like a method declaration
    // minus the return type
    public CoffeeCup() {
        // Body of constructor
    }
    // ...
}


메서드 처럼, 파라미터의 순서, 개수, 타입의 다양화를 통해 생성자를 오버로드할 수 있다. 두 개의 생성자를 가진 CoffeeCup 클래스는 다음과 같다 :

// In source packet in file init/ex3/CoffeeCup.java   
 
class CoffeeCup {
    private int innerCoffee;
    public CoffeeCup() {
        innerCoffee = 237;
    }
    public CoffeeCup(int amount) {
        innerCoffee = amount;
    }
    // ...
}


객체가 new 연산자로 초기화될 때, 생성자는 반드시 명시되어야 한다. 다음과 같이 두 개의 생성자를 가진 CoffeeCup 클래스는 두가지 방법으로 초기화될 수 있다 :

// In source packet in file init/ex3/Example3.java   
 
class Example3 {
    public static void main(String[] args) {
        // Create an empty cup
        CoffeeCup cup1 = new CoffeeCup();
        // Create a cup with 355 ml of coffee in it
        CoffeeCup cup2 = new CoffeeCup(355);
    }
}



인자가 없는 생성자 (The no-arg constructor)

자바 용어에서 파라미터를 가지지 않은(혹은 인자가 없는) 생성자는 "no-arg constructors"로 불린다. 코드에서 보여지듯, CoffeeCup 객체의 첫번째 인스턴스화는 인자가 없는 생성자에 명시되어있다. 두번째 인스턴스화는 int 형 파라미터를 요구하는 생성자에 명시되어있다.


this() 호출 (The this() invocation)

You may want to do this if you have several overloaded constructors in a class, all of which must execute much of the same code. Here's an example:

생성자로부터, this() 문을 사용함으로서 동일한 클래스에서 또다른 생성자를 명시적으로 호출할 수 있다. 클래스에서 여러 오버로드된 생성자가 있다면, 다음과 같이 모두 거의 동일한 코드를 수행해야만 한다.

// In source packet in file init/ex4/CoffeeCup.java   
 
class CoffeeCup {
    private int innerCoffee;
    public CoffeeCup() {
        this(237); // Calls other constructor
        // Could have done more construction here
    }
    public CoffeeCup(int amount) {
        innerCoffee = amount;
    }
    // ...
}


예와 같이, 인자가 없는 생성자는 int 와 같은 파라미터를 가진 또 다른 생성자를 호출한다. 237 이라는 값을 다른 생성자에 넘김으로서 innerCoffe 에 값을 할당한다.

메서드에서는 this()를 호출할 수 없다. 생성자에서 this()를 호출한다면, 반드시 그것을 생성자 내의 다른 코드 이전에 첫번째로 호출해야만 한다. 그리고, this()는 단 한번만 호출할 수 있다. this()가 호출된 후에 포함된 다른 코드들은 호출된 생성자가 완료된 후에 수행된다.


생성자는 메서드가 아니다.

메서드와 생성자사이의 다른 점은 다음 사실을 살펴보며 설명하겠다 : The name of a class is a valid name for its methods. In other words, class CoffeeCup could have methods named CoffeeCup:
클래스의 이름은 클래스에 속한 메서드의 이름에 적합한 이름이다. 달리말하면, CoffeeCup 클래스는 CoffeeCup 이란 메서드를 가질 수 있다.

// In source packet in file init/ex5/CoffeeCup.java
// THIS WORKS, BUT IT IS AN EXAMPLE OF POOR METHOD NAMING  
 
class CoffeeCup {
    private int innerCoffee;
    public CoffeeCup() {    // The constructor
        innerCoffee = 237;
    }
    public void CoffeeCup() {   // The method
        innerCoffee = 99;
    }
    // ...
}


위의 소스코드에서 CoffeeCup 클래스의 정의를 감안할 때, 다음과 같이 가능하다 :


// In source packet in file init/ex5/Example5.java 
 
class Example5 {
    public static void main(String[] args) {
        CoffeeCup cup = new CoffeeCup(); // invoke the constructor  
        cup.CoffeeCup(); // invoke the method
    }
}


비록 클래스명칭과 같은 이름의 메서드를 만들 수 있다하더라도, 실제론 다른 개발자들이 생성자와 혼동할 수도 있으며, 올바른 메서드 디자인 방식을 깨는 것이기에 결코 그렇게하면 안된다.

첫째, 클래스명은 '동사'가 아니라 '명사'이다.(적어도 '명사'여야 한다.) 메서드명칭은 동사여야 한다. 메서드가 수행하는 동작을 명칭으로 써야한다. 'CoffeeCup'은 동작이 아니다. 또한 'CoffeeCup'은 첫번째문자가 소문자여야 한다는 명명규칙 역시 따르지 않았다. 이 예제의 목적은 단지 생성자와 같은 명칭의 메서드가 생성자와 충돌하지 않는다는 것을 보여줌으로서 생성자는 메서드가 아니다라는 사실을 강조하는 것이다.


기본생성자(Default constructors)

만약 클래스에 생성자가 선언되어있지 않다면, 컴파일러는 자동으로 기본생성자(Default Constructor)를 생성한다. 기본 생성자는 파라미터가 없으며(no-arg constructor), 빈 (코드)몸체를 가진다. 개발자가 명시적으로 생성자를 선언하지 않았을 경우, 컴파일러가 자동적으로 기본생성자를 생성해주기 때문이다. 모든 클래스는 최소한 하나의 생성자가 생성된다.  예를 들어, CoffeeCup 클래스에 생성자가 선언되어있지 않은 경우는 다음과 같다. :



// In source packet in file init/ex6/CoffeeCup.java

class CoffeeCup {
    private int innerCoffee;
    public void add(int amount) {
        innerCoffee += amount;
    }
    //...
}


컴파일러는 마치 개발자가 명시적으로 빈 몸통을 가진 인자가 없는 생성자(no-arg constructor)를 명시적으로 선언한 것과 같은 클래스파일을 생성할 것이다. :


// In source packet in file init/ex7/CoffeeCup.java

class CoffeeCup {
    private int innerCoffee;
        public CoffeeCup() {
    }
    public void add(int amount) {
        innerCoffee += amount;
    }
    //...
}

컴파일러는 클래스 자체의 접근레벨과 동일한 권한을 가진 기본생성자를 제공한다. 이전 예제처럼 CoffeeCup 클래스는 public 이며, 기본생성자도 public 이다. 만약 CoffeeCup 클래스가 package 레벨의 접근권한을 가지고 있다면, 기본 생성자 역시 그러할 것이다.



>>  Java 에서 객체의 초기화 (3) 으로 이어짐.

2012년 7월 24일 화요일

Java 에서 객체의 초기화 (1)

※ 본 글의 출처는 Object initialization in Java 이며, 일종의 번역본이라 할 수 있다. 저작물번역에 대한 허가는 받지 않았으므로, 문제가 될 경우 삭제될 수 있다.

객체는 메모리를 조작하는 코드로 묶여진 메모리 덩어리다. 메모리에서, 객체는 생존시간(lifetime) 동안 객체가 변화하는 등의 상태(인스턴스 변수들의 값)를 유지한다. 정상적으로 새롭게 생성된 객체를 얻으려면, 객체에 새롭게 할당된 메모리는 반드시 적절한 초기상태로 초기화되어야 한다.이 문서는 적절한 초기화를 위한 클래스의 설계에 중점을 두었다. 이제 우리는 객체 초기화관리에 사용되는 메커니즘을 심층적으로 실펴볼 것이다.


자바의 초기화 기법 이면의 동기(motivation)

객체생명의 시작에서 자바가상머신(이하 JVM)은 객체의 인스턴스 변수들에게 제공된 힙에 충분한 메모리를 할당한다. 메모리가 최초 할당될 때, 그것이 데이터를 포함하였는지 예측할 수 없다. 만약 메모리가 그대로 사용된다면, 객체의 행동 또한 예측할 수 없다. 따라서 객체가 다른 코드들에 의해 사용되기 전에 이러한 시나리오를 방지하기 위해 자바는 최소한의 예측가능한 초기화 값으로 메모리를 초기화 한다.

초기화되지 않은 데이터는 일반적인 버그의 원인이 때문에, 초기화는 중요하다. 예를 들어, 데이터의 적절한 초기화를 강요하는 기법이 적용되지 않은 C의 경우에는 초기화되지 않은 데이터로 인한 버그가 정기적으로 발생한다. C 프로그래머는 항상 반드시 데이터가 메모리에 할당되고, 사용되기 이전에 데이터의 초기화를 기억해야만 한다. 이와 대비되어 자바는 새롭게 객체가 생성되며 차지하는 메모리의 적절한 초기화가 보장되도록 개발자에게 도움을 주는 기법을 내장하고 있다. 이들 기법의 적절한 사용은 개발자가 설계한 객체가 부적합한 초기상태를 가지고 생성되는 것을 방지한다.

자바는 객체의 적절한 초기화를 보장하기 위한 다음과 같은 세가지 기법을 가진다 :
instance initializers (인스턴스 초기화 블록으로 부르기도 한다.)
instance variable initializers
constructors. (Instance initializersinstance variable initializers 를 통틀어 "initializers" 라고도 한다.)
(Note. "Intializers"는 초기화 장치 쯤으로 해석하면 되지 않을까 했지만, 정확한 우리말이 어려워 번역하지 않았음.)
 이 모든 기법은 객체가 생성될 때 자동으로 수행되는 자바코드라 할 수 있다. 개발자가 new 연산자나 Class 클래스의 newInstance() 메서드를 써서 새로운 객체를 위한 메모리를 할당할 때, JVM은 개발자가 새롭게 할당된 메모리를 사용할 수 있게되는 시점 전에 초기화 코드의 수행을 보장한다. 만약 클래스들이 새롭게 생성된 객체를 위한 적절한 상태를 제공하는 initializer 나 생성자를 언제나 가지도록 설계된다면, 적절하게 초기화되지 못한 객체를 생성하거나 사용할 방법이 없을 것이다.


기본 초기값들(Default Initial Values)

만약 개발자가 인스턴스 변수들을 명확하게 초기화하지 않는다면, 해당 변수의 타입에 근거하여 변수들에 예측가능한 기본 초기값들이 부여될 것이다. 표1 은 각 변수형에 따른 기본 초기값을 보여준다. (기본 초기값들은 인스턴스 및 클래스 변수들 둘 다를 위한 것이다. 지역변수에는 기본초기값이 주어지지 않는다. 지역변수들은 사용하기 전에 반드시 명확한 초기화를 해야만 한다.)

Type
Default Value
booleanfalse
byte(byte) 0
short(short) 0
int0
long0L
char\u0000
float0.0f
double0.0d
object referencenull
표1. Default value for fields

만약 개발자가 인스턴스 변수를 명시적으로 초기화하지 않았다면, new 연산자가 객체의 참조를 반환할 때 변수는 기본 초기값을 가질 것이다. 예를 들어, 여기 innerCoffee 라는 명시적으로 초기화되지 않은 필드를 가진 CoffeeCup 이라는 클래스가 있다. (클래스의 생성자나 initializer 도 없다.) :

// In source packet in file init/ex1/CoffeeCup.java           
// This class has no constructors or initializers

class CoffeeCup {

    private int innerCoffee;
    //... 
}


결과적으로 새로운 CoffeeCup 객체의 참조가 new 연산자에 의해 최초로 리턴되었을 때, innerCoffee 필드는 기본 초기값을 가지고 있을 것이다.

개발자가 명시적으로 100 이란 값으로 innerCoffee 필드를 초기화하는 것의 의미는, CoffeeCup 객체가 생성될 때 innerCoffe 에는 두 번의 효과, 두 번의 초기화가 일어날 것이란 이야기다. 첫번째로 innerCoffee 는 기본 초기값인 0 이 주어질 것이며, 다음으로 0 에 적절한 초기값인 100 으로 덮여씌여질 것이다. 이 모든 일은 JVM 이 새로운 객체를 생성하는 동안 일어난다. (새로운 객체의 참조를 리턴하기 전에...) new 연산자로부터 반환된 새로운 CoffeeCup 객체가 참조되는 시점에 innerCoffee 필드는 100 으로 셋팅될 것이다.



>>  Java 에서 객체의 초기화 (2) 로 이어짐.