회고록 블로그
[공부 필기] Java 기본 공부하기 (28) 본문
공부 중인 강의 : 윤성우 선생님, 윤성우의 열혈 Java 프로그래밍 강의.
링크 : https://cafe.naver.com/cstudyjava
※ 강의 청강 중 필요한 내용만 필기함
※ 틀린 필기가 있을 수 있음..
1. Wrapper Class (래퍼 클래스)
- Wrap(감싸다)
- Wrapper Class : 기본 자료형의 값(3, 1.25, 'a' 등)을 인스턴스로 감싸는 것
→ 값(3, 1.25, 'a' 등)을 가지고 있는 인스턴스를 만든다고 생각해도 됨
- 왜 값을 감싸는 걸까
→ 제네릭에 가면 공부하게 되는데, 여기에서 간단하게 설명하면 이러함
→ 메소드가 하나 있다고 가정
* 인자로 [인스턴스]를 전달하는 메소드라고 가정 (정확하게는 인스턴스의 참조값을 전달할 것임)
* 만약 인자로 전달되는 값이 인스턴스가 아니라 "정수 3, 실수 1.25" 등 이라면?
인자로 전달될 수가 없음
* 인자로 [인스턴스]를 전달할 수 있기 때문
* 그렇다면 이 값들을 '인스턴스화' 시켜야함
- 즉, 인스턴스를 전달해야하는데, 부득이하게 기본 자료형의 값을 전달해야하는 상황이라면 Wrapper Class를 사용함
- 코드 예시
→ "Hello"라는 문자열(String형)을 인자로 전달받아 출력해주는 메소드 printData
→ 매개변수가 Object형이기 때문에 인스턴스를 받을 수 있음
→ 정수, 실수를 받으려면 인스턴스화 시켜야함 (그림2 참고)
→ 이러면 Integer 인스턴스가 생성되고 그 안에 '3'이라는 값이 저장됨
- 이렇게 기본 자료형의 값을 인스턴스로 감싸는 목적의 클래스가 Wrapper Class임
# 주의사항
사실 JDK 버전 9 부터 Integer, Double 클래스를 사용할 때, 생성자는 사용하지 않는다고 한다.
1> Integer 생성자 (매개변수가 int value 일 때)
: Integer num = new Integer(3); 보다는 Integer num = Integer.valueOf(3); 이 훨씬 공간이나 시간적으로 퍼포먼스가 좋다고 한다.
2> Integer 생성자 (매개변수가 String s 일 때)
: String형 값(= "3")을 정수형으로 변환한다고 가정할 때
: Integer num = new Integer("3"); 보다는 Integer num = Integer.parseInt("3");이나 Integer.valueOf("3"); 을 사용하라고 한다.
또한 String형 값을 인자로 Integer 인스턴스를 생성할 때 사용하는 메소드 "valueOf"와 "parseInt",
이 둘의 차이점을 찾기 위해서 API Documentation 문서를 찾아봤다.
- valueOf : String형 값이 Integer 객체로 반환됨
(전달된 문자열의 아스키코드 값이 반환되는게 아니라, 우리가 눈으로 보는 '그 숫자' 그대로 반환됨)
인자는 부호 있는 10진수로 해석됨
- parseInt : 부호가 있는 10진수를 인자로 전달받음
입력받은 문자열은 10진수가 되는데, 만약 첫번째 문자에 '-'가 있으면 음의 값(음수)으로 나타내고
'+'가 있으면 양의 값(양수)로 나타냄
결과로 나온 값은 정수(숫자)로 반환됨 (객체로 반환 아님)
그렇다고 한다.
##
- 래퍼 클래스의 종류는 아래와 같음
→ 기본 자료형 boolean의 래퍼 클래스 : Boolean
→ 기본 자료형 char의 래퍼 클래스 : Character
→ 기본 자료형 byte의 래퍼 클래스 : Byte
→ 기본 자료형 short의 래퍼 클래스 : Short
→ 기본 자료형 int의 래퍼 클래스 : Integer
→ 기본 자료형 long의 래퍼 클래스 : Long
→ 기본 자료형 float의 래퍼 클래스 : Float
→ 기본 자료형 double의 래퍼 클래스 : Double
- Float 객체와 Double 객체
→ 생성자 사용을 권장하지 않지만 어쨌든 Float 클래스에는 생성자가 세 가지 있음 (그림6 참고)
→ 매개변수가 double형인 생성자, 매개변수가 float형인 생성자, 매개변수가 String형인 생성자
→ 매개변수가 double형인 생성자가, float형인 생성자가 각각 존재하는 이유는 "전달되는 값이 double로 해석될 수 있기 때문"
* 만약 인자로 "1.1"을 전달한다고 가정
* "1.1" 뒤에 'F'를 붙인다면 float형으로 인식되겠지만, 기본적으로 리터럴은 컴파일러에 의해 double형으로 인식이 됨
* 큰 자료형(double)에서 작은 자료형(float)으로 바뀔 땐 강제 형변환이 필요하기 때문에
매개변수가 double형인 생성자가 존재하는 것
→ Double 클래스의 생성자는 두 가지 뿐인데, 그 이유 또한 형변환 때문
* 만약 인자로 "1.1F"가 전달되었다고 하더라도, 작은 자료형(float)은 큰 자료형(double)으로 자동 형변환 가능하기 때문에
매개변수가 float형인 생성자가 별도로 필요 없는 것
- 래퍼 클래스는 두 가지의 기능을 함
→ 첫번째는 Boxing (상자에 담는 것)
* 기본 자료형의 값을 인스턴스에 담는 것을 말함
// Integer numObj = new Integer(3);과 같은 의미
Integer numObj = Integer.valueOf(3);
→ 두번째는 Unboxing (상자에서 꺼내는 것)
* 기본 자료형의 값을 인스턴스에서 꺼내는 것을 말함
int number = numObj.intValue();
- 한가지 유의할 점은 래퍼 클래스는 Immutable object라는 것
→ 인스턴스를 한번 생성했으면 그 상태를 변경할 수 없음
→ 즉, 값(3, 1.1, 'c' 등)을 넣어서 인스턴스를 만들었다면 그 값을 변경할 수 없음
* 따라서 값을 변경하고 싶다면 새로운 인스턴스를 생성해야함
- 아래와 같이 num1의 값을 수정하는 코드를 작성할 수는 있지만,
이건 num1의 값이 [변경]된게 아니라 [새로운 인스턴스]를 가리키게 된 것
→ 만약 num1의 값이 변경되었다면 num2도 변경되었어야함
2. 오토 박싱(auto-boxing)과 오토 언박싱(auto-unboxing)
- 오토 박싱/언박싱은 [자동으로] 값이 인스턴스에 넣어지고 꺼내지는 것을 의미함
- 인스턴스가 와야할 위치에 값이 오면 오토 박싱이 진행되고
값이 와야할 위치에 인스턴스가 오면 언박싱이 진행됨
- 언제 오토 박싱이 진행될까
→ 대입 연산자를 기준으로 했을 때, 왼쪽에는 참조변수가 오고 오른쪽에는 리터럴이 올 때
* 참조변수가 온다는 것은 오른쪽의 피연산자에 [인스턴스]가 와야한다는 의미
→ 예시
// Integer num = new Integer(3); 또는 Integer num = Integer.valueOf(3);
// 위와 동일
Integer num = 3;
- 언제 오토 언박싱이 진행될까
→ 대입 연산자를 기준으로 했을 때, 왼쪽에는 기본 자료형+변수명이 오고 오른쪽에는 인스턴스가 올 때
* 원래 언박싱은.. 인스턴스의 intValue 메소드를 이용해서 값(정수)을 리턴 받은 후 리턴 받은 값을 변수에 저장하는 형태였음
→ 예시
// Integer num = Integer.valueOf(3);
// int number = num.intValue();
// 위의 코드와 동일
int number = num;
# 참고사항
Integer num = Integer.valueOf(3); 과 Integer num = 3;의 차이점?
→ Integer 객체를 대입하느냐, 리터럴을 대입하느냐
위에서 정리했듯이 박싱이냐 오토 박싱이냐의 차이이다.
리터럴을 넣어도 컴파일러는 자동으로 Integer.valueOf(3); 로 바꿔준다.
##
- 오토 박싱과 오토 언박싱이 적용된 놀라운 예시
→ 이 코드가 실행된다는건 굉장히 놀라운 일
→ 사실 '++' 연산자(증감 연산자)는 정수나 실수 등 자료형의 변수가 와야함
→ 하지만 [그림9]에서는 인스턴스 참조변수가 피연산자로 왔음 (심지어 에러 없이 결과가 출력됨)
→ 이는 컴파일러가 오토 박싱과 오토 언박싱을 진행해서 코드를 해석해주었기 때문
...
numObj++; // numObj = new Integer(numObj.intValue() + 1); 과 동일
...
→ 사실 numObj++; 는 아래와 같이 코드가 작성되어야 함
(1) numObj.intValue()를 처리해서 numObj의 값(정수)을 반환받음
* numObj.intValue() // 언박싱
(2) 반환 받은 값에 1을 더함 (예시에서는 정수 4가 됨)
* numObj.intValue() + 1
(3) 정수 4를 인자로 전달하는 Integer 인스턴스를 생성함
* new Intger(numObj.intValue() +1)
(4) 참조변수 numObj가 새로 생성된 Intger 인스턴스를 가리키도록 함
* numObj = new Intger(numObj.intValue() +1) // 박싱
- 물론 '++' 연산자(증감 연산자) 뿐만 아니라 복합 대입 연산자(+=, -= 등)도 컴파일러에 의해 오토 박싱과 오토 언박싱이 진행됨
3. Number 클래스
- 모든 래퍼 클래스의 부모 클래스
- java.lang.Number 클래스에는 추상 메소드들이 있음
- 앞에서 배웠던 내용에 의하면 추상 메소드는 클래스에서 구현(정의)해줘야 한다고 했음
→ 즉, Number 클래스를 상속하는 하위 클래스에서 추상 메소드를 구현해줘야함
→ 구현해주지 않으면 인스턴스 생성이 불가능함
- 따라서 래퍼 클래스(Integer, Double 등)에는 Number의 추상 메소드(intValue, longValue, floatValue, doubleValue 등)가 모두 구현되어 있다는 얘기
→ Integer 인스턴스에서 intValue만 사용할 수 있는게 아니라 doubleValue, longValue 등도 사용 가능하다는 이야기
→ 그럼, 프로그래머가 원한다면 Integer 인스턴스에 저장된 값(정수)을 double형으로도 반환할 수도 있다는 것
4. BigInteger 클래스 & BigDecimal 클래스
- long형 보다 더 큰 정수를 표현해야할 때 사용하는 클래스 : BigInteger
- 아주 작은 오차도 허용하지 않는 연산을 지원하기 위해 디자인된 클래스 : BigDecimal
→ float과 double형은 정밀도의 차이가 있다고 했었고
→ 실수 연산은 오차가 발생할 수 밖에 없다고 했었는데
→ BigDecimal 클래스는 작은 오차도 허용하지 않는 연산(우주공학, 수학 등)을 위해 마련되었음
→ 다만, 실수형(float, double)보다 연산 속도가 늦음
* 따라서, 정말 작은 오차도 허용할 수 없는 상황일 때 사용하는걸 권장함
* 실수 연산 시 작은 오차라도 발생하면 굉장히 큰 문제가 발생기는 상황(우주산업 등) 등이 아니라면
일반적으로 double 타입의 정밀도를 사용해도됨
- java.math.BigInteger 클래스
→ Immutable(불변의) 객체임
→ BigInteger 인스턴스 생성 방법
* 인자를 큰따옴표로 묶어 문자열로 표현한 이유는 '숫자(정수)'로 표현할 수 없기 때문임
* 만약 큰따옴표를 지우고 컴파일을 하면,
컴파일러는 "int형(리터럴을 저장하는 기본 단위)으로 표현할 수 있는 범위"를 넘어섰기 때문에 에러를 발생시킴
BigInteger big = new BigInteger("10000000000000000000000000000000");
- java.lang.BigDecimal 클래스
→ BigDecimal 인스턴스 생성 방법
* BigDecimal은 double형으로 표현할 수 있는 범위를 넘어선 것도 아닌데 왜 큰따옴표로 묶었나..
* 그 이유는 컴파일러에 의해 실수 값이 저장될 때 오차가 포함되기 때문임
* 만약 큰따옴표를 없애고 인자로 1.1 이라는 실수 값을 전달하면,
컴파일러는 이 리터럴을 double형으로 받아들임
그리고 메모리에 double형의 공간만큼 할당해서 1.1이라는 값을 저장해놓음
* 문제는 double형으로 표현하는 순간 오차가 존재하게 됨
(실수를 저장할 때 부동소수점 방식으로 표현 후 저장하기 때문)
* 그렇게 되면 BigDecimal 인스턴스를 생성할 때 [오차가 포함된 실수 값]을 인자로 전달해주게 됨
* 따라서 오차를 저장하지 않기 위해서 문자열로 저장을 하는 것
BigDecimal bd = new BigDecimal("1.1");
'2. 프로그래밍 언어 공부 > Java' 카테고리의 다른 글
[공부 필기] Java 기본 공부하기 (30) (0) | 2021.12.12 |
---|---|
[공부 필기] Java 기본 공부하기 (29) (0) | 2021.12.10 |
[공부 필기] Java 기본 공부하기 (27) (0) | 2021.12.06 |
[Java 공부하기] clone 메소드를 오버라이딩 해서 사용해야하는 이유에 대해서 찾아보기 (0) | 2021.12.06 |
[공부 필기] Java 기본 공부하기 (26) (0) | 2021.12.03 |