자바의 정석 - CH7 객체지향 언어 2 - 1
1. 상속
- 기존 클래스로 새로운 클래스를 작성하는 것 (코드의 재사용)
- 두 클래스를 부모와 자식으로 관계를 맺어주는 것.
- 자손은 조상의 모든 멤버를 상속받는다. (생성자, 초기화블럭 제외)
- 자손의 멤버 개수는 조상보다 적을 수 없다.(같거나 많다)
class Parent{}
class Child extends Parent{
// ...
} 에서 Child와 Parent는 상속관계이고, Parent가 부모이다.
- 자손의 변경은 조상에 영향을 끼치지 않는다.
Parent는 멤버 1개 Child는 멤버 2개 (상속받았기 때문) ... > 점점 확장해나가는 것 그래서 extends!
1- 2. 포함관계
포함 : 클래스의 멤버로 참조변수를 선언하는 것.
- 작은 단위의 클래스를 만들고, 이 들을 조합해서 클래스를 만든다.
circle이 클래스 point를 참조변수로 선언한 것.
=> 상속이나 포함이나 저장공간의 개수는 똑같다. 위치가 다를 뿐!
상속관계 : ~는 ~이다.(is-a)
포함관계 : ~는 ~을 가지고 있다. (has-a) (대부분의 경우 90%가 포함이다.)
1- 3. 단일상속
- Java는 단일 상속만을 허용한다.
- 비중이 높은 클래스 하나만 상속관계로, 나머지는 포함관계로 한다.
비중이 높은 Tv를 상속관계로 하고 DVD가 가진 메소드를 작성한다.
껍데기만 만들고 객체를 사용할 수 있도록 코드를 만든다. (DVD가 만든 메서드를 호출하는 것)
=> 이렇게 하면 다중상속의 효과를 낼 수 있다.
1- 4. Object 클래스
- 부모가 없는 클래스는 자동적으로 Object클래스를 상속받게 된다.(컴파일러가 자동 추가)
- Object는 최고 조상!
- 모든 클래스는 Object클래스에 정의된 11개의 메서드를 상속받는다.
Ex) toString() / /객체의 주소값 반환, equals(Object obj) // Object obj와 같은지 판, hashCode(), ...
2. (메서드)오버라이딩
Override : 덮어쓰다.
- 상속받은 조상의 메서드를 자신에 맞게 변경하는 것
- 선언문은 변경 불가. 내용만 변경 가능
Object 클래스의 toString()을 오버라이딩! (public은 toString가 원래 public이라서 쓴 것)
주석처리 된 부분은 생성자를 따로 만들지 않았을 때.
생성자를 만들고 난 이후로 코드가 2줄로 간결하게 줄은 것을 확인할 수 있다.
(this은 위에 선언문 x, y을 가르킴)
2-2 오버라이딩 조건
1. 선언부가 조상 클래스의 메서드와 일치해야한다.(이름, 매개변수, 리턴타입)
2. 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
3. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.
2-3 오버로딩 vs 오버라이딩
오버로딩 : 이름이 같은 기존에 없는 새로운 메서드를 정의하는 것(new)
오버라이딩 : 상속받은 메서드의 내용을 변경하는 것(change, modify)
2-4 참조변수 super (조상멤버, 자신멤버 구별할 때 사용)
- this와 비슷하다. (지역변수 lv와 인스턴스변수 iv를 구분할 때 사용)
- 객체 자신을 가리키는 참조변수. 인스턴스 메서드(생성자)내에서만 존재
- 조상의 멤버를 자신의 멤버와 구별할 때 사용
위처럼 조상멤버와 자신멤버에 x가 같이 있는 경우 구별해야하기 때문에 super을 사용!
=> 이름이 같아도 상속은 된다.
조상멤버의 x // super.x
자신멤버의 x // this.x -> 아래 sysout에서 x와 가깝기 때문에 가까운 쪽이 this!
=> 중복되지 않는 경우엔 this.와 super.이 같은 것을 가리킨다.
+super, this 둘 다 참조변수임!
2-5 super() - 조상의 생성자
참조변수 super와 다름!
- 조상의 생성자를 호출할 때 사용.
- 조상의 멤버는 조상의 생성자를 호출해서 초기화.
Point3D에서 선언한 것은 int z;이기 때문에 그것만 초기화할 수 있다.
즉, 조상의 멤버인 x, y를 초기화할 순 없다.
=> 그리하여 조상의 멤버를 직접 초기화하지 않고 조상의 생성자가 조상의 멤버를 초기화할 수 있도록
super()로 호출하는 것!
- 생성자 첫 줄에 반드시 생성자를 호출해야한다.
그렇지 않으면 컴파일러가 생성자(super or this)의 첫 줄에 super();을 삽입한다.
+ super()을 호출했다는 것은 object를 호출했다는 것!
19-21번째 줄 : 컴파일 에러 => 생성자가 없다!
자동으로 super();이 생성되어 point()를 호출하게 되는데 point에는 기본 생성자인 point()가 없고 point(int x, int y)만 있다.
+ 생성자가 하나 이미 있기 때문에 컴파일러가 넣어주지 않는 것.
=> 그래서 항상 class를 만들 때 기본 생성자를 만드는 것은 필수이다.
조상의 멤버는 자식이 직접 초기화할 수 없으므로 생성자를 생성하여 조상이 초기화할 수 있도록 해야한다.
2-6 패키지
: 서로 관련된 클래스의 묶음 (모든 class는 패키지 안에 들어있어야한다.)
- 클래스는 클래스파일(*.class), 패키지는 폴더. 하위 패키지는 하위 폴더
- 클래스의 실제 이름(full name)은 패키지를 포함(java.lang.String <- String의 실제 이름)
- 패키지는 소스파일의 첫 번째 문장으로 단 한번 선언
- 같은 소스 파일의 클래스들은 모두 같은 패키지에 속하게 된다. (하나의 소스파일에 단 하나의 public 클래스만 허용한다.)
- 모든 클래스는 하나의 패키지에 속하며 패키지가 선언되지 않은 클래스는 자동 이름없는(unnamed) 패키지에 속한다
패키지 선언이 안되면 default package가 되는 것!
2-6 클래스 패스
- 클래스 파일의 위치를 알려주는 경로
- 환경변수 classpath로 관리하며, 경로간의 구분자는 ;을 사용
- classpath(환경변수)에 패키지 루트를 등록해야함
클래스패스를 지정해놓으면 cmd창에서 바로 결과값을 출력가능하다.
클래스패스가 지정되어있지 않으면 파일위치를 입력한 후, 클래스 이름을 적어야한다.
2-7 import문(Ctrl + Shift + O)
- 클래스를 사용할 때 패키지 이름을 생략할 수 있다.
- 컴파일러에게 클래스가 속한 패키지를 알려준다.
- java.lang패키지의 클래스는 import하지 않고도 사용할 수 있다.
Ex)String, Object, System, Thread ... => 원래는 import java.lang.*을 선언했어야한다.
- import문은 패키지문과 클래스 선언 사이에 선언한다.
- Static import을 사용하면 클래스 이름을 생략할 수 있게 해준다.
=> 클래스 이름을 붙이면 코드가 너무 길어지기 때문에 생략하는 것, 꼭 필요할때만 사용하자.