[Chapter 9] java.lang 패키지와 유용한 클래스_1

slchoi·2022년 1월 18일
0

자바의 정석

목록 보기
18/19
post-thumbnail

'자바의 정석 3rd Edition'를 공부하며 정리한 내용입니다.

1. java.lang 패키지


  • 자바프로그래밍에 가장 기본이 되는 클래스들을 포함하기 때문에 import문 없이도 사용 가능

1.1 Object 클래스

  • 멤버변수는 없고 11개의 메서드만 가짐
    • 메서드들은 모든 인스턴스가 가져야할 기본적인 것들

1. equals(Object obj)

public boolean equals(Object obj){
	return (this==obj);
}
  • 매개변수로 객체의 참조변수를 받아 비교하여 그 결과를 boolean값으로 알려 주는 역할
  • 서로 다른 두 객체를 equals 메서드로 비교하면 항상 false를 결과로 얻음
    • 이유: 두 객체의 같고 다름을 참조변수의 값으로 판단
    • 객체를 생성할 때, 메모리의 비어있는 공간을 찾아 생성하므로 서로 다른 두 개의 객체가 같은 주소를 갖을 수 없음
    • 두 개 이상의 참조변수가 같은 주소값을 갖는 것(한 객체를 참조하는 것)은 가능
  • equals 메서드는 결국 두 개의 참조변수가 같은 객체를 참조하고 있는지, 즉 두 참조변수에 저장된 값(주소값)이 같은지를 판단하는 기능만 함
    • equals를 사용해 저장된 값을 비교하고 하고자 하나면 equals 메서드를 오버하이딩해 주소가 아닌 객체에 저장된 내용을 비교하도록 변경해주어야 함
    • String, Date, File, wrapped 클래스의 equals메서드는 주소값이 아닌 내용을 비교하도록 오버라이딩 되어 있음
    • StringBuffer 클래스는 오버라이딩 되어있지 않음

2. hashCode()

  • 해싱(hashing) 기법에 사용되는 '해시함수(hash function)'를 구현한 것
    • 해싱: 데이터관리기법 중 하나. 다량의 데이터를 저장하고 검색하는 데 유용
    • 해시함수: 찾고자하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드(hash code)를 반환
  • 해시코드가 같은 두 객체가 존재하는 것은 가능하지만, Object 클래스에 정의된 hashCode 메서드는 객체의 주소값으로 해시코드를 만들어 반환하기 때문에 32bit JVM에서는 서로 다른 두 객체는 결코 같은 해시코드를 가질 수 없없지만, 64bit JVM에서는 8byte 주소값으로 해시코드(4byte)를 만들기 때문에 해시코드가 중복될 수 있음
  • 클래스의 인스턴스변수 값으로 객체의 같고 다름을 판단해야하는 경우라면 equals 메서드와 hashCode 메서드도 적절히 오버라이딩해야 함
    • 같은 객체라면 hashCode 메서드를 호출했을 때의 결과값인 해시코드도 같아야하기 때문

3. toString()

public String toString() {
	return getClass().getName()+"@"+Integer.toHexString(hashcode());
}
  • 인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것
  • 인스턴스 정보를 제공한다는 것은 대부분의 경우 인스턴스 변수에 저장된 값들을 문자열로 표현한다는 의미
  • 클래스 작성시 toString()을 오버라이딩하지 않으면 클래스이름에 16진수의 해시코드를 얻게 됨
    • String 클래스의 toString()은 String 인스턴스가 갖고 있는 문자열을 반환하도록 오버라이딩됨
    • Date 클래스의 경우 Date 인스턴스가 갖고 있는 날짜와 시간을 문자열로 변환하여 반환하도록 오버라이딩됨

4. clone()

  • 자신을 복제하여 새로운 인스턴스를 생성
  • 어떤 인스턴스에 대해 작업할 때, 원래의 인스턴스는 보존하고 clone 메서드를 이용해 새로운 인스턴스를 생성하여 작업하면, 작업이전의 값이 보존되므로 작업에 실패해서 원래의 상태로 되돌리거나 변경되기 전의 값을 참고하는데 도움이 됨
  • Object 클래스에 정의된 clone()은 단순히 인스턴스변수의 값만 복사하기 때문에 참조타입의 인스턴스 변수가 있는 클래스는 완전히 인스턴스 복제가 이루어지지 않음
  • clone()을 사용하려면
    • 복제할 클래스가 Cloneable 인터페이스를 구현해야 하고
    • clone()을 오버라이딩하면서
    • 접근 제어자를 protected에서 public으로 변경 (상속관계가 없는 다른 클래스에서 clone() 호출 가능하도록)
    • 조상클래스의 clone()을 호출하는 코드가 포함된 try-catch문 작성

공변 반환타입

  • 공변 반환타입(covariant return type): 오버라이딩할 때 조상 메서드의 반환타입을 자손 클래스의 타입으로 변경을 허용하는 것
  • 장점: 조상의 타입이 아닌 실제로 반환되는 자손 객체의 타입으로 반환할 수 있어 형변환이 줄어듦

얕은 복사와 깊은 복사

  • 얕은 복사(shallow copy): 원본을 변경하면 복사본도 영향을 받음
  • 깊은 복사(deep copy): 원본이 참조하고 있는 객체까지 복제하는 것
    • 원본과 복사본이 다른 객체를 참조하기 때문에 원본의 변경이 복사본에 영향을 미치지 않음

5. getClass()

  • 자신이 속한 클래스의 Class 객체를 반환하는 메서드
    • Class 객체는 이름이 'Class'인 클래스 객체
  • Class 객체는 클래스의 모든 정보를 담고 있으며, 클래스 당 1개만 존재. 클래스 파일이 클래스 로더에 의해 메모리에 올라갈 때 자동으로 생성됨
    • 클래스 로더: 실행 시에 필요한 클래스를 동적으로 메모리에 로드하는 역할을 수행
      • 기존에 생성된 클래스 객체가 메모리에 존재하는지 확인하고, 있으면 객체의 참조를 반환하고 없으면 클래스 패스에 지정된 경로를 따라 클래스 파일을 찾음
      • 못 찾으면 ClassNotFoundException이 발생하고, 찾으면 해당 클래스 파일을 읽어서 Class 객체로 변환
    • 파일 형태로 저장되어 있는 클래스를 읽어서 Class 클래스에 정의된 형식으로 변환하는 것
    • 클래스 파일을 읽어서 사용하기 편한 형태로 저장해 놓은 것이 클래스 객체

Class 객체를 얻는 방법

  • 클래스 정보가 필요할 때, 가장 먼저 Class 객체에 대한 참조를 얻어와야 함
  • forName()은 특정 클래스 파일(예를 들어 데이터베이스 드라이버를 메모리에 올릴 때)에 주로 사용
  • Class 객체를 이용하면 클래스에 정의된 이름이나 개수 등 클래스에 대한 모든 정보를 얻을 수 있기 때문에 Class 객체를 통해 객체를 생성하고 메서드를 호출하는 등 동적인 코드 작성이 가능

1.2 String 클래스

  • 문자열을 저장하고 다루는데 필요한 메서드를 제공

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

  • 문자열을 저장하기 위해 문자형 배열 참조변수(char[]) value를 인스턴스 변수로 정의해놓음
    • 인스턴스 생성 시 생성자의 매개변수로 입력받는 문자열은 이 value에 문자형 배열로 저장됨
  • 한번 생성된 String 인스턴스가 갖고 있는 문자열은 읽어 올 수만 있고, 변경은 불가능
    • '+'를 사용해서 문자열을 결합하는 것은 매 연산 시 마다 새로운 문자열을 가지는 String 인스턴스가 생성되어 메모리 공간을 차지하게 되므로 결합횟수를 줄이는 것이 좋음
  • 문자열간의 결합이나 추출 등 문자열을 다루는 작업이 많이 필요한 경우에는 String 클래스 대신 StringBuffer 클래스를 사용하는 것이 좋음
    • StringBuffer 인스턴스에 저장된 문자열은 변경이 가능하므로 하나의 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()를 사용했을 때눈 문자열의 내용을 비교하기 때문에 두 경우 모두 true를 결과로 얻지만, String 인스턴스의 주소를 '=='로 비교했을 때는 리터럴을 사용할 경우 true, String 인스턴스의 경우 false가 나옴

문자열 리터럴

  • 자바 소스파일에 포함된 모든 문자열은 컴파일 시에 클래스 파일에 저장됨
  • 같은 내용의 문자열 리터럴은 한번만 저장됨
    • 이유: 문자열 리터럴도 String 인스턴스이고, 한번 생성하면 내용을 변경할 수 없으므로 하나의 인스턴스를 공유하면 되기 때문
  • 클래스 파일에는 소스파일에 포함된 모든 리터럴의 목록이 있음
    • 해당 클래스 파일이 클래스 로더에 의해 메모리에 올라갈 때, 이 리터럴의 목록에 있는 리터럴들이 JVM 내에 있는 상수 저장소(constant pool)에 저장됨

빈 문자열(empty string)

  • 변수를 선언할 때, 각 타입의 기본값으로 초기화하지만 String은 참조형 타입의 기본값인 null 보다는 빈 문자열로 초기화하는 것이 일반적

문자 인코딩 변환

  • getBytes(String charsetName)를 사용하면, 문자열의 문자 인코딩을 다른 인코딩으로 변경할 수 있음
byre[] utf8_str = "가".getBytes("UTF-8");	// 문자열을 UTF-8로 변환
String str = new String(utf8{_str, "UTF-8";)	// byte 배열을 문자열로 변환
  • 서로 다른 문자 인코딩을 사용하는 컴퓨터 간에 데이터를 주고받을 때는 적절한 문자 인코딩이 필요

기본형 값을 String으로, String 값을 기본형 값으로 변환

  • 기본형을 문자열로 변환하는 방법은 숫자에 빈문자열("")을 더해주면 됨. valueOf()을 사용하는 방법도 있음
  • String을 기본형으로 변환하는 방법은 valueOf()를 쓰거나 parseInt()을 사용하면 됨

1.3 StringBuffer 클래스와 StringBuilder 클래스

  • StringBuffer 클래스는 인스턴스를 생성할 때 지정된 문자열을 변경할 수 있음
    • 내부적으로 문자열 편집을 위한 버퍼를 가지고 있고, StringBuffer 인스턴스를 생성할 때 크기를 지정할 수 있음
    • StringBuffer 인스턴스가 생성될 때, char형 배열이 생성되며 이 때 생성된 char형 배열을 인스턴스 변수 value가 참조하게 됨

StringBuffer의 생성자

  • StringBuffer 클래스의 인스턴스를 생성할 때, 적절한 길이의 char형 배열이 생성되고, 이 배열은 문자열을 저장하고 편집하기 위한 공간으로 사용됨
  • 생성자 StringBuffer(int length)를 사용해서 StringBuffer 인스턴스에 저장될 문자열의 길이를 고려해 여유있는 크기로 지정하는 것이 좋음
    • 크기를 지정해주지 안흥면 16개의 문자를 저장할 수 있는 크기의 버퍼를 생성
  • StringBuffer 인스턴스로 문자열을 다룰 때, 버퍼의 크기가 작업하려는 문자열의 길이보다 작은 경우 내부적으로 버퍼의 크기를 증가시키는 작업이 수행됨
    • 배열의 길이는 변경될 수 없으므로 새로운 길이의 배열을 생성한 후에 이전 배열의 값을 복사해야 함

StringBuffer의 변경

  • append(): 반환타입이 StringBuffer로 자신의 주소를 반환

StringBuffer의 비교

  • StringBuffer 클래스는 equals 메서드를 오버라이딩하지 않아서 StringBuffer 클래스의 equals 메서드를 사용해도 '=='로 비교한 것과 같은 결과를 얻음
  • toString()은 오버라이딩되어 있어 StringBuffer 인스턴스에 toString()을 호출하면, 담고있는 문자열을 String으로 반환
  • StringBuffer 인스턴스에 담긴 문자열 비교를 위해선 StringBuffer 인스턴스에 toString()을 호출해서 String 인스턴스를 얻은 다음, 여기에 equals 메서드를 사용해서 비교해야 함

StringBuilder란?

  • StringBuffer는 멀티쓰레드에 안전(thread safe)하도록 동기화되어 있음
    • 동기화는 StringBuffer의 성능을 떨어트림
    • 멀티쓰레드로 작성된 프로그램이 아닌 경우, StringBuffer의 동기화는 불필요하게 성능만 떨어트림
  • StringBuffer에서 쓰레드의 동기화만 뺀 StringBuilder가 새로 추가됨
    • StringBuilder은 StringBuffer와 완전치 똑같은 기능으로 작성되어 있어, 소스코드에서 StringBuffer대신 StringBuilder를 사용하도록 바꾸기만 하면 됨

1.4 Math 클래스

  • Math 클래스는 기본적인 수학계산에 유용한 메서드로 구성되어 있음
  • Math 클래스의 경우 생성자는 접근 제어자가 private이기 때문에 다른 클래스에서 Math 인스턴스를 생성할 수 없도록 되어 있음
    • 이유: 클래스 내에 인스턴스변수가 하나도 없어서 인스턴스를 생성할 필요가 없기 때문
  • Math 클래스의 메서드는 모두 static

올림, 버림, 반올림

  • round(): 소수 첫째자리에서 반올림해서 정수값(long)을 결과로 돌려줌
  • rint(): 소수 첫째자리에서 반올림해서 double형으로 결과를 돌려줌
    • 두 정수의 정가운데 있는 값은 가장 가까운 짝수 정수를 반환
  • floor(): 버림 연산을 수행

예외를 발생시키는 메서드

  • JDK 1.8부터 메서드 이름에 'Exact'가 포함된 메서드들이 추가됨. 정수형간의 연산에서 발생할 수 있는 오버플로우를 감지하기 위한 것
  • 연산자는 단지 결과를 반환할 뿐, 오버플로우의 발생여부에 대해 알려주지 않음. Exact가 붙은 메서드들은 오버플로우가 발생하면 예외를 발생시킴

1.5 래퍼(wrapper) 클래스

사진 출처: https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=heartflow89&logNo=220975218499

  • 기본형 변수를 객체로 다뤄하는 경우 사용하는 것이 래퍼(wrapper) 클래스
  • 8개의 기본형을 대표하는 8개의 래퍼 클래스가 있고, 이 클래스들을 이용해 기본형 값을 객체로 다룰 수 있음
  • 래퍼 클래스 생성자는 매개변수로 문자열이나 각 자료형의 값들을 인자로 받음
    • 생성자의 매개변수로 문자열을 제공할 때, 각 자료형에 알맞는 문자열을 사용해야 함
  • 래퍼 클래스들은 객체생성 시에 생성자의 인자로 주어진 각 자료형에 알맞은 값을 내부적으로 저장하고 있고, 이와 관련된 여러 메서드가 정의되어 있음
  • 래퍼 클래스는 equals()가 오버라이딩되어 있어 주소값이 아닌 객체가 가지고 있는 값을 비교
  • 래퍼 클래스는 toString()도 오버라이딩되어 있어 객체가 가지고 있는 값을 문자열로 변환하여 반환

Number 클래스

  • 추상 클래스로 내부적으로 숫자를 멤버변수로 갖는 래퍼 클래스들의 조상
    • BigInteger: long으로도 다룰 수 없는 큰 범위의 정수를 처리하기 위한 것
    • BigDecimal: double로도 다룰 수 없는 큰 범위의 부동소수점수를 처리하기 위한 것

문자열을 숫자로 변환하기

  • 타입.parse타입(String s) 형식과 타입.valueOf() 메서드를 사용
    • parse는 반환값이 기본형이고 valueOf()는 반환값이 래퍼 클래스 타입
  • 오토박싱(autoboxing) 기능 때문에 반환값이 기본형일 때와 래퍼 클래스일 때의 차이가 없어짐

오토박싱 & 언박싱 (autoboxing & unboxing)

  • JDK 1.5 이전에는 기본형과 참조형 간의 연산이 불가능했기 때문에, 래퍼 클래스로 기본형을 객체로 만들어서 연산했어야 함
  • 하지만 이제는 기본형과 참조형 간의 덧셈이 가능
    • 이유: 컴파일러가 자동으로 변환하는 코드를 넣어주기 때문
  • 내부적으로 객체 배열을 가지고 있는 Vector 클래스나 ArrayList 클래스에 기본형 값을 저장해야할 때나 형변환이 필요할 때도 컴파일러가 자동적으로 코드를 추가해 줌

기본형 값을 래퍼 클래스의 객체로 자동변환해주는 것을 '오토박싱(autoboxing)', 반대로 변환하는 것을 '언박싱(unboxing)'이라 함

예시

  • 컴파일 전의 코드
Integer intg = (Integer) i;
Object obj = (Object) i;
Long lng = 100L;
  • 컴파일 후의 코드
Integer intg = Integer.valueOf(i);
Object obj = (Object) Integer.valueOf(i);
Long lng = new Long(100L);
profile
예비 백엔드 개발자

0개의 댓글