상속이란, 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것
코드의 재사용성을 높이고 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여
class Parent {} // 상위 클래스
class Child extends Parent {} // 상위 클래스를 상속받은 하위 클래스
class TV {
boolean power;
int channel;
void power() {
power = !power;
}
void channelUp() {
++channel;
}
void channelDown() {
--channel;
}
}
class CaptionTv extends TV {
boolean caption;
void displayCaption(String text) {
if (caption) {
System.out.println(text);
}
}
}
class CaptionTvTest{
public static void main(String[] args) {
CaptionTv ctv = new CaptionTv();
ctv.channel = 10;
ctv.channelUP();
System.out.println(ctv.channel);
ctv.displayCaption("Hello, World");
ctv.caption = true;
ctv.displayCaption("Hello, World");
}
}
자식 클래스의 인스턴스 생성 시 부모 클래스의 멤버와 자식 클래스의 멤버가 합쳐진 하나의 인스턴스가 생성된다.
그림을 보면 Tv클래스를 상속받은 CaptionTv는 TV클래스에 정의된 멤버변수를 사용할 수 있다.
부모 클래스가 변경되면 자식 클래스는 자동적으로 영향을 받지만 (TV클래스의 멤버 변경 시 CaptionTv도 변경) 자식 클래스가 변경되는 것은 부모 클래스에 아무런 영향을 주지 못한다.
하나 이상의 클래스로부터 상속받을 수 없다.
다중상속을 허용하지 않기 때문에 클래스의 포함관계 활용 가능
Object클래스는 모든 클래스의 부모로 다른 클래스로부터 상속 받지 않는 모든 클래스들은 컴파일러가 자동적으로 Object클래스로부터 상속받도록 한다.
부모 클래스로부터 상속받은 메서드의 내용을 자식 클래스에서 변경하는 것
부모 클래스의 메서드 선언부(이름, 매개변수, 반환타입)가 일치해야함
자식 클래스에서 부모 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다.
멤버변수와 지역변수의 이름이 같을 때 this를 붙여서 구별했듯이 부모 클래스와 자식 클래스의 멤버를 구별하기 위해 사용하며, 인스턴스메서드에서만 사용가능하다.
부모 클래스의 생성자 호출 시 사용하며 반드시 자식 클래스의 생성자 첫 줄에서 호출해야한다.
'클래스의', '공통적인'의미를 가지고 있으며 JVM의 메서드 영역에 저장되며 인스턴스를 생성하지 않아도 사용할 수 있다. 인스턴스변수는 하나의 클래스로부터 생성되었더라도 각기 다른 값을 유지하지만, 클래스변수(static멤버변수)는 인스턴스에 관계없이 같은 값을 갖는다.
변수에 사용되면 값을 변경할 수 없는 상수가 되며, 메서드에 사용되면 오버라이딩 불가, 클래스에 사용되면 상속이 불가능하다.
abstract class AbstractTest { // 추상 클래스(추상 메서드를 포함한 클래스)
abstract void move(); // 추상 메서드(구현부가 없는 메서드
}
추상 클래스는 아직 완성되지 않은 메서드가 존재하기 때문에 인스턴스를 생성할 수 없다.
해당 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하여 데이터를 보호할 수 있다.
private : 같은 클래스 내에서만 접근 가능
default : 같은 패키지 내에서만 접근 가능
protected : 같은 패키지, 다른 패키지의 자식 클래스에서 접근 가능
public : 접근 제한 없음
public class TimeTest {
public static void main(String[] args) {
Time t = new Time(12, 25, 30);
System.out.println(t);
t.setHour(t.getHour() + 1); // t.shour = 13처럼 멤버변수에 직접 접근 불가
System.out.println(t);
}
}
class Time {
private int hour;
@Override
public String toString() {
return hour + ":" + minute + ":" + second;
}
private int minute;
private int second;
Time(int hour, int minute, int second) {
setHour(hour);
setMinute(minute);
setSecond(second);
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
if (hour < 0 || hour > 23) {
return;
}
this.hour = hour;
}
public int getMinute() {
return minute;
}
public void setMinute(int minute) {
if (minute < 0 || minute > 59) {
return;
}
this.minute = minute;
}
public int getSecond() {
return second;
}
public void setSecond(int second) {
this.second = second;
}
}
hour는 0보다 같거나 크고 24보다는 작은 범위를 가져야하고 minute는 0보다 같거나 크고 59보다 작은 범위를 가져야한다. private 접근제어자를 통해 멤버변수의 직접적인 접근을 막고 getter, setter 메서드를 이용하여 간접적으로 멤버변수의 값을 읽고 변경할 수 있다.
public class Printer {
private static Printer printer = null; // 중복 객체 생성을 막기 위해 null로 초기화
private Printer(){} // 생성자의 접근 제어자를 private으로 지정하면 외부에서 인스턴스 생성 불가능
public static Printer getInstance() { // getInstance()메서드를 통해서만 생성 가능
if(printer == null) { // 객체가 이미 생성되었는지 판단
printer = new Printer();
}
return printer;
}
public void print(String input) {
System.out.println(input);
}
}
부모 클래스 타입의 참조변수로 자식 클래스의 인스턴스 참조 가능
TV t = new TV();
, CaptionTv c = new CaptionTv();
처럼 인스턴스 타입과 일치하는 참조변수 사용했지만 서로 상속관계에 있을 경우 부모 클래스의 타입의 참조변수로 자식 클래스의 인스턴스 참조가 가능하다.
Tv t = new CaptionTv();
이 경우 Tv클래스의 멤버들만 사용가능하며, CaptionTv에만 정의되어 있는 caption과 display Caption()은 참조변수 t로 사용이 불가능하다.
둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.
그렇다면 반대로 CaptionTv c = new Tv();
처럼 자식 타입의 참조변수로 부모 클래스 타입의 인스터를 생성하는 것은 가능할까?
불가능하다. Tv의 멤버 개수보다 참조변수 c가 사용할 수 있는 멤버 개수가 더 많기 때문이다.
참조변수의 형반환은 인스턴스 주소값은 그대로, 사용할 수 있는 멤버의 개수만 조절한다.
public class CastingTest1 {
public static void main(String[] args) {
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
fe.water();
car = fe; // car = (Car) fe; 형변환 생략 가능
// car.water(); 자식 클래스에 정의된 메서드 호출 불가
fe2 = (FireEngine) car; // 자식 타입 <- 부모 타입 형변환 생략 불가능
fe2.water(); // 자식 클래스에 정의된 메서드 호출 가능
}
}
class Car{
String color;
int door;
void drive() {
System.out.println("drive");
}
void stop() {
System.out.println("stop");
}
}
class FireEngine extends Car { // Car 클래스 상속
void water() {
System.out.println("water");
}
}