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

·2022년 12월 27일
1

자바의 정석

목록 보기
12/12
post-thumbnail

1권 졸업하자,,,..... 하지만 2권이 남아잇다..

java.lang 패키지

import문 없이 사용하는 클래스들

1. Object 클래스

모든 클래스들의 최고 조상

protected 제어자들은 오버라이딩으로 public 변경 후 사용해주자.

Object클래스의 메서드설 명
protected Object clone()객체 자신의 복사본을 반환한다.
protected void finalize()객체 소멸 시 가비지 컬렉터에 의해 자동호출. 이 때 수행되어야하는 코드가 있을 때 오버라이딩(거의 사용 안함)
public boolean equals(Object obj)객체 자신과 객체 obj가 같으면 true, 다르면 false
public Class getClass()객체 자신의 클래스 정보(설계도 정보)를 담고 있는 Class인스턴스를 반환한다.(class 아니고 Class임)
public int hashCode()객체 자신의 해시코드 반환
public String toString()객체 자신의 정보를 문자열로 반환한다.

이 외에 notify, wait 등의 메서드도 있는데 내가 학습이 부족합니다 미안해요
여튼 Object 클래스는 멤버변수 없이 총 11개의 메서드만 갖고 있다 다음 챕터에서 하나씩 살펴보자


equals(Object obj)

Object 클래스의 equals 메서드는 주소값을 갖고 비교를 한다.

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

요렇게 비교연산자를 갖고 결과값을 뱉기 때문에, 객체의 주소값으로 비교를 한다.

우리가 주로 봤던건 내용만 갖고 비교했는데?!?!?
이건 오버라이딩을 통해 변경한 equals다!

아래의 다양한 메서드들도 오버라이딩을 해서 사용하는데, 원래처럼 내용을 생각하면서, 원본 Object에 있는 메서드들은 이렇게 생겼었구나~ 하거 머리 한구석에 알아만 두자.

그럼 두 개의 예시를 보며 우리가 원래 알고있던대로 내용만 갖고 비교하는equals를 만들어보자.

class EqualsEx1 {
	public static void main(String[] args) {
    	Value v1 = new Value(10);
        Value v2 = new Value(10); // 서로 다른 객체 인스턴스. 주소 당연히 다름.
        
        if (v1.equals(v2)){ // v1이랑 v2 참조변수랑 참조변수 비교니까 둘이 주소값 같음? 이라고 묻게 됨
        	System.out.println("와 둘이 같다");
        } else
        	System.out.println("다름ㅋㅋ");
        
        v2 = v1; // v2에 v1의 주소값을 넣어준다.
        if (v1.equals(v2)){
        	System.out.println("와 둘이 같다");
        } else
        	System.out.println("다름ㅋㅋ");
    }
}

class Value {
	int value;
    
    Value(int value) {
    	this.value = value;
    }
}

숭이니까 그림판 켜라

package test.문자열;

public class EqualsEx2 {
  public static void main(String[] args) {
    Person person1 = new Person(1234567891011L);
    Person person2 = new Person(1234567891011L);

    if (person1.equals(person2)) {
      System.out.println("p1과 p2는 같은 사람입니다.");
    } else {
      System.out.println("p1과 p2는 다른 사람입니다.");
    }
  }
}

class Person {
  long id;

  @Override
  public boolean equals(Object obj){
    if (obj instanceof Person)
      return id == ((Person)obj).id;
    else
      return false;
  }

  /*
  * public boolean equals(Object obj) {
  *   if (!(obj instanceof Person)) return false;
  *
  *   Person p = (Person)Object;
  *   return this.id == p.id;
  * }
  *
  * */

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

equals는 그러시군아...


hashCode()

객체의 해시코드(정수값)을 반환하는 메서드.
Object 클래스의 hashCode()는 객체의 주소를 int로 변환해서 반환한다.

객체의 주소값으로 해시코드를 만들어 반환하기 때문에, 32bit 기준 JVM에서는 (int)4byte씩 만들어 서로 다른 두 객체는 같은 해시코드를 가질 수 없었지만, 64bit JVM에서는 기존의 두배인 8byte 주소값으로 해시코드(4byte)를 만들어 해시코드 중복 가능성이 있다.

여기까지 읽었을 땐 hashCode()의 목적이 해시코드를 통해 객체의 유일성 판별인가? 싶었는데 오버라이딩된 예시들을 보니 equals처럼 인스턴스의 iv로 비교해 같은지 다른지를 판별하는듯..

오버라이딩 후, equals() 결과가 true -> 두 객체의 hashCode는 같아야한다.

class HashCodeEx1{
	public static void main(String[] args) {
    	String str1 = new String("abc");
        String str2 = new String("abc");
        
        
        System.out.println(str1.equals(str2)); // 얘가 true면
        System.out.println(str1.hashCode());
        System.out.println(str2.hashCode()); // 위의 두 해시코드 같아야해
        System.out.println(System.identityHashCode(str1));
        System.out.println(System.identityHashCode(str2)); // 얘넨 오버라이딩 이전의 원조 hashCode(), 객체마다 다른 hashCode를 반환한다.
    }
}

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


toString()

객체의 정보를 문자열로 반환해보자.

원조는 이러하다...
그래서 오버라이딩 안하고 호출하면 클래스이름에 16진수 해시코드를 얻게 되어벌임
이건 뭐.. 오버라이딩 할 때 문자열로 반환 못하는 흑우 없제 해산


clone()

객체에 저장된 값을 그대로 복제.
객체 배열을 다룰 땐 같은 객체를 가리키게 되므로 오버라이딩이 필요하다.

Cloneable인터페이스를 구현해야 clone을 호출할 수 있다.

Object 클래스의 clone 메서드로 객체를 복제 시, 같은 배열의 주소를 갖게 되어 복제된 인스턴스의 작업이 원래의 인스턴스에도 영향을 미치게 된다. 이건 우리가 원하지 않는거라,,, 적절히 오버라이딩 해보자.

class Point implements Clonable { // 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() { // 다른 class에서 호출하려고 protected -> public 변경
    	Object obj = null;
        
        try {
        	obj = super.clone(); // 조상꺼 clone 호출
        } catch(CloneNotSupportedException e) {
        	return obj;
        }
    }
}


class CloneEx1 {
	public static void main(String[] args) {
    	Point original = new Point(3, 5);
        Point copy = (Point)original.clone(); // 새로운 객체를 생성, type 맞추려고 Object(조상 타입)로 반환되는거 형변환
        System.out.println(original);
        System.out.println(copy);
        
        int[] arr = {1, 2, 3, 4, 5};
    	int[] arrClone = arr.clone();

    	System.out.println(Arrays.toString(arr));
    	System.out.println(Arrays.toString(arrClone)); // 다른 배열도 잘 복사돼염 이건 Arrays clone, toString은 이미 오버라이딩 되어있음.
        //Arrays.hashCode(arr)로 arrClone과 비교해보면, 같은 객체임(얕은 복사)을 알 수 있다.
    }
}

질문 : 오버라이딩되지않은 equals로 찍어보면 false인데.. 왜지

공변 반환타입

오버라이딩 시, 조상 메서드의 반환타입을 자손클래스의 타입으로 변경 허용

모르면 걍 외우셈 형변환은 인스턴스에 아무런 영향을 미치지 않는다 벅벅벅

이전에는 오버라이딩 시, 조상에 선언된 메서드 반환타입을 그대로 사용했었는데 여기선 반환 시 자손의 타입으로 변경할 수 있다는 것을 배운당

//이전의 코드
public Object clone() { // 조상 반환타입 그대루
    Object obj = null;
        
    try {
        obj = super.clone();
    } catch(CloneNotSupportedException e) {
        return obj;
    }
}


// 공.. 공 머? 공변 반환타입 북북
public Point clone() { // 자손의 반환타입으로 바꼈다~!!
	Object obj = null;
    
    try {
    	obj = super.clone();
    } catch(CloneNotSupportedException e) {
    	return (Point) obj; // 반환 시에도 Object -> Point 형변환
    }
}

Point copy = (Point)original.clone();

Point copy = original.clone();

이렇게 오버라이딩된 clone 메서드는 배열, Vector, ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap, Calendar, Date와 같은 클래스들도 복사 가능

ArrayList list = new ArrayList();

ArrayList list2 = (ArrayList)list.clone();

얕은 복사, 깊은 복사

얕은 복사 : 원본과 복제본이 같은 객체를 공유, 원본, 복제본의 변경이 서로 영향을 미치게 된다. 객체 배열을 clone()으로 복제하는 경우.
깊은 복사 : 원본과 복제본이 서로 다른 객체를 참조. 원본이 참조하고 있는 객체까지 복제를 한다.

class Circle implements Clonable {
	Point p;
    double r;
    
    Circle(Point p, double r) {
    	this.p = p;
        this.r = r;
    }
    
    public Circle clone() {
    	Object obj = null;
        
        try {
        	obj = super.clone();
        } catch(CloneNotSupportedException e) {}
        return (Circle)obj;
    }
}

class Test {
	public static void main(String[] args) {
    	Circle c1 = new Circle(new Point(1, 1), 2.0);
        Circle c2 = c1.clone(); // 얇은 복사
    }
}

같은 객체를 가리키게 하지 말고, 새로운 객체를 만든 후 복사해서 넣어주자.

class Circle implements Clonable {
	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;
    }
    public String toString() {
    	return "[p=" + p + ", r=" + r + "]";
    }
}

class Point {
	int x, y;
    
    Point(int x, int y) {
    	this.x = x;
        this.y = y;
    }
    public String toString() {
    	return "(" + x + ", " + y + ")";
    }
}

class ShallowDeepCopy {
	public static void main(String[] args){
      Circle c1 = new Circle(new Point3(1, 1), 2.0);
      Circle c2 = c1.shallowCopy();
      Circle c3 = c2.deepCopy();

      System.out.println("c1=" + c1);
      System.out.println("c2=" + c2);
      System.out.println("c3=" + c3);

      c1.p.x = 9;
      c1.p.y = 100;
      System.out.println("c1 변경 이후");
      System.out.println("c1 = " + c1);
      System.out.println("c2 = " + c2);
      System.out.println("c3 = " + c3);
    }
}

c1을 만들고, c2는 c1의 얕은 복사, c3은 c2의 깊은 복사를 통해 만들어냄. 깊은 복사는 원본이 참조하고있는 객체까지 복사한것임을 알 수 있다.


2. String클래스

데이터(char[] 문자배열) + 메서드(문자열 관련)

불변 클래스

한번 생성된 String인스턴스가 갖고 있는 문자열은 읽어 올 수만 있고, 변경할 수는 없다.
만약 결합, 추출 등 문자열을 다루는 작업이 많이 필요하다면, StringBuffer, StringBuilder 클래스를 이용하도록 하자.

문자열의 비교

일단 문자열 어케만듬?

  1. 리터럴 지정
String str1 = "abc";
String str2 = "abc";
  1. String클래스의 생성자 사용
String str3 = new String("abc");
String str4 = new String("abc");

이렇게 봤듯이 리터럴로 비교하면 같은 객체를 공유하게 되어 주소값이 같고, String 클래스를 이용하면 다른 새로운 객체를 생성하게되는거니까 주소값이 다르다. 조금 더 살펴보자.

문자열 리터럴

컴파일 시 클래스 파일에 저장된다. 한번 생성하면 내용 변경이 불가능하니 한번만 저장되고, 상수 저장소(Constant pool)에 자동으로 생성되어 저장된다.

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

sry 나 이거 하나하나 적고잇엇으면 할배됏어요

join()과 StringJoiner

여러 문자열 사이에 구분자 넣어서 한 문자열로 만들자!!!

반대로 한 문자열을 구분자로 잘라서 여러 문자열로 만들고 싶다면, split() 쓰셈
미안합니다 StringJoiner도 있는데 전 그냥 join 쓸게요 사용법만 보세요

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

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

[AAA,BBB,CCC] 요래 나옴

머.. 비슷하게.. String.format() 얘도 printf랑 사용법이 같대요 글쿤


기본형 값을 String으로 변환

  1. 덧셈연산자(+) 문자열("")
  2. valueOf()
int i = 100;
String str1 = i + "";
String str2 = String.valueOf(i);

둘다 "100"

String을 기본형 값으로 변환

반대로 해보자

  1. parseInt()
  2. valueOf()
int i = Integer.parseInt("100");
int i2 = Integer.valueOf("100");

둘 다 정수 100

엥 parseInt 하면 Integer로 반환되지 안나요 내내 맞워요 근데 오토박싱이라는 개짱기능이 있어서 int로 자동변환도 대새요

그래서 정리하자면

너무큰데 걍 보세요 돋보기 벗어라

걍 저는 둘 다 valueOf 쓸게요 내내내


3. StringBuffer클래스 & StringBuilder클래스

문자열을 저장하고, 다뤄보자!!! 불변 개박살이용 ㅋㅋ

String클래스는 불변인데, 위의 두 클래스는 가변이다. 얘네 다 문자 배열(char[])을 갖고 다룸

StringBuffer의 생성자

StringBuffer의 버퍼의 크기를 지정하지 않으면, 버퍼의 크기는 16이 된다(16이 큰 의미는 없슴)

만약에 StringBuffer를 사용해 문자열을 다루고 있는데, ㅈ댓다 길이 산정을 잘못해서 길이가 부족하다면??
얘는 개짱천재라 내부적으로 버퍼 크기를 증가시키는 작업이 수행된다. 원래 우리는 울면서 길이 늘린 배열 새로 만들고, 이전 값 복사하고, 참조를 변경하고 햇는데 얜 개짱천재라 해주나보다..

StringBuffer의 변경

append() 이용하믄 된다.

append()는 문자열 마지막에 value를 추가한 후에 반환타입이 StringBuffer라서, 자신의 주소를 반환한다.

StringBuffer sb = new StringBuffer("abc"); //abc를 담은 sb

sb.append("123"); // "abc123"댓슴 sb
StringBuffer sb2 = sb.append("ZZ"); // sb2는 "abc123ZZ", sb도 "abc123ZZ"

sb2 = sb.append("ZZ")에서 반환된건 sb의 주소값이라 같은 객체를 공유하게 된다.

그런고로, 계속 append()해주면 되겠다 개짱~

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

StringBuffer의 비교

equals가 오버라이딩되어있지 않다. 값 비교가 아닌 주소 비교를 하니 오버라이딩 되어있는 클래스인 String으로 변환한 후, equals를 사용해주도록 하자.

  1. toString() 호출해서 String으로 바꿔
  2. equals 써서 내용만 갖고 비교해
StringBuffer sb = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");

String s = sb.toString();
String s2 = sb2.toString(); // 이제 둘다 StringBuffer -> String 댓슴

System.out.println(s.equals(s2)); // 내용만 갖고 비교하니까true

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

sb.append(24) 하면 문자('2', '4')로 바꿔서 String에 붙인다고 생각하면 된다. 내부적으로 도는건 char[]가 돌고있단걸 잊지 말라구~!

StringBuilder는 StringBuffer랑 90퍼 유사한 기능을 하는데, 멀티 쓰레드에서만 다르다고함.. 얜 동기화 처리가 안돼있대

4. Math클래스

올림, 버림, 반올림

  1. 올림 : ceil
  2. 버림 : floor
  3. 반올림 : round

얘네 다 정수로 반환해요 울어~

떠올릴 때마다 엄마보고싶던 Math.round에 자리수곱하기 시간인가?
엄마를 떠올리며 복습해보자.
Math.round()는 소수점 첫째자리에서 반올림 해 정수값(long)을 반환한다. 소수점 n번째 자리에서 반올림을 하고싶다면 어덯게 해야할까? 갓궁님의 예시처럼 소수 두번 째 자리에서 반올림해보자.

  1. 원래 값에 원하는 자리수만큼 10^n을 곱해주자.
    • 90.7552 * 10^2 = 9075.52
  2. 위의 결과에 Math.round() 빔을 쏴준다.
    • Math.round(9075.52); -> 9076 (첫째 자리에서 반올림한 정수 long)
  3. 다시 10^n.0 으로 나눠주자. 100으로 나누면 그대로 정수로 나옴요
    • 9076 / 100.0 = 90.76

+) 아아 싫어싫어~~ 형식화 배우고싶어~ 라고 생각하며 운다

rint도 반올림을 해주는데, 얜 짝수를 향해갈 땐 올림, 홀수일 땐 내림임 이거만으로도 골때리는데 double로 값을 반환하고, 평균을 낼 땐 정확도가 더 높다; 데박적임 그냥

5. 래퍼(wrapper) 클래스

8개의 기본형을 객체로 다루어야 할 때 사용하는 클래스.

아아 안심하세요 이름은 걍 전체 스펠링 쓰면서 첫글자를 대문자로 바꾸면 됩니다

문자열을 숫자로 변환하기

엥 이거 위에서 한거 아니냐 데자뷰 아니냐 엥 이거 위에서
맞습니다 좀 더 자세히 보래요 추가적인거도 보셈

맞습니다 저는 valueOf()로 대충 다 퉁치고 싶은데요, 얘는 내부에서 parseInt()를 호출함.. 그럼 왜 따로 쓰냐고
둘의 차이는 문자열 변환 시, 객체로 받아서 사용하는지(오토박싱 전의 valueOf) 기본 자료형으로 받아서 사용하는지(parseInt) 차이인거같음 글고 당연히 내부에서 호출당하는 parseInt()를 바로 쓰는게 성능상은 더 빠르겟죠..

글고 추가적으로 문자열이 10진수가 아닌 다른 진법의 숫자일 때도, 변환이 가능합니당

static int parseInt(String s, int radix)
static int valueOf(String s, int radix)

아아 이미 valueOf 쓸 생각밖에 없음 미쳣다 절대 오토박싱해

오토박싱 & 언박싱

킷타~~

컴파일의 능지상승으로 기본형과 참조형 간의 형변환, 참조형 간의 연산이 가능해졌다.

6. 정규식

텍스트 데이터 중, 원하는 조건과 일치하는 문자열을 찾아 사용해보자.

  • Pattern : 정규식을 정의
  • Matcher : 정규식(패턴)을 데이터와 비교하는 역할

사실 귀찮아서 그때그때 찾아보려구요 죄송합니다 선생님 하하

7. StringTokenizer클래스

긴 문자열을 지정된 구분자 기준으로 token이라는 여러 개의 문자열로 잘라내보자.

엥? split이랑 뭔차이임?

  • split은 정규식 쓰고요 얜 하나의 문자만 구분자로 쓸 수 있습니다 감사합니다

만약에 구분자에 "+@$(_!우하하" 라고 넣으면 문자 하나하나가 모두 구분자임!!

여러개 하고싶으면요? -> split 쓰시거나 이중for문 도세용
언럭키 split 아님? 왜씀? -> 간편하고 split은 결과를 배열에 담아 반환하잔아ㅡㅡ 토크나이저 기죽이지마요 성능 조음

profile
어?머지?

1개의 댓글

comment-user-thumbnail
2022년 12월 29일

너무 멋진 글입니다... 감동받고 갑니다.

답글 달기