본문 바로가기
Back-End/Java

객체지향 프로그래밍 Ⅱ

by 달의 조각 2021. 12. 16.
이 글은 남궁성 님의 Java의 정석 책을 바탕으로 정리한 글입니다.

 

 

상속

 

 

상속: 코드를 재사용하여 적은 양의 코드로 새로운 클래스를 작성하여 코드의 중복을 제거한다
포함: 클래스 멤버로 다른 클래스 타입의 참조변수를 선언한다

1. 생성자와 초기화 블럭은 상속되지 않는다. (멤버만 상속)
2. 단일 상속만 허용한다 (다중 상속 효과: 인터페이스)
3. Object 클래스: 모든 클래스의 조상

상속 관계 '~은 ~이다.(is-a)' : SportsCar는 Car이다
포함 관계 '~은 ~을 가지고 있다.(has-a)' : Empoyee는 Address를 가지고 있다
class TVCR extends TV { // 상속
    VCR vcr = new VCR(); // VCR클래스를 포함
}

 


 

오버라이딩

 

 

조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것
상위 클래스 타입으로 하위 클래스 메서드를 다룰 수 있다

1. 메서드의 선언부는 조상의 것과 완전히 일치해야 한다. (이름, 매개변수, 반환 타입)
2. 접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
3. 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
4. 인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없다.

오버로딩: 기존에 없는 새로운 메서드를 정의(new)
오버라이딩: 상속받는 메서드의 내용을 변경(change) - 덮어쓰기
        Vehicle bike = new Bike();
        Vehicle car = new Car();
        Vehicle motorBike = new MotorBike();
Vehicle[] vehicles = new Vehicle[] { new Bike(), new Car(), new MotorBike()};
for (Vehicle vehicle : vehicles) {
		vehicle.run();
}

 


1. 참조변수 super

this와 마찬가지로 인스턴스 메서드(생성자)에서만 사용 가능
메서드 호출 가능

class Upper {
    int count = 20; //super.count
}

class Lower extends Upper {
    int count = 15; //this.count
}

 

2. super()

조상 클래스의 생성자 호출 - this()는 같은 클래스의 다른 생성자 호출

1. 생성자 안에서만 사용 가능, 첫 줄에 와야 한다
2. Object클래스를 제외한 모든 클래스의 생성자는 첫 줄에 반드시 this() 또는 super()를 선언해야 한다
그렇지 않은 경우에는 컴파일러가 자동으로 super()를 선언하는데, 상위에 기본 생성자가 없으면 에러 발생
→ 기본 생성자를 생성하는 습관을 가지자
3. 자손의 클래스 멤버가 조상의 클래스 멤버를 사용할 수도 있으므로 먼저 초기화되어 있어야 한다
    어떤 클래스의 인스턴스 생성 시, 최고 조상인 Object까지 거슬러 가면서 모든 조상 클래스의 생성자가 호출됨

class Human {
    Human() {
        System.out.println("휴먼 클래스 생성자");
    }
}

class Student extends Human {
    Student() {
        super();
        System.out.println("학생 클래스 생성자");
    }
}

 

🥭 자손 클래스 인스턴스 생성 과정

1. 자손의 멤버 + 조상의 멤버가 합쳐진 하나의 인스턴스 생성
2. 조상 클래스 멤버의 초기화 작업이 수행되어야 한다
3. 자손 클래스의 생성자에서 조상 클래스의 생성자가 호출되어야 한다
    3.1 초기화 작업을 해 놓지 않으면 에러 발생
4. 조상 클래스의 멤버변수는 조상의 생성자에 의해 초기화되도록 한다.

인스턴스 생성 시 클래스를 선택하는 것만큼 생성자를 선택하는 것도 중요

1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것인가?
2. 생성자 - 선택한 클래스의 어떤 생성자를 이용해서 인스턴스를 생성할 것인가?

 

 


 

 

캡슐화

 

데이터 보호
외부에서 접근할 필요가 없는 멤버들을 외부에 노출시키지 않음

접근 제어자 public, protected, default, private (하나만 사용 가능)
그 외          static, final, abstract, native, transient, synchronized, volatile, strictfp
private: 같은 클래스 내에서만 접근 가능
default: 같은 패키지 내에서만 접근 가능
protected: 같은 패키지 내 + 다른 패키지의 자손패키지에서 접근 가능
public: 접근 제한 없음
1. 메서드에 static과 abstract를 함께 사용 불가능: static메서드는 몸통이 있는 메서드에만 사용 가능
2. 클래스에 abstract와 final 동시 사용 불가능
3. abstract메서드의 접근 제어자가 private일 수 없다.
4. 메서드에 private과 final을 같이 사용할 필요는 없다

 

final: 변경이 불가하거나 확장되는 성질을 지니게 된다

클래스 내 매개변수를 갖는 생성자 선언 후
이 기능을 활용하면 각 인스턴스마다 final이 붙은 멤버변수가 다른 값을 갖도록 하는 것 가능

 

 


 

다형성(polymorphism)

 

 

여러가지 형태를 가질 수 있는 능력

조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있다.

참조변수가 사용할 수 있는 멤버의 개수는 상위 클래스의 멤버의 수이다

자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.
참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다.

 

🎮 리모컨을 생각하자!

기능이 많은 리모컨으로 기능이 적은 티비를 조작하면 서로 맞지 않아서 문제가 된다
기능이 적은 리모컨으로 기능이 많은 티비를 조작하면 리모컨에 있는 버튼의 기능 하에 사용할 수 있다!

 

1. 참조변수의 형변환

리모컨 바꾸기

서로 상속 관계에 있는 클래스 사이에서,
참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(개수) 조절하는 것

 

2. instanceof 연산자

참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 사용
형변환이 가능한가?

if (c instanceof FireEngine) { // ture일 경우 연산 가능
	...
}

//c는 FireEngine의 자손이다 반대일 경우 X

 

3. 참조변수와 인스턴스의 연결

<조상 클래스에 선언된 멤버변수와 같은 이름의 인스턴스변수를 자손 클래스에 중복 정의할 경우>

  • 메서드는 참조변수의 타입에 관계 없이 항상 실제 인스턴스타입(오버라이딩된 메서드)에 정의된 메서드 호출
  • 인스턴스변수는 참조변수 타입에 영향
  • 인스턴스메서드는 참조변수 타입에 영향을 받지 않지만 static메서드는 참조변수의 타입에 영향을 받기 때문에 참조변수가 아닌 '클래스이름.메서드()'로 호출해야 한다.

 

4. 다형성의 활용

1. 매개변수의 다형성(매개변수로 자손 타입들을 받을 수 있다)
2. 여러 종류의 객체를 배열로 다루기: 배열의 크기는 Vector클래스 이용(내부적으로 Object타입의 배열)

 


 

추상클래스(abstract class)

미완성 설계도(부분적 완성)

 

 

메시지 바디가 없는 추상 메서드를 하나 포함한다
상속 관계에 있어서 새로운 클래스 작성에 유용하다 - 구현은 하위에서 하도록 비워 둘 수 있음!

abstract class AbstractExmple {
	abstract void start();
}

//AbstractExmple abstractExmple = new AbstractExmple(); //인스턴스(객체) 생성 불가

 


 

인터페이스(interface)

기본 설계도(구현된 것 X)

 

 

추상클래스보다 더 높은 추상성, 추상메서드의 집합
몸통을 갖춘 일반 메서드 또는 멤버변수를 구성원으로 가질 수 없다

인터페이스는 인터페이스로만 상속 가능, 다중 상속 가능(Object클래스와 같은 최고 조상 없음)

- 클래스에서는 부모 클래스에 동일한 이름의 필드 or 메서드가 존재하면 충돌 발생,
   But 인터페이스는 미완성 멤버를 가지므로 충돌 발생 여지 없음

리턴타입이 인터페이스: 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다

표준화가 가능하며, 서로 관계 없는 클래스끼리 관계를 맺어 줄 수 있다

interface 인터페이스이름 {
	public static final 타입 상수이름 = 값;
	public abstract 메서드이름(매개변수목록);
}

class 클래스이름 implements 인터페이스이름, 인터페이스이름2 {
	// 인터페이스에 정의된 모든 추상메서드 구현
	// 일부만 구현할 경우 abstract 표기
}
모든 멤버 변수는 public static final이어야 하며, 생략 가능(오버라이딩 시 접근 제어자 범위 유의)
모든 메서드는 public abstract이어야 하며, 생략 가능(JDK1.8부터 static메서드와 디폴트 메서드는 예외)

 

 


 

 

내부 클래스(inner class)

 

class Outer { // 외부 클래스
	
	class Inner {
		// 인스턴스 내부 클래스	
	}
	
	static class StaticInner {
		// 정적 내부 클래스
	}

	void run() {
		class LocalInner {
		// 지역 내부 클래스
		}
	}
} 

 

멤버 내부 클래스

main 메서드에서 내부 클래스를 사용하려면 먼저 외부 클래스를 만들어야 한다
때문에 클래스를 생성하지 않고 사용할 수 있는 정적 변수, 정적 메서드는 내부 클래스에서 선언할 수 없다

❓ 내부 클래스가 정적 변수를 사용할 수 있게 하려면?

정적 내부 클래스를 사용한다

'Back-End > Java' 카테고리의 다른 글

컬렉션 프레임웍  (0) 2021.12.30
예외 처리(exception handling)  (0) 2021.12.26
객체지향 프로그래밍 Ⅰ  (0) 2021.12.02
배열(array)  (0) 2021.11.25
연산자(Operator)  (0) 2021.11.16

댓글