21.7.29

최준영·2021년 7월 30일
0

TIL

목록 보기
5/95
post-custom-banner

Chap 8. 인터페이스


1. 인터페이스

  • 인터페이스는 개발 코드와 객체가 서로 통신하는 접점역할을 한다. 그렇기 때문에 개발 코드는 객체의 내부 구조를 알 필요 없이, 인터페이스의 메소드만 알면 된다.
  • 개발 코드를 수정하지 않고 사용하는 객체를 변경할 수 있도록 하기 위해 사용한다.

1) 인터페이스 선언

  • 인터페이스는 ~.java 형태의 소스파일로 작성되고 컴파일러(javac)를 통해 ~.class 형태로 컴파일 되기 때문에 물리적 형태는 클래스와 동일하다.
  • 클래스는 필드, 생성자, 메소드를 구성 멤버로 가지지만, 인터페이스는 상수 필드와 추상 메소드만을 가진다.
  • 객체로 생성할 수 없기 때문에 생성자가 없다.

상수 필드 선언

  • 객체의 사용 방법을 정의한 것이므로 실행 시 데이터를 저장할 수 있는 인스턴스나 정적 필드 선언은 불가능하지만, 상수 필드 선언은 가능하다. 단, 상수는 인터페이스에 고정된 값으로 실행 시에 데이터를 바꿀 수 없다.
  • 인터페이스에 선언된 필드는 public static final을 생략하더라도 컴파일 과정에서 자동으로 붙는다.
[public static final] 타입 상수이름 =;

추상 메소드 선언

  • 인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행되기 때문에 실행 블록이 필요없는 추상 메소드로 선언한다.
  • 인터페이스에 선언된 추상 메소드는 public abstract를 생략하더라도 컴파일 과정에서 자동으로 붙는다.
[public abstract] 리턴타입 메소드이름(매개변수, ...);

2) 인터페이스 구현

  • 객체는 인터페이스에서 정의된 추상 메소드와 동일한 메소드 이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야 한다. 이를 구현 객체라고 하고, 구현 객체를 생성하는 클래스를 구현 클래스라고 한다.
  • 구현 클래스는 보통의 클래스 선언부에 'implements 인터페이스 이름'을 추가한다.
  • 실체 메소드를 작성할 때 주의할 점은 인터페이스의 모든 메소드는 기본적으로 public 접근 제한을 가지기 때문에 이보다 낮은 접근 제한으로 작성할 수 없다.

다중 인터페이스 구현 클래스

  • 객체는 다수의 인터페이스 타입으로 사용할 수 있다.
  • 구현 클래스는 모든 인터페이스의 추상 메소드에 대해 실체 메소드를 작성해야 한다.
public class 구현클래스이름 implements 인터페이스A, 인터페이스B{
// 실체 메소드 선언
}

3) 인터페이스 사용

  • 클래스를 선언할 때 인터페이스는 필드, 생성자 또는 메소드의 매개변수, 생성자 또는 메소드의 로컬 변수로 선언될 수 있다.

2. 타입 변환과 다형성

  • 인터페이스를 사용해서 메소드를 호출하도록 코딩했다면, 프로그램 소스코드의 변화없이 구현 객체를 교체함으로써 실행 결과가 다양해진다. 이것이 인터페이스의 다형성이다.
  • 인터페이스 구현 클래스를 상속해서 자식 클래스를 만들었다면 자식 객체 역시 인터페이스 타입으로 자동 타입 변환이 가능하다.
  • 다형성은 필드, 메소드에도 적용된다.
  • 강제 타입 변환은 구현 객체가 인터페이스 타입으로 변환되어 있는 상태에서만 가능하다.
  • instanceof 연산자로 해당 객체가 어떤 타입인지 알 수 있다.

1) 인터페이스 상속

  • 인터페이스도 다른 인터페이스를 상속할 수 있다.
  • 인터페이스는 클래스와는 달리 다중 상속을 허용한다.
  • 하위 인터페이스를 구현하는 클래스는 하위 인터페이스의 메소드 뿐만 아니라 상위 인터페이스의 모든 추상메소드에 대한 실체 메소드를 가지고 있어야 한다.

Chap 9. 중첩 클래스와 중첩 인터페이스


1. 중첩 클래스와 중첩 인터페이스 소개

1) 중첩 클래스

  • 클래스 내부에 선언한 클래스를 말한다.
class A {
  class B {
  }
}
  • 두 클래스의 멤버들을 서로 쉽게 접근할 수 있고, 외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다는 장점이 있다.
  • 클래스 내부에 선언되는 위치에 따라 멤버 클래스와 로컬 클래스로 분류된다.
  • 중첩 클래스도 하나의 클래스이기 때문에 바이트 코드 파일(.class)이 별도로 생성된다.
    • 멤버 클래스일 경우 A $ B .class
    • 로컬 클래스일 경우 A $1 B .class

(1) 멤버 클래스

  • 클래스 멤버로서 선언된다.
  • 언제든지 재사용이 가능하다.

분류

  1. 인스턴스 멤버 클래스 : static 키워드 없이 중첩 선언된 클래스이다. 인스턴스 필드와 메소드만 선언 가능하고 정적 필드와 메소드는 선언할 수 없다.
class A {
  class B {
    B() {}
    int field1;
    void method1() {}
  }
}
  • A 클래스 외부에서 B 객체를 생성하려면, 먼저 A 객체를 생성해야한다.
  • 대부분 A 클래스 내부에서 B 객체를 생성해서 사용한다.
A a = new A();
A.B b = a.new B();
b.field1 = 3;
b.method1();
  1. 정적 멤버 클래스 : static 키워드로 선언된 클래스이다. 모든 종류의 필드와 메소드가 선언 가능하다.
class A {
  static class C {
    B() {}
    int field1;
    static int field2;
    void method1() {}
    static void method2() {}
  }
}
  • A 클래스 외부에서 C 객체를 생성하려면, A 객체를 생성하지 않아도 된다.
A.C c = new A.C();
c.field1 = 3;
c.method1();
A.C.filed2 = 3;
A.C.method2();

(2) 로컬 클래스

  • 생성자 또는 메소드 내부에서 선언된다. 메소드가 종료되면 없어진다.
  • 접근 제한자 및 static을 붙일 수 없다. 메소드 내부에서만 사용되기 때문이다.
  • 로컬 클래스 내부에서는 인스턴스 필드와 메소드만 선언 가능하다.
  • 주로 비동기 처리를 위해 스레드 객체를 만들 때 사용한다.
class A {
  void method() {
    class B { ... }
  }
}

2) 중첩 클래스의 접근 제한

  • 인스턴스 멤버 클래스는 상위 클래스의 객체가 구현이 되어야 하고, 정적 멤버 클래스는 그렇지 않다는 것을 명심하면 될 것 같다.

(1) 바깥 필드와 메소드에서 사용 제한

  • 인스턴스 멤버 클래스는 바깥 클래스의 인스턴스 필드의 초기값이나 인스턴스 메소드에서 객체를 생성할 수 있으나, 정적 필드의 초기값이나 정적 메소드에서는 객체를 생성할 수 없다.
  • 정적 멤버 클래스는 모든 필드의 초기값이나 모든 메소드에서 객체를 생성할 수 있다.

(2) 멤버 클래스에서 사용 제한

  • 인스턴스 멤버 클래스 안에서는 바깥 클래스의 모든 필드와 모든 메소드에 접근 가능하다.
  • 정적 멤버 클래스 안에서는 바깥 클래스의 정적 필드와 메소드에만 접근할 수 있고 인스턴스 필드와 메소드에는 접근할 수 없다.

(3) 로컬 클래스에서 사용 제한

  • 로컬 클래스의 객체는 메소드 실행이 종료되면 없어지는 것이 일반적이지만, 로컬 스레드 객체를 사용할 때와 같이 메소드가 종료되어도 계속 실행상태로 존재할 수 있다.
    • 메소드를 실행하는 스레드와 다르므로 메소드가 종료된 후에도 로컬 스레드 객체는 실행상태로 존재할 수 있다.
  • 컴파일 시 로컬 클래스에서 사용하는 매개 변수나 로컨 변수의 값이 달라지지 않게 하기 위해서 매개 변수나 로컬 변수를 final로 선언해야한다.
    • java 8부터는 final 키워드를 붙이지 않아도 final 특성을 가지고 있다.

중첩 클래스에서 바깥 클래스 참조 얻기

  • 중첩 클래스에서 this 키워드를 사용하면 중첩 클래스의 객체 참조가 된다.
  • 중첩 클래스 내부에서 바깥 클래스의 객체 참조를 얻을면 '바깥클래스.this'를 사용하면 된다.

3) 중첩 인터페이스

  • 클래스 멤버로 선언된 인터페이스를 말한다.
class A {
  interface B {
  }
}
  • 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해 사용한다.
  • 인스턴스 멤버 인터페이스와 정적 멤버 인터페이스 모두 가능하다.
    • 주로 정적 멤버 인터페이스를 많이 사용하는데 UI 프로그래밍에서 이벤트를 처리할 목적으로 많이 활용된다.

2. 익명 객체

  • 이름이 없는 객체를 말한다.
  • 만들려면 어떤 클래스를 상속하거나 인터페이스를 구현해야 한다.
부모클래스/인터페이스 변수 = new 부모클래스/인터페이스() {...};
  • 이경우 부모클래스는 이름이 없는 자식 객체를 참조하고, 인터페이스 변수는 이름이 없는 구현 객체를 참조한다.

1) 익명 자식 객체 생성

  • 자식 클래스를 명시적으로 선언하는 이유는 재사용성이 높기 때문이다. 하지만 재사용되지 않고, 특정 위치에서만 사용된다면 명시적 선언은 매우 귀찮은 작업이다.
  • 생성자 선언이 불가능하다.
  • 익명 자식 객체에서 새롭게 정의된 필드와 메소드는 익명 자식 객체 내부에서만 사용되고, 외부에서는 접근할 수 없다. 왜냐하면 익명 자식 객체는 부모 타입 변수에 대입되므로 부모 타입에 선언된 것만 사용할 수 있기 때문이다.
  • 익명 구현 객체도 동일하며, 다만 추상 메소드에 대해 모든 실체 메소드를 작성해야한다.

Chap 10. 예외 처리

1. 예외 클래스

  • 예외란 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류를 말한다.
  • 예외가 발생되면 프로그램은 곧바로 종료된다는 점에서 에러와 비슷하지만, 예외는 예외 처리를 통해 프로그램을 종료하지 않고 정상 실행 상태를 유지할 수 있다.

1) 예외와 예외 클래스

  • 자바는 예외를 클래스로 관리한다. JVM은 프로그램을 실행하는 도중에 예외가 발생하면 해당 에외 클래스로 객체를 생성한다. 그리고 나서 예외 처리 코드에서 예외 객체를 이용할 수 있도록 한다.
  • 일반 예외와 실행 예외로 구성된다. Runtime Exception의 하위 클래스가 인면 일반 예외 클래스이고, 하위 클래스이면 실행 예외 클래스이다.

2) 일반 예외

  • 컴파일러 체크 예외라고도 한다.
  • 컴파일 하는 과정에서 예외가 발생할 가능성이 높은 자바 소스에 예외처리 코드가 없다면 컴파일 오류를 발생한다.

3) 실행 예외

  • 컴파일러 넌 체크 예외라고도 한다.
  • 실행 시 예측할 수 없이 갑자기 발생하기 때문에 컴파일 하는 과정에서 예외 처리 코드가 있는지 검사하지 않는다.
  • 때문에 오로지 개발자의 경험에 의해 예외 처리 코드를 작성해야 한다.

자주 발생하는 오류들

  • NullPointerException : 객체가 참조가 없는 상태에서 객체를 사용하려 하면 발생
  • ArrayIndexOutOfBoundsException : 배열의 인덱스 범위를 초과할 경우 발생. length를 이용해서 배열의 길이 먼저 파악 권장.
  • NumberFormatException : 숫자로 변환될 수 없는 문자를 숫자로 변환하려 할 때 발생
  • ClassCastException : 타입 변환은 상위 클래스와 하위 클래스간에 발생하고 구현 클래스와 인터페이스 간에도 발생한다. 잘못된 타입 변환을 할 경우 발생한다.

2. 예외처리

  • 일반 예외는 자바 컴파일러가 강제적으로 예외 처리 코드를 작성하도록 요구하지만, 실행 예외는 그렇지 않기 때문에 개발자의 경험을 바탕으로 예외 처리 코드를 작성해야한다.

1) 예외 처리 코드

try-catch-finally

try {
// 예외 발생 가능 코드
} catch(예외 클래스 e) {
// 예외 처리
} finally {
// 항상 실행
}
  • try 블록의 코드에서 예외가 발생 시 즉시 실행을 멈추고 catch 블록으로 이동하여 예외 처리 코드를 실행한다.
  • finally 블록은 생략 가능하다.
  • 예외 별로 예외 처리 코드를 다르게 하려면 다중 catch블록을 작성하면 된다.
    • 주의할 점은 상위 예외 클래스가 하위 예외 클래스보다 아래쪽에 위치해야 한다. 왜냐하면 하위 예외는 상위 예외를 상속했기 때문에 상위 예외 타입도 되기 때문이다.

2) 예외 떠넘기기

  • 경우에 따라서는 throws를 사용하여 메소드를 호출한 곳으로 예외를 떠넘길 수도 있다.
  • throws 키워드를 메소드 선언부 끝에 작성한다.
  • 발생할 수 있는 예외의 종류별로 throws를 뒤에 나열하는 것이 일반적이지만, throws Exception만으로 모든 예외를 간단히 떠넘길 수 있다.
    • 좋은 방법은 아니다.
  • throws 키워드가 붙어있는 메소드는 반드시 try 블록 내에서 호출되어야 한다.
  • main() 메소드에서도 throws 키워드를 사용해서 예외를 떠넘길 수 있는데, 결국 JVM이 최종적으로 예외 처리를 하게 된다. JVM은 예외의 내용을 콘솔에 출력하는 것으로 예외 처리를 한다.
profile
do for me
post-custom-banner

0개의 댓글