Part 1
객체 지향 프로그래밍
•
Object Oriented Programming ( OOP )
•
현실 : 부품을 조립하여 하나의 완성품을 만든다.
•
객체 지향 프로그래밍 : 객체를 만들고 조립하여 하나의 완성된 프로그램을 만드는 기법.
객체란?
•
물리적으로 존재하거나 추상적으로 생각할 수 있는 것 중에서 자신의 속성을 가지고 있고 다른 것과 식별 가능한 것
◦
자동차, 자전거, 책, 사람
◦
학과, 주문, 강의, 업무
•
객체는 속성과 동작으로 구성되어 있다.
◦
사람 : 속성(나이, 이름), 동작(잔다, 먹는다)
◦
자동차 : 속성(배기량, 색상), 동작(달리다, 멈추다)
◦
자바 : 속성(필드), 동작(메소드)
•
객체 모델링 : 현실의 객체를 소프트웨어 객체로 설계하는 것
◦
현실의 속성, 동작 → 소프트웨어 필드, 메소드
객체의 상호작용
•
현실 세계의 모든 객체는 상호작용을 한다.
◦
나는 컴퓨터를 통해서 유튜브를 본다 → '나' - '컴퓨터' 간의 상호작용
•
소프트웨어의 독립적인 객체들이 서로 상호작용을 한다.
◦
상호작용 수단 = 메소드
◦
객체가 다른 객체의 기능을 이용 = 메소드 호출
•
메소드 호출
◦
객체 . 메소드
리턴값 = 객체.메소드(매개값1, 매개값2, ...);
Java
복사
•
리턴값 : 메소드가 실행되고난 이후 돌려주는 값
•
매개값 : 메소드를 실행하기 위해 필요한 값
객체 간의 관계
•
객체는 개별적으로 사용할 수 있지만, 보통 다른 객체와 관계를 맺고 있다.
•
집합 관계, 사용 관계, 상속 관계
•
집합 관계 : 객체와 객체를 구성하는 객체의 관계 ( 완성품 - 부품 )
•
사용 관계 : 서로 상호작용을 하는 객체의 관계 ( 사람 - 자동차 )
•
상속 관계 : 객체를 기반으로 생성된 하위 객체의 관계 ( 부모 - 자식 )
객체 지향 프로그래밍의 특징
캡슐화(Encapsulation)
•
필드, 메소드를 하나로 묶고 실제 구현 내용을 감추는 것
•
외부의 객체는 내부를 알 수 없고 의도적으로 드러낸 필드, 메소드만 이용할 수 있다.
•
외부의 잘못된 사용으로 객체가 손상되지 않기위한 목적
•
접근 제한자 : 캡슐화된 객체의 멤버(필드, 메소드)를 드러낼 것인지, 숨길 것인지 결정
상속(Inheritance)
•
부모의 재산을 자식이 물려받는 것
•
상위 객체의 필드와 메소드를 하위 객체가 물려받는 것
•
상위 객체의 재사용을 통해서 하위 객체를 쉽고 빠르고 중복을 최소화하여 설계
•
상위 객체의 수정으로 하위 객체에 영향을 주어 유지 보수가 용이함
다형성(Polymorphism)
•
동일한 타입으로부터 실행 결과가 다양한 객체를 이용할 수 있는 성질
•
자바는 다형성을 위해 부모 클래스 또는 인터페이스의 타입 변환을 허용
•
부모 타입에 모든 자식 객체 대입 가능
•
인터페이스 타입에 모든 구현 객체 대입 가능
•
객체의 부품화
객체와 클래스
•
클래스 : 객체를 생성하는데 필요한 필드와 메소드를 정의
•
클래스로부터 만들어진 객체 == 클래스의 인스턴스
•
클래스로부터 객체를 만드는 과정 = 인스턴스화
•
OOP 개발 단계
1.
클래스를 설계한다.
2.
설계한 클래스로 객체를 생성한다.
3.
생성한 객체를 사용한다.
클래스 선언
•
클래스의 이름 작성 규칙
1.
하나 이상의 문자
2.
첫 번째 글자로 숫자 불가능
3.
$와 _ 이외 특수문자 불가능
4.
자바 예약어 불가능
•
첫번째 문자는 대문자 ( 관례 )
•
클래스명과 동일한 파일명 작성 : Person 클래스 ⇒ Person.java
// Person.java
public class Person {
// 필드
// 메소드
}
// 하나의 파일에 두 개의 클래스가 선언되면?
class Male {
}
// 컴파일 시, 각 클래스명.class로 생성
// 파일명과 동일한 클래스만 public 접근 제한자 붙임
// 가급적 하나의 소스 파일에 하나의 클래스만 선언한다.
Java
복사
객체 생성과 클래스 변수
•
객체 생성 : new 클래스() → 생성자
•
new 연산자를 사용하여 객체를 생성하면 메모리의 힙 영역에 할당된다 → 객체의 주소를 반환한다.
•
참조 타입인 클래스 변수에 new 연산자가 반환한 객체의 주소를 저장
클래스 변수;
변수 = new 클래스();
or
클래스 변수 = new 클래스();
Java
복사
•
같은 클래스로부터 생성된 되었을지라도 new 연산자를 사용하여 생성된 객체는 서로 독립적이다.
•
라이브러리 클래스 / 실행 클래스
public class Student{
public static void main(String[] args){ // main() 메소드를 제공하는 Student 클래스
Study java = new Study(); // 다른 클래스에서 사용될 목적으로 설계된 Study 클래스
}
}
Java
복사
클래스의 구성 멤버
•
필드, 생성자, 메소드 : 생략되거나 여러개 일 수 있다.
필드
•
객체의 고유 데이터, 부품 객체, 상태 정보를 저장하는 곳
•
선언 형태는 변수와 비슷하나 변수라고 부르지 않는다.
◦
변수 : 생성자와 메소드 내에서만 사용됨 → 실행 종료시 소멸
◦
필드 : 생성자와 메소드 모두에서 사용됨 → 객체와 함께 유지
생성자
•
객체 생성 시 new 연산자로 호출되는 특별한 중괄호 블록
•
필드 초기화 , 메소드 호출로 객체를 사용할 준비 담당
•
메소드와 비슷한 형태
•
클래스 이름으로 되어있고 리턴 타입이 없다.
메소드
•
객체의 동작에 해당하는 중괄호 블록
•
메소드를 호출하면 중괄호 블록 내의 코드가 실행
•
필드를 읽고 수정 + 다른 객체를 생성하여 다양한 기능을 수행하기도 함
•
객체 간의 데이터 전달의 수단으로 사용
•
외부로 부터 매개값을 받거나, 실행 후 어떤 값을 반환할 수도 있다.
필드
•
객체가 가지는 고유 데이터, 상태 데이터, 부품 객체를 저장하는 곳
필드 선언
•
클래스의 중괄호 안에 존재하며 생성자, 메소드의 앞과 뒤 어디든 선언이 가능하다.
•
생성자와 메소드 안에서 선언된 것은 로컬 변수가 된다.
•
변수 선언과 유사하여 클래스 멤버 변수라고도 부른다.
•
기본 타입, 참조 타입
•
초기값은 선언 시 주어질 수도, 생략될 수도 있다.
•
초기화하지 않은 필드는 객체 생성시 자동으로 기본 초기값으로 초기화된다.
◦
정수 : 0, 실수 : 0.0, boolean : false, 참조 : null
필드 사용
•
필드를 사용하는 것은 필드값을 읽고, 변경하는 작업
•
객체 내부의 생성자와 메소드는 필드의 이름으로 읽고 변경
•
클래스 외부에서 사용할 경우, 우선 객체를 생성한 뒤 필드를 사용해야 한다 → 객체가 없으면 필드도 없다.
•
도트 연산자를 사용하여 객체 필드 사용 → 객체 . 필드
생성자
•
new 연산자와 함께 사용되어 클래스로부터 객체를 생성할 때 호출 → 객체의 초기화 담당
•
객체 초기화 : 필드를 초기화하거나, 메소드를 호출하여 객체를 사용할 준비를 하는 것
•
생성자를 실행시키지 않고는 클래스로부터 객체를 생성할 수 없다.
•
new 연산자와 함께 생성자가 실행되면 힙 영역에 객체가 생성되고 객체의 주소가 반환된다. 반환된 주소는 클래스 타입 변수에 저장되어 ( 참조 타입 ) 객체에 접근할 때 사용된다.
기본 생성자
•
모든 클래스는 무조건 하나 이상의 생성자를 갖는다.
•
클래스 내부에 생성자를 생략했다면 컴파일 시 기본 생성자를 바이트 코드(.class)에 추가한다.
•
기본 생성자 : 중괄호 {} 블록이 비어있는 생성자
•
클래스에 명시적으로 선언한 생성자가 있는 경우, 컴파일러는 기본 생성자를 추가하지 않는다.
Person me = new Person(); // 기본 생성자
Java
복사
생성자 선언
•
생서자는 메소드와 비슷한 형태를 갖지만 리턴 타입이 없고 클래스 이름과 동일하다.
•
생성자 선언시 매개변수를 추가하여 외부의 값을 생성자 블록 내부로 전달할 수 있다.
•
클래스에 매개변수가 존재하는 생성자를 명시적으로 선언한 경우, 객체 생성시 반드시 동일한 매개변수 타입과 순서로 생성자를 호출해야 한다 → 기본 생성자 사용 불가능
public class Car {
Car(String color, int cc){ // 매개변수가 있는 생성자 선언
...
}
}
Java
복사
public class CarExample {
Car myCar = new Car(); // 사용 불가능
Car myCar = new Car("black", 3000); // 사용 가능
}
Java
복사
필드 초기화
•
클래스로 부터 객체가 생성될 때, 필드는 기본 초기값으로 초기화된다.
•
다른 방법으로 필드를 초기화 하는 경우 → 선언과 동시에 초기화 or 생성자에서 초기화
•
필드 선언과 동시에 초기화 : 동일한 클래스로부터 생성된 객체들은 생성 시점에서 필드의 값이 동일
•
생성자에서 초기화 : 객체 생성 시점에서 외부로 부터 매개 변수로 값을 받아 필드 초기화
◦
초기화 하고자 하는 필드 명과 생성자의 매개 변수 명을 동일하게 하는 것이 관례
▪
생성자 내부에서 필드에 접근 불가능 → 동일한 이름의 매개 변수의 우선순위가 더 높다
▪
객체 스스로를 가리키는 'this'를 사용하여 필드에 접근
public class Korean{
String name;
String ssn;
public Korean(String name, String ssn){
name = name; // 필드와 매개변수 이름 동일. 필드 접근 불가
this.name = name; // 클래스로 부터 생성된 객체 자신 -> this 참조 타입 변수로 필드를 사용
}
}
Java
복사
생성자 오버로딩
•
외부에서 제공하는 다양한 데이터를 이용하여 객체를 초기화 하기위해 생성자도 다양화될 필요가 있다.
•
매개 변수의 타입과 순서가 다른 여러개의 생성자를 선언하는 것
public class Person{
String name;
int age;
public Person(){
}
public Person(String name){
this.name = name;
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
Java
복사
•
매개 변수의 타입과 개수, 순서가 똑같을 경우 매개 변수 이름만 바꾸는 것은 생성자 오버로딩이 아니다.
•
생성자가 오버로딩 된 경우, new 연산자로 생성자를 호출할 때, 외부에서 입력된 매개값의 타입과 개수, 순서에 의해 호출될 생성자를 결정한다.
다른 생성자 호출
•
생성자 오버로딩이 많아 질 경우, 생성자 간에 중복된 코드가 많아질 수 있다.
•
매개 변수만 다르고 필드 초기화 내용이 비슷한 생성자에서 자주 보인다.
•
this() : 자신의 다른 생성자를 호출하는 코드로 반드시 생성자의 첫줄에서만 사용한다.
•
this() 의 매개값은 호출할 생성자의 매개 변수 타입, 개수, 순서에 맞게 제공해야 하고 this() 코드 뒤에 추가적인 코드 작성이 가능하다.
String model;
String color;
int maxSpeed;
Car(String model){
this.model = model;
this.color = "silver";
this.maxSpeed = 200;
}
Car(String model, String color){
this.model = model;
this.color = color;
this.maxSpeed = 200;
}
Car(String model, String color, int maxSpeed){
this.model = model;
this.color = color;
this.maxSpeed = maxSpeed;
}
Java
복사
Car(String model){
this(model, "silver", 200);
}
Car(String model, String color){
this(model, color, 200);
}
Car(String model, String color, int maxSpeed){
this.model = model;
this.color = color;
this.maxSpeed = maxSpeed;
}
Java
복사
메소드
•
객체의 동작에 해당하는 부분
•
객체의 필드를 읽고 변경하거나 다른 객체를 생성하여 다양한 기능을 수행하기도 한다.
•
객체 간의 데이터 전달의 수단으로 외부로부터 매개값을 전달 받을 수 있고, 실행 후 값을 반환할 수 있다.
메소드 선언
•
선언부 ( 리턴타입 + 메소드 이름 + 매개 변수 ) 와 실행 블록 {} 으로 구성.
•
메소드 선언부를 메소드 시그니처 라고도 한다.
리턴 타입
•
리턴값 : 메소드 실행 후 반환하는 값
•
메소드는 리턴값이 있을 수도, 없을 수도 있다.
•
리턴값이 없는 메소드는 리턴 타입에 void가 와야한다.
•
리턴 타입이 있는 메소드는 리턴값을 받는 변수가 있을 수도, 없을 수도 있다.
메소드 이름
•
숫자로 시작하면 안된다.
•
$와 _ 를 제외한 특수 문자를 사용하면 안된다.
•
관례적으로 소문자로 작성한다.
•
두개 이상의 단어로 작성 할 경우, 뒤의 단어의 첫 글자는 대문자로 작성한다.
•
메소드의 기능을 파악할 수 있도록 너무 짧지 않게 작성한다.
매개 변수 선언
•
메소드가 필요로 하는 외부값을 받기 위해 사용하는 변수
•
매개 변수가 필요할 수도, 필요 없을수도 있다.
•
메소드 호출 시 매개값은 메소드 선언 시 작성한 매개 변수의 타입과 순서와 동일해야 한다.
매개 변수의 수를 모를 경우
•
매개 변수를 배열 타입으로 선언한다.
◦
메소드를 호출하기 전, 배열을 생성해야한다.
•
매개 변수를 ' ... ' 를 사용해서 선언한다.
◦
메소드 호출 시 값의 리스트를 넘겨준다.
리턴(return)문
리턴값이 있는 메소드
•
메소드 선언 시 리턴 타입이 있는 경우, 반드시 return 문을 사용해서 리턴값을 반환한다.
•
return 문이 실행되면 메소드는 즉시 종료된다.
•
리턴값은 선언시 지정한 리턴 타입이거나 리턴 타입으로 변환되어야 한다.
◦
리턴 타입이 int 인 경우, 리턴값이 byte, char, short이면 자동으로 int로 변환이 된다.
•
동일한 블록 내에서 return 문의 뒤에는 다른 실행문이 올 수 없다.
리턴값이 없는 메소드(void)
•
리턴 타입이 void로 선언된 메소드도 return 문이 올 수 있다.
•
return 문이 실행되면 메소드는 즉시 종료된다.
메소드 호출
•
클래스 내부의 다른 메소드를 호출하는 경우 메소드 명으로 호출한다.
•
클래스 외부에서 메소드를 호출하는 경우, 객채를 생성하고 참조 변수를 사용해 메소드를 호출한다.
•
리턴 타입에 맞는 변수를 선언하여 리턴 값을 대입한다.
객체 내부에서 호출
•
메소드의 이름을 사용하고 매개 변수의 타입과 수에 맞게 호출한다.
객체 외부에서 호출
•
호출할 객체의 클래스로부터 객체를 생성한다.
•
객체 . 메소드 ( 매개변수 ... )
메소드 오버로딩
•
클래스 내에 동일한 이름의 메소드를 여러 개 선언하는 것
◦
메소드 명은 동일하고 매개 변수의 타입, 개수, 순서중 하나가 달라야 한다.
•
매개값을 다양하게 받아 처리하기 위해 필요하다.
•
오버로딩된 메소드를 호출하는 경우, JVM은 매개값의 타입을 보고 메소드를 선택한다.
◦
매개 변수 타입이 일치하지 않는 경우, 자동 형변환이 가능한 지 검사한다.
•
매개 변수의 순서와 타입, 개수가 똑같을 때, 매개 변수 이름과 리턴 타입이 다른 것은 오버로딩이 아니다.
◦
리턴 타입은 JVM이 메소드를 선택하는 기준이 아니기 때문
Part 2
인스턴스 멤버와 this
•
객체(인스턴스)를 생성한 후 사용할 수 있는 필드와 메소드
◦
필드 : 인스턴스 필드
◦
메소드 : 인스턴스 메소드
•
객체를 생성해야 사용할 수 있다.
◦
인스턴스 필드는 힙 영역의 각 개체에 존재
◦
인스턴스 메소드는 메소드 영역에 존재되어 공유
•
객체 자신을 가리키는 this
◦
인스턴스 필드의 이름과 생성자/메소드의 매개변수 이름이 동일한 경우
▪
this.이름 → 인스턴스 필드임을 명시
정적 멤버와 static
•
정적(Static)의 의미는 '고정된'
•
정적 멤버 : 클래스에 고정되어 객체를 생성하지 않고도 사용할 수 있는 멤버 ( 클래스 멤버 )
◦
정적 필드, 정적 메소드
정적 멤버 선언
public class 클래스 {
static 타입 필드
static 리턴타입 메소드
}
Java
복사
•
클래스 로더가 클래스(바이트 코드)를 로딩해서 메소드 메모리 영역에 적재할 때, 클래스 별로 관리
◦
클래스 로딩이 끝나면 바로 사용가능
•
필드 선언 기준
◦
객체마다 가지고 있어야 할 데이터 : 인스턴스 필드
◦
공용적인 데이터 : 정적 필드
◦
예 ) 계산기 클래스 : 원의 둘레를 구할 때 필요한 '파이'는 정적 필드로 선언
•
메소드 선언 기준
◦
인스턴스 필드를 사용하는 메소드 : 인스턴스 메소드
◦
인스턴스 필드를 사용하지 않는 메소드 : 정적 메소드
◦
예 ) 계산기 클래스 : 덧셈, 뺄셈 등 외부 데이터를 받아 수행하는 메소드는 정적 메소드 선언
정적 멤버 사용
•
클래스가 로딩되면 바로 사용 가능
◦
클래스명 . 정적멤버
•
클래스명으로 접근하지만 객체를 생성해서 접근도 가능
◦
클래스명으로 접근 권장
정적 초기화 블록
•
객체의 필드는 생성자 호출 시 초기화 → 객체를 생성하지 않고 사용하는 정적 필드는 ?
◦
정적 블록에서 초기화 수행
•
정적 블록은 클래스 로딩될 때 자동으로 수행
public class TV {
static String company = "Samsung";
static String model = "4K";
static String info;
static {
info = company + model + "70inch" ;
}
}
Java
복사
정적 메소드와 블록 선언 시 주의할 점
•
객체를 생성하지 않고 사용할 수 있는 특성
◦
정적 메소드와 블록 내부에 인스턴스 멤버를 사용할 수 없다.
◦
객체 자신을 가리키는 this 키워드 사용할 수 없다.
•
사용해야 하는 경우, 블록 안에서 객체를 생성한 후 사용. ( main() 메소드 포함 )
public class MyClass {
static int A;
int B;
static {
A = 1;
B = 2; // Compile error !
}
static void Method() {
MyClass myClass = new MyClass();
myClass.B = 2;
}
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.B = 3;
}
}
Java
복사
싱글톤(Singleton)
•
전체 프로그램에서 단 하나만 생성하는 객체
◦
클래스 외부에서 new 연산자로 객체 생성 불가
◦
클래스 내부에서 객체 타입의 정적 필드 선언 및 생성자 호출
◦
내부에서 생성한 객체를 반환하는 getInstance() 함수 선언
// Singleton.java
public class Singleton {
private static Singleton obj = new Singleton();
private Singleton(){
}
static Singleton getInstance(){
return obj;
}
}
Java
복사
// UseSingleton.java
public class UseSingleton {
public static void main(String[] args) {
Singleton obj1 = new Singleton(); // Compile Error !
Singleton obj2 = Singleton.getInstance();
Singleton obj3 = Singleton.getInstance();
if (obj2 == obj3) {
System.out.println("같은 객체를 참조");
}
}
}
Java
복사
final 필드와 상수
final 필드
•
final은 '최종적'이라는 의미
◦
저장된 초기값이 최종값 → 한 번 초기화 되면 값을 바꿀 수 없다
final 타입 필드명
Java
복사
•
초기값을 주는 방법
◦
선언과 동시에 초기화
◦
생성자를 통해 초기화
•
초기화 되지 않은 final 필드가 남아있으면 컴파일 에러 발생
상수(static final)
•
불변의 값
◦
final 필드와 같다 → final = 상수 ??
◦
final : 객체마다 다른 값
◦
상수 : 모든 객체에 공용 ( static 특성 )
static final 타입 상수명
static {
상수명 = 초기값;
}
Java
복사
•
상수명은 모두 대문자로 작성이 관례
•
여러 개의 단어는 언더바 ( _ )로 구분
패키지
•
여러 개의 클래스를 관리하기 위해서 사용
•
물리적인 형태는 파일 시스템과 동일
•
클래스 명이 동일하더라도 패키지가 다르면 다른 클래스로 인식
•
상위패키지.하위패키지.클래스명
패키지 선언
package 상위패키지.하위패키지
public class 클래스 {
}
Java
복사
•
컴파일 시 파일 시스템의 폴더를 자동 생성
•
패키지명 작성
◦
$와 _를 제외한 특수문자와 숫자로 시작 불가
◦
java 로 시작하는 패키지는 자바 표준 API이므로 사용 불가
◦
모두 소문자로 작성
◦
도메인의 역순으로 작성
패키지 선언이 포함된 클래스 컴파일
•
javac -d 경로 : 경로부터 선언된 패키지가 생성된다.
•
java 패키지경로.바이트코드 : 패키지 시작 경로에서 실행
import 문
•
다른 패키지에 속한 클래스를 사용하는 방법
◦
패키지와 클래스 명을 모두 작성
◦
import 패키지명
package com.mycompany;
public class Car {
com.youcompnay.Tire tire = new com.yourcompay.Tire();
}
Java
복사
package com.mycompany;
import com.yourcompany.Tire;
public class Car {
Tire tire = new Tire();
}
Java
복사
•
package 와 class 선언문 사이에 선언
•
패키지명 마지막에 * 를 작성하여 모든 클래스 사용
◦
하위 패키지에 포함된 클래스는 사용 불가
•
서로 다른 패키지에 동일한 이름의 클래스를 사용하는 경우에는 모든 패키지명을 작성해야한다.
접근 제한자
•
외부로부터 클래스의 멤버 접근을 제한하기 위해서 사용
•
public : 모든 외부에서 자유롭게 사용
•
protected : 같은 패키지 또는 자식 클래스에서 사용
•
private : 모든 외부에서 사용 불가
•
default : 같은 패키지에서 사용
클래스의 접근 제한
•
클래스 선언시 적용할 수 있는 접근 제한자는 default, public
•
default : public을 생략하면 적용됨. 같은 패키지에서만 사용.
•
public : 모든 외부에서 사용. 라이브러리 클래스는 public 선언.
생성자의 접근 제한
•
기본 생성자의 접근 제한은 클래스의 접근 제한과 동일
•
default : 생성자 선언 시 접근 제한자를 생략하면 적용
필드와 메소드의 접근 제한
•
public : 필드와 메소드가 public 제한자인 경우, 클래스도 public을 가져야 한다.
Getter와 Setter 메소드
•
객체의 데이터를 외부에서 접근하면 객체의 무결성이 깨질 수 있다.
•
객체 지향 프로그래밍은 외부에서 호출 가능한 메소드를 통해 데이터를 변경하는 방법을 선호.
◦
외부에서 넘기는 매개값을 검증
•
Setter : 메소드를 통해서 객체의 데이터 변경
•
Getter : 메소드를 통해서 객체의 데이터를 반환
public class Car {
private int speed = 10;
public void setSpeed(int speed){
if(speed < 0) {
this.speed = 0;
} else {
this.speed = speed;
}
}
public double getSpeed() {
double km = speed * 1.6;
return km;
}
private boolean run = false;
public boolean isRun(){
return run;
}
}
Java
복사
어노테이션
•
메타데이터 → 컴파일, 런타임 시 코드를 어떻게 처리할 것인지 정의
•
@Override : 메소드가 재정의 되었음을 컴파일러에게 알려 검사 수행
어노테이션 타입 정의와 적용
•
정의 : 접근제한자 @interface 어노테이션이름
•
사용 : @어노테이션이름
•
엘리먼트를 멤버로 갖는다.
◦
타입 엘리먼트이름()
◦
default 값
public @interface AnnoName {
String eleName();
int eleNum() default 5;
}
Java
복사
@AnnoName(eleName="엘리먼트");
@AnnoName(eleName="엘리먼트", eleNum=3);
// default 값이 없는 엘리먼트는 사용시 꼭 값을 작성해야한다.
Java
복사
•
어노테이션은 기본 엘리먼트인 value 멤버를 가질 수 있다.
public @interface AnnoName {
String value();
int eleNum() default 5;
}
Java
복사
@AnnoName("값");
// value 엘리먼트를 갖는 어노테이션 사용 시, 어노테이션 이름을 생략할 수 있다.
@AnnoName(value="값", eleNum=3);
Java
복사
어노테이션 적용 대상
•
java.lang.annotation.ElementType 열거 상수로 정의
◦
TYPE —— 클래스, 인터페이스, 열거 타입
◦
ANNOTATION_TYPE —— 어노테이션
◦
FIELD —— 필드
◦
CONSTRUCTOR —— 생성자
◦
METHOD —— 메소드
◦
LOCAL_VARIABLE —— 로컬 변수
◦
PACKAGE —— 패키지
•
적용 대상을 지정할 때 @Target 어노테이션을 사용
◦
기본 엘리먼트 vaule는 ElementType 배열을 값으로 갖는다. → 여러 타입의 대상 지정
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
public @interface AnnoName {
...
}
Java
복사
어노테이션 유지 정책
•
사용 용도에 따라 어노테이션을 어느 범위까지 적용할 것인지 지정
•
소스 범위, 컴파일 범위, 런타임 범위
•
java.lang.annotation.RetentionPolicy 열거 상수로 정의
◦
SOURCE —— 소스 범위. 바이트 코드에 기록되지 않음.
◦
CLASS —— 컴파일 범위. 바이트 코드에 정보 유지.
◦
RUNTIME —— 런타임 범위. 리플렉션을 이용해서 런타임 시 어노테이션 정보 사용.
▪
리플렉션 : 런타임 시 클래스의 메타 정보(필드, 생성자, 메소드, 적용 어노테이션)를 알아내는 것.
•
유지 정책을 지정할 때 @Retention 어노테이션을 사용
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnoName {
...
}
Java
복사
런타임 시 어노테이션 정보 사용하기
•
어노테이션 자체는 아무 동작을 하지 않는 표식
•
리플렉션을 사용하여 어노테이션의 적용 여부와 엘리먼트 값을 읽고 처리
•
클래스에 적용된 어노테이션 → java.lang.Class 이용
•
필드, 생성자, 메소드에 적용된 어노테이션 → Class의 메소드를 통해 java.lang.reflect 패키지의 Field, Constructur, Method 타입 배열을 얻어야 한다.
◦
Field[] —— getFields() : 필드 정보를 Field 배열로 반환
◦
Constructor[] —— getConstructors() : 생성자 정보를 Constructor 배열로 반환
◦
Method[] —— getDeclaredMethods() : 메소드 정보를 Method 배열로 반환
•
Class, Field, Constructor, Method가 가지고 있는 메소드를 통해서 적용된 어노테이션 반환
◦
boolean —— inAnnotationPresent(Class<? extends Annotation> annotaionClass)
▪
지정한 어노테이션 적용 여부.
▪
Class에서 호출 시 상위 클래스에 적용된 경우도 true 반환.
◦
Annotation —— getAnnotation(Class<T> annotationClass)
▪
지정한 어노테이션 적용시 해당 어노테이션 반환. 그렇지 않으면 null 반환.
▪
Class에서 호출 시 상위 클래스에 적용된 경우도 어노테이션 반환.
◦
Annotation[] —— getAnnotations()
▪
적용된 모든 어노테이션을 반환. 없으면 길이가 0인 배열 반환.
▪
Class에서 호출 시 상위 클래스에 적용된 어노테이션도 포함.
◦
Annotation[] —— getDeclaredAnnotations()
▪
직접 적용된 모든 어노테이션 반환.
▪
Class에서 호출 시 상위 클래스에 적용된 어노테이션은 미포함.
// PrintAnnotation.java
@Target({ElementType.METHOD}) // 메소드에 적용
@Retention(RetentionPolicy.RUNTIME) // 런타임 범위까지 유지
public @interface PrintAnnotation {
String value() default "-";
int number() default 15;
}
Java
복사
// Service.java
public class Service {
@PrintAnnotation // 엘리먼트 기본값 사용
public void method1() {
System.out.println("실행 내용1");
}
@PrintAnnotation("*") // 엘리먼트 value 값 변경
public void method2() {
System.out.println("실행 내용2");
}
@PrintAnnotation(value="#", number=20) // 모든 엘리먼트 값 변경
public void method3() {
System.out.println("실행 내용3");
}
}
Java
복사
// PrintAnnotationExample.java
public class PrintAnnotationExample {
public static void main(String[] args) {
// Service 클래스의 모든 메소드의 배열 반환.
Method[] declaredMethods = Service.class.getDeclaredMethods();
for ( Method method : declaredMethods ) {
// 어노테이션 적용 여부 판단.
if(method.isAnnotationPresent(PrintAnnotation.class)) {
// 적용된 PrintAnnotation 객체 반환
PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class);
System.out.println("[" + method.getName() + "]");
for(int i=0; i<printAnnotation.number; i++){
System.out.print(printAnnotation.value());
}
try{
method.invoke(new Service()); // 메소드 호출
} catch (Exception e) {}
}
} // for
}
}
Java
복사