
상속(inheritance)이란 기존의 클래스에 기능을 추가하거나 재정의하여 새로운 클래스를 정의하는 것을 의미합니다.
상속은 캡슐화, 추상화와 더불어 객체 지향 프로그래밍을 구성하는 중요한 특징 중 하나입니다.
상속을 이용하면 기존에 정의되어 있는 클래스의 모든 필드와 메소드를 물려받아, 새로운 클래스를 생성할 수 있습니다.
기존에 정의되어 있던 클래스를 부모 클래스(parent class) 또는 상위 클래스(super class), 그리고 상속을 통해 새롭게 작성되는 클래스를 자식 클래스(child class) 또는 하위 클래스(sub class)라고 합니다.
생성자와 private 또는 default를 제외한 모든 것을 상속받는다.
Sub class로 객체를 생성하면 Super class와 자신의 생성자를 모두 호출한다.
다중상속을 할 수 없다.
기존에 작성된 클래스를 재활용할 수 있습니다.
자식 클래스 설계 시 중복되는 멤버를 미리 부모 클래스에 작성해 놓으면, 자식 클래스에서는 해당 멤버를 작성하지 않아도 됩니다.
클래스 간의 계층적 관계를 구성함으로써 다형성의 문법적 토대를 마련합니다.
class 자식클래스이름 extend 부모클래스이름 { ... }
package inheritance;
class AA{
public int a =3;
public void disp() {
this.a += 5;
System.out.println("AA : "+ a + " ");
}
}
class BB extends AA{
//필드는 Override 개념이 없다, 메소드에만 해당한다
public int a =8;
@Override
public void disp() {
// super.disp();
this.a+=5;
System.out.println("BB : "+ a + " ");
// super.disp();
}
}
public class OverrideMain {
public static void main(String[] args) {
BB aa = new BB(); //AA클래스와 BB클래스 메소드,필드 모두 사용 가능(AA클래스의 private 메소드,필드 제외)
aa.disp(); //BB : 13, 오버라이딩된 자식 메소드 호출
System.out.println("aa : "+aa.a); //aa : 13
System.out.println();
/*업캐스팅(부모 = 자식)
부모 클래스 변수를 가지고 자식 클래스의 객체를 참조하는 경우에는 부모 클래스에서 정의된 부분만 사용할 수 있다.
자식 클래스(BB) 중에서 부모 클래스(AA)로부터 상속받은 부분(disp())만을 bb를 통해서 사용할 수 있고 나머지는 사용하지 못한다.(부모 클래스(AA)의 메소드 필드 사용)
*/
AA bb = new BB();
bb.disp(); //BB : 13
System.out.println("bb : "+bb.a); //bb : 3
System.out.println();
//다운캐스팅( 자식 = 부모, 부모 객체를 자식 참조 변수로 참조(error) -> 자식 = (자식)부모 )
BB cc = (BB)bb; //자식 = (자식)부모
cc.disp();//BB : 18 //위에서 업캐스팅을 통해 객체가 생성되어서 a 값이 13으로 설정된후 다시 BB클래스의 disp()를 호출
System.out.println("cc : "+cc.a); //cc : 18
System.out.println();
AA dd = new AA();
dd.disp(); //AA : 8
System.out.println("dd : "+dd.a); //dd : 8
System.out.println();
//instanceof : 객체 타입 확인, 반환값:true,false
if(dd instanceof AA) System.out.println("ddd"); //ddd
}
}
package inheritance;
public class Super extends Object{
protected double weight, height;
Super(){
System.out.println("Super의 기본 생성자");
}
Super(double weight, double height){
this.weight = weight;
this.height = height;
}
public void disp() {
System.out.println("몸무게 = "+this.weight);
System.out.println("키 = "+height);
}
}
package inheritance;
public class ChildMain extends Super {
private String name;
private int age;
ChildMain(){
System.out.println("ChildMain의 기본 생성자");
}
ChildMain(String name, int age, double weight, double height){
super(weight,height); //부모 생성자 호출, super() 는 생성자의 첫줄에 써야 한다
// super.weight = weight; //this.weight = weight;
// super.height = height;
this.name = name;
this.age = age;
}
public void disp() {
System.out.println("이름 = "+name);
System.out.println("나이 = "+age);
super.disp();
}
public static void main(String[] args) {
//Sub class로 객체를 생성하면 Super class와 자신의 생성자를 모두 호출한다.
ChildMain aa = new ChildMain("홍길동", 25, 73.5, 182.6);
aa.disp();
System.out.println("-----------------");
Super bb = new ChildMain("코난", 13, 53.5, 156.6);
bb.disp(); //Override(부모클래스와 자식클래스에 같은 메소드 존재)
//모든 우선권은 자식클래스가 갖는다.
}
}
/*
이름 = 홍길동
나이 = 25
몸무게 = 73.5
키 = 182.6
-----------------
이름 = 코난
나이 = 13
몸무게 = 53.5
키 = 156.6
*/
package inheritance;
//상속받는 클래스는 상속해주는 클래스의 생성자와 private를 제외한 모든 것을 상속받는다
public class SubMain extends Super{
private String name;
private int age;
SubMain(){
System.out.println("SubMain의 기본 생성자");
}
SubMain(String name, int age, double weight, double height){
// this();
this.name = name;
this.age = age;
super.weight = weight; //this.weight = weight;
super.height = height;
}
public void output() {
System.out.println("이름 = "+name);
System.out.println("나이 = "+age);
super.disp(); //this.disp();
/*
super.disp() : 항상 부모 클래스의 disp() 메서드를 호출하며, 부모 클래스의 구현을 사용합니다.
this.disp() : 부모 클래스에서 오버라이딩한 경우에는 자식 클래스의 disp() 메서드를 호출하고, 그렇지 않으면 부모 클래스의 disp() 메서드를 호출합니다.
*/
}
public static void main(String[] args) {
//Sub class로 객체를 생성하면 Super class와 자신의 생성자를 모두 호출한다.
SubMain aa = new SubMain("홍길동", 25, 73.5, 182.6);
aa.disp(); //부모 메소드 호출
System.out.println("-----------------");
aa.output();
System.out.println("-----------------");
Super bb = new SubMain("코난", 13, 53.5, 156.6);
// bb.output(); //error(침조변수 bb는 Super클래스를 참조하기 때문이다)
bb.disp();
}
}
/*
자식클래스는 메모리 생성할 때
1.부모 클래스 생성 / new 부모() <- 기본 생성자
2.자식 클래스 생성 / new 자식()
*/
/*
Super의 기본 생성자
몸무게 = 73.5
키 = 182.6
-----------------
이름 = 홍길동
나이 = 25
몸무게 = 73.5
키 = 182.6
-----------------
Super의 기본 생성자
몸무게 = 53.5
키 = 156.6
*/
package inheritance;
class Test extends Object {
}
class Sample extends Object{
@Override
public String toString() {
return getClass()+"@용준"; //getClass() : 객체가 어떤 클래스로 생성되어있는지에 대한 정보를 반환한다.
}
}
class Exam extends Object{
private String name = "권용준";
private int age = 25;
@Override
public String toString() {
// return super.toString();
return name + "\t" + age;
}
}
public class ObjectMain {
public static void main(String[] args) {
Test t = new Test();
System.out.println("객체 t = "+t);//클래스명@16진수
System.out.println("객체 t = "+t.toString()); //toString() : 객체의 정보를 String(문자열)형으로 형변환 해준다.
System.out.println("객체 t = "+t.hashCode()); //hashCode() : 주소의 값을 10진수로 변경
System.out.println();
Sample s = new Sample();
System.out.println("객체 s = "+s.toString());
System.out.println();
Exam e = new Exam();
System.out.println("객체 e = "+e.toString());
System.out.println();
String aa = "apple";
System.out.println("객체 aa = "+aa);
System.out.println("객체 aa = "+aa.toString()); // toString을 써도 문자열로 출력(주소X)
System.out.println("객체 t = "+aa.hashCode());
System.out.println();
String bb = new String("apple");
String cc = new String("apple");
System.out.println("bb==cc : "+(bb==cc)); //주소 비교
System.out.println("bb.equals(cc) : "+bb.equals(cc)); //문자열 비교
System.out.println();
//Object에서는 == , equals() 가 모두 참조값(reference) 만으로 비교한다.
//단, String만이 equals()가 내용(문자열)을 비교한다.
Object dd = new Object();
Object ee = new Object();
System.out.println("dd==ee : "+(dd==ee)); //주소 비교
System.out.println("dd.equals(ee) : "+dd.equals(ee)); //주소 비교
System.out.println();
Object ff = new String(); //부모클래스가 자식클래스를 참조(부모 = 자식)
Object gg = new String();
System.out.println("ff==gg : "+(ff==gg)); //주소 비교
System.out.println("ff.equals(gg) : "+ff.equals(gg)); //문자열 비교
System.out.println();
}
}
/*
class Object {
public String toString() {} 클래스명@16진수
public int hashCode() {} 10진수
public boolean equals(Object ob){} 참조값 비교
}
class String extends Object
public String toString() {} 문자열
public int hashCode() {} 10진수 (믿으면 안된다) - 표기할 수 있는 문자열은 무한대이기 때문에 10진수로는 다 표기할 수 없다.
public boolean equals(Object ob){} 문자열 비교
}
*/
/*
객체 t = inheritance.Test@5ca881b5
객체 t = inheritance.Test@5ca881b5
객체 t = 1554547125
객체 s = class inheritance.Sample@용준
객체 e = 권용준 25
객체 aa = apple
객체 aa = apple
객체 t = 93029210
bb==cc : false
bb.equals(cc) : true
dd==ee : false
dd.equals(ee) : false
ff==gg : false
ff.equals(gg) : true
*/
package inheritance;
//상수들의 집합체
enum Color{
RED, GREEN, BLUE
}
class Final{
public final String FRUIT = "사과";
//클레스의 필드에 초기값 부여(생성자 사용)
public final String FRUIT2;
public Final() {
System.out.println("기본 생성자");
FRUIT2="딸기";
}
//static(실행하자마자 메모리에 자동으로 생성이 된다->new X)
public static final String ANIMAL = "기린";
//static final 필드는 static 구역에서 초기값을 주어야 한다
//static인 경우 생성자에서 초기화가 안된다(생성자는 new해야 하기 때문)
public static final String ANIMAL2;
static {
System.out.println("static 초기화 영역");
ANIMAL2="코끼리";
}
// public static final int RED=0;
// public static final int GREEN=1;
// public static final int BLUE=2;
}
public class FinalMain {
public static void main(String[] args) {
final int A =10;//상수화(final 변수는 대문자로만 기술)
//A=20; -error(final은 값을 변경할수 없다)
System.out.println("A = "+A);
//final 변수는 반드시 초기값을 주어야 한다
final int B;
B=30;
//B=40; -error
System.out.println("B = "+B);
Final f = new Final();
System.out.println("FRUIT = "+f.FRUIT);
System.out.println("ANIMAL = "+Final.ANIMAL);
System.out.println("ANIMAL2 = "+Final.ANIMAL2);
System.out.println();
System.out.println("빨강 = "+Color.RED);
System.out.println("빨강 = "+Color.RED.ordinal());
for(Color data : Color.values()) {
System.out.println(data+"\t"+data.ordinal());
}
}
}
/*
A = 10
B = 30
static 초기화 영역
기본 생성자
FRUIT = 사과
ANIMAL = 기린
ANIMAL2 = 코끼리
빨강 = RED
빨강 = 0
RED 0
GREEN 1
BLUE 2
*/