Java/Study

Ch7 상속

어굴애 2021. 3. 9. 15:55

자식(하위,파생) 클래스가 부모(상위) 클래스의 멤버를 물려받는 것

 

상속의 특징

 

1. 상속을 허용하지 않는 클래스(부모로 사용 불가능한 클래스) : final class 클래스명으로 선언

 

2. 단일상속만 가능하다 : 하나의 클래스로부터 부모 클래스는 하나만 존재할 수 있다.

자바언어 : 단일상속 class A extends P { }
C#,C++언어 : 다중상속 class B extends P1,P2 { }
Java <==> C#변환

3. 하나의 부모 클래스에 여러 자식 클래스가 있을 수 있지만, 하위 클래스들은 서로 무관하다.

 

4. 상속관계를 자손, 부모, 조부모 등 1:1로 계속 상속할 수 있다. 

 

5. 상위클래스는 하위 클래스의 어떤 멤버도 호출,사용할 수 없다.

자식클래스에 접근하고 싶다면 일반적인 타클래스의 접근 방법인 객체생성(new 연산자)를 써야한다.

cf. 다만 new연산자를 쓴다 하더라도 부모 클래스의 생성자 안에서 자식클래스의 생성자를 호출할 수 없다. (무한생성)

 

5. 상속은 필드와 메소드만 가능하다. 생성자의 경우 상속이 안되어 자식 클래스에서 생성자를 쓰기 위한 방법이 따로 필요하다(객체 생성 필요)

super(); - 항상 자식 생성자의 첫 줄에 호출된다. 사용자가 선언하지 않아도 컴파일러에 의해 생성된다.

더보기

자식 클래스의 인스턴스를 생성하면, 해당 인스턴스에는 자식 클래스의 고유 멤버뿐만 아니라 부모 클래스의 모든 멤버까지도 포함되어 있습니다.

따라서 부모 클래스의 멤버를 초기화하기 위해서는 자식 클래스의 생성자에서 부모 클래스의 생성자까지 호출해야만 합니다.

이러한 부모 클래스의 생성자 호출은 모든 클래스의 부모 클래스인 Object 클래스의 생성자까지 계속 거슬러 올라가며 수행됩니다.

 

따라서 자바 컴파일러는 부모 클래스의 생성자를 명시적으로 호출하지 않는 모든 자식 클래스의 생성자 첫 줄에 자동으로 다음과 같은 명령문(super();)을 추가하여, 부모 클래스의 멤버를 초기화할 수 있도록 해줍니다.

출처 : www.tcpschool.com/java/java_inheritance_super

자식클래스에서 부모클래스를  new 생성자로 객체생성할 수는 없을까?

가능은 하다. 다만 불필요할 뿐이다.

메인 클래스에서 자식 생성자를 호출하면 부모,자식객체가 모두 생성되어

1. a라는 참조변수 없이도 두 객체의 멤버를 자유롭게 사용할 수 있다.

2. 메모리를 낭비하는 일이다(부모객체가 두 개가 생기기 때문)

 
더보기

생성자 호출시 new연산자와 super()의 차이

부모 클래스 A의 생성자가 protected 접근권한을 가질 때 다른 패키지에 있는 자식클래스 B에서 new키워드로 A객체를 생성한다고 하자.

 

protected A(){} ----- A a  = new a(); //다만 new a();자체는 불가능하다.

이 때 "The constructor is not visible"이라는 에러경고가 발생한다.

A의 생성자가 protected로서 자식이 아니라면 다른 패키지에서의 접근을 막고 있기 때문이다. 
B클래스는 자식 클래스인데 왜 A에 대한 접근이 불가능할까? new를 통한 객체 생성 및 생성자 호출은 상속관계에서 이루어지지 않는다.

애초에 new생성자로 부모 객체를 생성하려는 순간, 두 클래스의 관계는 상속이 아닌 일반 클래스로 처리 되기 때문에

다른 클래스로부터의 접근을 불허한다는 protected 문법에 의해  해당 경고문구를 보내는 것이다. 

 

6. 자식 클래스의 객체가 생성될 때 부모의 기본생성자를 자동호출(부모클래스 힙메모리에 올림)하고 이어서 자식 객체의 생성자 호출한다.

(상속이 여러 대로 이어질 경우, 루트 directory까지 가서 기본생성자를 순서대로 실행한다)

 

자바에서 최상의 부모 클래스는 object 클래스임
자손클래스가 객체생성될 때 조부모, 부모 기본 생성자로 객체화한다.
자식을 두는 부모 클래스는 명시적 생성자를 사용하려면 기본생성자도 명시해 두어야 한다.
왜? 컴파일러는 안만들어 준다.
더보기

자식클래스의 객체가 생성되기 위해서는 부모 클래스의 생성자가 필요한데,
모든 생성자를 만들지 않으면 컴파일러가 기본 생성자를 만들기 때문에 괜찮으나
부모클래스가 명시적 생성자를 만드는 순간 컴파일에러

7. 상속의 개념으로 처리 순서 알아보기

부모클래스의 static{ } --> 자식클래스의 static{ } --> (메소드영역, 컴파일시)

부모클래스의 instance{ } --> 부모클래스의 기본생성자 --> 자식클래스의 instance{ } --> 자식클래스의 기본생성자 (힙영역, 객체호출)

자식클래스에서 부모클래스의 static 변수 사용할 수 있나요? 네
.java 원시코드
javac.exe 컴파일 static -> .class 바이트코드(2진수코드)
java.exe 실행단계 인스턴스자료 -> .exe실행코드

 

8-1. 부모클래스의 필드값 변경

자식 클래스의 메소드에서 매개변수를 받아 부모 클래스의 필드값을 변경해주려고 할 때 두 가지 방법이 존재한다.

1 .super 키워드 : 항상 부모객체를 참조한다. 

2 .this 키워드:  자기 객체를 참조했다가 해당 필드가 자기객체에 존재하지 않으면 부모객체를 참조한다. (그 위의 상위 클래스까지 갈 수 있음)

 

8-2. super 용도

1. 부모 클래스의 멤버를 가리킨다 super.name

2. 자식 생성자에 부모 생성자를 호출할 때 super(매개변수); 단, 반드시 첫줄에 나와야한다.

3. 오버라이딩된 부모 클래스의 메소드를 호출해야 할 때 super 키워드를 붙여 명시적으로 호출가능

cf.super.super 사용할 수 없다. super는 바로 윗 상위클래스만 가리킬 수 있다.

 


 

자식클래스 기본생성자 안에는 첫줄에 super( )가 생략되어 있다.

자식 클래스에서 부모 생성자를 호출하지 않아도 알아서 부모의 기본생성자부터 호출된 후 자식의 기본 생성자가 수행된다.

자식 클래스에서 부모생성자를 호출하는 키워드super~이 없다면, 부모생성자 또한 생략가능하다.

 

 

 

부모클래스에 명시적 생성자가 존재한다면, 자식 클래스에도 super(매개값) 명시적 호출이 요구된다.

부모 생성자 명시적 선언 → 자식 생성자 첫줄 "super(매개값)" 필수

Otherwise, "Implicit super constructor People() is undefined. Must explicitly invoke another constructor." 오류발생


Q. 부모클래스가 명시적 생성자를 선언했는데 컴파일 에러가 안나고 잘만 실행된다.

후에 코드가 수정되어 기본생성자 호출이 있을 경우를 대비해서 기본생성자를 선언해야하는건가?

 

지금까지의(3.11) 경험으로 본 결론은 "1:1대응 필요"

1. 부모 클래스: 명시적생성자 - 자식클래스 : 명시적 호출
2. 부모클래스 : 기본생성자 혹은 생략 - 자식클래스 : 기본 호출
3. 부모클래스: 명시적생성자 & 기본생성자 - 자식클래스 : 기본 호출 & 명시적 호출

 

하지만 코드 수정시에 에러가 날 확률이 높기 때문에 애초에 클래스를 만들 때

기본생성자를 만들어 두는 편을 추천한다.

 


Q. 부모 클래스의 name, ssn 필드와 자식 클래스의 name, ssn 필드는 서로 독립적인가?

아니면 부모만 name,ssn필드의 메모리를 실체적으로 가지나?

 

자식클래스의 생성자를 호출할 때, 부모의 객체도 먼저 생성된다.

자식클래스에서 직접적으로 선언하지 않은 name, ssn에 값을 넣어준다면

이는 생성된 부모 객체의 필드에 값을 넣어주는 것이다.

 

P - A B C D

또한 Parent 부모 클래스 밑에 A, B, C, D라는 네 개의 자식클래스가 있다고 가정한다.

자식클래스의 생성자를 각각 생성할 때, 네 개의 부모 객체 또한 생성된다.이는 P p = new P();로 만들어진 부모객체와도 구별된다. 


자식클래스의 기본생성자 안에 첫줄에 super(매개값)를 호출하면 부모의 명시적 생성자를 호출한다.

그러므로 부모클래스의 기본생성자는 호출되지 않는다.

 

package p3;

public class InHeriEx4 {
	public static void main(String[] args) {
		
//		C c = new C();	 
//		자식의 기본생성자 호출 : C(){[super();] :부모의 기본생성자 호출 > 자식의 기본생성자 호출
//		C c = new C();	 
//		자식의 기본생성자 호출 : C(){this(100);}  자식의 명시적 생성자로 갔다가 부모의 기본생성자 호출 > 자식의 명시적 생성자 호출
		C c = new C(30); 
//		자식의 명시적 생성자 호출 : C(int ip){ 1. [super();] : 부모의 기본생성자 호출 > 자식의 명시적 생성자 호출
//						{ 2. super(int ip)} >부모의 명시적 생성자호출 > 자식의 명시적 생성자호출
	}

}
class Parent{
	int ip;
	String name;
	Parent() {
		System.out.println("이곳은 부모의 기본생성자 입니다");
	}
	Parent(int sp){
		System.out.println("이곳은 부모 명시적 생성자 부분입니다 ");
	}
}

class C extends Parent{ //this는 자기자신이기 때문에 A안에서의 this는 A P안에서의 this는 P를 가리킨다.
	C(){
//		super(100); //부모의 명시적 생성자 호출
//		super();	//부모의 기본생성자 호출(생략가능)
		System.out.println("자식의 기본생성자");
//		this(100);
		
	}
	C(int ip){ 
//		super(); 이 생략되어 있다.
//		super(ip);
		System.out.println("이곳은 자식의 명시적 생성자");
	}
}

상속과 import

is a 관계 :  자식클래스는  부모클래스이다 : 상속

ex. 고양이는 동물이다

ex. x,y컬러의 클래스는 x,y클래스이다 : 자식 객체가 부모 객체를 전부 포함

 

has a 관계 : ~은 ~을 가지고 있다 : import로 다른 객체를 연결해 쓰는 것

클래스하나가 다른 클래스를 일부 부품으로 사용하지만 전체를 포함하지는 않는다.

 

 1. 스캐너클래스(자식)는 A클래스(부모)이다 > 상속

2. A클래스는 표준입력(스캐너)라는 클래스를 가지고 있다.> imort


 

상속과 자바의 실행순서

자식의 명시적 생성자를 통해 부모 객체에 값을 넣을 수 있는 이유

static  멤버(필드, 메소드)를 모두 메소드 영역에 올린 다음에 객체를 생성하여 인스턴스 멤버(필드, 메소드)를 힙영역에 올린다.

package p1;

public class AAA extends AA {

	public AAA() {
		System.out.println("AAA기본생성자");
		AA.dept = "웹디자인"; //부모의 객체가 생성되고 나서 자식의 생성자가 호출되기 때문에 값을 넣을 수 있다.
		schoolName = "서강대학교";
		aaMethod();
		AA.aaMethod11(); //aaMethod로 써도 되지만 static이라 AA.으로 써준다(정석)

	}

	public AAA(int a, boolean b) {
		super(a, b);
		System.out.println("AAA명시적생성자");
	}
	
	void aaaMethod() {
		AA.dept = "웹디자인"; 
		schoolName = "서강대학교";
		aaMethod();
		AA.aaMethod11(); 

	}
	static void aaaMethod11() {
		AA.dept = "웹디자인"; 
//		schoolName = "서강대학교"; //부모클래스의 멤버라할지라도 인스턴스라 쓸 수 없다.
//		aaMethod();
		AA.aaMethod11();
	}
}

메소드 오버라이딩

 

상속된 메소드의 내용이 자식 클래스에 맞지 않는 경우, 자식 클래스에서 동일한 메소드를 재정의하는 것

용도 : 다형성 사용

메소드가 오버라이딩되었다면 부모 객체의 메소드는 숨겨지기 때문에, 자식 객체에서 메소드를 호출하면 오버라이딩된 자식 메소드가 호출된다.

부모 클래스의 final 메소드는 자식 클래스가 오버라이딩(재정의)할 수 없다.

더보기

메소드 오버라이딩 이전

오버라이딩을 하지 않고 부모(A)의 메소드 printAll을 쓰고 싶다면 aa.printAll로 접근가능하다.

package p2;
		//부모 : x,y
		//자식1 : x,y,color
		//자식2 : x,y,z
public class InHerEx02 {
	public static void main(String[] args) {
		//A객체에 10,20을 넣어서 출력하기
       		A a = new a();
		a.setX(10);
		a.setY(20);

		//출력
		a.printAll();
		
		//AA객체에 30,40,50
		AA aa = new AA();
		aa.setX(30);
		aa.setY(40);
		aa.setZ(50);
//     	  	aa.printAA(); ==>30 40 50
		
		//출력
		System.out.println("----");
		aa.printAll(); ==> (30,40)
        //printAll은 A의 함수지만 AA는 A의 자식이기 때문에 쓸 수 있다. 다만 A와 AA의 각 필드는 독립적인 값(다른객체)을 가진다.
		
		//BB객체에 60,70,빨강을 넣어서 출력하기
		BB bb = new BB();
		bb.setX(60);
		bb.setY(70);
		bb.setColor("빨강");
//		bb.pritnBB(); =>60 70 빨강
        
		//출력
		System.out.println("=====");
		bb.printAll(); =>(60,70)
	}
}
-----------------------------------------------------------
package p2;

public class A {
	private int x;
	private int y;

	// 출력하기
	void printAll() {
		System.out.println("(" + x + "," + y + ")");
	}
	
	public int getX() {
		return x;
	}
	
	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}
}
------------------------------
package p2;

public class AA extends A {
	private int z;
	//printAll() 부모메소드 오버라이딩
	void printAA() {
		System.out.println(getX());
		System.out.println(getY());
		System.out.println(z);

	}

	public int getZ() {
		return z;
	}

	public void setZ(int z) {
		this.z = z;
	}
}
-----------------------------
package p2;

public class BB extends A {
	String color;
	//printAA
	void printAll() {
		System.out.println(getX());
		System.out.println(getY());
		System.out.println(color);
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}
}

 

 

더보기

메소드 오버라이딩 이후

오버라이딩을 한 후 부모의 메소드를 호출하고 싶다면 super.printAll();
package p2;
		//부모 : x,y
		//자식1 : x,y,color
		//자식2 : x,y,z
public class InHerEx02 {
	public static void main(String[] args) {
		//A객체에 10,20을 넣어서 출력하기
		//넣기
		A a = new A();
		a.setX(10);
		a.setY(20);

		//출력
		a.printAll();
		
		//AA객체에 30,40,50
		AA aa = new AA();
		aa.setX(30);
		aa.setY(40);
		aa.setZ(50);
		
		//출력
		System.out.println("----");
		aa.printAll(); //printAll은 A의 함수지만 AA는 A의 자식이기 때문에 쓸 수 있다. 다만 A와 AA의 각 필드는 독립적인 값(다른객체)을 가진다.
		
		//BB객체에 60,70,빨강을 넣어서 출력하기
		BB bb = new BB();
		bb.setX(60);
		bb.setY(70);
		bb.setColor("빨강");
		
		//출력
		System.out.println("=====");
		bb.printAll();
	}
}
----------------------------------------
package p2;

public class A {
	private int x;
	private int y;

	// 출력하기
	void printAll() {
		System.out.println("(" + x + "," + y + ")");
	}
	
	public int getX() {
		return x;
	}
	
	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}
}
-------------------------------------
package p2;

public class AA extends A {
	private int z;
	//printAll() 부모메소드 오버라이딩
	void printAll() {
		System.out.println(getX());
		System.out.println(getY());
		System.out.println(z);

	}

	public int getZ() {
		return z;
	}

	public void setZ(int z) {
		this.z = z;
	}
}
----------------------------------------
package p2;

public class BB extends A {
	String color;
	//printAll 오버라이딩
	void printAll() {
		System.out.println(getX());
		System.out.println(getY());
		System.out.println(color);
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}
}

 

 


메소드 오버라이딩의 규칙

 

1. 부모의 메소드와 동일한 시그니처(리턴타입,메소드이름, 매개변수리스트)를 가져야한다.

2. 접근 제한을 더 강하게 오버라이딩 할 수 없다 (public>protected>default>private, 자식 메소드의 접근제한이 더 넓어져야한다)

3. 새로운 예외(Exception)을 throw할 수 없다 (자식메소드의 예외의 계층은 작아져야한다)

 

접근제한자 리턴타입 메소드() { //코딩 다시}

cf.중괄호안만 고쳐 쓰는 것

 

 


Object

모든 클래스의 부모클래스가 되는 최상위 객체로 자바에 내장되어 있는 객체이다(java.lang)

따라서 extends 키워드를 통해 상속관계를 명시하지 않아도 사용가능하다.

 

부모 클래스 Object 클래스의 메소드를 모두 재정의해서 사용할 수 있다.

 toString()같은 메소드 또한 Object에 포함된 메소드로 오버라이딩 가능하다.


protected 접근제한자

같은 패키지이거나 다른 패키지여도 상속관계인 경우에만 사용을 허용한다.

package p3;

public class A {
	protected String field;
	
	protected A() {
		
	}
	
	protected void method() {
		
	}
}
-----------------------------------------
package p3;
		//protected - 상속관계는 아니지만 같은 패키지에 있으므로 객체화해서 사용함
public class B {
	public void method() {
		A a = new A();
		a.field = "value";
		a.method();
	}
}
-----------------------------------------
package p2;
		//protected 다른 패키지이고 상속관계가 아닌 클래스 protected 선언된 것은 사용할 수 없다.
import p3.A;

public class C {
	public void method() {
	/*	A a = new A();
		a.field = "value";
		a.method();
*/	}
}
-----------------------------------------
package p2;
		//protected 다른패키지이고 자손 클래스 protected에는 사용할 수 있다
import p3.A; //다른패키지라

public class D extends A { //상속
	public D() {
		super();
		this.field = "value";
		this.method();
	}
}

 

 

관련 코드

package p1;
//상속예제
public class InheritanceEx {

	public static void main(String[] args) {
	}
}

class Parent{ //부모가 되기 싫은 클래스는 final class Parent{}로 선언하면 됨
	//필드
	String name;
	int age;
	
	//메소드
	void cal() {
//		dept = "작곡과"; 컴파일에러. 부모는 자식 클래스를 쓸 수 없다.
	}
	
	//생성자
	Parent(){
		new Mother(); new Child();
	}
	Parent(String name, int age){
		this.name=name;
		this.age=age;
	}
	
}
class Mother extends Parent{
	void motherMethod() {
		name = "이순신";
		age = 30;
		cal();
		//childMethod();
		new Parent();
		new Parent(name,age);
		new Child(); //객체생성은 상속과 별개로 어디서든 할 수 있다.
	}
}

class Child extends Parent{
	String dept="컴퓨터공학";
	void childMEthod() { //부모 클래스의 값 초기화하려면 method를 이용해야 한다. 
		dept ="영어영문학";
		name ="홍길동";
		age = 20;
		cal();		//부모메소드 : 객체생성없이 쓸 수 있다.
		new Parent();	 //객체생성
		new Parent(name, age); //객체생성
//		motherMethod();  //컴파일에러. 형제클래스는 사실상 무관한 클래스이기 때문에 호출할 수 없다.
		new Mother();
	}
}		//자식 생성자는 부모생성자를 호출하는 게 아니라, 그냥 가져다씀. 쓰는 방법이 생성자호출과는 다르다.
		//상속의 개념에서 부모 생성자는 상속이 안된다. 필드와 메소드만 된다. 하지만 객체 생성 후 사용할 수 있다.

class GrandChild extends Child{
	
}

 

*6. 손자클래스의 객체 생성시 조부모의 기본생성자까지 자동호출

package p1;

public class Inheritance {
	public static void main(String[] args) {
		//부모 클래스 객체생성하기
		
		AA a = new AA(); //자식클래스 객체생성
	}
}

class P {
	//기본생성자
	P(){
		System.out.println("이곳은 부모클래스 부분입니다.");
	}	
} //p클래스끝

class A extends P {
	int korean;
	A(){
		System.out.println("이곳은 자식 클래스 A의 기본생성자 부분입니다~~");
    }
}

class AA extends A {
	AA(){
    	System.out.println("이곳은 손자 클래스 AA의 기본생성자 부분입니다~~~");
	}
}

 

*7. 상속의 개념으로 처리 순서 알아보기

package p2;

public class InheEx02 {

	public static void main(String[] args) {
		// 자식 클래스 객체생성
		A a = new A();
	}
}

class P {
	static int a; // 정적변수로서 컴파일단계에서 클래스로더가 메소드 영역에 자동으로 할당한다.
	int aa; // 인스턴스변수로서 실행단계에서 JVM이 힙영역에 할당한다.
	static { //static이 명시된 다른 부품클래스의 필드 사용 가능
		a = 10;
		// System.out.println("이곳은 부모 클래스의 static 블록 " + a + " " + b); 
		// 컴파일에러 : 부모객체가 메모리에 올라온 시점, 자식클래스는 메모리에 할당x 따라서 b를 쓸 수 없다.
		System.out.println("이곳은 부모 클래스의 static 블록 " + A.b); 
       		// static은 자식클래스에 있는 필드라할지라도 메모리에 둘다 올라가 있어 쓸 수 있음.
		System.out.println("이곳은 부모 클래스의 static 블록 " + a);
	}

	{
		a = 20;
		System.out.println("이곳은 부모클래스 인스턴스 블록 " + a);
	}

	P() {
		a = 30;
		System.out.println("이곳은 부모클래스의 기본생성자 " + a + " " + A.b + "  " + aa); 
    		    // 상속의 개념과 무관하게 객체생성해서 쓸 수 있다. A.b
	}
}

class A extends P {
	static int b;
	int bb;
	static {
		b = 100;
		System.out.println("이곳은 자식클래스의 static 블록 " + b + " " + a);
		// System.out.println("이곳은 자식클래스의 static 블록 " + aa + bb); 
   		   	//static 멤버에서 부모의 인스턴스 필드와 자신의 인스턴스필드 사용하지 못함
	}
	{
		b = 200;
		System.out.println("이곳은 자식클래스의 인스턴스 블록 " + b + " " + a + " " + aa + bb);
	}

	A() {
		b = 300;
		System.out.println("이곳은 자식클래스의 기본생성자 " + b + " " + a + aa + bb);
	}
}

 

*7. 상속의 개념으로 처리 순서 알아보기2

package p2;

public class InheEx02 {

	public static void main(String[] args) {
		// 자식 클래스 객체생성
		A a = new A();
	}
}

class P {
	static int a; // 정적변수로서 컴파일단계에서 클래스로더가 메소드 영역에 자동으로 할당한다.
	int aa; // 인스턴스변수로서 실행단계에서 JVM이 힙영역에 할당한다.
	static { //static이 명시된 다른 부품클래스의 사용 가능
		a = 1;
		System.out.println("이곳은 부모 클래스의 static 블록 " + "a="+ a);
	}

	{
		a = 3;
		System.out.println("이곳은 부모클래스 인스턴스 블록 " + "a="+a);
	}

	P() {
		a = 4;
		System.out.println("이곳은 부모클래스의 기본생성자 " + "a="+a); // 상속의 개념과 무관하게 객체생성해서 쓸 수 있다. A.b
	}
}

class A extends P {
	static int b;
	int bb;
	static {
		a = 2;
		System.out.println("이곳은 자식클래스의 static 블록  a="+ a);
	}
	{
		a=5;
		System.out.println("이곳은 자식클래스의 인스턴스 블록 " + "a="+ a );
	}

	A() {
		a=6;
		System.out.println("이곳은 자식클래스의 기본생성자 " +"a="+ a);
	}
}

*8. this와 super(부모 필드 변경)

-  static 필드 값을 넣어줄 때 this.fieldName을 사용할 수도 있지만 정적 변수는 class명.fieldName을 사용하는 것이 원칙이다.

package p3;

public class InHeriEx03 {
	public static void main(String[] args) {
		
		P p = new P();
		A a = new A();
		a.aMethod("김연아",100);
	}

}
class P{
	static int sp;
	int ip;
	String name;
	P() {
		this(200);
	}
	P(int sp){
		this.sp = sp;
	}
}

class A extends P{ //this는 자기자신이기 때문에 A안에서의 this는 A P안에서의 this는 P를 가리킨다.
	static int sa;
	int ia;
	void aMethod(String name, int sa) {
		super.name = name; //부모클래스
		this.name = name; //자식클래스의 멤버필드를 찾다가 자식에는 name필드가 없기 때문에 부모 클래스의 name에 넣어준다.
//      this.sa = sa; static 붙어있는 변수는 클래스명.변수명
        A.sa = sa; //(o) 옳은 방법
        //매개변수 sa의 값을 부모클래스의 정적변수 sp에 넣기
		P.sp = sa;
        //부모의 instance field ip에 값넣기
		super.ip = sa;
	}