java.lang패키지는 자바프로그래밍에 가장 기본이 되는 클래스들을 포함하고 있다. 그렇기 때문에 java.lang패키지의 클래스들은 import문 없이도 사용할 수 있게 되어 있다.(String, System 클래스)
1.1 Object클래스
Object클래스의 멤버들은 모든 클래스에서 바로 사용이 가능하다.
Object클래스는 멤버변수는 없고 오직 11개의 메서드만 가지고 있다.이 메서드들은 모든 인스턴스가 가져야 할 기본적인 것들
equals(Object obj)
- 객체자신(this)과 주어진객체(obj)를 비교한다. 같으면 true 다르면 false
- Object클래스의 equals()는 객체의 주소를 비교(참조변수 값 비교)
매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean값으로 알려주는 역할을 한다.
Object클래스에 정의되어 있는 equals메서드
public boolean equals(Object obj) {
return (this == obj) //주소비교. 주소가 같아야 true
}
서로 다른 두 객체를 equals메서드로 비교하면 항상 false를 결과로 얻게 된다.
value값을 비교하도록 하려면? equals메서드를 오버라이딩하여 주소가 아닌 객체에 저장된 내용을 비교하도록 변경
public boolean equals(Object obj) {
if(obj != null && obj instanceof Person) {
return id == ((Person)obj).id;
}else {
return false;
}
}
String클래스 역시 Object클래스의 equals메서드를 그대로 사용하는 것이 아니라 오버라이딩을 통해서 String인스턴스가 갖는 문자열 값을 비교하도록 되어있다.
❗Strign클래스뿐만 아니라 Date, File, wrapper클래스(Integer, Double등)의 equals메서드도 주소값이 아닌 내용을 비교하도록 오버라이딩 되어 있다. StringBuffer클래스는 x
hashCode()
- 객체의 해시코드를 반환하는 메서드
- Object클래스의 hashCode()는 객체의 주소를 int로 변환해서 반환
이 메서드는 해싱(hashing)기법에 사용되는 ‘해시함수(hash function)’을 구현한 것이다.
해시함수는 찾고자하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드(hashcode)를 반환나다.
클래스의 인스턴스변수 값으로 객체의 같고 다름을 판단해야하는 경우라면 equals메서드 뿐만 아니라 hashCode메서드도 오버라이딩 해야 한다.
*equals()의 결과가 true인 두 객체의 해시코드는 같아야 하기 때문
//정의
public class Object{
...
public native int hashCode(); <-//내용x
//네이티브 메서드 : OS의 메서드(C언어)
}
toString()
- 객체를 문자열(String)로 변환하기 위한 메서드
이 메서드는 인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것이다.
//정의
public String toString() {
return getClass().getName() + @ + Integer.toHexString(hashCode());
//반환 설계도객체 클래스이름 위치 16진수로 문자열로 바꿔서 hashCode(객체주소)를 출력
}
toString()을 오버라이딩하지 않는다면, 위와 같은 내용이 그대로 사용될 것이다. 즉, 클래스이름에 16진수의 해시코드를 얻게 될 것이다.
toString()은 일반적으로 인스턴스가 클래스에 대한 정보 또는 인스턴스 변수들의 값을 문자열로 변환하여 반환하도록 오버라이딩되는 것이 보통이다.
clone()
이 메서드는 자신을 복제하여 새로운 인스턴스를 생성하는 일을 한다.(단순히 인스턴스변수의 값만 복사하기 때문에 참조타입의 인스턴스 변수가 있는 클래스는 완전한 인스턴스 복제 x)
배열의 경우, 복제된 인스턴스도 같은 배열의 주소를 가져서 원래의 인스턴스에 영향을 미친다. clone메서드를 오버라이딩해서 새로운 배열을 생성하고 배열의 내용을 복사하도록 해야한다.
clone()을 사용하려면, 먼저 복제할 클래스가 Cloneable인터페이스를 구현해야하고, clone()을 오버라이딩하면서 접근 제어자를 protected에서 public으로 변경한다. 그래야만 상속관계가 없는 다른 클래스에서 clone()을 호출할 수 있다.
public class Object {
...
protected native Object clone() throws CloneNotSupporedtException;
...
}
마지막으로 조상클래스의 clone()을 호출하는 코드가 포함된 try-catch문을 작성한다.
class Point implements **Cloneable** { //①Cloneable인터페이스를 구현한다.
...
**public** Object clone() { //②접근 제어자를 protected에서 public으로 변경
Object obj = null;
try {
obj = **super.clone();** //③try-catch내에서 조상클래스의 clone()을 호출
} catch(CloneNotSupportedException e) {}
return obj;
}
}
Cloneable인터페이스를 구현한 클래스의 인스턴스만 clone()을 통한 복제가 가능하다.
Cloneable인터페이스가 구현되어 있다는 것은 클래스 작성자가 복제를 허용한다는 의미이다.
공변 반환타입
JDK1.5부터 ‘공변 반환타입(covariant return type)’이라는 것이 추가되었는데, 이 기능은 오버라이딩할 때 조상 메서드의 반환타입을 자손 클래스의 타입으로 변경을 허용하는 것이다.
getClass()
- 클래스 정보를 담기위한 클래스
자신이 속한 클래스의 Class객체를 반환하는 메서드인데, Class객체는 이름이 ‘Class’인 클래스의 객체이다.
//정의
public final class Class implements ... { //Class 클래스
...
}
Class객체는 클래스의 모든 정보를 담고 있으며, 클래스 당 1개만 존재한다. 그리고 클래스 파일이 ‘클래스 로더(ClassLoader)’에 의해서 메모리에 올라갈 때, 자동으로 생성된다.
클래스 파일을 읽어서 사용하기 편한 형태로 저장해 놓은 것이 클래스 객체이다.
Class객체를 얻는 방법
클래스의 정보가 필요할 때, 먼저 Class객체에 대한 참조를 얻어 와야 한다.
1.2 String 클래스
- String 클래스 = 데이터(char[]) + 메서드 / 문자배열+문자열관련 메서드
자바에서는 문자열을 다루기 위한 클래스를 제공한다. String클래스는 문자열을 저장하고 이를 다루는데 필요한 메서드를 함께 제공한다.
변경 불가능한(immutable)클래스
-내용을 변경할 수 없는 불변 클래스
String클래스에는 문자열을 저장하기 위해서 문자열 배열 참조변수(char[]) value를 인스턴스 변수로 정의해놓고 있다. 인스턴스 생성 시 생성자의 매개변수로 입력받는 문자열은 이 인스턴스변수(value)에 문자형 배열(char[])로 저장되는 것이다.
String 클래스 실제 코드
public final class String implements java.io.Serializable, Comparable{
private char[] value; //프라이빗으로 문자 배열을 가지고 있음
... //순서를 다루기 위한 메서드들이 들어있음
}
한번 생성된 String인스턴스가 갖고 있는 문자열은 읽어 올 수 만 있고, 변경할 수는 없다.
+연산자를 이용해서 문자열을 결합하는 경우 인스턴스 내의 문자열이 바뀌는 것이 아니라 새로운 문자열을 가진 String인스턴스가 생성된다.
*StringBuffer →문자열 변경 가능
문자열간의 결합이나 추출 등 문자열을 다루는 작업이 많이 필요한 경우에는 String클래스 대신 StringBuffer클래스를 사용하는 것이 좋다.
문자열의 비교
문자열을 만들 때는 두 가지 방법, 문자열 리터럴을 지정하는 방법과 String클래스의 생성자를 사용해서 만드는 방법이 있다.
String str1 = "abc"; //문자열 리터럴 "abc"의 주소가 str1에 저장됨 o
String str2 = "abc"; //문자열 리터럴 "abc"의 주소가 str2에 저장됨 o
//문자열 리터럴은 이미 존재하는 것을 재사용함
String str3 = new String("abc"); //새로운 String인스턴스를 생성 x
String str4 = new String("abc"); //새로운 String인스턴스를 생성 x
//생성자를 이용한 경우 new연산자에 의해서 메모리 할당이 이루어지기 때문에 항상 새로운 String인스턴스가 생성된다.
str3 == str4 //false 주소비교
str3.equals(str4) //true 내용비교
❗문자열 리터럴은 클래스가 메모리에 로드될 때 자동적으로 미리 생성된다.
문자열 리터럴
- new String(”abc”)안해도 자동으로 생성(constant pool(상수저장소)에 저장)
자바 소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 클래스 파일에 저장된다. 이때 같은 내용의 문자열 리터럴은 한번만 저장된다.
String s1 = "AAA"; //자동으로 new String("AAA") 이런식으로 만들어져서 constant pool에 저장됨
//참조변수 s1(0x100)이 "AAA"를 참조하게됨
빈 문자열(empty string)
- 내용이 없는 문자열. 크기가 0인 char형 배열을 저장하는 무낮열 -String str = “”; //str을 빈 문자열로 초기화
길이가 0인 배열 존재? yes → char형 배열도 길이가 0인 배열을 생성할 수 있고, 이 배열을 내부적으로 가지고 있는 문자열이 바로 빈 문자열이다.
‘String s = “”;’과 같은 문장이 있을 때, 참조변수 s가 참조하고 있는 String인스턴스는 내부에 ‘new char[0]’과 같이 길이거 0인 char형 배열을 저장하고 있는 것이다.
//X
String s = null;
char c = '\\u0000';//유니코드 첫번째 문자(빈 문자)
↓
//이게 더 좋은 코드
//O 일반적으로 공백으로 초기화 함
String s = ""; //빈 문자열로 초기화
char c = ' '; //공백으로 초기화
join()과 StringJoiner
❗JDK1.8부터 추가되었다.
join()은 여러 문자열 사이에 구분자를 넣어서 결합한다.
String animals = "dog,cat,bear";
String[] arr = animals.split(","); //문자열을 ','를 구분자로 나눠서 배열에 저장
String str = String.join("-", arr); //배열의 문자열을 '-'로 구분해서 결합
System.out.println(str); //dor-cat-bear
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]
String.format()
형식화된 문자열을 만들어내는 간단한 방법이다. printf()하고 사용법이 완전히 똑같다
String str = String.format("%d 더하기 %d는 %d입니다.",3,5,3+5);
기본형 값을 String으로 변환
숫자에 빈 문자열""을 더해주기만 하면 된다. 이 외에도 valueOf()를 사용하는 방법도 있다.
*성능은 valueOf()가 더 좋지만, 빈 문자열을 더하는 방법이 간단하고 편함
int i = 100;
String str1 = i + ""; //100을 "100"으로 변환하는 방법1
String str2 = String.valueOf(i); //100을 "100"으로 변환하는 방법2
❗참조변수에 String을 더하면, 참조변수가 가리키는 인스턴스의 toString()을 호출하여 String을 얻은 다음 결합한다.
String을 기본형 값으로 변환
String을 기본형으로 변환하는 방법도 간단하다. valueOf()를 쓰거나 앞서 배운 parseInt() (문자열을 정수로 변환) 를 사용하면 된다.
int i = Integer.parseInt("100"); //"100"을 100으로 변환하는 방법1
int i2 = Integer.valueOf("100"); //"100"을 100으로 변환하는 방법2 <-new 방법
//참조형 오토박싱에 의해서 자동 변환됨
Integer i2 = Integer.valueOf("100"); //원래는 반환타입이 Integer
1.3 StringBuffer클래스와 StringBuilder클래스
StringBuffer클래스는 문자열을 변경할 수 있다. 내부적으로 문자열 편집을 위한 버퍼(buffer)를 가지고 있으며, StringBuffer인스턴스를 생성할 때 그 크기를 지정할 수 있다.
public final class StringBuffer implements java.io.Serializable {
private char[] value;
}
문자열을 저장하기 위한 char형 배열의 참조변수를 인스턴스 변수로 선언해 놓고 있다. StringBuffer인스턴스가 생성될 때, char형 배열이 생성되며 이 때 생성된 char형 배열을 인스턴스변수 value가 참조하게 된다.
StringBuffer의 생성자
StringBuffer클래스의 인스턴스를 생성할 때, 적절한 길이의 char형 배열이 생성되고, 이 배열은 문자열을 저장하고 편집하기 위한 공간(buffer)으로 사용된다.
StringBuffer인스턴스를 생성할 때는 생성자 StringBuffer(int length)를 사용해서 StringBuffer인스턴스에 저장될 문자열의 길이를 고려하여 충분히 여유있는 크기로 지정하는 것이 좋다.
지정하지 않으면 16개의 문자를 저장할 수 있는 크기의 버퍼를 생성한다.
char newValue[] = new char[5];
System.arraycopy(value, 0, newValue, count);
value = newValue; //새로 생성된 배열의 주소를 참조변수 value에 저장
StringBuffer의 변경
String과 달리 StringBuffer는 내용을 변경할 수 있다.
append()는 반환타입이 StringBuffer인데 자신의 주소를 반환한다.
StringBuffer sb = new StringBuffer("abc");
sb.append("123");
StringBuffer sb2 = sb.append("zz");
System.out.println(sb); //abc123zz
System.out.println(sb2); //abc123zz
//sb에 새로운 문자열이 추가되고 sb자신의 주소를 반환하여 sb2에는 sb의 주소인 0x100이 저장된다.
StringBuffer의 비교
- StringBuffer는 equals()가 오버라이딩 되어있지 않다(주소비교)
- StringBuffer을 String으로 변환 후에 equals()로 비교해야 한다.
*toString()은 오버라이딩 되어있어 toString()을 호출해서 String 인스턴스를 얻은 다음 equals()메서드를 사용해서 비교
class Ex9_11 {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");
System.out.println("sb == sb2 ? " + (sb == sb2));
System.out.println("sb.equals(sb2) ? " + sb.equals(sb2));
// StringBuffer의 내용을 String으로 변환한다.
String s = sb.toString(); // String s = new String(sb);와 같다.
String s2 = sb2.toString();
System.out.println("s.equals(s2) ? " + s.equals(s2));
}
}
StringBuilder란?
싱글쓰레드 프로그램에서 사용하면 좋음
StringBuffer는 멀티쓰레드에 안전(thread safe)하도록 동기화되어 있다.
멀티쓰레드로 작성된 프로그램이 아닌 경우, StringBuffer의 동기화는 불필요하게 성능만 떨어뜨리게 된다.
StringBuffer에서 쓰레드의 동기화만 뺀 StringBuilder가 새로 추가되었다.
*Stringbuilder는 StringBuffer와 완전히 똑같은 기능으로 작성되어 있음(참조변수와 생성자만 변경)
1.4 Math클래스
Math클래스는 기본적인 수학계산에 유용한 메서드로 구성되어 있다.
접근제어자가 private이기 때문에 인스턴스를 생성할 수 없다.
Math클래스의 메서드는 모두 static이며, 2개의 상수만 정의해 놓았다.
public static final double E = 2.7182818284590452354 //자연로그의 밑 2log
public static final double PI = **3.14**159265358979323846; //원주율
올림, 버림, 반올림
소수점 n번째 자리에서 반올림한 값을 얻기 위해서는 round()를 사용해야 하는데, 이 메서드는 항상 소수점 첫째자리에서 반올림을 해서 정수값(long)을 결과로 돌려준다.
1. 원래 값에 100을 곱한다. 90.7552 * 100 → 9075.53
2. 위의 결과에 Math.round()를 사용한다. Math.round(9075.52) → 9076
3. 위의 결과를 다시 100.0으로 나눈다. 9076 / 100.0 → 90.76 9076 / 100 → 90
1.5 래퍼(wrapper)클래스
8개의 기본형을 객체로 다뤄야 할 때 사용하는 클래스
객체지향 개념에서는 모든 것은 객체로 다루어져야 한다. 그러나 8개의 기본형을 객체로 다루지 않는데 이것이 바로 자바가 완전한 객체지향 언어가 아니라는 얘기를 듣는 이유이다.
기본형 변수를 객체로 다뤄야 하는 경우
- 매개변수로 객체를 요규할 때
- 기본형 값이 아닌 객체로 저장해야할 때
- 객체간의 비교가 필요할 때
래퍼클래스를 이용하면 기본형 값을 객체로 다룰 수 있다.
래퍼클래스의 생성자는 매개변수로 문자열이나 각 자료형의 값들을 인자로 받는다.
public final class Integer extends Number implements Comparable {
...
private int value; <기본형을 감사고 있음
}
class Ex9_14 {
public static void main(String[] args) {
Integer i = new Integer(100);
Integer i2 = new Integer(100);
System.out.println("i==i2 ? "+(i==i2));
System.out.println("i.equals(i2) ? "+i.equals(i2));
System.out.println("i.compareTo(i2)="+i.compareTo(i2));
System.out.println("i.toString()="+i.toString());
System.out.println("MAX_VALUE="+Integer.MAX_VALUE);
System.out.println("MIN_VALUE="+Integer.MIN_VALUE);
System.out.println("SIZE="+Integer.SIZE+" bits");
System.out.println("BYTES="+Integer.BYTES+" bytes");
System.out.println("TYPE="+Integer.TYPE);
}
}
▼실행결과
i==i2 ? false
i.equals(i2) ? true
i.compareTo(i2)=0
i.toString()=100
MAX_VALUE=2147483647
MIN_VALUE=-2147483648
SIZE=32 bits
BYTES=4 bytes
TYPE=int
래퍼 클래스들은 모두 equals()가 오버라이딩되어 있어서 주소값이 아닌 객체가 가지고 있는 값을 비교한다. (toString도 오버라이딩 되어있음)
출처 : 남궁성. 「자바의 정석」. 도우출판. 2016
'프로그래밍언어 > Java' 카테고리의 다른 글
[자바의 정석] 11. 컬렉션 프레임웍 - 컬렉션 프레임웍의 핵심 인터페이스 (0) | 2023.08.06 |
---|---|
[자바의 정석] 09. java.lang패키지와 유용한 클래스 (2) - 유용한 클래스 (0) | 2023.08.05 |
[자바의 정석] 07. 객체지향 프로그래밍Ⅱ(3) (0) | 2023.08.03 |
[자바의 정석] 07. 객체지향 프로그래밍Ⅱ(2) (0) | 2023.08.02 |
[자바의 정석] 07. 객체지향 프로그래밍Ⅱ(1) (0) | 2023.08.01 |