1. 인터페이스
인터페이스
- 인터페이스는 클래스 간에 공통으로 구현해야 할 메소드를 정의하기 위해 사용된다.
- 자바에서 interface 키워드를 사용하여 인터페이스를 선언할 수 있다.
- 인터페이스는 메소드 선언만 포함하며, 메소드의 구현은 하지 않는다.
public interface Drivable {
void drive();
void stop();
}
- 인터페이스는 여러 클래스에서 동일한 메소드를 구현하도록 강제하여 코드의 일관성을 유지하고 다형성을 지원한다.
- 인터페이스를 구현하려면 implements 키워드를 사용한다.
- 구현 클래스는 인터페이스의 모든 메소드를 반드시 구현해야 한다.
public class Car implements Drivable {
@Override
public void drive() {
System.out.println("자동차가 주행 중입니다.");
}
@Override
public void stop() {
System.out.println("자동차가 멈췄습니다.");
}
}
- 위 예제에서 Car 클래스는 Drivable 인터페이스를 구현하여 drive()와 stop() 메소드를 정의하였다.
- 인터페이스를 사용하여 다형성을 구현할 수 있다.
- 인터페이스 타입의 변수는 구현 객체를 참조할 수 있으며, 이를 통해 동일한 인터페이스를 구현한 객체들이 같은 방식으로 호출될 수 있다.
Drivable myCar = new Car();
myCar.drive();
myCar.stop();
- 인터페이스 타입 변수 myCar는 Car 객체를 참조하고 있으며, Drivable 인터페이스에 정의된 메소드만을 호출할 수 있다.
타입 변환과 다형성
- 인터페이스를 구현한 클래스의 객체는 자동으로 인터페이스 타입으로 변환될 수 있다.
- 이를 통해 구현 객체를 인터페이스 타입으로 간단히 사용할 수 있다.
Drivable vehicle = new Car(); // Car 객체가 Drivable 타입으로 자동 변환
- 이렇게 변환하면 인터페이스에 정의된 메소드만 사용할 수 있지만, 이를 통해 코드의 유연성을 높일 수 있다.
- 인터페이스 타입의 필드는 다양한 구현 객체를 참조할 수 있다.
- 이를 통해 여러 객체를 같은 인터페이스 타입으로 처리할 수 있어 코드의 재사용성과 유연성을 높인다.
Drivable vehicle1 = new Car();
Drivable vehicle2 = new Truck();
- 이와 같이 하나의 인터페이스 타입 필드를 통해 여러 구현 객체를 참조할 수 있다.
- 메소드의 매개변수를 인터페이스 타입으로 선언하면, 다양한 구현 객체를 전달받아 처리할 수 있다.
public void operateVehicle(Drivable vehicle) {
vehicle.drive();
vehicle.stop();
}
operateVehicle(new Car()); // Car 객체 전달
operateVehicle(new Truck()); // Truck 객체 전달
- operateVehicle() 메소드는 Drivable 타입을 매개변수로 받기 때문에, Car와 Truck 객체 모두 전달받아 처리할 수 있다.
- 자동 타입 변환된 객체를 다시 원래의 타입으로 변환하려면 강제 타입 변환(다운캐스팅)을 수행해야 한다.
- 이때 instanceof 연산자를 사용하여 변환이 가능한지 확인한 후 안전하게 캐스팅한다.
Drivable myVehicle = new Car();
if (myVehicle instanceof Car) {
Car myCar = (Car) myVehicle;
myCar.drive();
}
- instanceof 연산자를 사용하여 객체가 특정 클래스의 인스턴스인지 확인한 후 캐스팅하면 런타임 오류를 방지할 수 있다.
- 객체가 특정 인터페이스를 구현했는지 확인하려면 instanceof 연산자를 사용한다.
if (myVehicle instanceof Drivable) {
System.out.println("myVehicle은 Drivable 인터페이스를 구현합니다.");
}
- 인터페이스는 다른 인터페이스를 상속받아 사용할 수 있다.
- 이를 통해 여러 인터페이스의 기능을 결합하여 확장할 수 있다.
public interface Flyable {
void fly();
}
public interface AdvancedDrivable extends Drivable, Flyable {
void autoPilot();
}
- AdvancedDrivable 인터페이스는 Drivable와 Flyable 인터페이스를 상속받아 두 인터페이스의 모든 메소드를 포함한다.
2. 중첩 클래스와 중첩 인터페이스
중첩 클래스와 중첩 인터페이스 소개
- 중첩 클래스는 클래스 내에 선언된 클래스로, 외부 클래스와 밀접한 관계가 있을 때 사용된다.
- 중첩 클래스는 외부 클래스의 멤버에 접근할 수 있으며, 코드를 논리적으로 묶어 가독성을 높이는 데 유용하다.
public class OuterClass {
class InnerClass {
void display() {
System.out.println("이것은 중첩 클래스입니다.");
}
}
}
- 중첩 클래스는 public, private, protected 접근 제한자를 가질 수 있다.
- 이를 통해 외부 클래스에서만 사용할 수 있는 클래스나 특정 클래스만 접근할 수 있는 클래스를 정의할 수 있다.
- 중첩 인터페이스는 클래스 내에 선언된 인터페이스로, 외부 클래스와 관련된 동작을 정의할 때 사용된다.
public class Button {
interface OnClickListener {
void onClick();
}
}
- 위 코드에서 OnClickListener 인터페이스는 Button 클래스에 특정한 동작을 정의하기 위해 사용된다.
익명 객체
- 익명 객체는 클래스를 정의하지 않고 한 번만 사용할 목적으로 객체를 생성할 때 사용된다.
- 익명 클래스는 부모 클래스를 상속받거나 인터페이스를 구현하여 사용한다.
Car myCar = new Car() {
@Override
public void drive() {
System.out.println("익명 객체가 주행 중입니다.");
}
};
myCar.drive();
- 인터페이스를 구현할 때도 익명 객체를 사용할 수 있다.
- 구현해야 할 메소드가 한두 개뿐일 경우 간편하게 사용할 수 있다.
Button.OnClickListener listener = new Button.OnClickListener() {
@Override
public void onClick() {
System.out.println("버튼이 클릭되었습니다.");
}
};
listener.onClick();
- 익명 객체는 생성된 메소드의 로컬 변수를 사용할 수 있지만, 이 로컬 변수는 final로 선언된 것처럼 변경할 수 없다.
public void start() {
int speed = 10;
Drivable vehicle = new Drivable() {
@Override
public void drive() {
System.out.println("속도: " + speed);
}
};
vehicle.drive();
}
- 위 코드에서 speed 변수는 익명 객체 내에서 사용되지만, 값을 변경할 수 없다.
3. 예외 처리
예외 클래스
- 예외는 프로그램 실행 중에 발생하는 오류를 의미하며, 자바에서는 예외를 처리하기 위해 Exception 클래스를 사용한다.
- 예외 클래스는 발생한 오류의 유형을 나타내며, 이 예외를 처리해야만 프로그램이 비정상적으로 종료되지 않는다.
- 예외는 크게 컴파일 시점에 확인되는 '검사 예외'와, 실행 중에 발생하는 '실행 예외'로 나뉜다.
- 실행 예외(Runtime Exception)는 컴파일 시에는 발견되지 않지만, 실행 중에 발생하는 예외이다.
- 예를 들어, 배열의 인덱스를 잘못 참조하거나, null 객체에 접근하려는 경우 실행 예외가 발생한다.
- 이러한 예외는 프로그램 실행 중에 예기치 못한 오류를 방지하기 위해 반드시 처리해야 한다.
public class RuntimeExceptionExample {
public static void main(String[] args) {
String text = null;
try {
System.out.println(text.length()); // NullPointerException 발생
} catch (NullPointerException e) {
System.out.println("예외 발생: null 객체에 접근할 수 없습니다.");
}
}
}
- 위 예제에서 NullPointerException이 발생할 수 있는 코드가 try 블록 내에 포함되어 있으며, catch 블록에서 예외를 처리하고 있다.
예외 처리
- 예외 처리는 프로그램이 예외 상황에서도 정상적으로 동작할 수 있도록 하기 위한 기법이다.
- 자바에서는 try, catch, finally 블록을 사용하여 예외를 처리한다.
public class ExceptionHandlingExample {
public static void main(String[] args) {
try {
int result = 10 / 0; // ArithmeticException 발생
} catch (ArithmeticException e) {
System.out.println("예외 발생: 0으로 나눌 수 없습니다.");
} finally {
System.out.println("예외 처리 완료.");
}
}
}
- 위 코드에서 try 블록에서 예외가 발생할 수 있는 코드를 실행하고, catch 블록에서 예외를 처리한다.
- finally 블록은 예외 발생 여부와 관계없이 항상 실행된다.
- 여러 종류의 예외가 발생할 수 있는 경우, 각 예외에 대해 별도의 catch 블록을 작성하여 처리할 수 있다.
- 이를 통해 다양한 예외 상황에 대처할 수 있다.
public class MultipleExceptionExample {
public static void main(String[] args) {
try {
int[] numbers = {1, 2, 3};
System.out.println(numbers[5]); // ArrayIndexOutOfBoundsException 발생
String text = null;
System.out.println(text.length()); // NullPointerException 발생
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("예외 발생: 배열 인덱스가 잘못되었습니다.");
} catch (NullPointerException e) {
System.out.println("예외 발생: null 객체에 접근할 수 없습니다.");
}
}
}
- 위 코드에서는 배열의 인덱스를 잘못 참조하는 경우와 null 객체에 접근하려는 경우 각각의 예외를 별도의 catch 블록으로 처리하고 있다.
- 메소드 내에서 발생할 수 있는 예외를 직접 처리하지 않고, 메소드를 호출한 쪽으로 떠넘길 수 있다.
- 이를 '예외 떠넘기기'라고 하며, throws 키워드를 사용하여 선언한다.
- 예외를 떠넘기면 해당 메소드를 사용하는 쪽에서 예외를 처리해야 한다.
public class ThrowsExample {
public static void main(String[] args) {
try {
divide(10, 0);
} catch (ArithmeticException e) {
System.out.println("예외 발생: " + e.getMessage());
}
}
public static void divide(int a, int b) throws ArithmeticException {
if (b == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
System.out.println("결과: " + (a / b));
}
}
- divide() 메소드는 ArithmeticException을 발생시킬 수 있으며, throws 키워드를 통해 이 예외를 호출한 메소드로 떠넘긴다.
- main() 메소드에서는 try-catch 블록을 사용하여 이 예외를 처리한다.
- 이처럼 예외 처리는 프로그램의 안정성을 높이고, 예외 상황에 유연하게 대응할 수 있는 중요한 기법이다.
'IT & AI > AI 지식' 카테고리의 다른 글
JAVA 기본 API, 스레드, 컬렉션 프레임워크 (0) | 2024.11.22 |
---|---|
JAVA 객체 지향 프로그래밍: 클래스, 상속 (0) | 2024.11.21 |
JAVA 조건문, 반복문, 참조 타입(배열) 정리 (2) | 2024.11.21 |
JAVA 초보자를 위한 변수, 연산자, 데이터 입출력 이해 (1) | 2024.11.20 |
C 언어 메모리 동적 할당, 구조체, 파일 입출력 (1) | 2024.11.20 |