[Java] 래퍼, Class클래스

szlee·2024년 8월 12일
0

Java

목록 보기
27/34

< 김영한의 실전 자바 - 중급편 > 강의를 보고 이해한 내용을 바탕으로 합니다.




기본형의 한계

자바는 객체 지향 언어이지만 자바 안에 객체가 아닌 것이 있다.
바로 기본형이다. -- int, double.. 등

기본형은 객체가 아니기 때문에 다음과 같은 한계가 있다.

  • 메서드를 제공할 수 없다.
  • null값을 가질 수 없다. (때로는 데이터가 없음 이라는 상태를 나타내야할 필요가 있지만 기본형은 항상 값을 가지기 때문에 이런 표현이 불가하다.)



자바 래퍼 클래스

래퍼 클래스는 기본형을 객체로 감싸서 더 편리하게 사용하도록 도와준다.
즉, 래퍼 클래스는 기본형의 객체 버전이다.
자바는 기본형에 대응하는 래퍼 클래스를 기본으로 제공한다.

  • byte -> Byte
  • short -> Short
  • int -> Integer
  • long -> Long
  • float -> Float
  • double -> Double
  • char -> Character
  • boolean -> Boolean

그리고 자바가 제공하는 기본 래퍼 클래스는 불변이며 equals로 비교해야 한다는 특징을 가진다.

래퍼 클래스 생성 : 박싱(Boxing)

  • 기본형을 래퍼 클래스로 변경하는 것을 마치 박스에 물건을 넣은 것 같다고 해서 박싱이라 한다.
  • 래퍼클래스.valueOf()를 사용해서 객체를 생성한다. --Integer.valueOf(10)
    • 성능 최적화 기능이 있다. -128~127 범위의 Integer클래스를 미리 생성해준다.
      해당 범위의 값을 조회하면 미리 생성된 Integer객체를 반환하고, 없으면 내부적으로 new Integer()를 호출한다. (마치 문자열 풀과 비슷)

xxxValue() : 언박싱(Unboxing)

래퍼 클래스에 들어있는 기본형 값을 다시 꺼내는 메서드.
(박스에 들어있는 물건을 꺼내는 것 같다고 해서 언박싱이다.)
--intValue(), doubleValue()

비교 - equals() 사용

  • 래퍼 클래스는 객체이기 때문에 ==비교를 하면 인스턴스의 참조값을 비교한다.
  • 내부의 값을 비교하도록 equals()를 재정의. 값을 비교하려면 equals()를 사용해야한다.

래퍼 클래스는 객체를 그대로 출력해도 내부에 있는 값을 문자로 출력하도록 toString()도 재정의 되어있다.

오토 박싱 Autoboxing

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); //boxedValue = 7
 	System.out.println("unboxedValue = " + unboxedValue); //unboxedValue = 7
 }

위는 기존 박싱, 언박싱 기법.
자바 1.5 부터 오토 박싱, 오토 언박싱을 지원한다.

오토 박싱, 오토 언박싱

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); //boxedValue = 7
 	System.out.println("unboxedValue = " + unboxedValue); //unboxedValue = 7
 }

오토 박싱과 오토 언박싱은 컴파일러가 개발자 대신 valueOf(), xxxValue() 등의 코드를 추가해주는 기능. => 기본형과 래퍼형을 서로 편리하게 변환할 수 있다.

Integer boxedValue = value; //오토 박싱(Auto-boxing)
Integer boxedValue = Integer.valueOf(value); //컴파일 단계에서 추가

int unboxedValue = boxedValue; //오토 언박싱(Auto-Unboxing)
int unboxedValue = boxedValue.intValue(); //컴파일 단계에서 추가

래퍼 클래스의 주요 메서드와 성능

주요 메서드

  • valueOf() : 래퍼 타입을 반환. 숫자, 문자열 모두 지원
  • parseXxx() : 문자열을 기본형으로 변환
  • compareTo() : 내 값과 인수로 넘어온 값 비교. 내 값이 크다->1, 같다->0, 작다->-1 반환.
  • Xxx.sum(), Xxx.min(), Xxx.max() : static 메서드. 간단한 덧셈, 작은 값, 큰 값 연산 수행

성능

래퍼 클래스는 객체이기 때문에 기본형보다 다양한 기능 제공.
그렇다면 기본형은 왜 제공하는가?
기본형 연산이 래퍼 클래스보다 빠르기 때문이다.

  • 기본형은 메모리에서 단순히 그 크기만큼의 공간을 차지한다. --int는 보통 4바이트의 메모리 사용.
  • 래퍼 클래스의 인스턴스는 내부에 필드로 가지고 있는 기본형의 값 뿐 아니라 자바에서 객체를 다루는데 필요한 객체 메타데이터를 포함하므로 더 많은 메모리를 사용한다.
    (대략 8-16바이트의 메모리를 추가로 사용한다.)

그렇다면 기본형, 래퍼 클래스 중 어떤 것을 사용할까?

  • 이 연산은 10억 번의 연산을 수행했을 때 0.3초와 1.5초의 차이이므로, 둘 다 매우 빠르게 연산이 수행된다. => 일반적인 애플리케이션을 만드는 관점에서 보면 이런 부분을 최적화해도 티가 잘 안난다.
  • CPU연산을 아주 많이 수행하는 특수한 경우 or 수만-수십만 이상 연속해서 수행해야 하는 경우라면 기본형을 사용해서 최적화. 일반적인 경우라면 유지보수하기 더 나은 것


Class 클래스

자바에서 Class 클래스는 클래스의 정보(메타데이터)를 다루는데 사용된다.
Class 클래스를 통해 실행중인 자바 애플리케이션 내에서 필요한 클래스의 속성과 메서드에 대한 정보를 조회하고 조작할 수 있다.

  • 타입 정보 얻기 : 클래스의 이름, 슈퍼클래스, 인터페이스, 접근 제한자
  • 리플렉션 : 클래스에 정의된 메서드, 필드, 생성자 조회. 이들을 통해 인스턴스 생성하거나 메서드 호출 가능.
  • 동적 로딩과 생성 : Class.forName() 메서드를 사용하여 클래스를 동적으로 로드하고 newInstane() 메서드를 통해 새로운 인스턴스 생성 가능.
  • 애노테이션 처리 : 클래스에 적용된 애노테이션 조회하고 처리하는 기능 제공.
Class clazz = String.class; // 1.클래스에서 조회
Class clazz = new String().getClass();// 2.인스턴스에서 조회
Class clazz = Class.forName("java.lang.String"); // 3.문자열로 조회

Class 클래스의 주요 기능

  • getDeclaredFields() : 클래스의 모든 필드를 조회
  • getDeclaredMethods() : 클래스의 모든 메서드를 조회
  • getSuperclass() : 클래스의 부모 클래스 조회
  • getInterfaces() : 클래스의 인터페이스 조회

클래스 생성하기

Class 클래스에는 클래스의 모든 정보가 들어있다.
이 정보를 기반으로 인스턴스 생성하거나 메서드 호출하고 필드의 값 변경이 가능하다.

public static void main(String[] args) throws Exception {
 	Class helloClass = Class.forName("lang.clazz.Hello");
 	Hello hello = (Hello) helloClass.getDeclaredConstructor().newInstance(); 
 	String result = hello.hello();
 	System.out.println("result = " + result); //result = hello!
 }

getDeclaredConstructor().newInstance()

  • getDeclaredConstructor() : 생성자 선택
  • newInstance() : 선택된 생성자를 기반으로 인스턴스 생성

리플렉션 - reflection

Class를 사용하면 클래스의 메타 정보를 기반으로 클래스에 정의된 메서드, 필드, 생성자 등을 조회하고 이들을 통해 객체 인스턴스를 생성하거나 메서드를 호출하는 작업을 할 수 있다. => 리플렉션
추가로 애노테이션 정보를 읽어서 특별한 기능을 수행할 수도 있고, 최신 프레임워크들은 이런 기능을 적극 활용한다.



System 클래스

시스템과 관련된 기본 기능들을 제공한다.

  • 표준 입력, 출력, 오류 스트림 : 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-정상 종료, 그 외-오류나 예외적인 종료)
  • 배열 고속 복사 : System.arraycopy는 시스템 레벨에서 최적화된 메모리 복사 연산 사용. 직접 반복문을 사용하여 배열을 복사할 때 보다 수 배 이상 빠른 성능 제공.


Math, Random 클래스

Math 클래스

수학 문제 해결. 너무 많은 기능을 제공하므로 실제 필요할 때 검색하거나 API 문서 찾아보자.

public static void main(String[] args) {
 	// 기본 연산 메서드
 	System.out.println("max(10, 20): " + Math.max(10, 20)); //최대값 max(10, 20): 20
 	System.out.println("min(10, 20): " + Math.min(10, 20)); //최소값 min(10, 20): 10
 	System.out.println("abs(-10): " + Math.abs(-10)); //절대값 abs(-10): 10
	 
     // 반올림 및 정밀도 메서드
	System.out.println("ceil(2.1): " + Math.ceil(2.1)); //올림 ceil(2.1): 3.0
 	System.out.println("floor(2.7): " + Math.floor(2.7)); //내림 floor(2.7): 2.0
 	System.out.println("round(2.5): " + Math.round(2.5)); //반올림 round(2.5): 3
    
    // 기타 유용한 메서드
 	System.out.println("sqrt(4): " + Math.sqrt(4)); //제곱근 sqrt(4): 2.0
 	System.out.println("random(): " + Math.random()); //0.0 ~ 1.0 사이의
doublerandom(): 0.006347084592260965
 }

Random 클래스

Math.random()을 사용해도 되지만 Random 클래스를 사용하면 더욱 다양한 랜덤값을 구할 수 있다.
(Math.random()도 내부에서는 Random 클래스를 사용한다. Random 클래스는 java.util 패키지 소속이다.)

public static void main(String[] args) {
 	Random random = new Random();
 	//Random random = new Random(1); //seed가 같으면 Random의 결과가 같다.
 	
    int randomInt = random.nextInt();
 	System.out.println("randomInt: " + randomInt); //randomInt: -1316070581
    
 	double randomDouble = random.nextDouble(); //0.0d ~ 1.0d
    System.out.println("randomDouble: " + randomDouble); //randomDouble: 0.37735342193577215
    
 	boolean randomBoolean = random.nextBoolean();
 	System.out.println("randomBoolean: " + randomBoolean); //randomBoolean: false
 	
    // 범위 조회
 	int randomRange1 = random.nextInt(10); //0 ~ 9까지 출력
 	System.out.println("0 ~ 9: " + randomRange1); //0 ~ 9: 5
 
 	int randomRange2 = random.nextInt(10) + 1; //1 ~ 10까지 출력
 	System.out.println("1 ~ 10: " + randomRange2); //1 ~ 10: 7
 }
  • random.nextInt() : 랜덤 int 값 반환
  • nextDouble() : 0.0d~1.0d 사이의 랜덤 double값 반환
  • nextBoolean() : 랜덤 boolean 값 반환
  • nextInt(int bound) : 0~bound미만의 숫자를 랜덤으로 반환. --3 입력 시 0, 1, 2 반환
  • nextInt(int bound)+1 : 1부터 특정 숫자의 int 범위 구하기

seed

랜덤은 내부에서 씨드 값을 사용해서 랜덤 값을 구한다. 이 씨드 값이 같으면 항상 같은 결과가 출력된다.

Random random = new Random(1); //seed가 같으면 Random의 결과가 같다.

Seed가 같으면 실행 결과는 반복 실행해도 같다.

  • new Random() : 생성자를 비워두면 내부에서 System.nanoTime()에 여러가지 복잡한 알고리즘을 섞어서 씨드값을 생성한다. --> 반복 실행해도 결과가 항상 달라진다.
  • new Random(int seed) : 생성자에 씨드 값을 직접 전달할 수 있다. 씨드 값이 같으면 여러번 반복 실행해도 실행 결과가 같다. 결과가 고정되므로 테스트 코드 같은 곳에서 같은 결과를 검증할 수 있다.
profile
🌱

0개의 댓글