9. java.lang 패키지와 유용한 클래스

메밀·2022년 11월 8일
0

자바의 정석

목록 보기
4/6

1. java.lang 패키지

자바 프로그래밍에 가장 기본이 되는 클래스
import문 없이도 사용 가능

ex) String, System 클래스

1) Object 클래스

모든 클래스의 최고 조상이기 때문에 Object 클래스의 멤버들은 모든 클래스에서 바로 사용 가능.
멤버변수는 없고 11개의 메서드만 가지고 있다.

메서드설명
protected Object clone()객체 자신의 복사본 반환
public boolean equals(Object obj)객체 자신과 객체 obj가 같은 객체인지
protexted void finalize()객체가 소멸될 때 가비지 컬렉터에 의해 자동적으로 호출된다. (거의 사용 안함)
public Class getClass()객체 자신의 클래스 정보를 담고 있는 Class 인스턴스를 반환
public int hashCode()객체 자신의 해시코드를 반환
public String toString()객체 자신의 정보를 문자열로 반환
public void notify()쓰레드 하나를 깨운다
public void notifyAll()가능한 모든 쓰레드를 깨운다
public void wait()
public void wait(long timeout)
public void wate(long timeout, int nanos)
다른 쓰레드가 notify()나 notifyAll()을 호출할 때까지 현재 쓰레드를 무한히, 또는 지정된 시간 동안 기다리게 한다.

— equals(Object obj)

주소 값으로 비교. 값이 같아도 false

이때 v2 = v1 식으로 대입을 수행하면 참조변수 v2엔 v1이 참조하는 인스턴스의 주소가 저장되므로 결과가 true가 된다.

equals 메서드로 값을 비교하기 위해선 해당 클래스에서 equals 메소드를 오버라이딩하여 주소가 아닌 값을 비교하도록 하면 된다.

class Person{
    long id;

    public boolean equals(Object obj){
        if(obj instanceof Person){
            return id == ((Person)obj).id;
            // obj가 Object 타입이므로 id값을 참조하기 위해선 Person 타입으로 형변환이 필요하다
        } else {
            return false;
        }
    }

    Person(long id){
        this.id = id;
    }
}

String 클래스도 오버라이딩을 통해 String 인스턴스의 문자열 값을 비교하도록 되어 있다.
(Date, File, wrapper 클래스 등도 마찬가지 / StringBuffer 클래스는 오버라이딩 X)

— hashCode()

해싱: 데이터 관리 기법 중 하나로 다량의 데이터의 저장, 검색에 유용
해시함수는 찾고자 하는 값을 입력하면 그 값이 저장된 위치를 알려주는 해시코드를 반환
32bit JVM에서는 서로 다른 두 객체가 같은 해시코드를 가질 수 없었지만, 64bit JVM에선 중복 가능
HashMap, HashSet에선 hashCode() 오버라이딩 필수

String 클래스는 문자열 내용이 같으면 동일한 해시코드를 반환하도록 오버라이딩 되어 있음

System.identityHashCode(Object x) → 모든 객체에 대해 항상 다른 해시코드 값을 반환할 것을 보장

— toString()

인스턴스에 대한 정보를 문자열로 제공
따로 오버라이딩 하지 않는 경우 클래스이름@16진수 해시코드
String 클래스는 문자열 반환, Date 클래스는 날짜와 시간을 문자열로 반환하도록 오버라이딩
Object 클래스의 toString()이 public이므로 오버라이딩할 클래스의 접근제어자도 public
⇒ 조상의 메서드를 자손에서 오버라이딩할 땐 조상의 접근 범위보다 같거나 넓어야

— clone()

자신을 복제하여 새로운 인스턴스를 생성 → 원래 인스턴스를 보존하고 싶을 때

Object.clone()은 단순히 인스턴스 변수의 값만 복사하므로 참조타입 인스턴스 변수가 있는 클래스는 완전한 인스턴스 복제가 이루어지지 않는다.
ex) 배열의 경우 복제된 인스턴스도 같은 주소 → 원래의 인스턴스에 영향

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){}

        return obj;
    }
}

public class Example {
    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);
    }
}
  • Cloneable 인터페이스를 구현한 클래스에서만 clone() 호출 가능
  • 구현하지 않고 clone() 호출 시 예외 발생
  • Cloneable 인터페이스가 구현되어 있다는 건 클래스 작성자가 복제를 허용한다는 의미

— 공변 반환타입

오버라이딩할 때 조상 메서드의 반환타입을 자손 클래스의 타입으로 변경하는 것을 허용

public Point clone(){
        Object obj = null;
        
        try{
            obj = super.clone();
            // 예외처리 필수
        } catch(CloneNotSupportedException e){}

        return (Point)obj;
}

⭐️ 배열 복사

import java.util.Arrays;

public class Example {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        int[] arrClone = arr.clone();
        arrClone[0] = 6;

        int[] arr2 = {1, 2, 3, 4, 5};
        int[] arrCopy = new int[arr2.length];
        System.arraycopy(arr, 0, arrCopy, 0, arr.length);

        System.out.println(Arrays.toString(arrClone)); // [6, 2, 3, 4, 5]
        System.out.println(Arrays.toString(arrCopy)); // [1, 2, 3, 4, 5]
    }
}

배열도 객체이기 때문에 Object 클래스를 상속받으며 Cloneable 인터페이스와 Serializable 인터페이스가 구현되어 있다. Object 클래스에서는 protected로 정의되어 있는 clone()을 배열에선 public으로 오버라이딩 하였기 때문에 직접 호출이 가능하다.

배열 뿐 아니라 Vector, ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap, Calendar, Date도 이와 같은 방식으로 복제 가능

— 얕은 복사와 깊은 복사

clone()은 단순히 저장된 값을 복사, 참조하고 있는 객체까지 복제하진 않음
고로 객체배열을 clone()으로 복제하면 원본과 복제본이 같은 객체를 공유 (영향 받음)

깊은 복사: 원본이 참조하고 있는 객체까지 복제하는 것 → 영향 X

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;
    }

★ 부분으로 복제된 객체가 새로운 Point인스턴스를 참조하도록

— getClass()

자신이 속한 클래스의 class 객체를 반환

Class객체: 클래스의 모든 정보, 클래스로더에 의해 메모리에 올라갈 때 자동으로 생성

— Class 객체를 얻는 방법

참조를 얻는 방법

  • .getClass() // 생성된 객체로부터 얻음
  • .class // 클래스 리터럴로부터 얻기
  • Class.forName(”Card”) // 클래스 이름으로부터 얻기
  • cf) Card.class.newInstance(); // 클래스 객체를 이용해서 객체 생성
  • cf2) 리플렉션 API: 동적으로 객체를 생성하고 메서드를 호출하는 방법

2) String 클래스

— 변경 불가능한 클래스

‘+’ 연산자로 문자열을 결합하는 경우 인스턴스 내 문자열이 바뀌는 것이 아니라 새로운 인스턴스가 생성됨
문자열 간 결합, 추출 등의 작업이 많이 필요한 경우 StringBuffer 클래스를 사용하는 편이 좋다.

— 문자열의 비교
생략

— 문자열 리터럴

String 리터럴들은 컴파일 시 클래스파일에 저장됨.
클래스 파일에는 소스파일에 포함된 모든 리터럴의 목록이 있고, 이들은 상수풀에 저장된다.

— 빈 문자열

길이가 0인 char 배열

— String 클래스의 생성자와 메서드

메서드설명
String(String s)
String(char[] value)
주어진 문자열을 갖는 String 인스턴스 생성
String s = new String(”Hello”);
String(StringBuffer buf)StringBuffer 인스턴스가 갖고 있는 문자열과 같은 내용의 String 인스턴스를 생성
char charAt(int index)~
int compareTo(String str)문자열 str과 사전순서로 비교. 같으면 0, 사전순 이전이면 음수, 이후면 양수
String concat(String str)문자열을 뒤에 덧붙인다
boolean contains(CharSequence s)지정된 문자열이 포함되었는지 검사
boolean endsWith(String suffix)지정된 문자열로 끝나는지 검사
boolean equals(Object obj)
boolean equalsIgnoreCase(String str)
~
int indexOf(int ch)못 찾으면 -1
int indexOf(int ch, int pos)ch가 문자열 내에 존재하는지 pos 위치부터 확인하여 알려줌
int indexOf(String str)~
String intern()문자열을 상수풀에 등록
int lastIndexOf(int ch), (String str)~
int length()~
String replace(char old, char nw)
String replace(charSequence old, charSequence new)
old 문자를 nw 문자로
String replaceAll(String regex, String replacement)regex와 일치하는 문자열을 replacement로 바꿈
String replaceFirst(String regex, String replacement)첫 번째 것만 바꿈
String[] split(String regex), (String regex, int limit)분리자를 기준으로 나눔
limit: 문자열 전체를 지정된 수로 자름
boolean startsWith(String prefix)주어진 문자열로 시작하는지
String substring(int begin) (int begin, int end)begin ≤ x < end (끝 인덱스 포함X)
String toLowerCase()
String toString()저장된 문자열 반환
String toUpperCase()
String trim()
static String valueOf()parameter로 아무거나 지정된 값을 문자열로 변환하여 반환 참조변수의 경우 toString()을 호출한 결과를 반환

— join()과 StringJoiner

join(): 여러 문자열 사이에 구분자를 넣어서 결합 (split의 반대 작업)

String animals = "dog,cat,bear";
String[] arr = animals.split(",");
String str = String.join("-", arr); // dog-cat-bear
System.out.println(str);

StringJoiner sj = new StringJoiner(",", "[", "]");
String[] strArr = {"aaa", "bbb", "ccc"};

for(String s : strArr){
	sj.add(s.toLowerCase());
}

System.out.println(sj.toString()); // [aaa,bbb,ccc]

— String.format()

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

— 기본형과 String의 형변환

  • 빈 문자열 "" 더하기
  • String.valueOf()
  • wrapper 클래스의 parse 붙은 메서드들

3) StringBuffer 클래스와 StringBuilder 클래스

String은 변경 불가, StringBuffer는 가능
내부적으로 문자열 편집을 위한 버퍼를 가지고 있으며, StringBuffer 인스턴스를 생성할 때 크기 지정 가능

— StringBuffer의 생성자

StringBuffer 클래스의 인스턴스를 생성할 때 char[]가 생성되고, 이 곳이 버퍼
생성 시 크기를 지정하지 않으면 16개 문자 길이의 버퍼 생성

— StringBuffer의 변경

String과 달리 StringBuffer는 내용 변경 가능

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

StringBuffer sb2 = sb.append("ZZ");

System.out.println(sb); // abc123ZZ
System.out.println(sb2); // abc123ZZ

—StringBuffer의 비교

String클래스에서는 equals 메서드를 오버라이딩해서 문자열의 내용을 비교하도록 구현, StringBuffer 클래스는 equals 메서드를 써도 ==로 비교한 것과 같은 결과 (오버라이딩X)

toString(): 오버라이딩되어 있음. String으로 반환

고로 StringBuffer 인스턴스의 문자열을 비교하려면 toString()으로 String 인스턴스를 얻은 다음, 여기에 equals()를 써야한다.

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

메서드설명
StringBuffer()16문자를 담을 수 있는 버퍼를 가진 StringBuffer 인스턴스 생성
StringBuffer(int length)length 만큼의 버퍼를 가진
StringBuffer(String str)지정된 문자열 값을 갖는
append(각종 타입)매개변수로 받은 값을 문자열로 변환하여 StringBuffer의 문자열 뒤에 붙임
int capacity()버퍼 크기
int length()버퍼에 담긴 문자열의 길이
char charAt(int index)지정된 인덱스의 문자 반환
StringBuffer delete(int start, int end)start ≤ x < end 까지의 문자 제거
StringBuffer deleteCharAt(int index)지정된 인덱스의 문자 제거
StringBuffer insert(int pos, 각종 타입)두 번째 매개변수로 받은 값을 문자열로 변환하여 pos 위치에 추가
StringBuffer replace(int start, int end, String str)start ~ end의 문자들을 주어진 문자열로 바꾼다 (start ≤ x < end)
StringBuffer reverse()거꾸로 나열
void setCharAt(int index, char ch)지정된 위치의 문자를 주어진 문자로 바꾼다
vodi setLength(int newLength)지정된 길이로 문자열의 길이를 변경, 나머지 공간은 널문자 ’\u0000’로 채움
String toString()StringBuffer 인스턴스의 문자열을 String으로 변환
String substring(int start),
(int start, int end)
지정된 범위 내의 문자열을 String으로 뽑아서 반환

— StringBuilder

  • 멀티쓰레드에 안전하도록 동기화
  • 동기화는 StringBuffer의 성능을 떨어뜨리기 때문에, 멀티쓰레드로 작성된 프로그램이 아닌 경우 StringBuffer의 동기화는 불필요한 성능저하를 야기
  • StringBuilder: StringBuffer에서 쓰레드의 동기화만 뺌
  • StringBuffer도 충분히 성능이 좋기 때문에 성능향상이 꼭 필요한 경우를 제외하고는 굳이 바꿀 필요 X

4) Math 클래스

— 올림, 버림, 반올림

Math.round() → return int
Math.ceil() → 올림
Math.floor() → 버림

Math.rint()

— 예외를 발생시키는 메서드

이름에 Exact가 포함된 메서드 → 오버플로우 감지

— 삼각함수와 지수, 로그

Math.sqrt() → 제곱근
Math.pow() → 제곱

— StrictMath 클래스

JVM이 설치된 OS의 메서드를 호출해서 사용. OS 의존적 계산.

— Math클래스의 메서드

  • abs() → 절대값 반환
  • ceil(), floor(), round() → 올림, 내림, 반올림
  • max() → 두 값 중 큰 쪽 반환
  • min()
  • random()
  • rint() → 가장 가까운 정수값을 double 타입으로 반환 — 정 가운데 값(1.5, 2.5)은 짝수 반환

cf) 매개변수의 값이 음수인 경우 rount()와 rint()의 값이 다름

— 래퍼(wrapper) 클래스

문자열 → 기본형문자열 → 래퍼 클래스

Integer.parseInt(100);
Integer.valueOf(100);

— 오토박싱 & 언박싱

기본형과 참조형 연산에서 컴파일러가 자동으로 변환하는 코드를 넣어줌

int i = 5;
Integer iObj = new Integer(7);
int sum = i + iObj;

// 컴파일 후의 코드: int sum = i + iObj.intValue();

이외에도 내부적으로 객체 배열을 가지고 있는 ArrayList 클래스 등에 기본형 값을 저장해야할 때나 형변환이 필요할 때도 컴파일러가 자동적으로 코드를 추가해준다.

오토박싱: 기본형 값 → 래퍼 클래스
언박싱: 래퍼 클래스 → 기본형 값

2. 유용한 클래스

1) java.util.Objects

Object 클래스의 보조클래스
모든 메서드가 static
null 체크에 유용

메서드설명
isNull(Object obj)
nonNull(Object obj)
requireNonNull()매개변수의 유효성 검사에 유용
compare()비교대상이 같으면 0, 크면 양수, 작으면 음수
equals()Object클래스의 equals()와 달리 null 체크를 하지 않아도 된다.
내부에서 null 체크를 해줌
둘 모두 null일 경우 true 반환
객체를 재귀적으로 비교하기 때문에 다차원 배열의 비교도 가능
String toString(Object o, String nullDefault)널일 때 사용할 값을 지정할 수 있어서 유용
hashCode()역시 내부적으로 널체크를 한 다음 Object 클래스의 hashCode()를 호출할 뿐
단, 널일 땐 0을 반환

보통은 클래스에 선언된 인스턴스 변수들의 hashCode()를 조합해서 반환하도록 hashCode()를 오버라이딩하는 편인데, 그 대신 매개변수의 타입이 가변인자인 int hash(Object …args)를 사용하면 편리하다.

  • Objects.requireNonNull()
void setName(String name){
	this.name = **Objects.requireNonNull**(name, "name must not be null.");
}
  • Objects.equals
if(a != null && a.equals(b)){ }
if(Objects.equals(a, b)){ }
deepEquals()

2) java.util.Random 클래스

난수 얻기.

종자값을 System.currentTimeMillis()로 하기 때문에 실행할 때마다 얻는 난수가 달라진다.

3) 정규식 — java.util.Regex 패키지

import java.util.regex.*; // Pattern과 Matcher가 속한 패키지

class Lab{
    public static void main(String[] args) {
        String[] data = {"bat", "baby", "bonus", "cA", "ca", "co", "c.", "c0", "car", "combat", "count", "date", "disc"};
        Pattern p = Pattern.compile("c[a-z]*"); // c로 시작하는 소문자 영단어

        for(int i = 0; i < data.length; i++){
            Matcher m = p.matcher(data[i]);
            if(m.matches())
                System.out.print(data[i] + ", ");
        }
    }
}

Pattern은 정규식을 정의
Matcher는 정규식(패턴)을 데이터와 비교하는 역할
단계별 설명

정규식을 매개변수로 Pattern 클래스의 static 메서드인 Pattern.compile(String regex)을 호출하여 Pattern 인스턴스를 얻는다.
정규식으로 비교할 대상을 매개변수로 Pattern 클래스의 Matcher matcher(CharSequence input)을 호출하여 Matcher 인스턴스를 얻는다.
Matcher 인스턴스에 boolean matcher()를 호출해서 정규식에 부합하는지 확인한다.
패턴들

regexexplanation
c[a-z]*c로 시작하는 영단어
c[a-z]c로 시작하는 두 자리 영단어
c[a-zA-z]c로 시작하는 두 자리 영단어, 대소문자 구별 안 함
c[a-zA-z0-9]c\wc로 시작하고 숫자와 영어로 조합된 두 글자
.*모든 문자열
c.c로 시작하는 두 자리 문자열
c.c.과 일치하는 문자열은 패턴 작성에 사용되는 문자이므로 이스케이프 문자인 \를 사용해야한다.
c\d c[0-9]c와 숫자로 구성된 두 자리 문자열
c.*tc로 시작하고 t로 끝나는 모든 문자열
[b|c].
[bc].

[b-c]
b 또는 c로 시작하는 문자열
[^b|c].
[^bc].

[^b-c]
b 또는 c로 시작하지 않는 문자열
.a.a를 포함하는 모든 문자열 → *: 0 또는 그 이상의 문자
.a.+a를 포함하는 모든 문자열 → +: 1 또는 그 이상의 문자, ‘+’는 ‘’과 달리 반드시 하나 이상의 문자가 있어야 하므로 a로 끝나는 단어는 포함되지 않는다. 즉 .a.는 ca를 포함하지만, .*a.+는 ca를 포함 안 함
[b|c].{2}b 또는 c로 시작하는 세 자리 문자열, bat, cat 같은 거

— 정규식 그룹화

import java.util.regex.*;

class Lab{
    public static void main(String[] args) {
        String source = "HP:011-1111-1111, HOME:02-999-9999";
        String pattern = "(0\\\\d{1,2})-(\\\\d{3,4})-(\\\\d{4})";

        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(source);

        int i = 0;
        while(m.find()){
				// find: 일치하면 true, 불일치하면 false
            System.out.println(++i + " : " + m.group() + " -> " + m.group(1) + ", " + m.group(2) + ", " + m.group(3));
        }
    }
}

(0\d{1,2}) — 0으로 시작하는 2, 3자리 숫자
(\d{3,4}) — 3, 4자리 숫자

4) Scanner

생략

5) java.util.StringTokenizer

  • 긴 문자열을 지정된 구분자를 기준으로 토큰이라는 여러 개의 문자열로 잘라내는 데 사용
  • String의 split이나 Scanner의 userDelimiter 등을 사용할 수도 있지만 StringTokenizer가 더 유용
  • 그러나 StringTokenizer는 구분자로 단 하나의 문자밖에 사용하지 못하기 때문에 보다 복잡한 형태의 구분자로 문자열을 나눠야 할땐 정규식을 써야함

— 생성자와 메서드

메서드설명
StringTokenizer (String str, String delim)
StringTokenizer(String str, String delim, boolean returnDelims)
returnDelims를 true로 하면 구분자도 토큰으로 간주된다
int countTokens()전체 토큰의 수를 반환
boolean hasNextToken()토큰이 남아있는지 알려준다

String nextToken()| 다음 토큰 반환

String expression = "x=100*(200+300)/2";
StringTokenizer st = new StringTokenizer(expression, "+-*/=()", true);

while(st.hasMoreTokens()){
	sout(st.nextToken());
}

/* 출력 결과
x
=
100
(
200
300
)
/
2
*/

단 한 문자의 구분자만 사용할 수 있기 때문에 +-*/=() 전체가 아니라 각각이 모두 구분자

cf) split()은 데이터를 토큰으로 잘라낸 결과를 배열에 담아 반환하기 때문에 데이터를 토큰으로 바로바로 잘라서 반환하는 StringTokenizer보다 성능이 떨어질 수밖에 없다. 그러나 데이터의 양이 많은 경우가 아니라면 별 문제가 되지 않으므로 크게 신경 쓸 부분은 아니다.

0개의 댓글