자식 클래스의 데이터 영역과 메소드 영역에 부모 클래스의 필드와 메소드를 상속 받는다. (extends)
class Animal { // 부모 클래스의 이름이 Animal
}
class Cat extends Animal { // 자식 클래스의 이름이 Cat
}
그러면 자식 클래스에서 생성된 객체의 주소를 통해 부모 클래스의 필드와 메소드를 사용 가능하다. 자식 클래스에 상속된 부모 클래스의 필드와 메소드는 자식 클래스에서 보이지는 않지만 사용 가능 반대로 부모 클래스에서 생성된 객체의 주소를 통해 자식 클래스의 필드와 메소드는 사용 불가능하다.
[ 타입 변환과 오버라이딩 ]
extends를 통해 Parent 클래스의 매소드와 필드를 Child의 매소드와 필드로 상속 받을 수 있다.
(Child 클래스에서 Parent 클래스의 영역과 Child 클래스의 영역 모두 사용 가능해짐)
그런데......
Parent parent = new Child(); (자동 타입 변환)
이런 식으로 Parent의 객체 참조 변수에 Child의 주소를 할당해주면....두 클래스의 객체 참조 변수가 Child 클래스에 생성된 주소를 참조하는 동시에 Child의 타입이 부모 클래스로 변환된다.
그러면 Child(자식 클래스)의 매소드와 필드를 사용할 수 없고 Parent 클래스 (부모 클래스) 의 영역만 사용할 수 있게 된다.
그 상태에서 굳이 Child의 영역을 사용해야 한다면 Child child = (Child) parent; (강제 타입 변환)를 통해 강제로 다시 자식 클래스로 형변환 해주면 사용 가능하다.
그렇다면
왜 굳이 자동 타입 변환까지 겪고 다시 강제 타입 변환까지 하면서
Parent parent = new Parent(); / Child child = new Child(); 를 써서 각각 다른 주소를 할당해서 쓰지 않고
Parent parent = new Child(); 를 사용해서 같은 주소(Child클래스에서 생성된 주소)를 참조하게 만든걸까?
=> 다형성을 구현하기 위해서!
다형성을 구현하기 위해서는 자동 타입 변환과 메소드 재정의(오버라이딩)가 필요함.
애초에 부모 클래스로 부터 메소드와 필드를 상속 받는 이유는 부모 클래스의 메소드와 필드를 재정의(오버라이딩) 해서 효율적으로 코드를 쓰기 위해서다.
(이미 잘 개발된 클래스를 재사용해서 새 클래스를 만들기 때문에 중복되는 코드를 줄여 개발 시간을 단축시킨다.)
(클래스의 수정을 최소화할 수 있다. - 부모 클래스를 수정하면 모든 자식 클래스에서 또 수정할 필요가 없음!)
자식 클래스에서 선언된 메소드와 필드는 비록 사용할 수 없지만 부모 클래스의 메소드를 가져와서 오버라이딩 하는 것은 가능하다. (자식 클래스에서 부모 클래스의 메소드를 오버라이딩 하면 부모 메소드가 아닌 오버라이딩된 자식 메소드가 호출된다.)
다형성 ?
사용 방법은 동일 : 두개 이상의 클래스 에서 동일한 메소드를 가지고 있음 / 실행 결과가 다양하게 나옴
오버로딩 오버로딩 뭐가 다른지? 오버로딩 ? 오버라이딩 ?
[ 오버 로딩 ] 한 클래스 내에서 혹은 상속 관계에서 둘 다 가능(상속이 있든 말든 상관없이 사용 가능) / 메소드 이름은 같고 / 입력받을 개수나 순서 또는 데이터 타입 다르게 / 리턴 타입과 매개변수의 이름은 상관 없음 : 오버로딩
class Name {
static void nameChange(int a, int b) {
System.out.println(a + b);
}
static void nameChange(int c, int d) {
System.out.println(c / d);
}
}
public class Overloading {
public static void main(String[] args) {
Name n01 = new Name();
Name.nameChange(20, 4);
Name.nameChange(20, 4);
}
}
메소드 이름이 같다. (1번 조건 성립)
매개변수의 개수와 데이터 타입도 같다. (2번 조건 성립x)
= > 오버로딩 성립이 안 된다.
메소드 이름이 같다 && 매개변수에도 변화가 있다 = 성립
매개변수의 이름(a,b,c,d)이 바뀌는 것은 오버로딩과 전혀 상관 없다.
소스코드의 가독성을 높이고 특정한 기능이나 유사한 기능을 빠르게 찾을 수 있지만 오히려 혼동을 줄 수도 있다. [ 오버 라이딩 ] 선언부(리턴 타입, 메소드 이름, 매개변수) 같고 메서드 블록의 내용이 재정의 : 오버라이딩
[ 추상 메서드 ]
- 추상 메서드를 선언하는 곳 : 추상 클래스 & 인터페이스 [1. 추상 클래스 ] 자식 클래스(Dog/Cat)로 부터 공통적(동물)인 필드나 메소드를 추출해서 부모 클래스(Animal)을 선언 후 상속 public abstract class 클래스 이름 { }
new 연산자를 이용해서 객체(인스턴스)를 직접 만들지 못하고 상속을 통해 자식 클래스만 만들 수 있다.
자식 클래스를 통해 객체를 만들어 대신 사용한다.
왜 사용하나?
자식 클래스들이 가지고 있는 공통 메소드를 뽑아내어 추상 클래스로 작성할 때, 메소드 선언부만 동일하고 실행 내용은 자식 클래스마다 달라야 하는 경우 추상 메소드를 선언할 수 있음
일반 메소드 선언과의 차이점은 abstract 키워드가 붙고, 메소드 실행 내용인 중괄호 { }가 없다.
동물은 소리는 낸다.(공통점/메소드 선언부 동일) 그런데 무슨 소리를 낼지는 아직 모른다.(실행 내용은 자식 클래스마다 다르다)
인터페이스를 통해 구현을 명령 받은 클래스를 통해 생성된 객체의 주소를 찾아가 데이터 영역과 메소드 영역을 제어/컨트롤 할 수 있다.
RemoteControl 인터페이스와 Searchable 인터페이스로 부터 구현을 명령 받은 SmartTelevision을 통해 생성된 객체의 주소에 찾아가 RemoteControl 인터페이스와 Searchable 인터페이스에 존재하는 추상 메소드를 반드시!! 구현해야 한다!
public static final - 상수 선언 public static final int MAX_NUMBER = 10; 원래 인터페이스는 일반 변수를 선언할 수 없는 구조 / 변수를 생성하면 알아서 상수로 구분되기 때문에 public static final 생략해도 알아서 상수로 인식해준다. (상수 선언은 대문자와 언더바를 이용하도록 한다.) 추상클래스는 일반 변수도 가질 수 있고 일반 메소드도 가질 수 있다. [ 예외 ] 예외: 잘못된 사용 또는 코딩으로 인한 오류 아예 실행이 불가능한 에러와 달리 예외 처리를 통해 계속 실행 상태를 유지할 수 있음 일반 예외(Exception): 컴파일러가 예외 처리 코드 여부를 검사하는 예외 실행 예외(Runtime Exception): 컴파일러가 예외 처리 코드 여부를 검사하지 않는 예외 try-catch-finally 반드시 catch 블록 넣어줘야 함 try가 정상 실행되었을 경우 try - finally try에서 예외 발생했을 경우 try - catch - finally
catch 블록은 예외 상황에 따라 여러 개 생성 가능하지만 최종적으로 발생한 예외에 해당하는 catch 블록 딱 하나만 실행된다.
[ 생성자 메소드 ]
객체를 생성한 후에 값을 셋팅하지 않고 객체를 생성하는 동시에 값을 셋팅하는 유일한 방법 defualt 생성자 메서드 선언 : 생성자 메서드 오버로딩을 할때는 반드시 같인 선언해줘야 한다.
public CreMethod() { // defualt 생성자 메소드 선언
// 처리 과정 추가
}
public CreMethod(String name) { // 생성자 메소드 오버라이딩1
System.out.println(name + "이름");
}
CreMethod b = new CreMethod(); // defualt 생성자 메소드의 주소 생성
CreMethod b = new CreMethod("홍길동"); // 생성자 메소드 오버라이딩1의 주소 생성
// 이런 경우에는 defualt 생성자 메소드 선언과 주소생성을 꼭 해줘야 한다.
오버로딩을 하지 않고 defualt 생성자 메서드만 필요할 때는 굳이 쓰지 않아도 된다.
public CreMethod() { // defualt 생성자 메소드 선언 - 생략 가능
// 처리 과정 추가
}
CreMethod b = new CreMethod(); // defualt 생성자 메소드의 주소 생성
// 이런 경우에는 defualt 생성자 메소드만 필요하기 때문에 굳이 선언 필요 없음
public CreMethod(String name) { // 생성자 메소드 선언
System.out.println(name + "이름");
}
CreMethod b = new CreMethod("홍길동"); // 생성자 메소드 주소 생성
// 이런 경우에는 생성자 메소드 오버라이딩 아니기 때문에 defualt 생성자 메소드 선언 필요 없음