해당 게시물은 자바의 정석을 정리한 내용 입니다.
1. java.lang 패키지
1.1 Object 클래스
Object 클래스는 모든 클래스의 최고 조상이기 때문에 Object 클래스의 멤버들은 모든 클래스에서 바로 사용 가능하다.
Object 클래스의 메서드 | 설 명 |
protected Object clone() | 객체 자신의 복사본을 반환한다. |
public boolean equals(Object obj) | 객체 자신과 주어진 객체(obj)를 비교한다. 같으면 true, 다르면 false |
protected void finalize() | 객체가 소멸될 때 가비지 컬렉터에 의해 자동적으로 호출된다. 이때, 수행 되어야 하는 코드가 있을 때 오버라이딩 한다. (거의 사용안함) |
public Class getClass() | 객체 자신의 클래스 정보를 담고 있는 Class 인스턴스를 반환한다. |
public int hashCode() | 객체 자신의 해시코드를 반환한다. |
public String toString() | 객체 자신의 정보를 문자열로 반환한다. |
public void notify() | 객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨운다. |
public void notifyAll() | 객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨운다. |
public void wait() public void wait(long timeout) public void wait(long timeout, int nanos) |
다른 쓰레드가 notify()나 notifyAll()을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간(timeout, nanos) 동안 기다리게 한다. (timeout은 천 분의 1초, nanos는 10^9분의 1초) |
[참고] notify(), notifyAll(), wait()은 쓰레드와 관련된 것들이며, 13장 쓰레드에서 자세히 설명한다.
1.2 equals(Object obj)
- equals()는 객체 자신과 주어진 객체(obj)를 비교한다. 같으면 true, 다르면 false.
- Object 클래스에 정의된 equals()는 참조변수의 값(객체의 주소)을 비교한다.
- equals()를 오버라이딩해서 인스턴스 변수의 값을 비교하도록 변경한다.
class Person {
long id;
public boolean equals(Object obj) {
if(obj!=null && obj instanceof Person) {
return id ==((Person)obj).id;
} else {
return false;
}
}
Person(long id) {
this.id = id;
}
}
class EqualsEx2 {
public static void main(String[] args) {
Person p1 = new Person(8011081111222L);
Person p2 = new Person(8011081111222L);
if(p1==p2)
System.out.println("p1과 p2는 같은 사람입니다.");
else
System.out.println("p1과 p2는 다른 사람입니다.");
if(p1.equals(p2))
System.out.println("p1과 p2는 같은 사람입니다.");
else
System.out.println("p1과 p2는 다른 사람입니다.");
}
}
equals 메서드가 Person 인스턴스의 주소 값이 아닌 멤버 변수 id의 값을 비교 하도록 하기 위해 equals 메서드를
오버라이딩 했다. 이렇게 함으로써 equals 메서드로 비교 했을 때, true를 결과로 얻게 할 수 있다.
String, 클래스 뿐만 아니라 Date, File, wrapper 클래스(Integer, Double 등)의 equals 메서드도 주소 값이 아닌 내용을 비교 하도록 오버라이딩 되어 있다.
그러나 StringBuffer 클래스는 equals()가 오버라이딩 되어 있지 않다.
1.3 hashCode()
- hashCode()는 객체 자신의 해시코드(int타입의 정수)를 반환한다.
- 다량의 데이터를 저장&검색하는 해싱기법에 사용된다.
- Object클래스의 hashCode()는 객체의 내부 주소를 반환한다.
- equals()를 오버라이딩하면, hashCode()도 같이 오버라이딩 해야한다.
equals()의 결과가 true인 두 객체의 hashcode는 같아야 하기 때문이다.
- System.identityHashCode(Object obj)는 객체의 주소 값으로 해시 코드를 생성한다.
모든 객체에 대해 항상 다른 해시코드 값을 반환할 것을 보장한다.
class HashCodeEx1 {
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1.equals(str2));
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
System.out.println(System.identityHashCode(str1));
System.out.println(System.identityHashCode(str2));
}
}
String 클래스는 문자열의 내용이 같으면, 동일한 해시코드를 반환하도록 hashCode()가 오버라이딩 되어 있다.
위의 코드에서 str1과 str2는 문자열의 내용이 같기 때문에 hashCode()를 호출하면 항상 동일한 해시코드 값을 얻는다.
1.4 toString()
- toString()는 객체 자신의 정보를 문자열로 반환한다.
- 객체의 정보를 제공한다는 것은 대부분의 경우, 인스턴스 변수에 저장된 값들을 문자열로 표현한다는 뜻이다.
- toString()를 오버라이딩 하지 않고 toString()을 호출하면 클래스 이름에 16 진수의 해시 코드를 얻게 된다.
class Card {
String kind;
int number;
Card() {
this("SPADE", 1); // Card(String kind, int number)를 호출
}
Card(String kind, int number) {
this.kind = kind;
this.number = number;
}
public String toString() {
// Card인스턴스의 kind와 number를 문자열로 반환한다.
return "kind : " + kind + ", number : " + number;
}
}
class CardToString2 {
public static void main(String[] args) {
Card c1 = new Card();
Card c2 = new Card("HEART", 10);
System.out.println(c1.toString());
System.out.println(c2.toString());
}
}
Card 인스턴스의 toString()을 호출하면 인스턴스가 갖고 있는 인스턴스 변수 kind와 number의 값을 문자열로 변환하여 반환하도록
toString()을 오버라이딩 했다.
Object 클래스에 정의된 toString()의 접근 제어자가 public 이므로 Card 클래스의 toString()의 접근 제어자도 public으로 했다.
그 이유는 조상에 정의된 메서드를 자손에서 오버라이딩 할 때는 조상에 정의된 접근 범위 보다 같거나 넓어야 하기 때문이다.
1.5 clone()
clone()
- clone()는 객체 자신의 복사본을 반환한다.
- Cloneable 인터페이스를 구현한 클래스의 인스턴스만 복사 할 수 있다.
- Object 클래스에 정의된 clone()은 인스턴스 변수의 값만을 복사한다.
- 인스턴스 변수가 참조형일 때, 참조하는 객체도 복사 되도록 오버라이딩 해야 함.
clone() 사용 방법
(1) 먼저 복사할 클래스가 Cloneable 인터페이스를 구현한다.
(2) clone()를 오버라이딩 하면서 접근 제어자를 protected에서 public으로 변경한다.
(3) try-catch 내에서 조상 클래스의 clone()을 호출한다.
[참고] Object 클래스의 clone()은 Cloneable을 구현하지 않은 클래스 내에서 호출되면 예외를 발생시킨다.
공변 반환 타입(covariant return type)
- '공변 반환 타입'은 오버라이딩 할 때 조상 메서드의 반환 타입을 자손 클래스 타입으로 변경 할 수 있는 것이다.
public Point clone(){ // (1) 반환 타입을 Object에서 Point로 변경한다.
Object obj = null;
try{
obj = super.clone();
}catch(CloneNotSupportedException e){}
return (Point) obj; // (2) Point 타입으로 형 변환한다.
}
- '공변 반환 타입'을 사용하면, 조상의 타입이 아닌, 실제로 반환되는 자손 객체의 타입으로 반환할 수 있어서 번거로운 형 변환이 줄어든다는 장점이 있다.
얕은 복사와 깊은 복사
- '얕은 복사(shallow copy)'은 객체에 저장된 값을 그대로 복제할 뿐, 객체가 참조하고 있는 객체까지 복제하지는 않는다.
- '깊은 복사(deep copy)'은 원본이 참조하고 있는 객체까지 복제하는 것이다.
import java.util.*;
class Circle implements Cloneable {
Point p; // 원점
double r; // 반지름
Circle(Point p, double r) {
this.p = p;
this.r = r;
}
public Circle shallowCopy() { // 얕은 복사
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {}
return (Circle)obj;
}
public Circle deepCopy() { // 깊은 복사
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {}
Circle c = (Circle)obj;
c.p = new Point(this.p.x, this.p.y);
return c;
}
public String toString() {
return "[p=" + p + ", r="+ r +"]";
}
}
class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
public String toString() {
return "("+x +", "+y+")";
}
}
class ShallowDeepCopy {
public static void main(String[] args) {
Circle c1 = new Circle(new Point(1, 1), 2.0);
Circle c2 = c1.shallowCopy();
Circle c3 = c1.deepCopy();
System.out.println("c1="+c1);
System.out.println("c2="+c2);
System.out.println("c3="+c3);
c1.p.x = 9;
c1.p.y = 9;
System.out.println("= c1의 변경 후 =");
System.out.println("c1="+c1);
System.out.println("c2="+c2);
System.out.println("c3="+c3);
}
}
위의 코드는 인스턴스 c1을 생성한 후에 얕은 복사로 c2를 생성하고, 깊은 복사로 c3를 생성하였다.
그 다음에 c1이 가리키고 있는 Point 인스턴스의 x와 y의 값을 9로 변경한다.
c1을 변경했을 뿐인데, c2도 영향을 받는다. 그러나 c3는 전혀 영향을 받지 않는다.
shallowCopy()의 내용을 보면, 단순히 Object 클래스의 clone()을 호출할 뿐이다.
Object 클래스의 clone()은 원본 객체가 가지고 있는 값을 그대로 복사한다. (얕은 복사)
deepCopy()는 복제된 객체가 새로운 Point 인스턴스를 참조하도록 했다.
getClass()
- getClass()는 객체 자신의 클래스 정보를 담고 있는 Class 객체를 반환한다.
- Class 객체는 클래스의 모든 정보를 담고 있으며, 클래스 당 1개만 존재한다.
- 클래스 파일이 클래스 로더(ClassLoader)에 의해서 메모리에 올라갈 때, 자동으로 생성된다.
Class 객체를 얻는 방법
- 해당 Class 객체에 대한 참조를 얻는 방법은 여러 가지가 있다.
Class cObj = new Card().getClass(); // 생성된 객체로 부터 얻는 방법
Class cObj = Card.class; // 클래스 리터럴(*.class)로 부터 얻는 방법
Class cObj = Class.forName("Card"); // 클래스 이름으로 부터 얻는 방법
- forName()은 특정 클래스 파일, 예를 들어 데이터베이스 드라이버를 메모리에 올릴 때 주로 사용한다.
- Class 객체를 통해서 객체를 생성하고 메서드를 호출하는 등 보다 동적인 코드를 작성할 수 있다.
Card c = new Card(); // new 연산자를 이용해서 객체 생성
Card c= Card.class.newInstance(); // Class 객체를 이용해서 객체 생성
[참고] 동적으로 객체를 생성하고 메서드를 호출하는 방법에 대해서 더 알고 싶다면, 리플렉션 API (reflection API)로 검색하면 된다.
1.6 String 클래스
String 클래스
- String 클래스는 문자열을 저장하고 이를 다루는데 필요한 메서드를 함께 제공한다.
- String 인스턴스의 내용은 변경 할 수 없다. (immutable : 변경 불가능한)
예를 들어, 아래 코드와 같이 '+' 연산자를 이용해서 문자열을 결합하는 경우, 인스턴스 내의 문자열이 바뀌는 것이 아니라
새로운 문자열("ab")이 담긴 String 인스턴스가 생성되는 것이다.
- 문자열 간의 결합이나 추출 등 문자열을 다루는 작업이 많이 필요한 경우에는 String 클래스 대신 StringBuffer 클래스를 사용하는 것이 좋다.
문자열의 비교
- 문자열을 만들 때는 두 가지 방법이 있다. (문자열 리터럴을 지정하는 방법과 String 클래스의 생성자를 사용해서 만드는 방법)
String str1 = "abc"; // 문자열 리터럴 "abc"의 주소가 str1에 저장됨
String str2 = "abc"; // 문자열 리터럴 "abc"의 주소가 str2에 저장됨
String str3 = new String("abc"); // 새로운 String 인스턴스를 생성
String str4 = new String("abc"); // 새로운 String 인스턴스를 생성
- String 클래스의 생성자를 이용한 경우에는 new 연산자에 의해서 메모리 할당이 이루어지기 때문에 항상 새로운 String 인스턴스가 생성된다.
그러나 문자열 리터럴은 이미 존재하는 것을 재사용하는 것이다.
equals()를 사용 했을 때는 두 문자열의 내용("abc")을 비교하기 때문에 두 경우, 모두 true를 결과로 얻는다.
하지만 각 String 인스턴스의 주소를 등가 비교 연산자 '=='로 비교 했을 때는 결과가 다르다.
class StringEx1 {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
System.out.println("String str1 = \"abc\";");
System.out.println("String str2 = \"abc\";");
System.out.println("str1 == str2 ? " + (str1 == str2));
System.out.println("str1.equals(str2) ? " + str1.equals(str2));
System.out.println();
String str3 = new String("\"abc\"");
String str4 = new String("\"abc\"");
System.out.println("String str3 = new String(\"abc\");");
System.out.println("String str4 = new String(\"abc\");");
System.out.println("str3 == str4 ? " + (str3 == str4));
System.out.println("str3.equals(str4) ? " + str3.equals(str4));
}
}
문자열 리터럴
- 자바 소스 파일에 포함된 모든 문자열 리터럴은 컴파일 시에 클래스 파일에 저장된다.
이때 같은 내용의 문자열 리터럴은 한번만 저장된다.
class StringEx2 {
public static void main(String args[]) {
String s1 = "AAA";
String s2 = "AAA";
String s3 = "AAA";
String s4 = "BBB";
}
}
위의 예제를 실행하면 "AAA"라는 문자열을 담고 있는 String 인스턴스가 하나 생성된 후, 참조변수 s1, s2, s3는 모두
이 String 인스턴스를 참조하게 된다.
빈 문자열(empty string)
- 빈 문자열은 내용이 없는 문자열이다.
(크기가 0인 char형 배열을 저장하는 문자열)
char[] chArr = new char[0]; // 길이가 0인 char 배열
int[] iArr = {}; // 길이가 0인 int 배열
- 크기가 0인 배열을 생성하는 것은 어느 타입이나 가능
- String str=“”;은 가능해도 char c = ‘’;는 불가능 (char형 변수에는 반드시 하나의 문자를 지정해야 한다.)
String은 참조형의 기본값인 null 보다 빈 문자열로 초기화하고 char형은 기본값인 ‘\u0000’보다 공백으로 초기화하자.
String 클래스의 생성자와 메서드
자세한 내용은 자바의 정석 p468~p471을 참고하자.
join()과 StringJoiner
- join()은 여러 문자열 사이에 구분자를 넣어서 결합한다.
String animals = "dog,cat,bear";
String[] arr = animals.split(","); // 문자열을 ','를 구분자로 나눠서 배열에 저장
String str = String.join("-", arr); // 배열의 문자열을 '-'로 구분해서 결합
System.out.println(str);
- java.util.StringJoiner클래스를 사용해서 문자열을 결합할 수 있다.
StringJoiner sj = new StringJoiner(",", "[", "]");
String[] strArr = {"aaa", "bbb", "ccc"};
for(String s : strArr)
sj.add(s.toUpperCase());
System.out.println(sj.toString()); // [AAA,BBB,CCC]
유니코드의 보충문자
- 유니코드는 원래 2 byte (16 비트) 문자 체계인데, 이걸로도 모자라서 20 비트로 확장하게 되었다.
그래서 하나의 문자를 char 타입으로 다루지 못하고 int 타입으로 다룰 수 밖에 없다.
- 보충 문자(supplementary characters) : 확장에 의해 새로 추가된 문자들을 말한다.
- String 클래스의 메서드 중에서 매개 변수가 'int ch'인 것들은 보충 문자를 지원하는 것이고, 'char ch'인 것들은 지원하지 않는 것들이다.
문자 인코딩 변환
- getBytes(String chartsetName)를 사용하면 문자열의 문자 인코딩을 다른 인코딩으로 변경할 수 있다.
- 자바가 UTF-16를 사용하지만, 문자열 리터럴에 포함되는 문자들은 OS의 인코딩을 사용한다.
byte[] utf8_str = "가".getBytes("UTF-8"); // 문자열을 UTF-8로 변환
String str = new String(utf8_str, "UTF-8"); // byte배열을 문자열로 변환
- 서로 다른 문자 인코딩을 사용하는 컴퓨터 간에는 데이터를 주고받을 때는 적절한 문자 인코딩이 필요하다.
class StringEx5 {
public static void main(String[] args) throws Exception {
String str = "가";
byte[] bArr = str.getBytes("UTF-8");
byte[] bArr2 = str.getBytes("CP949");
System.out.println("UTF-8:" + joinByteArr(bArr));
System.out.println("CP949:" + joinByteArr(bArr2));
System.out.println("UTF-8:" + new String(bArr, "UTF-8"));
System.out.println("CP949:" + new String(bArr2, "CP949"));
}
static String joinByteArr(byte[] bArr) {
StringJoiner sj = new StringJoiner(":", "[", "]");
for(byte b : bArr)
sj.add(String.format("%02X",b));
return sj.toString();
}
}
UTF-8은 한글 한 글자를 3 byte로 표현하고, CP949는 2 byte로 표현한다.
String.format()
- format()은 형식화된 문자열을 만들어낸다.
String str = String.format("%d 더하기 %d는 %d입니다.", 3,5,3+5);
System.out.println(str); // 3 더하기 5는 8입니다.
기본형 값을 String으로 변환
(1) 숫자에 빈 문자열 ""을 더한다.
(2) valueOf()를 사용한다.
int i = 100;
String str1 = i + ""; // 100을 "100"으로 변환하는 방법1
String str2 = String.valueOf(i); // 100을 "100"으로 변환하는 방법2
String을 기본형 값으로 변환
(1) parseInt()를 사용한다.
(2) valueOf()를 사용한다.
사실, valueOf()의 반환 타입은 래퍼 클래스이다. 오토 박싱(auto-boxing)에 의해 래퍼 클래스가 기본형 값으로 자동 변환된다.
int i = Integer.parseInt("100"); // "100"을 100으로 변환하는 방법1
int i2 = Integer.valueOf("100"); // "100"을 100으로 변환하는 방법2
[기본형과 문자열 간의 변환 방법 정리]
기본형 → 문자열 | 문자열 → 기본형 |
String String.valueOf(boolean b) |
boolean Boolean.parseBoolean(String s) byte Byte.parseByte(String s) short Short.parseShort(String s) int Integer.parseInt(String s) long Long.parseLong(String s) float Float.parseFloat(String s) double Double.parseDouble(String s) |
class StringEx6 {
public static void main(String[] args) {
int iVal = 100;
String strVal = String.valueOf(iVal); // int를 String으로 변환한다.
double dVal = 200.0;
String strVal2 = dVal + ""; // int를 String으로 변환하는 또 다른 방법
double sum = Integer.parseInt("+"+strVal)+Double.parseDouble(strVal2);
double sum2 = Integer.valueOf(strVal) + Double.valueOf(strVal2);
System.out.println(String.join("",strVal,"+",strVal2,"=")+sum);
System.out.println(strVal+"+"+strVal2+"="+sum2);
}
}
이 예제는 문자열과 기본형 간의 변환의 예를 보여준다.
parseInt()나 parseFloat() 같은 메서드는 문자열에 공백 또는 문자가 포함되어 있는 경우 변환 시 예외(NumberFormatException)가
발생할 수 있으므로 주의해야 한다.
그래서 문자열의 양 끝의 공백을 제거해주는 trim()을 습관적으로 같이 사용하기도 한다.
int val = Integer.parseInt(" 123 ".trim()); // 문자열 양 끝의 공백을 제거 후 변환
그러나 부호를 의미하는 '+'나 소수점을 의미하는 '.'와 float형 값을 뜻하는 f와 같은 자료형 접미사는 허용된다.
단 자료형에 알맞은 변환을 하는 경우에만 허용된다.
1.7 StringBuffer 클래스와 StringBuilder 클래스
StringBuffer 클래스의 특징
- String 처럼 문자형 배열(char[])을 내부적으로 가지고 있다.
- 그러나, String 클래스와 달리 StringBuffer 클래스는 내용을 변경할 수 있다.(mutable)
StringBuffer sb = new StringBuffer("abc"); // StringBuffer를 생성한다.
sb.append("123"); // sb에 문자열 "123"을 추가하면
// append()는 반환타입이 StringBuffer인데 자신의 주소를 반환한다.
StringBuffer sb2 = sb.append("ZZ"); // sb의 내용 뒤에 "ZZ"를 추가한다.
System.out.println(sb); // abc123ZZ
System.out.println(sb2); // abc123ZZ
- 인스턴스를 생성할 때 버퍼(배열)의 크기를 충분히 지정해주는 것이 좋다.
(버퍼가 작으면 작업 중에 더 큰 배열의 생성이 필요하므로 성능을 저하 시킨다.)
public StringBuffer(int length) {
value = new char[length];
shared = false;
}
public StringBuffer() {
this(16); // 버퍼의 크기를 지정하지 않으면 버퍼의 크기는 16이 된다.
}
public StringBuffer(String str) {
this(str.length() + 16); // 지정한 문자열의 길이보다 16이 더 크게 버퍼를 생성한다.
append(str);
}
/*
아래 코드는 StringBuffer 클래스의 일부이며
버퍼의 크기를 변경하는 내용의 코드이다. */
// 새로운 길이(newCapacity)의 배열을 생성한다. newCapacity는 정수값이다.
char newValue[] = new char[newCapacity];
// 배열 value의 내용을 배열 newValue로 복사한다.
System.arraycopy(value, 0, newValue, 0, count); // count는 문자열 길이
value = newValue; // 새로 생성된 배열의 주소를 참조변수 value에 저장
- StringBuffer 클래스는 equals()를 오버라이딩하지 않았다. (equals 메서드를 사용해도 ==로 비교한 것과 같은 결과를 얻는다.)
반면에 toString()는 오버라이딩 되어있다.
그래서 StringBuffer 인스턴스의 문자열을 비교하기 위해서는 StringBuffer 인스턴스의 toString()를 호출하여 String 인스턴스를 얻은 다음,
여기에 equals()를 사용해서 비교해야 한다.
StringBuffer sb = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");
String s = sb.toString();
String s2 = sb2.toString();
System.out.println(s.equals(s2)); // true
StringBuffer 클래스의 생성자와 메서드
자세한 내용은 자바의 정석 p480~p481을 참고하자.
StringBuilder란?
- StringBuffer는 멀티 쓰레드에 안전(thread safe)하도록 동기화 되어 있다.
(멀티 쓰레드로 작성된 프로그램이 아닌 경우, StringBuffer의 동기화는 불필요하게 성능만 떨어지게 함)
- StringBuilder는 StringBuffer에서 쓰레드의 동기화만 제거한 클래스이다. (그 외에는 완전히 똑같은 기능)
- StringBuffer도 충분히 성능이 좋기 때문에 성능 향상이 반드시 필요한 경우에만 StringBuilder를 사용한다.
1.8 Math 클래스
Math 클래스
- Math 클래스는 수학 계산에 유용한 메서드로 구성되어 있다.
(모두 static메서드이며 아래와 같이 2개의 상수만 정의 해놓았음)
public static final double E = 2.7182818284590452354; // 자연로그의 밑
public static final double PI = 3.14159265358979323846; // 원주율
올림, 버림, 반올림
- round(): 항상 소수점 첫째자리에서 반올림을 해서 정수값(long)을 결과로 돌려준다.
// 1) 원래 값에 100을 곱한다. (9075.52)
90.7552 * 100
// 2) 위의 결과에 Math.round()를 사용한다. (9076)
Math.round(9075.52) -> 9076
// 3) 위의 결과를 다시 100.0으로 나눈다. (90.76)
9076 / 100.0
예외를 발생시키는 메서드
- Exact가 포함된 메서드들이 JDK 1.8 부터 추가 되었다.
- 이들은 정수형 간의 연산에서 발생할 수 있는 오버플로우(overflow)를 감지한다.
- 아래의 메서드들은 오버플로우가 발생하면 예외(ArithmeticException)를 발생시킨다.
int addExact(int x, int y); // x + y
int subtractExact(int x, int y); // x - y
int multiplyExact(int x, int y); // x * y
int incrementExact(int a); // a++
int decrementExact(int a); // a--
int negateExact(int a); // -a
int toIntExact(long value); // (int)value - int로의 형변환
StrictMath 클래스
-Math 클래스는 최대한의 성능을 얻기 위해 JVM이 설치된 OS의 메서드를 호출해서 사용한다. (즉, OS에 의존적인 계산을 함)
- StrictMath 클래스는 어떤 OS에서 실행되어도 항상 같은 결과를 얻도록 Math클래스를 새로 작성한 것이다.
Math 클래스의 메서드
자세한 내용은 자바의 정석을 참고하자.
1.9 래퍼(wrapper) 클래스
래퍼(wrapper) 클래스
- 래퍼 클래스는 기본 자료형의 값을 객체로 변환할 때 사용한다.
- 내부적으로 기본형(primitive type) 변수룰 가지고 있다.
아래의 코드는 int형 래퍼 클래스인 Integer 클래스의 실제 코드이다.
public final class Integer extends Number implements Comparable{
...
private int value;
...
}
- 래퍼 클래스들은 equals()가 오버라이딩 되어 있어서 주소 값이 아닌 객체가 가지고 있는 값을 비교한다.
- 비교 연산자를 사용할 수 없다. 대신 compareTo()를 제공한다.
compareTo() : 같으면 0, 왼쪽 값이 크면 양수, 왼쪽 값이 작으면 음수이며 정렬에 사용됨
- toString()도 오버라이딩 되어 있어서 객체가 가지고 있는 값을 문자열로 변환하여 반환한다.
- 래퍼 클래스들은 MAX_VALUE, MIN_VALUE, SIZE, BYTES, TYPE 등의 static 상수를 공통적으로 가지고 있다.
Number 클래스
- Number 클래스는 추상 클래스이며 내부적으로 숫자를 멤버 변수로 갖는 래퍼 클래스들의 조상이다.
- Number 클래스의 자손으로 래퍼 클래스, BigInteger와 BigDecimal 등이 있다.
BigInteger: long으로도 다룰 수 없는 큰 범위의 정수를 처리하기 위한 것
BigDecimal: double로도 다룰 수 없는 큰 범위의 부동 소수점 수를 처리하기 위한 것
- 객체가 가지고 있는 값을 숫자와 관련된 기본형으로 변환하여 반환하는 메서드들을 정의하고 있다.
public abstract class Number implements java.io.Serializable {
public abstract int intValue();
public abstract long longValue();
public abstract float floatValue();
public abstract double doubleValue();
public byte byteValue() {
return (byte)intValue();
}
public short shortValue() {
return (short)intValue();
}
}
문자열을 숫자로 변환하기
- 다음은 문자열을 숫자로 변환하는 다양한 방법을 보여준다.
int i = new Integer("100").intValue(); // floatVlaue(), longValue(), ...
int i2 = Integer.parseInt("100"); // 주로 이 방법을 많이 사용
Integer i3 = Integer.valueOf("100");
- 아래 표는 래퍼 클래스의 '타입.parse타입(String s)' 형식의 메서드와 타입.valueOf() 메서드를 정리한 것이다.
전자는 반환 값이 기본형(primitive type)이고 후자는 반환 값이 래퍼 클래스 타입이다.
(JDK 1.5 부터 도입된 오토박싱 기능 때문에 차이가 없어졌다. 단, 성능은 valueOf()가 조금 더 느리다.)
문자열 → 기본형 | 문자열 → 래퍼 클레스 |
byte b = Byte.parseByte("100"); |
Byte b = Byte.valueOf("100"); |
- 문자열이 10 진수가 아닌 다른 진법(raidx)의 숫자일 때도 변환이 가능하도록 다음과 같은 메서드가 제공된다.
static int parseInt(String s, int radix);
static Integer valueOf(String s, int radix);
Integer.parseInt("100", 2); // 100(2) -> 4
Integer.parseInt("100", 8); // 100(8) -> 64
Integer.parseInt("100", 16); // 100(16) -> 256
Integer.parseInt("FF", 16); // FF(16) -> 255
Integer.parseInt("FF"); // NumberFormatException 발생
오토박싱 & 언박싱 (autoboxing & unboxing)
- JDK 1.5 이전에는 기본형과 참조형 간의 연산이 불가능 했기 때문에, 래퍼 클래스로 기본형을 객체로 만들어서 연산해야 했다.
그러나 이제는 기본형과 참조형 간의 연산이 가능하다. (그 이유는 오토박싱 & 언박싱)
오토박싱(autoboxing): 기본 자료형의 값을 래퍼 클래스 객체로 자동 변환하는 것
언박싱(unboxing): 래퍼 클래스 객체를 기본 자료형의 값으로 자동 변환하는 것
컴파일 전 | 컴파일 후 |
int i = 5; Integer iObj = new Integer(7); int sum = i + iObj; |
int i = 5; Integer iObj = new Integer(7); int sum = i + iObj.intValue(); |
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(10); // 오토박싱: 10 -> new Integer(10)
int value = list.get(0); // 언박싱: new Integer(10) -> 10
'Java > 객체 지향 개념 ~' 카테고리의 다른 글
자바의 정석 (Chapter 11. 컬렉션 프레임워크) (0) | 2020.08.23 |
---|---|
자바의 정석 (Chapter 10. 날짜와 시간 & 형식화) (0) | 2020.08.19 |
자바의 정석 (Chapter 8. 예외 처리) (1) | 2020.08.08 |
자바의 정석 (Chapter 7_3. 객체지향개념 2) (0) | 2020.08.03 |
자바의 정석 (Chapter 7_2. 객체지향개념 2) (0) | 2020.08.02 |
댓글