출처 : 자바의 정석(남궁성 저)
7 객체지향 프로그래밍
ch 1. 상속
상속이란, 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다. 상속을 통해서 클래스를 작성하면 보다 적은 양의 코드로
새로운 클래스를 작성할 수 있고 코드를 공통적으로 관리할 수 있기 때문에 코드의 추가 및 변경이 매우 용이하다.
이러한 특징은 코드의 재사용성을 높이고 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여한다. 자바에서 상속을 구현하는
방법은 아주 간단하다. 새로 작성하고자 하는 클래스의 이름 뒤에 상속받고자 하는 클래스의 이름을 'extends'와 함꼐 써주기만 하면 된다.
class Parent{}
class Child extends Parent{}
- 자손클래스는 조상클래스의 모든 멤버를 상속받는다.(단, 생성자와 초기화 블럭은 상속되지 않는다.)
- 자손 클래스의 멤버 갯수는 조상 클래스보다 항상 같거나 많다.
ch 2. 상속예제
ch 3. 클래스 간의 관계 - 포함관계
상속 이외에도 클래스를 재사용하는 또 다른 방법이 있는데, 그것은 클래스 간에 포함(composite)관계를 맺어주는 것이다. 클래스 간의 포함관계를
맺어 주는 것은 한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언하는 것을 뜻한다. 하나의 거대한 클래스를 작성하는 것보다 단위별로
여러 개의 클래스를 작성한 다음, 이 단위 클래스들을 포함관계로 재사용하면 보다 간결하고 손쉽게 클래스를 작성할 수 있다. 또한 작성된 단위
클래스들은 다른 클래스를 작성하는데 재사용될 수 있을 것이다.
ch 4. 클래스 간의 관계 설정하기
클래스를 작성하는데 있어서 상속관계, 포함관계 둘중 결정하는 것은 '~은 ~이다(is-a)'와 '~은 ~을 가지고 있다(has-a)'를 넣어서 문장을
만들어보면 간단하다.
- 상속관계 : '~은 ~이다(is-a)'
- 포함관계 : '~은 ~을 가지고 있다.(has-a)'
ch 5. 단일 상속(single inheritance)
다중상속을 허용하면 여러 클래스로부터 상속받을 수 있기 때문에 복합적인 기능을 가진 클래스를 쉽게 작성할 수 있는 장점이 있지만,
클래스 간의 관계가 매우 복잡해진다는 것과 서로 다른 클래스로부터 상속받은 멤버간의 이름이 같은 경우 구별할 수 있는 방법이 없다는 단점이
있다. 자바에서는 다중상속의 이러한 문제점을 해결하기 위해 단일 상속만을 허용한다. 단일 상속은 클래스 간의 관계가 보다 명확해지고 코드를
더욱 신뢰할 수 있게 만들어 준다.
ch 6. object 클래스 - 모든 클래스의 조상
Object클래스는 모든 클래스 상속계층도의 최상위에 있는 조상클래스이다. 다른 클래스로부터 상속 받지 않는 모든 클래스들은 자동적으로
Object클래스로부터 상속받게 함으로써 이것을 가능하게 한다. 자바의 모든 클래스들은 Object클래스의 멤버들을 상속받기 때문에 Object클래스에 정의된
멤버들을 사용할 수 있다.
toString(), equals(Object o) 등...
ch 7. 오버라이딩(overriding)
조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것을 오버라이딩이라고 한다. 상속받은 메서드를 그대로 사용하기도 하지만, 자손 클래스
자신에 맞게 변경해야 하는 경우에 조상의 메서드를 오버라이딩한다.
ch 8 .오버라이딩의 조건
오버라이딩은 메서드의 내용만을 새로 작성하는 것이므로 메서드의 선언부(메서드 이름, 매개변수, 반환타입)는 조상의 것과 완전히
일치해야 한다. 다만 접근 제어자(access modifier)와 예외(exception)는 제한된 조건 하에서만 다르게 변경할 수 있따.
1. 접근 제어자는 조상클래스의 메서드보다 좁은 범위로 변경할 수 없다.
2. 조사 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다
ch 9. 오버로딩 vs 오버라이딩
오버로딩(overloading) : 기존에 없는 새로운 메서드를 정의하는 것(new)
오버라이팅(overriding) : 상속받은 메서드의 내용을 변경하는 것(change, modify)
class Parent{
void parentMethod(){}
}
class Child extends Parent{
void parentMethod(){} // 오버라이딩
void parentMethod(int i){} // 오버로딩
void childMethod(){}
void childMethod(int i){} // 오버로딩
void childMethod(){} // 에러. 중복정의 되었음
}
ch 10. 참조변수 super
super는 자손 클래스에서 조상클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다. 멤버변수와 지역변수의 이름이 같을 때 this를
붙여서 구별했듯이 상속받은 멤버와 자신의 멤버와 이름이 같을 때는 super를 붙여서 구별할 수 있다.
모든 인스턴스 메서드에는 this와 super가 지역변수로 존재하는데, 이들에는 자신이 속한 인스턴스의 주소가 자동으로 저장된다. 조상의 멤버와
자신의 멤버를 구별하는데 사용된다는 점만 제외하면 this와 super는 근본적으로 같다.
ch 11. super() - 조상의 생성자
this()처럼 super()도 생성자이다. this()는 같은 클래스의 다른 생성자를 호출하는데 사용되지만, super()는 조상의 생성자를 호출하는데
사용된다. 클래스 자신에 선언된 변수는 자신의 생성자가 초기화를 책임지도록 작성하는 것이 좋다. 참고로 생성자는 상속되지 않는다.
Point3D(int x, int y, int z){
super(x, y); // 조상클래스의 생성자 Point(int x, int y)를 호출
this.z = z; // 자신의 멤버를 초기화
}
ch 12. 패키지
패키지란, 클랫의 묶음이다. 패키지에는 클래스 또는 인터페이스를 포함시킬 수 있으며, 서로 관련된 클래스들끼리 그룹 단위로 묶어 놓음으로써
클래스를 효율적으로 관리할 수 있다. 같은 이름의 클래스일지라도 서로 다른 패키지에 존재하는 것이 가능하므로, 자신만의 패키지 체계를
유지함으로써 다른 개발자가 개발한 클래스 라이브러리의 클래스와 이름이 충돌하는 것을 피할 수 있다.
지금까지는 단순히 클래스 이름으로만 클래스를 구분했지만, 사실 클래스의 실제 이름(full name)은 패키지명을 포함한 것이다. 그래서 같은
이름의 클래스일지라도 서로 다른 패키지에 속하면 패키지명으로 구별이 가능하다.
ch 13. 패키지의 선언
패키지를 선언하려면 클래스나 인터페이스의 소스파일(.java)의 맨 위에 다음과 같이 한 줄만 적어주면 된다.
package 패키지명;
위와 같이 패키지 선언문은 반드시 소스파일에서 주석과 공백을 제외한 첫 번째 문장이어야 하며, 하나의 소스파일에 단 한 번만 선언될 수 있다.
ch 14. 클래스 패스
ch 15. import문
소스코드를 작성할 때 다른 패키지의 클래스를 사용하려면 패키지명이 포함된 클래스 이름을 사용해야 한다. 하지만, 매번 패키지명을
붙여서 작성하기란 여간 불편한 일이 아니다. 클래스의 코드를 작성하기 전에 import문으로 사용하고자 하는 클래스의 패키지를 미리 명시해주면
소스코드에서 사용되는 클래스이름에서 패키지명은 생략할 수 있다. import문의 역할은 컴파일러에게 소스파일에 사용된 클래스의 패키지에 대한 정보를 제공하는 것이다.
import 패키지명.클래스명;
또는
import 패키지명.*;
ch 16. static import문
import문을 사용하면 클래스의 패키지명을 생략할 수 있는 것과 같이 static import문을 사용하면 static멤버를 호출할 떄 클래스이름을 생략할
수 있다.
ch 17. 제어자(modifier)
제어자는 클래스, 변수 또는 메서드의 선언부에 함께 사용되어 부가적인 의미를 부여한다.
- 접근 제어자 : public, protected, (default), private
- 그 외 : static, final, abstract, native, transient, synchronized, volatile, strictfp
접근 제어자는 한 번에 네가지 중 하나만 선택해서 사용할 수 있다. 즉, 하나의 대상에 대해서 public과 private를 함께 사용할 수 없다.
ch 18. static - 클래스의, 공통적인
static이 사용될 수 있는 곳
- 멤버 변수
모든 인스턴스에 공통적으로 사용되는 클래스 변수가 된다. 클래스 변수는 인스턴스를 생성하지 않고도 사용 가능하다. 클래스가 메모리에 로드될
때 생성된다.
- 메서드
인스턴스를 생성하지 않고도 호출이 가능한 static메서드가 된다. static메서드 내에서는 인스턴스멤버들을 직접 사용할 수 없다.
ch 19. final - 마지막의, 변경될 수 없는
final이 사용될 수 있는 곳
- 클래스 : 변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다. 그래서 final로 지정된 클래스는 다른 클래스의 조상이 될 수 없다.
- 메서드 : 변경될 수 없는 메서드, final로 지정된 메서드는 오버라이딩을 통해 재정의 될 수 없다.
- 멤버변수 : 변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다.
- 지역변수 : 멤버변수와 같은 의미.
ch 20. abstract - 추상의, 미완성의
abstract가 사용될 수 있는 곳
- 클래스 : 클래스 내에 추상 메서드가 선언되어 있음을 의미한다.
- 메서드 : 선언부만 작성하고 구현부는 작성하지 않은 추상 메서드임을 알린다.
ch 21. 접근제어자(access modifier)
접근 제어자가 사용될 수 있는 곳 : 클래스, 멤버변수, 메서드, 생성자
- private : 같은 클래스 내에서만 접근이 가능하다.
- (default) : 같은 패키지 내에서만 접근이 가능하다.
- protected : 같은 패키지 내에서, 그리고 다른 패키지의 자손클래스에서 접근이 가능하다.
- public : 접근 제한이 전혀 없다.
접근 범위가 넓은 -> 좁은순
public > protected > default > private
ch 22. 캡슐화와 접근제어자
접근 제어자를 사용하는 이유
- 외부로부터 데이터를 보호하기 위해서
- 외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해서
ch 23. 다형성(polymorphism)
다형성이란 '여러 가지 형태를 가질 수 있는 능력'을 의미하며, 자바에서는 한 타입의 참조변수로 여러타입의 객체를 참조할 수
있도록 함으로써 다형성을 프로그램적으로 구현하였다. 즉, 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록
하였다는 것이다.
- 조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있다.
- 반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.
ch 24. 참조변수의 형변환
기본형 변수처럼 참조변수도 형변환이 가능하다. 단, 서로 상속관계에 있는 클래스 사이에서만 가능하기 때문에 자손타입의 참조변수를
조상타입의 참조변수로, 조상타입의 참조변수를 자손타입의 참조변수로의 형변환만 가능하다.
서로 상속관계에 있는 타입간의 형변환은 양방향으로 자유롭게 수행될 수 있으나, 참조변수가 가리키는 인스턴스의 자손타입으로
형변환은 허용되지 않는다. 그래서 참조변수가 가리키는 인스턴스의 타입이 무엇인지 먼저 확인하는 것이 중요하다.
ch 25. 참조변수의 형변환 예제
ch 26. instanceof 연산자
참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof 연산자를 사용한다.주로 조건문에 사용되며 instanceof의
왼쪽에는 참조변수를, 오른쪽에는 타입(클래스명)이 피연산자로 위치한다. 그리고 연산의 결과로 boolean의 값인 true와 false 중 하나를 반환
한다.
어떤 타입에 대한 instanceof 연산의 결과가 true라는 것은 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.
ch 27. 매개변수의 다형성
참조변수의 다형적인 특징은 메서드의 매개변수에도 적용된다.
ch 28. 매개변수의 다형성 예제
ch 29. 여러 종류의 객체를 배열로 다루기
조상타입의 참조변수 배열을 사용하면, 공통의 조상을 가진 서로 다른 종류의 객체를 배열로 묶어서 다룰 수 있다. 또는 묶어서 다루고 싶은 객체들의
상속관계를 따져서 가장 가까운 공통조상 클래스 타입의 참조변수 배열을 생성해서 객체들을 저장하면 된다.
ch 30. 여러 종류의 객체를 배열로 다루기 예제
ch 31. 추상 클래스(abstract class)
추상클래스 : 미완성 설계도. 인스턴스 생성불가. 미완성 메서드(추상 메서드)를 포함하고 있는 클래스
abstract class 클래스이름{}
ch 32. 추상 메서드(abstract method)
메서드는 선언부와 구현부(몸통)로 구성되어 있다. 선언부만 작성하고 구현부는 작성하지 않은 채로 남겨 둔 것이 추상메서드이다.
즉, 설계만 해 놓고 실제 수행될 내용은 작성하지 않았기 때문에 미완성 메서드인 것이다. 메서드를 미완성 상태로 남겨놓은 이유는
메서드의 내용이 상속받는 클래스에 따라 달라질 수 있기 때문에 조상 클래스에서는 선언부만을 작성하고, 주석을 덧붙여 어떤 기능을 수행할
목적으로 작성되었는지 알려주고, 실제 내용은 상속받는 클래스에서 구현하도록 비워두는 것이다.
abstract 리턴타입 메서드 이름();
ch 33. 추상클래스의 작성
여러 클래스에 공통적으로 사용될 수 있는 클래스를 바로 작성하기도 하고, 기존의 클래스의 공통적인 부분을 뽑아서 추상클래스로 만들어
상속하도록 하는 경우도 있다.
ch 34. 추상클래스의 작성 예제
ch 35. 인터페이스(interface)
인터페이스는 추상클래스처럼 추상메서드를 갖지만 추상클래스보다 추상화 정도가 높아서 추상클래스와 달리 몸통을 갖춘 일반 메서드 또는
멤버젼수를 구성원으로 가질 수 없다. 오직 추상메서드와 상수만을 멤버로 가질 수 있다. 추상클래스를 '미완성 설계도'라고 한다면, 인터페이스는
구현된 것이 없이 밑그림만 그려져 있는 '기본 설계도'라 할 수 있다.
interface 인터페이스 이름{
public static final 타입 상수이름 = 값;
public abstract 메서드이름(매개변수목록);
}
- 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
- 모든 메서드는 public abstract 이어야하며, 이를 생략할 수 있다. 단, static 메서드와 디폴트 메서드는 예외.
ch 36. 인터페이스의 상속
인터페이스는 인터페이스로부터만 상속받을 수 있으며, 클래스와 달리 다중상속, 즉 여러개의 인터페이스로부터 상속받는 것이 가능하다.
클래스의 상속과 마찬가지로 자손 인터페이스(Fightable)는 조상 인터페이(Movable, Attackable)에 정의된 멤버를 모두 상속받는다. 그래서
Fightable 자체에는 정의된 멤버가 하나도 없지만 조상 인터페이스로부터 상속받은 두 개의 추상메서드, move(int x, int y)와 attack(Unit u)을
멤버로 갖게 된다.
ch 37. 인터페이스의 구현
인터페이스도 추상클래스처럼 그 자체로는 인스턴스를 생성할 수 없으며, 추상클래스가 상속을 통해 추상메서드를 완성하는 것처럼, 인터페이스도
자신에 정의된 추상메서드의 몸통을 만들어주는 클래스를 작성해야 하는데, 그 방법은 추상클래스가 자신을 상속받는 클래스를 정의하는 것과 같다.
class 클래스이름 implements 인터페이스 이름{
// 인터페이스에 정의된 추상메서드를 모두 구현해야 한다.
}
만일 구현하는 인터페이스의 메서드 중 일부만 구현한다면 , abstract를 붙여서 추상클래스로 선언해야 한다. 그리고 상속과 구현을 동시에 할 수도
있다.
ch 38. 인터페이스를 이용한 다형성
인터페이스는 이를 구현한 클래스의 조상이라 할 수 있으므로 해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며,
인터페이스 타입으로의 형변환도 가능하다.
Fightable f = (Fightable)new Fighter();
또는
Fightable f = new Fighter();
리턴타입이 인터페이스라면 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다.
ch 39. 인터페이스의 장점
- 개발시간을 단축시킬 수 있다
- 표준화가 가능하다.
- 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.
- 독립적인 프로그래밍이 가능하다.
ch 40. 디폴트 메서드와 static 메서드
디폴트 메서드(default method)는 추상 메서드의 기본적인 구현을 제공하는 메서드로, 추상 메서드가 아니기 때문에 디폴트 메서드가
새로 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.
interface MyInterface{
void method();
default void newMethod(){}
}
1. 여러 인터페이스의 디폴트 메서드 간의 충돌
- 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩해야 한다.
2. 디폴트 메서드와 조상 클래스의 메서드 간의 충돌
- 조상클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다.
ch 41. 디폴트 메서드와 static 메서드 예제
ch 42. 내부 클래스(inner class)
내부 클래스는 클래스 내에 선언된 클래스이다. 두 클래스는 긴밀한 관계에 있다.
- 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
- 코드의 복잡성을 줄일 수 있다(캡슐화).
ch 43. 내부 클래스의 종류와 특징
- 인스턴스 클래스(instance class) : 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 인스턴스 멤버처럼 다루어진다. 주로 외부 클래스의
인스턴스멤버들과 관련된 작업에 사용될 목적으로 선언된다.
- 스태틱 클래스(static class) : 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 static 멤버처럼 다루어진다. 주로 외부 클래스의
static멤버, 특히 static 메서드에서 사용될 목적으로 선언된다.
- 지역 클래스(local class) : 외부 클래스의 메서드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있다.
- 익명 클래스(anonymous class) : 클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용)
ch 44. 내부 클래스의 선언
변수가 선언된 위치와 내부클래스 선언위치가 동일하기 때문에 변수가 선언된 위치에 따라 인스턴스 변수, 클래스 변수(static 변수), 지역변수로
나뉘듯이 내부 클래스도 이와 마찬가지로 선언된 위치에 따라 나뉜다. 그리고 각 내부 크랠스의 선언 위치에 따라 같은 선언위치의 변수와 동일한
유효범위(scope)와 접근성(accessibility)을 갖는다.
class Outer{
class InstanceInner{}
static class StaticInner{}
void myMethod(){
class LocalInner{}
}
}
ch 45. 내부 클래스의 제어자와 접근성
ch 46. 내부 클래스의 제어자와 접근성 예제1
내부 클래스 중에서 스태틱 클랫만 static멤버를 가질 수 있다. 그러나 final과 static이 동시에 붙은 변수는 상수(constant)이므로 모든
내부 클래스에서 정의가 가능하다.
ch 47. 내부 클래스의 제어자와 접근성 예제 2
인스턴스 멤버는 같은 클래스에 있는 인스턴스멤버와 static멤버 모두 직접 호출이 가능하지만, static멤버는 인스턴스 멤버를 직접 호출할 수
없는 것처럼, 인스턴스클래스는 외부 클래스의 인스턴스멤버를 객체생성 없이 바로 사용할 수 있지만, 스태틱 클래스는 외부 클래스의 인스턴스 멤버를
객체생성 없이 사용할 수 있다. 마찬가지로 인스턴스클래스는 스태틱클래스의 멤버들을 객체생성 없이 사용할 수 있지만, 스태틱 클래스에서는
인스턴스 클래스의 멤버들을 객체생성 없이 사용할 수 없다.
ch 48. 내부 클래스의 제어자와 접근성 예제 3
ch 49. 내부 클래스의 제어자와 접근성 예제 4
ch 50. 내부 클래스의 제어자와 접근성 예제 5
ch 51. 익명 클래스(anonymous class)
익명 클래스는 특이하게도 다른 내부 클래스들과 달리 이름이 없다. 클래스의 선언과 객체의 생성을 동시에 하기 때문에 한번만 사용될 수 있고
오직 하나의 객체만을 생성할 수 있는 일회용 클래스이다.
new 조상클래스이름(){
// 멤버선언
}
또는
new 구현인터페이스이름(){
// 멤버선언
}
오로지 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만 구현할 수 있다.
ch 52. 익명 클래스 예제
'일 > JAVA' 카테고리의 다른 글
java05.class.class (0) | 2023.05.24 |
---|---|
java04. ref_type (0) | 2023.05.24 |
java03. if_for (0) | 2023.05.24 |
java02. operators (0) | 2023.05.23 |
java01. varibles (0) | 2023.05.23 |