스터디/JAVA

[자바의 정석] java.lang패키지와 유용한 클래스

멩주 2022. 10. 14. 22:49

✍️ java.lang 패키지란?

자바 프로그래밍에서 가장 기본이 되는 클래스들을 포함한 패키지이다.

import 문 없이 사용할 수 있다. 예) String, System

 

1.1 Object 클래스

Object 클래스란?

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

 

<메서드>

protected Object clone() 객체 자신의 복사본 반환
public boolean equals(Object obj) 객체 자신과 객체 o가 같은 객체인지 불린값
protected void finalize() 객체가 소멸될 때 가비지 컬랙터에 의해 자동으로 호출된다. 이 때 수행되어야하는 코드가 있을 때, 오버라이딩 한다.(잘 사용되지 않음)
pubic Class getClass() 객체 자신의 클래스 정보를 담고 있는 Class 인스턴스를 반환한다.
public int hashCode() 객체 자신의 해시코드를 반환한다.
public String toString() 객체 자신의 정보를 문자열로 반환한다.
public void notify() 객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨운다.
public void notifyAll() 객체 자신을 사용하려고 기다리는 모든 스레드를 깨운다.
public void wait() 다른 쓰레드가 notify()나 notifyAll() 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간 동안 기다리게 한다.

 

# equals(Object obj)

매개변수로 객체의 참조변수를 받아 주소값을 비교해 boolean 값을 알려준다.

String, Date, File, Wrapper 클래스의 경우 eqauls 메서드로 주소값이 아닌 내용을 비교하도록 오버라이딩되어 있다.

 

# hashCode()

해싱 기법에 사용되는 해시 함수를 구현한 것.

  • 해싱은 데이터 관리 기법 중 하나로 다량의 데이터를 저장하고 검색하는데 용이하다.
  • 찾고자하는 값을 해시함수에 입력하면, 그 값이 저장된 위치를 반환해 준다.
  • 해시코드가 같은 두 객체가 존재하는 것은 가능하지만, Object 클래스에 정의된 hashCode 메서드는 객체의 주소값으로 해시코드를 반환하기 때문에 32bit JVM에서는 같은 해시코드를 가질 수 없었다.
  • 그러나 64 bit JVM에서는 8byte 주소값으로 해시코드(4byte)를 만들기 때문에 해시코드가 중복될 수 있다.

 

🐤 String 클래스는 문자열의 내용이 같으면 동일한 해시코드를 반환하도록 hashCode 메서드가 오버라이딩되어 있다.

  • 반면에 System.indentifyHashCode(Object x)는 Object 클래스의 메서드처럼 객체의 주소값으로 해시코드를 생성해 모든 객체에 대해 항상 다른 해시코드값을 반환한다. <- 실행시마다 값이 변경될 수 있다.

 

# toString()

인스턴스에 대한 정보를 문자열로 제공할 목적을 가진다. 보통 인스턴스 변수에 저장된 값을 문자열로 표현한다.

기본 Object의 toString은 클래스명 + 16진수 해시코드를 반환한다.

  • 보통 인스턴스의 클래스 정보, 멤버 변수의 값을 문자열로 반환하도록 오버라이딩한다.
String,, Date는 String 인스턴스가 가지는 문자열 값, Date 인스턴스가 가지는 날짜, 시간을 문자열로 반환한다.
조상의 메서드를 자손 클래스에서 오버라이딩할 때, 접근제어자는 조상보다 크거나 같은 범위를 가져야한다.
Object의 toString 접근제어자는 public으로 자손 클래스는 public으로만 재정의할 수 있다.

 

# clone()

자신을 복제해 새로운 인스턴스를 생성한다.

  • 작업을 수행할 때, 원래 인스턴스를 보존하고 clone 메서드를 사용해 새로운 인스턴스로 작업을 진행해, 작업 이전의 값이 보존되게 한다. 실패할 때 되돌리기
  • Object의 clone()은 단순히 인스턴스 변수의 값만 복사하기 때문에 참조타입의 인스턴스 변수가 있는 클래스는 완전한 인스턴스 복제가 이루어지지 않는다.
배열의 경우, 복제된 인스턴스도 같은 배열의 주소를 갖기 때문에 복제된 인스턴스의 작업이 원래 인스턴스에 영향을 미친다.
clone 메서드를 오버라이딩해 새로운 배열을 생성, 복사 해야 한다.
  • clone을 사용하려면, 복제할 클래스에 대해 Cloneable 인터페이스를 상속해야 한다.
  • Object의 clone() 접근 제어자는 protected로 오버라이딩시, 다른 곳에서도 사용할 수 있게 public으로 변경해주자.
Cloneable 인터페이스를 상속한 클래스만 복제가 가능하다.
인스턴스의 데이터를 보호하기 위해서

 

JDK1.5부터 공변 반환타입을 지원한다.
상속의 오버라이딩에서 조상 메서드의 반환타입을 자손 클래스로 변경하는 것을 허용하는 것이다.
즉, Object의 clone은 Object를 반환했으나, public Point clone() {}으로 오버라이딩 할 수 있다. <- Point는 Object의 자손이다.
반환되는 곳에서 명시적 형변환 없이, 바로 자손 객체를 받아 사용할 수 있다.

 

🐤 Array clone 해보기!

  • clone으로 복제가 가능한 클래스인지 확인하고 싶다면, JavaAPI에서 Cloneable을 구현하고 있는지 확인하자

 

# 얕은 복사와 깊은 복사

clone()은 단순히 객체에 저장된 값을 그대로 복제하는 것이다.

객체가 참조하고 있는 객체까지 복사하지 않는다.

  • 기본형 배열의 경우, 문제가 없지만 객체배열을 clone하는 경우, 원본과 복제본이 같은 객체를 공유하게 된다.
  • 이런 복사를 "얕은 복사"라고 한다.
  • 원본이 참조하는 객체까지 복샇는 것을 "깊은 복사"라고 한다.

참조된 객체의 경우 얕은 복사가 이루어진다.

  • 새로 만들어서 반환할 수 있도록 한다.

 

# getClass()

자신이 속한 클래스의 Class 객체를 반환하는 메서드

  • Class 객체는 이름이 'Class'인 클래스의 객체이다.
  • Class 객체는 클래스의 모든 정보를 담고 있으며, 클래스 당 1개만 존재한다.
  • 클래스 파일이 클래스 로더에 의해 메모리에 올라갈 때, 자동으로 생성된다.
  • 클래스 로더란?
    • 실행 시 필요한 클래스를 동적으로 메모리에 로드한다.
    • 메모리에 클래스 객체가 존재하면 참조값을 반환한다.
    • 없으면 클래스 파일을 찾아 읽어서 Class 객체로 반환한다.

 

<Class 객체 얻는 방법>

  • 생성된 객체로부터 얻기: Class c1 = new Card().getClass();
  • 클래스 리터럴(*.class)로 얻기: Card.class;
  • 클래스명으로 얻기: Class.forName("Card");
  • Class 객체를 통해 클래스에 정의된 멤버의 이름이나 개수 등, 클래스의 모든 정보를 얻을 수 있다.
  • 객체 생성, 메소드 호출도 가능하다.
new Card() == Card.class.newInstance();

 

1.2 String 클래스

기존의 다른 언어에서는 문자열을 char[]로 다뤘으나 자바에서는 문자열을 위한 클래스가 존재한다.

 

# 변경 불가능(immutable) 클래스

String 클래스에는 문자열 저장을 위해 문자형 배열 참조변수(char[]) 값을 인스턴스 변수로 갖고 있으며, 이곳에 저장된다.

한 번 생성된 String 인스턴스가 갖고 있는 문자열은 읽기만 가능하고 변경할 수 없다.

  • String a = "a"; String b = "b"; a = a+b 는 새로운 문자열 "ab" 인스턴스를 반환한다.
  • 덧셈 연산자를 사용해 문자열을 결합한느 것은 새로운 String 인스턴스를 생성해 메모리 공간을 차지한다.
  • 대신, StringBuffer클래스를 사용하자. StringBuffer은 인스턴스에 저장된 문자열을 변경 가능하다.

 

# 문자열의 비교

  • 문자열은 같은 값을 가지고 있으면 같은 메모리를 가리키게 된다. // 문자열 리터럴 사용 == 재사용을 목적으로 한다.
  • new String("")을 통해 생성하면 새로운 인스턴스를 생성하기 때문에 다른 메모리를 가리킨다. // String 클래스 생성자 사용

문자열 리터럴, String 클래스 생성자 사용

 

# 빈 문자열

길이가 0인 배열은 존재할 수 있다. 예) String str = "";

char c = '';는 안된다. char형 변수에는 반드시 하나의 문자를 지정해야 한다.
공백은 가능하다.

 

# join()과 StringJoiner

join()

  • 여러 문자열 사이에 구분자를 넣어 결합한다. 구분자로 문자열을 나누는 split()과 반대의 작업을 진행한다.
  • String.join("{구분자}", String[] strArr)을 하면 구분자를 넣어 문자열 배열을 하나의 문자열로 반환해준다.

StringJoiner

StringJoiner의 사용 방법

  • text == "(Car Insurance,Health Insurance,Life Insurance)"

join과 StringJoiner은 JDK 1.8부터 추가되었다.

 

# String.format()

형식화된 문자열을 만들어내는 방법이다. printf()와 사용법이 동일하다.

String str = String.format("%d 더하기 %d는 %d이다", 3, 5, 3+5);

 

# 기본형을 String으로 변환

  • String.valueOf()을 사용하면 ()의 값이 스트링으로 변환된다. 또는 기본형에 빈 문자열을 더할 수 있다.
  • 또는 참조형에 문자열을 더하면 참조변수가 가리키는 인스턴스의 toString을 호출해 String을 얻고 결합한 값을 반환한다.

 

# String을 기본형 값으로 변환

정수의 경우 Integer.valueOf() 혹은 Integer.parseInt()를 사용해 변환할 수 있다.

  • 문자열 "A"를 문자 'A'로 변환하려면 "A".charAt(0)을 쓰면 된다.
  • Double.parseDouble(String s);
  • 문자열 양끝의 공백을 제거해주는 trim()을 사용해줘야한다.
  • substring(시작인덱스, 끝인덱스+1);

 

1.3 StringBuffer 클래스와 StringBuilder 클래스

StringBuffer 클래스는 재부적으로 문자열 편집을 위한 버퍼를 가지고 있어 지정된 문자열을 변경할 수 있다.

또한 인스턴스 생성시 그 크기를 지정할 수 있다.

  • 버퍼의 길이를 넘어서게 되면 버퍼의 길이를 늘리는 작업이 추가로 수행되어야 하기에 작업 효율이 떨어질 수 있다.
  • StringBuffer도 char형 배열의 참조변수를 인스턴스 변수로 둔다.

 

# StringBuffer의 생성자

StringBuffer 인스턴스 생성 시, 적절한 길이의 char형 배열이 생성되고, 이 배열은 문자열의 저장과 편집을 위한 버퍼로 사용된다.

  • new StringBuffer(int length)로 적절한 버퍼 크기를 지정해주는 것이 좋다. 없으면 기본으로 16개의 문자가 저장될 버퍼가 생성된다.
  • 문자열 길이를 넘어서게 되면 내부적으로 길이가 긴 새로운 배열을 생성해 이전 배열을 복사해준다.

 

# StringBuffer의 변경

StringBuffer sb = new StringBuffer();
sb.append("aaa").append("bbb");

// sb.toString() == "aaabbb"
  • append는 자기 자긴 sb를 반환한다.

 

# StringBuffer의 비교

String에서의 equals 메서드는 문자열의 내용을 비교하도록 되어있지만 StringBuffer은 오버라이딩 되지 않아 ==(등가비교연산자)와 같은 의미를 갖는다. 주소값 비교

 

# String Builder

StringBuffer는 멀티쓰레드에 안전하도록 동기화되어 있다.

멀티쓰레드로 작성된 프로그램이 아닌 경우, StringBuffer의 동기화는 불필요하게 성능만 떨어트릴 수 있다.

StringBuilder는 쓰레드의 동기화가 빠진 것이다.