객체지향개념 2
상속(Inheritance)
- 기존의 클래스로 새로운 클래스를 작성하는 것(코드의 재사용)
- 두 클래스를 부모와 자식으로 관계를 맺어주는 것
- 자손은 조상(부모와 그위모든애들)의 모든 멤버를 상속받는다
(생성자, 초기화블럭 제외) - 자손의 멤버 개수는 조상보다 같거나 많다.
- 자손의 변경이 조상에 영향을 미치지 않는다.
1 | // Ex7_1.java |
포함 관계
- 클래스의 관계 (2가지)
- 상속
- 포함
포함이란?
- 클래스의 멤버로 참조변수를 선언하는 것
- 작은 단위의 클래스를 만들고, 이들을 조합해서 클래스를 만든다.
- 자료구조가 달라짐 >> 메모리 그림 잘보기
- 아래처럼 재사용성 높이고, 복잡성 줄이기위해
클래스 간의 관계 결정하기
- 상속관계 = 상속관계 A는 B이다
- 포함관계 = A는 B를 가지고있다
- 대부분의 경우 포함관계를 사용
단일 상속 & Object클래스
단일상속
- java는 단일상속만을 허용하다.
- 다중이 필요할 경우, 하나만 상속관계로, 나머지는 포함관계로 처리한다
1 | class TvDVD extends Tv, DVD { // 에러 조상은 하나만 허용된다. |
- 이유는 두 조상에게 모두 void fuction(){A},void fuction(){B} 가있다면 어느것을 상속할것인지? >> 충돌위험 높음
Object 클래스 - 모든 클래스의 조상
- 부모가 없는 클래스는 자동적으로 Object 클래스를 상속받게 된다
- 모든 클래스는 Object클래스에 정의된 11개의 메서드를 상속받는다
toString(), equals(Object obj), hashCode() 등등
1 | class Tv { // 부모가 없는 클래스는 |
- println에 참조변수가 들어오면 참조변수.toString()을 호출
1 | class Tv2 { |
오버라이딩
오버라이딩(overriding) = 메서드 오버라이딩(덮어쓰다)
- 상속받은 조상의 메서드를 자신에 맞게 변경하는 것
- 메서드 선언문(반환타입 함수이름) 못바꿈, 내용만 변경가능
(오버라이딩 + 생성자 활용) 간결한코딩(!!!)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import java.util.*;
class MyPoint3 extends Object {
int x;
int y;
MyPoint3(int x, int y){
this.x =x;
this.y = y;
}
//Object 클래스의 toString()을 오버라이딩
public String toString() {
return 'x'+x+",y"+y;
}
}
public class vesta14 {
public static void main(String[] args) {
MyPoint3 p = new MyPoint3(5,3); // p.x , p.y초기화 코드없이 바로 생성자이
System.out.println(p); //print(참조변수)호출시 참조변수.toString()를 오버라이딩해놓
}
}
오버라이딩의 조건(!!!)
1. 선언부가 조상 클래스의 메서드와 일치해야한다
- 선언부(반환 타입, 메서드 이름, 매개변수 목록) 일치!!!!
2. 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수없다.
- public, protected, (default), private
3. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.
오버로딩 vs 오버라이딩
- 오버로딩
이름이 같은 기존에 없는 새로운 메서드를 정의하는 것(new) - 오버라이딩
상속받은 메서드의 내용을 변경하는 것(change, modify)
참조변수 super, 생성자 super()
참조변수 super
- 객체 자신을 가르키는 참조변수. 인스턴스 메서드,생성자 내에만 존재
- 조상의 멤버를 자신의 멤버와 구별할 때 사용
- static 메서드내에서 사용불가(당연히 this가 못썻던이유와같음)
참조변수 this와유사
this는 iv, lv를 구별하기 위해 사용됨
이름이 겹쳐도 상속됨.( super, this로 구별됨)
변수 x 만 쓴다면 당연히 가까운순이므로 this.x 값이다
만약 변수명과 겹치는것이 상속받은 클래스에 정의가 없으면
x==this.x==super.x이때는 참조변수 this, super 가르키는 주소 모두 같은 객체주소가짐
super() - 조상의 생성자
- 상속시 생성자, 초기화 블록은 상속안됨!!
- 필요하다면, 조상의 생성자를 호출할때 사용
- 조상의 멤버는 조상의 생성자를 호출해서 초기화
생성자 this()와 유사
- x,y는 조상의 멤버 임으로 super(x,y)를 통해 조상의 생성자를 호출이 마땅하다.
super() - 조상의 생성자(2)(!!!)
- 생성자의 첫줄의 반드시 생성자를 호출해야 한다.(!!!)
- 그렇지 않으면 컴파일러가 생성자의 첫 줄에 super();를 삽입한다. (에러이유)
- 많이 하는 실수 반드시 이유 찾아보기
- 이유는 Point3D 생성자 명령문의 첫줄에는 super();가 들어가며, 이를 수행시 Point에 기본 생성자
Point() {}
를 호출한다. 하지만 Point 클래스의 생성자가 이미 있었기 때문에 기본생성자가 기본적으로 만들어지지 않으므로, 에러가 나온다.
- 항상 클래스 만들때, 기본 생성자 항상 무조건 반드시 넣어주자
(다른 생성자 선언 있다면, 자동으로 안들어가므로) - 해결방법
- Point 클래스에 기본 생성자 넣어주기
- Point3D 클래스 생성자에서 this.x, this.y 대신
상속 생성자에게 생성하게 시키는 super(x,y) 로 교체
패키지
- 서로 관련된 클래스의 묶음( java9 약 400개 클래스)
- 클래스는 클래스 파일(*.class), 패키지는 폴더. 하위 패키지는 하위 폴더 (파일전까지,,폴더로구성)
- 클래스의 실제 이름(full name)은 패키지를 포함
(java.lang.String) - rt.jar는 클래스들을 압축한 파일(java9부터는 module개념으로 rt.jar없다.)
패키지의 선언
- 패키지는 소스파일의 첫 번째 문장으로 단 한번 선언
- 같은 소스 파일의 클래스들은 모두 같은 패키지에 속하게된다.
- 패키지 선언이없으면, 이름없는(unnamed)패키지에 속하게된다. (default package)
1 | package ~~~~~; |
클래스 패스(classpath)
- 클래스 파일(*.class)의 위치를 알려주는 경로(path)
- 환경변수 classpath로 관리하며, 경로간의 구분자는 ;로 사용
classpath(환경변수)에 패키지의 루트를 등록해줘야함
import문 & static import문
- 클래스를 사용할 때 패키지이름을 생략할 수 있다.
- 컴파일러에게 클래스가 속한 패키지를 알려준다.
- java.lang패키지의 클래스는 import하지 않고도 사용가능하다
(String, Object, System, Thread)
import 문의 선언
- import문을 선언하는 방법은 다음과 같다.
- import문은 컴파일 시에 처리되므로 프로그램의 성능에 영향없다.
static import 문
- static 멤버를 사용할 때 클래스 이름을 생략할수있게해준다.
제어자(modifier)
- 클래스와 클래스의 멤버(멤버 변수, 메서드)에 부가적인 의미 부여
- 접근제어자는 4개중 1개만 붙일수있다
- 보통 접근 제어자를 가장 왼쪽에 씀
static - 클래스의, 공통적인
- iv, im 사용불가
final - 마지막의, 변경될수 없는
- 제어자 사용시 대상도 구별해야함
abstract - 추상의, 미완성의
- 추상화에서 배움
- 미완성을 의미하며, 구현부없는 메서드(=추상 메서드) 앞에 붙여아함
- 추상 메서드를 포함한 클래스(=추상 클래스) 앞에 붙여야함
- 미완성이므로 추상 클래스의 인스턴스 생성 불가
- 따라서 추상 클래스는 무조건 상속받아서 추상 메서드를 완성해야 쓸수있다!
완전한 클래스 만든 후 객체 생성가능
접근 제어자(access modifier)
- 하나의 대상에 접근 제어자 4개중 1개만 사용가능
- 접근 제어자 없는경우 자동 default
- 클래스에는 public, default가능
- 주의할점은 클래스 파일하나에 public은 하나만 존재!!
따라서 public변경하고, 파일이름도 public 클래스 이름과 같아야함
- 주의할점은 클래스 파일하나에 public은 하나만 존재!!
- 멤버들에게는 4개모두 가능
- 다른패키지에서 사용시 private, default 멤버는 에러 / protected public 멤버는 사용성공
- 다른패키지에 자손도 아니라면 public멤버만 사용성공
- 컴파일후 폴더+파일 기준으로 보면
캡슐화와 접근 제어자(!!!)
- 접근 제어자를 사용하는 이유
외부로부터 데이터를 보호하기 위해서 - 외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해서
예시(데이터보호)
- 아래와 같이 직접 접근 막고, 간접접근 하도록 설계
- setHour(25)는 결국 23을 넘기 때문에, hour 변수값이 변하지 않는다.
(범위에 값이 항상 들어가게 되므로, 값보호)
1 | // 내부 접근은 함수를 통해 하도록 변경하면, 캡슐화 가능 |
다형성(polymorphism)(!!!)
- 조상 타입 참조 변수로 자손 타입 객체를 다루는 것
- 여러 가지 형태를 가질 수 있는 능력
- 추상클래스, 인터페이스 등 이해하는데 기초
- 타입 불일치 하여도 다룰수있음..
(보통 참조변수 타입과 객체 타입은 일치해야함)
타입일치와 불일치 차이
- 참조 변수와 객체 타입일치시 모든 멤버 사용가능
- 참조 변수와 객체 타입 불일치시 조상에서 정의된 멤버만 사용가능
- 참조변수로 사용할수있는 멤버의 갯수가달라짐
즉 리모콘(참조변수) 버튼수!!라고 생각하자.
- 반대의 경우인 자손 타입의 참조변수로 조상 타입의 객체를 가르킬수없다.(에러)
- 리모콘 기능 수보다 멤버가 많으면안돼!!! 없는것 호출해서 에러가뜸
참조변수의 형변환
참조변수의 형변환(!!!)
결론: 사용할 수 있는 멤버의 갯수를 조절하는 것 (값, 주소, 객체 안달라서 딱 갯수만달라짐)
조상, 자손 관계의 참조변수는 서로 형변환 가능 (부모자식간만됨)
자손타입 ← 조상타입. 형변환 생략 불가
자손타입 → 조상타입. 형변환 생략 가능
항상 참조변수 타입간 관계가 맞으면 컴파일러는 OK
하지만, 실제 인스턴스가 가지고있는 갯수를 넘는 참조변수타입은 에러뜸!(컴퍼일러는 oK함)예시를 확인해보자
- 객체는 그대로 이지만, 참조변수(리모콘)의 타입을 변경하므로, 사용가능한 멤버갯수를 변경가능하다.
Car c = (Car) f
>> 참조 변수 c가 인스턴스 주소를 가르킨다. (쓸수있는 멤버함수 4개)FireEngin f2 = (FireEngine)c;
>> 참조 변수 c가 가르키던 인스턴스 주소를 f2도 가르킨다. 쓸수있는 멤버함수 5개
instanceof 연산자
- 참조변수의 형변환 가능여부 확인에 사용, 가능하면 true반환
- 형변환 전에 반드시 instanceof로 확인해야함.(자식-조상만 가능)
- 자기자신, 조상들은 ture >> (같거나 범위줄어드는) 형변환가능 알려줌
매개변수의 다형성(다형성 장점1)
- 참조형 매개변수는 메서드 호출시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다.
- 참조형 매개변수는 오버로딩을 통해 일일히 함수를 정의 내려줄필요없어진다.
- 매개변수로 Product p를 하면, Tv,Computer, Audio타입인 자식관계 인스턴스가 들어와도, 다형성 특성에 의해 모두 한 함수로 처리가능하다.
1 | class Product { |
여러 종류의 객체를 배열로 다루기(다형성 장점2)
- 조상타입의 배열에 자손들의 객체를 담을 수있다.
이렇게 조상타입의 배열에는 자손들의 타입이 달라도 저장가능하다(다형성가능)
만약 Object로 이뤄진 배열(Vector)이있다면 모두의 조상이기에 모든 종류의 객체저장가능
예시
1 | class Product2 { |
추상 클래스, 추상 메서드
추상 클래스(abstract class)
- 미완성 설계도 == 미완성 메서드를 갖고있는 클래스
- 추상 클래스 == 추상 메서드(몸통이없는 메서드)를 갖고있는 클래스
- 반드시 abstract 붙여야함
- 다른 클래스 작성에 도움을 주기 위한것 (상속후, 추상메서드 구현완성!!후 객체 생성 목적)
- 인스턴스 생성 불가
- 상속후 구현완료되면 abstract없는것을 확인가능
- AudioPlayer또한 완성된 설계도임 >> 인스턴트 생성 가능해짐
- 만약
Player ap = new AudioPlayer()
한다면 조상 참조변수로 가능하며, 오버라이딩되었으므로 참조변수(리모콘) 으로 play(), stop() 호출시 정상 동작 가능
추상 메서드(abstract method)
- 미완성 메서드. 구현부(몸통, { })가 없는 메서드
- 꼭 필요하지만 자손마다 다르게 구현될 것으로 예상되는 경우
- 상속받은후 모두 구현완료해야하지만, 1개만 구현한다면 abstract필요
- abstract로 구현해놓으면 반드시 기능을 만들고 써야함을 강제할수있다.
- 객체 생성이 불가함에 따라 추상 메서드 인스턴스 메서드에서 호출도가능(어차피 선언문만있고, 구현완료해야 되니까)
1 | abstract class Player { |
추상클래스의 작성
- 여러 클래스에 공통적으로 사용될 수 있는 추상클래스르 바로 작성하거나
- 기존클래스의 공통 부분을 뽑아서 추상 클래스를 만든다.
- 리모콘이 조상것이라도, 리모콘이 가르키는것의 실제 인스턴스의 함수가 동작한다.
- 조상이 같다면, 하나의 배열에 다양한 객체를 담아 처리할수있다.(다형성)
- 조상에 abstract로 반드시 넣어야하는것들을 넣어놓음
- 이처럼 담아낼수있지만, move()가 각 종속 객체에 정의가 되어있지않아 에러다. 따라서 abstract로 정해놔야한다!!그래야 종속해서 만들때 move()도 만든테니까
- 반드시 아래 코드 쳐보기
1 | public class Ex7_10 { |
인터페이스의 선언, 상속, 구현
- 결론: 추상 메서드의 집합
- 구현된 것이 전혀 없는 설계도. 껍데기(모든 멤버가 public, abstract)
추상클래스와 인터페이스의 차이
추상클래스=일반클래스인데 추상메서드를 가짐((iv, 생성자등 있음)
인터페이스= 추상 메서드로만 구성됨 (iv, 생성자등 없음)(=원칙)
- 항상 public , 항상 static, 항상 abstract 그래서 항상 생략 가능
인터페이스의 상속
- 인터페이스의 조상은 인터페이스만 가능(Object가 최고 조상 아님)
- extends로 조상 인터페이스 상속받음
- 다중 상속이 가능함
(추상메서드는 충돌해도 문제없음)
(어차피 같은 이름 메서드의 구현부없음)
인터페이스의 구현
- 인터페이스에 정의된 추상 메서드를 완성하는 것
추상 클래스도 결국 extends로 받아서 구현모두해야 완성(매우 유사) - 클래스에 인터페이스를 implements로 받아서 구현모두해야 완성
- 클래스에서 한개만 구현했을경우 abstract를 클래스앞에 붙여야함
- interface의 함수는 모두 public, abstract 생략되었다
- class 클래스명 implements 인터페이스명 으로 받아서 미완성 메서드 모두 구현하면 이를 인터페이스 구현이라고함!
인터페이스 Q&A
- 미완성 설계도를 완성하는 것이 결국 구현
- (핵심!!)iv차이
인터페이스를 이용한 다형성
- 인터페이스도 구현 클래스의 부모!! 비슷하다
- 즉, 진짜 부모(조상클래스)는 상속(extends),
인터페이스도 상속(implements) 동시 가능 - 인터페이스, 조상클래스 똑같이 다형성 특성으로, 조상참조변수(리모콘) 사용가능
매개변수로 인터페이스로 지정 의미
- 예시에서 Fightable 참조변수로 활용시 정의되어있는 함수2개밖에사용못함
- 매개변수가 Fightable f로 되어있는것의 의미는 Fightable인터페이스로 구현한 클래스의 인스턴스만 가능 의미 (!!!)
반환타입을 인터페이스로 지정 의미
- 반환타입이 Fightable의미는 Fightable인터페이스를 구현한 클래스의 인스턴스를 반환한다는 의미(!!!)
- 형변환가능한 Fighter를 반환됬으니까.. 가능함
1 | abstract class Unit12 { |
인터페이스 구조가 변경유리
- 두 대상(객체)간의 ‘연결, 대화, 소통’을 돕는 ‘중간 역할’을 한다
- 기계의 껍데기 == 인터페이스(인간↔기계 사이존재)
- 선언(설계)와 구현을 분리시킬 수있게 한다.
- 선언만 때어내기!!!!!!
- 즉, A가 B에 의존할때 B의 껍데기(인터페이스)는 그대로 놧두고 알맹이(B의 구현내용)을 변경할수있다. (A입장에서는 변경할것없다)
- A 클래스가 인터페이스에 의존하면 간접적으로 B와 소통!!
- B 변경시 A클래스 변경 필요없다! 매우 중요!!
인터페이스의 장점
개발 시간을 단축할 수 있다.
A와 B가 따로 작업가능(wait 불필요)변경에 유리한 설계가 가능하다
A는 가만히 B만 알맹이 바꿀수있다.표준화가 가능하다
JDBC처럼 사용하는 쪽이나 구현하는 쪽에서 인터페이스를 지키게함서로 관계없는 클래스들을 관계를 맺어줄 수 있다
- 이처럼 클래스 상속(상속계층)과 다른 특성을 가진 애들의 관계를 맺어줄수있다.
인터페이스를 중복으로 상속가능하므로 다양한 관계 가능
- 이처럼 클래스 상속(상속계층)과 다른 특성을 가진 애들의 관계를 맺어줄수있다.
인터페이스의 디폴트 메서드와 static메서드
- 인터페이스에 디폴트 메서드, static메서드 추가 가능( JDK1.8부터)
- 인터페이스에 새로운 메서드(추상 메서드)를 추가하기 어려움
- 이 인터페이스를 사용하는 모든 클래스에 새로운 메서드를 다 구현해야됨
문제
- 이 인터페이스를 사용하는 모든 클래스에 새로운 메서드를 다 구현해야됨
- 디폴트 메서드는 인스턴스 메서드(인터페이스 원칙 위반)
(몸통이있는 메서드를 추가할수있게 해줌) - 디폴트 메서드가 기존의 메서드와 충돌할 떄의 해결책 (몸통이있으니까 충돌됨..)
- 해결책(충돌나면 일단 오버라이딩한다 생각하자.조상클래스가 우선됨)
내부 클래스(inner class)
- 클래스 안의 클래스
(바깥클래스에서 쓸 맴버를 만드는것!목적)
- 내부 클래스 장점
- 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할수있다
(외부 클래스의 멤버인 내부 클래스이므로 거의 내부에서만 쓰인다) - 코드의 복잡성을 줄일 수있다.
(캡슐화)(완전 외부에서는 classB접근 거의불가)
- 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할수있다
1 | class AAA { ///AAA는 BBB의 외부 클래스 |
내부 클래스의 종류와 특징
- 내부 클래스의 종류와 유효범위(scope)는 변수와 동일
내부 클래스의 제어자와 접근성
- 내부 클래스의 제어자는 변수에 사용 가능한 제어자와 동일
- 원래 클래스는 deflaut와 public 밖에없지만, 내부클래스는 4종류 제어자 모두 사용가능
static클래스만 static멤버를 정의할수있다
따라서 static이 필요할경우 클래스도 static이여야함
또한 static 내부 클래스에서는 외부 클래스의 인스턴스 멤버에 접근할수없다.(당연히 static 메서드에서 iv참조못하는것과비슷)final static 상수는 3종류의 내부클래스 모두 사용가능
final static같은 경우는 클래스명.변수이름 으로 접근가능예제2
인스턴스멤버는 인스턴스 멤버와 static맴버사용가능
static멤버는 인스턴스멤버사용불가private 핵심 사용가능.(private는 클래스안에서만사용가능)
- 내부 클래스는 외부 클래스의 private 멤버 접근 가능하다
- 내부 지역 클래스는 해당 메서의 지역변수는 final이 붙은 변수(상수)만 접근가능하다. (그냥iv는 먼저 메서드종료로사라질수있다)
(JDK1.8부터는 값변경안되면 final생략취급)
1 | class Outer { |
- 완전 외부에서 내부클래스 사용방법
- 접근방법
익명 클래스(anonymous class)
- 이름이 없는 일회용 클래스. 정의와 생성을 동시에
- 이름도 없고 바로 객체 생성해야하며, 조상클래스이름 또는 구현인터페이스이름 쓰면됨