본문 바로가기
카테고리 없음

[Java] 당신이 빠뜨릴 수 있었던 사사로운 Java 지식들 2 - 객체 그냥 출력하면? System.out.println() 간단히 해부해 보기!

by 개발자만타 2023. 7. 23.

양심고백컨데, 저는 이 사실을 정말 최근에 알았습니다. ssafy 에서 자바 수업을 듣기 전에는, "객체를 출력하면 그냥 당연히 ObjectName@1234 형식과 같이 나오는 줄 알고 있었습니다. 하지만, 충격적이게도 아니었습니다.

Object 의 toString() method 를 overriding 해 준다면, 단순히 객체를 출력해도 원하는 방식으로 나오게 할 수 있다 는 사실을 저는 몇일 전에 알았습니다. 결과를 먼저 보여드립니다.

Code :

public class LittleTest {
    public static void main(String[] args) {
        TestObject object = new TestObject(0, "네가 넣은 정보");
        System.out.println(object);
    }
}

class TestObject{
    int data;
    String testString;

    public TestObject(int data, String testString) {
        this.data = data;
        this.testString = testString;
    }

    @Override
    public String toString() {
        return "네가 그냥 객체를 출력해도, 나는 " + this.testString + " 을 돌려줄거야!";
    }
}

Result :

어찌 이런 일이 가능한 것일까요? 직접 구현체를 뜯어 가며, 설명해 드리려 합니다.

1. 먼저 System.out.println() 은 어떻게 생겼기에 이런 일이 가능할까?

여러분들도 잘 아시다시피, 자바의 대표적인 출력문인 System.out.println() 은 여러 개의 method 로 overloading 되어 있는 함수입니다.

System.out.println() 에 들어갈 수 있는 다양한 파라미터들입니다.

우리는 여기서, @Nullable String x@Nullable Object x 부분이 어떻게 구현되어 있는지 확인해 보려고 합니다.

 

1.1 System.out

 

System.out 의 out 을 확인하기 위해 들어가 보면, public static final PrintStream out 이라는 문구를 확인할 수 있습니다. 자세히는 들여다보지 못했지만, Java 를 구성하고 있는 native 예약어의 함수가 out 을 어떻게든 세팅하고 있어서 우리가 System.out 을 활용하고 있는 것이 아닐까 싶습니다. 혹시나 이에 대해서 더 궁금하신 분들이 많으시면, 다시 포스팅하도록 하겠습니다!

2. String 을 출력하는 System.out.println()

System.out 이 PrintStream 객체로 이루어져 있어, PrintStream 객체의 내부 메소드를 따라가면서 보려고 합니다. 

 

아직 저도 Thread 부분을 많이 공부하지는 못했지만, synchronized(this) 예약어 조합은, 두 Thread 가 동시에 System.out 객체를 사용하지 못하게 방지하고 있는 부분인 것 같습니다. 

 

이후, print(x) 와 newLine() 으로 이루어지는 것으로 보아서는, 출력 이후 개행 문자로 넘어가는 것처럼 보입니다. 그래도 print() 가 궁금하니, 보고 넘어가고자 합니다.

 

알고 보니, 그냥 System.out.print() 의 print() 와 같은 method 임을 확인할 수 있었습니다. 더 타고 들어가 보도록 하겠습니다.

 

내용을 대충 보아하니, I/O 가 열려 있는지 확인하고, BufferedWriter 의 객체인 textOut 객체의 write() method 호출 후 flushBuffer() 를 두번 그리고, out 객체의 flush() 메소드를 사용하고 있다는 것을 볼 수 있습니다. 와중에 System.out.print() 조차도 BufferedWriter 를 사용하고 있네요! 여기서 더 들어갔다가는, 글의 본질에 맞지 않은 내용일 것 같아서 더 파고들지는 않도록 하겠습니다. 

 

어찌 되었던, String x 를 출력하는 방식을 대충 파악해 볼 수 있었습니다.

 

3. Object 를 출력하는 System.out.println()

 

아주 중요한 단서를 발견했다는 생각이 들지 않을 수 없었습니다. String 에서의 System.out.println() 과 차이가 나는 유일한 부분은, String s 변수를 초기화하는 부분입니다. Object x 를 받고 있는데, String.valueOf(x) method 를 사용하여 String 으로 변환해 주고 있습니다. 더 파고 들어가 보도록 하겠습니다.

 

String.valueOf() method 입니다. obj 가 null 인지 체크하고, null 이 맞다면 "null" 을 돌려주며, 그렇지 않다면 obj.toString() 을 돌려주는 것을 볼 수 있습니다. 

 

여기서, 만약에 toString() 을 Overriding 하지 않은 케이스에서는 어떻게 출력되는지 확인하러 들어가 보도록 합니다.

Object.toString() 메소드. Overriding 하지 않았다면, 위의 케이스처럼 출력되게 짜여져 있다.

우리가 단순히 객체를 출력할 때, 익히 알듯이 객체이름 + @ + 객체번호 라고 알고 있었던 방식이 Object.toString() method 안에 녹아 있는 것을 확인할 수 있었습니다.

 

하지만, Java 에서는, 부모 클래스의 method 를 자식 클래스에서 Overriding 해서 사용할 수 있다 고 우리는 알고 있습니다. 그렇기에, 단순히 객체만 넣었을 때, System.out.println() 의 파라미터로, 모든 객체들의 부모 클래스인 Object 클래스로 System.out.println() 이 작동되는 것입니다. 그리고 실행되는 중간에 제가 Override 해 둔 TestObject 의 toString() method 가 Object.toString() 대신에 작동하였기 때문에, 우리는 위와 같은 결과를 얻을 수 있었던 것입니다. 

 

 

 

Java 에서 기본적으로 구현되어 있었던 method 를 파고 들다보면 참으로 신기한 것 투성이인 것 같습니다. 혹시나, 그냥 배워서 외우고 계셨던 분들께 "이렇게 작동하는구나!" 를 보여드릴 수 있었던 글이 되었기를 소망합니다.