상속 - super(), 오버라이딩, 오버로딩, final 예약어

oYJo·2024년 12월 17일

Java

목록 보기
19/25

상속(Inheritance)

다른 클래스가 가지고 있는 멤버(필드, 메소드)들을 새로 작성할 클래스에서 직접 만들지 않고 상속을 받음으로써 새 클래스가 자신의 멤버처럼 사용할 수 있는 기능

상속의 목적

클래스의 재사용, 연관된 일련의 클래스들에 대한 공통적인 규약 정의

상속의 추상화(코드의 중복을 제거하기 때문에 프로그램 생산성 유지보수에 크게 기여

상속의 장점

  1. 보다 적은 양의 코드로 새로운 클래스 작성 가능
  2. 코드를 공통적으로 관리하기 때문에 코드의 추가 및 변경 용이
  3. 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여

방법

클래스 간의 상속 시에는 extends 키워드 사용

표현식

[접근제한자] class 클래스명 extends 클래스명 {}
public class Academy extends Company {}

단일 상속(Single Inheritance)

클래스간의 관계가 다중 상속보다 명확하고 신뢰성 있는 코드 작성
자바에서는 다중 상속 미지원 → 단일상속만 지원

다중 상속(Multiple Inheritance)

C++에서 가능한 기능으로 여러 클래스로부터 상속을 받으며
복합적인 기능을 가진 클래스를 쉽게 작성 가능
서로 다른 클래스로부터 상속 받은 멤버 간의 이름이 같은 경우 문제 발생

상속의 특징

  1. 모든 클래스는 Object클래스의 후손
    Object클래스가 제공하는 메소드를 오버라이딩하여 메소드 재구현 가능
    ex) java.lang.String 클래스의 equals()와 toString()
  2. 부모클래스의 생성자, 초기화 블록은 상속 안 됨
    자식 클래스 생성 시, 부모 클래스 생성자가 먼저 실행
    자식 클래스 생성자 안에서 부모 클래스 생성자 호출을 명시하고 싶으면 super() 활용
  3. 부모의 private멤버는 상속은 되지만 직접 접근 불가
    자식 객체 생성 시에 부모의 필드 값도 전달 받은 경우,
    자식 생성자 안에서 부모의 private 필드에 직접 접근하여 대입 불가
    super() 이용하여 전달받은 부모 필드 값을 부모 생성자 쪽으로 넘겨서 생성하거나 setter, getter 메소드를 이용하여 접근

super()

부모 객체의 생성자를 호출하는 메소드로 기본적으로 후손 생성자에 부모 생성자 포함
후손 객체 생성 시에는 부모부터 생성이 되기 때문에 후손 클래스 생성자 안에는 부모 생성자를 호출하는 super()가 첫 줄에 존재
(부모 생성자가 가장 먼저 실행되어야 하기 때문에 명시적으로 작성 시에도 반드시 첫 줄에만 작성)
매개변수 있는 부모 생성자 호출은 super(매개변수, 매개변수)를 넣으면 됨

super.

상속을 통한 자식 클래스 정의 시 해당 자식 클래스의 부모 객체를 가리키는 참조변수
자식 클래스 내에서 부모 클래스 객체에 접근하여 필드나 메소드 호출 시 사용

오버라이딩(Overriding)

자식 클래스가 상속 받은 부모 메소드를 재작성 하는 것
부모가 제공하는 기능을 후손이 일부 고쳐 사용하겠다는 의미로 자식 객체를 통한 실행 시 후손 것이 우선권을 가짐

  • 특징
    메소드 헤드라인 위에 반드시 Annotation, @Override 표시
    접근 제어자를 부모 것보다 같거나 넓은 범위로 변경 가능
    부모 메소드의 예외처리 클래스 처리범위보다 좁은 범위로 예외처리 클래스 수정 가능
  • 성립 조건
    부모 클래스의 메소드와 자식 클래스의 메소드 비교
  1. 메소드 이름 동일
  2. 매개변수의 개수, 타입, 순서 동일
  3. 리턴 타입 동일
  4. private 메소드 오버라이딩 불가

오버로딩(Overloading)

한 클래스 내에서 같은 이름의 메소드를 여러 개 정의하는 것

  • 성립 조건
    같은 메소드 이름
    다른 매개변수 선언부(매개변수 타입, 개수, 순서)

  • 주의 사항
    메소드의 리턴타입은 오버로딩 조건과 관계 없음

final 예약어

  • final 클래스
    상속이 불가능한 클래스
    public final class finalClass {}

  • final 메소드
    상속 시 오버라이딩이 불가능한 메소드
    public final void method() {}

대상에 따른 사용 가능한 제어자,예약어

  • 유의사항
    클래스에 abstrac와 final 동시에 사용 불가능
    메소드에 static과 abstract 동시에 사용 불가능
    abstract 메소드의 접근제어자로 private 불가능

class Person

package edu.kh.inheritance.model.vo;

public class Person /*extends Object*/ {
	// class명에 상속 구문이 없다면
	// 컴파일러가 자동으로 extends Object 구문을 추가
	
	// 필드
	private String name;
	private int age;
	private String nationality; // 국적

	// 생성자
	public Person() {}

	public Person(String name, int age, String nationality) {
		this.name = name;
		this.age = age;
		this.nationality = nationality;

	}
	// 메소드
	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getNationality() {
		return nationality;
	}

	public void setNationality(String nationality) {
		this.nationality = nationality;
	}

	// 기능
	// 상속의 특징 : 코드 추가 및 수정에 용이함
	// (새로운 메소드를 Person에 추가하였을 때 
	//  상속 받은 Student, Employee가 사용 가능한지 확인)
	public void breath() {
		System.out.println("사람은 숨을 쉰다.");
	}

	public void move() {
		System.out.println("사람은 움직일 수 있다.");
	}

	// - 상속 받은 자식들에 대한 공통적인 규약 정의할 수 있다.
	// -> 상속 받은 자식들은 모두 부모와 같은 필드, 메소드를 가지고 있으므로
	//    클래스들이 일부 공통된 형태를 띈다.
	
	
	// Object.toString() 메소드 오버라이딩
	// - toString() 메소드는 객체가 가지고 있는 모든 값(필드)을
	//   하나의 문자열로 반환하는 용도의 메소드
	
	@Override
	public String toString() {
		return name + " / " + age + " / " + nationality;	
	}
}

class Parent

package edu.kh.inheritance.model.vo;

public /*final*/ class Parent { // 부모 클래스
	// Object 상속 중
	
	// ** final 클래스 **
	// -> 마지막 클래스 라는 의미로
	// 	  "더 이상 상속 불가"를 의미함
	
	public /*final*/ void method() {
		System.out.println("테스트용 메소드");	
	}
}

class Child extends Parent

package edu.kh.inheritance.model.vo;

public class Child extends Parent{
	// class Parent 쪽 public final class Parent인 상태로
	// extends Parent라고 쓰면 오류 발생

	// The type Child cannot subclass the final class Parent
	// (final 클래스인 Parent는
	// Child 클래스를 자식으로 가질 수 없다.)

	@Override
	public void method() {
		System.out.println("오버라이딩");
	
		// Cannot override 
		// the final method from Parent
	
	}
}

class Employee extends Person

package edu.kh.inheritance.model.vo;

public class Employee extends Person{
	// Person 상속 받음

	// 필드
	private String company; // 회사

	// 생성자
	public Employee() {}
	public Employee(String name, int age, String nationality, String company) {
		super(name, age, nationality);
		this.company = company;
	}


	// 메소드
	public String getCompany() {
		return company;
	}
	public void setCompany(String company) {
		this.company = company;
	}


	// Person으로 부터 상속 받은 메소드 중
	// move() 메소드를 Employee에 맞게 재정의(==오버라이딩)
	// 기본적인 틀은 가져오고 내가 수정하는 것



	// 이것만 보면 move가 Employee의 기능이라고 생각할 수 있으니 아니라고 알려줘야함
	// @Override 어노테이션 : 해당 메소드가 오버라이딩 되었음을
	//				  	    컴파일러에게 알려주는 역할

	// 어노테이션(Annotation) : 컴파일러에게 알려주기 위한 코드
	//					  	  (컴파일러 인식용 주석)
	@Override
	public void move() {
		// 기존 부모 코드 삭제 후 자식이 새롭게 재정의
		System.out.println("오버라이딩된 move() 메소드");
		System.out.println("효율적으로 일을 마치고 칼퇴한다.");
	}

	// *** 오버라이딩 성립 조건 ***
	// 1. 메소드 이름 동일
	// 2. 반환형 동일
	// 3. 매개변수 동일
	// 4. 접근 제한자 같거나 더 넓은 범위
	// 		ex) (부모) protected -> (자식) protected or public
	// 5. 예외처리 범위는 같거나 더 좁게

	// + 부모의 private 메소드는 오버라이딩 불가
	// -> 왜? 자식이 접근할 수 없기 때문에

	@Override
	public String toString() {
		return super.toString() + " / " + company;


	}
}

class Student extends Person

package edu.kh.inheritance.model.vo;

public class Student extends Person{
	// Student 클래스에 Person 클래스 내용을 연장한다.
	// == Student 클래스에 Person 클래스 내용(필드, 메소드)을 추가하여 확장한다.
	
	// *** 상속 ***
	// extends : 확장하다, 연장하다 +
	
	// 필드
	private int grade; // 학년
	private int classRoom; // 반
	
	// 생성자
	public Student() {
		
		// Student() 객체 생성 시
		// 내부에 상속 받은 person 객체를 생성하기 위한
		// Person 생성자 호출 코드가 필요하다!
		
		// super : 상위 <-> sub : 하위
		// super == Person (자식의 상위는 Person이다)
		
		super(); // super() 생성자 : 부모의 생성자를 호출하는 코드
		// 반드시 자식 생성자 최상단에 작성되어야 한다!!!
		
		// * super() 생성자 때문에
		// 자식 객체 내부에 부모 객체가 생성된다!
		
		// * super() 생성자 미작성 시
		// 컴파일러가 컴파일 단계에서 자동으로 추가해줌
		
		
	}
	
	
	public Student(String name, int age, String nationality, int grade, int classRoom) {
		// 전달 받은 값 중 name, age, nationality
		// 부모 필드에 세팅하기
		
		//this.name = name; 
		// 오류 발생 : The field Person.name is not visible
		// 상속 받은 부모의 필드(name)를 자식의 필드처럼 사용하려 했으나 오류 발생
		// 왜? 부모의 필드가 private 접근 제한자로 되어 있어서
		//    아무리 물려 받은 자식이라도 다른 객체이기 때문에 직접 접근 불가
		// -> 간접 접근 방법 사용(getter, setter 이용)
		
		/* 1)
		setName(name);
		setAge(age);
		setNationality(nationality);
		*/
		// setter로 해결되긴 하는데 비효율적이라 잘 사용하지 않음
		
		super(name, age, nationality); // 부모 매개변수 생성자 호출
		// 부모 생성자 중에서 3개를 가져오라
		
		this.grade = grade;
		this.classRoom = classRoom;
	}
	
	
	// 메소드
	// getter / setter
	public int getGrade() {
		return grade;
	}
	public void setGrade(int grade) {
		this.grade = grade;
	}
	public int getClassRoom() {
		return classRoom;
	}
	public void setClassRoom(int classRoom) {
		this.classRoom = classRoom;
	}
	
	// toString 오버라이딩
	@Override
	public String toString() {
		return super.toString() + " / " + grade + " / " + classRoom;	
	}	
}

class InheritanceService

package edu.kh.inheritance.model.service;

import java.util.Scanner;

import edu.kh.inheritance.model.vo.Employee;
import edu.kh.inheritance.model.vo.Person;
import edu.kh.inheritance.model.vo.Student;

public class InheritanceService {

	public void ex1() {
		// 상속 확인
		// - Person을 상속 받은 Student가 Person의 필드, 메소드를 사용할 수 있나?

		Person p = new Person();

		// p.name = "홍길동";
		// 오류 발생 : The field Person.name is not visible
		// private 때문에 직접 접근 불가
		p.setName("홍길동");
		p.setAge(30);
		p.setNationality("대한민국");

		System.out.println(p.getName());
		System.out.println(p.getAge());
		System.out.println(p.getNationality());

		
		System.out.println("--------------------------------");


		// Student 객체 생성
		Student std = new Student();

		std.setGrade(3);
		std.setClassRoom(5);

		// Person 클래스로부터 상속 받은 메소드
		std.setName("김길동");
		std.setAge(35);
		std.setNationality("대한민국");


		System.out.println(std.getGrade());
		System.out.println(std.getClassRoom());

		// Student가 Person의 메소드 뿐만 아니라 필드도 상속 받았는지 확인
		System.out.println(std.getName());
		System.out.println(std.getAge());
		System.out.println(std.getNationality());

		
		System.out.println("--------------------------------");

		
		Employee emp = new Employee();

		// Employee만의 고유 메소드
		emp.setCompany("KH정보교육원");
		emp.setAge(22);
		emp.setNationality("일본");
		
		System.out.println(emp.getCompany());
		System.out.println(emp.getAge());
		System.out.println(emp.getNationality());
		
		// 추가된 breath() 메소드 확인하기
		// Person의 기능
		
		System.out.println("--------------------");
		
		p.breath();
		std.breath();
		emp.breath();

	}

	public void ex2() {
		// super() 생성자 사용 방법
		
		// Student 매개변수 5개 짜리 생성자
		Student std = new Student("공유", 38, "한국", 1, 5);
		
		System.out.println(std.getName()); // 공유
		System.out.println(std.getAge()); // 38
		System.out.println(std.getNationality()); // 한국
		System.out.println(std.getGrade()); // 1
		System.out.println(std.getClassRoom()); // 5
		
	}
	
	public void ex3() {
		// 오버라이딩 확인 예제
		
		Student std = new Student();
		Employee emp = new Employee();
		
		std.move(); // 오버라이딩 X -> Person의 메소드 수행 Person.move()
		emp.move(); // 오버라이딩 O -> Employee 메소드 수행 Employee.move()
	}
	
	public void ex4() {
		
		// 모든 클래스는 Object 클래스의 후손
		// == 모든 클래스의 최상위 부모 Object
		
		// 1) Object 상속 여부 확인
		
		Person p = new Person(); // Object를 상속 받은 Person 객체 생성

		Student std = new Student();
		
		Scanner sc = new Scanner(System.in); // Person을 상속 받은 Student 객체 생성
		
		String str = "abc";
		// ** Object 상속과 단계적인 상속 확인
		System.out.println(p.hashCode()); // Object에서 물려 받은 haseCode()
		
		System.out.println(std.getAge()); // Person에서 물려 받은 getAge()
		
		System.out.println(std.hashCode());
		// Person이 Object에서 물려 받은 hashCode()를
		// Student가 또 물려 받아 사
		
		// -> 상속의 상속의 상속의 ... 상속 가능
		
		// * Object가 모든 클래스의 최상위 부모인지 확인
		System.out.println(sc.hashCode());
		// Object.hashCode()
		
		System.out.println(str.hashCode());
		// String.hashCode()
		// -> String이 Object에게 물려 받은 hashCode()를 오버라이딩함
		
	}
	
	public void ex5() {
		Person p = new Person("김무열", 34, "한국");
		System.out.println(p.toString());
		System.out.println(p);
		
		// print 구문 수행 시 참조 변수명을 작성하면
		// 자동으로 toString() 메소드를 호출해서 출력한다!
		
		System.out.println("---------------------------");
		
		Student std = new Student("윈터", 28, "미국", 4, 6);
		
		System.out.println(std.toString());
		// 1) Student 클래스 toString() 오버라이딩 전
		// Person으로 부터 상속 받은 오버라이딩된 toString() 수행
		
		// 2) Student 클래스 toString() 오버라이딩 후
		// Student의 toString() 수행
		
		Employee emp = new Employee("김건강", 28, "일본", "00증권");
		System.out.println(emp);
	}
}

class InheritanceRun

package edu.kh.inheritance.run;

import edu.kh.inheritance.model.service.InheritanceService;

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

		InheritanceService service = new InheritanceService();

//		service.ex1();
//		service.ex2();
//		service.ex3();
//		service.ex4();
		service.ex5();
	}

}

객체 배열 실습문제 2


class Person

package edu.kh.inheritance.practice.model.vo;

public class Person {
	protected String name;
	private int age;
	private double height;
	private double weight;


	public Person() {}


	public Person(int age, double height, double weight) {
		this.age = age;
		this.height = height;
		this.weight = weight;
	}


	public String getName() {
		return name;
	}


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


	public int getAge() {
		return age;
	}


	public void setAge(int age) {
		this.age = age;
	}


	public double getHeight() {
		return height;
	}


	public void setHeight(double height) {
		this.height = height;
	}


	public double getWeight() {
		return weight;
	}


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


	@Override
	public String toString() {
		return this.getName() + " , " + this.getAge() 
		+ " , " + this.getHeight()+ " , "+ this.getWeight();
	}
}

class Employee extends Person

package edu.kh.inheritance.practice.model.vo;

public class Employee extends Person {
	private int salary;
	private String dept;

	public Employee() {}

	public Employee(String name, int age, double height, double weight, 
			int salary, String dept) {
		super(age, height, weight);
		super.name = name;
		this.salary = salary;
		this.dept = dept;
	}

	public int getSalary() {
		return salary;
	}

	public void setSalary(int salary) {
		this.salary = salary;
	}

	public String getDept() {
		return dept;
	}

	public void setDept(String dept) {
		this.dept = dept;
	}
	
	@Override
	public String toString() {
		return getName() + " , " + getAge() + " , " + getHeight()
		+ " , " + getWeight() + " , " + getSalary() + " , " + getDept();
	}	
}

class Student extends Person

package edu.kh.inheritance.practice.model.vo;

public class Student extends Person {
	private int grade;
	private String major;

	public Student(){}

	public Student(String name, int age, double height, double weight
			, int grade, String major) {
		super(age, height, weight);
		super.name = name;
		this.grade = grade;
		this.major = major;
	}

	public int getGrade() {
		return grade;
	}

	public void setGrade(int grade) {
		this.grade = grade;
	}

	public String getMajor() {
		return major;
	}

	public void setMajor(String major) {
		this.major = major;
	}
	
	@Override
	public String toString() {
		return getName() + " , " + getAge() + " , " + getHeight()
		+ " , " + getWeight() + " , " + getGrade() + " , " + getMajor();
	}
}

class PracticeService

package edu.kh.inheritance.practice.model.service;

import java.util.Scanner;

import edu.kh.inheritance.practice.model.vo.Employee;
import edu.kh.inheritance.practice.model.vo.Student;

public class PracticeService {

	public void homework() {

		Scanner sc = new Scanner(System.in);


		Student[] arr = new Student[3];
		arr[0] = new Student("카리나", 20, 168.2, 70.0, 1, "정보시스템공학과");
		arr[1] = new Student("정해인", 21, 187.3, 80.0, 2, "경영학과");
		arr[2] = new Student("박서준", 23, 179.0, 45.0, 4, "정보통신공학과");

		for( Student std : arr ) {
			System.out.println(std);
		}

	
		Employee[] emp = new Employee[10];
		int count = 0;
		boolean flag = true;

	
		while(flag) {
			System.out.print("이름 : ");
			String name = sc.next();

			System.out.print("나이 : ");
			int age = sc.nextInt();

			System.out.print("신장 : ");
			double height = sc.nextDouble();

			System.out.print("몸무게 : ");
			double weight = sc.nextDouble();

			System.out.print("급여 : ");
			int salary = sc.nextInt();

			System.out.print("부서 : ");
			String dept = sc.next();

			emp[count] = new Employee(name, age, height, weight, salary, dept);
			
			count++;

			
			while(true) {
				System.out.print("계속 입력하시겠습니까?(Y/N) : ");
				char input = sc.next().toUpperCase().charAt(0);

				if(count == 10 || input == 'N') {
					flag = false;
					break;
				}

				if(input == 'Y') {
					break;

				} else {
					System.out.println("잘못 입력하셨습니다.");
				}
			}
		}
		for(int i=0; i<count; i++) {
			System.out.println(emp[i]);
		}
	}
}

class PracticeRun

package edu.kh.inheritance.practice.run;

import edu.kh.inheritance.practice.model.service.PracticeService;

public class PracticeRun {
	public static void main(String[] args) {
		PracticeService service = new PracticeService();
		service.homework();
	}	
}
profile
Hello! My Name is oYJo

0개의 댓글