래퍼 클래스(Wrapper Class) 변수
프로그램에 따라 기본 타입의 데이터를 객체로 취급해야 하는 경우가 있다. 예를 들어, 메소드의 인수로 객체 타입만이 요구되면, 기본 타입의 데이터를 그대로 사용할 수 없기 때문에 객체로 변환해야 한다.
래퍼 클래스는 기본 자료타입을 객체로 다루기 위해, 기본형 변수를 인수로 전달 받아 클래스로 랩핑하여 해당 값을 가지는 객체로 만들어 준다.
박싱 VS 언박싱
- 박싱 : 기본 타입에서 래퍼 클래스 변수로 변수를 감싸 인스턴스로 변화시키는 것
- 언박싱 : 래퍼 클래스 변수를 기본 타입 변수로 가져오는 것, 객체에서 기본 타입의 데이터를 얻어냄
// 박싱
// Integer 래퍼 클래스 num 에 21 의 값을 저장
int number = 21;
Integer num = new Integer(number);
// 언박싱
int n = num.intValue(); // 래퍼 클래스들은 intValue() 같은 언박싱 메서드들을 제공, Integer 객체 안의 int 값을 가져옴
// 박싱과 언박싱이 필요한 상황에서 자바 컴파일러가 이를 자동으로 처리해주는 오토 박싱과 오토 언박싱 기능이 있음
Character ch = 'X'; // Character ch = new Character('X'); : 오토박싱
char c = ch; // char c = ch.charValue(); : 오토언박싱
Integer num1 = new Integer(7); // 박싱
Integer num2 = new Integer(3); // 박싱
int int1 = num1.intValue(); // 언박싱
int int2 = num2.intValue(); // 언박싱
// 내부적으로 래퍼 클래스인 피연산자를 오토언박싱하여 기본 타입끼리의 연산을 수행
Integer result1 = num1 + num2; // 10
Integer result2 = int1 - int2; // 4
int result3 = num1 * int2; // 21
값 비교
public class Wrapper {
public static void main(String[] args) {
Integer num1 = new Integer(10); // 래퍼 클래스
Integer num2 = new Integer(10); // 래퍼 클래스
Integer num3 = new Integer(20); // 래퍼 클래스
int n = 10; // 기본 타입
System.out.println(num1 < num3); // true
System.out.println(num1 == num2); // false
System.out.println(num1.equals(num2)); // true
System.out.println(num1 == n); // true
System.out.println(num1.equals(n)); // true
}
}
래퍼 클래스의 비교 연산도 오토언박싱을 통해 가능해지지만, 래퍼 객체는 내부 값 비교를 위해 equals() 메소드를 사용해야 한다.
래퍼 클래스도 객체이므로 동등 연산자(==)를 사용하게 되면, 두 인스턴스의 값을 비교하는 것이 아니라 두 인스턴스의 주소값을 비교하게 된다. (== 연산자는 내부의 값이 아니라 주소값을 비교함)
컴파일러가 자동으로 오토박싱과 언박싱을 하기 때문에 래퍼 클래스와 기본자료형과의 비교는 == 연산과 equals연산 모두 가능하다.
Casting(형변환)
래퍼 클래스에서 제공하는 메소드를 이용하여 문자열을 숫자로 변경할 수 있다.
// Integer 래퍼 클래스의 parse 메소드를 사용하여 문자열을 숫자로 형변환
String str1 = "10";
String str2 = "3.14";
int num1 = Integer.parseInt(str1);
double num2 = Double.parseDouble(str2);
참조형
참조형 변수 = 주소값을 저장하는 주소형 변수
기본형 변수가 실제 값을 저장하는 저장공간 이라면, 참조형 변수는 실제 값이 아닌 원본값의 주소값을 저장한다.
- 기본형 변수 : 원본값이 Stack 영역에 있다.
- 참조형 변수 : 원본값이 Heap 영역에 있다. Stack 영역에는 따로 저장 해둔 원본값의 Heap 영역주소를 저장한다.
- Stack 영역 : 정적으로 할당된 메모리 영역이다. (크기가 몇 byte 인지 정해져있는 기본형 변수를 저장한다. 크기가 정해진 참조형 변수의 주소값도 저장한다.) 함수의 호출과 관계되는 지역 변수와 매개변수가 저장되며, 해당 변수들은 함수 호출 시 생성되고 함수 종료 시 소멸한다. 스택 영역은 후입선출(LIFO; Last-In First-Out) 방식으로 동작한다.
- Heap 영역 : 동적으로 할당된 메모리 영역이다. 크기가 계속 늘어날 수 있는 참조형 변수의 원본을 저장한다. 사용자에 의해 메모리 공간이 동적으로 할당 및 해제 된다.
- 정적 할당 : 컴파일 단계에 메모리 공간을 할당 받는다. 컴파일해서 메모리 공간 할당 시 실행 단계에서 할당이 해제되는 일은 없고, 함수가 종료되면 운영체제가 알아서 해당 메모리 공간을 회수해가기 때문에 메모리 누수를 걱정하지 않아도 된다. 그러나 메모리 공간의 크기가 정해져 있어 프로그램이 실행될 때 크기를 조절할 수 없고 메모리 공간의 낭비가 발생할 수 있다.
- 동적 할당 : 실행 단계에 메모리 공간을 할당 받는다. 포인터를 사용하여 Heap 영역을 가리켜 해당 공간을 할당한다. 원하는 만큼의 메모리가 맞춤으로 할당되며 실행 중 유동적으로 크기가 조절된다. 실행 단계에서 메모리 공간을 할당받고 해제하니 경제적이다. 그러나 더 이상 사용하지 않는 공간을 사용자가 직접 해제해주어야 하기 때문에 메모리 누수의 가능성이 존재한다.
형변환
형변환은 주로 기본형 변수인 정수 ↔ 실수 ↔ 문자 사이에서 일어난다.
명시적 형변환 / 강제 형변환(= 캐스팅)
표현 범위가 넓은 데이터 타입을 좁은 데이터 타입으로 변환해주지만, 데이터 값에 손실이 생긴다. 값의 손실이 생기기 때문에 자동으로 형변환을 해주지 않고 개발자가 선택하여 형변환을 한다.
// (Int)캐스팅 방식으로 실수를 정수로 치환, 실수형의 소수점아래자리는 버려짐
double doubleNumber = 10.101010;
float floatNumber = 10.1010
int intNumber;
intNumber = (int)doubleNumber; // double -> int 형변환
intNumber = (int)floatNumber; // float -> int 형변환
// (Double,Float) 캐스팅으로 정수형을 실수형으로 변환
int intNumber = 10;
double doubleNumber = (double)intNumber; // int -> double 형변환
float floatNumber = (float)intNumber; // int -> float 형변환
// 문자형을 정수형으로 변환
char letter = 'a';
char letter2 = '1';
int asciiNumber = (int)letter; // 숫자로 형변환을 해주면 저장되어있던 아스키 숫자값으로 표현됨, 97 저장
// +) 명시적 타입 캐스팅 대신 사용 가능한 메소드
int num = Character.getNumericValue(letter); // a의 아스키코드값 97 저장
num = Character.getNumericValue(letter2); // 숫자로 된 char 형은 숫자 형태 그대로 반환함, 1 저장
묵시적 형변환 / 자동 형변환
표현 범위가 작은 크기의 타입에서 큰 크기의 타입으로 형변환이 발생한다. 값의 손실이 없기 때문에 형변환을 직접적으로 캐스팅하지 않아도 자동으로 형변환이 되는 경우로, 프로그램 실행 도중에 값을 저장하거나 계산할 때 자동으로 타입변환이 일어난다.
// 작은 크기의 타입에서 큰 크기의 타입으로 저장될때 큰 크기로 형변환이 발생
byte byteNumber = 10;
int intNumber = byteNumber; // byte -> int 형변환
System.out.println(intNumber); // 10
char charAlphabet = 'A';
intNumber = charAlphabet; // char -> int 형변환
System.out.println(intNumber); // A의 유니코드 : 65
intNumber = 100;
long longNumber = intNumber; // int -> number 형변환
System.out.println(longNumber); // 100
intNumber = 200;
double doubleNumber = intNumber; // int -> double 형변환
System.out.println(doubleNumber); // 200.0 (소수점이 추가된 실수출력
// 작은 크기의 타입이 큰 크기의 타입과 계산될때 자동으로 큰 크기의 타입으로 형변환이 발생
int intNumber = 10;
double doubleNumber = 5.5;
double result = intNumber + doubleNumber; // result 에 15.5 저장됨 (int -> double)
intNumber = 10;
int iResult = intNumber / 4; // iResult 에 2 저장됨 (int형 연산 -> 소수점 버려짐)
intNumber = 10;
double dResult = intNumber / 4.0; // dResult 에 2.5 저장됨 (double형 연산 -> 소수점 저장)
'Java' 카테고리의 다른 글
컬렉션 (0) | 2023.10.20 |
---|---|
연산자, for 문, 배열과 String 메소드 (0) | 2023.10.19 |
JVM, Java 실행 과정, 변수 (1) | 2023.10.13 |
문자열 비교 (0) | 2023.10.02 |
프로그래머스) 문자열 정렬하기 (1) | 2023.09.28 |