💡 해당 글은 『자바의 신 3판』을 복습하며 도서의 내용과 본인의 주관적인 생각을 정리한 글입니다.
☁️ 내용정리
java.lang
자바에서 기본으로 제공하는 라이브러리이다. import를 하지 않아도 사용할 수 있다.
아래는 자바의 신에서 중요하다고 언급하는 java.lang의 클래스이다.
- 문자열:
CharSequence
,String
,StringBuilder
,StringBuffer
- 기본 자료형 및 숫자: 기본자료형 래퍼클래스들,
Math
,Number
- 쓰레드:
Runnable
,Thread
,ThreadGroup
,ThreadLocal
- 예외:
Throwable
,Exception
,RuntimeException
대표적인 에러
OOME
(OutOfMemoryError): JVM의 메모리가 부족하면 발생 (보통 heap의 메모리)StackOverflowError
: stack에 쌓을 수 있는 메소드 호출 정보의 한계를 넘어선 경우 발생
숫자를 처리하는 클래스
기본 자료형은 heap
에 저장되지 않고 각 스레드의 stack
에 저장된다. 따라서 연산 시 heap으로의 참조가 발생하지 않으므로 빠른 처리가 가능하다.
하지만 기본 자료형을 객체로 처리해야할 때가 있다. 예를들면 Collection
의 Element
로 지정할 때, String
타입을 int
타입으로 변경할 때, 제네릭
에서 사용할 때 등 이다.
이때 기본 자료형을 객체로 포장한 래퍼 클래스가 사용된다.
Integer
, Charater
, Boolean
, Double
…, Charater와 Boolean을 제외한 래퍼클래스는 Number
클래스를 확장한다. 자바에서 오토박싱, 언박싱을 제공하기 때문에 참조형이지만 일반자료형처럼 사용할 수 있으나, 성능 오버헤드가 발생할 수 있으니 유의해야 한다.
돈 계산과 같은 정확한 값을 요구하는 연산을 수행할 때는 정수형 BigInteger
와 소수형 BigDecimal
을 사용하는 것이 좋다.
System
System
클래스는 각종 정보를 확인하거나, 시스템과 관련된 기능을 수행할 때 사용한다. 생성자가 존재하지 않고 static으로 동작한다.
System 클래스에는 3개의 클래스 변수가 선언되어 있다.
- PrintStream
err
: 에러 및 오류를 출력할 때 사용한다. - InputStream
in
: 입력값을 처리할 때 사용한다. - PrintStream
out
: 출력값을 처리할 때 사용한다.
우리가 자주 사용하는 System.out.println()
메서드는 System 클래스의 out
변수를 통해 PrintStream
의 println()
메서드가 호출되는 것이다.
System 클래스는 다음과 같은 다양한 역할을 한다.
- 시스템 속성(Property)값 관리:
clearProperty(String key), getProperties() ,,,
- 시스템 환경(Environment)값 조회:
getenv(), getenv(String name)
- GC 수행:
gc(), runFinalzation()
- JVM 종료:
exit(int status)
- 현재 시간 조회:
currentTimeMillis(), nanoTime()
- 기타 관리용 메서드들 …
우리가 사용하지 말아야 할 메서드는 GC수행과 JVM종료이다. 성능의 문제를 야기할 수 있기 때문이다.
☁️ 내 생각
서버에서 사용자의 요청에서 정수형을 받을 때, int? Integer?
예전에 현직자분이 서버에서 사용자의 요청에서 정수형을 받을 때, 성능적으로 int가 좋을까 Integer가 좋을까 라는 질문을 받은 적이 있다.
그 때는 gc에 대해 키워드만 알고 있었기에, 메모리 측면에서의 대답은 하지 못하고 아래처럼 답변하였다.
Integer형은 기본값으로 null을 저장하고, int는 0을 저장한다. 따라서 0이라는 값이 로직에서 특정 상태를 나타낸다면 Integer를 사용하는 것이 좋을 것 같다. Integer를 사용하는 경우 NPE를 유의해야 한다.
이후에 Integer는 메모리에 계속 생겨날텐데 메모리적으로 문제가 없을까요? 라는 질문을 흘러가듯 하셨는데 솔직히 무슨 문제가 발생할지 잘 알지 못하였다.
지금 생각하는 나의 답변은 아래와 같다.
사용자의 request를 통해 서버로 전달되는 객체는 참조가 빨리 끊길 것이라 생각된다. 따라서 사용자의 요청에서 정수형을 저장하기 위해 Integer객체를 사용하는 경우, 많은 객체가 생성되고 빠르게 참조가 끊길 것이고, 해당 객체는 GC에 의해 unreachable 하다고 판단되어 수거될 것이다. 이는 gc를 빈번하게 호출할 가능성이 있으며, 성능 오버헤드를 발생시킬 수 있다. 따라서 원시타입 int를 통해 받고 기본 값인 0에 대해서는 로직에서 처리하는 것이 효율적이라 생각된다.
내가 실무에서 서버를 개발해보면 위의 답변도 변경될 수 있을 것이라 판단된다. 아직까지는 잘 모르겠다.
System 클래스의 정적 변수들은 소스코드 상 null 로 초기화되던데 왜 NPE가 발생하지 않는 것일까?
System.out is declared as static final and initialized with null?
/* Register the natives via the static initializer.
*
* The VM will invoke the initPhase1 method to complete the initialization
* of this class separate from <clinit>.
*/
private static native void registerNatives();
static {
registerNatives();
}
public static final InputStream in = null;
public static final PrintStream out = null;
public static final PrintStream err = null;
System 클래스를 보면 registerNatives라는 네이티브 메서드가 존재하고 이를 정적으로 실행한다.
JVM에 의해 registerNatives 메서드를 호출하며 in/out/err 변수들을 초기화 시켜준다는 것인데, 리플렉션을 사용하면 final 변수에 접근하여 값을 변경할 수 있기 때문에 해당 동작이 가능한 것으로 보인다.
근데 왜 굳이 리플렉션으로 넣어줄까? 스트림은 동적으로 할당되는 것이기 때문인가,, 잘 모르겠다.
System.out.println(null)을 수행하면 어떤 메서드가 호출될까?
한 5초동안 의문이 들었던 내용인데, 생각해보니 컴파일 오류가 발생한다.
PrintStream에는 println메서드가 여러 개 오버로딩 되어있다. 따라서 null을 전달하는 경우 어떤 메서드를 호출해야하는지 컴파일러가 판단하지 못하기 때문에 컴파일 오류가 발생한다.
public static void main(String[] args) {
System.out.println(null); // 컴파일 오류
Object o = null;
System.out.println(o); // null
}
null이 할당된 오브젝트를 넘겨주는 경우 null을 출력하는 이유는 Object를 매개변수로 받는 println 메서드를 호출했기 때문이고, 해당 메서드에서는 매개변수에 대해 String.valueOf()를 수행한 뒤 출력하기 때문이다.
따라서 객체를 로깅하거나 출력할 필요가 있을 경우 혹시 모를 NPE
를 방지하기 위해 String.valueOf()
의 사용을 지향하자.
☁️ 질문
'JAVA > 자바의 신' 카테고리의 다른 글
22장. 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - List (1) | 2024.01.10 |
---|---|
21장. 실수를 방지하기 위한 제네릭이라는 것도 있어요 (0) | 2024.01.09 |
19장. 이쯤에서 자바의 역사와 JVM에 대해서 알아보자 (0) | 2024.01.08 |
18장. 이제 기본 문법은 거의 다 배웠으니 정리해 봅시다 (1) | 2024.01.08 |
17장. 어노테이션이라는 것도 알아야 한다. (0) | 2024.01.08 |