2025. 3. 27. 15:39ㆍJava
◎ 기본형의 한계
-> 자바의 int, double과 같은 기본형에는 몇가지 한계가 있다.
- 객체가 아님 : 기본형 데이터는 객체가 아니기 떄문에 객체와 관련된 메서드 등을 이용할 수 없다.
- null 값을 가질 수 없음 : 기본형은 null 값을 가질 수 없다. 데이터가 없는 상태를 나타내야 할 때는 기본형 데이터로는 나타낼 수 없다.
-> 다음은 기본형 데이터를 이용한 두 정수의 대소 관계를 비교하는 코드이다.
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);
}
private static int compareTo(int value, int target) {
if (value < target) {
return -1;
} else if (value > target) {
return 1;
} else {
return 0;
}
}
}
-> 결과
-> value라는 int형 값을 compareTo()라는 외부 메서드를 이용해 두 정수를 비교한다. 이 경우 자기 자신인 value와 다른 값을 연산하는 것이기 때문에 항상 자기 자신의 값인 value가 사용된다. value가 객체였다면 value객체 스스로 자기 자신의 값과 다른 값을 비교한는 메서드를 만드는 것이 더 효율적이다.
-> 다음은 int값을 가지고 클래스를 만들어 클래스에서 제공하는 메서드를 이용해 두 정수의 대소관계를 비교하는 코드이다.
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); // 숫자를 문자로 변환
}
}
-> MyInteger 클래스는 int value라는 기본형 변수를 가지고 있다. 이 기본형 변수를 편리하게 사용하도록 메서드를 제공한다.
-> compareTo() 메서드를 클래스 내부로 캡슐화했다. 이제 int를 MyInteger 클래스를 통해 객체로 다룰 수 있다.
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);
}
}
-> 결과
-> myInteger.compareTo()는 자기 자신의 값을 외부의 값과 비교한다.
-> MyInteger는 객체이므로 해당 객체가 가진 메서드를 편리하게 호출할 수 있다. 단순히 int형만 사용했을 경우 int가 기본형이기 때문에 메서드를 스스로 갖지 못했던 문제를 해결할 수 있다.
-> 기본형은 항상 값을 가져야 한다. 따라서 null(데이터 없음)인 상태가 될 수 없다. 다음은 기본형만을 사용하여 int 배열에 있는 값을 찾는 코드이다.
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, 17));
}
private static int findValue(int[] intArr, int target) {
for (int value : intArr) {
if (value == target) {
return value;
}
}
return -1;
}
}
-> 특정 값이 intArr 이라는 int형 배열에 존재하는지 검사하여 있으면 해당 값을 리턴하고, 아니면 -1을 리턴하도록 했다.
-> 결과
-> intArr에 -1이 있는지 검증하였고, 해당 배열에는 -1이 있기 때문에 찾고자 하는 값 -1을 리턴하여 출력했다. 또한 17이라는 값은 intArr에 없기 때문에 -1을 리턴하여 출력했다. 이렇게 되면 출력 결과만 봤을 때 해당 값이 배열에 존재하지 않아 -1이 출력된건지 찾고자 하는 값이 -1이라 -1을 출력한 것인지 알 수 없다. 이렇듯 기본형은 반드시 어떠한 값이 들어가 있어야 하기 때문에 이런 문제가 발생한다.
-> 이번에는 이전에 만들었던 MyInteger 객체를 사용해 해당 객체의 배열을 만들어 배열 내 값을 찾는 코드를 만들었다.
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, 17));
}
private static MyInteger findValue(MyInteger[] intArr, int target) {
for (MyInteger myInteger : intArr) {
if (myInteger.getValue() == target) {
return myInteger;
}
}
return null;
}
}
-> MyInteger를 생성하여 MyInteger의 int형 필드 value에 값을 넣고, 특정 값을 찾을 때 해당 객체의 getValue()메서드를 이용해 value 필드의 값을 꺼내와서 특정 값과 비교하였고, 그 값이 있으면 해당 값을 출력하고 없으면 null을 출력했다.
-> 결과
-> 이렇게 객체를 이용하여 비교하면 어떤 값이 배열에 존재하지 않는지 명확하게 알 수 있다.
◎ 래퍼 클래스
-> 위에서 설명한 래퍼 클래스는 자바의 기본형을 객체로 감싸고, 객체에 메서드를 정의하여 편리하게 사용하도록 했다.
-> 래퍼 클래스는 기본형의 객체 버전이다.
-> 자바는 각각의 기본형에 대응하는 래퍼 클래스를 제공한다.
- byte -> Byte
- short -> Short
- int -> Integer
- long -> Long
- float -> Float
- double -> Double
- char -> Character
- boolean -> Boolean
-> 자바가 제공하는 기본 래퍼 클래스는 불변하고, equals(객체라서 new 키워드로 생성하면 서로 다른 참조 값을 가진 인스턴스가 생성되기 때문)로 비교해야 한다는 특징을 가지고 있다.
public class WrapperClassMain {
public static void main(String[] args) {
Integer newInteger = new Integer(10); // 미래에 삭제 예정. 가급정 valueOf()를 사용할 것
Integer integerObj = Integer.valueOf(10);
System.out.println("newInteger = " + newInteger);
System.out.println("integerObj = " + integerObj);
Long longObj = Long.valueOf(100);
Double doubleObj = Double.valueOf(10.7);
System.out.println("longObj = " + longObj);
System.out.println("doubleObj = " + doubleObj);
System.out.println();
System.out.println("내부 값 읽기");
int iv = integerObj.intValue();
long lv = longObj.longValue();
double dv = doubleObj.doubleValue();
System.out.println("iv = " + iv);
System.out.println("lv = " + lv);
System.out.println("dv = " + dv);
System.out.println();
System.out.println("비교");
System.out.println("== : " + (newInteger == integerObj));
System.out.println("equals : " + (newInteger.equals(integerObj)));
}
}
-> 결과
-> Integer.valueOf(10)를 사용하면 Integer 객체를 생성한다.
-> 이렇게 기본형 값을 객체를 생성하여 넣는 것을 박싱(boxing)이라 한다.
-> Integer.valueOf()에는 -128~127 범위의 Integer 클래스를 미리 생성하여 해당 범위 값을 조회하면 미리 생성된 Integer 객체를 반환한다.(최적화 기능임) 즉, -128~127 범위 내의 값을 넣고 Integer 객체들을 생성하고 == 비교를 하면 true가 나온다.
-> 반대로 intValue()를 통해 Integer에 있는 int형 값을 꺼내는 것을 언박싱(unboxing)이라 한다.
◎ 오토 박싱/언박싱
-> 기본형을 래퍼 클래스로 변환할 때 valueOf()를 래퍼 클래스를 기본형으로 가져올 때는 xxxValue()를 사용하여 각각 박싱과 언박싱을 했다.
-> 자바에서는 기본형을 래퍼 클래스로 변환하거나 래퍼 클래스를 기본형으로 변환할 때 자동으로 변환하도록 해준다.
public class AutoBoxingMain2 {
public static void main(String[] args) {
// primitive -> wrapper
int value = 7;
Integer boxedValue = value; // auto boxing
// wrapper -> primitive
int unBoxedValue = boxedValue; // auto unboxing
System.out.println("boxedValue = " + boxedValue);
System.out.println("unBoxedValue = " + unBoxedValue);
}
}
-> 위 처럼 따로 변환 메서드를 사용하지 않아도 알아서 박싱과 언박싱을 한다.
-> 실제로는 컴파일러가 아래와 같이 valueOf()와 xxxValue()를 추가하여 박싱과 언박싱을 한다.
Integer boxedValue = value; // auto boxing
Integer boxedValue = Integer.valueOf(value); // 컴파일러가 해당 코드를 자동으로 추가
int unboxedValue = boxedValue; // auto unboxing
int unboxedValue = boxedValue.intValue(); // 컴파일러가 해당 코드를 자동으로 추가
◎ 래퍼 클래스 성능
-> 다음은 기본형과 래퍼 클래스의 성능 차이를 비교하는 코드이다.
public class WrapperVsPrimitive {
public static void main(String[] args) {
int iterations = 1000000000; // 반복 횟수
long starTime, endTime;
// 기본형 long
long sumPrimitive = 0;
starTime = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
sumPrimitive += i;
}
endTime = System.currentTimeMillis();
System.out.println("sumPrimitive : " + sumPrimitive);
System.out.println("기본 자료형 long 실행 시간 : " + (endTime - starTime) + "ms");
}
}
-> 먼저 기본형 long을 10억번 더하는 연산이다.
-> 결과
public class WrapperVsPrimitive {
public static void main(String[] args) {
int iterations = 1000000000; // 반복 횟수
long starTime, endTime;
// 래퍼 클래스 Long 사용
Long sumWrapper = 0L;
starTime = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
sumWrapper += i; // 오토 박싱 발생
}
endTime = System.currentTimeMillis();
System.out.println("sumWrapper = " + sumWrapper);
System.out.println("래퍼 클래스 Long 실행 시간 : " + (endTime - starTime) + "ms");
-> 다음은 래퍼 클래스 Long을 이용한 코드이다.
-> 결과
-> 연산 결과 기본형이 더 빠른 것을 알 수 있다.
-> 기본형은 메모리에서 단순히 해당 기본형 크기만큼만 차지한다. int형이면 4byte의 메모리를 사용한다. 반면 래퍼 클래스는 내부에 필드로 가지고 있는 기본형의 값과 자바에서 객체를 다루는데 필요한 객체의 메타데이터를 포함하고 있기 때문에 더 많은 메모리를 차지한다.
◎ Class 클래스
-> 클래스의 정보(메타 데이터)를 다루른데 사용한다. Class를 통해 개발자는 실행 중인 자바 애플리케이션 내에 필요한 클래스 속성과 메서드에 대한 정보를 조회 및 조작을 할 수 있다.
-> 다음은 Class 클래스의 주요 기능이다.
- 타입 정보 얻기 : 클래스 이름, 슈퍼 클래스, 인터페이스 등과 같은 정보 조회
- 리플렉션 : 클래스에 정의된 메서드, 필드, 생성자 등을 조회하고 이를 통해 객체 인스턴스를 생성하거나 메서드를 호출하는 등의 작업 가능
- 동적 로딩과 생성 : Class.forName() 메서드를 이용해 클래스를 동적으로 로드하고, newInstance()를 통해 새로운 인스턴스를 생성한다.
- 애너테이션 처리 : 클래스에 적용된 애너테이션을 조회하고 처리하는 기능 제공
-> 다음은 String 클래스에 대한 메타 데이터를 조회하는 코드이다.
public class ClassMetaMain {
public static void main(String[] args) throws Exception {
// Class 조회
Class clazz = String.class; // 1. 클래스에서 조회
// Class clazz = new String().getClass(); // 2. 인스턴스에서 조회
// Class clazz = 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("i = " + i.getName());
}
}
}
-> Class 클래스는 다음과 같이 3가지 방법으로 조회할 수 있다.
Class clazz = String.class; // 1. 클래스에서 조회
Class clazz = new String().getClass(); // 2. 인스턴스에서 조회
Class clazz = Class.forName("java.lang.String"); // 3. 문자열로 조회
◎ System 클래스
-> System과 관련된 기본 기능을 제공하는 클래스
public class SystemMain {
public static void main(String[] args) {
// 현재 시간(밀리초)
long currentTimeMillis = System.currentTimeMillis();
System.out.println("currentTimeMillis = " + currentTimeMillis);
// 현재 시간(나노초)
long currentTimeNano = System.nanoTime();
System.out.println("currentTimeNano = " + currentTimeNano);
// 환경 변수 조회
System.out.println("getenv = " + System.getenv());
// 시스템 속성 조회
System.out.println("properties = " + System.getProperties());
System.out.println("Java Version : " + System.getProperty("java.version"));
// 배열 고속 복사
char[] originalArr = {'h', 'e', 'l', 'l', 'o'};
char[] copiedArr = new char[5];
System.arraycopy(originalArr, 0, copiedArr, 0, originalArr.length);
// 배열 출력
System.out.println("copiedArr = " + copiedArr);
System.out.println("Arrays.toString = " + Arrays.toString(copiedArr));
// 프로그램 종료
System.exit(0);
}
}
★ 참고 및 출처
'Java' 카테고리의 다른 글
중첩 클래스 ,내부 클래스1 (0) | 2025.04.07 |
---|---|
열거형 - ENUM (0) | 2025.04.01 |
String 클래스 (0) | 2024.11.18 |
불변객체 (0) | 2024.10.31 |
Object 클래스 (0) | 2024.10.29 |