java.lang 패키지
java.lang패키지는 자바 프로그래밍에 가장 기본이 되는 클래스를 포함하고 있기 때문에, java.lang패키지의 클래스들은 import문 없이도 사용할 수 있습니다. 다음에 정리할 내용들은 java.lang패키지에 포함되어 있는 대표적인 클래스들입니다.
Object 클래스
모든 클래스들의 최고 조상으로, 11개의 메서드를 지니고 있습니다.
equals(Object obj)
public boolean equals(Object obj){
return (this==obj);
}
위의 코드는 Object클래스에 정의되어 있는 equals메서드로, 두 객체의 같고 다름을 참조 변수 값으로 비교합니다.
package ch9;
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는 다릅니다.");
}
}
}
class Value{
int value;
Value(int value){
this.value = value;
}
}
이 결과를 통해 Object클래스로부터 상속받은 equals메서드는 두개의 참조 변수가 같은 객체를 참조하고 있는지, 두 참조변수에 저장된 값이 같은지를 판단하는 기능밖에 할 수 없습니다.
위의 예제는 주소값만을 비교해서 같은지를 판단하는 기능을 수행하고 있습니다.
값을 비교하기 위해서는, Value클래스에서 equals메서드를 오버라이딩하여 주소가 아닌 객체에 저장된 내용을 비교하도록 변경하면 됩니다.
package ch9;
class Person {
long id;
public Person(long id) {
this.id = id;
}
public boolean equals(Object obj){
if(obj instanceof Person)
return id == ((Person)obj).id;
else
return false;
}
}
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의 값을 비교하도록 하여 다른 인스턴스일지라도 같은 id를 지니고 있다면 true를 반환받게끔 했습니다.
hashCode()
해싱기법에 사용되는 해시함수를 구현한 메서드로, 찾고자 하는 값을 입력하면 그 값이 저장된 위치를 알려주는 해시코드를 반환합니다.
package ch9;
public 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메서드에서 오버라이딩 되어있어서 96354라는 동일한 결과가 나옵니다.
반면 System.identityHashCode(Object x)는 Object클래스의 hashCode메서드처럼 객체의 주소값으로 해시값을 생성하기 때문에 모든 객체에 대해 항상 다른 해시코드값을 반환하는 것을 보장합니다. 그래서 str1과 str2가 같지만 서로 다른 객체임을 알 수 있습니다.
toString()
이 메서드는 인스턴스에 대한 정보를 문자열로 제공할 목적으로 정의한 것입니다.
인스턴스의 정보를 제공한다는 것은 대부분의 경우 인스턴스 변수에 저장된 값들을 문자열로 표현한다는 뜻이지요.
public String toString(){
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
package ch9;
class Card{
String kind;
int number;
Card(){
this("SPADE",1);
}
Card(String kind, int nubmer){
this.kind = kind;
this.number = nubmer;
}
}
class CardToString {
public static void main(String[] args) {
Card c1 = new Card();
Card c2 = new Card();
System.out.println(c1.toString());
System.out.println(c2.toString());
}
}
Card클래스에서 toString()을 오버라이딩하지 않았기 때문에, Card 인스턴스에 toString()을 호출하면 Object클래스의 toString()이 호출되기 때문에 클래스 이름과 해시코드가 출력되었습니다.
package ch9;
class Card{
String kind;
int number;
Card(){
this("SPADE",1);
}
Card(String kind, int nubmer){
this.kind = kind;
this.number = nubmer;
}
public String toString(){
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()을 오버라이딩하게 하였습니다.
clone()
이 메서드는 자신을 복제하여 새로운 인스턴스를 생성하는 일을 합니다.
package ch9;
class Point implements Cloneable {
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();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return obj;
}
}
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()메서드는 자신을 복제하여 새로운 인스턴스를 생성하는 일을 합니다.
clone()을 사용하려면 먼저 복제한 클래스가 Cloneable인터페이스를 구현해야하고(클래스 작성자가 복제를 허락한다는 의미), clone()을 오버라이딩하면서 제어자를 protected에서 public으로 변경해야 합니다.
공변 반환타입
JDK 1.5부터 추가된 기능으로, 오버라이딩할 때 조상 메서드의 반환 타입을 자손 클래스의 타입으로 변경을 허용하는 것입니다. 번거로운 형변환의 과정을 생략할 수 있습니다.
package ch9;
import java.util.Arrays;
public class CloneEx2 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
int[] arrClone = arr.clone();
arrClone[0] = 6;
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arrClone));
}
}
얕은 복사와 깊은 복사
얕은 복사란 원본과 복제본이 같은 객체를 참조하는 것으로, 원본을 변경하면 복제본도 변경됩니다. 반면 깊은 복사는 원본을 변경하면 참조하고 있는 객체는 영향을 받지 않습니다.
package ch9;
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;
}
@Override
public String toString() {
return "Circle{" +
"p=" + p +
", r=" + r +
'}';
}
}
deepCopy()는 shallowCopy()와 다르게 복제된 객체가 새로운 Point인 인스턴스를 참조하도록 하였습니다.
String 클래스
String 클래스는 문자열을 저장하고 이를 다루는데 필요한 메서드를 함께 제공합니다.
변경 불가능한 클래스
public final class String implements java.io.Serializable, Comparable{
private char[] value;
}
String 클래스에는 문자열을 정하기 위해서 문자형 배열 참조변수 (char[]) value를 인스턴스 변수로 정의하며, 인스턴스 생성시 생성자 매개 변수로 입력받는 문자열은 이 value값에 문자형 배열로 저장됩니다.
'+' 연산자를 이용하여 String 변수 a와 b를 결합할시 인스턴스 내 문자열이 바뀌는 것이 아니라 새로운 문자열이 담긴 String 인스턴스가 생성됩니다. ( 이러한 문자 결합이 잦을 경우 저장된 문자열 변경할 수 있는 StringBuffer클래스 이용하는 게 좋다고 함)
문자열 리터럴
자바 소스파일에 포함된 문자열 리터럴은 컴파일 시 클래스 파일에 저장되며, 같은 내용의 리터럴은 한번만 저장됩니다. 문자열 리터럴도 instance이기 때문이고, 한번 생성하면 내용을 바꿀 수 없으니 하나의 인스턴스만 공유하는 것이 효율적이기 때문입니다. -> JVM 내부의 Constant Pool에 저장된다고 합니다.
기본형 값을 String으로 변환
int i = 100;
String str1 = i + ""; // 100을 "100"으로 변경하는 방법
String str2 = String.valueOf(i); // 100을 "100"으로 변환하는 방법2
기본형 값을 String 값으로 변환해주기 위한 두가지 방법입니다. valueOf()가 성능이 더 좋다고 합니다.
String을 기본형 값으로 변환
int i = Integer.parseInt("100");
int i2 = Integer.valueOf("100"); // 원래는 반환타입이 Integer이나 Auto Boxing에 의해 int형으로 변환
public static Integer valueOf(String s) throws NumberFormateException{
return Integer.valueOf(parseInt(s,10));
}
StringBuffer클래스
String클래스는 인스턴스를 생성할때 지정된 문자열을 변경할 수 없지만 StringBuffer클래스는 가능합니다. StringBuffer클래스의 인스턴스를 생성할 때, 적절한 길이의 char형 배열이 생성되고 이 배열은 문자열을 저장하고 편집하기 위한 buffer로 사용됩니다. 생성시 여유있게 크기를 지정해주는 것이 좋으며, 지정해주지 않을 경우 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);
}
StringBuffer인스턴스로 문자열을 다룰때, 버퍼의 크기가 작업하려는 문자열의 길이보다 작을 때는 내부적으로 버퍼의 크기를 증가시키는 작업이 수행됩니다.
// 새로운 길이의 배열을 생성한다.(newCapacity는 정수임)
char newValue[] = new char[newCapacity];
// 배열 value의 내용을 newValue로 복사한다
System.arraycopy(value,0,newValue,0,count);
value = newValue; // 새로 생성된 배열의 주소를 참조변수 value에 저장
String클래스에서는 equals메서드를 오버라이딩해서 문자열의 내용을 비교하도록 구현되어있지만, StringBuffer클래스는 equals메서드를 오버라이딩 하지 않아서 StringBuffer클래스의 equals메서드를 사용해도 등가비교연산자(==)로 비교한 것과 같은 결과를 얻습니다.
문자열을 비교하기 위해선 toString()으로 호출하여 String으로 반환한 뒤 equals메서드로 비교해주면 됩니다.
package ch9;
public class StringBufferEx1 {
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));
String s = sb.toString();
String s2 = sb2.toString();
System.out.println("s.equals(s2) ? " + s.equals(s2));
}
}
StringBuilder 클래스
StringBugger는 멀티쓰레드에 안전하도록 동기화되어있습니다. 동기화는 StringBuffer의 성능을 떨어뜨리고, 이를 개선하기 위해 StringBuilder가 새롭게 추가되었습니다.
Wrapper클래스
매개변수로 객체를 요구하거나, 기본형 값이 아닌 객체로 저장해야 할 때, 객체간의 비교가 필요할 때 등등 기본형 값들을 객체로 변환하여 수행해야 하는 경우가 종종 있습니다. 이때 사용되는 것이 Wrapper 클래스입니다.
Wrapper클래스는 객체 생성시 생성자의 인자로 주어진 각 자료형에 알맞는 값을 내부적으로 저장하고 있습니다.
ex) boolean - Boolean , char-Character, byte - Byte, short - Short , int - Integer, long - Long, float - Float, double - Double
Wrapper클래스들은 equals()메서드가 오버라이딩 되어있어서 주소값이 아닌 객체가 가지고 있는 값을 가지고 비교하여, 같은 내용일 경우 true를 리턴합니다.
문자열을 숫자로 변환하기
문자열을 숫자로 변환할 때에는 아래의 방법 중 하나를 선택하면 됩니다.
int i = new Integer("100").intValue();
int i2 = Integer.parseInt("100");
Integer i3 = Integer.valueOf("100");
오토박싱과 언박싱
JDK1.5 이전에는 기본형과 참조형간의 연산이 불가능했기때문에, 래퍼 클래스로 기본형을 객체로 만들어서 연산해야했습니다.
하지만 이후부터는 컴파일러가 자동으로 변환하는 코드를 넣어주기 때문에, 기본형과 참조형간의 덧셈이 가능합니다.
컴파일 전
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(); // intValue()는 Integer객체의 타입을 int타입의 값으로 변환해줍니다.
기본형 값을 래퍼 클래스의 객체로 자동 변환해주는 것을 autoboxing이라고 하고, 반대로 변환하는 것을 unboxing이라고 합니다.
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(10); // autoboxing -> new Integer(10);
int value = list.get(0); -> unboxing. new Integer(10) -> 10
'Language > Java' 카테고리의 다른 글
Collection Framework (0) | 2022.01.02 |
---|---|
날짜와 시간 & 형식화 date, time and formatting (0) | 2021.12.25 |
예외처리 (2) | 2021.12.12 |
객체지향 프로그래밍2 (0) | 2021.12.05 |
완독 스터디 - 객체지향 프로그래밍1 (0) | 2021.11.28 |