상속이란, 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다.
사용하는 방법
class 자식클래스 extends 부모클래스 {
// ....
}
조상클래스 : 부모(parent)클래스, 상위(super)클래스, 기반(base)클래스
자손클래스 : 자식(childd)클래스, 하위(sub)클래스, 파생된(derived)클래스
조상 클래스가 변경되면 자손 클랫는 자동적으로 영향을 받게 되지만, 자손클래스가 변경되는 것은 조상클래스에 아무런 영향을 주지 못한다.
즉, 상속을 거듭할수록 상속받는 클래스의 멤버 개수는 점점 늘어나게 된다.
※ 참고
접근제어자가 private 또는 default인 멤버들은 상속되지 않는다기 보다는 상속은 받지만 자손 클래스로 부터의 접근이 제한되는 것이다.
class Parent { }
class child extends Parent {}
![](https://velog.velcdn.com/images/hoegon02/post/0480ef9a-e34f-4794-a5ae-c476df6b3058/image.png)
class child2 extends Parent {}
class grandchild extends child {}
클래스간의 관계를 맺어 주고 클래스를 재사용하는 방법
포함이란?
예제
여기서 다른 클래스에서 호출하고싶을때(예를들어 main함수) 인스턴스 객체이기 때문에 객체를 생성했을때
Circle c = new Circle();
왼쪽에서 코드의 참조변수 c가 가르키는 인스턴스 멤버변수는
c.x
c.y
c.r
오른쪽에서 코드의 참조변수 c가 가르키는 인스턴스 멤버변수는
c.c.x
c.c.y
c.r
이와 같이 한 클래스를 작성하는 데 다른 클래스를 멤버 변수로 선언하여 포함시크는 것은 좋은 생각이다.
상속관계 : ~은 ~이다
포함관계 : ~은 ~을 가지고 있다
① 포함 : 90% (잘모르겠다 하면 포함해서 코드개선)
② 상속 : 꼭 필요할때만
자바에서는 오직 단일 상속만을 허용한다.
둘이상의 클래스로 부터 상속을 받을 수 없다.
추상화(다중상속가능 메서드가 정의되지 않은 것이기 때문에)
단일 상속 : 하나의 클래스로부터 상속받는것을 단일 상속이라고 한다.
단일 상속을 하는 이유 : 두개의 조상클래스로 부터 상속을 받게된다면, 각각의 클래스 내부의 동일한 메서드명을가진 메서드끼리 충돌이 발생하게된다. 그러하면 오버로딩을 진행하여 매개변수를 변경해주거나 조상 클래스의 메서드명을 변경해줘야한다. 이렇게 하면 그 조상 클래스의 메서드를 사용하는 모든 클래스들도 변경을 해야하므로 그리 간단한 문제는 아니다.
자바에서는 다중상속의 이러한 문제점을 해결하기 위해 다중상속의 장점을 포기하고 단일 상속만을 허용한다.
장점 : 하나의 조상 클래스만을 가질 수 있기 때문에 다중상속에 비해 불편한 점도 있지만, 클래스 간의 관계가 보다 명확해지고 코드를 더욱 신뢰할 수 있게 만들어 준다는 점에서 다중상속보다 유리하다.
다중상속과 비슷한 효과를 나타내는 방법 :
① 하나의 조상클래스로부터 단일상속을 받는다.
② 두번째 해당하는 조상클래스를 클래스간의 관계에서의 포함관계를 이용하여 활용한다.
이러하면 메서드간의 충돌을 최소화할수 있다.
상속 계층도를 단순화하기 위해서는 Object클래스를 생략하는 경우가 많다.
조상클래스로 부터 상속받은 메서드의 내용을 변경하는 것을 오버라이딩 이라고한다.
< 오버라이딩 1차 조건 >
① 조상클래스로부터 상속 받아야한다.
② 조상의 메서드를 오버라이딩 한다. (재정의한다.)
② 구현부를 수정한다.
ex
class Point {
int x;
int y;
String getLocation() {
return "x :" + x + ", y:" + y;
}
}
class Point3D extends Point {
int z;
String getLocation() {
return "x :" + x + ", y:" + y + ", z :" + z;
}
}
자손 클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와
한마디로 요약하면 선언부가 서로 일치해야한다.
다만, 접근 제어자와 예외는 제한된 조건 하에만 다르게 변경할 수 있다.
만일 조상 클래스에 정의된 메서드의 접근 제어자가 protected라면, 이를 오버라딩하는 자손 클래스의 메서드는 접근 제어자가 protected나 public이어야 한다. 대부분의 경우 같은 범위의 접근 제어자를 사용한다. 접근 제어자의 접근범위를 넓은 것에서 좁은 것 순으로 나열하면 public, protected, (default), private이다.
코드로 이해
예외처리 + 오버라이딩을 진행할때 다음과같이 진행을 해야한다.
예외의 조상 Exception
조상 클래스의 메서드를 자손클래스에서 오버라이딩 할때
접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.
인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없다.
오버로딩과 오버라이딩은 서로 혼동하기 쉽지만 사실 그 차이는 명백하다.
오버로딩(overloading) 적재한다
오버라이딩(overrriding) 덮어쓴다
super : super는 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수 이다.
멤버변수와 지역변수의 이름이 같을 때 this를 붙여서 구별했듯이 상속받은 멤버와 자신의 멤버와 이름이 같을 때는 super를 붙여서 구별할 수 있다.
조상 클래스로부터 상속받은 멤버도 자손 클래스 자신의 멤버이므로 super 대신 this를 사용할 수 있다. 그래도 조상 클래스의 멤버와 자손클래스의 멤버가 중복 정의되어 서로 구별해야하는 경우에만 super를 사용하는 것이 좋다.
컴파일러 실행결과
① 컴파일러가 Child c = new Child(); 객체생성을 읽음
② c.method(); 호출
③ System.out.println("x=" +x); 메서드의 int x = 20; 을 가져옴
④ System.out.println("this.x=" +this.x); 메서드의 인스턴스 멤버변수를 가져옴
⑤ System.out.println("super.x=" +super.x); 조상의 초기값을 가져옴
this() 와 마찬가지로 super() 역시 생성자 이다. this()는 같은 클래스의 다른 생성자를 호출하는 데 사용하지만,
super()는 조상 클래스의 생성자를 호출하는데 사용된다.
자손 클래스의 인스턴스를 생성하면(상속을 받은 후 인스턴스 객체화를 진행하면) 자손의 멤버와 조상의 멤버가 모두 합쳐진 하나의 인스턴스가 생성된다. 즉 상속을 받게되면 상속받은 변수들은 조상 클래스의 멤버들을 사용하려면 초기화 작업이 수행되어야 하는데 이때 조상클래스의 멤버들의 초기화를 하는 방식이 super()를 사용하는 것이다.
인스턴스를 생성할 때 주의하고 생각해야되는 점
1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것인가?
2. 생성자 - 선택한 클래스의 어떤 생성자를 이용해서 인스턴스를 생성할 것인가?
코드의 이해
패키지 : 클래스의 묶음
String 클래스의 실제 이름은 java.lang.String 이다
java.lang 패키지에 속한 String 클래스 라는 의미이다.
클래스가 물리적으로 하나의 클래스파일(.class)인 것과 같이 패키지는 물리적으로 하나의 디렉토리이다.
개발자 규칙
하나의 소스파일에는 첫 번쨰 문장으로 단 한 번의 패키지 선언만 허용한다.
모든 클래스는 반드시 하나의 패키지에 속해야 한다.
패키지는 점(.)을 구분자로 하여 계층구조로 구성할 수 있다.
패키지는 물리적으로 클래스 파일(.class)을 포함하는 하나의 디렉토리 이다.
classpath 는 컴파일러(javac.exe)나 JVM등 클래스의 위치를 찾는데 사용되는 경로이다.
윈도우즈에서 환경 변수 설정하는 방버
소스코드를 작성 할때 다른 패키지의 클래스를 사용하려면 패키지명이 포함된 클래스 이름을 사용해야한다.
클래스의 코드를 작성하기 전에 import문으로 사용하고자 하는 클래스의 패키지를 미리 명시해주면 소스코드에 사용되는 클래스이름에서 패키지명을 생략할 수 있다.
이클립스 단축키 ctrl + shift + o 를 누르면, 자동으로 import 문을 추가해주는 편리한 기능을 제공한다.
참고
import 문은 프로그램의 성능에 전혀 영향을 미치지 않는다. import 문을 많이 사용하면 컴파일 시간이 아주 조금 더 걸릴 뿐이다
일반적인 소프아일 (*.java)의 구성은 다음의 순서로 되어있다.
① package문
② import 문
③ 클래스 선언
import 문을 사용하면 클래스의 패키지명을 생략할 수 있는 것과 같이 static import문을 사용하면 static멤버를 호출 할 때 클래스 이름을 생략할 수 있다. 특정 클래스의 static 멤버를 자주 사용할 때 편리하다.
ex
import static java.lang.Integer.*;
import static java.lang.Math.random;
import static java.lang.System.out;
만일 위와 같이 static import 문을 선언하였다면, 아래의 왼쪽 코드를 오른쪽 코드와 같이 간략히 할 수 있다.
제어자는 클래스, 변수 또는 메서드의 선언부에 함께 사용되어 부가적인 의미를 부여한다. 제어자의 종류는 크게 접근 제어자와 그 외의 제어자로 나눌 수 있다.
접근 제어자 : public, protected, default, private
그 외 : static, final, abstract, native, transient, synchronized, volatile, strictfp
제어자는 클래스나 멤버변수와 메서드에 주로 사용되며, 하나의 대상에 대해서 여러 제어자를 조합하여 사용하는 것이 가능하다.
참고
제어자들 간의 순서는 관계없지만 주로 접근 제어자를 제일 왼쪽에 놓는 경향이 있다.
접근 제어자를 사용하는 이유?
외부로부터 데이터를 보호하기 위해서, 직접 접근은 막고 메소드를 통한 간접 접근을 허용하려고
외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해서
접근제어자 주의해야 할 점
메서드에 static과 abstract를 함께 사용할 수 없다.
static 메서드는 몸통이 있는 메서드에만 사용할 수 있기 때문이다.
클래스에 abstract와 final을 동시에 사용할 수 없다.
클래스에 사용되는 final은 클래스를 확장할 수 없다는 의미이고 abstract는 상속을 통해서 완성되어야 한다는 의미이므로 서로 모순되기 때문이다.
abstract 메서드의 접근 제어자가 private일 수 없다
abstract메서드는 자손 클래스에서 구현해주어야 하는데 접근 제어자가 private이면, 자손클래스에서 접근할 수 없기 때문이다.
메서드에 private과 final을 같이 사용할 필요는 없다.
접근 제어자가 private인 메서드는 오버라이딩될 수 없기 때문이다. 이 둘 중 하나만 사용해도 의미가 충분하다.
※참고
클래스는 상속을 통해서 확장될 수는 있어도 축소 될 수는 없어서, 조상 인스턴스의 멤버 개수는 자손 인스턴스의 멤버 개수보다 항상 적거나 같다.
조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있다.
반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.
Q. 참조변수의 타입은 인스턴스의 타입과 반드시 일치해야 하나요?
아닙니다. 일치하는 것이 보통이지만 일치 하지 않을 수도 있습니다.
Q. 참조변수가 조상타입일 때와 자손타입일 때의 차이?
참조변수로 사용할 수 있는 멤버의 갯수가 달라집니다.
Q. 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 있나요?
아니요 허용되지 않습니다.
기본형 변수와 같이 참조변수도 형변환이 가능하다. 단, 서로 상속관게에 있는 클래스사이에서만 가능하기 때문에 자손타입의 참조변수를 조상타입의 참조변수로, 조상타입의 참조변수를 자손타입의 참조변수로의 형변환만 가능하다.
형변환 과정
① 확인 : 형변환 해도 되는지
② 형변환
확인하는 방법
void dowork(Car c) {
if(c instanceof FireEngine) {
FireEngine fe = (FireEngine)c;
fe.water();
}
}