Don't give up!

[Java] 자바의 정석 정리(7장) 본문

개발서적/JAVA의 정석

[Java] 자바의 정석 정리(7장)

Heang Lee 2021. 5. 15. 01:55
자바의 정석을 읽고 정리한 내용입니다.

Java의 정석 - YES24

 

Java의 정석

최근 7년동안 자바 분야의 베스트 셀러 1위를 지켜온 `자바의 정석`의 최신판. 저자가 카페에서 12년간 직접 독자들에게 답변을 해오면서 초보자가 어려워하는 부분을 잘 파악하고 쓴 책. 뿐만 아

www.yes24.com


1. 클래스의 재사용

(1) 상속(Inherit) - 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것.

(2) 생성자와 초기화 블럭은 상속되지 않는다. 멤버만 상속된다.

(3) 자손 클래스의 인스턴스를 생성하면 조상 클래스의 멤버와 자손 클래스의 멤버가 합쳐진 하나의 인스턴스로 생성된다.

(4) 자바에서는 오직 단일 상속만을 허용한다. 

(5) 포함(Composite) - 클래스의 멤버 변수로 다른 클래스 타입의 참조변수를 선언하는 것.

(6) 상속 받지 않는 클래스 선언시 컴파일러는 자동적으로 'extends Object'를 추가하여 클래스가 Object 클래스로부터 상속받도록 한다.

(7) toString함수, equals함수 등의 메서드들을 정의하지 않아도 Object 클래스에 정의되어 있기 때문에 사용할 수 있다.

2. 오버라이딩

(1) 조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것.

(2) 오버라이딩하는 메서드는 조상 클래스의 메서드와 이름이 같아야하고, 매개변수가 같아야 하고, 반환 타입이 같아야한다. (접근 제어자, 예외는 조건을 만족했을 때에만 변경 가능)

(3) 오버라이딩할 때 조상 클래스의 메서드보다 접근 제어자를 좁은 범위로 변경할 수 없고, 예외를 더 많이 선언할 수 없고, 메서드의 종류(static, instance)를 변경할 수 없다.

3. 패키지

(1) 패키지 - 서로 관련된 클래스들을 효율적으로 관리하기 위해 그룹 단위로 묶어 놓은 것.

(2) 같은 이름의 클래스라도 서로 다른 패키지에 존재할 수 있다.

(3) 소스파일을 작성할 때 패키지를 선언하지 않는다면 '이름없는 패키지(unnamed package)'에 속하게 된다. 패키지를 지정하지 않는 모든 클래스들은 같은 패키지에 포함된다.

(4) import문은 '패키지명.클래스명'으로 선언하는데, 같은 패키지에서 여러 클래스가 사용될 때 '패키지명.*'으로 패키지에 속하는 모든 클래스를 import할 수 있다. 컴파일러가 해당 패키지에서 일치하는 클래스의 이름을 찾아야하지만 실행 시 성능상의 차이는 없고 어느 클래스가 어느 패키지에 속하는지 구별하기 어렵다는 단점은 있다. (*은 클래스명을 대신할 뿐 패키지명을 대신하지 않는다.)

(5) import를 하지 않았다면 사용하려는 패키지의 클래스명을 'java.패키지명.클래스명'으로 일일이 적어줘야함.

(6) 모든 소스파일에는 'java.lang.*'의 import문이 묵시적으로 선언되어 있음.

(7) static import는 패키지 클래스의 정적 멤버를 선언함으로서 static 멤버를 호출할 때 클래스 이름을 생략할 수 있게 해준다. 'static import java.패키지명.클래스명.정적멤버명' 으로 선언한다.

4. 제어자

(1) 접근 제어자(public, protected, default, private)는 하나만 선택해서 사용할 수 있다.

(2) default 제어자는 같은 패키지 내에서만 접근이 가능하도록 한다. 접근 제어자가 지정되어 있지 않다면 default임을 의미.

(3) 인스턴스 멤버를 사용하지 않는 메서드는 static 메서드로 선언하는 것이 더 편리하고 빠르다.

(4) final 제어자는 변수를 상수로 만들고 메서드의 오버라이딩을 막는다. final 클래스는 다른 클래스의 조상이 될 수 없다. 

(5) abstract 제어자는 인스턴스를 생성하지 못하게 한다. 상속 받은 클래스가 일부 원하는 메소드만 오버라이딩 할 수 있다는 장점이 있다.

(6) 메서드에 static과 abstract는 함께 사용할 수 없다.

(7) 클래스에 abstract와 final을 동시에 사용할 수 없다.

(8) abstract메서드의 접근 제어자는 private을 사용할 수 없다.

(9) 메서드에 private과 final은 같이 사용할 필요가 없다.(private만으로도 메서드는 오버라이딩될 수 없다.)

5. 다형성

(1) 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조할 수 있도록 하는 것.

(2) 상속관계의 클래스끼리 형변환이 가능하다. 자손 타입에서 조상 타입으로의 형변환(Up-Casting)은 생략가능하지만 조상 타입에서 자손 타입으로의 형변환(Down-Casting)은 생략불가능하다.

(3) 형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것이 아니므로 인스턴스에 아무런 영향을 미치지 않는다. 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위를 조절하는 것 뿐이다.

(4) 자손 참조변수에 조상 인스턴스를 형변환하여 사용하는 것은 허용되지 않는다.(컴파일 시에는 문제가 없지만 실행 시 에러 발생.)

(5) instanceof연산자는 참조 변수에 참조되어 있는 인스턴스가 될 수 있는 타입을 확인할 수 있다.

(6) 조상 클래스 타입의 매개변수, 배열을 사용하여 상속 받는 클래스들을 묶어 사용함으로서 코드를 간단히 할 수 있다.

6. 인터페이스

(1) 인터페이스는 일종의 추상클래스.

(2) 인터페이스는 오직 추상 메서드와 상수만을 멤버로 가질 수 있다.

(3) 인터페이스의 모든 멤버변수는 public static final, 메서드는 public abstract이 기본값이다.(생략 가능, 컴파일 시에 컴파일러가 자동적으로 추가해준다.)

(4) 인터페이스는 인터페이스로부터만 상속받을 수 있다. 클래스와 달리 다중상속이 가능하다.

(5) 추상클래스처럼 그 자체로는 인스턴스를 생성할 수 없다. 클래스에 implements를 사용하여 클래스에 인터페이스의 함수들을 구현할 수 있음.

(6) 인터페이스를 구현하는 클래스는 인터페이스의 모든 메서드를 구현해야 한다.(그렇지 않으면 abstract로 추상화해야한다.)

(7) 인터페이스의 이름을 '~able'로 작성한다면 인터페이스임을 쉽게 추측할 수 있다.

(8) 인터페이스의 메소드를 구현하고자 할 때 public을 붙여야 한다.(public abstract를 상속받은 것이기 때문.)

(9) 인터페이스는 메서드의 매개변수의 타입으로 사용될 수 있고 클래스 메서드의 리턴 타입으로 인터페이스의 타입을 지정하는 것도 가능하다.

(10) 리턴타입이 인터페이스라는 것은 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 것을 의미한다.

(11) 인터페이스의 장점 : 개발시간을 단축시킬 수 있다, 표준화가 가능하다, 서로 관계없는 클래스들에게 관계를 맺어줄 수 있다, 독립적인 프로그래밍이 가능하다.

 

package Test;
class Test{
	public static void main(String[] args){
    	Tank tank = new Tank();
        Dropship dropship = new Dropship();
        Marine marine = new Marine();
        SCV scv = new SCV();
        
        scv.repair(tank);	//OK
        scv.repair(dropship);	//OK
//	scv.repair(marine);	//ERROR!
    }
}
interface Repairable {}
class Unit{
	int hitPoint;
	final int MAX_HP;
    Unit(int hp){
    	MAX_HP = hp;
    }
}
class Tank extends Unit implements Repairable{
	Tank(){
    	super(150);
        hitPoint = MAX_HP;
    }
}
class Dropship extends Unit implements Repairable{
	Dropship(){
    	super(125);
        hitPoint = MAX_HP;
    }
}
class Marine extends Unit{
	Marine(){
    	super(40);
        hitPoint = MAX_HP;
    }
}
class SCV extends Unit implements Repairable {
	SCV(){
    	super(60);
        hitPoint = MAX_HP;
    }
	void repair(Repairable r){
    		if(r instanceof Unit){
        	Unit u = (Unit) r;
            while(u.hitPoint!=u.MAX_HP){
            	u.hitpoint++;
            }
        }
    }
}

7. 내부 클래스

(1) 내부 클래스로 선언하면 두 클래스의 멤버들 간에 서로 쉽게 접근할 수 있고 외부에 불필요한 클래스를 감춤으로서 코드의 복잡성을 줄일 수 있다.

(2) 

내부 클래스 타입 특징
instance class 외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용
static class 외부 클래스의 static 멤버, static 메서드에서 사용
local class 선언된 영역 내부에서만 사용가능
anonymous class 클래스의 선언과 객체의 생성을 동시에 하는 일회용 클래스

(3) 내부 클래스도 클래스이기 때문에 제어자를 사용할 수 있다.

(4) 내부 클래스에서 static을 사용해야 한다면 static class로 선언해야 한다.(final static 변수는 상수이므로 예외)

(5) 내부 클래스와 외부 클래스에 선언된 변수의 이름이 같을 때 변수 앞에 'this' 또는 '외부 클래스명.this'로 구별할 수 있다.

(6) 익명 클래스는 이름이 없기 때문에 생성자도 가질 수 없으며 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만을 구현할 수 있다.