Programming Language/자바(JAVA)

[JAVA] java.base 모듈 - 각종 클래스, 리플렉션, 어노테이션

토자맨 2024. 11. 26. 18:54

Object 클래스

JAVA의 모든 클래스는 Object의 자식 or 자손 클래스

  • 클래스를 선언할 때 extends로 상속받지 않으면 암시적으로 Object 클래스 상속 받음

Object 메소드 - equals(), hashCode(), toString()

equals()

  • boolean equals(Object obj)
    • 객체의 번지(주소)를 비교하고 결과를 리턴(== 연산자와 같은 기능)
  • String에서 equals()
    • String에서는 객체의 번지가 아닌 내용을 주소하도록 오버라이딩되어 있다.
    • ==와 다르게 리터럴로 선언했든 객체 생성 연잔사(new)로 선언했든 상관 없이 값으로만 비교하여 반환한다.
public class Main {
    public static void main(String[] args) {

        String str1 = "test";
        String str2 = "test";
        String str3 = new String("test");
        String str4 = new String("test");

        System.out.println(str1==str3); //false        

    }
}

hashCode()

객체를 식별하는 정수로써 객체의 메모리 번지를 이용해서 해시코드를 생성한 뒤 리턴(객체마다 다른 값을 리턴)

  • int hashCode()
  • 두 객체가 동등한지를 비교할 때 주로 사용한다.
  • equals()과 마찬가지로 객체의 데이터를 기준으로 오버라이딩해서 새로운 정수값을 리턴하는 것이 일반적이다.
  • 아래 사진과 같이 equals()hashCode()를 오버라이딩해서 두 객체의 값을 비교할 수 있다.

toString()

  • String toString()
    • 객체 문자 정보를 리턴
    • 각 클래스별로 오버라이딩하여 사용
      • String 클래스
        • 저장된 문자열 리턴
      • Date 클래스
        • 현재 날짜, 시간 리턴

레코드 선언

  • DTO 작성 시 반복적으로 사용되는 코드 줄이기 위해 JAVA 14부터 레코드 도입
  • 레코드 소스를 컴파일하면 변수의 타입과 이름을 이용해 private final 필드가 자동 생성되고, 생성자 및 Getter 메소드가 자동으로 추가된다. hashCode(), equals(), toString() 메소드를 재정의한 코드도 자동 추가
  • 즉, record로 선언하면 아래 코드들을 직접 작성하지 않아도 자동으로 만들어줌
// Person DTO
public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String name() { return this.name; }
    public String age() { return this.age; }

    @Override
    public int hashCode() { ... }

    @Override
    public boolean equals(Object obj) { ... }

    @Override
    public String toString() { ... }
}

// 위 동일한 코드를 생성하는 레코드 선언
public record Person(String name, int age) { ... }

롬복

다양한 메소드들을 자동으로 생성해주는 어노테이션을 제공하는 라이브러리

  • 레코드와 다르게 필드가 final이 아니다.
  • Getter, Setter를 getXxx(), setXxx()로 생성한다.
어노테이션 설명
@Data Getter, Setter, hashCode(), equals(), toString() 메소드 자동 생성
@NoArgsConstructor 기본(매개변수가 없는) 생성자 포함
@AllArgsConstructor 모든 필드를 초기화시키는 생성자 포함
@RequiredArgsConstructor 기본적으로 매개변수가 없는 생성자 포함. 만약 final 또는 @NonNull이 붙은 필드가 있다면 이 필드만 초기화시키는 생성자 포함
@Getter Getter 메소드 포함
@Setter Setter 메소드 포함
@EqualsAndHashCode equals()와 hashCode() 메소드 포함
@ToString toString() 메소드 포함

문자열(String) 클래스

StringBuilder 클래스

내부 버퍼(데이터를 저장하는 메모리)에 문자열을 저장해두고 그 안에서 추가, 수정, 삭제 작업

  • String은 내부 문자열을 수정할 수 없다.
  • String 객체에 값을 추가하는 것처럼 보여도 실제론 새로운 String 객체를 생성하는 것이다.
    • "A" String 객체는 그대로 있고 새로운 "AB" String 객체를 생성하여 참조하는 방식
String data = "A";
data += "B";
  • 이러한 방식은 너무 비효율적이기 때문에 잦은 변경 작업을 해야 한다면 StringBuilder를 사용한다.
리턴 타입 메소드(매개변수) 설명
StringBuilder append(기본값 문자열)
StringBuilder insert(위치, 기본값 문자열)
StringBuilder delete(시작 위치, 끝 위치) 문자열 일부 삭제
StringBuilder replace(시작 위치, 끝 위치, 문자열) 문자열 일부 대체
String toString() 완성된 문자열 리턴

StringTokenizer 클래스

구분자로 연결된 문자열을 구분자를 기준으로 분리

  • split()와 차이점
    • split()
      • 정규 표현식으로 구분
      • 여러 종류로 구분되어 있을 경우 사용
    • StringTokenizer
      • 문자로 구분
      • 한 구분자만 있을 때 사용
String data = "홍길동&이수홍,박연수,김자바-최명호";
String[] names = data.split("&|,|-");

String data = "홍길동/이수홍/박연수";
StringTokenizer st = new StringTokenizer(data, "/");
리턴 타입 메소드(매개변수) 설명
int countTokens() 분리할 수 있는 문자열의 총 수
boolean hasMoreTokens() 남아 있는 문자열이 있는지 여부
String nextToken() 문자열을 하나씩 가져옴

포장 클래스

자바는 기본 타입(byte, char, short, int, long, float, double, boolean)의 값을 갖는 객체를 생성할 수 있는데, 이런 개체를 포장(wrapper) 객체라 한다.

  • 포장 객체는 객체이기 때문에 비교 연산자를 사용할 때 == 연산자가 아닌 객체의 번지를 비교하는 equals() 연산자를 사용해야 한다.
  • 박싱
    • 기본 타입 -> 포장 객체
  • 언박싱
    • 포장 객체 -> 기본 타입
Integer obj = 100;    // 박싱
int value = obj;    // 언박싱

수학 클래스

  • Math 클래스가 제공하는 메소드는 모두 static 메소드이므로 객체 생성 없이 클래스로 바로 사용 가능Math 클래스 메소드
    • 절대값: Math.abs(n)
    • 올림값: Math.ceil(n)
    • 버림값: Math.floor(n)
    • 최대값: Math.max(a, b)
    • 최소값: Math.min(a, b)
    • 랜덤값: Math.random() -> 0.0과 1.0 사이의 double 타입 난수를 리턴

Random 클래스 메소드

생성자 설명
Random() 현재 시간을 이용해서 종자값을 자동 설정한다.
Random(long seed) 주어진 종자값을 사용한다.

리턴값 메소드(매개변수) 설명
boolean nextBoolean() boolean 타입의 난수를 리턴
double nextDouble() double 타입의 난수를 리턴 (0.0 <= x < 1.0)
int nextInt() int 타입의 난수를 리턴 (-2^32 <= x <= 2^32 - 1)
int nextInt(int n) int 타입의 난수를 리턴 (0 <= x < n)

정규 표현식 클래스

문자열이 올바르게 구성되어 있는지 검증하는 클래스

  • (02|010)-\d{3,4}-\d{4}
    • 전화번호 형식 검사
  • \w+@\w+\.\w+(\.\w+)?
    • 이메일 형식 검사
  • 이러한 검사는 Pattern 클래스로 검증할 수 있다.
    • Pattern 클래스는 정규 표현식으로 문자열을 검증하는 matches() 메소드 제공한다.
      • boolean result = Pattern.matches("정규식", "검증할 문자열");

리플렉션

메타 정보(패키지 정보, 타입 정보, 멤버 정보(생성자, 필드, 메소드) 등)를 프로그램에서 읽고 수정하는 행위

클래스 정보 얻기

  • Class clazz = 클래스이름.class;
  • Class clazz = Class.forName("패키지...클래스이름");
  • Class clazz = 객체참조변수.getClass();

패키지와 타입 정보 얻기

  • Package getPackage() : 패키지 정보 읽기
  • String getSimpleName() : 패키지를 제외한 타입 이름
  • String getName() : 패키지를 포함한 전체 타입 이름

멤버 정보 얻기

  • Constructor[] getDeclaredConstructors() : 생성자 정보 읽기
  • Field[] getDeclaredFields() : 필드 정보 읽기
  • Method[] getDeclaredMethods() : 메소드 정보 읽기

리소스 경로 얻기

  • URL getResource(String name) : 리소스 파일의 URL 리턴
  • InputStream getResourceAsStream(String name) : 리소스 파일의 InputStream 리턴
  • clazz.getResource("photo.jpg").getPath(); - URL 객체의 메소드로 절대 경로를 리턴

어노테이션

@로 작성되는 요소로써 클래스 또는 인터페이스를 컴파일하거나 실행할 때 어떻게 처리해야 할 것인지를 알려주는 설정 정보

  • 용도
    • 컴파일 시 사용하는 정보 전달
    • 빌드 툴이 코드를 자동으로 생성할 때 사용하는 정보 전달
    • 실행 시 특정 기능을 처리할 때 사용하는 정보 전달
  • 대표적인 어노테이션 @Override
    • 컴파일러가 메소드 재정의 검사를 하도록 설정
    • 정확히 재정의 되지 않았다면 에러 발생

어노테이션 타입 정의와 적용

  • 어노테이션은 속성을 가질 수 있음
    • 기본값은 이름 뒤에 default 기본값으로 설정
// 어노테이션 정의
public @interface AnnotationName {
    // 어노테이션 속성
    String prop1();
    int prop2() default 1; // 기본값 설정
}

// 사용
@AnnotationName(prop1= "값", prop2=3);

public @interface AnnotationName {
    // 어노테이션 속성
    String value();
    int prop2() default 1; // 기본값 설정
}

@AnnotationName("값"); // value 속성을 가진 어노테이션을 사용할 때는 값만 기술
@AnnotationName(prop1= "값", prop2=3); // 다른 속성과 같이 값을 동시에 주고 싶다면 value 속성 이름 언급 해야 함

어노세이션 적용 대상 - @Target

  • @Target 어노테이션을 사용하여 적용 대상 지정

// 클래스, 인터페이스, 열거 타입, 필드, 메소드 적용 가능
@Target( { ElementType.Type, ElementType.FIELD, ElementType.METHOD } )
public @interface AnnotationName {
}

어노테이션 유지 정책 - @Retention

  • @Retention 어노테이션을 사용하여 어노테이션을 언제까지 유지할 것인지 지정
  • @Retention의 기본 속성인 value는 RetentionPolicy 열거 상수 값 가짐

@Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD } )
@Retention( RetentionPolicy.RUNTIME )
public @interface AnnotationName {
}

@AnnotationName            // TYPE(클래스)에 적용
public class ClassName {
    @AnnotationName        // 필드에 적용
    private String fieldName;

    // @AnnotationName    // @Target에 CONSTRUCT가 없으므로 생성자에는 적용 못함
    public ClassName() {}

    @AnnotationName        // 메소드에 적용
    public void methodName() {}
}    

어노테이션 설정 정보 이용

  • 리플렉션을 이용하여 적용 대상으로부터 어노테이션 정보를 다음 메소드로 얻어낼 수 있다.
  • 이런 게 있다 정도만 알아두자
    • isAnnotationPresent(AnnotationName.class) : 지정한 어노테이션이 적용되었는지 여부 - boolean 리턴
    • getAnnotation(AnnotationName.class) : 지정한 어노테이션이 적용되어 있으면 어노테이션을, 그렇지 않다면 null을 리턴
    • getDeclaredAnnotations() : 적용된 모든 어노테이션을 리턴(Annotation[])