일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 자바의 정석 기초편 ch14
- 스프링 mvc2 - 검증
- 자바의 정석 기초편 ch13
- 스프링 db1 - 스프링과 문제 해결
- 스프링 입문(무료)
- 자바의 정석 기초편 ch1
- 코드로 시작하는 자바 첫걸음
- 스프링 mvc2 - 타임리프
- jpa 활용2 - api 개발 고급
- 자바의 정석 기초편 ch9
- 2024 정보처리기사 수제비 실기
- 스프링 mvc2 - 로그인 처리
- 스프링 db2 - 데이터 접근 기술
- 자바의 정석 기초편 ch5
- 스프링 mvc1 - 스프링 mvc
- jpa - 객체지향 쿼리 언어
- 자바의 정석 기초편 ch8
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch4
- 자바 기본편 - 다형성
- 스프링 mvc1 - 서블릿
- 스프링 고급 - 스프링 aop
- 자바의 정석 기초편 ch2
- 자바의 정석 기초편 ch11
- 게시글 목록 api
- 자바의 정석 기초편 ch6
- 자바 중급1편 - 날짜와 시간
- 자바의 정석 기초편 ch7
- @Aspect
- 자바의 정석 기초편 ch12
- Today
- Total
나구리의 개발공부기록
래퍼와 Class 클래스, 래퍼 클래스(기본형의 한계, 자바 래퍼 클래스, 오토 박싱, 주요 메서드와 성능), Class 클래스, System 클래스, Math 클래스, Random 클래스 본문
래퍼와 Class 클래스, 래퍼 클래스(기본형의 한계, 자바 래퍼 클래스, 오토 박싱, 주요 메서드와 성능), Class 클래스, System 클래스, Math 클래스, Random 클래스
소소한나구리 2025. 1. 19. 13:16출처 : 인프런 - 김영한의 실전 자바 - 중급1편 (유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용
1. 래퍼 클래스
1) 기본형의 한계
(1) 기본형의 한계
- 자바는 객체 지향 언어이지만 int, double 같은 기본형 타입은 객체가 아닌데 객체가 아니기 때문에 한계가 존재함
- 객체가 아님: 객체가 아니기 때문에 객체 지향 프로그래밍의 장점을 사용할 수가 없음
- 객체는 유용한 메서드를 제공할 수 있는데 기본형은 메서드를 제공할 수 없음
- 객체 참조가 필요한 컬렉션 프레임워크를 사용할 수 없으며 제네릭도 사용할 수 없음(나중에 배움) - null 값을 가질 수 없음: 기본형 데이터 타입은 null값을 가질 수 없는데, 때로는 데이터가 없다는 상태를 나타내야할 필요가 있지만 기본형은 항상 값을 가지기 때문에 이런 표현을 할 수가 없음
(2) MyIntegerMethodMain0 - 기본형 타입 비교
- main() 메서드에 선언한 기본형인 value의 값을 비교하려고 하면 외부에 선언된 별도의 메서드를 호출해야함
- compareTo(int value, int target): 두 값을 비교하여 왼쪽의 값이 더 작으면 -1, 두 값이 같으면 0, 왼쪽의 값이 더 크면 1을 반환
- 자기 자신인 value와 다른 값을 연산하는 것이기 때문에 항상 자기 자신의 값인 value가 사용되는데, 이런경우 만약 value가 객체라면 객체 스스로 자기 자신의 값과 다른 값을 비교하는 메서드를 만드는 것이 더 유용할 것임
package lang.wrapper;
public class MyIntegerMethodMain0 {
public static void main(String[] args) {
int value = 10;
int i1 = compareTo(value, 5);
int i2 = compareTo(value, 10);
int i3 = compareTo(value, 20);
System.out.println("i1 = " + i1);
System.out.println("i2 = " + i2);
System.out.println("i3 = " + i3);
}
public static int compareTo(int value, int target) {
if (value < target) {
return -1;
} else if (value > target) {
return 1;
} else {
return 0;
}
}
}
/* 실행 결과
i1 = 1
i2 = 0
i3 = -1
*/
(2) MyInteger - 직접 만든 래퍼 클래스
- int를 클래스로 만들려면 int 값을 가지고 클래스로 한번 감싸서 만들면되는데, 이렇게 특정 기본형을 감싸서(Wrap)만드는 클래스를 래퍼클래스(Wrapper class)라고 함
- MyInteger는 int value라는 단순한 기본형 변수를 하나 가지고 있으며 이 변수를 편리하게 사용하도록 다양한 메서드를 제공함
- 앞서 만들었던 compareTo()메서드를 클래스 내부로 캡슐화하고 불변으로 설계하였음
- 기본형이였던 int를 MyInteger를 통해 객체로 다룰 수 있게 되었음
package lang.wrapper;
public class MyInteger {
private final int value;
public MyInteger(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public int compareTo(int target) {
if (value < target) {
return -1;
} else if (value > target) {
return 1;
} else {
return 0;
}
}
@Override
public String toString() {
return String.valueOf(value); // 숫자를 문자로 변경하도록 오버라이딩
}
}
(3) MyIntegerMethodMain1
- 기본형인 int는 스스로 메서드를 가질 수 없지만 직접 만든 MyInteger는 객체이므로 자신이 가진 메서드를 편리하게 호출할 수 있음
- MyInteger에 정의한 compareTo()메서드는 자기 자신의 값을 외부의 값과 비교 후 결과를 반환함
package lang.wrapper;
public class MyIntegerMethodMain1 {
public static void main(String[] args) {
MyInteger myInteger = new MyInteger(10);
int i1 = myInteger.compareTo(5);
int i2 = myInteger.compareTo(10);
int i3 = myInteger.compareTo(20);
System.out.println("i1 = " + i1);
System.out.println("i2 = " + i2);
System.out.println("i3 = " + i3);
}
}
2) 자바 래퍼 클래스
(1) MyIntegerNullMain0 - 기본형
- 때때로 데이터가 '없음'이라는 상태가 필요할 수 있는데 기본형은 항상 값을 가져야함
- findValue() 메서드는 배열에 찾는 값이 있으면 해당 값을 반환하고 찾는 값이 없으면 반환 타입이 기본형인 int이므로 null이 아닌 -1을 반환함
- 보통 기본형 반환타입 시 값을 찾지 못하면 -1이나 0을 반환하도록 함 - 실행 결과를 보면 입력값이 -1일때 -1을 반환하는데, 배열에 없는 값이 100을 입력해도 -1을 반환하여 배열에 -1이 있어서 -1을 반환한 것인지 값이 없어서 -1을 반환한 것인지 명확하게 판단하기가 어려움
package lang.wrapper;
public class MyIntegerNullMain0 {
public static void main(String[] args) {
int[] intArr = {-1, 0, 1, 2, 3};
System.out.println(findValue(intArr, -1));
System.out.println(findValue(intArr, 0));
System.out.println(findValue(intArr, 1));
System.out.println(findValue(intArr, 100)); // 값을 못찾아서 -1을 반환함
}
private static int findValue(int[] intArr, int target) {
for (int value : intArr) {
if (value == target) {
return value;
}
}
return -1;
}
}
/* 실행 결과
-1
0
1
-1
*/
(2) MyIntegerNullMain1 - 객체
- findValue()메서드의 반환타입을 앞에서 만든 MyInteger 래퍼 클래스를 사용하여 배열의 값과 target의 값이 같으면 해당 값을 반환하는 것은 기존과 같지만 값이 다를 때에는 null로 반환함
- 기본형은 항상 값이 존재해야하기에 null을 사용할 수 없지만 객체인 참조형은 값이 없다는 null을 사용할 수 있음
- null값을 반환하는 경우 잘못하면 NullPointerException이 발생할 수 있기 때문에 주의해서 사용해야함
package lang.wrapper;
public class MyIntegerNullMain1 {
public static void main(String[] args) {
MyInteger[] intArr = {new MyInteger(-1), new MyInteger(0), new MyInteger(1)};
System.out.println(findValue(intArr, -1));
System.out.println(findValue(intArr, 0));
System.out.println(findValue(intArr, 1));
System.out.println(findValue(intArr, 100));
}
private static MyInteger findValue(MyInteger[] intArr, int target) {
for (MyInteger myInteger : intArr) {
if (myInteger.getValue() == target) {
return myInteger;
}
}
return null;
}
}
/* 실행 결과
-1
0
1
null
*/
** 참고
- 기본형은 사실 실수로 값을 안넣거나 하는 것을 방지하여 항상 값을 가지게 할 수 있도록 하는 좋은 제약을 가지고 있는 타입이며 잘못된 것이 아님
- 그러나 때로는 값이 없다는 걸 표현해야 할 때도 있기 때문에 참조형을 써야할 때도 있음
3) 자바 래퍼 클래스
(1) 기본형에 대응하는 자바의 래퍼 클래스
- byte -> Byte
- short -> Integer
- int -> Integer
- long -> Long
- float -> Float
- double -> Double
- char -> Character
- boolean -> Boolean
- 자바가 제공하는 기본 래퍼클래스는 모두 불변이므로 equals로 비교해야함
(2) WrapperClassMain - 래퍼클래스 사용법
- 래퍼클래스.valueOf(값)으로 래퍼클래스를 생성하면 됨
- 래퍼클래스안에 저장된 실제 값을 기본형 타입으로 꺼낼수도 있음
package lang.wrapper;
public class WrapperClassMain {
public static void main(String[] args) {
Integer newInteger = new Integer(10); // Deprecated 됨, 대신에 valueOf()사용
Integer integerObj = Integer.valueOf(10); // -128 ~ 127 자주 사용하는 숫자 값 재사용, 불변
Long longObj = Long.valueOf(10);
Double doubleObj = Double.valueOf(10.5);
System.out.println("newInteger = " + newInteger);
System.out.println("integerObj = " + integerObj);
System.out.println("longObj = " + longObj);
System.out.println("doubleObj = " + doubleObj);
System.out.println("내부 값 읽기");
int intValue = integerObj.intValue();
System.out.println("intValue = " + intValue);
long longValue = longObj.longValue();
System.out.println("longValue = " + longValue);
System.out.println("비교");
System.out.println("==: " + (newInteger == integerObj));
System.out.println("equals: " + (newInteger.equals(integerObj)));
}
}
/* 실행 결과
newInteger = 10
integerObj = 10
longObj = 10
doubleObj = 10.5
내부 값 읽기
intValue = 10
longValue = 10
비교
==: false
equals: true
*/
(2-1) 래퍼 클래스 생성 - 박싱(Boxing)
- 기본형을 래퍼 클래스로 변경하는 것을 마치 박스에 물건을 넣은 것 같다고 해서 박싱(Boxing)이라 함
- new Integer(10)은 Deprecated되었으므로 향후 자바에서 제거될 예정이기 때문에 사용하지 말고 Integer.valueOf(10) 처럼 사용해야함
- valueOf()로 사용하면 내부에서 new Integer()를 사용하여 객체를 생성하고 반환하는데 일반적으로 자주 사용하는 -128 ~ 127 범위의 Integer 클래스를 미리 생성해두기 때문에 해당 범위의 값을 조회하면 미리 생성된 Integer 객체를 반환하여 성능 최적화가 되어있음
- 즉, -128 ~ 127 범위는 마치 문자열 풀과 비슷하게 미리 생성해두고 재사용하고 없으며 new Integer()를 호출하여 객체를 생성함
- 이런 최적화 방식은 미래에 더 나은 방식으로 변경될 수 있음
(2-2) intValue() - 언박싱(Unboxing)
- 래퍼 클래스에 들어있는 기본형 값을 다시 꺼내는 메서드
- 박스에 들어있는 물건을 꺼내는 것 같다고 하여 언박싱(Unboxing)이라 함
(2-3) 비교는 equlas() 사용
- 래퍼 클래스는 객체이므로 == 비교를 하면 인스턴스의 참조값을 비교함
- 래퍼 클래스들은 내부의 값을 비교하도록 equals()가 오버라이딩 되어있기 때문에 값을 비교하기 위해선 equals()를 사용해야 함
** 참고
- 래퍼클래스는 기본형처럼 사용할 수 있도록 객체를 그대로 출력해도 내부에 있는 값을 문자로 출력하도록 toString()도 오버라이딩 되어 있음
4) 오토 박싱(Autoboxing)
(1) AutoboxingMain1 - 수동 박싱, 언박싱
- 위에서 다루었던 int -> Integer 변환과 Integer -> int 변환 과정을 보면 valueOf()로 박싱을 하고, intValue()로 언박싱을 직접 해주어야 했음
- 하지만 이렇게 기본형과 래퍼클래스간의 변환 작업은 매우 자주 발생하는데 많은 개발자들이 불편함을 호소하였고 자바는 이런 문제를 해결하기 위해 1.5부터 오토박싱(Auto-boxing), 오토언박싱(Auto-Unboxing)을 지원함
package lang.wrapper;
public class AutoboxingMain1 {
public static void main(String[] args) {
// Primitive -> Wrapper
int value = 7;
Integer boxedValue = Integer.valueOf(value);
// Wrapper -> Primitive
int unboxedValue = boxedValue.intValue();
System.out.println("boxedValue = " + boxedValue);
System.out.println("unboxedValue = " + unboxedValue);
}
}
(2) AutoboxingMain2 - 오토 박싱, 언박싱
- 오토 박싱과 오토 언박싱은 컴파일러가 개발자 대신 valueOf, xxxValue()등의 코드를 추가해주는 기능으로 해당 메서드들을 사용하지 않고 바로 대입하여도 정상적으로 동작함
- 덕분에 기본형과 래퍼형을 서로 편리하게 변환할 수 있게 되었고 실행해보면 AutoboxingMain1과 동일하게 동작하는 것을 확인할 수 있음
package lang.wrapper;
public class AutoboxingMain2 {
public static void main(String[] args) {
// Primitive -> Wrapper
int value = 7;
Integer boxedValue = value; // 오토 박싱
// Wrapper -> Primitive
int unboxedValue = boxedValue; // 오토 언박싱
System.out.println("boxedValue = " + boxedValue);
System.out.println("unboxedValue = " + unboxedValue);
}
}
5) 주요 메서드와 성능
(1) WrapperUtilsMain - 주요 메서드
- valueOf(): 래퍼 타입을 반환하고 숫자, 문자열을 모두 지원함
- parseInt(): 문자열을 기본형으로 변환함
- compareTo(): 내 값과 인수로 넘어온 값을 비교, 내 값이 크면1, 같으면0, 작으면 -1을 반환
- sum(), min(), max(): static 메서드로 메서드 이름 그대로 덧셈, 작은값, 큰값 연산을 수행함
package lang.wrapper;
public class WrapperUtilsMain {
public static void main(String[] args) {
Integer i1 = Integer.valueOf(10); // 숫자 -> 래퍼 객체 변환
Integer i2 = Integer.valueOf("50"); // 문자열 -> 래퍼 객체 변환
int intValue = Integer.parseInt("80"); // 문자열 전용 -> 기본형 변환
// 비교
int compareResult = i1.compareTo(20);
System.out.println("compareResult = " + compareResult);
// 산술 연산
System.out.println("sum: " + Integer.sum(i1, i2));
System.out.println("min: " + Integer.min(i1, i2));
System.out.println("max: " + Integer.max(i1, i2));
}
}
/* 실행 결과
compareResult = -1
sum: 60
min: 10
max: 50
*/
(1-2) parseInt() vs valueOf()
- valueOf()는 래퍼타입을 반환하고, parseInt()는 기본형을 반환하므로 상황에 따라 원하는 타입에 맞는 메서드를 사용하면 됨
- Long.parseLong() 처럼 각 타입에 parseXxx()처럼 메서드가 모두 존재함
(2) WrapperVsPrimitive - 래퍼 클래스와 성능 비교
- 기본형 long과 래퍼형인 Long에 각각 10억번 더해서 결과를 측정해보면 맥북 m1 Pro기준으로 기본형이 약 2.5배 정도 빨랐음
- 계산 결과는 시스템사양마다 다를 수 있으나 기본형이 래퍼 클래스보다 빠른것은 변함이 없음
- 기본형은 메모리에서 단순히 그 크기만큼(int는 보통 4byte) 공간을 차지함
- 반면 래퍼 클래스는 인스턴스는 내부에 필드로 가지고 있는 기본형의 값 뿐만 아니라 객체를 다루는데 필요한 객체 메타데이터를 포함하므로 더 많은 메모리를 사용함(자바 버전과 시스템마다 다르지만 대략 8 ~ 16byte를 추가로 사용함)
package lang.wrapper;
public class WrapperVsPrimitive {
public static void main(String[] args) {
int iterations = 1_000_000_000;
long startTime, endTime;
// 기본형 long 사용
long sumPrimitive = 0;
startTime = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
sumPrimitive += i;
}
endTime = System.currentTimeMillis();
System.out.println("sumPrimitive = " + sumPrimitive);
System.out.println("기본 자료형 long 실행 시간: " + (endTime - startTime) + "ms");
// 래퍼 클래스 Long 사용
Long sumWrapper = 0L;
startTime = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
sumWrapper += i; // 오토 박싱 발생
}
endTime = System.currentTimeMillis();
System.out.println("sumPrimitive = " + sumWrapper);
System.out.println("래퍼 자료형 Long 실행 시간: " + (endTime - startTime) + "ms");
}
}
/* 실행 결과
sumPrimitive = 499999999500000000
기본 자료형 long 실행 시간: 643ms
sumPrimitive = 499999999500000000
래퍼 자료형 Long 실행 시간: 1618ms
*/
(3) 기본형, 래퍼 클래스 어떤것을 사용해야 할까?
- 지금의 테스트 결과도 10억번의 연산을 수행했을 때 0.6초와 1.6초의 차이였을뿐 기본형이든 래퍼 클래스든 이것을 1회로 환산해보면 둘다 매우 빠르게 연산이 수행이 됨
- 요즘에는 컴퓨터가 매우 빨라졌기 때문에 일반적인 애플리케이션을 만드는 관점에서보면 이런 부분을 최적화하는 것은 사막의 모래알 하나 정도의 차이가 날 뿐임
- CPU연산을 아주 많이 수행하는 특수한 경우나, 수만 ~ 수십만 이상 연속하여 연산을 수행해야하는 경우라면 이렇게 기본형을 사용하여 최적화를 하는 것이 의미가 있을 수 있으나, 그렇지 않은 일반적인 경우라면 코드를 유지보수에 좋은 것을 선택하면 됨
(4) 유지보수 vs 최적화
- 유지보수 vs 최적화를 고려해야하는 상황이라면 유지보수하기 좋은 코드를 먼저 고민해야 함
- 특히 최신 컴퓨터들은 매우 빠르기 때문에 메모리 상에서 발생하는 연산을 몇 번 줄인다고해도 실질적인 도움이 되지 않는 경우가 많음
- 코드 변경 없이 성능 최적화를 하면 가장 좋겠지만 대부분의 성능 최적화는 단순함보다 복잡함을 요구하고 더 많은 코드들을 추가로 만들어야 하므로 최적화를 하기위해 유지보수해야 하는 코드가 더 늘어날 수 있음
- 그러나 최적화를 한다고 했지만 전체 애플리케이션의 성능 관점에서 보면 불필요한 최적화를 진행할 가능성이 있음
- 특히 웹 애플리케이션의 경우 메모리 안에서 발생하는 연산 하나보다 네트워크 호출 한 번이 많게는 수십만배 더 오래걸리며 자바 메모리 내부에서 발생하는 연산을 수천번에서 한 번으로 줄이는 것 보다, 네트워크 호출 한 번을 더 줄이는 것이 더 효과적인 경우가 많음
- 개발 이후에 성능 테스트를 해보고 정말 문제가 되는 부분을 찾아서 최적화 하는 것을 권장함
- 쓸데없는 최적화는 지양하고 코드를 작성하면서 어디가 최적화가 필요한지 알려면 그만큼 경험치가 쌓여야 한다!
2. Class 클래스
1) Class 클래스
(1) 설명 및 주요 기능
- 클래스의 정보(메타데이터)를 다루는데 사용되며 Class 클래스를 통해 개발자는 실행 중인 자바 애플리케이션 내에서 필요한 클래스의 속성과 메서드에 대한 정보를 조회하고 조작할 수 있음
- 타입 정보 얻기: 클래스의 이름, 슈퍼클래스, 인터페이스, 접근 제한자 등과 같은 정보를 조회할 수 있음
- 리플렉션: 클래스에 정의된 메서드, 필드, 생성자 등을 조회하고 이들을 통해 객체 인스턴스를 생성하거나 메서드를 호출하는 등의 작업을 할 수 있음
- 동적 로딩과 생성: Class.forName() 메서드를 사용하여 클래스를 동적으로 로드하고, newInstance()메서드를 통해 새로운 인스턴스를 생성할 수 있음
- 애노테이션 처리: 클래스에 적용된 애노테이션(annotation)을 조회하고 처리하는 기능을 제공
(2) ClassMetaMain
- class는 자바의 예약어이므로 패키지명, 변수명으로 사용할 수 없으므로 자바에서는 class 대신 clazz라는 이름을 관행으로 사용함
- Class 클래스 조회: 클래스에서 조회, 인스턴스에서 조회, 문자열로 조회할 수 있음
- Class 클래스의 주요 기능은 아래와 같음
- getDeclaredFields(): 클래스의 모든 필드를 조회함
- getDeclaredMethods(): 클래스의 모든 메서드를 조회
- getSuperclass(): 클래스의 부모 클래스를 조회
- getInterfaces(): 클래스의 인터페이스들을 조회
** 참고
- throws Exception 부분은 이후에 예외 처리에서 학습함(Class.forName() 메서드에서 체크 예외가 발생함)
- <String>, <?>, <? extends String>은 제네릭이라고 하며 이부분도 이후에 학습함
package lang.clazz;
public class ClassMetaMain {
public static void main(String[] args) throws Exception {
// Class 조회
Class<String> clazz = String.class; // 1. 클래스에서 조회
// Class<? extends String> clazz2 = new String().getClass(); // 2. 인스턴스에서 조회
// Class<?> clazz3 = Class.forName("java.lang.String"); // 3. 문자열로 조회, 체크예외 발생
// 모든 필드 출력
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("Field: " + field.getType() + " " + field.getName());
}
// 모든 메서드 출력
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("Method: " + method);
}
// 상위 클래스 정보 출력
System.out.println("Superclass: " + clazz.getSuperclass().getName());
// 인터페이스 정보 출력
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> i : interfaces) {
System.out.println("Interface: " + i.getName());
}
}
}
(3) 클래스 생성하기
- Class클래스에는 클래스의 모든 정보가 들어있기 때문에 이 정보를 기반으로 인스턴스를 생성하거나, 메서드를 호출하고 필드의 값도 변경할 수 있음
- getDeclaredConstructor(): 생성자를 선택
- newInstance(): 선택된 생성자를 기반으로 인스턴스를 생성
- 문자열을 인수로 하는 forName()메서드로 조회한 Class에서 인스턴스를 생성하면 다운 캐스팅이 필요하지만 Hello.class로 조회한 Class에서 인스턴스를 생성하면 해당 클래스로 바로 반환됨
package lang.clazz;
public class ClassCreateMain {
public static void main(String[] args) throws Exception {
Class<Hello> helloClass = Hello.class;
Hello hello = helloClass.getDeclaredConstructor().newInstance();
String result = hello.hello();
System.out.println("result = " + result);
Class<?> helloClass2 = Class.forName("lang.clazz.Hello");
Hello hello2 = (Hello) helloClass2.getDeclaredConstructor().newInstance();
String result2 = hello2.hello();
System.out.println("result = " + result2);
}
}
/* 실행 결과
result = hello
result = hello
*/
(4) 리플렉션 - reflection
- 클래스의 메타 정보를 기반으로 클래스에 정의된 메서드, 필드, 생성자 등을 조회하고 이들을 통해 객체 인스턴스를 생성하거나 메서드를 호출하는 작업, 애노테이션 정보를 읽어서 특별한 기능등을 수행 할 수 있는데 이런 작업을 리플렉션이라고 함
- 최신 프레임워크들은 이런 기능을 적극 활용하고 있음
- 지금 이과정을 학습하는 단계에서는 Class가 뭔지 대략 어떤 기능을 제공하는지만 알아두면 충분하며 리플렉션은 이것보다 더 중요한 기본기들을 학습한 다음 이후에 학습해도 늦지 않음
3. System 클래스
1) System 클래스
(1) SystemMain
- 표준 입력, 출력, 오류 스트림: System.in, System.out, System.err는 각각 표준 입력, 표준 출력, 표준 오류 스트림을 나타냄
- 시간 측정: System.currentTimeMillis()와 System.nanoTime()으로 현재 시간을 밀리초 또는 나노초 단위로 제공함
- 환경 변수(운영체제에서 설정한 환경 변수): System.getenv()로 OS에서 설정한 환경 변수의 값을 얻을 수 있음
- 시스템 속성(자바에서 사용하는 설정): System.getProperties()로 현재 시스템 속성을 얻거나 System.getProperty(String key)로 특정 속성을 얻을 수 있음
- 시스템 종료: System.exit(int status)로 프로그램을 종료하고 OS에 프로그램 종료의 상태코드를 전달하며 0은 정상종료, 0이 아니면 오류나 예외적인 종료로 표현할 수 있음
- 필요할 때도 있으나 프로그램이 의도하지 않게 종료될 수 있고 특히 웹 애플리케이션에선 종료 시 필요한 작업이 있는데 이를 무시하고 종료할 수 있으므로 가급적 사용하지 말것 - 배열 고속 복사: System.arraycopy()는 자바가 아닌 시스템 레벨에서 최적화된 메모리 복사 연산을 사용하기 때문에 직접 반복문을 사용해서 배열을 복사할 때 보다 몇배 이상 빠른 성능을 제공함
- 파라미터 설명: 1.복사할 배열, 2.복사할 데이터의 시작 위치, 3.저장할 배열, 4.저장할 배열에 저장을 시작할 인덱스 위치, 5.복사할 요소의 개수
package lang.system;
public class SystemMain {
public static void main(String[] args) {
// 현재 시간을 밀리초로 가져옴
long currentTimeMillis = System.currentTimeMillis();
System.out.println("currentTime = " + currentTimeMillis);
// 현재 시간을 나노초로 가져옴
long currentNanoTime = System.nanoTime();
System.out.println("currentNanoTime = " + currentNanoTime);
// 환경 변수 읽기 - 운영체제가 사용하는 속성(Map으로 반환됨)
System.out.println("getenv = " + System.getenv());
// 시스템 속성을 읽음 - 자바가 사용하는 속성
System.out.println("properties = " + System.getProperties());
System.out.println("Java version = " + System.getProperty("java.version")); // 속성 중 자바버전만 확인
// 배열을 고속으로 복사 - 자바에서 복사하는 것이 아니라 운영체제와 하드웨어가 통채로 복사함
char[] originalArray = new char[]{'h', 'e', 'l', 'l', 'o'};
char[] copiedArray = new char[5];
// 파라미터 설명 - 1.복사할 배열, 2.복사할 데이터의 시작 위치, 3.저장할 배열, 4.저장할 배열에 저장을 시작할 인덱스 위치, 5.복사할 요소의 개수
System.arraycopy(originalArray, 0, copiedArray, 0, originalArray.length);
// 배열 출력
System.out.println("copiedArray = " + copiedArray);
System.out.println("Arrays.toString = " + Arrays.toString(copiedArray)); // 배열의 출력을 할 수 있음
// 프로그램 종료 - 프로그램이 의도하지 않게 종료될 수 있기 때문에 가급적 사용 X
System.exit(0);
}
}
4. Math, Random 클래스
1) Math - 수학 문제 해결
(1) MathMain
- 기본 연산 메서드
- abs(x) : 절대값
- max(a, b) : 최대값
- min(a, b) : 최소값 - 지수 및 로그 연산 메서드
- exp(x) : e^x 계산
- log(x) : 자연 로그
- log10(x) : 로그 10
- pow(a, b) : a의 b 제곱 - 반올림 및 정밀도 메서드
- ceil(x) : 올림
- floor(x) : 내림
- rint(x) : 가장 가까운 정수로 반올림
- round(x) : 반올림 - 삼각 함수 메서드
- sin(x) : 사인
- cos(x) : 코사인
- tan(x) : 탄젠트 - 기타 유용한 메서드
- sqrt(x) : 제곱근
- cbrt(x) : 세제곱근
- random() : 0.0과 1.0 사이의 무작위 double 값 생성
** 참고
- 너무 많은 기능을 제공하기 때문에 대략 어떤 것이 있는지 파악하는 정도면 충분하며 실제 필요할 때 검색하면 됨
- 아주 정밀한 숫자와 반올림 계산이 필요하다면 BigDecimal을 검색하여 사용하면 됨(돈계산 등)
package lang.math;
public class MathMain {
public static void main(String[] args) {
// 기본 연산 메서드
System.out.println("max(10, 20): " + Math.max(10, 20)); //최대값
System.out.println("min(10, 20): " + Math.min(10, 20)); //최소값
System.out.println("abs(-10): " + Math.abs(-10)); //절대값
// 반올림 및 정밀도 메서드
System.out.println("ceil(2.1): " + Math.ceil(2.1)); //올림
System.out.println("floor(2.7): " + Math.floor(2.7)); //내림
System.out.println("round(2.5): " + Math.round(2.5)); //반올림
// 기타 유용한 메서드
System.out.println("sqrt(4): " + Math.sqrt(4)); //제곱근
System.out.println("random(): " + Math.random()); //0.0 ~ 1.0 사이의 double 값
}
}
2) Random
(1) RandomMain
- 랜덤의 경우 Math.random()을 사용해도 되지만 Random 클래스를 사용하면 더욱 다양한 랜덤값을 구할 수 있음
- random.nextInt(): 랜덤 int 값을 반환
- nextDouble(): 0.0d ~ 1.0d 사이의 랜덤 double 값을 반환
- nextBoolean(): 랜덤 boolean 값을 반환
- nextInt(int bound): 0 ~ bound 미만의 숫자를 랜덤으로 반환, 3을 입력하면 0~2를 반환 함
- 1부터 특정 숫자의 int 범위를 구하는 경우 nextInt()의 결과에 +1을 하면 됨
** 참고
- Math.random()도 내부에서는 Random 클래스를 사용함
- Random클래스는 java.util 패키지 소속임
package lang.math;
import java.util.Random;
public class RandomMain {
public static void main(String[] args) {
Random random = new Random();
int randomInt = random.nextInt();
System.out.println("randomInt = " + randomInt);
double randomDouble = random.nextDouble();
System.out.println("randomDouble = " + randomDouble);
boolean randomBoolean = random.nextBoolean();
System.out.println("randomBoolean = " + randomBoolean);
// 범위 조회
int randomRange1 = random.nextInt(10); // 0 ~ 9 까지 출력
System.out.println("0 ~ 9 = " + randomRange1);
int randomRange2 = random.nextInt(10) + 1; // 1 ~ 10 까지 출력
System.out.println("1 ~ 10 = " + randomRange2);
}
}
(2) 씨드 - Seed
- 랜덤은 내부에서 씨드(Seed)값을 사용하여 랜덤값을 구하기 때문에 씨드 값이 같으면 항상 같은 결과가 출력됨
- 위의 코드를 아래처럼 입력하고 코드를 돌려보면 계속 동일한 결과가 출력되는 것을 확인할 수 있음
- new Random(): 생성자를 비워두면 내부에서 System.nanoTime()에 여러가지 복잡한 알고리즘을 섞어서 씨드값을 생성하므로 반복 실행해도 결과가 항상 달라짐
- new Random(1): 생성자에 씨드 값을 직접 전달할 수 있는데, 씨드 값이 같으면 여러번 반복 실행해도 실행결과가 같아지므로 랜덤값을 구할 수 없음
- 테스트 코드 같은 곳에서 같은 결과를 검증하고자 할 때 씨드 값을 주고 검증할 수 있음
Random random = new Random(1); // seed가 같으면 Random의 결과가 같음
5. 문제와 풀이
1) parseInt()
(1) 문제 설명
- 문자로 입력된 str1, str2 두 수의 합을 구하기
package lang.wrapper.test;
public class WrapperTest1 {
public static void main(String[] args) {
String str1 = "10";
String str2 = "20";
// 코드 작성
}
}
실행 결과
두 수의 합: 30
(2) 정답
package lang.wrapper.test;
public class WrapperTest1 {
public static void main(String[] args) {
String str1 = "10";
String str2 = "20";
int result = Integer.parseInt(str1) + Integer.parseInt(str2);
System.out.println("두 수의 합: " + result);
}
}
2) parseDouble()
(1) 문제 설명
- 배열에 입력된 모든 숫자의 합을 구하기
- 숫자는 double 형이 입력될 수 있음
package lang.wrapper.test;
public class WrapperTest2 {
public static void main(String[] args) {
String[] array = {"1.5", "2.5", "3.0"};
// 코드 작성
}
}
실행 결과
sum = 7.0
(2) 정답
package lang.wrapper.test;
public class WrapperTest2 {
public static void main(String[] args) {
String[] array = {"1.5", "2.5", "3.0"};
double sum = 0;
for (String s : array) {
double value = Double.parseDouble(s);
sum += value;
}
System.out.println("sum = " + sum);
}
}
3) 박싱, 언박싱
(1) 문제 설명
- String str을 Integer로 변환하여 출력
- Integer를 int로 변환하여 출력
- int를 Integer로 변환하여 출력
- 오토 박싱, 오토 언박싱을 사용하지 말고 직접 변환해야 함
package lang.wrapper.test;
public class WrapperTest3 {
public static void main(String[] args) {
String str = "100";
// 코드 작성
}
}
실행 결과
integer1 = 100
intValue = 100
integer2 = 100
(2) 정답
package lang.wrapper.test;
public class WrapperTest3 {
public static void main(String[] args) {
String str = "100";
Integer integer1 = Integer.valueOf(str);
int intValue = integer1.intValue();
Integer integer2 = Integer.valueOf(intValue);
System.out.println("integer1 = " + integer1);
System.out.println("intValue = " + intValue);
System.out.println("integer2 = " + integer2);
}
}
4) 오토 박싱, 오토 언박싱
(1) 문제 설명
- 3번의 문제와 동일한 과정을 오토 박싱, 오토 언박싱을 사용하여 변환
- 문제는 동일하므로 생략
(2) 정답
package lang.wrapper.test;
public class WrapperTest4 {
public static void main(String[] args) {
String str = "100";
Integer integer1 = Integer.valueOf(str);
int intValue = integer1;
Integer integer2 = intValue;
System.out.println("integer1 = " + integer1);
System.out.println("intValue = " + intValue);
System.out.println("integer2 = " + integer2);
}
}
5) 로또 번호 자동 생성기
(1) 문제 설명
- 로또 번호를 자동으로 만들어주는 자동 생성기 만들기
- 로또 번호는 1 ~ 45사이의 숫자를 6개 뽑아야 하며 숫자는 중복되면 안됨
- 실행할 때 마다 결과가 달라야 함
- 어떻게 풀든 실행 결과처럼 나오면 됨
실행 결과
로또 번호: 11 19 21 35 29 16
(2) 정답
LottoGenerator
package lang.math.test;
import java.util.Arrays;
import java.util.Random;
public class LottoGenerator {
private final Random random = new Random();
int max = 45;
int[] lottoArr = new int[6];
public int[] generateLotto() {
for (int i = 0; i < lottoArr.length; i++) {
int lottoNum = random.nextInt(max) + 1;
if (isUnique(i, lottoNum)) {
lottoArr[i] = lottoNum;
}
}
Arrays.sort(lottoArr);
return lottoArr;
}
private boolean isUnique(int i, int lottoNum) {
for (int j = 0; j < i; j++) {
if (lottoNum == lottoArr[j]) {
return false;
}
}
return true;
}
}
LottoGeneratorMain
package lang.math.test;
public class LottoGeneratorMain {
public static void main(String[] args) {
LottoGenerator lottoGenerator = new LottoGenerator();
int[] generateLottoArr = lottoGenerator.generateLotto();
System.out.print("로또 번호:");
for (int lotto : generateLottoArr) {
System.out.print(" " + lotto);
}
}
}