우선 System이라는 클래스에 registerNative라는 변수가 private static native void 형태로 저장이 되어있습니다.
private : 접근 제한자이며 변수가 클래스 내부에서만 사용될 수 있게 합니다.
static : 클래스가 메모리에 올라갈 때 static 영역에 고정적으로 할당된다. 객체 생성 없이도 사용 가능합니다.
native : 자바로 구현하기 까다로운 것을 다른 언어로 구현하여 자바에서 사용하기 위한 키워드입니다.
registerNative 메소드가 static 메모리에 올라가 있습니다.
registerNative : Java와 C++ 함수 간의 매핑을 생성하는 데 사용되는 JNI(Java Native Interface)메소드, 인수로 전달된 클래스에 네이티브 메소드를 등록합니다.
System클래스의 기본 생성자가 private으로 선언되어 있습니다.
out 필드가 public static final을 가지고 PrintStream타입으로 null값을 가지고 있습니다.
public : 모든 패키지에서 사용 가능
final : 상수값, 변경 불가능
정리해보면 out 필드는 모든 패키지에서 사용 가능고 인스턴스를 생성하지 않고 사용해야하며 변경이 불가능한 값입니다.
그리고 out필드는 PrintStream 타입으로 선언되어 있는데 PrintStream을 살펴보겠습니다!
PrintStream
publicclassPrintStreamextendsFilterOutputStream{...publicvoidprintln(){newLine();}/**
* Prints a boolean and then terminates the line. This method behaves as
* though it invokes {@link #print(boolean)} and then
* {@link #println()}.
*
* @param x The {@code boolean} to be printed
*/publicvoidprintln(booleanx){if(getClass()==PrintStream.class){writeln(String.valueOf(x));}else{synchronized(this){print(x);newLine();}}}/**
* Prints a character and then terminates the line. This method behaves as
* though it invokes {@link #print(char)} and then
* {@link #println()}.
*
* @param x The {@code char} to be printed.
*/publicvoidprintln(charx){if(getClass()==PrintStream.class){writeln(String.valueOf(x));}else{synchronized(this){print(x);newLine();}}}/**
* Prints an integer and then terminates the line. This method behaves as
* though it invokes {@link #print(int)} and then
* {@link #println()}.
*
* @param x The {@code int} to be printed.
*/publicvoidprintln(intx){if(getClass()==PrintStream.class){writeln(String.valueOf(x));}else{synchronized(this){print(x);newLine();}}}/**
* Prints a long and then terminates the line. This method behaves as
* though it invokes {@link #print(long)} and then
* {@link #println()}.
*
* @param x a The {@code long} to be printed.
*/publicvoidprintln(longx){if(getClass()==PrintStream.class){writeln(String.valueOf(x));}else{synchronized(this){print(x);newLine();}}}/**
* Prints a float and then terminates the line. This method behaves as
* though it invokes {@link #print(float)} and then
* {@link #println()}.
*
* @param x The {@code float} to be printed.
*/publicvoidprintln(floatx){if(getClass()==PrintStream.class){writeln(String.valueOf(x));}else{synchronized(this){print(x);newLine();}}}...publicvoidprintln(Stringx){if(getClass()==PrintStream.class){writeln(String.valueOf(x));}else{synchronized(this){print(x);newLine();}}}/**
* Prints an Object and then terminates the line. This method calls
* at first String.valueOf(x) to get the printed object's string value,
* then behaves as
* though it invokes {@link #print(String)} and then
* {@link #println()}.
*
* @param x The {@code Object} to be printed.
*/publicvoidprintln(Objectx){Strings=String.valueOf(x);if(getClass()==PrintStream.class){// need to apply String.valueOf again since first invocation// might return nullwriteln(String.valueOf(s));}else{synchronized(this){print(s);newLine();}}}...}
위 코드와 같이 PrintStream클래스는 FilterOutputStream클래스를 상속받고 많은 println 메소드에 대해 오버로딩하여 다양한 타입의 매개변수를 받아 구현하고 있습니다.
근데 얘가 좀 이상한게 자바 파일 안에 있는 getClass메소드 설명과 API 문서에 있는 설명이 다릅니다…
자바 파일에는 이렇게 적혀있습니다.
Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by java.util.HashMap.
‘객체에 대한 해시 코드 값을 반환합니다. 이 메서드는 java.util.HashMap과 같은 해시 테이블에서 사용될 목적으로 지원됩니다.’
다음은 API 문서에 있는 설명입니다.
Returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class.
‘이 Object의 실행 클래스를 반환합니다. 반환된 Class 객체는 표시된 클래스의 static synchronized 메서드에 의해 잠겨진 객체입니다.’
그럼 우선 hash code에 대해 알아보겠습니다.
해시코드를 간단하게 말하면 해시 알고리즘에 의해 생성된 정수 값입니다.
int hashCode()로 정의된 hashCode 메소드는 실행 중에(Runtime) 객체의 유일한 integer값을 반환합니다. Object 클래스에서는 heap에 저장된 객체의 메모리 주소를 반환하도록 되어있습니다.
hashCode는 HashTable과 같은 자료구조를 사용할 때 데이터가 저장되는 위치를 결정하기 위해 사용됩니다.
찾아보니 위와 같은 설명이 있습니다. 정리해보면 객체의 유일한 Integer값, 그러니까 heap에 저장된 객체의 메모리 주소를 반환하는 메소드인 것 같습니다.
x값을 특별한 방식으로 출력하기 위해 사용하는 메소드인 것 같은데 구글에 writeln에 대한 정보가 없습니다…
publicvoidprint(inti){write(String.valueOf(i));}
하지만 print 메소드가 이렇게 구성되어 화면에 출력을 해주는 메소드이니 writeln은 출력을 하고 줄바꿈을 해주는 메소드라는 것을 알 수 있습니다.
그리고 현재 객체가 PrintStream클래스가 아닌 경우는 synchronized block을 통해 thread-safe하게 동작하도록 하고 print메소드를 통해 문자를 출력하고 newLine()메소드를 통해 줄바꿈을 해주는 것을 알 수 있습니다.
synchronized block : Synchronized 키워드는 여러개의 스레드가 한개의 자원을 사용하고자 할 때,현재 데이터를 사용하고 있는 해당 스레드를 제외하고 나머지 스레드들은 데이터에 접근 할 수 없도록 막는 개념
정리..👍
이렇게 System.out.println을 뜯어봤습니다. 이것만으로도 API문서와 자바 코드, 구글을 찾아보며 2시간 정도를 썼지만 찾아보는 도중의 깨달음과 자바의 문서와 코드 찾아보는 법을 더 잘 알게된 것 같습니다. 제가 해냈다는게 사실 너무 뿌듯한 것 같습니다. 한번씩 코드를 뜯어보면 제 실력 향상에 좋을 것 같습니다!