# JAVA Ch09. java.lang 패키지와 유용한 클래스[1]

uuuu.jini·2022년 3월 3일
0

JAVA -자바의 정석

목록 보기
10/18
post-thumbnail

목차

  1. java.lang 패키지

1. java.lang패키지

java.lang패키지는 자바프로그래밍에 가장 기본이 되는 클래스들을 포함하고 있다. 그렇기 때문에 import문 없이도 사용할 수 있게 되어 있다. (System클래스나 String클래스를 import문 없이 사용할 수 있었던 이유가 java.lang패키지에 속한 클래스들이었기 떄문이다. )

1.1 ] Object클래스

Object클래스는 모든 클래스의 최고 조상이기 때문에 Object 클래스의 멤버들은 모든 클래스에서 바로 사용이 가능하다.

Object 클래스의 메서드설명
protected Object clone()객체 자신의 복사본을 반환한다.
public boolean equals(Object obj)객체 자신과 객체 obj가 같은 객체인지 알려준다.(같으면 true)
protected void finalize()객체가 소멸될때 가비지 컬렉터에 의해 자동적으로 호출된다. 이 때 수행되어야 하는 코드가 있을 때 오버라이딩한다.(거의 사용안함)
public Class getClass()객체 자신의 클래스 정보를 담고 있는 클래스 인스턴스를 반환한다.
public int hasCode()객체 자신의 해쉬코드를 반환한다.
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()을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간 동안 기다리게 한다.

11개의 메서드만 가지고 있다.

equals(Object obj)

매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean으로 알려준다.

public boolean equals(Object obj){
	return (this==obj);
}

두 객체의 같고 다름을 참조변수의 값으로 판단한다. 그렇기 때문에 서로 다른 두 객체를 equals메서드로 비교하면 항상 false를 결과로 얻게 된다. (객체 생성시 메모리의 비어있는 공간을 찾아 생성하므로, 서로 다른 두개의 객체가 같은 주소를 갖는 일은 없다. 두 개이상의 참조변수가 같은 주소값을 갖는 것은 가능하다. )

package ch09;
class Value{
	int value;
	Value(int value){
		this.value = value;
	}
}
public class EqualsEx1 {
	public static void main(String args[]) {
		Value v1 = new Value(10);
		Value v2 = new Value(10);
		
		if(v1.equals(v2)) {
			
			System.out.println("v1과 v2는 같습니다.");
		}else {
			System.out.println("v1과 v2는 다릅니다.");
		}
		
		v2 = v1;
		
		if(v1.equals(v2)) {
			
			System.out.println("v1과 v2는 같습니다.");
		}else {
			System.out.println("v1과 v2는 다릅니다.");
		}
	}
}

value라는 멤버변수를 갖는 Value클래스를 정의하고 해당 클래스의 인스턴스 v1과 v2를 각각 생성한 경우 두 객체는 서로 다른 메모리 주소를 가지고 이는 equals 비교시 서로 다른 값이라는 것을 의미한다. v2=v1을 통해 v2 참조변수가 v1과 같은 객체를 가리키게 되면서 equals 결과가 참으로 바뀌게 된다.

equlas(Object obj) 는 두 객체의 주소값으로 비교를 한다. 두개의 찹조변수가 같은 객체를 참조하고 있는지를 판단하는 기능을 한다.

만약 euqals메서드를 다른 용도로 사용하고 싶다면, 오버라이딩을 이용하면 된다 .

class Person{
	long id;
	
	public boolean equals(Object obj) {
		if(obj instanceof Person) {
			Person p = (Person)obj;
			return p.id == this.id;
		}else
			return false;
		}
	
	Person(long id){
		this.id = id;
	}
}

위의 코드를 보면 equals 메서드를 오버라이딩 하여 비교하는 인스턴스와 id값이 같을 경우 true를 반환하는 형식으로 작성하였다. 이럴 경우 서로 다른 인스턴스라도 id가 같으면 같은 값이라고 결과를 얻는다.

String클래스도 equals메서드를 그대로 사용하는 것이 아닌 오버라이딩을 통해 String인스턴스가 갖는 문자열 값을 비교하도록 되어있다. 같은 내용의 문자열을 갖는 두 String인스턴스에 equals메서드를 사용시 true를 얻는다.

hasCode()

해싱기법에 사용되는 해시함수를 구현한 것이다. 다량의 데이터를 저장하고 검색하는 데 유용하다. 해시함수는 찾고자 하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드를 반환한다.

Object클래스에 정의된 hasCode메서드는 객체의 주소값으로 해시코드를 만들어 반환하기 때문에 서로 다른 두 객체는 결코 같은 해시코드를 가질 수 없었지만, 64bit JVM에서는 중복될수 있다.

클래스의 인스턴스변수 값으로 객체의 같고 다름을 판단해야 하는 경우라면 equals메서드 뿐만 아니라 hasCode메서드도 적절히 오버라이딩 해야 한다. _같은 객체라면 hasCode메서드를 호출 했을 때 결과값인 해시코드도 같아야 하기 때문이다.

String클래스는 문자열이 같으면, 동일한 해시코드를 반환하도록 hasCode메서드가 오버라이딩 되어있다. System.identityHashCode(object)는 실제 객체의 주소값에 따른 해시코드를 생성하기 때문에 모든 객체별로 다른 해시코드값을 반환하는 것을 보장한다. ( 해시코드는 갖지만 서로 다른 객체임)

toString()

인스턴스에 대한 정보를 문자열로 제공할 목적으로 정의한 것이다. (인스턴스변수에 저장된 값들을 문자열로 표현한다는 뜻이다.)

public String toString(){
	return getClass().getName()+"@"+Integer.toHexString(hasCode());

toString()을 오버라이딩 하지 않는 경우 위의 내용이 그대로 사용될 것이다. (클래스이름@해시코드 를 얻음)

public class ToStringTest {
	public static void main(String[] args) {
		String str = new String("KOREA");
		java.util.Date today = new java.util.Date();
		
		System.out.println(str.toString());
		System.out.println(today.toString());
	}
}

위의 String클래스와 Date클래스의 toString()을 호출하는 경우 클래스이름과 해시코드 대신 String은 인스턴스의 문자열을 , Date클래스는 인스턴스가 갖고 있는 날짜와 시간을 문자열로 변환하여 반환하도록 오버라이딩되어 있다.

보통 toString()은 인스턴스나 클래스에 대한 정보 도는 인스턴스 변수의 값을 문자열로 변환하여 반환하도록 오버라이딩 되는 것이 보통이다.

clone()

자신을 복제하여 새로운 인스턴스를 생성하는 일을 한다. clone메서드를 이용하여 새로운 인스턴스를 생성하여 작업을 하면 작업이전의 값이 보존되므로 작업에 실패해서 원래의 상태로 되돌리거나 변경되기 전의 값을 참고한느데 도움이 될 것이다.

clone()은 단순히 인스턴스변수의 값만 복사하기 때문에 참조타입의 인스턴스 변수가 있는 클래스는 완전한 인스턴스 복제가 이루어지지 않는다. 예를 들어, 멤버변수로 배열을 가지는 경우, 복제된 인스턴스도 같은 배열의 주소를 갖기 때문에 복제된 인스턴스의 작업이 원래 인스턴스에 영향을 미친다. 이런 경우 오버라이딩을 통해 해결해야 한다.

package ch09;
class Point implements Cloneable{
	// Cloneable을 구현한 클래스만 clone()메서드를 호출할 수 있다.
	
	int x,y;
	
	Point(int x, int y ){
		this.x = x;
		this.y = y;
	}
	public String toString() {
		return "x : " + x + " y : " + y;
	}
	
	public Object clone() {
		Object obj = null;
		try {
			obj = super.clone(); // clone()반드시 예외 처리 
		}catch(CloneNotSupportedException e) {		}
		
		return obj;
	}
}
public class CloneEx1 {
	public static void main(String[] args) {
		Point original = new Point(3,5);
		Point copy = (Point)original.clone();
		System.out.println(original);
		System.out.println(copy);
	}
}

clone()을 사용하려면 먼저 사용하려는 클래스가 Cloneable인터페이스를 구현해야 하면 오버라이딩 하여 접근 제어자를 public으로 변경한다. (상속관계가 없는 클래스에서 clone()호출이 가능)

조상클래스의 clone() 을 호출하는 코드가 포함된 try-catch문을 작성한다.

Cloneable인터페이스를 구현한다는 것은 클래스 작성자가 복제를 허용한다는 것이다.

공변 반환타입(covariant return type)

오버라이딩시 조상 메서드의 반환타입을 자손 클래스의 타입으로 변경을 허용하는 것이다. 공변 반환타입을 사용하면, 조상의 타입이 아닌, 실제로 반환되는 자손 객체의 타입으로 반환할 수 있어서 번거로운 형변환이 줄어든다는 장점이 있다.

배열의 복사는 배열도 객체이기 때문에 Object클래스를 상속받으면 동시에 Cloneable인터페이스를 구현한다. clone()메서드를 public으로 오버라이딩하고 반환값을 해당 배열타입으로 반환하므로 형변환이 필요가 없다.(Vector,ArrayList,LinkedList,HashSet,TreeSet..등도 동일)

얕은 복사와 깊은 복사

clone()은 단순히 객체에 저장된 값을 그대로 복제 할 뿐, 객체가 참조하고 있는 객체까지 복제하지는 않는다. 원본과 복제본이 같은 객체를 공유하게 되면 완전한 복제라고 보기 어려우며 이런 복제를 얕은 복사(shallow copy)라고 한다. 얕은 복사에서는 원본을 변경하면 복사본도 영향을 받는다.

원본이 참조하고 있는 객체까지 복제하는 것을 깊은 복사(deep copy)라고 하며, 깊은 복사에서는 원본과 복사본이 서로 다른 객체를 참조하기 때문에 원본의 변경이 복사본에 영향을 미치지 않는다.

getClass()

자신이 속한 클래스의 Class객체를 반환하는 메서드이다. Class객체는 이름이 Class인 클래스의 객체이다. Class객체는 클래스의 모든 정보를 담고 있으며, 클래스 당 1개만 존재한다. 클래스 파일이 클래스 로더에 의해서 메모리에 올라갈 때, 자동으로 생성된다.

Class 객체를 얻는 방법

클래스의 정보가 필요할 때, 먼저 Class객체에 대한 참조를 얻어와야 하는데, 해당 Class객체에 대한 참조를 얻는 방법은 여러 가지가 있다.

Class cObj = new Card().getClass(); //생성된 객체로 부터 얻는 방법
Class cObj = Card.class; // 클래스 리터럴로부터 얻는 방법
Class cObj = Class.forName("Card"); //클래스 이름으로 부터 얻는 방법

forName()은 특정 클래스 파일, 예를 들어 데이터베이스 드라이버를 메모리 올릴 때 주로 사용한다. Class객체를 이용하면 클래스 정의된 멤버 이름,개수 등 클래스 모든 정보를 얻을 수 있기 때문에 동적인 코드를 작성할 수 있다.

1.2 ] String 클래스

인터넷 끊겨서 다시 작성해야 하는데 복습할떄 해야지(아휴;)

String을 기본형 값으로 변환

valueOf() 를 사용하거나 parseInt() 를 사용하면 된다.

문자열 --> 기본형
Boolean.parseBoolean(String s)
Byte.parseByte(String s)
Short.parseShort(String s)
Integer.parseInt(String s)
Long.parseInt(String s)
Float.parseFloat(String s)
Double.parseDouble(String s)

Wrapper Class

: 기본형을 감싸고 있는 클래스라는 뜻이며, 기본형을 클래스로 표현한 것이다.

parseInt나 parseDouble같은 메서드는 문자열에 공배 또는 문자가 포함되어 있는 경우 반환시 예외(NumberFormatException)가 발생할 수 있다. -- 문자열 양끝 공백 제거하는 trim()을 습관적으로 같이 사용한다.

int val = Integer.parseInt("123".trim());

부호를 의미하는 +나 소수점을 의미하는 .와 float형 값을 뜻하는 f와 같은 자료형 접미사는 허용된다.

substring(index)

문자열의 내용의 일부를 추출하는 메서드로 substring(int start,int end)를 사용시 start와 end의 범위중 end위치에 있는 문자는 결과에 포함되지 않는다.

1.3 ] StringBuffer클래스와 StringBuilder클래스

String클래스는 인스턴스를 생성할 떄 지정된 문자열을 변경할 수 없지만 StringBuffer클래스는 변경이 가능하다. 내부적으로 편집을 위한 버퍼를 가지고 있으며, 인스턴스 생성시 그 크기를 지정할 수 있다.

StringBuffer클래스는 String클래스와 같이 문자열을 저장하기 위한 char형 배열의 참조변수를 인스턴스 변수로 선언해 놓고 있다.

StringBuffer의 생성자

해당 클래스의 인스턴스를 생성할 때, 적절한 길이의 char형 배열이 생성되고, 이 배열은 문자열을 저장하고 편집하기 위한 공간(buffer)으로 사용된다.

인스턴스 생성시에는 생성자 StringBuffer(int length)를 사용해서 인스턴스에 저장되 문자열의 길이를 고려하여 충분히 여유있는 크기로 지정하는 것이 좋다. (지정안할시 16개의 문자를 저장할 수 있는 크기의 버퍼를 생성한다.)

public StringBuffer(int length){
	value = new char[length];
    shared = false;
}

public StringBuffer(){
	this(16);
}

public StringBuffer(String str){
	this(str.length()+16);
    append(str); // 지정한 문자열 길이보다 16이 더 크게 버퍼를 생성한다.
}

StringBuffer인스턴스로 문자열을 다루는 작업을 할 때, 버퍼의 크기가 작업하려는 문자열의 길이보다 작을 때는 내부적으로 버퍼의 크기를 증가시키는 작업이 수행된다. (배열의 길이는 변경될 수 없으므로 새로운 길이의 배열을 생성한 후에 이전 배열의 값을 복사해야 한다.)

StringBuffer의 변경

String과 달리 내용을 변경할 수 있다.

StringBuffer sb = new StringBuffer("abc");
sb.append("123");

append() 는 반환타입이 StringBuffer인데 자신의 주소를 반환한다. 즉 append를 수행하면 sb에 새로운 문자열이 추가되고 sb자신의 주소를 반환하여 새로운 StringBuffer인스턴스가 참조하도록 한다.

즉, 하나의 StringBuffer인스턴스에 연속적으로 append()를 호출하는 것이 가능하다.

StringBuffer sb = new StringBuffer("abc");
sb.append("123").append("zzz");

StringBuffer의 비교

StringBuffer클래스는 equals(Object obj) 를 오버라이딩 하지 않아서 등가비교연산자(==)로 비교한 것과 같은 결과를 얻는다.

반면에 toString() 메서드는 오버라이딩 되어 있어서 담고있는 문자열을 String으로 반환한다. 그래서, 담고있는 문자열의 비교를 위해서는 각 문자열을 toString() 으로 얻은 다음, 여기에 equals메서드를 사용해서 비교해야 한다.

String s = sb.toString();
String s2 = sb2.toString();

System.out.println(s.equals(s2));

StringBuffer클래스의 생성자와 메서드

String클래스와 유사한 메서드이외에에 추가,변경,삭제와 같이 저장된 내용을 변경할 수 있는 메서드들이 추가로 제공된다.

메서드설명
append(매개변수)매개변수로 입력된 값을 문자열로 변환하여 StringBuffer인스턴스가 저장하고 있는 문자열의 뒤에 덧붙인다.
int capacity()StringBuffer인스턴스의 버퍼크기를 알려준다. length()는 버퍼에 담긴 문자열의 길이를 알려준다.
StringBuffer deleteCharAt(int index)지정된 위치의 문자를 제거한다.
StringBuffer delete(int strat,int end)시작위치부터 끝 위치 사이에 있는 문자를 제거한다.
StringBuffer insert(int pos, 매개변수 )두번째 매개변수로 받은 값을 문자열로 변환하여 지정된 위치에 추가한다.
StringBuffer reverse()문자열의 순서를 거꾸로 나열한다.
void setLength(int newLength)지정된 길이로 문자열의 길이를 변경한다. 길이를 늘리는 경우 빈공간을 널문자 '\u000'로 채운다.

StringBuilder란 ?

StringBuffer는 멀티쓰레드에 안전하도록 동기화 되어 있다. 동기화 는 STringBuffer의 성능을 떨어뜨린다. 그래서 StringBuffer의 쓰레드의 동기화만 뺸 StringBuilder가 새로 추가되었다. 즉, StringBuffer타입의 참조변수를 선언한 부분과 StringBuffer의 생성자만 바꾸면 된다.

StringBuilder sb;
sb = new StringBuilder();
sb.append("abc");

성능향상이 반드시 필요한 경우를 제외하고는 기존에 작성한 코드에서 StringBuffer를 StringBulder를 굳이 바꿀 필요는 없다.

profile
멋쟁이 토마토

0개의 댓글