쇼규모 프로그램에서는 개발자의 생산성과 코드품질을 향상시킬 수 있으나, 일반적인 대규모 소프트웨어에서는 그렇지 않다.
낮은 수준에서 발생하는 특정유형의CheckedException의 경우 (File I/O, Network, Database …) 일반적인 응용프로그램에서는 알 필요 없거나 알고싶지 않아한다. 만약 Exception을 캐치한다고 하더라도 적절하게 대응하기 힘든경우가 대부분이라RuntimeException으로 rethrow처리하는 경우가 많다.
코드의 확장성에 이슈가 생길수 있다. 단일 CheckedException 사용은 훌륭하게 동작하는것으로 보이나 4~5개의 서로다른CheckedExcpetion을 사용하는 하위 API를 호출하는 경우 Exception 체인이 기하급수적으로 증가할 수 있다.
반드시 예외 처리를 하는 코드를 작성해야 한다. 예외를 던지는 것은 모든 하위 메서드, 호출 트리에 누적되기 때문에 수많은 메서드를 조정해야 할 수도 있다.
2. Java 8 기준으로 GC는 어떻게 실행이 되는가? (heap young/old generation 동작 관점)
parellel GC
멀티 스레드로 GC를 수행하기 때문에 stop-the-world 시간을 줄일 수 있다.
minor GC(young)에서 멀티 스레드를 사용한다.(후에는 major GC(old)에서도 멀티 스레드 사용)
5. enum 어떤 메모리에 저장이 될까요? (method 영역, Heap 영역 분리하여)
enum 클래스의 정보가 메서드 영역에 저장된다.
enum 객체 인스턴스가 힙 영역에 저장된다.
enum 클래스는 상수 하나당 인스턴스 하나를 생성해서 public static final로 공개한다. 열거 타입의 인스턴스는 클라이언트가 직접 생성할 수 없고, 인스턴스는 런타임에 한 번만 생성된다. 이런 특징으로 싱글톤 패턴을 구현할 때 사용되기도 한다.
6. inner class vs nested class (+ GC의 대상이 되는건 누구?)
innerclass
static 키워드를 사용하지 않고 다른 클래스 내부에 정의된 클래스
내부 클래스는 외부 클래스 멤버에 접근할 수 있다.
내부 클래스 객체를 생성하려면 외부 클래스의 객체부터 생성해야 한다.
내부 클래스에서 외부 클래스 멤버에 접근할 때는 외부클래스.this 로 사용한다. 내부 클래스 멤버에 접근하려면 this만 사용한다.
nested class
static키워드를 사용하고 다른 클래스 내부에 정의된 클래스
기술적으로는 내부 클래스가 아니다.
내부 클래스에서 외부 클래스의 멤버에 접근할 수 없다.
내부 클래스를 인스턴스화하기 위해 외부 클래스를 인스턴스화할 필요는 없다.
innerclass는 외부 클래스에 종속되어 있기 때문에 외부 클래스의 인스턴스가 더 이상 쓰이지 않고 GC의 대상이 되었을 때 innerclass도 GC의 대상이 된다. 즉, 외부 클래스의 인스턴스가 메모리에 계속 남아있다면 innerclass도 그렇다. nested class는 외부 클래스와 독립적으로 이루어져 있고 static이 붙은 클래스이기 때문에 GC의 대상이 될 수 없다.
7. 어노테이션을 왜 쓸까요? (+ 리플렉션 적용 과정)
용도
소스 코드에 메타데이터 추가
컴파일러에게 코드 작성 문법 에러를 체크하도록 정보 제공
소프트웨어 개발툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보 제공
실행 시 특정 기능을 실행하도록 정보 제공
리플렉션 : 힙 영역에 로드된 Class 타입의 객체를 통해, 원하는 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있도록 지원하는 API이다. 컴파일타임이 아닌 런타임에 동적으로 특정 클래스의 정보를 추출할 수 있는 프로그래밍 기법이다.
어노테이션은 소스 코드에 메타데이터를 추가하는 방법이며, 리플렉션은 실행 시간에 클래스의 정보를 검사하고 조작하는 데 사용됩니다. 어노테이션을 사용하여 클래스에 메타데이터를 추가하고, 리플렉션을 사용하여 그 정보를 동적으로 검사하고 처리한다.
리플렉션 적용 과정
클래스 객체 얻기
얻고자 하는 클래스의 정보를 얻기 위해 클래스 객체의 메서드를 사용한다. 이러한 메서드는 클래스의 필드, 메서드, 생성자, 어노테이션 등에 접근할 수 있는 다양한 방법을 제공합니다.
얻은 클래스, 필드, 메서드 등에 어노테이션을 조사하여 필요한 작업을 수행할 수 있다.
Class<?> clazz = MyClass.class;
MyClass obj = new MyClass();
Class<?> clazz = obj.getClass();
8. GC에서 사용하는 알고리즘은 무엇이 있고, Java는 어떤 알고리즘을 사용하나요?
Mark and sweep
가비지 컬렉션이 될 대상을 식별(mark)하고 제거(sweep)하며 객체가 제거되어 파편화된 메모리 영역을 앞에서부터 채워나가는 작업을 수행한다.(compaction)
mark : 루트 스페이스부터 그래프를 순회하며 연결된 객체를 찾아 각각 어떤 객체를 참조하고 있는지 찾아서 마킹한다.
instanceof를 사용하면 알맞은 타입을 찾을 때까지 컴파일 시에 모든 타입을 돌며 검사해야한다.
대신에 is타입명() 과 같은 메서드의 다형성을 적용해야 성능이 더 좋다.
8. 언제 interface 사용하고, 언제 abstract class 사용 하는가?
인터페이스 : 상속 관계를 타고 올라갔을 때 다른 부모 클래스를 상속하는데 같은 기능이 필요할 경우
추상 클래스 : 상속 관계를 타고 올라갔을 때 같은 부모 클래스를 상속하는데 같은 기능도 필요할 경우
상속을 받아 기능을 확장하고자 할 때는 추상클래스를 사용하고 동일한 기능을 구현하는 클래스 간의 메서드나 변수의 이름을 통일하고자 할 때는 인터페이스를 사용한다.
9. final 키워드에 대해 설명
final 키워드는 한 번 초기화되면 변경할 수 없음을 나타낸다.
final 변수
한 번 할당되면 값을 변경할 수 없다. 즉, 변수가 초기화된 후 다른 값으로 재할당할 수 없다.
선언 시 초기화를 해야 한다. 초기화를 하지 않고 선언만 하면 컴파일 오류가 발생한다.
보통 상수를 표현할 때 사용된다.
final 메서드
하위 클래스에서 오버라이딩할 수 없다. 즉, final 메서드는 하위 클래스에서 변경할 수 없다.
보안, 성능 최적화, 메서드의 의도된 동작 보호 등을 위해 사용됩니다.
final 클래스
상속할 수 없는 클래스이다. 즉, final 클래스는 하위 클래스를 가질 수 없다.
보안, 불변 클래스(immutable class) 설계, 효율성 등을 위해 사용된다.
final 키워드를 사용하면 코드의 안정성을 높일 수 있다. final 변수는 값이 변경되지 않음을 보장하고, final 메서드와 final 클래스는 의도된 대로 동작하도록 보호할 수 있다. 따라서 코드의 의도를 명확히 전달하고 오류를 방지하기 위해 final 키워드를 적절히 활용하는 것이 좋다.
10. exception vs error
exception(프로그램 안)
프로그램의 실행 도중 발생할 수 있는 예외적인 상황을 나타낸다. 이러한 예외적인 상황은 일반적으로 프로그램의 사용자 또는 외부 환경의 영향을 받아 발생한다.
주로 프로그램 코드 내에서 예상할 수 있는 오류나 예외적인 상황을 처리하기 위해 사용된다. 예를 들어, 파일을 찾을 수 없는 경우나 배열 범위를 벗어나는 경우 등이 있다.
checked exception은 반드시 처리되어야 하는 예외이며, 컴파일 시에 확인된다. unchecked(runtime) exception은 명시적으로 처리하지 않아도 되는 예외로, 실행 시에 확인된다.
스레드에만 영향을 준다.
checked exception
unchecked(runtime) exception : 예외를 미리 감지하디 못했을 때 발생. 컴파일시에 체크를 하지 않는다.
error(프로그램 밖)
일반적으로 프로그램이 복구할 수 없는 심각한 상황을 나타낸다. 이러한 상황은 주로 시스템 레벨에서 발생하며, 프로그램 자체의 문제보다는 주로 시스템 리소스 부족, 가상 머신의 오동작, 하드웨어 장애 등과 관련이 있다.
프로그램이 직접 처리할 수 없으며, 대부분의 경우 프로그램이 종료되거나 예외처리 기능을 사용하여 처리할 수 없다. Error는 주로 시스템 관리자나 개발자에게 알려주는 역할을 한다.
예를 들어, OutOfMemoryError는 메모리 부족으로 인해 발생하는 오류이며, StackOverflowError는 스택 오버플로우로 인해 발생하는 오류이다.
프로세스에 영향을 준다.
exception은 프로그램의 예외적인 상황을 처리하는 데 사용되며, 프로그램이 복구할 수 있는 상황을 나타내는 반면, error는 프로그램이 처리할 수 없는 심각한 상황을 나타내며, 주로 시스템 레벨의 문제와 관련이 있다.
11. String vs StringBuilder vs StringBuffer
String
불변(immutable)한 클래스이다. 즉, 한 번 생성된 문자열은 변경할 수 없다.
문자열이 변경되면 새로운 문자열 객체가 생성되며, 기존 문자열은 가비지 컬렉션의 대상이 된다.
StringBuilder
가변적인(mutable)인 문자열을 다루는 데 사용된다. 즉, 문자열을 변경할 수 있다.
내부적으로 가변적인 크기의 문자열을 저장하기 위한 버퍼를 사용하며, 이 버퍼는 동기화(synchronization)를 지원하지 않기 때문에단일 스레드 환경에서 안전하게 사용할 수 있다.
StringBuffer
가변적인 문자열을 다루는 데 사용된다. 그러나 StringBuilder와 달리 StringBuffer는 스레드 안전(thread-safe)한 클래스이다.
멀티 스레드 환경에서 안전하게 사용할 수 있습니다. 여러 스레드가 동시에 StringBuffer 객체를 수정해도 상호간섭없이 안전하게 작동한다.
멀티 스레드 환경에서는 StringBuffer를 사용해야 하지만 대부분의 경우 단일 스레드 환경에서 문자열 조작이 이루어지기 때문에 StringBuilder를 주로 사용한다. StringBuffer는 동기화를 적용하기 때문에 오버헤드가 발생할 수 있다.
12. C1 컴파일러와 C2 컴파일러의 차이
C1 컴파일러 : 빠른 컴파일 시간과 작은 코드 풋프린트를 갖는 환경에서 사용된다. 1~3레벨, 낮은 수준의 최적화
C2 컴파일러 : 더 높은 수준의 최적화를 수행하여 더 높은 성능을 제공하는 대규모 서버 및 데스크톱 애플리케이션과 같은 환경에서 사용된다. 4레벨
13. 스프링 부트 + JSP 프로젝트에서 .jar로 빌드하는 방법은 없는가?
스프링 부트 + JSP 프로젝트를 jar로 빌드하면 WEB-INF를 생성하지 못하기 때문에 jsp 파일을 가져올 수 없다.
추가적으로 필요한 resource들을 담아주는 역할도 하는 META-INF를 사용하면 해결가능하다. resource에 jsp를 넣어주는 방법이다. 다음과 같이 WEB-INF를 복사해주고 원래 WEB-INF를 지워준다. 그리고 sub application을 dependency로 활용할 수 있도록 변경을 해줘야 한다.(https://oingdaddy.tistory.com/426 참고)
## 프로젝트
다음 3가지 주제 중 원하는 것 하나 정해서 "카카오 오븐으로 프로토 타이핑", "유저 시나리오 (글 + 그림으로 표현하면 좋음" 작성하기 (유저 시나리오까지 시간 어려우면 프로토 타이핑까지라도)
JSP를 사용하여 화면을 구성할 때 - 현재 회사에서 스프링부트에 war 파일을 쓰는 이유
외장 WAS를 사용할 때
4.데드락 해결 방법(예방, 회피 등)
데드락 발생 조건
상호 배제 : 프로세스끼리 자원을 공유하지 않을 경우. 동시에 프로세스 하나만 해당 자원을 사용할 수 있다.
점유 대기 : 자원을 최소한 하나 보유하고, 다른 프로세스에 할당된 자원을 점유하기 위해 대기하는 프로세스가 존재한다.
비선점 : 이미 할당된 자원을 강제로 뺏을 수 없다. 자원을 반환할 때까지 기다려야 한다.
순환 대기 : 프로세스들이 순환하는 형태로 서로의 자원을 기다린다.
예방 : 데드락 발생 조건 4가지 중 1개가 충족되지 않도록 시스템 디자인
상호 배제 : 프로세스끼리 자원을 공유하도록 설정. 하지만 이 방법은 거의 불가능하다. 프린터, CPU는 모두 동시에 사용할 수 없다.
점유 대기 : 프로세스 실행에 필요한 모든 자원을 한꺼번에 요구하고 허용할 때까지 작업을 보류한다.
비선점 : 추가적으로 자원을 기다려야 하면 다른 프로세스가 자원을 가져갈 수 있도록 한다.
순환 대기 : 모든 자원에 순서 체계를 부여해서 오름차순으로 자원을 요청. 자원을 한 쪽 방향으로만 요구하도록 한다.
회피 : 실행 환경에서 현재 사용 가능한 자원, 이미 사용중인 자원 등의 정보를 활용해 데드락이 발생할 것 같은 상황을 미리 예측해서 자원을 할당하지 않거나 이미 할당된 자원을 해제함으로써 데드락을 회피한다. Banker Algorithm(은행원 알고리즘)을 사용하는데 자원 요청을 허락했을 때 데드락 발생 가능성이 있으면 자원을 할당해도 안전할 때까지 계속 요청을 거절하는 알고리즘이다.
탐지와 복구 : 데드락을 허용하고 데드락이 발생하면 복구하는 과정. 복구 작업은 데드락을 일으킴 프로세스 중 하나 혹은 여러 개를 중단시키거나, 일시적으로 자원을 선점하는 것을 허용한다.
5. 하드디스크 vs SSD (동작 원리 중심으로)
하드디스크 : 기계적인 부품을 사용하여 데이터를 저장한다. 데이터를 읽고 쓰는 데 시간이 더 오래 걸린다.
SSD : 반도체 메모리와 전기적인 신호를 이용해 데이터를 저장한다. 시간이 더 짧게 걸린다.
6. JVM에서 클래스를 로드하는 타이밍은? / 클래스로더 기초 학습
클래스 로딩 시점 : 클래스의 인스턴스 생성, 클래스의 정적 변수 사용(final 키워드 X), 클래스의 정적 메서드 호출
클래스로더는 런타임 중에 JVM의 메서드 영역에 동적으로 클래스를 로드하는 역할을 한다.
클래스 로드의 단계
로드 : 자바 바이트 코드를 메서드 영역에 저장한다. 로드된 클래스와 부모 클래스의 정보, 클래스와 Interface, Enum의 관련 여부, 변수나 메서드의 정보를 저장한다.
링크
검증 : 읽어 들인 클래스가 자바 언어 명세 및 JVM 명세에 명시된 대로 잘 구성되어 있는지 검사한다.
준비 : 클래스가 필요로 하는 메모리를 할당하고, 클래스에서 정의된 필드, 메소드, 인터페이스를 나타내는 데이터 구조를 준비한다.
분석 : 심볼릭 메모리 레퍼런스를 메소드 영역에 있는 실제 레퍼런스로 교체한다.
초기화 : 클래스 변수들을 적절한 값으로 초기화 한다. 즉, static 필드들이 설정된 값으로 초기화한다.
클래스 로더의 종류
부트스트랩 클래스 로더 : JVM 시작 시 가장 최초로 실행되는 클래스 로더이다. 부트스트랩 클래스 로더는 자바 클래스를 로드하는 것이 아닌, 자바 클래스를 로드할 수 있는 자바 자체의 클래스 로더와 최소한의 자바 클래스(java.lang.Object, Class, ClassLoader)만을 로드한다.
확장 클래스 로더 : 확장 클래스 로더는 부트스트랩 클래스 로더를 부모로 갖는 클래스 로더로서, 확장 자바 클래스들을 로드한다. java.ext.dirs 환경 변수에 설정된 디렉토리의 클래스 파일을 로드하고, 이 값이 설정되어 있지 않은 경우${JAVA_HOME}/jre/lib/ext에 있는 클래스 파일을 로드한다.
시스템 클래스 로더 : 자바 프로그램 실행 시 지정한 Classpath에 있는 클래스 파일 혹은 jar에 속한 클래스들을 로드한다. 쉽게 말하자면, 우리가 만든 .class 확장자 파일을 로드한다.
클래스 로더의 동작 방식
클래스가 호출되면 메서드 영역에 해당 클래스가 로드되어 있는지 확인한다. 있다면 해당 클래스를 사용한다.
정보가 없으면 시스템 클래스 로더에게 클래스 로드를 요청한다.
시스템 클래스 로더는 확장 클래스 로더에 요청을 위임한다.
확장 클래스 로더는 부트스트랩 로더에 요청을 위임한다.
부트스트랩 클래스 로더는 classpath에 해당 클래스가 있는지 확인하고 없다면 확장 클래스 로더에 요청을 넘긴다.
확장 클래스 로더는 classpath에 해당 클래스가 있는지 확인하고 없다면 시스템 클래스 로더에 요청을 넘긴다.
시스템 클래스 로더는 classpath에 해당 클래스가 있는지 확인하고 없다면 ClassNotFoundException을 발생시킨다.
7. Pass by value vs Pass by Reference
자바는 모든 데이터를 Pass By Value로 전달한다.
객체가 전달되는 경우 복사 후 전달되는 값은 실제 메모리를 가리키는 참조값인 포인터라는 것이다.
8.상속은 주로 언제 쓸까요?
코드 재사용
일반적인 클래스가 이미 구현이 되어 있는 상태에서 그보다 좀 더 구체적인 클래스를 구현하기 위해 사용
9.상속의 단점은?
상속을 제대로 활용하기 위해서는 부모 클래스의 내부 구현에 대해 상세하게 알아야 하기 때문에 자식 클래스와 부모 클래스 사이의 결합도가 높아질 수밖에 없다.
상속 관계는 컴파일 시점에 결정되고 고정되기 때문에 코드를 실행하는 도중에 변경할 수 없다.
따라서 여러 기능을 조합해야 하는 설계에 상속을 이용하게 된다면 모든 조합별로 클래스를 하나하나 추가해야 하는데 이 때클래스 폭발 문제가 일어난다.
부모 클래스에 메서드를 추가했을 때, 자식 클래스에는 적합하지 않는 메서드가 상속될 수 있다.
부모 클래스에 결함이 있으면 자식 클래스에도 그대로 상속된다.
부모 클래스를 변경하면 자식 클래스도 함께 변경해야 한다.
10.상속과 조합(합성)의 차이
상속은 하나의 클래스를 확장해서 사용하는 것 , 조합은 필드로 클래스의 인스턴스를 참조하게 만드는 설계
상속은 객체 간의 관계가 수직 관계이고 조합은 수평 관계가 된다.
조합은 구현에 대한 의존성을 결합에 대한 의존성으로 변경하여 결합도를 낮춘다.
코드 작성 시점에 결정한 상속 관계는 변경이 불가능하지만 합성 관계는 실행 시점에 동적으로 변경할 수 있다.
11. instanceOf 키워드를 사용할 때 문제점은?
하나의 타입이 여러 인스턴스를 가질 수 있을 때 instanceOf를 사용한다. (상속 관계일 때)
object instanceOf type : object가 type 을 상속받는 클래스라면 true를 리턴하고 아니라면 false 리턴
추상화 계층을 깨뜨린다(캡슐화가 깨진다) : 추상 클래스를 이용하여 상속을 하면 하나의 추상화 계층이 형성된다. 추상화 계층이 형성되면, 해당 추상화 계층을 사용하는 외부에서는 아래의 하위 문제를 알 필요가 없어진다. instanceof는 추상화 및 캡슐화를 지키지 못한다는 의미를 담고 있다. 상위 계층인 클래스가 하위 계층인 클래스를 직접적으로 알 필요도 없고 알아서는 안된다.
OCP(Open Closed Principle, 객체가 확장에는 열려있고 변화에는 닫혀 있어야 한다) 위반 : 새로운 객체가 추가될 때마다 instanceof로 체크하는 로직이 추가되어야 하는데 이렇게 되면 기존의 코드를 계속 변경해야하기 때문에 OCP를 위반하게 된다.
SRP(Single Responsibility Principle, 객체는 한 가지의 책임만 가져야 한다) 위반 : instanceof를 사용하지 않으면 상위 계층 클래스를 사용하여 하위 계층 클래스의 로직은 알 필요가 없지만 instanceof를 사용하면 히위 클래스가 책임을 가지게 된다.
해결 방법 : 다형성을 사용해서 해결한다.
상위 클래스에 추상 메서드 선언
하위 클래스에 추상 메서드 오버라이딩하여 구현
외부에서 상위 클래스의 추상 메서드를 사용하여 메세지 전달
12.얕은 복사 vs 깊은 복사
얕은 복사는주소값을 복사하는 것이고,깊은 복사는실제 값을 새로운 메모리 공간에 복사하여 저장하는 것이다.
얕은 복사의 경우는 주소값을 복사하기 때문에 참조하고 있는 값이 같다. 복사한 객체가 변경되면 기존의 객체도 변경된다.
CopyObject original = new CopyObject("JuHyun", 20);
CopyObject copy = original; //얕은 복사
깊은 복사
cloneable 인터페이스 구현
public class CopyObject implements Cloneable //인터페이스 구현
CopyObject original = new CopyObject("JuHyun", 20);
CopyObject copy = original.clone(); //깊은 복사
복사 생성자, 복사 팩토리
/* 복사 생성자 */
public CopyObject(CopyObject original) {
this.name = original.name;
this.age = original.age;
}
/* 복사 팩터리 */
public static CopyObject copy(CopyObject original) {
CopyObject copy = new CopyObject();
copy.name = original.name;
copy.age = original.age;
return copy;
}
CopyObject original = new CopyObject("JuHyun", 20);
CopyObject copyConstructor = new CopyObject(original); //복사 생성자를 통한 깊은 복사
CopyObject copyFactory = CopyObject.copy(original); //복사 팩토리를 통한 깊은 복사
13. 인터페이스란?
추상화된 형태의 계약으로, 해당 인터페이스를 구현하는 클래스가 가져야 할 메서드의 시그니처를 정의한다. 이러한 메서드는 구현되지 않고 단지 선언만 되어 있으며, 실제로는 해당 인터페이스를 구현하는 클래스에서 메서드를 구현해야 한다. 이를 통해 다형성을 구현하고 객체 간의 느슨한 결합을 가능하게 한다. 인터페이스는 코드 재사용을 높이고, 코드 유지보수를 용이하게 한다.
다형성이라는 객체 지향의 특징을 구현하는 핵심
객체의 내부 구현이 어떻든 깊이 알 필요없이 필요한 메서드만 호출하고 원하는 결과 값을 제대로 받게 해주는 간편한 상호작용 기능
인터페이스의 역할
프레임워크의 내부구성 학습 없이 지원해주는 메서드를 이용하여 간편하게 프로그램을 개발할 수 있게 해준다.
일종의 '스펙'을 지정하게 하여 소프트웨어 확장을 유리하게 한다.
추상화를 통해 관련 있는 메서드들의 집합을 정의한다.
메서드의 명세만을 가지고 있고(시그니처만 선언하고) 메서드에 대한 구현부분은 없다.
다중 상속이 가능하다. 한 클래스에서 여러 인터페이스를 구현할 수 있다.
자바 API에서도Comparable, Runnable, Serializable 등의 인터페이스를 구현하고 있다.
위의 세가지 인터페이스는 마커 인터페이스. 아무 메서드도 선언하지 않은 인터페이스. 객체의 타입과 관련된 정보만을 제공한다.
인터페이스에서는 구현체 간의 일관된 동작을 보장하고 상태의 변경을 허용하지 않아야 해서 필드를 상수로만 선언할 수 있다.
자바8 이후부터는 디폴트 메서드와 스태틱 메서드를 통해 구현 메서드를 정의할 수 있다.
인터페이스 사용의 장점
인터페이스 다형성 : 클래스가 여러 개의 인터페이스를 구현하게 되면 변수로 타입으로도 다양하게 쓸 수 있다는 말이다. 인터페이스 타입으로 변수를 선언하면 사용하는 입장에서는 뒤에 오는 모든 인터페이스만 구현한 객체이면 되기 때문에 시스템이 더 유연해진다. 적당한 인터페이스가 있다면 반환값, 변수, 필드, 매개변수를 인터페이스로 사용하면 좋다.
형제 관계를 맺어줌 : 아무 관계도 없는 클래스들에게 공통적인 인터페이스를 구현함으로 관계를 맺어줄 수 있다.