: 상속된 메소드의 내용이 자식 클래스에 맞지 않을 경우, 자식 클래스에서 동일한 메소드를 재정의 하는 것을 말한다.
(상속의 개념에서 부모 클래스의 메소들르 자식의 클래스로 재정의하는 것)
: 메소드가 오버라이딩되었다면 부모 객체의 메소드는 숨겨지기 때문에, 자식 객체에서 메소드를 호출하면 오버라이딩 된 자식 메소드가 호출된다.
: 어노테이션은 (@Override 표시는) 생략 가능하지만, 실수를 줄여줄 수 있으니 사용하는 것이 좋다.
(BB 클래스와 출력 클래스에서 확인 가능)
결과
: 에러 안남. 부모와 자식의 메소드 이름은 같지만
내용이 다르면 설정 가능 (메소드 오버로딩 같은 느낌)
: BB 클래스를 객체화해 BB.Aprint()를 출력했을 때
부모의 Aprint() 메소드가 아니라 본인의 Aprint() 메소드가 출력되는 것을 확인
1) A 클래스
// 점 x, y 에 대한 클래스
public class A {
private int x;
private int y;
// 출력 메소드
void Aprint() {
System.out.println("(" + x + "," + y+")");
}
// private로 막아줬으니 값 받고 내보낼 Setter Getter 설정
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;
}
}
2) AA 클래스
// A를 상속받으며
// 점 x, y, z 에 대한 클래스
public class AA extends A {
private int z;
// 풀력 메소드
// (x,y,z) 찍기
void aaPrint() {
// AA가 A의 자식 클래스라도 private로 막아놨기 때문에 바로 가져올 수 없다
// System.out.println("(" + x + "," + y+ "," + z + ")");
// getter setter 메소드를 가져와 사용해야함
System.out.println(getX());
System.out.println(getY());
System.out.println(z);
}
// z도 private로 막아줬기 때문에 Getter Setter 설정
public int getZ() {
return z;
}
public void setZ(int z) {
this.z = z;
}
}
3) BB 클래스
//
public class BB extends A {
private String color;
// 출력하기
void bbPrint() {
System.out.println(getX());
System.out.println(getY());
System.out.println(color);
}
// 부모와 똑같은 이름의 출력 메소드 작성해보기
// 결과
// : 에러 안남. 부모와 자식의 메소드 이름은 같지만
// 내용이 다르면 설정 가능 (메소드 오버로딩처럼 상속 관계에서도 성립)
// : BB 클래스를 객체화해 BB.Aprint()를 출력했을 때
// 부모의 Aprint() 메소드가 아니라 본인의 Aprint() 메소드가 출력되는 것을 확인
// @Override
// void Aprint() {
// 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;
}
}
4) 객체 생성 후 출력해보는 main
public class business {
public static void main(String[] args) {
// A 객체 생성해서 10, 20 출력하기
A a = new A();
// 값 넣기
a.setX(10);
a.setY(20);
//출력하기
a.Aprint();
System.out.println("-------------------------------");
// AA 객체 생성해 30, 40, 50 출력하기
AA aa = new AA();
// 값 넣기
aa.setX(30);
aa.setY(40);
aa.setZ(50);
//출력하기
aa.aaPrint();
System.out.println("\n AA에서 A클래스의 Aprint() 메소드를 출력했습니다");
aa.Aprint();
System.out.println("-------------------------------");
// BB 객체 생성해 60, 70, red 출력하기
BB bb = new BB();
// 값 넣기
bb.setX(60);
bb.setY(70);
bb.setColor("red");
// 출력하기
bb.bbPrint();
System.out.println("\n BB에서 A클래스의 Aprint() 메소드를 출력했습니다");
bb.Aprint();
System.out.println("-------------------------------");
}
}
실행 결과
기본적으로 있는 Object 클래스를 상속받아 오버라이딩을 사용해보자
1) Object에 이미 정의 된 클래스를 가져와 실행내용 부분만 재정의
//
public class KK extends Object{
// 부모인 Object 클래스의 것을 가져와 내용만 변경한 오버라이딩 부분
// 주석 처리하면 Object 클래스의 실행부분이 나온다
@Override
public String toString() {
// 원래 모습
// return getClass().getName() + "@" +Integer.toHexString(hashCode());
// 재정의
return "지금 오바라이딩 배우는 중 \n 이곳은 자식 클래스 부분" ;
}
}
class Child extends Object{
// Child 클래스는 Object 클래스의 자식 클래스
// 부모 클래스 Object 클래스의 메소드를 재정의해 사용할 수 있다.
// final (static)으로 작성된 메소드가 아니라면 모두 가능
@Override
public boolean equals(Object obj) {
// 원래 모습
// return (this == obj);
// 재정의
return true;
}
}
2) 객체 생성 후 출력 모양 보는 main
//
public class RunClass {
public static void main(String[] args) {
// 객체 생성 후 메소드 호출
KK kk = new KK();
String r = kk.toString();
System.out.println(r);
// 객체 생성 후 메소드 호출
Child ch = new Child();
ch.equals(null);
}
}
실행결과
자식 클래스에서 부모 클래스의 메소드를 오버라이딩하게 되면, 부모의 메소드는 숨겨지고, 오버라이딩 된 자식의 메소드만 사용된다. 그러나 자식 클래스 내부에서 오버라이딩된 부모 클래스의 메소드를 호출해야 하는 상황이 발생한다면 super 키워드를 사용해준다.
(1) 사용해보자
1) 자식 클래스
//
class Two extends One{
static int sa;
int ip;
void twoMethod(String name, int sa) {
// 부모 필드에 있는 name에 넣을것이다
super.name = name;
// two 는 one을 상속 받기 때문에
// 자기 자신의 필드를 먼저 확인하고, 했는데 name이 없으면
// 부모필드의 name을 슬그머니 가져와서 사용함
// 고로 에러는 안나는데
// this와 super는 다르니 그 경로를 잘 기억해둔다
this.name = name;
// static 붙어있는 변수는
// 클래스명.변수명
// 의 형태로 사용한다
Two.sa = sa;
// 오류는 안나지만 권장하지 않는 방밥
// this.sa = sa;
// 메개변수 sa의 값을 부모의 sp에 넣어라
super.sp = sa;
// 부모의 인스턴스 필드에 값 넣기
super.ip = sa;
}
Two(){
// this(10);
// 부모의 명시적 생성자 부분을 호출
super(100);
System.out.println("자식의 기본 생성자");
}
Two(int ip) {
// 자식 클래스의 기본 생성자 안에는 첫 줄에 super()가 생략된 것이다
// 자식 클래스의 기본 생성자 안에 첫줄에 super(매개변수)를 호출하면
// 부모의 명시적 생성자를 호출 할 것이므로
// 부모 클래스의 기본 생성자는 호출되지 않음
// (지우고 출력하는 것과 그냥 출력하는 것과 차이가 없음.
// 왜냐면 지워도 기본으로 super()가 있는데 안보이는 거니까)
super();
System.out.println("자식의 명시적 생성자");
// 지금 있는 (Two) 필드에 있는 ip에 넣을 것이다
this.ip = ip;
// super : 부모 필드에 있는 ip 에 넣은것이다.
super.ip = ip;
}
}
2) 부모 클래스
//
public class Inheritance5 {
public static void main(String[] args) {
// One one = new One();
Two two = new Two(300);
// two.twoMethod("김자바", 100);
}
}
실행결과
(2) 여러 클래스가 이어져 있어도 사용가능한가
1) 메인 클래스
public class Bussiness {
public static void main(String[] args) {
// 하위 클래스 객체 생성
// 하게되면 연결되어있는 최상위-상위-하위 모두 메모리에 올라간다
AA aa = new AA();
}
}
2. 최상위 P 클래스
public class P {
int a = 1;
public P() {
System.out.println("상위 클래스");
}
}
3. 상위 A 클래스
public class A extends P{
int a = 10;
public A() {
System.out.println("하위 클래스");
}
}
4. 하위 AA 클래스
public class AA extends A{
int a = 200;
public AA() {
// 하나 위에있는 A로 가서 값을 가져온다
System.out.println("손녀 클래스 " + super.a);
// AA위에 있는 A의 위에있는 P의 값을 가져오고 싶어도
// 안됨
// System.out.println("손녀 클래스 " + super.super.a);
}
}
실행 결과
각각 해석이 조금 다르지만 특정 클래스, 필드, 메소드를 상속해주고 싶지 않다면 사용한다.
클래스 선언시 final 키워드를 class 앞에 붙이게 되면 이제 이 클래스는 최종적인 클래스가 되었기에 상속할 수 없게 된다.
// final 설정을 해주게 되면
final class One{ }
// 이렇게 사용할 때 에러가 난다
// One은 상속을 받을 수 없는 클래스니까
class two extends One { }
부모 클래스를 상속해서 자식 클래스를 선언할 때 부모 클래스에 선언된 final 메소드는 자식 클래스에서 재정의할 수 없다
1) 상위 클래스
public class Car {
//필드
public int speed;
// 메소드
public void speedUp() {
speed +=1;
}
// final 메소드
// =사용은 가능하나, 재정의가 불가한 메소드로 선언
public final void stop() {
System.out.println("차를 멈춤");
speed = 0;
}
}
2) 하위 클래스
public class CarSports extends Car {
@Override
public void speedUp() {
speed += 10;
}
// 상위 메소드에서 final 메소드로 설정해줬기 때문에
// 오버라이딩해 사용할 수 없다
// @Override
// public void stop() {
// System.out.println("스포츠카 멈춤");
// speed = 0;
// }
}
3) 출력해보자
class CarPrint {
public static void main(String[] args) {
// 객체 생성
CarSports cp = new CarSports();
System.out.println(cp.speed);
cp.speedUp();
System.out.println(cp.speed);
cp.stop();
}
}
접근 제한자는 public, protected, default, private와 같이 네 가지 종류가 있다.
1) A 클래스
package package1;
public class Protected_A {
protected String field;
protected Protected_A() { }
protected void method() { }
}
2) B 클래스
package package1;
public class Protected_B {
public void method() {
// 상속 관계는 아니지만 같은 패키지에 있으므로 객체화해 사용 가능
Protected_A a = new Protected_A();
a.field = "value";
a.method();
}
}
1) A 클래스
package package1;
public class Protected_A {
protected String field;
protected Protected_A() { }
protected void method() { }
}
2) C 클래스
package package2;
import package1.Protected_A;
// 다른 패키지이고, 상속관계도 아닌 protecte 클래스를 사용할 수 없다
public class Protected_C {
public void method() {
// 생성자가 protected 이므로 에러
// Protected_A a = new Protected_A();
//
// a.field = "value";
// a.method();
}
}
1) A 클래스
package package1;
public class Protected_A {
protected String field;
protected Protected_A() { }
protected void method() { }
}
2) D 클래스
package package2;
import package1.Protected_A;
// 같은 패키지는 아니지만 자식 클래스이기 때문에 A클래스를 사용 가능
public class Protected_D extends Protected_A{
public Protected_D() {
// 부모인 A생성자 호출
super();
// 현재 D 클래스에 field와 method()가 없으므로 this. 사용 가능
// 현재 클래스에 동일명으로 작성된게 있다면 그거부터 찾아가기 때문에
// 사용할 수 없다
this.field = "value";
this.method();
// 부모에 있는 값과 메소드에 넣고 싶은 것이니 super. 도 사용가능
super.field = "value2";
super.method();
}
}