작성 도중 계정이 로그아웃되어 작성해두었던 네모 칸, 코드, 표들이 전부 날라갔습니다. 24-10-20까지 수정할 예정이니 참고 바랍니다.
12.1 API 도큐먼트
자바 표준 모듈에서 제공하는 라이브러리는 방대하기 때문에 쉽게 찾아서 사용할 수 있도록 도와주는 API(Application Programming Interface) 도큐먼트가 있다. 라이브러리가 클래스와 인터페이스의 집합이라면, API 도큐먼트는 이를 사용하기 위한 방법을 기술한 것이다.
다음 URL을 방문하면 JDK 버전별로 사용할 수 있는 API 도큐먼트를 볼 수 있다.
String 도큐먼트를 통해 API 도큐먼트를 읽는 방법을 알아보자.
클래스 선언부 보기
API 도큐먼트에서 String 클래스가 어떻게 정의되었는지 보려면 ① 선언부를 보면 된다. 여기서는 클래스가 final인지 추상(abstract)인지를 알 수 있고, 부모 클래스와 구현 인터페이스를 볼 수 있다. 전체 상속 관계를 보려면 ② 상속 계층도를 보면 된다.
구성 멤버 보기
String이 가지고 있는 멤버를 보려면 상단 메뉴의 SUMMARY를 활용한다. SUMMARY는 선언된 멤버별로 이동하는 링크를 제공한다. 링크가 있으면 공개된(public, protexted) 멤버가 있다는 뜻이고, 링크가 없으면 공개된 멤버가 없다는 뜻이다.
- NESTED: 중첩 클래스/중첩 인터페이스 목록으로 이동하는 링크
- FIELD: 필드 목록으로 이동하는 링크
- CONSTR: 생성자 목록으로 이동하는 링크
- METHOD: 메소드 목록으로 이동하는 링크
필드 보기
Modifier and Type에서는 static 여부와 필드 타입을 알 수 있고, Field와 Desciption은 필드명과 그에 대한 간단한 설명이다. 필드명을 클릭하면 필드 선언부와 상세한 설명이 나온다. 관례적으로 필드 이름이 모두 대문자이면 public static final로 선언된 상수 필드이다.
생성자 보기
Constructor에서는 생성자의 매개변수 타입을 알 수 있고, Description은 이에 대한 간단한 설명이다. String 클래스는 매개변수 타입과 개수를 달리한 10개가 넘는 생성자들이 오버로딩되어 있다. 이 생성자들 중 하나를 이용해서 String 객체를 생성할 수 있다.
메소드 보기
- All Methods: 모든 메소드 목록을 보여준다.
- Static Methods: 정적 메소드 목록을 보여준다.
- Instance Methods: 인스턴스 메소드 목록을 보여준다.
- Concrete Methods: 완전한 실행부를 갖춘 메소드 목록을 보여준다.
- Deprecated Methods: 향후 제거될 메소드 목록을 보여준다.
Modifier and Type에서는 static 여부와 리턴 타입이 무엇인지 알 수 있다. Method에서는 메소드 명과 매개변수 타입 및 개수를 알 수 있고, Description은 그에 대한 간단한 설명이다. 각 메소드명을 클릭하면 상세 설명을 읽을 수 있다.
12.2 java.base 모듈
java.base는 모든 모듈이 의존하는 기본 모듈로, 모듈 중 유일하게 requires하지 않아도 사용할 수 있다. 이 모듈에 포함되어 있는 패키지는 대부분의 자바 프로그램에서 많이 사용하는 것들이다. 다음은 java.base 모듈에 포함된 주요 패키지와 용도를 설명한 표이다.
패키지 | 용도 |
java.lang | 자바 언어의 기본 클래스를 제공 |
java.util | 자료 구조와 관련된 컬렉션 클래스를 제공 |
java.text | 날짜 및 숫자를 원하는 형태의 문자열로 만들어 주는 포맷 클래스를 제공 |
java.time | 날짜 및 시간을 조작하거나 연산하는 클래스를 제공 |
java.io | 입출력 스트림 클래스를 제공 |
java.net | 네트워크 통신과 관련된 클래스를 제공 |
java.nio | 데이터 저장을 위한 Buffer 및 새로운 입출력 클래스 제공 |
String, System, Integer, Double, Exception, Exception, RuntimeException 등의 클래스는 java.lang 패키지에 있고, 키보드 입력을 위한 Scanner는 java.util 패키지에 있다.
java.lang은 자바 언어의 기본적인 클래스를 담고 있는 패키지로, 이 패키지에 있는 클래스와 인터페이스는 import 없이 사용할 수 있다. 다음은 java.lang 패키지에 포함된 주요 클래스와 용도를 설명한 표이다.
클래스 | 용도 | |
Object | ─ 자바 클래스의 최상위 클래스로 사용 | |
System | ─ 키보드로부터 데이터를 입력받을 때 사용 ─ 모니터(콘솔)로 출력하기 위해 사용 ─ 프로세스를 종료시킬 때 사용 ─ 진행 시간을 읽을 때 사용 ─ 시스템 속성(프로퍼티)을 읽을 때 사용 |
|
문자열 관련 | String | ─ 문자열을 저장하고 조작할 때 사용 |
StringBuilder | ─ 효율적인 문자열 조작 기능이 필요할 때 사용 | |
java.util.StringTokenizer | ─ 구분자로 연결된 문자열을 분리할 때 사용 | |
포장 관련 | Byte, Short, Character, Integer, Float, Double, Boolean | ─ 기본 타입의 값을 포장할 때 사용 ─ 문자열을 기본 타입으로 변환할 때 사용 |
Math | ─ 수학 계산이 필요할 때 사용 | |
Class | ─ 클래스의 메타 정보(이름, 구성 멤버) 등을 조사할 때 사용 |
12.3 Object 클래스
클래스를 선언할 때 extends 키워드로 다른 클래스를 상속하지 않으면 암시적으로 java.lang.Object 클래스을 상속하게 된다. 따라서 자바의 모든 클래스는 Object의 자식이거나 자손 클래스이다.
따라서 Object가 가진 메소드는 모든 객체에서 사용할 수 있다. 다음은 Object가 가진 주요 메소드를 설명한 표이다.
메소드 | 용도 |
boolean equals(Object obj) | 객체의 번지를 비교하고 결과를 리턴 |
int hashCode() | 객체의 해시코드를 리턴 |
String toString() | 객체 문자 정보를 리턴 |
객체 동등 비교
Object의 equals() 메소드는 객체의 버닞를 비교하고 boolean 값을 리턴한다.
public boolean equals(Object obj)
equals() 메소드의 매개변수 타입이 Object이므로 자동 타입 변환에 의해 모든 객체가 매개값으로 대입될 수 있다. equals() 메소드는 두 객체가 동일한 객체라면 true를 리턴하고, 그렇지 않으면 false를 리턴한다.
일반적으로 Object의 equals() 메소드는 재정의해서 동등 비교용으로 사용된다. 동등 비교란 객체가 비록 달라도 내부의 데이터가 같은지를 비교하는지를 비교하는 것을 말한다.
객체 해시코드
객체 해시코드란 객체를 식별하는 정수를 말한다. Object의 hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시드를 생성하기 때문에 객체마다 다른 정수값을 리턴한다. hashCode() 메소드의 용도는 두 객체가 동등한지를 비교할 때 주로 사용한다.
public int hashCode()
hashCode()는 객체의 데이터를 기준으로 재정의해서 새로운 정수값을 리턴하도록 하는 것이 일반적이다. 객체가 다르다 할지라도 내부 데이터가 동일하다면 같은 정수값을 리턴하기 위해서이다.
자바는 두 객체가 동등함을 비교할 때 hashCode()와 equals() 메소드를 같이 사용하는 경우가 많다. hashCode()가 리턴하는 정수값이 같은지를 확인하고, 그 다음 equals() 메소드가 true를 리턴하는지를 확인해서 동등 객체임을 판단한다.
객체 문자 정보
Object의 toString() 메소드는 객체의 문자 정보를 리턴한다. 객체의 문자 정보란 객체를 문자열로 표현한 값을 말한다. 기본적으로 Object의 toString() 메소드는 '클래스명@16진수해시코드'로 구성된 문자열을 리턴한다.
Object obj = new Object();
System.out.println(obj.toString());
java.lang.Object@de6ced
객체의 문자 정보가 중요한 경우에는 Object의 toString() 메소드를 재정의해서 간결하고 유익한 정보를 리턴하도록 해야 한다.
레코드 선언
데이터 전달을 위한 DTO(Data Transfer Object)를 작성할 때 반복적으로 사용되는 코드를 줄이기 위해 Java 14부터 레코드(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() { ··· }
}
다음 코드는 위와 동일한 코드를 생성하는 레코드 선언이다. class 키워드 대신에 recode로 대체하고 클래스 이름 뒤에 괄호를 작성해서 저장할 데이터의 종류를 변수로 선언한다.
public record Person(String name, int age) {
}
이렇게 선언된 레코드 소스를 컴파일하면 변수의 타입과 이름을 이용해서 private final 필드가 자동 생성되고, 생성자 및 Getter 메소드가 자동으로 추가된다. 그리고 hashCode(), equals(), toString() 메소드를 재정의한 코드도 자동으로 추가된다.
롬복 사용하기
롬복(Lombok)은 JDK에 포함된 표준 라이브러리는 아니지만 개발자들이 즐겨 쓰는 자동 코드 생성 라이브러리이다. 롬복은 DTO 클래스를 작성할 때 Getter, Setter, hasCode(), equals(), toString() 메소드를 자동으로 생성하기 때문에 작성할 코드의 양을 줄여준다.
레코드와의 차이점은 필드가 final이 아니며, 값을 읽는 Getter는 getXxx(또는 isXxx)로, 값을 변경하는 Setter는 setXxx로 생성된다는 것이다.
롬복 라이브러리를 사용할 때는 class 선언 위에 @Data를 붙인다. 이 @Data를 어노테이션이라고 한다.
@Data가 붙게 되면 컴파일 과정에서 기본 생성자와 함께 Getter, Setter, hashCode(), equals(), toString() 메소드가 자동 생성된다.
@Data 외에도 다음과 같은 어노테이션을 사용할 수 있다.
어노테이션 | 설명 |
@NoArgsConstructor | 기본(매개변수가 없는) 생성자 포함 |
@AllArgsConstructor | 모든 필드를 초기화시키는 생성자 포함 |
@RequiredArgsConstructor | 기본적으로 매개변수가 없는 생성자 포함. 만약 final 또는 @NonNull이 붙은 필드가 있다면 이 필드만 초기화시키는 생성자 포함 |
@Getter | Getter 메소드 포함 |
@Setter | Setter 메소드 포함 |
@EqualsAndHashCode | equals()와 hashCode() 메소드 포함 |
@ToString | toString() 포함 |
12.4 System 클래스
자바 프로그램은 운영체제상에서 바로 실행되는 것이 아니라 자바 가상 머신(JVM) 위에서 실행된다. 따라서 운영체제의 모든 기능을 자바 코드로 직접 접근하기란 어렵다. 하지만 java.lang 패키지에 속하는 System 클래스를 이용하면 운영체제의 일부 기능을 이용할 수 있다.
System 클래스의 정적(static) 필드와 메소드를 이용하면 프로그램 종료, 키보드 입력, 콘솔(모니터) 출력, 현재 시간 읽기, 시스템 프로퍼티 읽기 등이 가능하다.
콘솔 출력
out 필드를 이용하면 콘솔에 원하는 문자열을 출력할 수 있다. err 필드도 out 필드와 동일한데, 차이점은 콘솔 종류에 따라 에러 내용이 빨간색으로 출력된다는 것이다.
키보드 입력
자바는 키보드로부터 입력된 키를 읽기 위해 System 클래스에서 in 필드를 제공한다. 다음과 같이 in 필드를 이용해서 read() 메소드를 호출하면 입력된 키의 코드값을 얻을 수 있다.
키 코드느 각 키에 부여되 있는 번호로, 다음과 같다.
read() 메소드는 호출과 동시에 키 코드를 읽는 것이 아니라, Enter 키를 누르기 전까지는 대기 상태이다가 Enter 키를 누르면 입력했던 키들을 하나씩 읽기 시작한다. 단, read() 메소드는 IOException을 발생할 수 있는 코드이므로 예외 처리가 필요하다.
프로세스 종료
운영체제는 실행 중인 프로그램을 프로세스(process)로 관리한다. 자바 프로그램을 시작하면 jvm 프로세스가 생성되고, 이 프로세스가 main() 메소드를 호출한다. 프로세스를 강제 종료하고 싶다면 System.exit() 메소드를 사용한다.
exit() 메소드는 int 매개값이 필요한데, 이 값을 종료 상태값이라고 한다. 종료 상태값으로 어떤 값을 주더라도 프로세스는 종료되는데 정상 종료일 경우 0, 비정상 종료는 1 또는 -1로 주는 것이 관례이다.
진행 시간 읽기
System 클래스의 currentTimeMillis() 메소드와 nanoTime() 메소드는 1970년 1월 1일 0시부터 시작해서 현재까지 진행된 시간을 리턴한다.
이 두 메소드는 프로그램 처리 시간을 측정하는 데 주로 사용된다. 프로그램 처리를 시작할 때 한 번, 끝날 때 한 번 읽어서 그 차이를 구하면 프로그램 처리 시간이 나온다.
시스템 프로퍼티 읽기
시스템 프로퍼티(System Property)란 자바 프로그램이 시작될 때 자동 설정되는 시스템의 속성을 말한다. 다음은 시스템 프로퍼티의 주요 속성 이름(key)과 값(value)에 대해 설명한 것이다.
12.5 문자열 클래스
자바에서 문자열과 관련된 주요 클래스는 다음과 같다.
String 클래스
String 클래스는 문자열을 저장하고 조작할 떄 사용한다. 문자열 리터럴은 자동으로 String 객체로 생성되지만, String 클래스의 다양한 생성자를 이용해서 직접 객체를 생성할 수도 있다.
프로그램을 개발하다 보면 byte 배열을 문자열로 변환하는 경우가 종종 있다. 이때는 String 생성자 중에서 다음 두 가지를 사용해 String 객체로 생성할 수 있다.
StringBuilder 클래스
String은 내부 문자열을 수정할 수 없다. 다음 코드를 보면 다른 문자열을 결합해서 내부 문자열을 변경하는 것처럼 보이지만 사실 'ABCDEF'라는 String 객체를 생성하는 것이다. 그리고 data 변수는 새로 생성된 String 객체를 참조하게 된다.
문자열의 + 연산은 새로운 string 객체가 생성되고 이전 객체는 계속 버려지는 것이기 때문에 효율성이 좋다고는 볼 수 없다. 잦은 문자열 변경 작업을 해야 한다면 StringBuilder를 사용하는 것이 좋다.
StringBuilder는 내부 버퍼(데이터를 저장하는 메모리)에 문자열을 저장해두고 그 안에서 추가, 수정, 삭제 작업을 하도록 설계되어 있다. StringBuilder가 제공하는 조각 메소드는 다음과 같다.
StringTokenizer 클래스
문자열이 구분자(delimiter)로 연결되어 있을 경우, 구분자를 기준으로 문자열을 분리하려면 String의 split() 메소드를 이용하거나 java.util 패키지의 StringTokenizer 클래스를 이용할 수 있다. split은 정규 표현식으로 구분하고, StringTokenizer는 문자로 구분한다는 차이점이 있다.
다음과 같은 문자열에서 &, 쉼표(,), 하이픈(-)으로 구분된 사람 이름을 뽑아낼 경우에는 정규 표현식으로 분리하는 split() 메소드를 사용해야 한다.
그러나 다음과 같이 여러 종류가 아닌 한 종류의 구분자만 있다면 StringTokenizer를 사용할 수도 있다. StringTokenizer 객체를 생성할 때는 첫 번째 매개값으로 전체 문자열을 주고, 두 번째 매개값으로 구분자를 주면 된다.
StringTokenizer 객체가 생성되면 다음 메소드들을 이용해서 분리된 문자열을 얻을 수 있다.
nextToken() 메소드는 분리된 문자열을 하나씩 가져오고, 더 이상 가져올 문자열이 없다면 예외를 발생시킨다. 그래서 nextToken()을 사용하기 전에 hasMoreTokens() 메소드로 가져올 문자열이 있는지 먼저 조사하는 것이 좋은 방법이다.
12.6 포장 클래스
자바는 기본 타입의 값을 갖는 객체를 생성할 수 있다. 이런 객체를 포장(wrapper) 객체라고 한다. 값을 포장하고 있다고 해서 붙여진 이름이다.
포장 객체를 생성하기 위한 클래스는 java.lang 패키지에 포함되어 있는데, char 타입과 int 타입이 각각 Character와 Integer인 것만 제외하고는 기본 타입의 첫 문자를 대문자로 바꾼 이름을 가지고 있다.
포장 객체는 포장하고 있는 기본 타입의 값을 변경할 수 없고, 단지 객체로 생성하는 데 목적이 있다. 이런 객체가 필요한 이유는 컬렉션 객체 때문이다. 컬렉션 객체는 기본 타입의 값은 저장할 수 없고, 객체만 저장할 수 있다.
박싱과 언박싱
기본 타입의 값을 포장 객체로 만드는 과정을 박싱(boxing)이라고 하고, 반대로 포장 객체에서 기본 타입의 값을 얻어내는 과정을 언박싱(unboxing)이라고 한다.
박싱은 포장 클래스 변수에 기본 타입 값이 대입될 때 발생한다. 반대로 언박싱은 기본 타입 변수에 포장 객체가 대입될 때 발생한다.
언박싱은 다음과 같이 연산 과정에서도 발생한다. obj는 50과 연산되기 전에 언박싱된다.
문자열을 기본 타입 값으로 변환
포장 클래스는 문자열을 기본 타입 값으로 변환할 때도 사용된다. 대부분의 포장 클래스에는 'parse+기본타입' 명으로 되어있는 정적(static) 메소드가 있다. 이 메소드는 문자열을 해당 기본 타입 값으로 변환한다.
포장 값 비교
포장 객체는 내부 값을 비교하기 위해 ==와 != 연산자를 사용할 수 없다. 이 연산은 내부의 값을 비교하는 것이 아니라 포장 객체의 번지를 비교하기 때문이다. 객체를 다르게 정의한 변수가 동일한 값을 갖고 있어도 == 연산의 결과는 false가 나온다.
예외도 있다. 포장 객체의 효율적 사용을 위해 다음 범위의 값을 갖는 포장 객체는 공유된다. 이 범위의 값을 갖는 포장 객체는 ==와 != 연산자로 비교할 수 있지만, 내부 값을 비교하는 것이 아니라 객체 번지를 비교한다는 것을 알아야 한다.
포장 객체에 정확히 어떤 값이 저장될 지 모르는 상황이라면 ==과 !=은 사용하지 않는 것이 좋다. 대신 equals() 메소드로 내부 값을 비교할 수 있다. 포장 클래스의 equals() 메소드는 내부의 값을 비교하도록 재정의되어 있다.
12.7 수학 클래스
Math 클래스는 수학 계산에 사용할 수 있는 메소드를 제공한다. Math 클래스가 제공하는 메소드는 모두 정적(static)이므로 Math 클래스로 바로 사용이 가능하다. 다음은 Math 클래스가 제공하는 주요 메소드이다.
random() 메소드는 0.0과 1.0 사이의 double 타입 난수를 리턴한다. 이 값을 이용해서 start부터 시작하는 n개의 정수(start <= ··· < (start+n)) 중 하나의 정수를 얻기 위한 공식을 만들면 다음과 같다.
난수를 얻는 또 다른 방법으로 java.util.Random 클래스를 이용할 수 있다. 이 클래스를 이용하면 boolean, int, double 난수를 얻을 수 있다. 다음은 Random 객체를 생성하기 위한 생성자이다.
종자값(seed)이란 난수를 만드는 알고리즘에 사용되는 값으로, 종자값이 같으면 같은 난수를 얻는다. 다음은 Random 클래스가 제공하는 메소드이다.
12.8 날짜와 시간 클래스
자바는 컴퓨터의 날짜 및 시각을 읽을 수 있도록 java.util 패키지에서 Date와 Calendar 클래스를 제공하고 있다. 또한 날짜와 시간을 조작할 수 있도록 java.time 패키지에서 LacalDateTime 등의 클래스를 제공한다.
Date 클래스
Date는 날짜를 표현하는 클래스로 객체 간에 날짜 정보를 주고받을 때 사용된다. Date 클래스에는 여러 개의 생성자가 선언되어 있지만 대부분 Deprecated(더 이상 사용되지 않음)되어 Date() 생성자만 주로 사용된다. Date90 생성자는 컴퓨터의 현재 날짜를 읽어 Date 객체로 만든다.
현재 날짜를 문자열로 얻고 싶다면 toString() 메소드를 사용할 수 있지만 영문으로 출력된다.
Calendar 클래스
Calendar 클래스는 달력을 표현하는 추상 클래스이다. 날짜와 시간을 계산하는 방법이 지역과 문화에 따라 다르기 때문에 특정 역법(날짜와 시간을 매기는 방법)에 따르는 달력은 자식 클래스에서 구현하도록 되어 있다.
특별한 역법을 사용하는 경우가 아니라면 직접 하위 클래스를 만들 필요는 없고 Calendar 클래스의 정적 메소드인 getInstance() 메소드를 이용하면 컴퓨터에 설정되어 있는 시간대(TimeZome)를 기준으로 Calendar 하위 객체를 얻을 수 있다.
Calendar가 제공하는 날짜와 시간에 대한 정보를 얻기 위해서는 get() 메소드를 이용한다. get() 메소드의 매개값으로 Calendar에 정의된 상수를 주면 상수가 의미하는 값을 리턴한다.
날짜와 시간 조작
Date와 Calendar는 날짜와 시간 정보를 얻기에는 충분하지만, 날짜와 시간을 조작할 수는 없다. 이때는 java.time 패키지의 LocalDateTime 클래스가 제공하는 다음 메소드를 사용하면 매우 쉽게 날짜와 시간을 조작할 수 있다.
LocalDateTime 클래스를 이용해서 현재 컴퓨터의 날짜와 시간을 얻는 방법은 다음과 같다.
날짜와 시간 비교
LocalDateTime 클래스는 날짜와 시간을 비교할 수 있는 다음 메소드도 제공한다.
비교를 위해 특정 날짜와 시간으로 LocalDateTime 객체를 얻는 방법은 다음과 같다. year부터 second까지 매개값을 모두 int 타입 값으로 제공하면 된다.
12.9 형식 클래스
Format(형식) 클래스는 숫자 또는 날짜를 원하는 형태의 무낮열로 변환해주는 기능을 제공한다. Format 클래스는 java.text 패키지에 포함되어 있는데, 주요 Format 클래스는 다음과 같다.
DecimalFormat
DecimalFormat은 숫자를 형식화된 문자열로 변환하는 기능을 제공한다. 원하는 형식으로 표현하기 위해 다음과 같은 패턴을 사용한다.
패턴 정보와 함께 DecimalFormat 객체를 생성하고 format() 메소드로 숫자를 제공하면 패턴에 따른 형식화된 문자열을 얻을 수 있다.
SimpleDateFormat
SimpleDateFormat은 날짜를 형식화된 문자열로 변환하는 기능을 제공한다. 원하는 형식으로 표현하기 위해 다음과 같은 패턴을 사용한다.
패턴에는 자릿수에 맞게 기호를 반복해서 작성할 수 있다. 패턴 정보와 함께 SimpleDateFormat 객체를 생성하고 format() 메소드로 날짜를 제공하면 패턴과 동일한 문자열을 얻을 수 있다.
12.10 정규 표현식 클래스
문자열이 정해져 있는 형식으로 구성되어 있는지 검증해야 하느 경우가 있다. 예를 들어 이메일이나 전화번호를 사용자가 제대로 입력했는지 검증할 때이다. 자바는 정규 표현식(Regular Expression)을 이용해서 문자열이 올바르게 구성되어 있는지 검증한다.
정규 표현식 작성 방법
정규 표현식은 문자 또는 숫자와 관련된 표현과 반복 기호가 결합된 문자열이다. 다음은 정규 표현식을 구성하는 표현 및 기호에 대한 설명이다.
다음은 02-123-1234 또는 010-1234-5678과 같은 전화번호를 위한 정규 표현식이다.
다음은 white@naver.com과 같은 이메일을 위한 정규 표현식이다.
Pattern 클래스로 검증
java.util.regex 패키지의 Pattern 클래스는 정규 표현식으로 문자열을 검증하는 matches() 메소드를 제공한다. 첫 번째 매개값은 정규 표현식이고, 두 번째 매개값은 검증할 문자열이다. 검증한 후의 결과는 boolean 타입으로 리턴된다.
12.11 리플렉션
자바는 클래스와 인터페이스의 메타 정보를 Class 객체로 관리한다. 여기서 메타 정보란 패키지 정보, 타입 정보, 멤버(생성자, 필드, 메소드) 정보 등을 말한다. 이러한 메타 정보를 프로그램에서 읽고 수정하는 행위를 리플렉션(reflection)이라고 한다.
프로그램에서 Class 객체를 얻으려면 다음 3가지 방법 중 하나를 이용하면 된다.
①과 ②는 클래스 이름만 가지고 Class 객체를 얻는 방법이고, ③은 객체로부터 Class 객체를 얻는 방법이다. 셋 중 어떤 방법을 사용하더라도 동일한 Class 객체를 얻을 수 있다.
패키지와 타입 정보 얻기
패키지와 타입(클래스, 인터페이스) 이름 정보는 다음 메소드를 통해 얻을 수 있다.
멤버 정보 얻기
타입(클래스, 인터페이스)가 가지고 있는 멤버 정보는 다음 메소드를 통해 얻을 수 있다.
각각 Constructor 배열, Field 배열, Method 배열을 리턴한다. Constructor, Field, Method 클래스는 모두 java.lang.reflect 패키지에 있는데 각각 생성자, 필드, 메소드에 대한 선언부 정보를 제공한다.
리소스 경로 얻기
Class 객체는 클래스 파일(~.class)의 경로 정보를 가지고 있기 때문에 이 경로를 기준으로 상대 경로에 있는 다른 리소스 파일(이미지, XML, Property 파일)의 정보를 얻을 수 있다. 이때 사용하는 메소드는 다음과 같다.
12.12 어노테이션
코드에서 @으로 작성되는 요소를 어노테이션(Annotation)이라고 한다. 어노테이션은 클래스 또는 인터페이스를 컴파일하거나 실행할 때 어떻게 처리해야 할 것인지를 알려주는 설정 정보이다. 어노테이션은 다음 세 가지 용도로 사용된다.
- 컴파일 시 사용하는 정보 전달
- 빌드 툴이 코드를 자동으로 생성할 때 사용하는 정보 전달
- 실행 시 특정 기능을 처리할 때 사용하는 정보 전달
컴파일 시 사용하는 정보 전달의 대표적인 예는 @Override 어노테이션이다. @Override는 컴파일러가 메소드 재정의 검사를 하도록 설정한다. 정확히 재정의되지 않았다면 컴파일러는 에러를 발생시킨다.
어노테이션 타입 정의와 적용
어노테이션을 사용하기 위해서는 먼저 정의부터 해야 한다. 어노테이션을 정의하는 방법은 @interface 뒤에 사용할 어노테이션 이름이 온다.
public @interface AnnotationName {
}
이렇게 정의한 어노테이션은 코드에서 다음과 같이 사용된다.
@AnnotationName
어노테이션은 속성을 가질 수 있다. 속성은 타입과 이름으로 구성되며, 이름 뒤에 괄호를 붙인다. 속성의 기본값은 default 키워드로 지정할 수 있다. 기본값이 없으면 반드시 값을 기술해야 하고, 기본값이 있다면 생략 가능하다.
public @interface AnnotationName {
String prop1();
int prop2() default 1;
}
@AnnotationName(prop1 = "값");
@AnnotationName(prop1 = "값", prop2 = 3);
어노테이션은 기본 속성인 value를 다음과 같이 가질 수 있다.
public @interface AnnotationName {
String value();
int prop2() default 1;
}
value 속성을 가진 어노테이션을 코드에서 사용할 때에는 다음과 같이 값만 기술할 수 있다. 이 값은 value 소것ㅇ에 자동으로 대입된다.
@AnnotationName("값");
하지만 value 속성과 다른 속성의 값을 동시에 주고 싶다면 value 속성 이름을 반드시 언급해야 한다.
@AnnotationName(value = "값", prop2 = 3);
어노테이션 적용 대상
어노테이션을 어느 클래스에 적용할 것인지, 메소드에 적용할 것인지 명시해야 한다. 적용할 수 있는 대상의 종류는 ElementType 열거 상수로 정의되어 있다.
Element 열거 상수 | 적용 요소 |
TYPE | 클래스, 인터페이스, 열거 타입 |
ANNOTATION_TYPE | 어노테이션 |
FIELD | 필드 |
CONSTRUCTOR | 생성자 |
METHOD | 메소드 |
LOCAL_VARIABLE | 로컬 변수 |
PACKAGE | 패키지 |
적용 대상을 지정할 떄에는 @Target 어노테이션을 사용한다. @Target의 기본 속성인 value는 ElementType 배열을 값으로 가진다. 이것은 적용 대상을 복수 개로 지정하기 위해서이다.
@Target( { ElementType.TYPE, ElementType.FIELD, ElementTYpe.METHOD } )
public @interface AnnotationName {
}
이 어노테이션은 다음과 같이 클래스, 필드, 메소드에 적용할 수 있고 생성자는 적용할 수 없다.
@AnnotationName // TYPE(클래스에 적용)
public class ClasName {
@AnnotationName// 필드에 적용
private String fieldName;
// @Annotation // @target에 CONSTRUCT가 없으므로 생성자에는 적용 못함
public ClassName() { }
@AnnotationName // 메소드에 적용
public void methodName() { }
}
어노테이션 유지 정책
어노테이션을 적용할 때 한 가지 더 추가해야 할 내용은 @AnnotationName을 언제까지 유지할 것인지를 지정하는 것이다. 어노테이션 유지 정책은 RetentionPolicy 열거 상수로 다음과 같이 정의되어 있다.
RetentionPolicy 열거 상수 | 어노테이션 적용 시점 | 어노테이션 제거 시점 |
SOURCE | 컴파일할 때 적용 | 컴파일된 후에 제거됨 |
CLASS | 메모리로 로딩할 때 적용 | 메모리로 로딩된 후에 제거됨 |
RUNTIME | 실행할 때 적용 | 계속 유지됨 |
유지 정책을 지정할 때에는 @Retention 어노테이션을 사용한다. @Retention의 기본 속성인 value는 RetentionPolicy 열거 상수 값을 가진다. 다음은 실행 시에도 어노테이션을 설정 정보를 이용할 수 있도록 유지 정책을 RUNTIME으로 지정한 예이다.
@Target( { ElementType.TYPE, ElementType.FIELD, ElementTYpe.METHOD } )
@Retention( RetentionPolicy.RUNTIME)
public @interface AnnotationName {
}
어노테이션 설정 정보 이용
어노테이션은 아무런 동작을 가지지 않는 설정 정보일 뿐이다. 이 설정 정보를 이용해서 어떻게 처리할 것인지는 애필리케이션의 몫이다. 애플리케이션은 리플렉션을 이용해서 적용 대상으로부터 어노테이션의 정보를 다음 메소드로 얻어낼 수 있다.
리턴 타입 | 메소드명(매개변수) | 설명 |
boolean | isAnnotationPresent(AnnotatinName.class) | 지정한 어노테이션이 적용되었는지 여부 |
Annotation | getAnnotation(AnnotationName.class) | 지정한 어노테이션이 적용되어 있으면 어노테이션을 리턴하고, 그렇지 않으면 null을 리턴 |
Annotation[ ] | getDeclaredAnnotations() | 적용된 모든 어노테이션을 리턴 |
'JAVA 개념 > Part 03; 라이브러리 활용' 카테고리의 다른 글
[이것이 자바다] CHAPTER 14. 멀티 스레드 (2) | 2024.10.19 |
---|---|
[이것이 자바다] CHAPTER 13. 제네릭 | 개념 (0) | 2024.10.18 |