6.1.1 객체란?
- 객체는 속성과 동작으로 구성되어 있다.
- 자바에서는 속성을 필드(field)라고 부르고, 동작은 메소드(method)라고 부른다.
6.1.2 객체의 상호작용
- 객체들 사이의 상호작용 수단은 method이다.
- 객체의 field와 method에 접근할 때는 도트(.) 연산자를 사용한다.
6.1.3 객체 간의 관계
- 객체는 다른 객체와 관계를 맺을 때 그 가치가 높아진다.
- 관계의 종류에는 집합 관계, 사용 관계, 상속 관계가 있다.
- 자동차는 엔진, 타이어, 핸들 등으로 구성되어 있는 것은 집합 관계,
- 사람이 자동차를 사용하는 것은 사용 관계,
- 자동차는 기계의 종류인 것은 상속관계이다.
6.1.4 객체 지향 프로그래밍의 특징
- 1. 캡슐화
- - 객체의 field, method를 하나로 묶고 실제 구현 내용을 감추는 것을 말한다.
- - 외부 객체는 객체의 내부 구조를 알지 못하며, 객체가 노출해서 제공하는 field와 method만을 사용할 수 있다.
- - 접근제한자를 사용하여 field와 method의 사용범위를 제한하여 외부로부터의 접근을 차단한다.
- 2. 상속
- - 부모가 가지고 있는 field와 method를 자식에게 물려주는 것을 말한다.
- - 코드의 중복을 줄여준다.
- - 상위 개체의 수정으로 모든 하위 객체들의 수정 효과를 가져오기 때문에 유지보수가 쉬워진다.
- 3. 다형성
- - 같은 타입이지만 실행 결과가 다양한 객체를 이용할 수 있는 성질을 말한다.
- - 자바는 다형성을 위해 부모 클래스 또는 인터페이스의 타입 변환을 허용한다.
- - 부모 타입에는 모든 자식 객체가 대입될 수 있고, 인터페이스 타입에는 모든 구현 객체가 대입될 수 있다.
6.2 객체와 클래스
- 객체를 생성하기 위한 설계도가 클래스이다.
- 클래스에서는 field와 method가 정의되어 있다.
- 클래스로부터 만들어진 객체를 해당 클래스의 인스턴스(instance)라고 한다.
6.3 클래스 선언
- 자바에서의 클래스의 이름을 작성할 때 첫 글자는 대문자를 사용하는 것이 관례이다.
- 클래스 이름과 동일한 '클래스이름.java' 파일을 생성해야 한다.
- 일반적으로 소스파일 하다당 하나의 클래스를 선언한다.
- 클래스가 선언된 소스 파일을 컴파일하면 .class의 바이트 코드 파일이 생성된다.
6.4 객체 생성과 클래스 변수
- new 키워드를 사용하여 객체를 생성한다. 그리고 생성된 객체의 주소를 리턴한다.
- new 연산자로 생성된 객체는 메모리의 heap 영역에 생성된다.
- 클래스의 용도는 1) 라이브러리 2) 실행용 이렇게 2가지가 있다.
- 프로그램에서 여러개의 클래스가 선언되었다면, 이 중 하나만 실행용의 클래스이고, 나머지는 라이브러리이다.
- 대부분의 객체 지향 프로그램은 라이브러리와 실행 클래스가 분리되어 있다.
6.5 클래스의 구성 멤버
- 필드(field)
- - 객체의 데이터가 저장되는 곳
- - 변수는 메소드와 생성자가 종료되면 자동 소멸되지만, 필드는 객체가 존재하는한 소멸되지 않는다.
- 생성자(constructor)
- - 객체 생성 시 초기화 담당
- - 필드를 초기화하거나, 메소드를 호출해서 객체를 사용할 준비를 한다.
- - 생성자의 이름은 클래스 이름과 동일하며, 리턴 타입이 없다.
- 메소드(method)
- - 객체의 동작에 해당하는 곳
- - 메소드는 필드를 읽고 수정하는 역할을 한다.
- - 다른 객체를 생성하는 역할을 하기도 한다.
- - 객체간의 데이터 전달 수단으로 사용한다.
- - 외부로부터 매개값을 받을 수 있다.
6.6 필드
- 객체의 데이터를 저장하는 곳이다.
6.6.1 필드 선언
- 필드 선언은 클래스 중괄호 블록 내 어디든 존재할 수 있다.
- 필드의 초기값은 선언 시 주어질 수도 있고, 생략될 수도 있다.
- 초기값이 지정되지 않은 필드는 객체 생성 시 자동으로 기본 초기값으로 설정된다.
- 참조타입 객체는 null로, boolean 타입의 field는 false로 초기화된다.
6.6.2 필드 사용
- 필드는 생성자와 모든 메소드에서 사용이 가능하다.
- 도트 연산자를 이용하여 객체의 field에 접근할 수 있다.
6.7 생성자
- 생성자는 new 연산자와 같이 사용되어 클래스로부터 객체를 생성할 떄 호출되어 객체의 초기화를 담당한다.
- 생성자를 실행시키지 않고 클래스로부터 객체를 만들 수 없다.
- 즉, 객체를 생성할 때 자동으로 생성자가 호출된다.
- 생성자가 성공적으로 실행되면 heap 영역에 객체가 생성되고 이 객체의 주소라 리턴된다.
- 리턴된 객체의 주소는 클래스 타입 변수에 저장되어 객체에 접근할 때 이용된다.
6.7.1 기본 생성자
- 모든 클래스는 생성자가 반드시 존재한다.
- 하나 이상의 생성자를 가질 수 있다.
- 명시적으로 생성자를 선언하지 않는다면, 컴파일러는 기본 생성자를 자동으로 추가한다.
- 명시적으로 생성자를 선언한 것이 하나라도 존재한다면, 컴파일러는 기본 생성자를 추가해주지 않는다.
6.7.2 생성자 선언
- 생성자는 method와 유사하게 생겼으나, 리턴 타입이 없고 클래스의 이름과 동일하다.
- 생성자 블록 내부에서는 객체 초기화 코드가 작성된다.
6.7.3 필드 초기화
- 생성자의 parameter의 이름과 field의 이름이 동일한 경우, this.field이름 이런 방식을 사용하여 field에 접근할 수 있다.
- this는 객체 자신의 참조이다.
6.7.4 생성자 오버로딩
- 생성자 오버로딩이란, 매개변수를 달리하는 생성자 여러개를 선언하는 것을 말한다.
- 매개변수의 타입과 개수, 그리고 순서가 동일한 경우 오버로딩으로 볼 수 없다.
6.7.5 다른 생성자 호출
- 생성자에서 다른 생성자를 호출할 때 this() 코드를 사용한다.
- this()는 자신의 다른 생성자를 호출하는 코드로, 반드시 생성자의 첫 줄에서만 허용된다.
6.8 메소드
- 메소드는 필드를 읽고 수정하는 역할
- 다른 객체를 생성해서 다양한 역할을 수행
- 객체 간의 데이터 전달의 수단
- 외부로부터 매개값을 받아 특정한 결과를 리턴하는 역할
6.8.1 메소드 선언
- 메소드 선언은 선언부(리턴타입, 메소드 이름, 매개변수 선언)와 실행 블록으로 구성된다.
- 메소드의 선언부를 메소드 시그니쳐라고도 한다.
- 메소드의 이름은 관례적으로 소문자로 시작한다.
- 매개변수의 수를 모를 경우에는 매개변수를 배열 타입으로 선언하는 것이 좋다.
package ch6_8;
public class Calculator {
int add(int x, int y) {
return x+y;
}
// 배열을 생성해서 넘겨주어야 하는 단점
int add1(int[] values){
int sum = 0;
for(int val: values) {
sum += val;
}
return sum;
}
// 메소드 호출 시 넘겨준 값의 수에 따라 자동적으로 배열이 생성된다!
int add2(int ... values) {
int sum = 0;
for(int val: values) {
sum += val;
}
return sum;
}
}
package ch6_8;
public class CalculatorExample {
public static void main(String[] args) {
// TODO Auto-generated method stub
Calculator calc = new Calculator();
System.out.println(calc.add(1, 2)); // 3
int[] array = {1, 2, 3, 4, 5};
calc.powerOn();
System.out.println(calc.add1(array));
System.out.println(calc.add2(1));
System.out.println(calc.add2(1, 2));
System.out.println(calc.add2(1, 2, 3));
System.out.println(calc.add2(1, 2, 3, 4));
System.out.println(calc.add2(1, 2, 3, 4, 5));
}
}
6.8.2 리턴문
- 메소드 선언에 리턴 타입이 있었다면 반드시 리턴해야 한다.
- 만약 return문이 없다면 컴파일 오류가 발생한다.
- 메소드 선언에 리턴타입이 void 이더라도 return; 을 사용할 수 있다. (이 경우 메소드를 강제 종료하는 효과)
6.8.3 메소드 호출
- 클래스 내부의 다른 메소드에서 호출할 경우에는 단순히 메소드 이름으로 호출
- 클래스 외부에서 호출할 경우에는 클래스로부터 객체를 생성한 뒤 참조 변수를 이용하여 메소드 호출
6.8.4 메소드 오버로딩
- 클래스 내에 같은 이름의 메소드를 여러개 선언하는 것을 메소드 오버로딩(overloading)일고 한다.
- 오버로딩의 사전적 의미는 많이 싣는 것을 말한다.
- 메소드 오버로딩의 조건은 매개변수의 타입, 개수, 순서 중 적어도 하나가 달라야 한다는 것이다.
- 타입, 개수, 순서가 동일하고 매개변수의 이름만 다른 경우에는 메소드 오버로딩이라고 볼 수 없다.
- 자바가상기계가 메소드를 선택할 때 매개변수의 이름 정보를 사용하지 않기 때문에 컴파일 오류까지 발생하게 된다.
6.9 인스턴스 멤버와 this (cf. 정적 멤버)
- 인스턴스 멤버란 객체를 생성한 후 사용할 수 있는 필드와 메소드를 말한다.
- 이것을 각각 인스턴스 필드, 인스턴스 메소드라고 부른다.
- (인스턴스 멤버에 인스턴스 필드, 인스턴스 메소드가 있다고 생각하면 될 것 같다)
- 인스턴스 필드와 인스턴스 메소드는 객체에 소속된 멤버이기 때문에 객체 없이는 사용할 수 없다.
- 인스턴스 필드는 객체마다 따로 존재하고 (=메모리 공간이 다르다)
- 인스턴스 메소드는 객체마다 존재하지 않고, 메소드 영역에 저장되고 공유된다 (=메모리 공간 공유)
6.10 정적 멤버와 static (cf. 인스턴스 멤버)
- 정적 멤버는 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메소드를 말한다.
- 이것을 각각 정적 필드, 정적 메소드라고 부른다. (위에서 언급한 인스턴스 필드와 인스턴스 메소드와는 다르다)
- 정적 필드와 메소드는 객체에 소속된 것이 아니기 때문에 클래스 멤버라고도 부른다.
6.10.1 정적멤버 선언
- 정적 필드와 정적 메소드를 선언하는 방법은 static 키워드를 추가적으로 붙여주는 것이다.
- 필드를 선언할 때 인스턴스 필드로 선언할지, 정적 필드로 선언할지 선택하는 기준은
- 객체마다 별도로 가지고 있어야 하는 필드라면 인스턴스 필드로, 객체끼리의 공용데이터로 사용한다면 정적 필드로 선언
- 인스턴스 필드를 사용한다면 인스턴스 메소드로, 그렇지 않다면 정적 메소드로 선언한다.
6.10.2 정적 멤버 사용
- 클래스이름.정적필드
- 클래스이름.정적메소드(argument값들)
- 이렇게 사용한다.
- 객체를 이용하여 정적 멤버, 정적 메소드에 접근할 수 있지만 권장하지 않는다.
6.10.3 정적 초기화 블록
- 정적(static) 필드는 필드 선언과 동시에 초기값을 주는 것이 보통이다.
- 정적 필드는 생성자에서 초기화할 수 없고, 정적 블록 내에서 초기화가 가능하다.
- 정적 블록은 클래스가 메모리에 로딩될 때 자동적으로 실행된다.
public class Television {
static String company = "Samsung";
static String model = "LCD";
static String info;
// static block
static {
info = company + "-" + model;
}
}
6.10.4 정적 메소드와 블록 선언 시 주의할 점
- 정적 메소드와 정적 블록안에서 인스턴스 필드나 인스턴스 메소드를 사용할 수 없다.
- 정적 메소드와 정적 블록에서 인스턴스 멤버를 사용하고 싶다면, 이 안에서 객체를 생성하고 접근해야 한다.
static void Method(){
ClassName obj = new ClassName();// 객체 생성
obj.field = 10; // 인스턴스 필드 접근
obj.method(); // 인스턴스 메소드 접근
}
6.10.5 싱글톤 (singleton)
- 전체 프로그램 내에서 단 하나의 객체만 존재해야 하는 경우가 있는데, 이렇게 생성된 단 하나의 객체를 싱글톤이라고 한다.
- 싱글톤을 만들려면 클래스 외부에서 new 연산자를 사용하여 생성자를 호출할 수 없도록 막아야 한다.
- 생성자를 외부에서 호출할 수 없도록 하려면 생성자 앞에 private 접근제한자를 붙여주면 된다.
- 자신의 타입인 정적 필드를 하나 선언하고 자신의 객체를 생성해 초기화한다.
- 정적 필드도 private 접근제한자를 붙여 외부에서 필드값에 접근하지 못하도록 막는다.
- 대신 외부에서 호출할 수 있는 정적 메소드를 만들어 정적 필드에서 참조하고 있는 자신의 객체를 리턴한다.
public class Singleton {
// private -> 외부에서 Singleton.singleton 이렇게 접근하지 못하도록 제어
private static Singleton singleton = new Singleton();
// 외부에서 생성자를 호출하지 못하도록 private 접근제한자
private Singleton() {
}
// static method
static Singleton getInstance() {
return singleton;
}
}
public class SingletonExample {
public static void main(String[] args) {
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
System.out.println(obj1 == obj2); // true
System.out.println(obj1 != obj2); // false
}
}
6.11.1 fianl 필드 (!= 상수)
- final 필드는 초기값이 지정되면 수정할 수 없다.
- final 필드의 초기값을 주는 방법은 아래와 같이 2가지만 존재한다.
- 1) 필드 선언 시 주는 방법
- 2) 생성자에서 주는 방법
public class Person {
final String nation = "Korea"; // final 필드 초기화하는 방법1
final String ssn;
String name;
public Person(String ssn, String name) {
this.ssn = ssn; // final 필드 초기화하는 방법2
this.name = name;
}
}
6.11.2 상수 (static final)
- final 필드를 상수라고 부르지 않는다.
- 상수는 객체마다 저장할 필요가 없는 공용성을 띄어야 하며, 여러가지 값으로 초기화될 수 없어야 하기 때문이다.
- final 필드는 객체마다 저장되고, 생성자를 통해 여러가지 값으로 초기화될 수 있기 때문에 상수라고 부르지 않는다.
- 상수는 static final 이어야 한다. (객체마다 저장되지 않는 공용성 + 하나의 값으로만 초기화)
public class Earth {
// 상수 (1. 공용 + 2. 하나의 값으로만 초기화)
static final double EARTH_RADIUS = 6400;
static final double EARTH_SURFACE_AREA;
// static block
static {
EARTH_SURFACE_AREA = 4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS;
}
}
6.12 패키지
- 자바에서 클래스를 체계적으로 관리하기 위해 패키지를 사용한다.
- 폴더를 만들어 파일을 관리하듯이 패키지를 만들어 클래스를 관리한다.
- 패키지는 클래스를 유일하게 만들어주는 식별자 역할을 한다.
- 클래스 이름이 동일하더라도 패키지가 다르면 다른 클래스로 인식한다.
- (파일이름이 동일해도 위치가 다르면 구분되는 것과 유사)
- 클래스의 전체 이름은 "패키지명+클래스명"인데,
패키지가 상/하위로 구분되어 있다면 상위패키지.하위패키지.클래스명 이렇게 표현한다.
6.12.1 패키지 선언
- 패키지 선언은 가장 윗줄에 package 상위패키지.하위패키지; 코드를 추가해주면 된다.
- 패키지는 클래스를 컴파일하는 과정에서 자동으로 생성되는 폴더이다.
- 여러 회사가 참여하는 대규모 프로젝트에서는 도메인 이름을 사용하여 패키지 이름을 작성한다.
- 이 때 도메인 이름 역순으로 패키지 이름을 지어주는데, 그 이유는 포괄적인 이름이 상위 패키지가 되도록 하기 위함이다.
- ex) com.samsung.projectname / com.hyundai.projectname
- 패키지 이름은 모두 소문자로 작성하는 것이 관례이다.
6.12.2 패키지 선언이 포함된 클래스 컴파일
> javac -d 패지지가_생성될_경로 자바파일.java
> javac -d ../sub Car.java
6.12.3 이클립스에서 패키지 생성과 클래스 생성
- 패키지 생성 시 Name 입력칸에 상위 패키지와 하위 패키지를 도트로 구분해서 입력하면 된다.
6.12.4 import문
- 같은 패키지에 속하는 클래스는 아무 조건없이 다른 클래스를 사용할 수 있지만, 다른 패키지에 속하는 클래스를 사용하기 위해서는 두 가지 방법 중 하나를 선택해야 한다.
- 첫 번째 방법은 패키지와 클래스를 모두 기술하는 것이다.
package ch6_12.sub2;
public class PersonExample {
public static void main(String[] args) {
Person person1 = new Person("kim", 20);
ch6_12.sub1.Person person2 = new ch6_12.sub1.Person("jeong", "kim");
}
}
- 두 번째 방법은 import문을 사용하는 것이다.
- 사용하고자 하는 패키지를 import문으로 선언하고 클래스를 사용할 때에는 패키지를 생략하는 것이다.
- import문이 작성되는 위치는 패키지 선언과 클래스 선언 사이이다.
import java.util.Scanner;
java 패키지 속 util 패키지 속 Scanner라는 클래스를 가져오겠다는 의미이다.
6.13 접근 제한자
- 라이브러리 클래스를 설계할 때에는 외부 클래스에서 접근할 수 있는 멤버와 접근할 수 없는 멤버로 구분하는 것이 필요하다.
- 객체 생성을 막기 위해 생성자를 호출하지 못하게 하거나, 특정 메소드를 호출할 수 없도록 제한하는 것도 위와 같은 이유이다.
- 자바에서는 이러한 기능을 구현하기 위해 접근제한자를 제공하고 있다.
- 접근제한자로는 public, protected, default, private 이렇게 4가지 종류가 있다.
- public, protected, default, private에서 오른쪽으로 갈수록 접근 제한이 강하다.
- 1. public
- -모든 외부 클래스에서 클래스, 필드, 생성자, 메소드 호출할 수 있다.
- 2. protected
- - 자식 클래스가 아닌 다른 패키지에 소속된 클래스는 필드, 생성자, 메소드에 접근할 수 없다.
- - 같은 패키지에 속하건, 다른 패키지에 속하더라도 자식 클래스라면 접근 가능
- 3. default
- - 다른 패키지에 소속된 클래스는 클래스, 필드, 생성자, 메소드에 접근할 수 없다.
- - 같은 패키지에 속해야만 접근 가능 (자식이지만, 다른 패키지에 속한다면 접근 X - protected와의 차이점)
- 4. private
- - 모든 외부 클래스에서 필드, 생성자, 메소드에 접근할 수 없다.
6.13.1 클래스의 접근 제한
- 클래스를 선언할 때 고려할 사항은 같은 패키지 내에서만 사용할 것인지, 아니면 다른 패키지에서도 사용할 것인지 결정하는 것이다.
- 클래스에 적용할 수 있는 접근제한은 public, defaut 단 두 가지이다.
- default 접근제한자
- - 클래스를 선언할 때 public를 생략했다면 default 접근 제한을 가진다.
- - 같은 패키지에서는 아무런 제한없이 사용할 수 있지만 다른 패키지에서는 사용할 수 없다.
- public 접근제한자
- - 클래스를 선언할 때 public을 명시적으로 적어주어야 한다.
- - 같은 패키지이든, 다른 패키지이든 아무런 제한 없이 사용할 수 있다.
- - 다른 개발자들이 사용할 수 있도록 개발하는 라이브러리 클래스는 모두 public 접근제한자이다.
6.13.2 생성자의 접근 제한
- 객체를 생성하기 위해서는 new 연산자를 이용해야 한다.
- 하지만 생성자를 어디에서나 호출할 수 있는 것은 아니다.
- 생성자가 어떠한 접근제한자를 갖느냐에 따라 new 연산자를 이용해 객체를 생성할 수 있는지가 달라진다.
- 어떠한 생성자도 선언하지 않았을 때 컴파일러가 자동으로 생성해주는 기본생성자는 클래스의 접근제한자와 동일한 접근제한자를 갖는다.
6.13.3 필드와 메소드의 접근 제한
- 필드와 메소드를 선언할 때 클래스 내부에서만 사용할 것인지, 패키지 내에서만 사용할 것인지, 다른 패키지에서도 사용할 것인지 결정해야 한다.
- public
- - 모든 패키지에서 제한없이 필드와 메소드에 접근할 수 있다.
- - 필드와 메소드가 public 접근제한자를 갖는다면, 클래스의 접근제한자 역시 public이어야 한다.
- protected
- - protected 접근제한은 default 접근제한과 마찬가지로 같은 패키지에 속하는 클래스에서 필드와 메소드를 사용할 수 있다.
- - 다만, 다른 패키지에 속한 클래스라 하더라도 해당 클래스의 자식 클래스라면 필드와 메소드를 사용할 수 있다.
- default
- - 접근제한자를 기재하지 않았다면 default 접근제한자를 갖는다.
- - 같은 패키지에 속한다면 필드와 메소드에 접근할 수 있으나, 그렇지 않다면 접근 불가능하다.
- - 자식 클래스라 하더라도, 다른 패키지에 속해있다면 접근 불가능하다.
- private
- - 동일 패키지든, 다른 패키지든 필드와 메소드에 접근할 수 없다.
- - 오로지 그 클래스 내부에서만 사용이 가능하다.
6.14 Getter와 Setter 메소드
- 일반적으로 객체지향 프로그래밍에서 접근제한자를 private로 설정하여 외부에서 객체의 데이터에 접근하는 것을 막는다. (객체의 무결성을 유지하기 위함)
- 그 대신 데이터에 접근할 수 있는 getter, setter 메소드를 제공한다. (또한 이 방법을 선호한다!)
- setter 메소드에서는 수정하고자 하는 데이터가 조건을 충족하는지 검사할 수 있다.
public class Car {
// field
private int speed;
private boolean stop;
// constructor
// method
public int getSpeed() {
return this.speed;
}
public void setSpeed(int speed) {
this.speed = speed < 0 ? 0 : speed;
}
public boolean getStop() {
return this.stop;
}
public void setStop(boolean stop) {
this.stop = stop;
this.speed = 0;
}
}
6.15 어노테이션
- 어노테이션(annotation)은 소스코드에 주는 메타데이터(metadata)라고 볼 수 있다.
- 메타데이터는 application이 처리해야 할 데이터가 아니라, 컴파일 과정과 실행 과정에서 코드를 어떻게 컴파일하고 처리할 지 알려주는 정보이다.
- 어노테이션의 3가지 용도는 다음과 같다.
- - 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공
- - 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보 제공
- - 실행 시 특정 기능을 실행하도록 정보 제공
6.15.1 어노테이션 타입 정의와 적용
@Override
void method(){
....
}
- 어노테이션은 element를 멤버를 가질 수 있다.
- 각 element는 타입과 이름으로 구성되며, default 값을 가질 수 있다.
// annotation 선언
public @interface Author{
String name();
String date();
}
// annotation 사용
@Author(
name = "Benjamin Franklin",
date = "3/27/2003"
)
class MyClass(){
...
}
- value element를 가진 어노테이션을 코드에서 사용할 때는 다음과 같이 값만 기술할 수 있다.
@SuppressWarnings(value="unchecked")
@SuppressWarnings("unchecked")
6.15.2 어노테이션 적용 대상
어노테이션을 적용할 수 있는 대상은 java.lang.annotaiton.ElementType 열거 상수로 다음과 같이 정의되어 있다.
ElementType 열거상수 | 적용 대상 |
TYPE | 클래스, 인터페이스, 열거 타입 |
ANNOTATION_TYPE | 어노테이션 |
FIELD | 필드 |
CONSTRUCTOR | 생성자 |
METHOD | 메소드 |
LOCAL_VARIABLE | 로컬 변수 |
PACKAGE | 패키지 |
- 어노테이션이 적용될 대상을 지정할 때에는 @Target 어노테이션을 사용한다.
- @Target의 기본 앨리먼트인 value은 ElementType 배열을 값으로 가진다.
- 이것은 어노테이션이 적용될 대상을 복수로 지정하기 위해서이다.
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
public @interface AnnotationName{
}
...
@AnnotationName
public class ClassName{
@AnnotationName
private String fieldName;
// @AnnotationName // Target에 constructor가 없어 생성자는 적용 못함
pbulic ClassName(){
}
@AnnotationName
public void methodName(){
}
}
6.15.3 어노테이션 유지 정책
- 어노테이션을 소스에만 유지할 것인지, 컴파일된 클래스까지 유지할 것인지, 런타임 시에도 유지할 것인지 지정해야 한다.
- 유지 정책은 java.lang.annotation.RetentionPolicy 열거상수로 다음과 같이 정의되어 있다.
RetentionPolicy 열거상수 | 설명 |
SOURCE | 소스상에서만 어노테이션 정보를 유지한다. 소크토드를 분석할 때만 의미가 있으며 바이트 코드 파일에는 정보가 남지 않는다. |
CLASS | 바이트 코드 파일까지 어노테이션 정보를 유지한다. 리플렉션을 이용해서 어노테이션 정보를 얻을 수는 없다. |
RUNTIME | 바이트 코드 파일까지 어노테이션 정보를 유지한다. 리플렉션을 이용해서 어노테이션 정보를 얻을 수 있다. |
- 리플렉션이란 러낱임 시에 클래스의 메타 정보를 얻는 기능을 말한다.
- 클래스가 가지고 있는 필드는 무엇인지, 어떠한 생성자를 갖고 있는지, 어떠한 메소드를 가지고 있는지, 적용된 어노테이션이 무엇인지 알아내는 것이 리플렉션이다.
- 리플렉션을 이용해 런타임 시에 어노테이션 정보를 얻으려면 어노테이션 유지 정책을 RUNTIME으로 설정해야 한다.
- 어노테이션 유지 정책을 지정하기 위해서는 @Retention 어노테이션을 사용하면 된다.
- @Retention의 기본 element인 value는 RetentionPolicy 타입이므로 위 세 가지 상수 중 하나를 지정하면 된다.
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationName{
}
6.15.4 런타임 시 어노테이션 정보 사용하기
- 클래스에 적용된 어노테이션 정보를 얻으려면 java.lang.Class를 이용하면 된다.
- 필드, 생성자, 메소드에 적용된 어노테이션 정보를 얻으려면 java.lang.reflect 패키지의 Field, Constructor, Method 타입의 배열을 얻어야 한다.
리턴 타입 | 메소드명 (매개변수) | 설명 |
Field[] | getFields() | 필드 정보를 Field 배열로 리턴 |
Constructor[] | getConstrcutors() | 생성자 정보를 Constructor 배열로 리턴 |
Method[] | getDeclareMethods() | 메소드 정보를 Method 배열로 리턴 |
그 후 Class, Field, Constructor, Method가 가지고 있는 메소드를 호출하여 어노테이션 정보를 얻을 수 있다.
리턴 타입 | 메소드명 (매개변수) | 설명 |
boolean | isAnnotationPresent(Class<? extends Annotation> annotationClass) | 지정한 어노테이션이 적용되었는지 여부 Class에서 호출했을 때 상위 클래스에 적용된 경우에도 true 리턴 |
Annotation | getAnnotation(Class<T> annotationClass) | 지정한 어노테이션이 적용되어 있으면 어노테이션을 리턴하고 그렇지 않다면 null을 리턴한다. Class에서 호출했을 때 상위 클래스에 적용된 경우에도 true 리턴 |
Annotation[] | getAnnotations() | 적용된 모든 어노테이션을 리턴한다. Class에서 호출했을 때 상위 클래스에 적용된 어노테이션도 모두 포함한다. 적용된 어노테이션이 없을 경우 길이가 0일 배열을 리턴한다. |
Annotation[] | getDeclaredAnnotations() | 직접 적용된 어노테이션을 리턴한다. Class에서 호출했을 때 상위 클래스에 적용된 어노테이션은 포함되지 않는다. |
PrintAnnotation.java
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintAnnotation {
String value() default "-";
int number() default 15;
}
Service.java
public class Service {
@PrintAnnotation
public void method1() {
System.out.println("실행 내용1");
}
@PrintAnnotation("*")
public void method2() {
System.out.println("실행 내용2");
}
@PrintAnnotation(value="#", number=20)
public void method3() {
System.out.println("실행 내용3");
}
PrintAnnotationExample.java
package ch6_15;
import java.lang.reflect.*;
public class PrintAnnotationExample {
public static void main(String[] args) {
// TODO Auto-generated method stub
Method[] declaredMethods = Service.class.getDeclaredMethods();
for(Method method : declaredMethods) {
if(method.isAnnotationPresent(PrintAnnotation.class)) {
PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class);
System.out.println("["+method.getName()+"]");
for(int i=0; i<printAnnotation.number(); i++) {
System.out.print(printAnnotation.value());
}
System.out.println();
try {
method.invoke(new Service());
}
catch(Exception e) {
}
System.out.println();
}
}
}
}
실행결과
출처 : 이것이 자바다
'다전공_컴퓨터공학 > C, JAVA, PYTHON' 카테고리의 다른 글
[Python] nonlocal (0) | 2021.01.08 |
---|---|
[JAVA] 3장 연산자 (0) | 2021.01.06 |
[JAVA] 2장 변수와 타입 (0) | 2021.01.05 |
[C] struct, union (0) | 2020.12.03 |
[C] volatile (0) | 2020.12.01 |