Java; Class

🗓️

Object class

java.lang 패키지

  • java.lang.*은 컴파일 할때 자동으로 추가된다.

모든 클래스의 최상위 클래스 Object

  • Object클래스는 모든 자바 클래스의 최상위 클래스다.
  • 모든 클래스는 Object 클래스로부터 상속을 받는다.
  • 클래스를 만들때 extends Object를 하지 않지만, 컴파일 과정에서 자동으로 들어간다.
  • 모든 클래스가 Object 클래스를 상속 받았으므로 Object의 메소드를 사용할 수 있고 재정의 할 수도 있다

주로 사용되는 Object클래스의 메소드

  • String toString() : 객채를 문자열로 표현하여 반환한다. 재정의하여 객체에 대한 설명이나 특정 맴버변수 값을 반환한다.
  • boolean equals(Object obj) : 두 인스턴스가 동일한지 여부를 반환한다. 재정의하여 논리적으로 동일한 인스턴스임을 정의한다.
  • int hashCode() : 객체의 해시 코드 값을 반환한다.
  • Object clone() : 객체를 복제하여 동일한 멤버 변수를 가진 새로운 인스턴스를 생성한다.
  • Class getClass() : 객체의 Class 클래스를 반환한다.
  • void finalize() : 인스턴스가 힙 메모리에서 제거될 떄 GC에 의해 호출되는 메소드. 네트웍 연결 해제, 열려있는 파일 스트림 해제 등을 구현한다.
  • void wait() : 멀티스레드 프로그램에서 사용한다. 스레드를 non runnable로 만든다.
  • void notify() : wait() 메소드에 의해 no runnable를 runnable로 가져온다.

toString() 메소드

  • toString() 메소드는 인스턴스의 정보를 문자열로 반환하는 메소드다. toString()의 원형은 인스턴스의 클래스 이름과 주소값을 보여준다.
  • toString()의 원형
getClass().getName() + '@' + Integer.toHexString(hashCode())
  • String과 Integer의 경우는 toString()을 호출해도 원형대로 반환하지 않는다. 이미 오버라이딩 되어있기 때문이다.

equals() 메소드

  • 두 인스턴스의 주소값을 비교하여 boolean값을 반환해준다.
  • 주소값이 같다면 당연히 같은 인스턴스다.
  • 하지만 다른 주소값을 가지더라도 논리적으로 같은 인스턴스라고 판단해야 할 때가 있다.
  • equals()의 재정의
class Student {

    int studentId;
    String studentName;

    public Student(int studentId, String studentName) {
        this.studentId = studentId;
        this.studentName = studentName;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Student) {
            Student std = (Student) obj;
            if (this.studentId == std.studentId) {
                return true;
            } else {
                return false;
            }
        }
        return false;
    }

    public String toString() {
        return studentId + ", " + studentName;
    }
}

public class EqualsTest {

    public static void main(String[] args) {
        Student student1 = new Student(100, "KANG");
        Student student2 = new Student(100, "KANG");
        System.out.println(student1 == student2);
        System.out.println(student1.equals(student2));
    }
}

hashCode() 메소드

  • Hash는 정보를 저장하거나 검색할때 사용하는 자료구조다. 정보를 어디에 저장할 것인지, 어디서 가져올 것인지 해시 함수를 사용해 구현한다. 해시 함수는 객체의 특정정보를 매개변수 값으로 넣으면 그 객체가 저장되어야 할 위치저장된 해시 테이블 주소를 반환한다.
  • 자바에서는 인스턴스를 힙 메모리에 생성하여 관리할 때 해시 알고리즘을 사용한다.
hashCode = hash(key);
  • hashCode로 출력되는 15진수 숫자값은 JVM이 힙 메모리에 저장한 인스턴스의 주소값이다.
  • 두 인스턴스가 같다면 hashCode()의 반환값도 같아야한다.
  • 논리적으로 같은 두 객체를 위해 hashCode()를 오버라이딩 할 필요가 있다.

clone() 메소드

  • 객체 원본을 유지해놓고 복사본을 사용하거나, 기본 프로토타입의 복사본을 사용해 동일한 인스턴스를 만들어 복잡한 생성과정을 간단하게 할때 사용한다.
  • 객체를 복제해 또 다른 객체를 반환해 준다.
  • clone()으로 인스턴스를 복제하는 예제
class Point {

    int x;
    int y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public String toString() {
        return "x = " + x + ", y = " + y;
    }
}

class Circle implements Cloneable {

    Point point;
    int radius;

    Circle(int x, int y, int radius) {
        this.radius = radius;
        point = new Point(x, y);
    }

    @Override
    public String toString() {
        return "Circle{" +
            "point=" + point +
            ", radius=" + radius +
            '}';
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class CloneTest {

    public static void main(String[] args) throws CloneNotSupportedException {
        Circle circle = new Circle(10, 20, 30);
        Circle copyCircle = (Circle) circle.clone();

        System.out.println(circle);
        System.out.println(copyCircle);
        System.out.println(System.identityHashCode(circle));
        System.out.println(System.identityHashCode(copyCircle));
    }
}
  • clone() 메소드를 사용하려면 객체를 복제해도 된다는 의미로 Cloneable 인터페이스를 구현해야한다.
  • Object의 clone()은 클래스의 인스턴스를 새로 복제하여 생성해준다. 맴버젼수가 동일한 인스턴스가 다른 메모리에 새로 생성되는 것이다.

String class

String str1 = new String("test");
String str2 = "test";
String str3 = "test";
4b56130b13abea82397aaf60ceee5aea.png
  • new String으로 생성된 문자열은 새로운 메모리가 할당되고 인스턴스 된다.
  • 상수를 바로 가리키는 경우에는 상수 풀을 참조하게 된다.

StringBuffer와 StringBuilder 활용

  • StringBuffer와 StringBuilder는 내부에 변경 가능한 char[]를 변수로 가지고 있다.
  • 문자열을 연결하면 기존에 사용하던 char[]배열이 확장되 메모리를 낭비하지 않는다.
  • 두 클래스의 큰 차이는 아래와 같다
    • StringBuffer : 문자열이 안전하게 변경되도록 멀티스레드에서 보장된다.
    • StringBuilder : 멀티스레드에서 작업이 보장되지 않지만 실행속도가 빠르다.
public class StringBuilderTest {

    public static void main(String[] args) {
        String javaStr = new String("Java");
        System.out.println(System.identityHashCode(javaStr));

        StringBuilder buffer = new StringBuilder(javaStr);
        System.out.println(System.identityHashCode(buffer));

        buffer.append(" jammy");
        buffer.append(" it-da!");
        System.out.println(System.identityHashCode(buffer));

        javaStr = buffer.toString();
        System.out.println(javaStr);
        System.out.println(System.identityHashCode(javaStr));
    }

}

String.intern()

  • String.intern()
  • String에 intern() 이라는 메서드가 있는데, 변수 문자열값에 대해 상수풀을 뒤져서 상수풀에 존재하면 그걸 반환함

Wrapper class

  • 기본자료형을 클래스로 씌워놓은것을 wrapper class라고 한다.

기본 자료형을 위한 클래스

  • primitive을 클래스로 써야하는 경우
    • 매개변수가 객체거나 반환 값이 객체형인 경우
    • 제네릭의 반환 타입을 지정할때 (자바 언어 스팩)

오토박싱과 언박싱

  • 오토박싱 : primitive 타입을 reference타입으로 바꾸는 것
  • 언박싱 : reference 타입을 primitive타입으로 바꾸는 것

Class class

  • 자바의 모든 클래스와 인터페이스는 컴파일 되고 나면 class파일로 생성된다.
  • Class클래스는 컴파일 된 class 파일에 저장된 클래스나 인터페이스 정보를 가져오는데 사용된다.
  • 코드를 작성할때 사용하는 여러 클래스의 모든 정보를 알고있는것은 아니다 (필요한 매개변수 또는 반환타입)
  • 이런 경우 정보를 알고싶을때 Class 클래스를 활용한다.
  1. Object 클래스의 getClass()활용
String s = new String();
Class c = s.getClass();
  1. 클래스 파일 이름을 Class변수에 직접 대입하기
Class c String.Class;
  1. Class.forName(“CLASS_NAME”) 사용하기
Class c = Class.forName("java.lang.String");
c.getConstructors();    // 모든 생성자 가져오기
c.getFields();    // 모든 멤버 변수 가져오기
c.getMethods();    // 모든 메소드 가져오기

Reflection programming

  • Class 클래스를 사용하는 방법은 클래스의 자료형을 직접 사용하여 프로그래밍 하는 것 보다 더 복잡하고 예외처리도 해야 한다. 이미 우리가 자료형을 알고 있는 클래스인 경우 또는 컴파일 할 때 직접 참조 할 수 있는 클래스는 Class 클래스를 활용할 필요가 없다. 클래스의 정보를 모두 알고 있는 상황에서 리플렉션 프로그래밍을 하면 오히려 코드가 복잡해지고 속도도 느려진다.
  • 따라서 리플렉션 프로그래밍은 컴파일 시점에 알 수 없는 클래스, 즉 프로그램 실행 중에 클래스를 메모리에 로딩 하거나 객체가 다른 곳에 위치해서 원격으로 로딩하고 생성할 때 사용한다.