
class child extends Parent{
...
}
생성자와 초기화 블럭은 상속되지 않는다. 멤버만 상속된다.
자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.
자손 클래스의 인스턴스를 생성하면 조상 클래스의 멤버와 자손 클래스의 멤버가 합쳐진 하나의 인스턴스로 생성된다.
class Circle{
int x; // 원점의 x좌표
int y; // 원점의 y좌표
int r; // 반지름
}
class Point{
int x; // x좌표
int y; // y좌표
}
// 재사용 할 경우
class Circle{
Point c = new Point();
int r;
}
원(Circle)은 점(Point)이다. -> 포함
원(Circle)은 점(Point)을 가지고 있다. -> 상속
class TVCR extends TV, VCR{ // 에러. 조상은 하나만 허용된다.
...
}
class Tv (extends Object){ // 실제로 compile을 하면 괄호 안이 생성된채 실행된다.
...
}
자손 클래스에서 오버라이딩 하는 메서드는 조상 클래스의 메서드와
1. 이름이 같아야 한다.
2. 매개변수가 같아야 한다.
3. 반환타입이 같아야 한다.
오버로딩(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) // 오버로딩
}
class SuperTest{
public static void main(String args[]){
Child c = enw Child();
c.method();
}
}
class Parent{
int x=10;
}
class Child extends Parent{
int x=20;
void method(){
System.out.println("x="+x); // x=20
System.out.println("this.x="+this.x); // this.x = 20
System.out.println("super.x="+super.x); // super.x = 10
}
}
class PointTest{
public static void main(String args[]){
Point3D p3 = new Point3D(1,2,3);
}
}
class Point{
int x,y;
Point(int x, int y){
this.x = x;
this.y = y;
}
String getLocation(){
return "x : "+x+", y :"+y;
}
}
class Point3D extends Point{
int z;
Point3D(int x, int y, int z){
super(); // 중요!!!!!!!!!! -> 조상 클래스의 생성자 Point(int x, int y)를 호출한다.
this.z = z;
}
String getLocation(){
return "x : "+x+", y :"+y+",z:"+z; // 오버라이딩
}
}
class PointTest2{
public static void main(String args[]){
Point 3D p3 = new Point3D();
System.out.println("p3.x="+p3.x);
System.out.println("p3.y="+p3.y);
System.out.println("p3.z="+p3.z);
}
}
class Point {
int x=10;
int y=20;
Point{
super(); // 조상인 Object클래스의 생성자 Object()를 호출한다.
this.x = x;
this.y = y;
}
}
class Point3D extends Point {
int z = 30;
Point3D(){
this(100, 200, 300); // Point3D(int x, int y, int z)를 호출한다.
}
Point3D(int x, int y, int z){
super(x,y); // Point(int x, int y)를 호출한다.
this.z = z;
}
}
- 하나의 소스파일에는 첫 번째 문장으로 단 한번의 패키지 선언만을 허용한다.
- 모든 클래스는 반드시 하나의 패키지에 속해야 한다.
- 패키지는 점(.)을 구분자로 하여 계층구조로 구성할 수 있다.
- 패키지는 물리적으로 클래스 파일(.class)를 포함하는 하나의 디렉토리이다.
package 패키지명;
import문은 프로그램의 성능에 전혀 영향을 미치지 않는다. import문을 많이 사용하면 컴파일 시간이 아주 조금 더 걸리 뿐이다.
일반적인 소스파일(*.java)의 구성은 1. package문 2. import문 3. 클래스 선언의 순서로 되어 있다.
import 패키지명.클래스명;
//or
import 패키지명.*; // 실행시 성능차이 X
import static java.lang.Integer.*; // Integer클래스의 모든 static메서드
접근 제어자 : public, protected, default, private
그 외 : static, final, abstract, native, transient, synchronized, voltaile, strictfp
static이 사용될 수 있는 곳 - 멤버변수, 메서드, 초기화 블럭
class StaticTest{
static int width = 200; // 클래스변수(static변수)
static int height = 120; // 클래스변수(static변수)
static { // 클래스 초기화 블럭
// static변수의 복잡한 초기화 수행
}
static int max(int a, int b){ // 클래스 메서드(static메서드)
return a>b ? a: b;
}
}
final이 사용될 수 있는 곳 : 클래스, 메서드, 지역변수, 멤버변수
final class FinalTest{ // 조상이 될 수 없는 클래스
final int MAX_SIZE=10; // 값을 변경할 수 없는 멤버변수(상수)
final void getMaxSize(){ // 오버라이딩할 수 없는 메서드(변경 불가)
final int LV = MAX_SIZE; // 값을 변경할 수 없는 지역변수(상수)
return MAX_SIZE;
}
}
class Card{
final int NUMBER;
final STRING KIND;
static int width = 100;
static int height = 250;
Card(String kind, int num){
this.kind = kind;
this.num = num;
}
Card(){
this("HEART", 1);
}
public String toString(){
return KIND + " " + NUMBER;
}
}
class FinalCardTest{
public static void main(String args[]){
Card c = new Card("HEART", 10);
// c.NUMBER = 5; // 에러
System.out.println(c.KIND);
System.out.println(c.NUMBER);
System.out.println(c);
}
}
abstract가 사용될 수 잇는 곳 - 클래스, 메서드
abstract class AbstractTest{ // 추상 클래스
abstract void move(); // 추상 메서드
}
접근 제어자가 사용될 수 있는 곳 - 클래스, 멤버변수, 메서드, 생성자
private 같은 클래스 내에서만 접근이 가능하다.
default 같은 패키지 내에서만 접근이 가능하다.
protected 같은 패키지 내에서, 그리고 다른 패키지의 자손클래스에서 접근이 가능하다.
public 접근 제한이 전혀 없다.
접근 범위가 넓은 쪽에서 좁은 쪽의 순으로 왼쪽부터 나열하면 다음과 같다.
public > protected > (default) > private
public class Time{
private int hour; // 접근 제어자를 private으로 하여 외부에서 직접 접근하지 못하도록 한다.
private int minute; // 접근 제어자를 private으로 하여 외부에서 직접 접근하지 못하도록 한다.
private int second; // 접근 제어자를 private으로 하여 외부에서 직접 접근하지 못하도록 한다.
public int getHour() { return hour; }
public void setHour(int hour){
if (hour <0 || hour>23) return;
this.hour = hour
}
...
}
class Singleton{
...
private static Singleton s = new Singleton(); // getInstance()에서 사용될 수 있도록 인스턴스가 미리 생성되어야 하므로 static이어야 한다.
private Singleton(){
...
}
// 인스턴스를 생성하지 않고도 호출할 수 있어야 하므로 static이어야 한다.
public static Singleton getInstance(){
return s;
}
...
}
이처럼 생성자를 통해 직접 인스턴스를 생성하지 못하게 하고 public메서드를 통해 인스턴스에 접근함으로써 사용할 수 있는 인스턴스의 개수를 제한할 수 있다.
또 한가지, 생성자가 private인 클래스는 다른 클래스의 조상이 될 수 없다. 왜냐하면, 자손클래스의 인스턴스를 생성할 때 조상클래스의 생성자를 호출해야만 하는데, 생성자의 접근 제어자가 private이므로 자손 클래스에서 호출하는 것이 불가능하기 때문이다. 그래서 클래스 앞에 final을 추가하여 상속할 수 없는 클래스라는 것을 알리는 것이 좋다.
class Tv{
boolean power;
int channel;
void power(){...};
void channlUp(){...};
void chnnelDown(){...};
}
class CaptionTv extends Tv{
String text;
void caption(){...}
}
CaptionTv c = new CaptionTv();
Tv t = new CaptionTv();
// Caption Tv cc = new Tv; // 에러 발생 -> 허용X
클래스는 상속을 통해서 확장될 수는 있어도 축소될 수는 없어서, 조상 인스턴스의 멤버 개수는 자손 인스턴스의 멤버 개수보다 항상 적거나 같다.
자손 타입 -> 조상타입(Up-casting) : 형변환 생략가능
자손 타입 <- 조상타입(Down-casting) : 형변환 생략불가능
class Car{...}
class FireEngine extends Car{...}
class Ambulance extends Car{...}
// 예시1
FireEngine f;
Ambulance a;
a = (Ambulance) f; // 에러
f = (FireEngine) a; // 에러
// 예시2
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
car = fe; // car = (Car)fe;에서 형변환됨. 업캐스팅
fe2 = (FireEngine)car//형변환을 생략 불가능. 다운 캐스팅
서로 상속관계에 있는 타입간의 형변환은 양방향으로 자유롭게 수행될 수 있으나, 참조변수가 가리키는 인스턴스의 자손타입으로 형변환은 허용되지 않는다. 그래서 참조변수가 가리키는 인스턴스의 타입이 무엇인지 확인하는 것이 중요하다.
void doWork(Car c){
if (c instanceof FireEngine){
FireEngine fe = (FireEngine)c;
fe.water;
...
} else if (c instanceof Ambulance){
Ambulance a = (Ambulance)c;
a.siren();
...
}
...
}
문자열과 참조변수의 덧셈(결합연산)은 참조변수에 toString()을 호출해서 문자열을 얻어 결합한다.
클래스를 설계도에 비유한다면, 추상클래스는 미완성 설계도에 비유할 수 있다. 미완성 설계도란, 단어의 뜻 그대로 완성되지 못한 채로 남겨진 설계도를 말한다.
클래스가 미완성이라는 것은 멤버의 개수에 관계된 것이 아니라, 단지 미완성 메서드(추상메서드)를 포함하고 있다는 의미이다.
추상클래스는 상속을 통해서 자손클래스에 의해서만 완성될 수 있다.
추상클래스 자체로는 클래스로서의 역할을 다 못하지만, 새로운 클래스를 작성하는데 있어서 바탕이 되는 조상클래스로서 중요한 의미를 갖는다.
추상클래스는 키워드 'abstract'를 붙이기만 하면 된다.
abstract class 클래스이름{
...
}
추상메서드를 포함하고 있지 않은 클래스에도 키워드 'abstract'를 붙여서 추상클래스로 지정할 수도 있다. 추상메서드가 없는 완성된 클래스라 할지라도 추상클래스로 지정되면 클래스의 인스턴스를 생성할 수 없다.
/* 주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명한다. */
abstract 리턴타입 메서드이름();
추상화 : 클래스간의 공통점을 찾아내서 공통의 조상을 만드는 작업
구체화 : 상속을 통해 클래스를 구현, 확장하는 작업
interface 인터페이스이름{
public static final 타입 상수이름 = 값;
public abstract 메서드이름(매개변수 목록);
}
- 모든 멤버변수는 public static final이어야 하며, 이를 생략할 수 있다.
- 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다.
interface Movable{
void move(int x, int y);
}
interface Attackable{
void attack(Unit u);
}
interface Fightable extends Movable, Attackable{};
abstract class Fighter implements Fightable{
public void move(int x, int y){...}
}
인터페이스의 이름에는 주로 Fightable과 같이 '~을 할 수 있는'의 의미인 'albe'로 끝나는 것들이 많은데, 그 이유는 어떠한 기능 또는 행위를 하는데 필요한 메서드를 제공하는 의미를 강조하기 위해서이다. 이름이 'able'로 끝나는 것은 인터페이스라고 추측할 수 있지만, 모든 인터페이스의 이름이 반드시 'able'로 끝나야 하는 것은 아니다.
interface Movable{
void move(int x, int y);
}
class Fighter extends Unit implements Fightable{
public void move(int x, int y){...};
public void attack(Unit u);
}
Fightable f = (Fightable)new Fighter();
또는
Fightable f = new Fighter();
Fightable타입의 참조변수로는 인터페이스 Fightable에 정의된 멤버들만 호출이 가능하다.
class Fighter extends Unit implements Fightable{
public void move(int x, int y) {...};
public void attack(Fightable f) {...};
}
Fightable method(){
...
Fighter f = new Fighter();
return f; // 위두문장은 return new Fighter(); 과 동일
}
- 개발시간을 단축시킬 수 있다.
- 표준화가 가능하다.
- 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.
- 독립적인 프로그래밍이 가능하다.
- 클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있다.
- 메서드를 사용(호출)하는 쪽(User)에서는 사용하려는 메서드(Provider)의 선언부만 알면 된다.
// 추상 메서드일 경우
interface MyInterface{
void method();
void newMethod(); // 추상 메서드
// 디폴트 메서드
interface MyInterface{
void method();
default void newMethod(){};
}
새로 추가된 디폴트 메서드가 기존의 메서드와 이름이 중복되어 충돌하는 경우를 해결하기 위한 규칙
1. 여러 인터페이스의 디폴트 메서드 간의 충돌 : 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩 해야한다.
2. 디폴트 메서드와 조상 클래스의 메서드 간의 충돌 : 조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다.
내부 클래스의 장점
- 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
- 코드의 복잡성을 줄일 수 있다.
class InnerEx6{
Object lv = new Object(){void method() {} }; // 익명 클래스
static Object cv = new Object(){void method() {} }; // 익명 클래스
void myMethod(){
Object lv = new Object(){ void method(){} }; // 익명 클래스
}
}