자바 API(System.out.prinln() 뜯어보기🔨)

김민제·2023년 10월 16일
0
post-thumbnail

Java Api

  • java 개발자가 제공하는 명령어
  • 프로그램 개발에 자주 사용되는 클래스 및 인터페이스 모음, 라이브러리라고 부르기도 한다.
  • String 클래스, System 클래스 등도 API에 속하는 클래스임

API 문서

🔨 System.out.println() 뜯어보기 🔨

  • API에 대해 학습하다보니 System클래스에 대해 자주 예시로 드는 글들이 많다
  • 그래서 System.out.println()을 뜯어보며 API문서 참고, 자바 코드 참고 등을 연습해보려 한다!
  • 우리는 자바를 사용하며 System.out.println()라는 코드를 수도 없이 사용한다. 이 한 줄의 문장을 하나하나 뜯어보자

System

  • 우선 System 클래스로 들어가보자
public final class System {
	private static native void registerNatives();
  static {
        registerNatives();
  }
	private System() {
  }
	public static final InputStream in = null;
	public static final PrintStream out = null;
	...
}
  • 위와 같이 구성되어 있었다.
  • 우선 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

public class PrintStream extends FilterOutputStream{
	...
	public void println() {
        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
     */
    public void println(boolean x) {
        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.
     */
    public void println(char x) {
        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.
     */
    public void println(int x) {
        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.
     */
    public void println(long x) {
        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.
     */
    public void println(float x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }
	...
	public void println(String x) {
        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.
     */
    public void println(Object x) {
        String s = String.valueOf(x);
        if (getClass() == PrintStream.class) {
            // need to apply String.valueOf again since first invocation
            // might return null
            writeln(String.valueOf(s));
        } else {
            synchronized (this) {
                print(s);
                newLine();
            }
        }
    }
	...
}
  • 위 코드와 같이 PrintStream클래스는 FilterOutputStream클래스를 상속받고 많은 println 메소드에 대해 오버로딩하여 다양한 타입의 매개변수를 받아 구현하고있다.
  • println 메소드를 살펴보자
public void println(int x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }
  • 위 코드는 int 타입을 출력하고 줄바꿈을 해주는 println메소드이다.
  • 조건식 안의 getClass는 Object 클래스 안에 있는 메소드이다.
    • 근데 얘가 좀 이상한게 자바 파일 안에 있는 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에 저장된 객체의 메모리 주소를 반환하는 메소드인 것 같다.
    • 그러면 다음으로 런타임 클래스를 알아보자

      • 실행 시 JVM에 로드되는 클래스라고 한다.
    • 헷갈려서 간단하게 getClass를 사용해보았다.

      String s = "kimminje";
      System.out.println(s.getClass());
      -> class java.lang.String
      • 현재 변수가 참조중인 클래스를 반환한다.
      • 자바 API 문서에 있는 런타임 클래스를 반환한다는 말이 맞는 것 같은데 일단 나의 실력 성장 후 다시 공부해봐야 할 것 같다…
    • 아니다 getClass는 현재 객체의 클래스를 반환한다고 했다.

    • println은 System.out을 사용하니 위에서의 getClass는 System.out 객체의 클래스를 반환하지 않을까? 시도해보자

      System.out.println(System.out.getClass());
      ->class java.io.PrintStream
      • 이렇게 PrintStream으로 출력되는 것을 알 수 있다.

      • 그러면 println의 메소드로 들어가면 if 문이 참이 되어 실행될 것이다.

      • 이것도 확인해보자

        if (System.out.getClass()== PrintStream.class){
            System.out.println("true");
        }
        ->true
      • true가 출력된 것으로 보아 println의 메소드도 참이 되어 writeln(String.valueOf(x));가 실행되는 것을 알 수 있다.

    • 그렇다면 getClass()가 PrintStream 클래스와 같으니 writeln에 x값을 String으로 변환하여 넣어준다.

    • x값을 특별한 방식으로 출력하기 위해 사용하는 메소드인 것 같은데 구글에 writeln에 대한 정보가 없다….

      public void print(int i) {
              write(String.valueOf(i));
          }
    • 하지만 print 메소드가 이렇게 구성되어 화면에 출력을 해주는 메소드이니 writeln은 출력을 하고 줄바꿈을 해주는 메소드라는 것을 알 수 있다.

  • 그리고 현재 객체가 PrintStream클래스가 아닌 경우는 synchronized block을 통해 thread-safe하게 동작하도록 하고 print메소드를 통해 문자를 출력하고 newLine()메소드를 통해 줄바꿈을 해주는 것을 알 수 있다.
    • synchronized block : Synchronized 키워드는 여러개의 스레드가 한개의 자원을 사용하고자 할 때,현재 데이터를 사용하고 있는 해당 스레드를 제외하고 나머지 스레드들은 데이터에 접근 할 수 없도록 막는 개념

회고..👍

  • 이렇게 System.out.println을 뜯어봤다. 이것만으로도 API문서와 자바 코드, 구글을 찾아보며 2시간 정도를 썼지만 찾아보는 도중의 깨달음과 자바의 문서와 코드 찾아보는 법을 더 잘 알게된 것 같다. 내가 해냈다는게 사실 너무 뿌듯하다. 한번씩 코드를 뜯어보면 내 실력 향상에 좋을 것 같다!
profile
블로그 이전했습니다!! 👉 https://alswp006.github.io/

0개의 댓글

관련 채용 정보