본문 바로가기

개발일기/Java

[Java] Java의 정석 (6) 객체지향개념 3

 

제어자

 

제어자란?

 

- 클래스, 변수, 메소드의 선언부에 사용되어 부가적인 의미를 부여한다.

- 하나의 대상에 여러 개의 제어자를 조합해 사용할 수 있으나, 접근 제어자는 단 하나만 사용할 수 있다.

 

- 접근 제어자 : public, protected, default, private

- 그 외 제어자 : static, final, abstract, native, transient, synchronized, volatile, strictfp

 

 

static

- 멤버변수, 메소드, 초기화 블럭에 사용될 수 있다.

- 인스턴스를 생성하지 않고도 호출이 가능한 변수, 메소드로 만들어준다.

 

 

final

- 클래스, 메소드, 멤버변수, 지역변수에 사용될 수 있다.

- 변경할 수 없도록 만든다. (변수는 상수로, 클래스는 상속 불가능하도록, 메소드는 오버라이딩될 수 없도록 한다.)

 

* 인스턴스 변수의 경우, final이 붙어도 선언과 동시에 초기화하지 않고 생성자에서 초기화할 수 있다.

class Card {
    final int NUMBER;
    final String KIND; // 상수지만 멤버변수이므로 생성자에서 초기화할 수 있다.
    
    Card(String kind, int num){
        KIND = kind;
        NUMBER = num;
    }
}

 

 

 

abstract

 

- 클래스, 메소드에 사용될 수 있다.

- 클래스에 사용된 경우, 클래스 내에 추상 메소드가 선언되어 있는 추상 클래스임을 의미한다.

- 메소드에 사용된 경우, 구현부가 작성되지 않은 추상 메소드임을 의미한다.

 

 

접근 제어자

private 같은 클래스 내에서만 접근이 가능하다.
default 같은 패키지 내에서만 접근이 가능하다.
protected 같은 패키지 내에서, 그리고 다른 패키지의 자손 클래스에서 접근이 가능하다.
public 접근 제한이 전혀 없다.

- 클래스에는 public과 default, 메소드에는 모두 사용할 수 있다.

- 외부로부터 데이터를 보호하기 위해, 그리고 외부에는 불필요한 부분을 감추기 위해 사용한다. (캡슐화)

- 캡슐화 : 클래스 내부의 멤버를 외부로부터 직접 접근하지 못하도록 감추고, 해당 클래스에 선언된 메소드를 통해서만 접근할 수 있도록 하는 것.

 

* 생성자의 접근 제어자

- 일반적으로 생성자의 접근 제어자는 클래스의 접근 제어자와 일치하나, 생성자에 접근 제어자를 사용함으로써 인스턴스의 생성을 제한할 수 있다.

final class Singleton {
    private static Singleton s = new Singleton();
    
    private Singleton(){ // 생성자
        //...
    }
    
    public static Singleton getInstance(){
        if(s==null){
            s = new Singleton();
        return s;
    }
    
    // ...
}

class SingletonTest{
    public static void main(String args[]){
        // Singleton s = new Singleton(); -> 접근 제어자로 인해 에러 발생
        Singleton s = Singleton.getInstance();
    }
}

 

 

 

 

 

다형성 (Polymorphism)

 

 

다형성이란?

 

- 하나의 참조변수로 여러 타입의 객체를 참조할 수 있는 것.

- 조상타입의 참조변수로 자손타입의 객체를 다룰  수 있는 것. (반대는 불가능)

class Tv{
    boolean power;
    int channel;
}

class CaptionTv extends Tv{
    String text;
}

class test{
    public static void main(String argv[]){
        CaptionTv c = new CaptionTv(); 
        // CaptionTv 클래스의 모든 멤버(power, channel, text)에 접근 가능
        
        Tv t = new CaptionTv(); 
        // 부모 타입의 참조변수로 자손 타입의 객체를 다룰 수 있음
        // Tv 클래스에 정의된 멤버(power, channel)에만 접근 가능
        
        // CaptionTv c2 = new Tv(); 는 불가
    }
}

 

 

 

참조변수의 형변환

 

- 서로 상속관계에 있는 타입 간의 형변환만 가능하다.

- 자손 타입에서 조상 타입으로 형변환 하는 경우엔 형변환을 생략할 수 있다.

class Car{
    String color;
    int door;
}

class FireEngine extends Car{
    void water(){
        // ...
    }
}

class test{
    public static void main(String args[]){
        Car car = null;
        FireEngine fe = new FireEngine();
        FireEngine fe2 = null;
        
        car = fe; // 자손 -> 조상으로 형변환 (Up-casting)
        fe2 = (FireEngine)car; // 조상 -> 자손으로 형변환 (Down-casting)
        
        // car.water(); -> 에러 발생. car 참조변수로는 자손 클래스의 멤버에 접근 불가.
        fe2.water();
    }
}

 

 

 

instanceof 연산자

 

- 참조변수가 참조하는 인스턴스의 실제 타입을 확인하는 데 사용한다.

- 결과값이 true이면 해당 타입으로 형변환이 가능하다.

class test{
    public static void main(String args[]){
        FireEngine fe = new FireEngine();
        
        if(fe instanceof FireEngine){
            // true.
        }
        
        if(fe instanceof Car){
            // true.
        }
        
        if(fe instanceof Object){
            // true.
        }
    }
}

 

 

 

참조변수와 인스턴스변수의 연결

 

- 멤버변수가 중복정의된 경우, 참조변수의 타입에 따라 연결되는 멤버변수가 달라진다.

- 메소드가 중복정의된 경우, 참조변수의 타입에 관계없이 항상 실제 인스턴스의 타입에 정의된 메소드가 호출된다.

class Parent{
    int x = 100;
    
    void method(){
        System.out.println("Parent");
    }
}

class Child extends Parent{
    int x = 200;
    
    void method(){
        System.out.println("Child");
    }
}

class test{
    public static void main(String args[]){
        Parent p = new Child();
        Child c = new Child();
        
        System.out.println(p.x); // 100
        System.out.println(c.x); // 200
        // 멤버변수는 참조변수의 타입에 영향을 받는다.
        
        p.method(); // "Child"
        c.method(); // "Child"
        // 메소드는 참조변수의 타입에 관계없이 생성된 인스턴스에 정의된 메소드를 호출한다.
    }
}

 

 

매개변수의 다형성

- 참조형 매개변수는 메소드 호출 시, 자신과 같은 타입 또는 자손 타입의 인스턴스를 넘겨줄 수 있다.

class Product {
    int price;
    int bonusPoint;
}

class Tv extends Product {}
class Computer extends Product {}
class Audio extends Product {}

class Buyer {
    int money = 1000;
    int bonusPoint = 0;
    
    void buy(Product p){
        money -= p.price;
        bonusPoint +=p.bonusPoint;
    }
}

class test{
    public static void main(String args[]){
        Buyer b = new Buyer();
        
        Tv tv = new Tv();
        Computer com = new Computer();
        
        b.buy(tv);
        b.buy(computer);
        // buy 메소드의 매개변수로는 Product의 참조변수를 취하나, 
        // Product의 자손 클래스인 Tv나 Computer의 참조변수도 넘겨줄 수 있다.
    }
}

 

 

여러 종류의 객체를 하나의 배열로 다루기

- 조상 타입의 참조변수에 자손 타입의 객체를 다룰 수 있음을 이용하여, 조상 타입의 배열에 자손들의 객체를 담을 수 있다.

Product p[] = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();

 

 

Vector 클래스

- 모든 종류의 객체들을 저장할 수 있는 클래스

Vector() 10개의 객체를 저장할 수 있는 Vector 인스턴스를 생성한다. 
10개 이상의 인스턴스가 저장되면 자동적으로 크기가 증가된다.
boolean add(Object o) Vector에 객체를 추가한다. 
추가에 성공하면 true, 실패하면 false를 반환한다.
boolean remove(Object o) Vector에 저장되어 있는 객체를 제거한다.
제거에 성공하면 true, 실패하면 false를 반환한다.
boolean isEmpty() Vector가 비어있는지 검사한다.
Object get(int index) 지정된 위치의 객체를 반환한다.
int size() Vector에 저장된 객체의 개수를 반환한다.

 

Vector cart = new Vector();

void buy(Product p){
    cart.add(p);
}