▩ 목 차 ▩
1. java,lang 패키지는 특별하다
1-1. java.lang 패키지에서 제공하는 인터페이스, 클래스, 예외 클래스
1-2. 숫자를 처리하는 클래스들
1-3. 각종 정보를 확인하기 위한 System 클래스
1-3-1. 시스템 속성값 관리(Property)
1-3-2. 시스템 환경 값 조회(Environment)
1-3-3. GC 수행
1-3-4. GC 종료
1-3-5. 현재 시간 조회
1-4. System.out을 살펴보자.
■ 1. java,lang 패키지는 특별하다 ■
java.lang 패키지에 있는 클래스들은 import를 안해도 사용할 수 있기 때문에 특별하다.
■ 1-1. java.lang 패키지에서 제공하는 인터페이스, 클래스, 예외 클래스
java.lang 패키지에서 제공하는 인터페이스, 클래스, 예외 클래스는 다음과 같이 분류할 수 있다.
[ 굵은 글씨로 표시한 항목들은 알고 있어야 한다.]
java.lang 패키지에 정의되어 있는 에러는 많지만 OutOfMemoryError(OOME)와 StackOverflowError는 알고 있어야 한다.
- OutOfMemoryError(OOME) : 메모리가 부족하여 발생하는 에러다. 자바는 가상 머신에서 메모리를 관리하지만, 프로그램을 잘못 작성하거나 설정이 제대로 되어 있지 않을 경우에는 이러한 에러가 발생할 수 있다.
- StackOverflowError : 호출된 메소드의 깊이가 너무 깊을 때 발생한다. 예를 들어 메소드가 자기 자신을 호출하는 재귀 메소드를 잘못 잘석했다면 스택에 쌓을 수 있는 메소드 호출 정보의 한계를 넘어설 때 발생할 수 있다.
■ 1-2. 숫자를 처리하는 클래스들
기본자료형은 heap영역에 저장되지 않고, stack영역에 저장되어 관리된다.
==> 계산할 때 보다 빠른 처리가 가능하다.
기본자료형의 숫자를 객체를 처리해야 할 필요가 있다.
==> 그러므로 기본자료형으로 선언되어 있는 타입의 클래스들이 선언되어 있다.
- Byte
- Short
- Integer
- Long
- Float
- Double
- Character
- Boolean
Character와 Boolean을 제외한 숫자를 처리하는 클래스를 Wrapper(감싼) 클래스라고 불리며 모두 number라는 abstract 클래스를 확장(extends)한다.
[EX] - Wrapper 클래스
package pat20;
public class JavaLangNumber {
public static void main(String[] args) {
JavaLangNumber sample = new JavaLangNumber();
sample.nuberTypeCheck();
}
public void nuberTypeCheck() {
String value1="3";
String value2="5";
byte byte1=Byte.parseByte(value1); //1
byte byte2=Byte.parseByte(value2); //1
System.out.println(byte1 + byte2); //2
Integer refInt1= Integer.valueOf(value1); //3
Integer refInt2= Integer.valueOf(value2); //3
System.out.println(refInt1+refInt2+"7"); //4
}
}
8
87
위의 코드에서 주석처리된 숫자를 봐보자.
- 3과 5라는 String 값을 parseByte()메소드를 사용하여 byte로 변환하고,
- 두 값을 더한 결과를 출력했다.
- valueOf() 메소드를 사용하여 Integer 타입으로 변환한 후 두 값을 더 한 후
- "7"이라는 String을 더하여 출력했다.
코드를 보면 알겠지만, 이 두가지 메소드는 모두 static 메소드다.
==> 따라서 타입의 객체를 생성 할 필요 없이 바로 사용할 수 있다. 둘 다 String 같은 문자열을 숫자 타입으로 변환한다는 공통점이 있지만, parse타입이름()메소드는 기본 자료형을 리턴하고, valueof() 메소드는 참조 자료형을 리턴한다.
위의 결과값에서 "87"의 결과값에 의문 드는것이 있을 것이다.
참조 자료형 중에서 더하기 연산을 가능한 것은 String뿐이다. 그런데 참조자료형 중에서 Byte, Short, Integer, Long, Float, Double 타입들은 기본자료형처럼 사용할 수 있다.
==> 즉, 8(Integer의 더하기 이기에 기본자료형으로 인식) + 7(더하기 연산이 가능한 String 참조자료형) 이라 생각하면 편하다.
이러한 숫자를 처리하는 참조 자료형을 만든 이유가 뭘까?
- 매개변수를 참조자료형만으로만 받는 메소드를 처리하기 위해서
- 제네릭과 같은 기본자료형을 사용하지 않는 기능을 사용하기 위해서(추후에 배움)
- MIN_VALUE(최소값)나 MAX_VALUE(최대값)와 같이 클래스에 선언된 상수 값을 사용하기 위해서
- 문자열을 숫자로, 숫자를 문자열로 쉽게 변환하고, 2, 8, 10, 16진수 변환을 쉽게 처리하기 위해서
기본자료형을 참조 자료형으로 만든 클래스들은 Boolean 클래스를 제외하고 모두 MIN_VALUE와 MAX_VALUE라는 상수를 갖고 있다.
해당 타입이 나타낼 수 있는 값의 범위를 확인하려면 static으로 선언되어 있는 이 상수를 다음과 같이 사용하면 된다.
[EX] - 해당 타입이 나타낼 수 있는 값의 범위
위를 보게 되면 Integer 타입과 Long 타입을 읽기 어렵다.
==> 그래서 이 값을 2진수나 16진수로 표현해서 보면 보기 편하다.
[EX] - 해당 타입이 나타낼 수 있는 값의 범위를 2진수 및 16진수로 표현하기
public void integerMinMaxCheckBinary() {
System.out.println("Inter BINARY min ="+Integer.toBinaryString(Integer.MIN_VALUE));
System.out.println("Inter BINARY max ="+Integer.toBinaryString(Integer.MAX_VALUE));
System.out.println("Integer HEX min ="+Integer.toHexString(Integer.MIN_VALUE));
System.out.println("Integer HEX max ="+Integer.toHexString(Integer.MAX_VALUE));
}
Inter BINARY min =10000000000000000000000000000000
Inter BINARY max =1111111111111111111111111111111
Integer HEX min =80000000
Integer HEX max =7fffffff
위와 같이 어떤 값을 원하는 진수로 표현하고 싶을 때에는 직접 구현하는 것보다는 숫자클래스에서 제공되는 메소드를 사용하면 된다.
돈 계산과 중요한 연산을 할 경우 정수형 BigInetger, 소수형 BigDecimal을 사용하여야 정확한 계산이 가능하다. [ 이 두 클래스들은 java.lang.Number 클래스의 상속을 받았으며, java.math 패키지에 선언되어 있다. ]
■ 1-3. 각종 정보를 확인하기 위한 System 클래스
이 클래스의 가장 큰 특징은 생성자가 없다는 것이다.
System 클래스의 3개의 static 변수가 선언되어 있다.
System.out.println()을 예시로 설명해보자.
System은 클래스 이름이다. 그 다음에 있는 out은 System 클래스에 static으로 선언된 변수 이름이다. out은 위 표에서 보면 알 수 있듯이 PrintStream 타입이다.
==> 그러므로 println()이라는 메소드는 PrintStram 클래스에 선언되어 있으며, static 메소드이다. 그렇기에 out 클래스 변수와 println()이라는 메소드가 모두 static으로 선언되어 있기 때문에 별도의 클래스 객체를 생설할 필요가 없었던 것이다. 그러므로 출력과 관련된 메소드는 System 클래스에서 찾으면 안되고 PrintStram 클래스에서 찾아야만 한다.[ PrintStream과 InputStream은 모두 java.io패키지에 선언되어 있다. ]
생각해보면 실제 System 클래스에 선언되어 있는 메소드들을 살펴보면 출력과 관련된 메소드들은 없다. 즉, Sytem 클래스는 이름 그대로 시스템에 대한 정보를 확인하는 클래스이다. 이 클래스에서 제공되는 메소드를 분류해보면다면 다음과 같이 다양한 역할을 한다는 것을 알수 있다.
- 시스템 속성값 관리
- 시스템 환경값 조회
- GC 수행 [절대로 수행해서는 안됨]
- JVM 종료 [절대로 수행해서는 안됨]
- 현재 시간 조회
- 기타 관리용 메소드들
■ 1-3-1. 시스템 속성값 관리(Property)
Property와 관련된 이 메소드들에 대해서 이해하려면 Properties라는 클래스를 알아야 한다.
Properties는 java.util 패키지에 속하며, Hashtable의 상속을 받은 클래스다. [ 추후 컬렉션 관련 클래스들에 대해서 배우면 Hashtable이 어떤 클래스 인지 알 것이다.]
자바 프로그램을 실행을 하면 필요 여부와 상관없이 Properties 객체가 자동 생성된다.
==> 그 값은 언제, 어디서든지 같은 JVM 내에서는 꺼내서 사용할 수 있다.
[EX] - System.getProperty() 사용
package pat20;
public class JavaLangSystem {
public static void main(String[] args) {
JavaLangSystem sample = new JavaLangSystem();
sample.systemPropertiesCheck();
}
public void systemPropertiesCheck() {
System.out.println("java.version="+System.getProperty("java.version"));
}
}
java.version=17.0.1
위의 코드는 System.getProperty()라는 메소드를 사용하여 "java.version"이라는 키에 있는 값을 출력하도록 했다.
[ 노트북에 설치되어 있는 버전에 따라서 결과가 다르게 나올 것이다. ]
그런데 우리가 "java.version" 이라는 키를 어떻게 알았을까?
==> 추후에 배울 24장 "자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Part2 Map"에서 다루도록 하겠다.
■ 1-3-2. 시스템 환경 값 조회(Environment)
위에 있는 Properties라는 것은 추가할 수도 있고, 변경 할 수도 있다. 하지만, 시스템 환경값 env라는 것은 변경하지 못하고 읽기만 할 수 있다.
==> 대부분 OS나 장비와 관련된 것들이기 때문이다.
[EX] - 시스템 환경 값 조회
System.out.println("JAVA_HOME="+System.getenv("JAVA_HOME"));
JAVA_HOME=null
필자는 JAVA_HOME이 설정되어 있지 않아서 null 값이 나왔다.
매개 변수가 없는 getenv()메소드는 리턴 타입이 Map<String, String)이므로, 추후에 설명을 하도록 하겠다.
■ 1-3-3. GC 수행
위에 있는 메소드는 사용하면 안된다.
==> 즉, 자바는 메모리 처리를 개별자가 별도로 하지 않는다. 따라서 System.gc()라는 메소드를 호출하면 가비지 컬렉션을 명시적으로 처리하도록 할 수 있다. [Object 클래스에 선언되어 있는 finalize() 메소드를 명시적으로 수행하도록 하는 runFinalization()메소드가 있음]
위에 있는 2개의 메소드들은 우리가 호출하지 않아도 JVM이 GC작업과 finalization 작업을 실행한다.
■ 1-3-4. GC 종료
이 메소드는 절대 절대 호출하면 안되는 것 중 하나다.
안드로이드 앱이나, 웹 애플리케이션에서 이 메소드를 사용하면 해당 애플리케이션의 JVM이 죽어버린다.
■ 1-3-5. 현재 시간 조회
currentTimeMillis() 메소드는 현재 시간을 나타낼 때 매우 유용한 메소드다. UTC라는 Universal time 기준으로 1970년 1월 1일 00:00부터 지금까지의 밀리초 단위의 차이를 출력한다. [ 밀리초는 1/1,000초다. ]
nanoTime()메소드는 현재 시간을 알아내기 위해서 사용하는 메소드는 절대 아니고, 시간의 차이를 측정하기 위한 용도의 메소드다.
여기서 제공하는 시간은 나노초이며, 1/1,000,000,000초를 의미한다.
[EX] - 어떤 작업이 소요되는지를 확인 가능한 nanoTime(), 현재 시간을 확인하기 위한 currentTimeMills()
package pat20;
public class JavaLangSystem {
public static void main(String[] args) {
JavaLangSystem sample = new JavaLangSystem();
// sample.systemPropertiesCheck();
sample.numberMinMaxElapsedCheck();
}
public void systemPropertiesCheck() {
// System.out.println("java.version="+System.getProperty("java.version"));
System.out.println("JAVA_HOME="+System.getenv("JAVA_HOME"));
}
public void numberMinMaxElapsedCheck() {
long startTime = System.currentTimeMillis();
long startNanoTime = System.nanoTime();
JavaLangNumber sample = new JavaLangNumber();
sample.integerMinMaxCheckBinary();
System.out.println("Milli second=" + (System.currentTimeMillis()-startTime));
System.out.println("Nano second=" + (System.nanoTime()-startNanoTime));
}
}
Inter BINARY min =10000000000000000000000000000000
Inter BINARY max =1111111111111111111111111111111
Integer HEX min =80000000
Integer HEX max =7fffffff
Milli second=1
Nano second=1389416
위의 코드에서 nuberMinMaxElapsedCheck()메소드는 어떤 작업이 얼마나 소요되는지를 확인하는 메소드다.
- 처음에 현재 시간을 milli와 nano로 저장할 수 있는 currentTimeMillis()메소드와 nonoTime()을 이용하여 변수에 저장한다.
- JavaLangNumber클래스로부터 sample 객체를 생성하고 integerMinMaxCheckBinary()메소드를 수행한다.
- 그리고 다시 currentTimeMillis()메소드와 nonoTime()메소드를 이용하여 현재 시간에서 아까 현재 시간을 저장한 변수를 빼어 그 전에 한 작업들에 대한 시간을 구한다.
여기서 알 수 있는 것은 시간을 측정할 필요가 있을때는 nanotime() 메소드를 이용하는 것을 권장한다.
==> 나노초가 더 정확하기도 하고 nanoTime()메소드는 시간 측정을 위해서 만들었기 때문이다.
currentTimeMillis()메소드는 현재 시간을 확인하기 위한 메소드라고 생각해라.
■ 1-4. System.out을 살펴보자.
System 클래스에 선언되어 있는 out(System.out)과 err(System.err) 변수는 PrintStream이라는 동일한 클래스의 객체다.
단지, 정상적인 출력인지, 에러가 났을 때의 출력 결과인지의 차이만 존재한다.
PrintStream 클래스는 static하게 사용하므로, 생성자를 살펴볼 필요가 없다.
PrintStream 클래스의 출력을 위한 주요 메소드
- print()
- println()
- format()
- printf()
- write()
이 중 write()는 많이 사용 안된다.
주요 메소드는 print() println() 메소드다. 이 둘의 차이는 print()는 줄바꿈을 하지 않고, println()은 줄바꿈을 한다. 또한 println()은 매개 변수가 없는 매소드가 존재한다. 공통점으로는 두 메소드 모두 기본자료형과 참조자료형을 매개변수로 사용할 수 있다.
[ 줄바꿈 처리를 할때 println(""); 으로 하게 되면 필요없는 String 객체가 생기므로 println();으로 처리하는 것이 깔끔하다. ]
print()와 println()의 매개변수
잘 살펴보면 byte 타입이나 short 타입을 매개변수로 받는 매소드가 선언되어 있지 않다.
==> 두 개 모두 정수형 이기때문에 int 타입을 매개 변수로 받는 메소드에서 알아서 처리해주기 때문이다.
[EX] - print() println()은 toStrin을 단순히 호출한다? 아니다 valueOf가 호출된다.
public void printNull() {
Object obj = null;
System.out.println(obj);
System.out.println(obj + " is object's value");
}
null
null is object's value
위의 코드를 보면 이상할 것이다. null인 객체를 출력해보았는데 컴파일 오류가 아닌 null 이라고 찍혔기 때문이다.
==> 왜냐하면 obj 객체가 null이다. 객체를 출력할 때 toString()메소드를 단순히 호출한다고 생각했는데, 여기서 obj는 null 값인데 toString()을 호출했기 때문이다. 즉, null인 obj는 아무런 할당이 되어 있지 않기 때문에 메소드를 호출할 수 없기 때문이다. 앞에서 말했던 생각은 틀렸다.
그렇기에 print()와 println()메소드에서는 단순히 toString()메소드 결과를 출력하지 않는다. String의 valuOf()라는 static 메소드를 호출하여 결과를 받은 후 출력한다. 즉, print()와 println()메소드에서는 String.valueOf(obj)이 호출된 것이다. 만약 obj.toString을 한다면 예외가 발생할 것이다.
즉, 객체를 출력할 때에는 toString()을 사용하는 것보다 valueOf()메소드를 사용하는 것이 훨씬 안전하다.
[EX] - null 객체를 toString()으로 출력하기
public void printNull() {
Object obj = null;
System.out.println(obj);
System.out.println(obj.toString());
}
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Object.toString()" because "obj" is null
at pat20.JavaLangSystem.printNull(JavaLangSystem.java:30)
at pat20.JavaLangSystem.main(JavaLangSystem.java:10)
위의 예제를 보아라. null 객체를 toString()으로 출력하니까 오류가 뜨는것을 확인 가능하다.
즉, 객체를 출력할 때에는 toString()을 사용하는 것보다 valueOf()메소드를 사용하는 것이 훨씬 안전하다.
obj + " is object's value"
obj.toString()의 구문은 오류가 뜨는데 왜 위에 있는 구문은 오류가 뜨지 않을까?
==> 앞 장에서 배운 StringBuilder의 append()메소드에 null을 넣어버린 것과 동일하므로 문제 없이 null을 출력한 것이다.
즉, new StringBuilder().append(obj).append(" is object's value") 구문과 동일한 것이다.
그 다음에 자주 사용하는 메소드는 format()와 printf()메소드인데 두 메소드는 이름만 다르고 처리하는 것은 동일하다. 따라서 편한대로 선택하여 사용하면 된다.
'JAVA > 자바의신 2' 카테고리의 다른 글
24장 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Part3(Map) (1) | 2022.09.19 |
---|---|
23장 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Part2(Set과 Queue) (0) | 2022.09.18 |
22장 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Part1(List) (0) | 2022.09.18 |
21장 실수를 방지하기 위한 제네릭 (0) | 2022.09.17 |
19장 자바에 대해서 더 알아보자 (1) | 2022.09.15 |