ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JAVA] 상속(Extends)
    언어/JAVA 2022. 9. 7. 18:01

    -다수개의 클래스들이 중복되는 멤버 변수, 멤버 메서드를 포함하고 있는 경우 부모 자식 관계를 생성하여 코드의 중복을 방지하는 것을 1차 목적으로 사용함

    -2차 목적으로 코드의 재활용 방법을 목적으로 사용함

    -코드의 중복이 발생하고 있는 클래스에서 중복되는 멤버들을 별도의 클래스로 선언하고 상속을 구현하여 사용함

    상속의 구현

    -extends 키워드의 사용

    -class SubClass명(자식클래스) extend SuperClass명(부모클래스)

    -상속을 받는 클래스: 서 클래스, 자식클래스, 하위클래스

    -상속을 제공 해주는 클래스: 슈퍼클래스, 부모클래스, 상위클래스

    //코드의 중복이 발생하고 있는 클래스들
    /*class PersonA{
    	String name; int age;
    	String hakbun;
    }
    class PersonB{
    	String name; int age;
    	String empNo;
    }*/
    //중복되는 멤버들이나 메서드 등 코드를 구성요소로한 부모클래스를 생성
    class Person{
    	String name; int age;
    }
    //부모를 상속한(상속을 구현한) 자식클래스를 생성
    class PersonA extends Person{
    	String hakbun;
    }
    class PersonB extends Person{
    	String empNo;
    }
    
    public class Extends{
    	public static void main(String[] args) {
    		PersonA a=new PersonA();
    		PersonB b=new PersonB();
    		a.name="홍길동";
    		a.age=30;
    		System.out.println(a.name);
    		System.out.println(a.age);
    		b.name="홍길서";
    		b.age=25;
    		System.out.println(b.name);
    		System.out.println(b.age);
    	}
    }
    class Car{ // 부모클래스 선언
    	int 엔진;
    	int 배기량;
    	int 도어개수;
    }
    class K7 extends Car{ // 자식클래스 선언
    	public void printInfo() { // 자식클래스에서는 상속받은 멤버를 사용할 수 있음
    		System.out.printf("engine: %d\n",this.엔진);
    		System.out.printf("engine displacement: %d\n",this.배기량);
    		System.out.printf("number of door: %d\n", this.도어개수);
    	}
    }
    
    public class Extends {
    	public static void main(String[] args) {
    		K7 k=new K7();
    		k.엔진=6; k.배기량=3000; k.도어개수=5;
    		k.printInfo();
    		Car c=new Car();
    		// c.printInfo(); -> error 자식메서드에 있는걸 부모메서드는 사용 못함
    	}
    }

     

    접근지정자와 상속

    // 부모클래스의 private멤버는 아무리 자식 클래스여도 접근할 수 없는 멤버
    
    class SuperA{
    // private: 현재 클래스 내에 있는 멤버메서드를 통해서만 접근 가능한 완전 은닉 n1
    	private int n1;
    	public int n2; // public: 어디서든 자유롭게 접근 가능
    	int n3; 
    // default 접근지정자: 동일한 패키지 내부에서는 public으로 동작
    // 다른 패키지에서는 private으로 동작
    // 자신의 클래스에 있는 private member에 access하는 public 멤버메서드를 생성해서 값을 리턴받아 사용할 수 있음
    	public int getN1() {
    		return n1;
    	}
    // 자식클래스에서는 public과 같으며 외부로부터는 private로 작동
    	protected int n4;
    // getter 메서드를 만들어야 한다면 private 멤버의 getter는 반드시 부모클래스 안에서
    // protected 멤버는 부모, 자식 클래스 어디서도 생성이 가능함
    	public int getN41() {
    		return n4;
    	}
    }
    
    class SubA extends SuperA{
    	public void printinfo(){
    // 부모클래스의 private 멤버에는 자식클래스도 절대 access할 수 없음
    // System.out.println(this.n1); ->error
    	
    // 부모클래스의 public member에는 access할 수 있음
    		System.out.println(this.n2);
    		
    // 부모클래스의 디폴트접근지정자는 동일한 패키지에서 public과 같으므로
    // access가 가능
    		System.out.println(this.n3);
    		
    // public으로 선언된 멤버메서드를 이용하면 부모클래스의 private 멤버에도 access가 가능
    		System.out.println(this.getN1());
    		
    // 부모클래스의 protected는 자식클래스에게 public과 같음
    		System.out.println(this.n4);
    	}
    	public int getN42() {
    		return n4;
    	}
    }
    
    public class Extends {
    	public static void main(String[] args) {
    	}
    }

     

    일반 객체의 생성

    1. 멤버 필드 메모리 로딩

    2. 생성자 호출

     

    상속관계에서의 객체 생성과정

    1. 멤버 필드의 메모리 로딩-부모/자식 클래스의 모든 멤버필드가 메모리 로딩

    2. 생성자 호출(먼저 자식 클래스의 생성자 호출)

    3. 자식클래스의 생성자의 첫 번재 실행 코드인 super()에 의해 부모클래스의 생성자 호출. super(); 라는 명령은 따로 쓰지 않아도 이미 존재하며 부모클래스가 있다면 자동 호출되는 명령.

    4. 자식 클래스의 생성자의 나머지 코드들 실행

    -부모 클래스의 private멤버와 같은 경우 자식클래스에서 초기화를 할 수 없기 때문에 부모클래스의 생성자를 통해 초기화를 실행

    class SuperB{
    	int superNum;
    	public SuperB() {
    		System.out.println("부모클래스의 생성자 실행");
    	}
    }
    class SubB extends SuperB{ // SuperB 클래스 상속
    	int subNum;
    	// 숨어있던 디폴트 생성자의 첫 명령에는 
    	// super(); 부모클래스 생성자를 호출하는 명령이 같이 숨어 있음
    	SubB(){ // 실행2번
    		super();	
    // 따로 정의된 디폴트 생성자 안에는 따로 쓰지 않아도 super();가 있지만 위와 같이 별도로 써줘도 무방함. 생략 가능
    // 자식클래스에서 부모클래스 생성자 호출은 super();라고 명령하며 
    // 반드시 첫번재 실행코드로 써야함.(this와 같은 맥락)
    // 그렇다면 둘다 쓸 경우 this(); super(); 뭐가 먼저?
    // 둘은 같이 쓰는 경우 없음
    // 현재의 경우 오버로딩된 형제생성자가 없기 때문에 this();를 쓸일 없음
    // 만약 오버로딩된 형제생성자가 있다면 this(); 쓰면 호출된 생성자에서 super();가 실행되는 거고
    // this(); 대신 super();를 쓰면 현재에서 부모생성자를 호출하고 마는 형식
    // 좀더 쉽게 얘기하면 super();는 써도 실행, 안써도 실행임
    // 다만, this();가 쓰이면 super(); 생략. 
    // 호출된 형제생성자에 super();를 사용할 권한을 일임하는 방식임
    		System.out.println("자식클래스의 디폴트 생성자 실행"); 
            // 실행 2번
    	}
    	SubB(int subNum){
    		this(); // 실행1번
    		System.out.println("자식클래스의 오버로딩된 생성자 실행"); 
            // 매개변수 1개
            // 실행 3번
    // 자식클래스의 오버로딩된 생성자의 첫번째 실행코드는 super() or this()를 코딩하는데 그 둘을 같이 실행할 수 없음
    // 현재 클래스의 매개변수가 없는 생성자를 this()를 이용해서 호출하고 그 안에서 super()가 호출되도록 함
    	}
    // 매개변수가 있는 생성자로 자식생성자가 오버로딩되고 
    // this()로 형제생성자를 호출하지 않은 경우
    	public SubB(int subNum, int num) {
    		super();
    	System.out.println("자식클래스의 오버로딩된 생성자 실행(this())");
    	}
    }
    
    public class Extends {
    	public static void main(String[] args) {
    		SubB b=new SubB();
    		// 1. 자식클래스 객체 생성(자식클래스 생성자 호출)
    		// 2. 자식클래스 내부에서 부모클래스의 생성자 호출
    		// 3. 부모클래스 생성자 실행
    		// ** 2번의 부모클래스 호출은 따로 기술하지 않아도 자동으로 
    		//     자식클래스의 내부 첫번재 명령으로 호출&실행됨
    		System.out.println();
    		SubB c=new SubB(20);
    		System.out.println();
    		SubB d=new SubB(20,40);
    	}
    }
    class SuperC{
    	public SuperC() {
    		System.out.println("부모클래스의 매개변수가 없는 생성자");
    	}
    	public SuperC(int num) {
    		System.out.println("부모클래스의 매개변수가 있는 생성자");
    	}
    }
    class SubC extends SuperC{
    	// 부모클래스의 디폴트 생성자가 존재하지 않는 경우 자식클래스의 생성자에서는 반드시 super를 사용하여 
        // 명시적으로 매개변수가 있는 부모클래스의 생성자를 호출해야함
    	SubC(){
    		// super(10); // 매개변수가 없으면 생략 가능
    		// this(10);
    		super();
    	}
    	public SubC(int n){
    		 super(n);
    	}
    	// 자식클래스 생성자에서 super를 호출했으면 "그에 맞게 오버로딩된 부모생성자가 있거나"
    	// 없으면 "지금 존재하는 부모생성자에 맞춰서 호출하거나"...
    }
    public class Extends {
    	public static void main(String[] args) {
    		SubC c=new SubC();
    	}
    class SuperD{
    	public SuperD() { 
    // 전달인수 없는 디폴트 생성자 4
    		this(1);
    		System.out.println("SubD");
    	}
    	public SuperD(int arg) { 
    // 정수값을 매개변수로 갖는 생성자 3
    		this(1.1);
    		System.out.println("SubD int");
    	}
    	public SuperD(double arg) { 
    // 실수값을 매개변수로 갖는 생성자 2
    		this(" ");
    		System.out.println("SubD double");
    	}
    	public SuperD(String art) { 
    // 문자열 자료를 매개변수로 갖는 생성자 1(출력 순서)
    		System.out.println("SubD String");
    	}
    }
    class SubD extends SuperD{
    	public SubD() {
    		this(1);
    		System.out.println("SubD"); // 8
    	}
    	public SubD(int arg) {
    		this(1.1);
    		System.out.println("SubD int"); // 7
    	}
    	public SubD(double arg) {
    		this(" ");
    		System.out.println("SubD double"); // 6
    	}
    	public SubD(String art) {
    		// super(); 생략가능
    		System.out.println("SubD String"); // 5
    	}
    }
    
    public class Extends {
    	public static void main(String[] args) {
    		SubD d=new SubD();
    	}
    }

     

    상속 문법

    1. 각각의 클래스에서 발견되는 공통 변수, 메소드 등을 하나의 클래스로 선언하여 물려받는 문법

    2. 상속은 모든 클래스의 공통되는 속성, 기능을 정의된 부모클래스를 자식클래스에게 물려주는 방식임

    3. 부모클래스 정의되는 속성 또는 기능들은 모든 자식클래스에서 동작해야 하므로 일반적인 정의만 사용할 수 있음

    4. 상속 받은 메서드들은 자식클래스의 생성 목적이나 용도에 따라 사용이 어울리지 않은 메서드일 가능성이 있음

    메소드 오버라이딩

    1. 메소드의 이름이 동일해야 함

    2. 메소드의 매개변수 타입, 개수, 순서가 다를 경우 같은 이름의 메소드라도 다른 메소드로 인식함

    3. 상속관계에서만 사용할 수 있음

    4. 부모클래스의 메소드의 원형과 반드시 일치해야 함(리턴값의 타입, 메소드 이름, 매개변수)

    5. 접근지정자는 축소될 수 없음(다형성의 구현때문)

    6. 부모클래스의 final로 정의된 메소드는 오버라이딩 할 수 없음

    7. 오버라이딩 된 자식클래스의 메서드에서 super.crying(); 이란 명령으로 부모클래스의 메서드를 호출할 수 있음

    8. 클래스의 외부에서는 super라는 말을 쓸 수 없으므로 생성된 객체를 통한 super 사용은 허용하지 않음. c.super.crying(); 는 오류남

    class SuperF{
    	int 부모멤버변수;
    	void abc() { System.out.println("부모메서드");}
    }
    class SubF extends SuperF{
    	int 자식멤버변수;
    	void abc() { System.out.println("자식메서드");}
    }
    
    public class Extends08_TypeCasting {
    	public static void main(String[] args) {
    		// 부모클래스의 객체 생성
    		SuperF super1=new SuperF();
    		// 자식클래스의 객체 생성
    		SubF sub1=new SubF();
    // 1. 자식 클래스의 인스턴스 주소값을 부모클래스의 래퍼런스변수에 저장이 가능함
    		// 부모참조변수 <- 자식 인스턴스의 주소 OK
    		super1=sub1;
    		super1=new SubF();
    // 2. 부모 클래스의 인스턴스 주소값을 자식클래스의 래퍼런스 변수에 저장하는 것은 불가능함
    		// 자식참조변수 <- 부모 인스턴스의 주소 NOT OK
    		super1=new SuperF();
    		// sub1=super1; error
    		// sub1=newSuperF(); error
    // 3. 부모 클래스의 래퍼런스 주소가 갖고 있던 주소를 자식클래스의 랲러런스 변수에 그래도 저장하고 싶다면, 
    //    강제캐스팅 연산을 이용해서 저장은 가능함
    		SuperF super2=new SuperF();
    		// SubF sub3=(SubF)super2;  
            // ->문법상 오류는 없지만 실행하면 생기는 런타임에러가 발생.
    		// 결국 자식클래스의 래퍼런스 변수는 지밖에 모르기 때문에 저장된 주소를 찾아갔을 때, 
            // 그것이 자식클래스의 인스턴스가 아니고 부모클래스의 인스턴스면 런타임에러가 발생함
    		// 그럼 SubF sub3=(SubF)super2; 는 왜 문법에러가 없을까
    		// 부모래퍼런스 변수가 갖고 있던 주소값을 자식 래퍼런스에 넣을 수 있는 경우를 선별해야 하는데
    		// 현재의 부모 래퍼런스변수가 저장한 주소에 자식 인스턴스가 저장된 경우로 제한함
    		SuperF super3=new SubF(); 
            // 부모 래퍼런스 변수에 자식 인스턴스 저장
    		SubF sub3=(SubF)super3;
            // 부모 래퍼런스값을 자식 래퍼런스 변수에 저장
    		// 자식래퍼런스 <- (타입캐스팅) 부모래퍼런스 <- 자식 인스턴스의 주소
    		// 자식래퍼런스 <- (타입캐스팅) 부모래퍼런스 <- 부모 인스턴스의 주소 =>런타임에러
    		short a=10;
    		int b=a;
    		a=(short)b;
    // 4. 강제 캐스팅을 사용한 형변환 시 에러를 방지하는 방법
    		// instanceof 연산자
    		// SuperF super5=new SuperF(); // 실행 결과: 형변환 실패
    		SuperF super5=new SubF();
    		// SubF 클래스로의 형변환가능성을 true/false로 판단
    		if(super5 instanceof SubF) {
    			SubF sub5=(SubF)super5;
    			System.out.println("success");
    		} else {
    			System.out.println("fail");
    		}
    // 5. 자식 인스턴스를 저장하고 있는 부모 래퍼런스는 부모가 상속해준 멤버변수에만 접근이 가능함
    		SuperF super6=new SubF();
    		super6.부모멤버변수=100; // O
    		// super6.자식멤버변수=100; // X
    		super6.abc();
    // 다만 메서드가 오버라이딩 되었다면 부모클래스의 래퍼런스로 실행한
    // 메서드는 자식 클래스에서 오버라이딩 된 메서드가 우선 실행됨
    	}
    }

    '언어 > JAVA' 카테고리의 다른 글

    [JAVA] Object 클래스  (1) 2022.09.08
    [JAVA] Extends TypeCastion  (0) 2022.09.08
    [JAVA] SingletonPattern  (0) 2022.09.07
    [JAVA] Static  (0) 2022.09.07
    [JAVA] This  (0) 2022.09.07

    댓글

Designed by Tistory.