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

+ Recent posts