자바는 모든 것이 클래스로 이루어져 있다. 간단한 코드를 작성하는데도 반드시 클래스를 거쳐가야 한다. 그런 것들이 빡빡하게 느껴지기도 한다. 이 클래스란것이 어떻게 작동하고 있는가에 대한 의문을 품게 된다. JVM (자바 가상머신) 의 구조는 어떻게 되어있나도 궁굼해진다.

 

1. java.lang 패키지

 

 의문에 대한 해답을 찾으려면 조상을 찾아가야 한다. 그곳엔 모든 클래스의 상위 클래스 Object 클래스가 있다. 벌써 클래스의 이름부터가 심상치가 않다. Object Oriented Programming 인데 Object 이름을 쓰고 있으니 Object는 클래스의 시작점이다.

 

다음 예제 코드는 항상 사용하던 코드이다. 사용자가 만든 BasicClass가 있다. 이 클래스는 갑자기 하늘에서 내려온 것이 아니라 이미 어떤 클래스로부터 상속을 받고 시작한다. 그것이 Object 클래스 이다. 아무것도 없이 상속을 받을 수 없으니까 패키지를 import 한다. 이 과정은 자바 컴파일러가 자동으로 추가하는 작업이다. 코드상에 보이지 않을 뿐 아래처럼 바뀌어 있다고 봐야한다.

import java.lang.*;

public class BasicClass extends Object{
    public static void main(String[] args){
        System.out.printf("Finding Object Class...");
    }
}

때문에 Object 의 메소드를 기본으로 사용할 수 있다. 메소드들은 아래 documentation 에서 확인할 수 있다.

 

*Object Class Link

 

Object (Java Platform SE 8 )

Request _create_request(Context ctx, String operation, NVList arg_list, NamedValue result, ExceptionList exclist, ContextList ctxlist) Creates a Request instance initialized with the given context, method name, list of arguments, container for the me

docs.oracle.com

java.lang 패키지


또한 java.lang.* 패키지를 import 해서 시작하기 때문에 그 안에 있는 클래스들을 사용할 수가 있다. java.lang 패키지에는 위의 클래스들이 포함된다. String 클래스 System 클래스가 이미 다 포함되어 있다. String 클래스는 본래 다음과 같이 인스턴스를 만든다. 워낙 자주 쓰는 필수적인 클래스이기 때문에 생략이 가능토록 한 것이다.

        String str1 = new String("We are good to go now");
        System.out.println(str1);

 

 

이렇게 인스턴스 str1을 생성하면 여타 클래스들과 사용법이 똑같다. System.out.println 은 System 클래스가 Printstream 클래스의 static 참조변수를 가지고 문자열을 출력하는 것이다.

 

그럼 Object 클래스의 toString 메서드를 사용해보자. toString은 String 으로(to) 변경해주는 메서드다.

public class BasicClass extends Object{
    String name;

    BasicClass(String name){
        this.name = name;
    }
    public static void main(String[] args){
        BasicClass bc1 = new BasicClass("MR KIM DOL");
        System.out.println(bc1);
        System.out.println(bc1.toString());
    }
}

결과값

toString을 출력해보니 클래스의 풀네임과 해시코드가 출력된다. bc1에 들어있는 name을 출력하려면 메소드를 오버라이드 해주면 된다. 코드는 다음과 같다.

public class BasicClass extends Object{
    String name;

    BasicClass(String name){
        this.name = name;
    }

    @Override
    public String toString() {
        return this.name;
    }
    public static void main(String[] args){
        BasicClass bc1 = new BasicClass("MR KIM DOL");

        System.out.println(bc1);
        System.out.println(bc1.toString());
    }
}

 

bc1.toString이나 bc1 이나 값이 같다. 둘다 toString을 호출하기 때문이다.

 

그밖에 Object 클래스의 메소드에는 인스턴스의 주소값을 비교하는 equals 메소드, 객체의 저장 위치를 반환하는 hashCode 메소드 등이 있다. 

 

2. String 클래스

String 클래스도 Object 클래스의 하위클래스이다. (class String extends Object) String 클래스로 Object 클래스를 알아보자.

 

import java.lang.*;

public class BasicClass extends Object{

    public static void main(String[] args){
        String str1 = "I am String.";
        String str2 = "I am String.";

        String str3 = new String("I am String.");

        System.out.println(str1.hashCode());
        System.out.println(str2.hashCode());

        System.out.println(str3.hashCode());
    }
}

상단의 예제의 결과는 어떨까? 3개의 다른 메모리 주소가 나와야 하는 것이 아닌가? 그런데 결과는 다음과 같다.

String 결과값

 

똑같은 주소값(위치)를 반환한다. 이것은 참조변수가 모두 "I am String." 이라는 상수값(리터럴이라 한다)을 가리키고 있다는 말이다.

hashCode()

new 키워드로 인스턴스를 생성한줄 알았는데 아니다. 자바 컴파일러가 변수를 선언하고 초기화할 때 메모리 어딘가에 리터럴을 저장하는 곳으로 연결시켜 놓은 것이다. 문자열이라는게 한번 만들어지면 똑같은 의미를 갖는다. 굳이 새로운 문자열을 새로운 곳에 생성할 필요가 없다. 동일한 문자열을 동일한 주소로 지정하면 메모리도 아끼고 속도도 빨라진다는 관점에서 보면 효율적이라 볼 수 있다. 그럼 기존 문자열에 한글자를 추가해보면?

 

public class BasicClass extends Object{

    public static void main(String[] args){
        String str1 = "I am String.";
        String str2 = "I am String.";

        String str3 = new String("I am String.Z");

        System.out.println(str1.hashCode());
        System.out.println(str2.hashCode());

        System.out.println(str3.hashCode());
    }
}

결과가 달라진다.

해쉬코드값이 달라진다. 이것이 의미하는 것은 "I am String." + "Z" 가 아니라

 

"I am String."

"I am String.Z"

 

두개를 만든다는 것이다. 아래의 설명과 같다.

 

String 설명

리터럴을 저장하는 메모리도 아끼고 인스턴스를 생성하는 수고도 덜겠지만, 문자열을 자르고 붙여야 할 때는 불리하게 된다. Z글자 하나 추가할때에 전체 문장을 다시 써야 된다. 한문장이 아니라 100문장,1000문장이라면...? String 클래스의 장점은 바로 단점이 된다. String은 final 형이기 때문에 한번 생성된 문자열은 변경할 수 없다. 즉 불변한다(Immutable)

 

이런 단점을 보완해 주는 클래스가 StringBuffer 와 StringBuilder 클래스이다. java.lang 패키지에 import 되어 있기 때문에 바로 사용할 수 있다.


*StringBuilder 클래스

import java.lang.*;

public class BasicClass extends Object{

    public static void main(String[] args){

        StringBuilder longText = new StringBuilder("This is adjustable long text. ");
        System.out.println(longText);
        System.out.println("First address : " + System.identityHashCode(longText));

        longText.append("Another line to go.");

        System.out.println(longText);
        System.out.println("First address : " + System.identityHashCode(longText));

        longText.append("\nLastly text goes on. Good Job!");

        System.out.println(longText);
        System.out.println("First address : " + System.identityHashCode(longText));

        String finalString = longText.toString();
        System.out.println(finalString);
        System.out.println("First address : " + System.identityHashCode(finalString));
    }
}

결과값

StringBuilder 클래스의 경우 추가(append) 하는 동안 해시코드가 같다. 마지막에 String 으로 변환 시켰을 때는 달라진다. 해시코드가 같다는 것은 하나의 메모리 주소에 문자열을 연결시킨다는 것을 의미한다. 물리적으로 바로옆에 연결시키거나 아니면 논리적으로 연결시키거나 그런 부분은 자바 컴파일러가 담당하는 부분이다. 사용자 입장에서는 하나의 주소를 가지고 작업을 하는 결과가 된다. 변경작업이 다 끝나고 toString 메소드를 통해 다시 String 문자열로 변경이 된다. 두개의 클래스를 번갈아 가면서 쓰면 기존 String 클래스의 단점을 보완할 수 있다.

 

 

 

Java API 링크

 

Java Platform SE 8

 

docs.oracle.com

 

 

공유하기

facebook twitter kakaoTalk kakaostory naver band