[Java의 정석] chapter06 객체지향 프로그래밍 II - 요약정리
🖤 1. 상속
1.1 상속의 정의와 장점
- extends 키워드 사용.
- 생성자, 초기화 블럭은 상속되지 않는다. 멤버만 상속된다.
1.2 클래스간의 관계 - 포함관계
포함관계 : 한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언하는 것
- ~은 ~이다 → 상속관계
- ~은 ~을 가지고 있다 → 포함관계
- ex_ 원은 점이다 X , 원은 점을 가지고있다 O
class Point {
int x,y;
}
class Circle {
Point c = new Point();
int r;
}
1.4 단일 상속 (single inheritance)
자바에서는 오직 단일 상속만을 허용한다.
1.5 Object클래스 - 모든 클래스 조상
☀️Object 클래스
- 모든 클래스 상속계층도의 최상위에 있는 조상클래스
- 컴파일러가 클래스에 자동으로 extends Object 추가
🖤 2. 오버라이딩
2.1 오버라이딩이란?
조상 클래스로부터 상속받은 메서드 내용을 변경하는 것
2.2 오버라이딩 조건
자손 클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와 선언부가 서로 일치해야한다.
- 이름이 같아야 한다.
- 매개변수가 같아야 한다.
- 반환타입이 같아야 한다.
조상 클래스의 메서드를 자손 클래스에서 오버라이딩 할 때
- 접근 제어자를 조상 클래스 메서드보다 좁으 범위로 변경할 수 없다.
- 예외는 조상 클래스 메서드보다 많이 선언할 수 없다.
- 인스턴스 메서드를 static메서드로 또는 그 반대로 변경할 수 없다.
2.3 오버로딩 vs 오버라이딩
오버로딩: 기존에 없는 새로운 메서드 정의 (new)
오버라이딩: 상속받은 메서드 내용 변경 (change, modify)
class Parent {
void parentMethod() {}
}
class Child extends Parent {
void parentMethod() {} // 오버라이딩
void parentMEthod(int i) {} // 오버로딩
void childMethod() {}
void childMethod(int i) {} // 오버로딩
void childMethod() {} // 에러. 중복정의!!!
}
2.4 super
자손클래스에서 조상 클래스로부터 상속받은 멤버 참조하는 참조변수
조상,자손 클래스 멤버가 중복 정의되어 서로 구별해야 하는 경우에만 super를 사용
class Parent{
int x=10;
}
class Child extends Parent{
void method(){
System.out.println("x=" + x);
System.out.println("this.x=" + this.x);
System.out.println("super.x" + super.x);
}
}
// method호출시
x = 10
this.x = 10
super.x = 10
아래는 위와 달리 같은 이름 멤버변수가 Parent에도 있을때이다. 이때에는 super.x와 this.x는 서로 다른 값을 참조한다.
class Parent{
int x=10;
}
class Child extends Parent{
int x=20;
void method(){
System.out.println("x=" + x);
System.out.println("this.x=" + this.x);
System.out.println("super.x" + super.x);
}
}
// method호출시
x = 20
this.x = 20
super.x = 10
2.5 super() - 조상 클래스의 생성자
super()는 조상 클래스의 생성자를 호출한다.
Object 클래스를 제외한 모든 클래스의 생성자 첫 줄에 생성자, this() 또는 super()를 호출해야한다. 그렇지 않으면 컴파일러가 자동적으로 super();를 생성자의 첫 줄에 삽입한다.
예를들어 아래를 보자.
class Point{
int x,y;
Point(int x, int y){
this.x = x;
this.y = y;
}
}
class Point3D extends Point{
int z;
Point3D(int x, int y, int z){ // 컴파일 에러 발생!!!
this.x=x;
this.y=y;
this.z=z;
}
void method(){
System.out.println(x +" " + y + " " + z);
}
}
위는 컴파일에러가 발생한다. Point3D의 생성자 첫 줄에서 다른 생성자를 호출하지 않기 때문에, 컴파일러는 super();를 첫줄에 삽입한다. 그러나 Point 클래스에 생성자 Point()가 정의되어있지않기 때문에 컴파일 에러가 발생한 것이다.
Point3D(int x, int y, int z){
super(); // super(x,y);
this.x=x;
this.y=y;
this.z=z;
}
따라서, 이 에러를 수정하려면 Point클래스에 Point()를 추가하거나 Point3D에 super(x,y)를 추가해주면 된다.
참고로, 생성자가 정의되어 있는 클래스에는 컴파일러가 기본 생성자를 자동적으로 추가하지않는다.
조상 클래스의 멤버변수는 조상의 생성자에 의해 초기화되도록해야한다. 아래예시를 보자.
class Main {
public static void main (String[] args) throws java.lang.Exception {
Point3D p3 = new Point3D();
System.out.println("p3.x=" + p3.x);
System.out.println("p3.y=" + p3.y);
System.out.println("p3.z=" + p3.z);
}
}
class Point{
int x=10;
int y=20;
Point(int x,int y) {
this.x = x;
this.y = y;
}
}
class Point3D extends Point {
int z = 30;
Point3D() {
this(100,200,300); // Point3D(int x, int y,int z) 호출
}
Point3D(int x, int y,int z){
super(x, y); // Point(int x, int y)를 호출
this.z = z;
}
}
p3.x=100
p3.y=200
p3.z=300
위의 생성자 호출 순서는 다음과 같다. 거슬러올라가며 모든 조상클래스의 생성자가 순서대로 호출된다.
Point3D() → Point3D(int x, int y,int z) → Point(int x, int y) → Object()
🖤 4. 제어자 (modifier)
4.1 제어자란?
클래스, 변수 또는 메서드 선언부에 함께 사용되어 부가적인 의미를 부여한다. 크게 접근 제어자와 그 외로 나뉜다.
4.3 final - 마지막의, 변경될 수 없는
final은 거의 모든 대상에 사용될 수 있다. 변수에 사용시 상수가 되며, 메서드에 사용되면 오버라이드(재정의)할 수 없고 클래스에 사용되면 자신을 확장하는 자손 클래스를 정의하지 못하게 된다.
final class FinalTest { // 조상이 될 수 없는 클래스
final int MAX_SIZE = 10; // 값 변경 불가 멤버변수 (상수)
final void getMaxSize() { // 오버라이딩할 수 없는 메서드 (변경불가)
final int LV = MAX_SIZE; // 값 변경 불가 지역변수 (상수수)
return MAX_SIZE;
}
}
또한, 인스턴스 변수의 경우 생성자에서 초기화가 가능하다.
class Card {
final int NUM;
Card(int num){ // 생성자에서 단 한 번 초기화 가능
NUM = num;
}
}
4.4 abstract - 추상의, 미완성의
abstract는 클래스,메서드에서 사용될 수 있다.
추상 클래스는 아직 완성되지 않은 메서드가 존재하는 '미완성 설계도'이다.
4.5 접근 제어자 (access modifier)
- private: 같은 클래스 내 접근 가능
- default: 같은 패키지 내 접근 가능
- protected: 같은 패키지 내+다른 패키지 자손클래스에서 접근 가능
- public: 접근 제한이 전혀 없다.
☀️캡슐화
- 접근제어자를 사용하는 이유는 외부 데이터를 보호하기 위해서, 외부에 불필요한 내부 부분을 감추기 위해서이다.
- 예를 들어, 멤버변수를 private이나 protected로 제한하고, public메서드를 제공함으로써 간접적으로 멤버변수 값을 다룰 수 있게 할 수 있다.
☀️생성자의 접근 제어자
- 생성자의 접근 제어자를 private으로 지정하면, 외부에서 생성자에 접근할 수 없으므로 인스턴스를 생성할 수 없게 된다.
class SingleTon {
// getInstance()에서 사용될 수 있도록 인스턴스가 미리 생성되어야 하므로 static
private static Singleton s = new Singleton();
private Singleton() {
}
// 인스턴스를 생성하지 않고도 호출가능해야하므로 static
public static Singleton getInstance(){
return s;
}
}
- 생성자가 private인 클래스는 다른 클래스의 조상이 될 수 없다. ( 자손클래스인스턴스생성시 조상클래스 생성자를 호출하는데, 이때 접근 제어자가 private이므로 호출 불가 ) 이럴때에는 클래스 앞에 final을 추가하여 상속할 수 없는 클래스임을 알리는 것이 좋다.
final class SingleTon {...}
다형성부터..to be continue...