위로
아래
상속
상속
- 클래스가 가지고 있는 멤버를 다른 클래스에게 계승시키는 것
- C++은 다중 상속 (여러 클래스를 동시에 상속)을 쓰고, 자바는 단계별 상속을 이용한다.
- 상위 객체 클래스 : 부모 클래스(parent class), 슈퍼 클래스(super class), 기본 클래스(base class)
- 하위 객체 클래스 : 자식 클래스(child class), 서브 클래스(sub class), 파생 클래스(derived class), 확장 클래스(extended class)
상속의 장점
- 중복 코드를 줄일 수 있다
- 클래스 간의 전체 계층 구조를 파악하기 쉽다
- 새로운 클래스, 데이터, 메소드를 추가하기가 쉽다
- 데이터와 메서드를 변경할 때 상위의 있는 것만 수정해도 일괄 수정되어 유지보수가 용이하다
is-a와 has-a
is - a관계
- 상속관계
- 하얀 삼각형 화살표 사용
- 예시 : 원 - 도형, 사과 - 과일, 호랑이 - 동물
has - a관계
- 소유관계 (클래스 안의 클래스)
- 검정 삼각형 화살표 사용
- 예시 : 엔진 - 자동차, 카메라 - 스마트폰, 마우스 - 컴퓨터
예시
더보기
----------------------One.java 파일-------------------------
public class One {
private int secret = 1; // 전부 접근 불가
int roommate = 2; // default. 동일 패키지까지만 접근 가능
protected int child = 3; // 동일 패키지 접근 가능, 다른 패키지의 자식 패키지까지 접근 가능
public int anybody = 4; // 전부 접근 가능
}
---------------------One2.java 파일 (is - a 관계) -----------------------
public class One2 extends One{ // is - a 관계 (상속 관계)
void print() {
//System.out.println(secret); //private는 접근 불가
//System.out.println(roommate); // 자식 클래스라도 부모와 다른 패키지에 있으면 default에 접근 불가
System.out.println(child);
System.out.println(anybody);
}
}
---------------------Two.java 파일 (has - a 관계) -----------------------
public class Two {
void print() {
One o = new One(); // has - a 관계 (클래스 안의 클래스)
//System.out.println(o.secret) // 같은 패키지에 잇어도 다른 객체의 private 멤버 접근 불가
System.out.println(o.roommate);
System.out.println(o.child);
System.out.println(o.anybody);
}
}
extends
기본형
//부모 클래스
class SuperClass{ 내용 }
//상속 받는 자식 클래스
class SubClass extends SuperClass { 내용 }
예시
더보기
-----------------------부모 클래스 Ex01Circle.java 파일-------------------------
public class Ex01Circle {
private void secret() { // private : 상속관계에서 호출 불가능.
System.out.println("비밀이다.");
}
protected void findRadius() { // protected : 상속관계에서 호출 가능.
System.out.println("반지름은 10.0센티이다.");
}
public void findArea() { // public : 상속관계에서 호출 가능.
System.out.println("넓이는 ~이다.");
}
}
---------------------자식 클래스 Ex01Ball.java 파일-----------------------
public class Ex01Ball extends Ex01Circle {
private String color;
public Ex01Ball(String color) {
this.color = color;
}
public void findColor (){
System.out.println(color + "색 공");
}
@Override
public void findArea() {
System.out.println("넓이는 4*(~)이다.");
}
public void findVolume() {
System.out.println("부피는 ~이다.");
}
}
---------------------메인 클래스 Ex01Main.java 파일-----------------------
public class Ex01Main {
public static void main(String[] args) {
Ex01Circle c1 = new Ex01Circle();
Ex01Ball c2 = new Ex01Ball("빨강");
System.out.println("원 :");
//c1.secret(); // private라 직접 접근 불가
c1.findRadius();
c1.findArea();
System.out.println("공 : ");
//c2.secret(); // private라 상속 불가
c2.findRadius(); // Circle에게서 상속 받은 메소드
c2.findColor(); // Ball이 새로 생성한 메소드
c2.findArea(); // Circle에게서 상속 받은 메소드
c2.findVolume(); // Ball이 새로 생성한 메소드
}
}
오버라이딩 (Overriding)
특징
- 상속된 메소드와 동일한 이름, 동일한 매개변수를 가지는 메소드를 정의하여 메소드를 덮어씌우는 것
- 부모 클래스로부터 상속 받은 메소드를 자식 클래스에서 재정의하는 것.
- 오버라이딩 할 경우엔, public, default 이런 접근지시자의 범위를 좁혀서는 안 된다.
- 어노테이션 : 오버라이딩 한 코드에는 표시하기 위해서 @Override를 붙이기도 한다. 오버라이딩 문법에 어긋나면 오류를 띄워 알려준다.
- 오버라이딩 불가능한 경우
- private 메소드 : 부모 클래스 전용이라 자식 클래스가 접근할 수 없다.
- static 메소드 : 클래스는 힙 영역에, static은 class method영역에 저장되어, 메모리가 저장되는 위치가 다르다
- final 메소드 : 오버라이드 불가
- 상속 받은 클래스를 자식 클래스의 상황에 맞게 변경해야 할 경우 사용
- 상속받은 부모 클래스 메소드의 기능 변경
- 상속받은 부모 클래스 메서드데 기능 추가
접근지시자 예시
더보기
-----------------------sec 패키지 One.java 파일-------------------------
public class One {
private int secret = 1; // 전부 접근 불가
int roommate = 2; // default. 동일 패키지까지만 접근 가능
protected int child = 3; // 동일 패키지 접근 가능, 다른 패키지의 자식 패키지까지 접근 가능
public int anybody = 4; // 전부 접근 가능
public void show() {} // 추상 클래스
}
---------------------sec 패키지 One1.java 파일-----------------------
public class One1 extends One{
void print() {
//System.out.println(srcret);
System.out.println(roommate);
System.out.println(child);
System.out.println(anybody);
}
//void show() {} // 오버라이딩할 때 접근 범위가 좁아지면 오류 발생.
//오류 발생 안 하려면 부모랑 똑같이 public 붙여줘야 함.
}
어노테이션 @Override 예시
더보기
public class Ex01Ball extends Ex01Circle {
private String color;
public Ex01Ball(String color) { // 생성자
this.color = color;
}
public void findColor (){ // 새로 추가한 메소드
System.out.println(color + "색 공");
}
@Override
public void findArea() { // 상속 받은 메소드 재정의(오버라이드)
System.out.println("넓이는 4*(~)이다.");
}
}
상속 제한
private
- 같은 클래스 내에서만 사용할 수 있도록 규제되기에, 상속 받은 클래스라 하더라도 사용할 수 없다.
final
- 필드, 메소드, 클래스에 붙이면 상속이 제한된다.
- final 변수 : 상수
- final 메소드 : 하위 클래스에서 오버라이딩할 수 없는 메소드.
- final 클래스 : 상속이 불가능한 클래스.
Static
- 인스턴스 메소드나 인스턴스 변수는 heap 영역에 저장되어 런타임 중 상속이 가능하나, 메소드 영역 안에 있는 static 영역은 프로그램 첫 실행 때 먼저 실행되므로 컴파일 때 저장된다.
- static이 붙으면 같은 이름의 메소드를 오버라이드하더라도, 상속이 되지 않고 각자의 메소드로 사용하게 된다.
생성자와 super
특징
- 자식 생성자를 호출하면 부모 생성자를 더 우선으로 호출한다. (자식 클래스로 인스턴스를 만들면 부모 생성자부터 확인한다)
- 부모 클래스에게 매개변수가 있는 생성자만 있을 경우, 자식 클래스의 생성자에 super를 꼭 써줘야 한다.
- 오류 해결법
- 부모 클래스에 디폴트 생성자 넣어주기
- 자식 클래스 생성자에 super 메소드 넣어주기
super
- 부모 클래스의 생성자를 호출하는 메소드.
- 자식 클래스의 생성자 첫 줄에 써야 한다.
예시
더보기
-----------------------부모 클래스 Animal.java 파일-------------------------
class Animal {
public Animal() {
System.out.println("종 : 이름");
}
public Animal(String s) {
System.out.println("동물 : "+s);
}
}
---------------------자식 클래스 Mammal.java 파일-----------------------
class Mammal extends Animal {
public Mammal() {
System.out.println("포유류 : 원숭이");
}
public Mammal(String s) {
super(s);
System.out.println("포유류 : "+s);
}
}
---------------------메인 클래스 Main.java 파일-----------------------
public class Main {
public static void main(String[] args) {
Mammal ape = new Mammal();
Mammal lion = new Mammal("사자");
}
}
타입 변환
타입변환 (casting)
- 자동 타입변환 (auto casting or Promotion)
- 부모 클래스 변수에 자식 객체 타입을 대입하면, 자동으로 타입변환이 일어난다.
- 상속 관계가 아니면 타입 변환이 불가능하다! (어떠한 객체든지 최상위 클래스 Object와는 상속 관계를 맺고 있으니 Object 자료형을 써주면 해결할 수 있다)
- 자동 타입변환 후, 변수는 자식 객체를 참조하고 있지만, 변수로 접근 가능한 클래스 멤버는 부모 클래스 멤버로만 한정된다.
- 단, 메소드가 자식클래스에서 오버라이딩 되었다면, 부모 클래스의 메소드 대신 오버라이딩된 메소드가 호출된다.
- 강제 타입변환 (Casting)
- 부모 타입을 자식 타입으로 변환하는 것
- 가능한 경우는 하나 : 자식 타입이 부모 타입으로 자동 변환 되어 있을 때, 다시 자식 타입으로 변환할 때 사용.
예시
더보기
-----------------------부모 클래스 Person.java 파일-------------------------
public class Person {
String name = "사람";
void whoami() {
System.out.println("나는 사람이다.");
}
-----------------------자식 클래스 Student.java 파일-------------------------
public class Student extends Ex06Person {
int number = 7;
void work() {
System.out.println("나는 공부한다.");
}
}
-----------------------메인 클래스 Main.java 파일-------------------------
public class Main {
public static void main(String[] args) {
Person p;
// p 가 가지고 있는 멤버들은 whoami(), name
Student s = new Student();
// s가 가지고 있는 멤버들은 whoami(), name - 상속 받은 것, work(), number - 본인 것
// 하위 클래스 변수가 상위 클래스 변수로 저장 가능
p = s; // 자동 형변환 (auto casting). p가 부모 클래스라, p에 s를 담으면 자동으로 s의 정보를 담은 Person의 인스턴스가 된다.
//p.number = 1; // p는 Person의 인스턴스이므로, Student의 멤버는 사용하지 못한다.
//p.work(); // p는 Person의 인스턴스이므로, Student의 멤버는 사용하지 못한다.
p.whoami(); // Person의 멤버(메소드)이므로 호출 가능.
Student s1 = (Student) p; //강제 형변환 (casting). Person인 p를 강제로 Student로 형변환해서, p의 정보를 담은 Student인 s1 선언.
s1.number=6; // s1은 Student의 인스턴스라 Student 멤버 사용 가능.
s1.work(); // s1은 Student의 인스턴스라 Student 멤버 사용 가능.
}
}
instanceof
어떤 클래스로 만들어졌는지 확인
고객의 등급 (vip, 일반) 등을 확인할 때 쓰인다.
기본형
변수 instanceof 타입
// 변수 : 객체를 참조하는 변수
// instanceof : boolean 값 반환
// 타입 : 클래스 이름 혹은 인터페이스 이름
예시
더보기
-----------------------부모 클래스 Person.java 파일-------------------------
public class Person {
String name = "사람";
void whoami() {
System.out.println("나는 사람이다.");
}
-----------------------자식 클래스 Student.java 파일-------------------------
public class Student extends Ex06Person {
int number = 7;
void work() {
System.out.println("나는 공부한다.");
}
}
-----------------------메인 클래스 Main.java 파일-------------------------
public class Main {
public static void main(String[] args) {
Ex06Student s = new Student();
Ex06Person p = new Person();
System.out.println(s instanceof Person); // 결과 true.
// Student의 인스턴스도 Person을 상속받았기 때문에, 결국 Person으로 만들어졌다
System.out.println(s instanceof Student); // 결과 true
System.out.println(p instanceof Student); // 결과 false
//System.out.println(s instanceof String); // 불가능.
downcast(s); // Student를 전송
downcast(p); // Person을 전송
}
static void downcast (Person p) { // Person이 부모이기 때문에, Person도 Student도 받을 수 있다. (다형성)
if(p instanceof Student) { // p가 Student로 만들어졌는지?
Student s = (Student) p;
System.out.println("ok. 하향 타입 변환"+s.number);
s.work();
} else {
p.whoami();
}
}
}
언제 쓰느냐
같은 내용을 지닌 채로 전송 시에는 부모 클래스로 형변환해 쉽게 오가다가, 도착 후에는 본인 클래스로 돌아와 정보를 풀어놓는다.
코드가 짧아진다.
-----------------------메인 클래스 MsgSendExam2.java 파일-------------------------
public class MsgSendExam2 {
public static void main(String[] args) {
MsgSend msg[] = {
new EmailSend(),
new SmsSend(),
new FaxSend()};
for(MsgSend m : msg) { // 다형성
m.send(); // 각자 자기 메소드 실행
if(m instanceof EmailSend) {
EmailSend e = (EmailSend) m;
e.createEmail(); // 결과 이건 이메일 거
}
if(m instanceof SmsSend) {
SmsSend s= (SmsSend) m;
s.createSMS(); // 결과 이건 SMS 거
}
}
}
}
-----------------------부모 클래스(추상 클래스) MsgSend.java 파일-------------------------
public class MsgSend {
public void send() {}
}
-----------------------자식 클래스 MsgSend.java 파일-------------------------
public class EmailSend extends MsgSend {
@Override
public void send() {
System.out.println("여기는 Email 전송");
}
public void createEmail() {
System.out.println("이건 이메일 거");
}
}
-----------------------자식 클래스 SmsSend.java 파일-------------------------
public class SmsSend extends MsgSend {
@Override
public void send() {
System.out.println("여기는 SMS 전송");
}
public void createSMS() {
System.out.println("이건 SMS거");
}
}
-----------------------자식 클래스 FaxSend.java 파일-------------------------
public class FaxSend extends MsgSend {
@Override
public void send() {
System.out.println("여기는 Fax 전송");
}
}