각종 후기/우아한테크코스

[우아한 테크코스 3기] LEVEL 1 회고 (18일차)

제이온 (J.ON) 2021. 2. 19.

안녕하세요? 제이온입니다.

 

오늘은 미션에 대한 피드백이 아직 없어서 상대적으로 널널한 하루를 보냈던 것 같습니다. 오랜만에 독서도 좀 하고 정보처리산업기사도 공부하였습니다.

 

 

제이슨의 Java 수업

오늘은 문자열, List, 제네릭, Enum에 대해서 배웠습니다.

 

먼저, 우리가 잘 알고 있는 문자열인 String에서 여러 가지 쏠쏠한 지식을 챙겨갈 수 있었습니다. String을 생성하는 방법은 아래와 같이 2가지가 있습니다.

 

 

String a = "j.on";
String b = new String("j.on");

 

 

두 개의 차이가 뭘까요? 후자는 새로운 메모리를 할당하므로 위 두 개를 '=='으로 비교하면 false가 나오게 됩니다. 만약, b도 "j.on"으로 할당되었고 '=='으로 a와 b를 비교한다면 true가 나옵니다.

 

따라서 String을 만들 때 new 키워드를 사용하지 말라고 알려주었습니다. 하지만, 보통 우리는 문자열 비교를 할 때 '=='는 지양하고 equals()를 이용하여 내용을 비교합니다. 그래서 단순히 '=='로 비교하지 말라는 이유만으로 String을 만들 때 new 키워드를 사용하지 말라는 것에는 납득이 가지 않았습니다.

 

구글링을 해 보니까 성능 차이라는 이유 하나를 알 수 있었습니다. 우선, String Pool이라는 개념을 알아야 합니다. 간단하게 말하면 String Pool은 자주 사용하는 문자열이 저장되는 공간을 말합니다.

 

 

String a = "j.on";
String b = "j.on";
String c = new String("j.on");

 

 

처음 a에 할당할 때 String Pool에 "j.on"이 없으므로 String Pool에 이 문자열을 등록한 다음 a에 할당합니다. 그리고 b의 경우는 String Pool에 "j.on"이 있으므로 String Pool에 있는 "j.on"을 참조하게 됩니다.

 

반면 c의 경우는 new 키워드를 통해 새로운 메모리를 할당하므로 String Pool이 아닌 힙 영역에 새로운 메모리를 생성하게 됩니다. 따라서 a, b와는 다른 주소가 할당되므로 a와 b는 '=='로 비교하면 true가 나오지만 a 혹은 b와 c는 '=='로 비교하면 false가 나오는 것입니다.

 

 

그렇다면, String Pool은 왜 필요할까요? 한 가지 예를 봅시다.

 

 

for (int i = 0; i < 100000; i++) {
  System.out.println("j.on");
}

 

 

위 코드는 "j.on"을 100000번 출력합니다. 처음 출력할 때만 String Pool에 "j.on"을 등록하고, 그 이후부터는 String Pool에 존재하는 "j.on"을 찾아서 출력합니다. 만약, String Pool이 없다면 매번 String 인스턴스를 생성해야할 것입니다. 그리고 반복 횟수가 늘어날수록 더더욱 성능은 떨어지게 되겠죠.

 

 

다음으로, String의 '+' 연산입니다. String은 불변 객체이므로 매번 새로 만들어서 할당하므로 '+' 연산을 많이해야한다면 StringBuilder를 사용하는 것이 좋습니다. 그런데, 제이슨이 바이트 코드를 조사하는 것을 보니까 Java 컴파일러가 내부적으로 String을 StringBuilder 임시 객체를 생성하여 '+'가 append 연산을 하게 된다는 것을 알게 되었습니다.

 

물론, 그럼에도 StringBuilder를 안 쓰고 '+' 연산을 한다면 StringBuilder 임시 객체를 만드는 행위때문에 속도는 느리지만, Java 컴파일러 내부적으로 StringBuilder를 사용한다는 것은 흥미로웠습니다.

 

 

이렇게 String 수업은 끝나고 제네릭 시간이 되었습니다. 여기서 가장 인상깊었던 내용은 아래 사진입니다.

 

 

 

 

test1은 제네릭의 상한 경계를 나타내는 것인데, 인자가 Animal의 자식 클래스만 들어올 수 있습니다. 그런데, animals 리스트에 Dog 객체를 추가하려고 하니까 에러가 발생하였습니다. 주석에서 알 수 있듯이 Animal의 자식 클래스 중 Dog라고 확정할 수가 없기 때문이었습니다.

 

반면에 test2는 제네릭의 하한 경계를 나타내는 것인데, 인자가 Animal의 부모 클래스만 들어올 수 있습니다. 모든 클래스의 최상위 클래스는 Object이므로 위와 같이 animals의 get()으로 요소를 반환할 수 있습니다. 그리고 Dog는 Animal이므로 animals에 Dog 객체를 추가하는 것이 가능합니다.

 

 

마지막으로, enum 클래스입니다. 제이슨이 문자가 +, -, *, / 일 때 각각 필요한 연산을 하는 계산기를 만드는 실습을 보여주셨습니다. 이 과정에서 enum 클래스를 사용하였는데, 인상 깊은 부분이 2가지 있었습니다.

 

전자는 문자열을 비교할 때, 's.equals("+")'가 아니라 '"+".equals(s)'를 사용하라는 것입니다. 만약, s가 null이라면 널포인터익셉션이 발생할 것이기때문이죠.

 

후자는 enum 클래스 내부에 추상 클래스를 정의하면 열거형 상수가 모두 오버라이드를 해야한다는 점입니다. 지금 생각하면 당연한데, 각각의 열거형 상수가 덧셈, 뺼셈, 곱셈, 나눗셈을 추상 클래스를 통해 오버라이드한다는 점이 굉장히 신선했습니다.

 

 

독서

화요일부터 목요일까지는 정신 없이 수업 듣고 미션을 수행하느라 책에 손도 못 댔습니다. 그래서 최근에 구매한 '코딩을 지탱하는 기술'을 열심히 읽고 깃허브에 정리를 하였습니다. 아직 다 못 읽었으나, 오늘 남은 시간이랑 내일까지해서 전부 읽고 깃허브에 정리해 두려고 합니다.

 

 

정리

리뷰어 님인 데이브가 오늘 저녁에서 내일 오전 사이에 코드리뷰가 마무리 된다고 해 주셔서 오늘은 모처럼 여유롭게 보낼 수 있었습니다. 내일은 본격적으로 피드백을 반영해야할텐데 제가 생각한 부분을 포스팅으로 올려두겠습니다.

추천 글