초록꼬마의 devlog
article thumbnail

2021.10.29(금)

🌿 객체 지향 프로그래밍

🌱 추상클래스

  • 표현법: 접근제한자 abstract class 클래스이름 (abstract: 예약어로써, 추상클래스, 추상메소드 선언 시 붙임)
  • 추상메소드가 존재하는 순간 추상클래스로 정의됨 → abstract라는 예약어를 사용해서 정의해야 함
  • 객체 생성이 불가능함
  • 부모 클래스 구실은 가능 → 다형성 적용 가능
  • 추상 클래스 = 미완성 설계도, 구체적인 구현을 빼버리고 추상화만 한 것
  • 추상 클래스 = 추상메소드 + 일반필드 + 일반메소드 → 3가지 모두 생략 가능, 개발자의 역량/판단에 따라 추상 메소드가 없어도 추상 클래스로 정의 가능
  • 사용 examples
    • 개념적으로 개발자가 판단했을 때 해당 클래스가 아직 구체적으로 덜 구현된 상태인 것 같을 때
    • 기술적으로 개발자가 판단했을 때 이 클래스는 객체 생성이 불가능해야 할 때
  • 추상 메서드 사용 시 메서드 사용의 통일성 확보 가능, 표준화된 틀 제공 ← 상속받은 자식클래스는 반드시 미완성/추상 메소드를 완성시켜야 함
public abstract class Sports {    
    private int people; // 선수 숫자

    public Sports() {}

    public Sports(int people) {
        this.people = people;
    }

    public int getPeople() {
        return people;
    }

    public void setPeople(int people) {
        this.people = people;
    }

    @Override
    public String toString() { // toString을 오버라이딩 받아서 쓴다는 의미; 자식클래스에서는 toString() 안 씀
        return "people : " + people;
    }

    // 추상메소드 (vs 구체클래스(구현을 구체적으로 해 둔 것))
    // 표현법: 접근제한자 abstract 반환형 메소드이름();
    public abstract void rule(); // 메소드 선언했는데, 메소드 signature만 있고, 메소드의 몸통(body, code block) 없음
    // 자식들에게 자료형을 나눠주기 위한/다형성을 위한 용도로만 사용, Sports의 부모클래스로서만 존재 ← 규칙이 없는 Sports 종목은 없음    
    // method body가 존재하지 않는 미완성 메소드 → 미완성 메소드가 하나라도 포함되는 순간 해당 클래스는 미완성(추상) 클래스가 됨 → 해당 클래스명 앞에도 abstract 예약어 씀
}

🌱 인터페이스

  • 상수 필드와 추상 메소드만으로 이루어진, 추상 클래스의 변형체(인터페이스는 클래스가 아님)
  • 표현법:
접근제한자 interface 인터페이스이름 {
          상수 필드 // 인터페이스의 모든 필드들은 암묵적으로 public + 상수 필드(키워드 static final -> static 메모리 영역에 프로그램 시작 시 할당되어 프로그램 종료 시까지 존재, 프로그램 동작 중에 같은 이름의 변수가 생길 일 없음)
          추상 메소드 // 인터페이스의 모든 메소드들은 암묵적으로 public + abstract
}
  • 무조건 구현해야 하는 것이 있을 때 인터페이스에 틀만 만들어두고 상속하게 됨
  • 추상 클래스보다 좀 더 강한 규칙성/강제성을 가짐
  • Java에서는 메소드 및 필드 충돌 때문에 단일 상속(한 번에 한 번 상속)만 가능 vs 인터페이스는 다중 상속을 허용/다중 상속해도 문제 발생 여지 없음(메소드가 겹치더라도 최종 구현 부분은 실제 구현 클래스에서 기술하기 때문에)
  • 인터페이스 간의 상속 관계: 자식인터페이스 extends 부모인터페이스1, 부모인터페이스2.. (다중 가능)

추상 클래스 vs 인터페이스
공통점

  • 객체 생성은 안 되나, 참조변수로 사용은 가능(-> 다형성 적용 가능)
  • 상속하는 클래스에 추상 메소드를 오버라이딩(추상 메소드의 몸통 구현)하도록 강제
  • 추상클래스도 추상클래스를 상속할 수 있고, 인터페이스는 인터페이스를 상속할 수 있음

차이점: 존재하는 목적이 다름

  • 추상 클래스: 클래스 멤버로 변수 및 함수 생성이 가능하고, 추상 메소드가 포함되어 있거나 abstract 키워드를 통해 정의됨 -> 추상 클래스를 상속받아 기능을 이용하고 자식 클래스에서 클래스 확장해서 씀
  • 인터페이스: 모든 필드는 상수 필드 + 모든 메소드는 추상 메소드로 정의 -> 클래스의 기능/메소드 구현을 강제하기 위해 사용 -> 구현을 강제함으로써 구현 객체의 같은 동작을 보장할 수 있음
  • extends(상속받다, 클래스 간의 상속 관계, 자식클래스 extends 부모클래스) vs implements(구현하다, 클래스와 인터페이스의 구현 관계, 클래스 implements 인터페이스)

🌿 예외처리

  • errors(오류)의 종류

    • 시스템 에러: 컴퓨터의 오작동으로 인해 발생하는 에러 -> 소스코드로 해결이 안 됨; 심각함..
    • 컴파일 에러: 프로그램 실행 전 소스코드 상의 문법적인 문제로 발생하는 에러 -> 빨간줄로 알려줌 -> 소스코드 수정으로 해결
    • 런타임 에러: 프로그램 실행 중 발생하는 에러; 소스코드 상 문법적인 문제가 없는데 발생(빨간줄 안 생김) <- 개발자가 예측 가능한 경우 제대로 처리를 안 해 놓았을 경우 ou 사용자의 잘못일 경우(버그)
    • 논리 에러: 소스코드 상 문법적인 문제도 없고, 실행했을 때도 굳이 문제가 발생하지 않지만, 프로그램 의도 상 맞지 않음; 기획과 설계(내가 가진 도구들을 가지고 어떻게 만들지 고민)가 잘못된 경우 발생; 찾는 데 시간 아주 오래 걸림
  • 예외: 시스템 에러를 제외한 나머지(컴파일, 런타임, 논리 에러) 비교적 덜 심각한 에러들; 개발자가 해결 가능함; 프로그램을 만든 개발자/프로그래머가 예상한 정상적인 처리에서 벗어나는 경우

    • exception 클래스를 상속받는 클래스 = checked exception
    • RunTime exception(프로그램 실행 시 발생되는 예외들) 클래스를 상속받는 클래스 = unchecked exception -> 빨간줄 안 뜸, 개발자가 완벽하게는 아니더라도 어느 정도 예측이 가능함 -> 조건문으로 해결 가능함 -> 굳이 예외처리 할 필요 없음
      e.g. ArrayIndexOutOfBoundsException: 배열의 부적절한 index로 접근할 때 발생하는 예외
      NegativeArraySizeException: 배열의 크기를 음수로 지정할 경우 발생하는 예외
      ClassCastException: 상속 구조가 아닌 경우 등 허용할 수 없는 형변환이 진행될 경우 발생하는 예외
      NullPointerException: 객체를 참조했더니 주소값이 null이 들어있을 경우 발생하는 예외
      ArithmeticException: 나누기 연산 시 0으로 나누면 발생하는 예외 등
  • 예외 처리: 오류 발생을 막기 위해서(x) 예외들이 발생했을 경우를 대비해서 -> 예외가 발생하더라도(오류 발생은 막을 수 없음) 잘 동작하도록 하기 위해(o) 예외 처리는 항상 해줘야 하고, 하는 방법은 많음 -> 상황에 따라 다름

  • 예외 처리 방법

  1. 조건문
  2. try ~ catch문
  3. 다중 catch block
  4. throws 등
public class B_CheckedException {    
    /* CheckedException: 문법적으로 반드시 예외 처리를 해줘야 하는 예외들; 빨간 줄 나옴
     * 예측 불가능한 곳에서 발생 -> 미리 예외처리 구문을 작성해야 함
     * 주로 외부 매체와 어떤 입출력 시 발생
     */
    public void method1() { // 메소드2 호출
        try {
            method2();
        }
        catch(IOException e) {
            System.out.println("IOException 예외 발생함");
        }
    }

    public void method2() throws IOException { // 문자열 입력받는 메소드
        System.out.print("아무 문자열이나 입력해주세요~");

        // Scanner와 같이 키보드로 값을 입력받을 수 있는 객체(단, 문자열로만 가능)
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

//        String str = br.readLine(); // 아래 예외처리 안 하면 "Unhandled exception type IOException(이 메소드 호출 시 IOException 발생할 수도 있음을 알려줌)" error 발생 -> 예외 처리 반드시 해줘야 문제없이 실행됨 

        // 방법1) try ~ catch: 내가 해결하겠다
        try { // 문제 발생 여지가 있는 부분을 try 블럭{} 안에 넣음
            String str = br.readLine();
            System.out.println("문자열의 길이 : " + str.length());
        }
        catch(IOException e) {
            System.out.println("IOException 예외 발생함");
        }

        // 방법2) throws: 내가 지금 여기(method2)에서 처리 안 하겠다고, 메소드 호출한 곳(method1)으로 예외를 떠넘겨서/위임해서/던져서 처리하게 함
        // 왠만하면 자신에게 발생한 예외는 자신이 처리하는 것이 좋음
        // 떠넘기는 경우 예시: 내가 하기 귀찮거나.. 내가 할 필요가 없거나.. 예외들 모아서 예외처리하는 곳을 만들어둔 경우..
        String str = br.readLine();

        System.out.println("문자열의 길이 : " + str.length());        
    }
}

📗 소감

  • 약 3주간의 Java 기본 문법 수업이 마무리되었다. 아직 Java의 정석 2권 내용은 거의 모르는데, 일단 Java 수업 끝이라니.. 2중/중첩 배열 연습도 못했는데.. DB 수업 후 11월 중순에 조금 더 알려주신다고 하니, 일단 객체 지향 개념 잊지 않도록 종종 연습해야겠다.
  • 특히 오늘 배운 내용은 비교적 적은 예시로 빨리 지나갔는데, 개념이 생소할 뿐만 아니라 어디에 왜 사용하게 될지도 아직 잘 모르겠다. 그래서 더 어려운 느낌이다.
  • 예외 처리도 중요하다고 들었는데, 충분히 예제를 보고 연습해서 익힐 시간 없이, 수업에서 너무 금방 지나갔다. 보충해서 공부하자!

📗 homework: 상속
(실습 파일 위치: 08 Inheritance > src > com > kh > hw > person)