본문 바로가기
Study/JAVA

[JAVA] 6주차_상속 (메서드 오버라이딩, 패키지와 부모 생성자, Triangle 클래스와 Member 클래스)

by 8희 2022. 4. 8.

1차시 메서드 오버라이딩

 


메서드 오버라이딩

: 상속을 통해 부모 클래스로부터 물려받은 메서드를 자식 클래스에 맞도록 수정하는 것

 

메서드 오버라이딩 규칙

1. 부모 클래스의 메서드와 동일한 시그니처를 사용, 반환 타입까지 동일해야 한다.

- 메서드 오버로딩과 헷갈리지 않기!! 메서드 오버로딩에서는 반환 타입이 중요하지 않다.

- 메서드 오버라이딩이 메서드 오버로딩보다 규칙이 더 강력하다.

2. 부모 클래스의 메서드보다 접근 범위를 더 좁게 수정할 수 있다.

- 하지만 대체적으로 접근 범위를 동일하게 한다.

3. 추가적인 예외가 발생할 수 있음을 나타낼 수 없다.

 

메서드 오버라이딩 불가 - 상속 관계가 불가능해서 메서드 오버라이딩도 불가능하다!

1. private 메서드: 부모 클래스 전용이므로 자식 클래스에 상속되지 않는다.

2. 정적 메서드: 클래스 소속이므로 자식 클래스에 상속되지 않는다.

3. final 메서드: final 메서드는 더이상 수정 불가능하므로 자식 클래스가 오버라이딩할 수 없다.

 

메서드 오버라이딩을 하면 컴파일러는 자식 클래스를 우선시해서 실행한다!

- 자식 클래스가 메서드를 오버라이딩하면 자식 객체는 부모 클래스의 오버라이딩된 메서드를 숨긴다.

(= 메서드의 우선순위가 바뀐다.)

package sec03; //같은 패키지 내의 3개의 자바 파일 Circle.java Ball.java InheritanceDemo.java

public class Circle { //부모 클래스 Circle
	private void secret() {
		System.out.println("비밀이다.");
	}

	protected void findRadius() {
		System.out.println("반지름이 10.0센티이다.");
	}

	public void findArea() {
		System.out.println("면적은 (π*반지름*반지름)이다.");
	}
}
package sec03;

public class Ball extends Circle { //자식 클래스 Ball
	private String color; //Ball 클래스에 추가한 필드

	public Ball(String color) { //Ball 클래스에 추가한 생성자
		this.color = color;
	}

	public void findColor() { //Ball 클래스에 추가한 메서드
		System.out.println(color + " 공이다.");
	}

	public void findArea() { //메서드 오버라이딩 //오버라이드한 findArea()
		System.out.println("넓이는 4*(π*반지름*반지름)이다.");
        //상속받은 findArea()를 Ball 클래스에 맞게 수정
	}

	public void findVolume() { //Ball 클래스에 추가한 메서드
		System.out.println("부피는 4/3*(π*반지름*반지름*반지름)이다.");
	}
}
package sec03;

public class InheritanceDemo {
	public static void main(String[] args) {
		Circle c1 = new Circle(); //Circle 클래스로 c1 객체 생성
		Ball c2 = new Ball("빨간색"); //Ball 클래스로 c2 객체 생성

		System.out.println("원 :");
		c1.findRadius();
		c1.findArea(); 
        //여기서 findArea()는 Circle 클래스의 findArea() 실행
        
		System.out.println("\n공 :");
		c2.findRadius(); 
		c2.findColor();
		c2.findArea(); 
        //여기서 findArea()는 메서드 오버라이딩을 한 후기 때문에 실행 시 Ball 클래스의 findArea()가 실행됨
		c2.findVolume();
	}
}

 

부모 클래스의 멤버 접근

- 자식 클래스가 메서드를 오버라이딩해서 자식 객체에서 숨겨진 부모 클래스의 오버라이딩된 메서드는 super 키워드를 사용해서 호출

- super는 현재 객체에서 부모 클래스의 참조를 의미

package sec03.spr;

public class Circle { //부모 클래스 Circle
	private void secret() {
		System.out.println("비밀이다.");
	}

	protected void findRadius() {
		System.out.println("반지름이 10.0센티이다.");
	}

	public void findArea() {
		System.out.println("면적은 (π*반지름*반지름)이다.");
	}
}
package sec03.spr;

public class Ball extends Circle { //자식 클래스 Ball
	private String color;

	public Ball(String color) {
		this.color = color;
	}

	public void findColor() {
		System.out.println(color + " 공이다.");
	}
	
    //메서드 오버라이딩한 findArea()
	public void findArea() {
		findRadius(); 
		super.findArea(); //부모 메서드(Circle 클래스의 메서드) 실행
		// super.secret(); //secret()은 private이라 오류 발생

		System.out.println("넓이는 4*(π*반지름*반지름)이다.");
	}

	public void findVolume() {
		System.out.println("부피는 4/3*(π*반지름*반지름*반지름)이다.");
	}
}

 

메서드 오버라이딩과 메서드 오버로딩 비교 

 


2차시 패키지와 부모 생성자

 


패키지

: 파일 시스템의 폴더를 이용하며 클래스 파일을 묶어서 관리하기 위한 수단

 

패키지에 의한 장점

- 패키지마다 별도의 이름 공간(Namespace)이 생기기 때문에 클래스 이름의 유일성 보장

- 클래스를 패키지 단위로도 제어할 수 있기 때문에 좀 더 세밀하게 접근 제어

 

대표적인 패키지

- java.lang 패키지: import 문을 선언하지 않아도 자동으로 임포트되는 자바의 기본 클래스를 모아둔 것

- java.awt 패키지: 그래픽 프로그래밍에 관련된 클래스를 모아둔 것

- java.io 패키지: 입출력과 관련된 클래스를 모아둔 것

 

패키지의 선언

- 패키지는 주석문을 제외하고 반드시 첫 행에 위치

- 패키지 이름은 모두 소문자로 명명하는 것이 관례

- 일반적으로 패키지 이름이 중복되지 않도록 회사의 도메인 이름을 역순으로 사용

package com.hankuk.people;
//.을 사용해서 각각의 정보를 활용
//이 패키지의 경우 파일 위치를 확인하면 (작업 위치) > com > hankuk > people

- 패키지의 이름을 설정하지 않을 시 default package

 

패키지의 사용

- 다른 패키지에 있는 공개된 클래스를 사용하려면 패키지 경로를 컴파일러에게 알려줘야 한다.

 

1. 패키지의 이름을 접두어로 사용하는 방법

- 패키지 이름이 짧거나 한두 번 사용할 시엔 괜찮으나 그렇지 않을 경우엔 전체 코드가 지저분해진다.

 

2. impor문을 이용하는 방법

- import 문: 패키지의 경로를 미리 컴파일러에게 알려주는 문장

- import 문은 소스 파일에서 package 문과 첫 번째 클래스 선언부 사이에 위치

/* impot 문 사용 방법 */
import 패키지이름.클래스;
import 패키지이름.*;

/* import 문 사용 예시 */
import com.hankuk.*; //com.hankuk 패키지에 포함된 모든 클래스
import com.hankuk.people.*; //com.hankuk.people 패키지에 포함된 모든 클래스

- 정적 import 문: 패키지 단위로 임포트하지 않고 패키지 경로와 정적 메서드를 함께 임포트

import static java.util.Arrays.sort; //정적 import 문
import java.util.Calender; //import 문

public class StaticImportDemo {
	public static void main(Strings[] args) {
    		int[] data = { 0, 8, 0, 2 };
            
            /* 정적 import 문이 있어서 클래스 이름 없이 다른 패키지에 있는 공개된 클래스의 멤버 사용 */
            sort(data); 
            
            /* improt 문일 때는 클래스 이름과 함께 필드 사용 */
            System.out.println(Calendar.JANUARY); //JANUARY는 상수
            
            /* improt 문일 때는 클래스 이름과 함께 메서드 사용 */
            Calendar.getInstance();

 


자식 클래스와 부모 생성자

- 자식 생성자를 호출하면 부모 생성자도 자동으로 호출

- 자식 생성자는 첫 행에 부모 생성자 호출 코드 super()가 있다. (super() 생성자 호출은 맨 먼저 해야 한다!)

class Box { //부모 클래스
	public Box() {
    	...
	}
}

class ColoredBox extends Box { //자식 클래스
	public ColoredBox() { //자식 클래스의 생성자
    	/* (2) 부모 클래스의 생성자 호출 */
    	super(); //부모 생성자 호출 //무조건 첫 행에!
        /* (3) 부모 클래스의 생성자를 마치고, 자식 클래스의 생성자로 돌아옴 */
    }
}

public class BoxDemo { //메인 메서드 갖는 클래스
	public static void main(Strings[] args) {
    	/* (1) 자식 클래스의 생성자 호출 */
        /* (4) 자식 클래스의 생성자를 마침 */
    	ColoredBox b = new ColoredBox(); //자식 클래스의 생성자 호출해서 객체 b 생성
        }
}

- 부모 생성자와 자식 생성자의 시그니처가 같아야 한다. 

class Parent { //부모 클래스
	public Parent(String s) //생성자가 있으므로 기본 생성자 추가 X
}

class Child extends Parent { //자식 클래스
	//생성자가 없으므로 기본 생성자 Child() 추가
    //Child()는 제일 첫 행에서 부모 생성자 super() 호출
    //부모 클래스에 Parent()가 없어서 오류 발생!

}
class Parent { //부모 클래스
	//생성자가 없으므로 기본 생성자 Parent() 추가
}

class Child extends Parent { //자식 클래스
	public Chile(String s) {
    	super(s); //부모 클래스에 Parent(String ...)이 없어서 오류 발생!
    }
}

 


3차시 Triangle 클래스와 Member 클래스

 


class Triangle { //Triangle 클래스

	double base; //필드: 삼각형의 속성 - 실수 값의 밑변
	double height; //필드: 삼각형의 속성 - 실수 값의 높이

	public Triangle() {} //기본 생성자
    
	public Triangle(double base, double height) { //매개변수가 있는 생성자
    	this.base = base; //왼쪽이 필드 값, 오른 쪽이 매개변수
		this.height = height;
	}

	public double getBase() { //밑변 접근자
		return this.base; //밑변 필드 값 반환
	}

	public double getHeight() { //높이 접근자
		return this.height; //높이 필드 값 반환
	}

	public double findArea() { //넓이 구하기 메서드
		return (this.height * this.base / 2); //삼각형 넓이 반환
	}
}

public class TriangleTest { //메인 메서드를 갖는 클래스
	public static void main(String[] args) { //메인 메서드 
		Triangle t1 = new Triangle(10.0, 5.0); //Triangle 클래스의 생성자를 호출해서 t1 객체 생성
           	 System.out.println(t1.findArea()); //객체 t1의 넓이 구하기 
     	  	 //클래스 외부에서 멤버 접근하는 것이므로 . 연산자를 이용해서 findArea()에 접근
	}
}

 

다음 main() 메서드를 실행할 수 있도록 삼각형을 나타내는 Triangle 클래스에
생성자 2개를 추가하여 Triangle 클래스를 완성하시오.

조건 1: 밑변이 1.0이고 높이가 1.0인 객체 t2를 생성하는 생성자 구현하기 
         -> 기본생성자 이용하기
조건 2: 밑변과 높이가 모두 3.0인 객체 t3를 생성하는 생성자 구현하기
         -> this() 이용하기
class Triangle {
	private double base, height; //private으로 캡슐화
	//private double height; //하나씩 선언 가능
    
    	/* 생성자 오버로딩 - 생성자 이름 같고 시그니처가 다름 */
	public Triangle(double base, double height) { //(1) 매개변수가 있는 생성자
		this.base = base;
		this.height = height;
	}
	
	public Triangle() { //(2) 기본 생성자
		this.base = 1.0;
		this.height = 1.0;
	}
	
	public Triangle(double base) { //(3) 매개변수가 있는 생성자
		this(base, 3.0); //this() 사용
        	//this()는 자기자신의 생성자에 접근하게 하므로 (1) 생성자에 접근
       		//this()는 생성자에서 다른 생성자를 호출
        	//this()는 가장 첫 번째 줄에 나와야 된다!
	}
	
	public double findArea() { //넓이 구하기 메서드
		return (base*height)/2;
	}
    
	public double getBase() { //접근자
		return this.base;
	}
	
	public double getHeight() { //접근자
		return this.height;
	}
}

public class TriangleTest {
	public static void main(String[] args) {  
			Triangle t1 = new Triangle(10.0, 5.0); 
			Triangle t2 = new Triangle(); //밑변 1.0, 높이 1.0
			Triangle t3 = new Triangle(3.0); //밑변=높이=3.0
            
			System.out.println("t1=>" + t1.findArea()); //(1) 호출
			System.out.println("t2=>" + t2.findArea()); //(2) 호출
			System.out.println("t3=>" + t3.findArea()); //(3) 호출
		}

}

 


class Member {
    String name;
    String id;  
    String pwd;  
    int age;
    
    public Member(); //기본 생성자
    
    public Member(String name, String id, String pwd, int age) { //매개변수 있는 생성자
    	this.name = name;
        this.id = id;
        this.pwd = pwd;
        this.age = age;
     }
     
     /* 설정자 */
     public void setName (String name) {
     	this.name = name; //이름 설정
     }
     
     public void setId (String id) {
     	this.id = id; //아이디 설정
     }
     
     public void setPwd (String pwd) {
     	this.pwd = pwd; //비밀번호 설정
     }
     
     public void setAge (String age) {
     	this.age = age; //나이 설정
     }
     
     /* 접근자 */
     public String getName() {
     	return this.name; //이름 반환
     }
     public String getId() {
     	return this.id; //아이디 반환
     }
     public String getPwd() {
     	return this.pwd; //비밀번호 반환
     }
     public int getAge() {
     	return this.age; //나이 반환
     }
     
}

public class MemberTest {
	public static void main(String[] args) { //메인 메서드
    	Member mem1 = new Member("대한민국", "apple", "1234", 20); //mem1 객체 생성
        
        /* 클래스 외부에서 접근자를 호출하는 것이므로 점 연산자 이용 */
        System.out.println("이름 : " + mem1.getName()); //mem1 이름 출력
        System.out.println("아아디 : " + mem1.getId()); //mem1  아이디 출력
        System.out.println("나이 : " + mem1.getAge()); //mem1 나이 출력
    }
}