멀티캠퍼스 백엔드 과정 15일차[6월 21일] - JAVA 상속

GoldenDusk·2023년 6월 23일
0

상속

1. 상속 개념

상속

  • 이미 잘 개발된 클래스를 재사용해서 새로운 클래스를 만들기 때문에 중복되는 코드를 줄일 수 있어 개발 시간 단축
    • 부모클래스의 필드와 메소드를 자식 클래스에 재활용 + 자기꺼
    • 자식클래스가 부모클래스에 요청
  • 자바는 단일 상속만 있음
    • 무결성을 위함
  • 상속 = 내려받는다 = 공용

상속의 이점

  • 이미 개발된 클래스를 재사용하므로 중복 코드를 줄임
  • 클래스 수정 최소화
  • 유지 관리의 효율성 증

2. 클래스 상속

클래스 상속

  • 단일 상속 : 가능
public class 자식클래스 extends 부모클래스{

}
  • 다중 상속 : 불가
public class 자식클래스 extends 부모클래스1, 부모클래스2{

}

예시

  • Phone.java
package day0621.Inheritance;

public class Phone
{
	//필드 선언
	public String model;
	public String color;
	
	//메소드 선언
	public void bell() {
		System.out.println("벨이 울립니다.");
	}
	
	public void sendVoice(String message) {
		System.out.println("본인: "+message);
	}
	
	public void receiverVoice(String message) {
		System.out.println("상대방: "+message);
	}
	
	public void hangUp() {
		System.out.println("전화를 끊습니다. 통화 종료!");
	}
}
  • SmartPhone
public class SmartPhone extends Phone //다형성
{
	public boolean wifi;
	
	public SmartPhone(String model, String color) {
		super(); //생략가능 나의 부모 생성자를 호출 => 부모가 먼저 만들어짐
		this.model = model;
		this.color=color; //Phone으로부터 상속받은 필드를 내것처럼 사용
	}
	
	public void setWifi(boolean wifi) {
		this.wifi = wifi;
		System.out.println("와이파이 상대를 변경했습니다.");
	}
	
	public void internet() {
		
	}
}
  • 실행 클래스
**public class SmartPhoneEX
{

	public static void main(String[] args)
	{
		//SmartPhone 객체 생성 model : 갤럭시 color :white
		SmartPhone myPhone = new SmartPhone("갤럭시", "white");
		
	//Phone로부터 상속받은 필드 읽기 출력
		System.out.println(myPhone.model);
		System.out.println(myPhone.model);
		
	//SmartPhone의 필드 읽기 출력
		System.out.println(myPhone.wifi);
		
		// Phone으로부터 상속받은 전체 메소드 호출
		myPhone.bell();
		myPhone.sendVoice("안녕하세요");
		myPhone.receiverVoice("안녕 나는 누구게?");
		myPhone.sendVoice("근데 나 바쁘니까 좀이따 전화할게 끊어!");
		myPhone.hangUp();
		
		//SmarPhone의 메소드 호출
		myPhone.setWifi(true);
		myPhone.internet();

	}

}**

3. 부모 생성자 호출

부모 생성자 호출

  • 자식 객체를 생성하면 부모 객체가 먼저 생성된 다음 자식 객체 생성
자식클래스 변수 = new 자식클래스();
  • 부모 생성자는 자식 생성자의 맨 첫 줄에 숨겨져 있는 super()에 의해 호출
// 자식 생성자 선언
public 자식클래스(){
super();
}

super

  • super()일 때 : 모의 생성자를 호출하는 특수 메소드
class Phone
{
	//필드 선언
	public String model;
	public String color;
	
	public Phone() {
		System.out.println("Phone() 생성자 실행");
	}
	public Phone(String model, String color) {
		System.out.println("SmartPhone(String model, String color)생성자 실행 완료!");
	}
}
class SmartPhone extends Phone //다형성
{
	public boolean wifi;
	
	public SmartPhone(String model, String color) {
		//super(); //생략가능 (컴파일시 자동 추가) => 부모의 생성자를 호출하는 특수 메소드
		// => 이로 인해 Phone() 생성자 실행 출력
		super();
		this.model = model;
		this.color=color; //Phone으로부터 상속받은 필드를 내것처럼 사용
		System.out.println("SmartPhone(String model, String color)생성자 실행 완료!");
	}
	
	
}
class SmartPhoneEX
{

	public static void main(String[] args)
	{
		// SmartPhone 객체 생성 model : 갤럭시 color :white
		SmartPhone myPhone = new SmartPhone("갤럭시", "white");
		
		//Phone으로부터 상속받은 필드 입력 출력
		System.out.println(myPhone.model);
		System.out.println(myPhone.color);
		
	}

}
  • super(매개변수1, 매개변수2) : 선택해서 불러오기 가능
    • 위의 코드에서 SmartPhone class만 변경

    • this.model = model 해주지 않아도 됨

      class SmartPhone extends Phone //다형성
      {
      	public boolean wifi;
      	
      	public SmartPhone(String model, String color) {
      		super(model, color);
      		System.out.println("SmartPhone(String model, String color)생성자 실행 완료!");
      	}
      	
      	
      }

4. 메소드 재정의

메소드 오버라이딩(Overriding)

  • 상속된 메소드를 자식 클래스에서 재정의하는 것
  • 해당 부모 메소드는 숨겨지고 자식 메소드가 우선적 사용
  • 부모 메소드의 선언부(리턴 타입, 메소드 이름, 매개변수)동일해야 함
  • 접근 제한을 더 강하게 오버라이딩은 불가(public ⇒ private으로 변경 불가)
  • 새로운 예외를 throws할 수 없음

부모 메소드 호출

  • 자식 메소드 내에서 super 키워드도트(.) 연산자를 사용하면 숨겨진 부모 메소드를 호출
  • 부모 메소드를 재사용함으로써 자식 메소드의 중복 작업 내용을 없애는 효과

예제

public class Calculator 
{
	static final double PI = 3.14;
	
	public double areaCircle(double r) {
		System.out.println("Calculator의 areaCircle() 실행");
		return r*r*PI;
	}
}
public class Computer extends Calculator
{
	// 메소드 오버라이딩
	@Override // 컴파일 단계에서 정확히 오버라이딩이 되었는지 체크 해줌
	public double areaCircle(double r) {
		double result=super.areaCircle(r); //super(); 부모의 생성자 호출
		System.out.println("부모의 areaCircle()실행"+result);
		System.out.println("Computer의 areaCircle() 실행");
		return r*r*Math.PI;
	}
}
public class ComputerEx
{
	public static void main(String[] args) {
		Calculator cal = new Calculator();
		System.out.println(cal.areaCircle(5.5));
		
		Computer cal1 = new Computer();
		System.out.println(cal1.areaCircle(5.5));
	}
}

예제 2

부모 클래스

class Drink{
	
	 String name;
	 int price;
	 int count;
	
public Drink() {}
public Drink(String name, int price, int count) {
	this.name = name;
	this.price = price;
	this.count = count;
}
	
public int getTotalPrice() {
	int totalPrice = this.price * this.count;
	return totalPrice;
} 

public static void printTitle() {
	System.out.printf("%8s%8s%8s%8s%n","상품명","단가","수량","금액");
}

public void printData() {
	 System.out.printf("%8s%8d%8d%d%n",name,price,count,getTotalPrice());
}

}

자식 클래스

  • 참고 super(name, price, count); : 부모클래스 꺼 사용
    • 다만 해당 클래스(자식)의 생성자 안에서 호출 가능한 메소드
class Alcohol extends Drink{
	 double alcper;
	
	public Alcohol(){}
	public Alcohol(String name, int price, int count,double alcper) {
		this.name = name;
		this.price = price;
		this.count = count; // 위 3개 대신 super(name, price, count); 가능
		this.alcper = alcper;
	}
	
	public static void printTitle()
	{
		System.out.printf("%8s%8s%8s%-3s%n","상품명(도수[%])","단가","수량","금액");
	}
	
	
	@Override
	public void printData()
	{
		System.out.printf("%s   %d   %d %d%n",name+"("+alcper+")",price,count,getTotalPrice());
	}

실행 클래스 : Prob1

public class Prob1
{
	public static void main(String[] args)
	{

		Drink coffee = new Drink("커피", 1500, 4);
		Drink tea = new Drink("녹차", 1100, 7);
		Alcohol wine = new Alcohol("와인", 5000, 3, 15.1);

		System.out.println("***** 매 출 전 표 ***** ");
		Drink.printTitle();
		coffee.printData();
		tea.printData();
		System.out.println();

		Alcohol.printTitle();
		wine.printData();

		int sum = coffee.getTotalPrice() + tea.getTotalPrice() + wine.getTotalPrice();

		System.out.println();
		System.out.println("*** 총금액 " + sum + "원 ***");

	}
}

5. final 클래스와 final 메소드

final 클래스

  • final 클래스는 부모클래스가 될 수 없어 자식 클래스를 만들 수 없음

final 메소드

  • 메소드를 선언할 때 final 키워드를 붙이면 오버라이딩할 수 없음
  • 부모 클래스를 상속해서 자식 클래스를 선언할 때, 부모 클래스에 선언된 final 메소드는 자식
    클래스에서 재정의할 수 없음

6. protected 접근 제한자

protected 접근 제한

  • protected는 상속과 관련이 있고, public과 default의 중간쯤에 해당하는 접근 제한
  • protected는 같은 패키지에서는 default처럼 접근이 가능하나, 다른 패키지에서는 자식 클래스만 접근을 허용

7. 타입 변환

자동 타입 변환

  • 자동적으로 타입 변환이 일어나는 것
부모타입 변수 = 자식타입객체;
  • 자식은 부모의 특징과 기능을 상속받기 때문에 부모와 동일하게 취급

예제

public class Parent
{
	public void method1() {
		System.out.println("Parent-method1()");
	}
	
	public void method2() {
		System.out.println("Parent-method2()");
	}
}
public class Child extends Parent
{
	@Override
	public void method2()
	{
		System.out.println("Child-method2()");
	}
	public void method3()
	{
		System.out.println("Child-method3()");
	}

}
  • 실행 클래스
public class ChildEx
{

	public static void main(String[] args)
	{
		// 자식 객체 생성
		Child child = new Child();
		
		// 부모로 자식을 자동타입 변환
		Parent parent = child;
		
		parent.method1();
		parent.method2(); //자식이 재정의해서  자식의 method2()를 호출 Child-method2()
		//parent.method3() : 부모는 자식꺼의 메소드를 가져올 수 없음 	Parent parent = child;이기에 가져올 수 없다.

	}

}

강제 타입 변환

  • 부모 타입은 자식 타입으로 자동 변환되지 않음. 대신 캐스팅 연산자로 강제 타입 변환 가능
자식 타입 변수 = (자식타입) 부모타입객체;
  • 자식 객체가 부모 타입으로 자동 변환하면 부모 타입선언된 필드와 메소드만 사용 가능

예제

public class Parent
{
	
	String field1;
	
	public void method1() {
		System.out.println("Parent-method1()");
	}
	
	public void method2() {
		System.out.println("Parent-method2()");
	}
}
public class Child extends Parent
{
	String field1;
	String field2;
	
	@Override
	public void method2()
	{
		System.out.println("Child-method2()");
	}
	public void method3()
	{
		System.out.println("Child-method3()");
	}

}
  • 실행 클래스
public class ChildEx
{

	public static void main(String[] args)
	{
		// 자식 객체 생성
		Child child = new Child();
		
		// 1. 먼저 Parent 공간이 생김. m1, m2를 가지고 있음
		// 2. 자식을 담을려고 보니.... 자식이 더 커서 자식만 가지고 있는 메서드 같은건 담지 못함
		 Parent parent = new Child();
		 System.out.println(parent.field1);
		 parent.method1();
		 parent.method2();
		// parent.field2; // 접근 불가능 => 자식에만 있는 필드이기 때문에
		 
		 // 만약... 접근 가능하게 하고 싶다면..자식으로 강제형 변환을 통해 접근 가능하도록
		 Child child1 = (Child)parent;
		 child1.field2="data2";
		 child1.method3();
		 
		// 부모로 자식을 자동타입 변환
		// Parent parent = child;
		
		parent.method1();
		parent.method2(); //자식이 재정의해서  자식의 method2()를 호출 Child-method2()
		//parent.method3() : 에부모는 자식꺼의 메소드를 가져올 수 없음 	Parent parent = child;이기에 가져올 수 없다.

	}

}

8. 다형성

다형성

  • 사용 방법은 동일하지만 실행결과가 다양하게 나오는 성질
    • 자동차의 부품을 교환하면 성능이 다르게 나온다.
    • 객체는 부품과 같아서 프로그램을 구성하는 객체를 바꾸면 프로그램의 실행 성능이 다양하게 나올 수 있다.
  • 다형성 = 자동 타입 변환 + 메소드 재정의

필드 다형성

  • 필드 타입은 동일하지만, 대입되는 객체가 달라져서 실행 결과가 다양하게 나올 수 있는 것
public class Tire
{
	public void roll() {
		System.out.println("Tire");
	}

}

public class HankookTire extends Tire
{
	public void roll()
	{
		System.out.println("HankbookTire");
	}

}

public class KumhoTire extends Tire
{
	public void roll() {
		System.out.println("KumhoTire");
	}
}

public class Car
{
	public Tire tire;
	
	public void run() {
		tire.roll();
	}
}

public class CarEx
{

	public static void main(String[] args)
	{
		Car mycar = new Car();
		
		mycar.tire = new Tire(); //타이어를 낌
		mycar.run(); //달리기
		
		mycar.tire = new KumhoTire(); //타이어를 바꿔낌
		mycar.run();
		
		mycar.tire = new HankookTire(); //타이어를 바꿔낌
		mycar.run();

	}

}

매개변수의 다형성

  • 메소드가 클래스 타입의 매개변수를 가지고 있을 경우, 호출할 때 동일한 타입의 자식 객체를
    제공할 수 있음
  • 어떤 자식 객체가 제공되느냐에 따라서 메소드의 실행 결과가 달라짐
  • 다형성 필드보다는 메소드를 호출 시 많이 발생한다.
  • 메소드가 클래스타입의 매개변수를 가지고 있을 경우 호출 할 때 동일한 타입의 객체를 제공받는 것이 정석이기 하지만 자식 객체를 제공할 수 있다.(=자동타입 변환)

매개변수 다형성 예제

**class Vehicle{
	public void run() {
		System.out.println("차량이 달립니다.");
	}
}

class Bus extends Vehicle{

	@Override
	public void run()
	{
		System.out.println("qjtmrk 달립니다.");
	}
	
}

class Taxi extends Vehicle{

	@Override
	public void run()
	{
		System.out.println("택시가 달립니다.");
	}
	
}

class Driver{
	public void drive(Vehicle v) { //클래스 타입의 매개변수를 전달받는다.
		v.run(); 
	}
}

public class DriverEx
{
	public static void main(String[] args) { 
		// 1. 운전자 객체 생성
		Driver driver = new Driver(); 
		
		//2. 운전자에게 버스객체를 전달 => 버스를 운전한다.
		Bus bus = new Bus();
		if(bus instanceof Vehicle ) { //자동타입변환이 되기 때문에 Vehicle 사용 가능
		driver.drive(bus);
		}
		
		// 3. 운전자에게 택시객체를 전달 => 택시를 운전한다.
		Taxi taxi = new Taxi();
		driver.drive(taxi);
		if(taxi instanceof Vehicle ) { //자동타입변환이 되기 때문에 Vehicle 사용 가능
			driver.drive(taxi);
			}
	}
}**

매개변수 예제 2

class Person{
	public String name;
	
	//생성자
	public Person() {}
	
	//name을 받아서 초기화시키는 생성자 선언
	Person(String name) {
		this.name=name;
	} 
	
	public void walk(){
		System.out.println("걷습니다.");
		}
	}

class Student extends Person{
	public int stuNo;
	
	Student(){};
	
	// 학생 이름과 학생번호를 전달받아 초기화하는 생성자
	Student(String name, int no){
		super(name);
		this.stuNo=no;
	}
	
	public void study() {
		System.out.println("공부를 합니다.");
	}
}

public class InstanceOfEx
{
	public static void personInfo(Person person) {
		//person객체인지 student 객체인지 비교하는 것 찾기
		System.out.println("name ; "+person.name);
		person.walk();
		
		//person이 참조하는 객체가 Student 타입인지 확인
		if(person instanceof Student student) {
			System.out.println("stuNo : "+student.stuNo);
			student.study();
		}
		
	}

	public static void main(String[] args)
	{
		//Person객체를 매개값으로 제공하고 personInfo() 호출한다.
		Person p1 = new Person("김홍도");
		personInfo(p1); //같은 static 타입이라 바로 부를 수 있음
		
		Person p2 = new Student("김길동",10);
		personInfo(p2);
	}

}

9. 객체 타입 확인

instanceof 연산자

  • 매개변수가 아니더라도 변수가 참조하는 객체의 타입을 확인할 때 instanceof 연산자를 사용
  • instanceof 연산자에서 좌항의 객체가 우항의 타입이면 true를 산출하고 그렇지 않으면 false를 산출
boolean result = 객체 instanceof
  • java 12부터는 instanceof 연산의 결과가 true일 경우 우측 타입 변수를 사용할 수 있기 때문에 강제타입 변환이 필요 없음

10. 추상 클래스

추상 클래스

  • 객체를 생성할 수 있는 실체 클래스들의 공통적인 필드나 메소드를 추출해서 선언한 클래스
  • 추상클래스
    • 실체 클래스의 부모 역할
    • 공통적인 필드나 메소드를 추출해서 선언한 클래스
  • 실제 클래스
    • 객체를 생성할 수 있는 클래스
    • 실제 클래스는 추상 클래스 상속해서 공통적인 필드나 메소드를 물려받아 사용할 수 있다.
  • abstract : 실체 간에 공통적인 특성을 추출한 것

Untitled

추상 클래스 선언

  • 클래스 선언에 abstract 키워드를 붙임
  • new 연산자를 이용해서 객체를 직접 만들지 못하고 상속을 통해 자식 클래스만 만들 수 있다.
    • 사용 불가한 코드

      Animal animal = new Animal(); 
    • 사용 가능한 코드

추상 메소드와 재정의

  • 자식 클래스들이 가지고 있는 공통 메소드를 뽑아내어 추상 클래스로 작성할 때, 메소드 선언부만 동일하고 실행 내용자식 클래스마다 달라야 하는 경우 추상 메소드를 선언할 수 있음
  • 일반 메소드 선언과의 차이점은 abstract 키워드가 붙고, 메소드 실행 내용인 중괄호 { }가 없다.

재정의 예제

abstract class Animal{
	public void breathe() {
		System.out.println("숨을 쉽니다.");
	}
	public void eat() {
		System.out.println("먹습니다.");
	}
	public abstract void sound(); //{body}가 없는 추상메소드
}

//추상클래스를 상속할 때, 추상클래스 안에 추상메소드가 있다면 자식클래스에서는 반드시 추상메소드를 정의해줘야 함

class Dog extends Animal{

	@Override
	public void sound()
	{
		System.out.println("멍멍");
		
	}
	
}

class Cat extends Animal{

	@Override
	public void sound()
	{
		System.out.println("냥냥");
		
	}
	
}

public class AbstractEx
{

	public static void main(String[] args)
	{
		Dog dog = new Dog();
		dog.sound();
		dog.breathe();
		dog.eat();
		
		Cat cat = new Cat();
		cat.breathe();
		cat.sound();
		cat.eat();
		
		System.out.println("=============");
		animalSound(dog);
		animalSound(cat);

	}
	
	public static void animalSound(Animal animal) {
		animal.sound();
	} //전달받은 동물의 타입에 맞추어 소리를 내는

}

실습

abstract class Shape{
	protected double area;
	private String name;
	
	public Shape() {
	}
	public Shape(String name) {
		 this.name = name;
	}
	
	abstract void calculationArea();
	
	public void print(){
		System.out.println(this.name +"이고 면적은 "+ area+ "입니다." );
	}
	/**
	 * @return the name
	 */
	public String getName()
	{
		return name;
	}
	/**
	 * @param name the name to set
	 */
	public void setName(String name)
	{
		this.name = name;
	}
	
}

class Circle extends Shape{
	
	Circle c;
	private double radius;
	
	public Circle(){}
	public Circle(double r) {
		this.radius = r;
	}
	public Circle(String name,double r) {
		super(name);
		this.radius = r;
	}
	
	@Override
	void calculationArea() {
		
		double area = radius * radius * Math.PI;
		super.area = area;
		
	}

	/**
	 * @return the radius
	 */
	public double getRadius()
	{
		return radius;
	}

	/**
	 * @param radius the radius to set
	 */
	public void setRadius(double radius)
	{
		this.radius = radius;
	}
	
}

class Rectangular extends Shape{
	private double width;
	private String height;
	
	public Rectangular(){}
	public Rectangular(String name, double width, String height) {
		super(name);
		this.width = width;
		this.height=height;
	}
	
	@Override
	void calculationArea() {
		
		double area = width * Integer.parseInt(height);
		super.area = area;
		
	}

	
}

public class ShapeEx
{

	public static void main(String[] args)
	{
		
		Circle circle = new Circle("원", 5);
		circle.calculationArea();
		circle.print();
		
		Rectangular rect = new Rectangular("직사각형", 100,"2");
		rect.calculationArea();
		rect.print();
		

	}

}
profile
내 지식을 기록하여, 다른 사람들과 공유하여 함께 발전하는 사람이 되고 싶다. gitbook에도 정리중 ~

0개의 댓글