클라우드 융합 Full-stack 웹 개발자 양성과정/Java

프로그래밍 언어응용-추상화, 인터페이스, 예외처리

thesunset 2022. 9. 25. 17:20

# Sports

package com.kh.chap02_abstractAndInterface.part01_basic.mode.vo;

> 추상클래스

1. [표현법]

접근제한자 abstract class 클래스이름 {

abstract 예약어 -> 추상클래스, 추상메소드 선언할 때 붙임.

 

- 추상메소드가 존재하는 순간 추상클래스로 정의

* abstract라는 예약어를 사용해서 정의해야함

* 객체생성이 불가능함. 부모클래스 구실은 가능(다형성 적용 가능)

* 추상메소드는 있을수도 있고 없을수도 있음

 

* 추상클래스 == 일반필드 + 일반메소드 + 추상메소드(생략가능)

* => 추상메소드가 없어도 추상클래스로 정의 가능

* 언제사용? 개발자의 역량

 

* => 기술적으로 개발자가 판단했을 때 이 클래스는 객체생성이 불가능해야한다라고 생각이들면 추상클래스로 가능

* => 개념적으로 개발자가 판단했을 때 해당 클래스가 아직 구체적으로 덜 구현된 상태인 것 같다라고 생각이 들면 추상클래스로 가능

 + 일관된 인터페이스 제공, 꼭 필요한 기능 강제화

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

> 추상메소드

1. [표현법]

접근제한자 abstract 반환형 메소드이름(); {뒤에괄호없음}

public abstract void rule();

* method body가 존재하지 않는 미완성 메소드

* abstract 예약어를 써서 정의해야 함.

 

* 미완성메소드가 하나라도 포함되는 순간 해당 클래스는 미완성 클래스(추상클래스)가 됨

* 해당 클래스 앞에도 abstract라고 써줘야함

@Override

public String toString() {

return "Sports [people=" + people + "]";

}

}


# Soccer

package com.kh.chap02_abstractAndInterface.part01_basic.mode.vo;​

public class Soccer extends Sports {

@Override
public void rule() {
System.out.println("손으로 공잡지 않기");
	}
}

- 만약 추상클래스의 메소드를 오버라이딩 하지 않으면, 에러가 발생함. 


# Baseball

package com.kh.chap02_abstractAndInterface.part01_basic.mode.vo;​

public class Baseball extends Sports {

@Override
public void rule() {
System.out.println("경기장에 맥주들고가지 않기");​
	}
}

# Run

package com.kh.chap02_abstractAndInterface.part01_basic.run;

import com.kh.chap02_abstractAndInterface.part01_basic.mode.vo.*;

public class Run {

public static void main(String[] args) {

//추상클래스로 객체 생성 불가!

//클래스가 미완성되었기때문

//Sports s = new Sports(); 생성자에만 에러

//Sports s; //참조형 변수로 사용가능 => 다형성으로 적용가능

//s = new Sports(); //객체생성 불가능

//Sports s = new Soccer();

//다형성을 이용해서 자식객체를 받아주는 용도로 사용가능

//객체배열

Sports[] arr = new Sports[2];

arr[0] = new Soccer();

arr[1] = new Baseball();

for(int i = 0; i<arr.length; i++) {

arr[i].rule();

}

}

}


# Person

package com.kh.chap02_abstractAndInterface.part02_family.model.vo;​

public abstract class Person { //추상클래스 

private String name;
private double weight;
private int health;

public Person() {
}

public Person(String name, double weight, int health) {
this.name = name;
this.weight = weight;
this.health = health;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getWeight() {
return weight;
}

public void setWeight(double weight) {
this.weight = weight;
}

public int getHealth() {
return health;
}

public void setHealth(int health) {
this.health = health;
}

public abstract void eat(); //추상메소드
public abstract void sleep(); //추상메소드 


@Override
public String toString() {
return "Person [name=" + name + ", weight=" + weight + ", health=" + health + "]";
	}
}

# Mother

package com.kh.chap02_abstractAndInterface.part02_family.model.vo;

public class Mother extends Person implements Basic { //Person상속, Basic인터페이스

private int babyCount;

public Mother() {}

public Mother(String name, double weight, int health, int babycount) {
super(name, weight, health);
this.babyCount = babyCount;
}


public int getBabyCount() {
return babyCount;
}

public void setBabyCount(int babyCount) {
this.babyCount = babyCount;
}

​

@Override
public String toString() {
return super.toString() + "Mother [babyCount=" + babyCount + "]";
}


@Override
public void eat() { //추상메소드오버라이딩
super.setWeight(super.getWeight()+10);
super.setHealth(super.getHealth()+15);
}

@Override
public void sleep() { //추상메소드오버라이딩
super.setHealth(super.getHealth()+15);
	}
}

# Baby

package com.kh.chap02_abstractAndInterface.part02_family.model.vo;

public class Baby extends Person implements Basic​​{ //인터페이스는 이중상속이 가능

//필드부
//없음
//생성자부
//기본 매개변수

public Baby() { }
public Baby(String name, double weight, int health) {
super(name, weight, health);
}

@Override
public String toString() {
return "Baby [" + super.toString() + "]";
}

@Override
public void eat() { //밥을 먹으면
//몸무게 5증가
//weight == 부모의 필드 + 5
//수정할 몸무게 == 기존의 몸무게 + 5 -> set
super.setWeight(super.getWeight()+5);
//건강도 5증가
super.setHealth(super.getHealth()+5);
}

@Override
public void sleep() {
//건강도 10증가
super.setHealth(super.getHealth()+10);
}
}

# Basic - 인터페이스

package com.kh.chap02_abstractAndInterface.part02_family.model.vo;

>  인터페이스

 [표현법]

* 접근제한자 interface 인터페이스이름{

상수필드

추상메소드

 }

 

* - 상수필드와 추상메소드만으로 이루어진 추상클래스의 변형체

* - 인터페이스의 필드는 "무조건" 상수필드

* - 인터페이스의 메소드는 "무조건" 추상메소드

 

* - 인터페이스는 다중상속을 허용한다. = 가장 큰 속성

* => 메소드가 겹치더라도 최종구현부분은 실제구현클래스에서 기술함(*문제없음)

* - "무조건"구현해야하는게 있을 때 인터페이스 틀만 만들어주고 상속하게 만듬 => 오버라이딩

 

* 추상클래스보다 좀 더 강한 규칙성, 강제성을 가진다.

 

* 추상클래스와 인터페이스

1. 공통점

 - 객체 생성은 불가능하지만, 참조변수로 사용이 가능하다.(다형성을 적용할 수 있다)

 - 상속받는 클래스에서 추상메소드를 오버라이딩 하도록 강제한다.

2. 차이점

 - 추상클래스: 클래스 멤버로, 일반필드, 일반메소드 생성이 가능하고, 추상메소드가 포함되어있거나 abstract키워드를 통해 정의됨

 - 인터페이스: 모든 필드는 다 상수 필드, 모든 메소드를 추상메소드로 정의해야 함

 => 존재하는 목적이 다름:

* 추상클래스는 추상클래스를 상속받아서 멤버변수(필드 메소드)를 이용하고 클래스를 확장하기 위한 용도

* 인터페이스는 클래스 기능(메소드)를 강제하기 위해서 사용.

 

* extends와 implements

* - 클래스 간의 상속관계: 자식클래스 extends 부모클래스

* - 클래스와 인터페이스 구현관계: 클래스 implements 인터페이스

 

* 만약 다중상속을 하는 경우,

* implements 인터페이스1, 인터페이스2

 

public interface Basic {

 

//인터페이스에서는 무조건 상수필드와 추상메소드밖에 정의가 불가하므로

//접근제한자, 예약어 생략 가능

int num = 10;//public static final(상수필드)

void sleep(); //public abstract(추상메소드)

void eat();

 

=> 인터페이스의 모든 필드들은 암묵적으로 public static final

=> 인터페이스의 모든 메소드들은 암묵적으로 public abstract

}


 

# Run

package com.kh.chap02_abstractAndInterface.part02_family.run;

import com.kh.chap02_abstractAndInterface.part02_family.model.vo.*;

public class Run {

public static void main(String[] args) {

//Person p = new Person();

//person은 추상클래스이므로 객체생성불가!

1. 다형성적용, 인터페이스적용 전

Person mom = new Mother("엄마", 50, 90, 1);
Person baby = new Baby("응애", 5.3, 90);

System.out.println(mom);
System.out.println(baby);

mom.eat();
baby.eat();
mom.sleep();
baby.sleep();

System.out.println("====다음날====");

System.out.println(mom);
System.out.println(baby);

2. 인터페이스 적용 후

//Basic b = new Basic(); //객체 생성 불가 

Basic mom = new Mother("엄마", 50, 90, 1);
Basic baby = new Baby("응애", 5.3, 90);

System.out.println(mom);
System.out.println(baby);​

mom.eat();
baby.eat();
mom.sleep();
baby.sleep();

System.out.println("====다음날====");​

System.out.println(mom);​
System.out.println(baby);​
}
}

# Run

package com.kh.run;

import com.kh.controller.A_UncheckedException;

import com.kh.controller.B_CheckedException;

public class ExceptionRun {

 

> 에러(예외)의 종류

 - 시스템 에러 : 컴퓨터의 오작동으로 인해 발생하는 에러

 => (심각) 소스코드로 해결이 안됨

 

 - 컴파일 에러: 프로그램 실행 전 소스코드 상의 문법적인 문제로 발생하는 에러

 => 소스코드 수정으로 해결(빨간줄로 오류 알려줌)

 

 - 런타임 에러: 프로그램 실행 중 발생하는 에러

 소스코드 상 문법적인 문제는 없는데 발생

 => 개발자가 예측 가능한 경우 처리를 안했을 때

 => 사용자의 잘못일 가능성도 있음

 

 - 논리 에러 : 소스코드 상 문법적인 문제도 없고, 실행했을 때도 굳이 문제가 발생하진 않지만

 프로그램의도상 맞지 않는 것(기회과 설계가 잘못되었을 경우 발생)

 

* 예외: 시스템에러를 제외한 나머지 컴파일, 런타임, 논리에러와 같이 비교적 덜 심각한 에러들

특히 런타임에러를 주로 예외로 다룬다.

* 예외처리: 예외들이 발생했을 경우에 대비해서 처리하는 방법을 정의해두는 것

 

> 방법

 1. try ~ catch문을 이용

 2. throws를 이용(떠넘기기)

 

public static void main(String[] args) {

//A_UncheckedException a = new A_UncheckedException();
//a.method1();
//a.method2();
//a.method3();

B_CheckedException b = new B_CheckedException();
//b.method2();
b.method1();
	}
}

 


# A_UncheckedException

package com.kh.controller;

import java.util.InputMismatchException;

import java.util.Scanner;

public class A_UncheckedException {

* RuntimeException

: 프로그램 실행 시 발생되는 예외들

 

* RuntimeException의 자식클래스들

 - NullpointerException: 주소값 객체를 참조했더니 null이 들어있을 경우 발생하는 예외

 - ArrayIndexOutOfBoundsException: 배열의 부적절한 인덱스로 접근할 때 발생하는 예외

 - ClassCastException: 허용할 수 없는 형변환을 진행할 경우 발생하는 예외

 - ArithmeticException: 나누기 연산을 0으로 나누면 발생하는 예외

 - NagativeArraySizeException: 배열크기를 음수로 지정할 경우 발생하는 예외

 .....

 

* 이러한 RuntimeException과 관련된 예외들의 공통점

 => 개발자가 예측이 가능한 것들이다.

* 굳이 예외처리를 할 필요가 없음 => 조건문으로 해결가능함.

 

ex) ArithmeticException

public void method1() {

//사용자에게 두 개의 정수값을 입력받아서 나눗셈결과 출력

Scanner sc = new Scanner(System.in);​

System.out.println("첫 번째 정수>");​
int num1 = sc.nextInt();
System.out.println("두 번째 정수>");
int num2 = sc.nextInt();
sc.nextLine();

방법1. 조건문으로 처리(예외발생을 막음)

: if문으로 조건검사 후에 계산을 진행 => 예외가 안나오도록

 

if(num2!=0) {
System.out.println("나눗셈 결과: " + (num1/num2));
} else {System.out.println("잘못된 값입니다.");
}System.out.println("종료합니다.");​

방법2. 예외처리 구문

: 예외가 발생했을 때 실행할 내용을 정의해두는 것

​> try ~ catch 문

[표현법]

try {

//예외가 발생할 법한 구문

(num1/num2)

             (ArithmeticException)    e

} catch (발생할예외클래스이름 변수명 => 일종의 매개변수) {

 //해당 예외가 발생했을 때 실행할 구문

}

try {
System.out.println("나눗셈 결과: "+ (num1/num2));​
System.out.println("문제가 발생하지 않았습니다."); //만약 문제 발생 시 이 구문은 무시한 채 바로 catch구문으로 넘어감 ​
} catch(ArithmeticException e) {
//System.out.println("잘못된 값입니다.");
e.printStackTrace();//오류가 발생한 정보 , 오류를 추적할 수 있는 메소드
//현재 오류가 발생한 정보를 볼 수 있고, 어디서 에러가 났는지 나열해줌
//개발단계와 테스트단계에서만 써야 함. 반드시!! 에러문구를 사용자가 보면 안 됨. => 악의적으로 이용당할 수 있음.
}
System.out.println("종료합니다."); //문제를 해결하는 것이 아닌, 문제를 예방
//문제가 발생한 곳에서 프로그램이 종료되는 것이 아닌 문제 발생 시 실행할 구문을 실행하고
//그 다음 구문도 실행해줌.(if문과 try ~ catch구문의 차이점)
}

 

> 다중 catch문

public void method2() {

* 다중 catch블록

* catch할 예외가 여러개일 경우 주로 사용

 

Scanner sc = new Scanner(System.in);

System.out.println("정수입력(0제외) > ");

try {
	int num = sc.nextInt(); //1. 정수 이외 입력

	System.out.println("나눗셈결과: " + (100/num)); //2. 0입력

} catch(InputMismatchException e) { //1. 우선적으로 입력받은게 정수인지 판단
System.out.println("정수를 입력해주세요.");​

} catch(ArithmeticException e) { //2. 정수라면 0인지 아닌지 판단
System.out.println("0을 제외하고 입력해주세요.");
	}
	System.out.println("종료합니다.");
}

public void method3() {

ex) 배열 활용

- ArrayIndexOutOfBoundsException : 배열의 부적절한 인덱스로 접근할 때 발생하는 예외

- NegativeArraySizeException : 배열의 크기를 음수로 지정할 때 발생하는 예외

//사용자로부터 배열의 크기를 입력받아서 배열 만들기

//100번째 인덱스 값을 출력하기

Scanner sc = new Scanner(System.in);

System.out.println("배열의 크기 > ");

try {

int size = sc.nextInt();//1. InputMismatchException

int[] arr = new int[size];//2. NegativeArraySizeException

System.out.println(arr[100]);//3. ArrayIndexOutOfBoundsException

}

//catch(RuntimeException e) { //부모클래스로 포괄적으로 접근

//System.out.println("배열에러");

1.  if문 사용 시 

if(e instanceof InputMismatchException) {
	System.out.println("정수");​
} else if(e instanceof NegativeArraySizeException) {
	System.out.println("양수");​
} else if (e instanceof ArrayIndexOutOfBoundsException ) {
	System.out.println("100이상 입력");
} => 너무 귀찮

2. 다중 catch문 사용 시

catch(RuntimeException e) {
	System.out.println("배열에러");
}

​RuntimeException가 부모 클래스이기 때문에 처리가능

뭉뚱그려서 포괄적인 오류처리만 가능함 => 단점

//System.out.println("배열의 크기가 잘못됐거나, 부적적한 인덱스가 입력됐거나, 정수가 아닌 값이 입력됨..?");

catch(InputMismatchException e) {
	System.out.println("정수를 입력해주세요.");
} catch(NegativeArraySizeException e) {
	System.out.println("양수를 입력해주세요.");
} catch(ArrayIndexOutOfBoundsException e) {
	System.out.println("100이상을 입력해주세요.");
}

//=> 다중 catch문 작성 시 범위가 작은 자식타입의 예외클래스부터 기술해야 함. 반대로 적으면 죽은 클래스가 됨.


# B_CheckedException

package com.kh.controller;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.util.Scanner;

public class B_CheckedException {

 

> CheckedException

* 문법적으로 반드시 예외처리를 해줘야하는 예외들!

* (예측불가한 곳에서 발생하기 때문에 미리 예외처리 구문을 작성해야함)

=> 주로 외부매체와 어떤 입출력 시 발생

public void method2() throws IOException {

System.out.println("아무 문자열이나 입력해주세요.>");​

//Scanner sc = new Scanner(System.in);​
//String str = sc.nextLine();


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

방법1. try ~ catch구문 사용

try {
	String str = br.readLine();//예외에 대한 핸들링을 해줘야 함 (무조건 예외처리를 해줘야 함)
    //이 메소드 호출 시 IOException발생할 수도 있음
	System.out.println("문자열의 길이: " + str.length());//미리 빨간줄로 알려줌
} catch(IOException e) {
	System.out.println("IOException발생하였습니다.");​
}

방법2. throws(던짐) : 지금 여기서(method2)에서 말고 현재 이 메소드를 호출한 곳에서 예외를 위임해서 처리하게끔 하겠다.

String str = br.readLine();
System.out.println("문자열 길이: " + str.length());
} //method2 끝

public void method1() {
//method2();//IOException오류위임받음-throws로

try {
method2();
} catch (IOException e) {
System.out.println("예외 발생");​
		}
	}
}