멋사 Backend 28일차 🦁 JAVA 특강

신재원·2023년 5월 26일

👀 Java 특강

🙆‍♂️ 추상클래스와 인터페이스

추상클래스

  • 추상 클래스의 특징
  1. 하나이상의 추상 메소드를 포함하는 추상 클래스 이며, abstract 키워드를 통해 정의합니다.
  2. 추상 메서드는 메서드 선언만 있으며, 구현이 없는 메서드 입니다.
  3. 추상 클래스는 직접적으로 인스턴스화할 수 없으며, 상속을 통해 사용됩니다.

📌 추상 클래스

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(); // 멍멍
    }
}

💯 DIP 개념

Animal Dog = new Bird(); : DIP (의존 역전원칙) 개념이 적용됩니다.

Dog dog = new Dog; 로 인스턴스를 생성해줘도 되지만,
[추상클래스 및 인터페이스] 명칭 = new 구현체(); 로 해주는 이유는 "변하기가 쉬운 클래스에 의존하지 않는 것" 으로 해줌으로 영향을 받지 않는 상태로 개선할수 있습니다.

  • 추상 클래스의 목적

공통된 기능을 가진 클래스를 모델링하고 코드 재사용성을 높이는 것입니다.

정리

  • 위의 코드의 예시로 동물을 모델링하는 Animal 이라는 클래스를 생성할수 있습니다.
  • makeSound() 울음소리 라는 공통된 동작 (메소드)을 가지며,
    실제 동물들의 구체적인 클래스(하위 클래스)가 Animal 클래스를 상속받아 구현할 수 있습니다.
  • 현업에서는 추상클래스를 사용하긴하지만, 인터페이스를 많이 사용합니다.
    ( 추상클래스는 다중 상속을 지원 X 인터페이스은 다중상속을 지원 )

❗❗❗ 추상 클래스는 추상메소드 및 "일반 메소드"가 존재합니다.

인터페이스

❗❗❗ 인터페이스는 일반메소드는 포함이 안되며, 디폴트 메소드가 가능합니다.
( 추상 메서드, 상수도 포함 가능)

  • 인터페이스의 특징
  1. 인터페이스는 "골격"의 명세를 정의한다는것임 골격으로 정의해놓은 것을 구현 메소드로 만들어줘야합니다.

📌 인터페이스 (도형)

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);

  • 인터페이스의 목적
  1. 클래스간의 결합도를 낮추고, 의존성을 관리하기 쉽게 해주게됩니다. (중요)
  2. 인터페이스는 추상 메소드로 "이런 명칭으로 만들어야 된다"라고 정하기 때문에 메소드명의 혼동이 없습니다.

🧲 인터페이스의 다중 상속

📌 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(); // 다리를 움직입니다.
    }
}

✔ 인터페이스는 추상 클래스와 달리 다중 상속이 가능하게 됩니다.

추상클래스 / 인터페이스 공통점과 차이점

⭕ 공통점

  1. 추상 클래스와 인터페이스 모두 추상 메서드를 포함한다.
  2. 둘다 상속을 통해 기능을 확장, 다형성을 지원합니다.

❌ 차이점

  1. 추상클래스는 추상 메소드 외에도 일반 메소드와 인스턴스 변수를 포함
    반면 인터페이스는 추상 메소드, 디폴트 메소드, 정적 메소드, 상수을 가질수있습니다.
  2. 추상 클래스는 단일 상속만 가능하지만, 인터페이스는 다중 상속을 지원합니다. ( 인터페이스 기술의 꽃 같은 존재 )
  3. 인터페이스는 "구현"에 초점, 추상 클래스는 "상속" 에 초점

🎯 상황에 따른 추상 클래스와 인터페이스의 선택 기준

  • 추상클래스는 확장과 계층 구조가 필요한 경우 사용
  • 인터페이스는 공통된 동작을 정의하며, 다중 상속이 필요하고, 클래스들
    간의 관계를 약결합하고, 유연성을 갖춰야 하는 경우 사용 합니다.

📢 제네릭, 컬렉션 예외처리

제네릭

  • 제네릭 이란? 기존의 공부하면서 작성했지만 더 작성해보겠습니다.
  • 제네릭은 타입을 지정해주는것이며, 컴파일 시점에 타입이 맞지않을경우 컴파일 에러를 표시해줍니다.

📌 제네릭 타입 사용 예시

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);
    }
}
  • type을 T로 지정하는것이 관례라고 합니다.
  • type은 참조 타입만 가능합니다.

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"
  • 제네릭 메소드를 사용하면 메소드마다 타입을 지정하여 다양한 타입의 인자와 반환값을 처리할 수 있습니다.

컬렉션

  • 자바에서 데이터를 저장 조작하는데 사용하는 API 집합 입니다.

컬렉션과 배열의 차이

  1. 배열은 크기를 정의해야되는 반면에 컬렉션은 크기를 동적으로 조절할수있습니다.
  2. 배열은 타입을 확실히 지정해줘야 되는 반면에 위의 제네릭의 예시처럼 저장되는 요소의 타입을 자유롭게 사용할수있습니다.

👀 컬렉션 인터페이스의 종류

  • 컬렉션 별로 메소드는 많이 작성하다보면 익숙해 지며 특징만 소개하겠습니다.

Collection : 다른 컬렉션의 부모 인터페이스로 사용됩니다.
List : 순서가 있는 컬렉션을 정의하며, 중복된 원소를 허용합니다.
Set : 중복되는 원소를 허용하지 않으며, 순서가 없습니다.
Map : 키 (Key) 와 값 (Value) 각 원소는 유일한 키를 가지며, 키를 통해 값을 검색 / 조작 할수있습니다.
(Map<String, List<String>> 형으로 현업에서 많이 사용하게됩니다.)
Stack : 주로 재귀 알고리즘, 브라우저의 뒤로 가기 기능 등에서사용
Queue : 주로 작업 대기열 이벤트 처리 등에서 사용됩니다.

예외처리

에러 (error) vs 예외 (exception)

  • 에러는 예측이 불가능한 메모리 부족, 개발자가 직접 처리할수 없습니다.
  • 예외는 프로그램 실행 중에 예측 가능하고 개발자가 예외처리를 통해 해결할수 있습니다.

❓ 예외

  1. Checked Exception
  • 컴파일러에 확인되는 예외

반드시 코드내에서 예외 처리를 해줘야 실행 단게로 넘어갑니다.

try
{
	// Exception 클래스로 예외를 실행시킨다.
	throw new Exception();
} catch (Exception e)
{
// Checked Exception 이므로 예외처리를 해주는데 해주지 않으면 컴파일 실패
System.out.println("Exception");
}
  1. Unchecked Exception
  • 컴파일러에 확인되지 않는 예외 ( 개발자의 실수로 인해서 발생하는 예외 )

대표적으로 NullPointerException, ArrayIndexOutOfBoundsException 가 있습니다.

public class ExceptionEx1 {
	public static void main(String[] args)
	{
            // 에러가 발생하지만 컴파일은 가능하다.
		throw new RuntimeException();
	}
}

예외 처리 방법

  1. try - catch 문을 통한 예외처리 방법
try {
    // 예외가 발생할 수 있는 코드
} catch (예외 타입1 변수명1) {
    // 예외 처리 코드
} catch (예외 타입2 변수명2) {
    // 예외 처리 코드
}
  • 위의 코드에서 사용한 try - catch문 입니다.
    처리할 예외들이 많아질수록 catch문이 증가하며 catch문이 증가할수록 코드의 가독성을 떨어지게 됩니다.
  1. throws를 통한 예외 처리
void 메서드명() throws 예외타입1, 예외타입2 {
    // 예외가 발생할 수 있는 코드
}
  • 메소드 뒤에 throws (예외를 던짐) 으로 메소드를 정의하여 메소드를 호출하여 예외처리 합니다.
  1. 사용자 정의 예외 처리
class MyException extends Exception {
    public MyException() {
        super("사용자 정의 예외가 발생했습니다.");
    }
    
    public MyException(String message) {
        super(message);
    }
}
  • Exception 클래스를 상속하여 사용하는 방법이며 생성자를 통해 예외 메시지를 설정할수 있습니다.

JVM 동작 방식 및 구조

  • 자바 컴파일러에 의해 생성된 클래스 파일은 JVM에서 실행 가능한 자바 바이트코드로 변환되어 JVM에 의해 적재되고 실행됩니다.

👀 이 문장을 기억하라고 하셨습니다.

📣 번외 (면접 질문)

면접 질문
: 인터페이스와 추상클래스의 차이
: 컬렉션과 배열의 차이
: JVM 동작 방식

0개의 댓글