관리 메뉴

나구리의 개발공부기록

래퍼와 Class 클래스, 래퍼 클래스(기본형의 한계, 자바 래퍼 클래스, 오토 박싱, 주요 메서드와 성능), Class 클래스, System 클래스, Math 클래스, Random 클래스 본문

인프런 - 실전 자바 로드맵/실전 자바 - 중급 1편

래퍼와 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);
        }
    }
}