[스터디]Java의 정석 23일차

Kristopher·2022년 1월 25일
0

Java 스터디

목록 보기
23/31

(CH12) 3. 애너테이션 ~

(Ch13) 3. start()와 run()

애너테이션

자바를 개발한 사람들은 소스코드와 문서를 하나의 파일로 관리하는 것이 바람직하다고 생각했다. 소스코드의 주석에 소스코드에 대한 정보를 저장하고, 소스코드의 주석으로부터 HTML문서를 생성해내는 프로그램을 만들어서 사용했다. 이때 @ 기호를 이용하여 주석 안에 정보를 저장하고 프로그램이 정보를 읽어 문서 작성에 사용되도록 했는데, 이 기능을 응용하여, 프로그램의 소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것이 바로 애너테이션이다.

자바에서 제공하는 표준 애너테이션은 주로 컴파일러에게 유용한 것이고, 새로운 애너테이션을 정의하는 경우 메타 애너테이션을 사용하여 정의한다.

표준 애너테이션의 종류

@Override : 조상의 메소드를 오버라이딩한 메소드라는 것을 컴파일러에게 알려주는 역할

@Deprecated : 더 이상 사용되지 않는 필드나 메소드에 붙여 사용하지 않는것을 권하는 역할

@FunctionalInterface : 컴파일러가 함수형 인터페이스가 올바르게 선언되었는지 확인하고 잘못된 경우 에러를 발생시키는 역할

@SuppressWarnings : 컴파일러가 보여주는 경고메세지가 나타나지 않게 억제하는 역할

@SafeVarargs : 메소드에서 선언된 가변인자 타입이 non-reifiable타입인 경우, 메소드 선언/호출 부분에서 unchecked 경고가 발생하는데, 해당 코드가 문제 없는 경우 경고를 억제하기 위해 사용하는 애너테이션

  • non-reifiable : 컴파일 후에도 제거되지 않는 타입

메타 애너테이션

애너테이션을 정의할 때 애너테이션의 적용대상이나 유지기간 등을 지정하는데 사용되는 애너테이션을 메타 애너테이션이라고 한다.

@Target : 애너테이션이 적용가능한 대상을 지정하는데 사용

@Retention : 애너테이션이 유지되는 기간을 지정하는데 사용

@Documented : 애너테이션에 대한 정보가 javadoc으로 작성한 문서에 포함하는데 사용

@Inherited : 애너테이션이 자손 클래스에 상속되도록하는데 사용

@Repeatable : 애너테이션을 여러 번 붙이기 위해 사용

@Native : 네이티브 메서드에 의해 참조되는 상수 필드에 붙이는 애너테이션=

  • 네이티브 메서드 : JVM이 설치된 OS의 메소드

애너테이션 직접 정의하기

새로운 애너테이션을 만드는 방법은 interface 앞에 @를 붙여 정의하면 된다.

@ interface Annotation_Name {
    Type Element_name();
    ...
}

애너테이션 내에 선언된 메소드를 애너테이션의 요소라고 하며, 요소는 반환값이 있고, 매개변수는 없는 추상 메소드의 형태를 가지고 상속을 통해 구현하지 않아도 된다. 다만 애너테이션을 적용할 때 이 요소들의 값을 빠짐없이 지정해야 한다. 지정하지 않으면 default값으로 설정된다.

애너테이션 요소의 규칙

  • 요소의 타입은 기본형, String, enum, 애너테이션, Class만 허용된다.
  • ()안에 매개변수를 선언할 수 없다.
  • 예외를 선언할 수 없다.
  • 요소를 타입 매개변수로 정의할 수 없다.

모든 애너테이션의 조상은 java.lang.annotation.Annotation이다. Annotation은 일반적인 인터페이스로 정의되어 있기 때문에, 모든 애너테이션 객체에 대해 equals(), hashCode(), toString()과 같은 메서드를 호출하는 것이 가능하다.

마커 애너테이션

값을 지정할 필요가 없는 경우, 애너테이션의 요소를 하나도 정의하지 않을 수 있다. 요소가 하나도 정의되지 않은 애너테이션을 마커 애너테이션이라고 한다.

프로세스와 쓰레드

프로그램이 실행되기 위해 OS로부터 실행에 필요한 자원을 할당받으면 프로세스가 된다. 프로세스는 프로그램 수행에 필요한 데이터와 자원 그리고 쓰레드로 구성되어 있으며 프로세스의 자원을 황용하여 실제 작업을 수행하는 주체가 바로 쓰레드이다. 따라서 모든 프로세스에는 하나 이상의 쓰레드가 존재하며, 두개 이상의 쓰레드를 가진 프로세스를 멀티쓰레드 프로세스라고 한다.

멀티 쓰레딩의 장점

  • CPU 사용률의 향상
  • 자원의 효율적 사용
  • 사용자에 대한 응답성 향상
  • 작업 분리로 인한 코드 간결화

쓰레드의 구현과 실행

쓰레드를 구현하는 방법에는 Thread클래스를 상속받는 방법과 Runnable인터페이스를 구현하는 방법 두 가지가 있다. 전자는 다른 클래스를 상속받을 수 없기에 보통 후자를 선택한다.

//Thread클래스를 상속
class MyThread extends Thread{
    public void run() { /* ... */} //run()을 오버라이딩
}

//Runnable인터페이스를 구현
class MyThread implements Runnable{
    public void run() {/* ... */} // run()을 구현
}

Thread클래스를 상속받으면, 자손 클래스에서 조상인 Thread클래스의 메소드를 직접 호출할 수 있지만, Runnable을 구현하면 Thread클래스의 static메소드인 currentThread()를 호출하여 쓰레드에 대한 참조를 얻어 와야만 호출이 가능하다.

쓰레드를 실행시킬 때는 start()를 호출해야만 쓰레드가 실행된다. 한 번 실행된 쓰레드는 다시 실행할 수 없는데, 이는 하나의 쓰레드에 대해 start()가 한 번만 호출될 수 있다는 뜻이다. 하나의 쓰레드에 대해 두 번 이상 호출시 IllegalThreadStateException이 발생한다.

쓰레드 내의 메소드로 run()을 오버라이딩/구현하였는데 왜 호출할 때는 start()를 사용하는 것일까? run()을 호출하는 것은 단순히 클래스에 선언된 메소드를 호출하는 것이고, 쓰레드가 실행되기 위해서는 start()를 사용하여야 한다. start() 호출시 main메소드 위에 start메소드가 호출되고 새로운 호출스택에 run()메소드가 호출되어 작업을 수행한다.

Reference

Java의 정석
남궁성의 정석코딩

profile
개발자 지망생입니다.

0개의 댓글