[JAVA] TIL 013 - 23.07.28

유진·2023년 7월 28일

09_다형성(Polymorphism) : 추상

  • 추상(=설계) : 공통적인 것 추출 / 개별적인 것 제외
    -> 오버라이딩 강제화!
  • 추상클래스 : 객체화가 될 수 없음
    상속 받은 자식 클래스에서 객체 사용 가능
package edu.kh.poly.ex2.model.vo;

public abstract class Animal {
	// 추상 클래스 (abstract class)
	// 1. 미완성 메소드(추상 메소드)를 보유하고있는 클래스
	// 2. 객체로 만들면 안되는 클래스
	// -> 여러 타입들을 관리하기 위한 상위 타입의 목적
	//   객체로 생성하여 사용하기 위한 목적이 아니다
	

	
	
	// 필드
	private String type; // 종/과 구분
	private String eatType; // 식성(초식, 육식, 잡식)
	
	// 생성자
	public Animal() {} // 기본생성자
	
	// - 추상 클래스는 new 연산자를 통해 직접적인 객체 생성은 불가능하지만
	//   상속받은 객체 생성 시 내부 부모 부분이 생성될 때 사용된다.
	
	
	public Animal(String type, String eatType) { // 매개변수 생성자(오버로딩 적용)
		this.type = type;
		this.eatType = eatType;
	}

	// getter / setter
	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getEatType() {
		return eatType;
	}

	public void setEatType(String eatType) {
		this.eatType = eatType;
	}
	
	// toString() 오버라이딩
	@Override // 오버라이딩이 되었음을 컴파일러에게 알려주는 어노테이션
	public String toString() {
		return type + " / " + eatType;
	}
	
	// 동물의 공통 기능 추출(추상화)
	// -> 동물은 공통적으로 먹고, 숨쉬고, 움직이지만
	//   어떤 동물이냐에 따라 그 방법이 다름!
	//    -> 해당 클래스에 메소드를 정의할 수 없다!
	//   --> 그럼 어떡할까?
	//   미완성 상태로 두어 상속받은 자식이 해당 메소드를 정의 하도록
	//   오버라이딩 강제화 시킴 --> 추상(abstract) 메소드로 작성
	
	// 먹다
	public abstract void eat(); // 추상 메소드
	
	// 숨쉬다
	public abstract void breath();
	
	// 움직이다
	public abstract void move();

}
package edu.kh.poly.ex2.model.vo;

public class Person extends Animal{
	// Animal의 추상 메서드를 오버라이딩 하지 않으면 오류 발생
	
	private String name;
	
	public Person() {}
	
	
	public Person(String type, String eatType, String name) {
		super(type, eatType);
		this.name = name;
	}



	// getter / setter
	public String getName() {
		return name;
	}

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

	@Override
	public void eat() {
		System.out.println("숟가락, 젓가락 등 도구를 이용해서 먹는다.");
		
	}
	
	@Override
	public void breath() {
		System.out.println("코와 입으로 숨쉰다.");
		
	}

	@Override
	public void move() {
		System.out.println("두발로 걷는다.");
		
	}
	
	// toString() 오버라이딩
	@Override
	public String toString() {
		return "Person : " + super.toString() + " / " + name;
	}

}
package edu.kh.poly.ex2.model.vo;

public class Fish extends Animal{
	
	public Fish() {}
	
	public Fish(String type, String eatType) {
		super(type, eatType);
	}

	@Override
	public void eat() {
		System.out.println("입을 뻐끔 뻐끔 거리면서 먹는다.");
		
	}

	@Override
	public void breath() {
		System.out.println("아가미 호흡을 한다.");
		
	}

	@Override
	public void move() {
		System.out.println("꼬리로 헤엄치며 움직인다.");
		
	}
	
	@Override
	public String toString() {
		return "Fish : " + super.toString(); 
	}

}
package edu.kh.poly.ex2.model.service;

import edu.kh.poly.ex2.model.vo.Animal;
import edu.kh.poly.ex2.model.vo.Fish;
import edu.kh.poly.ex2.model.vo.Person;

public class AbstractService {

	public void ex1() {
		
		//Animal a1 = new Animal();
		// Cannot instantiate the type Animal (객체화 할수없음)
		
		// 클래스 : 객체의 속성, 기능을 정의한 것(일종의 설계도)
		// 추상 클래스 : 미완성 메서드를 포함한 클래스(미완성 설계도)
		// -> 미완성 설계도로는 객체를 만들 수 없다! -> 오류발생
		
		// 해결 방법 : Animal을 상속받아 미완성 부분을 구현한
		// 				클래스를 이용해 객체 생성
		
		// 추상클래스를 상속받은 자식 객체 생성
		Person p1 = new Person();
		
		p1.setName("홍길동");
		
		// 상속받은 기능 호출
		p1.setType("척추동물");
		p1.setEatType("잡식");
		
		// 오버라이딩한 메서드 호출
		p1.eat(); // 숟가락, 젓가락 등 도구를 이용해서 먹는다.
		p1.breath(); // 코와 입으로 숨쉰다.
		
		
		Fish f1 = new Fish();
		
		f1.eat(); // 입을 뻐끔 뻐끔 거리면서 먹는다.
		f1.breath(); // 아가미 호흡을 한다.
		
	}
	
	public void ex2() {
		// * 추상 클래스와 다형성 + 바인딩
		
		// - 추상클래스는 객체로 만들수 없다!!!
		// --> 하지만 "참조 변수"로는 사용할 수 있다!
		
		//Animal a1 = new Animal(); (X)
		
		// 사람 -> 동물   /   물고기 -> 동물
		//Animal a1 = new Person(); (O)
		//Animal a2 = new Fish();   (O)
		
		Animal[] arr = new Animal[2];
		// Animal 참조변수 배열 선언 및 할당
		
		arr[0] = new Person("사람", "잡식", "김사람");
		// Animal 부모 = Person 자식(다형성 중 업캐스팅)
		
		arr[1] = new Fish("물고기", "잡식");
		// Animal 부모 = Fish 자식(다형성 중 업캐스팅)
		
		// 바인딩 확인
		for(int i = 0; i < arr.length; i++) {
			// arr[i] == Animal 참조변수
			arr[i].eat();
			arr[i].breath();
			System.out.println(arr[i].toString());
			
			// void edu.kh.poly.ex2.model.vo.Animal.eat() - 정적바인딩
			
			System.out.println("-------------------------------");
			
			// 프로그램 실행 시
			// 참조하고 있는 자식 객체의 오버라이딩된 eat() 메서드 수행
			// - 동적 바인딩
			// --> 부모 타입 참조 변수로 메소드를 호출했지만
			// 	   자식 타입의 오버라이딩된 메소드가 수행된다.
			
			
		}
		
		
		
	}
}
package edu.kh.poly.ex2.run;

import edu.kh.poly.ex2.model.service.AbstractService;

public class AbstractRun {
	
	public static void main(String[] args) {
		
		AbstractService service = new AbstractService();
		
		//service.ex1();
        service.ex2();
	}

}

09_다형성(Polymorphism) : 인터페이스

  • 인터페이스 : 더 추상화된 개념(접점) = 추상메소드 외에 사용불가
  • 추상클래스 : 추상메소드 + 멤버변수, 일반메소드 사용가능

** 자바에서 인터페이스란? 클래스간의 접점!
-> 클래스가 공통적으로 가져야하는 필드, 메소드 모아두고 상속

package edu.kh.poly.ex2.model.service;

// 계산기 인터페이스
// -> 모든 계산기에 대한 공통 필드, 기능명을 제공하는 접점(interface)의 용도
public interface Calculator {
	
	// 인터페이스 : 추상클래스의 변형체 (추상클래스보다 추상도 높음)
	// - 추상클래스 : 일부만 추상 메서드(0개 이상)
	// - 인터페이스 : 모두 추상메서드
	
	// 필드(묵시적으로 public static final ==> 상수)
	public static final double PI = 3.14;
	static final int MAX_NUM = 100000;
	public int MIN_NUM = -100000;
	int ZERO = 0;
	
	// 메서드 (묵시적으로 public abstract)
	public abstract int plus(int num1, int num2);
	
	public abstract int minus(int num1, int num2);
	
	public abstract int multiple(int num1, int num2);
	
	public abstract int divide(int num1, int num2);
}
package edu.kh.poly.ex2.model.service;

public class LYJCalculator /*extends Animal */implements Calculator{
	// 클래스, 인터페이스 동시 상속 가능
	// 인터페이스는 다중 상속 가능
	
	// extends : 확장하다 , implements : 구현하다
	// (부)클래스 - (자)클래스 : extends (추상클래스 포함)
	// (부)인터페이스 - (자)클래스 상속 시 implements

	@Override
	public int plus(int num1, int num2) {
		int result = num1 + num2;
		return result;
	}

	@Override
	public int minus(int num1, int num2) {
		int result = num1 - num2;
		return result;
	}

	@Override
	public int multiple(int num1, int num2) {
		int result = num1 * num2;
		return result;
	}

	@Override
	public int divide(int num1, int num2) {
		int result = num1 / num2;
		return result;
	}
	
	

}
package edu.kh.poly.ex2.run;

import edu.kh.poly.ex2.model.service.AbstractService;
import edu.kh.poly.ex2.model.service.LYJCalculator;

public class AbstractRun {
	
	public static void main(String[] args) {
    	
        LYJCalculator cal = new LYJCalculator();
		
		
		System.out.println("합 : " + cal.plus(20, 15));
		System.out.println("차 : " + cal.minus(20, 15));
		System.out.println("곱 : " + cal.multiple(20, 15));
		System.out.println("몫 : " + cal.divide(20, 15));
		
		
		// 인터페이스 == 미완성 설계도 == 객체생성 불가능
		// -> 추상클래스처럼 참조 변수로는 사용 가능
		
		// 코드의 큰 수정 없이
		// 객체 생성 코드만 바꾸면 새로운 클래스 코드를 수행할 수 있다.
		
		
		// 인터페이스 특징
		// 1) 인터페이스를 부모 참조변수로 사용하면
		//    다형성 중 업캐스팅이 적용되서
		//    상속받은 모든 클래스를 자식 객체로 참조할 수 있다.
		
	    // -> 이를 이용하여 중요한 메인코드의 수정을 최소화 할 수 있다.
		// ex) Run에 작성된 합, 차, 곱, 몫을 출력하는 코드의 수정 없이
		//     객체 생성 코드 한 줄만 수정하여 다르게 작성된 기능을 수행할 수 있다.
		
		// 2) 자식 클래스에 공통된 메서드 구현을 강제하기 때문에
		// 모든 자식 클래스가 동일한 형태를 띄게 된다.
		// -> 이를 이용하여 공동 작업(팀 프로젝트)에 필요한
		// 		개발 환경을 조성할 수 있다.
		
	}

}

10_예외 처리

  • 프로그램 오류
    • 컴파일 에러 : 개발자가 코드를 잘못 쓴 경우
    • 런타임 에러 : 콘솔 빨간줄
      (위 두 에러는 우리가 해결가능)
    • 시스템 에러 : (외부적 요인으로 인한) 버그

package edu.kh.exception.test;

import java.util.Scanner;

public class ExceptionTest {
	
	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		
		while(true) {
			System.out.print("정수 입력(0 입력시 종료) : ");
			int input= sc.nextInt();
			
			// 컴파일 에러 => 개발자가 코드 잘못쓴것.
			// 자료형이 맞지않아 연산을 못해서 "컴파일 에러"가 발생(코드 틀림)
			//int a = 99.9;
			int a = (int)99.9; // 코드로 수정 가능!
			
			
			if(input == 0) {
				break;
			}
		}
		
		// 런타임에러 예제
		// 런타임 에러 : 프로그램 수행 중 발생하는 에러
		//				 주로 if문으로 처리가 가능하다.
		
		int[] arr = new int[3]; // 길이 3 : 인덱스 0,1,2 까지
		
		arr[0] = 10;
		arr[1] = 20;
		arr[2] = 30;
		//arr[3] = 40;
		
		//java.lang.ArrayIndexOutOfBoundsException : 배열 범위 초과 예외
		
		if( 3 >= arr.length) { // 배열 인덱스 범위 초과 시
			System.out.println("배열 범위를 초과했습니다.");
		} else {
			arr[3] = 40;
		}
		
		
	}

}
package edu.kh.exception.model.service;

import java.io.IOException;
import java.util.InputMismatchException;
import java.util.Scanner;

public class ExceptionService {
	
	// 예외(Exception) : 소스 코드의 수정으로 해결 가능한 오류
	
	// 예외는 두 종류로 구분됨
	// 1) Unchecked Exception : 선택적으로 예외 처리
	// 2) Checked Exception : 필수적으로 예외 처리
	
	private Scanner sc = new Scanner(System.in);
		
	public void ex1() {
		
		System.out.println("두 정수를 입력받아 나누기한 몫을 출력");
		
		System.out.print("정수 1 입력 : ");
		int input1 = sc.nextInt();
		
		System.out.print("정수 2 입력 : ");
		int input2 = sc.nextInt();
		
		try {
			System.out.println("결과 : " + input1 / input2);
			// ArithmeticException : 산술적예외 0으로 나눌 수 없다.
			
		} catch(ArithmeticException e) {
			// try 에서 던져진 예외를 catch문 매개변수 e로 잡음.
			
			System.out.println("infinity"); // 처리 코드
		}
		
		
		
		if(input2 != 0) {
			System.out.println("결과 : " + input1 / input2);
		} else {
			System.out.println("infinity");
		}
		
		// 발생하는 예외 중 일부 예외는 try-catch 구문 사용 안해도
		// 예외 상황을 방지할 수 있다!
		// 일부예외 == 대부분 UncheckedException

	}
	
	public void ex2() {
		// 여러가지 예외에 대한 처리 방법
		
		
		try {
			
			System.out.print("입력1 : ");
			int num1 = sc.nextInt(); //java.util.InputMismatchException
			
			System.out.print("입력2 : ");
			int num2 = sc.nextInt(); //java.util.InputMismatchException
			
			System.out.println("결과 : " + num1 / num2); // ArithmeticException
			
			
			String str = null;
			
			System.out.println( str.charAt(0) );
			// java.lang.NullPointerException : 참조하지 않는 참조변수를 이용해서 코드 수행 시 발생
			
		} catch(InputMismatchException e) {
			System.out.println("타입에 맞는 값만 넣어주세요");
		
		} catch(ArithmeticException e) {
			System.out.println("0으로 나눌 수 없습니다.");
			
		} catch(Exception e) { // 다형성 : 업캐스팅
			System.out.println("뭔지 모르겠으나 예외가 발생해서 처리함.");
			
			// 예외처리 + 다형성
			
			// Exception 클래스 : 모든 예외의 최상위 부모
			// 다형성 - 업캐스팅 : 부모타입 참조변수로 자식객체를 참조.
			
			// ** 상위 타입의 예외 클래스를 catch문에 작성하면
			// 	  다형성 업캐스팅에 의해 모두 잡아서 처리 가능!! **
			
		}
	}
	
	public void ex3() {
		// 1) try - catch - finally
		// finally : try구문에서 예외가 발생 하든 말든 무조건 마지막에 수행
		
		try {
			
			System.out.println( 4 / 0 );
			
			
		} catch(ArithmeticException e) {
			System.out.println("예외처리됨");
			
			// 2) catch문 매개변수 활용
			// 매개변수 e : 예외 관련된 정보 + 예외 관련 기능
			
			System.out.println( e.getClass() ); // 어떤 예외 클래스인가?
			// class java.lang.ArithmeticException
			
			System.out.println( e.getMessage() ); // 예외 발생시 출력된 내용
			// / by zero
			
			System.out.println( e ); // e.toString();
			// java.lang.ArithmeticException: / by zero
			
			e.printStackTrace();
//			java.lang.ArithmeticException: / by zero
//			at edu.kh.exception.model.service.ExceptionService.ex3(ExceptionService.java:96)
//			at edu.kh.exception.run.ExceptionRun.main(ExceptionRun.java:13)
			
		} finally {
			System.out.println(" 무조건 수행 됨 ");
		}
		
	}
	
	public void ex4() {
		// throws : 호출한 메서드에게 예외를 던짐
		//      -> 호출한 메서드에게 예외를 처리하라고 위임하는 행위
		
		// throw : 예외 강제 발생 구문
		
		
		try {
			methodA();
			
		} catch(Exception e) {
			// Exception : 모든 예외의 최상위 부모
			// Exception이 catch 매개변수로 작성되었다
			// == 예외 종류 상관없이 모두 처리
			
			e.printStackTrace();
			// 발생한 예외가 메서드와 위치에 대한 모든 내용을 출력
			// 예외 발생 지점 추적
		}
	}
	
	public void methodA() throws IOException{
		methodB();
	}
	
	public void methodB() throws IOException{
		methodC();
	}
	
	public void methodC() throws IOException{
		
		// 예외 강제 발생 구문
		throw new IOException();
	}
	
	
}
package edu.kh.exception.run;

import edu.kh.exception.model.service.ExceptionService;

public class ExceptionRun {
	
	public static void main(String[] args) {
		
		ExceptionService service = new ExceptionService();
		
		//service.ex1();
		//service.ex2();
		//service.ex3();
		service.ex4();
	}

}

0개의 댓글