- 하나이상의 추상 메소드를 포함하는 추상 클래스 이며,
abstract키워드를 통해 정의합니다.- 추상 메서드는 메서드 선언만 있으며, 구현이 없는 메서드 입니다.
- 추상 클래스는 직접적으로 인스턴스화할 수 없으며, 상속을 통해 사용됩니다.
📌 추상 클래스
public abstract class Animal {
public abstract void makeSound();
public void eat(){
System.out.println("냠냠");
}
}
📌 자식 클래스에서 추상 메소드를 구현 (새)
public class Bird extends Animal {
@Override
public void makeSound() {
System.out.println("쨱쨱");
}
public static void main(String[] args) {
Animal bird = new Bird();
bird.eat(); // 냠냠
bird.makeSound(); // 짹짹
}
}
📌 자식 클래스에서 추상 메소드를 구현 (강아지)
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("멍멍");
}
public static void main(String[] args) {
Animal Dog = new Dog();
Dog.makeSound(); // 멍멍
}
}
Animal Dog = new Bird(); : DIP (의존 역전원칙) 개념이 적용됩니다.
Dog dog = new Dog;로 인스턴스를 생성해줘도 되지만,
[추상클래스 및 인터페이스] 명칭 = new 구현체();로 해주는 이유는 "변하기가 쉬운 클래스에 의존하지 않는 것" 으로 해줌으로 영향을 받지 않는 상태로 개선할수 있습니다.
공통된 기능을 가진 클래스를 모델링하고 코드 재사용성을 높이는 것입니다.
- 위의 코드의 예시로 동물을 모델링하는 Animal 이라는 클래스를 생성할수 있습니다.
makeSound() 울음소리라는 공통된 동작 (메소드)을 가지며,
실제 동물들의 구체적인 클래스(하위 클래스)가 Animal 클래스를 상속받아 구현할 수 있습니다.- 현업에서는 추상클래스를 사용하긴하지만, 인터페이스를 많이 사용합니다.
( 추상클래스는 다중 상속을 지원 X 인터페이스은 다중상속을 지원 )
❗❗❗ 추상 클래스는 추상메소드 및 "일반 메소드"가 존재합니다.
❗❗❗ 인터페이스는 일반메소드는 포함이 안되며, 디폴트 메소드가 가능합니다.
( 추상 메서드, 상수도 포함 가능)
- 인터페이스는 "골격"의 명세를 정의한다는것임 골격으로 정의해놓은 것을 구현 메소드로 만들어줘야합니다.
📌 인터페이스 (도형)
public interface Shape {
double calculateArea(); // 넓이
double calculatePerimeter(); // 둘레
// default 메소드
default void print(){
System.out.println("안녕하세요");
}
}
📌 구현체 클래스 Circle (원)
public class Circle implements Shape {
private static final double PI = 3.14159;
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return PI * radius * radius;
}
@Override
public double calculatePerimeter() {
return 2 * PI * radius;
}
}
📌 구현체 클래스 사각형
public class Rectangle implements Shape {
private int width;
private int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
@Override
public double calculatePerimeter() {
return (width + height) * 2;
}
}
📌 Main
public class Main {
public static void main(String[] args) {
Shape rectangle = new Rectangle(5, 30);
Shape circle = new Circle(3.5);
System.out.println("사각형의 넓이 : " + rectangle.calculateArea());
System.out.println("사각형의 둘레 : " + rectangle.calculatePerimeter());
System.out.println("원의 넓이 : " + circle.calculateArea());
System.out.println("원의 둘레 : " + circle.calculatePerimeter());
rectangle.print(); // default 메소드 확인
}
}
🙋♂️
Main 클래스를 보게되면은 default 메소드 의 출력결과를 확인할수 있으며, "넓이" "둘레" 라는 동일한 기능을 인터페이스의 추상메소드로 정의하였고
클래스에서는 추상메소드를 구현하여 "사각형" "원" 이라는 인스턴스에 구현한 클래스를 맞춰 도형에 대한 넓이와 둘레를 구하는 것입니다.
여기에서도 위에서 정리한 DIP 개념이 적용된것입니다.
Shape rectangle = new Rectangle(5, 30);
Shape circle = new Circle(3.5);
- 클래스간의 결합도를 낮추고, 의존성을 관리하기 쉽게 해주게됩니다. (중요)
- 인터페이스는 추상 메소드로 "이런 명칭으로 만들어야 된다"라고 정하기 때문에 메소드명의 혼동이 없습니다.
📌 Arm 인터페이스
public interface Arm {
void movingArm();
}
📌 Leg 인터페이스
public interface Leg {
void movingLeg();
}
📌 Human 인터페이스
( Arm, Leg 인터페이스를 다중 상속하여 Human 이라는 인터페이스로 모았습니다.)
public interface Human extends Arm, Leg {
@Override
void movingArm();
@Override
void movingLeg();
}
📌 Moving 구현 클래스
public class Moving implements Human{
@Override
public void movingArm() {
System.out.println("팔을 움직입니다.");
}
@Override
public void movingLeg() {
System.out.println("다리를 움직입니다.");
}
}
📌 Main
public class Main {
public static void main(String[] args) {
Human human = new Moving();
human.movingArm(); // 팔을 움직입니다
human.movingLeg(); // 다리를 움직입니다.
}
}
✔ 인터페이스는 추상 클래스와 달리 다중 상속이 가능하게 됩니다.
⭕ 공통점
- 추상 클래스와 인터페이스 모두 추상 메서드를 포함한다.
- 둘다 상속을 통해 기능을 확장, 다형성을 지원합니다.
❌ 차이점
- 추상클래스는 추상 메소드 외에도 일반 메소드와 인스턴스 변수를 포함
반면 인터페이스는 추상 메소드, 디폴트 메소드, 정적 메소드, 상수을 가질수있습니다.- 추상 클래스는 단일 상속만 가능하지만, 인터페이스는 다중 상속을 지원합니다. ( 인터페이스 기술의 꽃 같은 존재 )
- 인터페이스는 "구현"에 초점, 추상 클래스는 "상속" 에 초점
📌 제네릭 타입 사용 예시
public class Box<T> {
private T item;
public T getItem(){
return item;
}
public void setItem(T item){
this.item = item;
}
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setItem("123");
stringBox.setItem("456");
Box<Integer> integerBox = new Box<>();
integerBox.setItem(123);
integerBox.setItem(456);
}
}
Box<String> stringBox = new Box<>();
: 타입을 String 으로 지정해줌으로, 문자열만 담을수 있습니다.
Box<Integer> integerBox = new Box<>();
: 타입을 Integer 으로 지정해줌으로, 정수만 담을수 있습니다.
📌 제네릭 메소드 사용 예시
public <T> T getFirstElement(List<T> list) {
if (list.isEmpty()) {
return null;
}
return list.get(0);
}
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");
String firstElement = getFirstElement(stringList); // "Hello"
- 배열은 크기를 정의해야되는 반면에 컬렉션은 크기를 동적으로 조절할수있습니다.
- 배열은 타입을 확실히 지정해줘야 되는 반면에 위의 제네릭의 예시처럼 저장되는 요소의 타입을 자유롭게 사용할수있습니다.
👀 컬렉션 인터페이스의 종류
Collection : 다른 컬렉션의 부모 인터페이스로 사용됩니다.
List : 순서가 있는 컬렉션을 정의하며, 중복된 원소를 허용합니다.
Set : 중복되는 원소를 허용하지 않으며, 순서가 없습니다.
Map : 키 (Key) 와 값 (Value) 각 원소는 유일한 키를 가지며, 키를 통해 값을 검색 / 조작 할수있습니다.
(Map<String, List<String>> 형으로 현업에서 많이 사용하게됩니다.)
Stack : 주로 재귀 알고리즘, 브라우저의 뒤로 가기 기능 등에서사용
Queue : 주로 작업 대기열 이벤트 처리 등에서 사용됩니다.
에러 (error) vs 예외 (exception)
반드시 코드내에서 예외 처리를 해줘야 실행 단게로 넘어갑니다.
try
{
// Exception 클래스로 예외를 실행시킨다.
throw new Exception();
} catch (Exception e)
{
// Checked Exception 이므로 예외처리를 해주는데 해주지 않으면 컴파일 실패
System.out.println("Exception");
}
대표적으로
NullPointerException,ArrayIndexOutOfBoundsException가 있습니다.
public class ExceptionEx1 {
public static void main(String[] args)
{
// 에러가 발생하지만 컴파일은 가능하다.
throw new RuntimeException();
}
}
try {
// 예외가 발생할 수 있는 코드
} catch (예외 타입1 변수명1) {
// 예외 처리 코드
} catch (예외 타입2 변수명2) {
// 예외 처리 코드
}
void 메서드명() throws 예외타입1, 예외타입2 {
// 예외가 발생할 수 있는 코드
}
throws (예외를 던짐) 으로 메소드를 정의하여 메소드를 호출하여 예외처리 합니다.class MyException extends Exception {
public MyException() {
super("사용자 정의 예외가 발생했습니다.");
}
public MyException(String message) {
super(message);
}
}
👀 이 문장을 기억하라고 하셨습니다.
면접 질문
: 인터페이스와 추상클래스의 차이
: 컬렉션과 배열의 차이
: JVM 동작 방식