자바가 확장한 객체 지향(2)

de_sj_awa·2021년 6월 5일
0
post-custom-banner

5. instanceOf 연산자

instanceOf 연산자는 만들어진 객체가 특정 클래스의 인스턴스인지 물어보는 연산자이다. instanceOf 연산자는 결과로 true 또는 false를 반환한다. 사용법은 아래와 같다.

객체_참조_변수 instaceOf 클래스명

예제 코드는 다음과 같다.

package instaceOf01;

class 동물 {

}

class 조류 extends 동물 {

}

class 펭귄 extends 조류 {

}

public class Driver {
    public static void main(String[] args) {
        동물 동물객체 = new 동물();
        조류 조류객체 = new 조류();
        펭귄 펭귄객체 = new 펭귄();
    
    	System.out.println(동물객체 instanceOf 동물);
        
        System.out.println(조류객체 instanceOf 동물);
        System.out.println(조류객체 instanceOf 조류);
        
        System.out.println(펭귄객체 instanceOf 동물);
        System.out.println(펭귄객체 instanceOf 조류);
        System.out.println(펭귄객체 instanceOf 펭귄);
        
        System.out.println(펭귄객체 instanceOf Object);
    }
}

실행해보면 모두 true가 나온다.

package instaceOf02;

class 동물 {

}

class 조류 extends 동물 {

}

class 펭귄 extends 조류 {

}

public class Driver {
    public static void main(String[] args) {
        동물 동물객체 = new 동물();
        동물 조류객체 = new 조류();
        동물 펭귄객체 = new 펭귄();
    
    	System.out.println(동물객체 instanceOf 동물);
        
        System.out.println(조류객체 instanceOf 동물);
        System.out.println(조류객체 instanceOf 조류);
        
        System.out.println(펭귄객체 instanceOf 동물);
        System.out.println(펭귄객체 instanceOf 조류);
        System.out.println(펭귄객체 instanceOf 펭귄);
        
        System.out.println(펭귄객체 instanceOf Object);
    }
}

위 예제 실행결과 역시 모두 true이다. 객체 참조 변수 타입이 아닌 실제 객체의 타입에 의해 처리하기 때문이다.

instaceOf 연산자는 클래스들의 상속관계 뿐만 아니라 인터페이스의 구현 관계에서도 동일하게 적용된다.

package instaceOf03;

interface 날수있는 {

}

class 박쥐 extends 날수있는 {

}

class 참새 extends 날수있는 {

}

public class Driver {
    public static void main(String[] args) {
        날수있는 박쥐객체 = new 박쥐();
        날수있는 참새객체 = new 참새();
        
        System.out.println(박쥐객체 instanceOf 날수있는);
        System.out.println(박쥐객체 instanceOf 박쥐);
        
		System.out.println(참새객체 instanceOf 날수있는);
        System.out.println(참새객체 instanceOf 참새);
    }
}

실행해보면 역시 모두 true를 반환한다.

6. package 키워드

package 키워드는 네임스페이스(이름공간)을 만들어주는 역할을 한다. 각자 다른 패키지에 동일한 클래스 이름이 있어도 완전 별개의 클래스가 된다.

7. interface 키워드와 implements 키워드

인터페이스는 public 추상 메서드와 public 정적 상수만 가질 수 있다. 또한 인터페이스는 추상 메서드와 정적 상수만 가질 수 있기에 따로 메서드에 public과 abstract, 속성에 public과 static과 final을 붙이지 않아도 자동으로 자바가 알아서 붙여준다.

또한 자바 8에서는 "변수에 저장할 수 있는 로직(함수)"인 람다가 등장했다. 변수는 값을 저장할 수 있고, 메서드의 인자로 쓰일 수 있고, 메서드의 반환값으로 사용할 수 있다. 결국 람다로 인해 변수에 로직을 저장할 수 있고, 로직을 메서드의 인자로 쓸 수 있고, 로직을 메서드의 반환값으로 사용할 수 있다는 결론에 도달한다. 이것은 함수형 언어가 지닌 특성을 자바도 수용했다는 것을 의미한다.

자바에서 람다는 인터페이스를 기초로 하고 있다. 이에 따라 자바 8 이전까지의 인터페이스는 정적 상수와 추상 메서드만 가질 수 있었지만 자바 8부터는 디폴트 메서드라고 하는 객체 구상 메서드와 정적 추상 메서드를 지원할 수 있게 되었다.

9. this 키워드

this는 객체가 자기 자신을 지칭할 때 쓰는 키워드이다. 지역변수와 객체변수의 이름이 동일하다면 지역변수에 우선권이 있다. 즉, 지역 변수에 저장되어 있는 값이 아닌 객체 변수에 저장되어 있는 값을 사용하고 싶은데 지역 변수와 객체 변수의 이름이 같을 경우 this.var라고 하면 객체 변수 var에 저장한 값을 사용하게 된다.

  • 지역 변수와 속성(객체 변수, 정적 변수)의 이름이 같은 경우 지역 변수가 우선한다.
  • 객체 변수와 이름이 같은 지역 변수가 있는 경우 객체 변수를 사용하려면 this를 접두사로 사용한다.
  • 정적 변수와 이름이 같은 지역 변수가 있는 경우 정적 변수를 사용하려면 클래스명을 접두사로 사용한다.

8. super 키워드

super는 바로 위 상위 클래스의 인스턴스를 지정하는 키워드다.

package Super;

class 동물 {
    void method() {
        System.out.println("동물");
    }

class 조류 extends 동물 {
    void method() {
        super.method();
        System.out.println("조류");
    }
}

class 펭귄 extends 조류 {
    void method() {
        super.method();
        System.out.println("펭귄");
        
        // Syntax error on token "super", Identifier expected
        // super.super.method();
    }
}

public class Driver {
    public static void main(String[] args) {
        펭귄 뽀로로 = new 펭귄();
        뽀로로.method();
    }
}

super 키워드를 이용해 상위 클래스의 인스턴스 메서드를 호출하고 있다. 그러나 super.super 형태로 상위의 상위 클래스의 인스턴스에는 접근이 불가능하다.

10. 객체 멤버 메서드

package stack;

class 펭귄 {
    void test() {
        System.out.println("Test");
    }
}

public class Driver {
    public static void main(String[] args) {
        펭귄 뽀로로 = new 펭귄();
        
        뽀로로.test();
    }
}

만약 펭귄 객체가 뽀로로 하나가 아니라 펭귄[100]과 같이 요소가 100개인 배열이라면 힙 영역에 생기는 펭귄 객체는 100개가 되고 test() 메서드도 각 펭귄 객체에 따라 힙에 100개가 만들어져야 한다.

그런데 객체 멤버 메서드는 각 객체별로 달라지는 것이 아니다. 객체 멤버 메서드에서 사용하는 객체 멤버 속성의 값만 다를 뿐이다. 똑같은 객체 멤버 메서드인 test()를 힙 영역에 100개나 만든다는 것은 심각한 메모리 낭비라고 할 수 있다.

그래서 JVM은 지능적으로 객체 멤버 메서드를 스태틱 영역에 단 하나만 보유한다. 그리고 눈에 보이지는 않지만, 객체 멤버 메서드를 호출할 때 객체 자신을 나타내는 this 객체 참조 변수를 넘긴다. 즉, 위의 코드는 JVM에 의해 아래와 같이 변경된다고 생각하면 된다.

package stack;

class 펭귄 {
    static void test(펭귄 this) {
        System.out.println("Test");
    }
}

public class Driver {
    public static void main(String[] args) {
        펭귄 뽀로로 = new 펭귄();
        
        펭귄.test(뽀로로);
    }
}

T 메모리로 표현하면 다음과 같다.

참고

  • 스프링 입문을 위한 자바 객체지향의 원리와 이해
profile
이것저것 관심많은 개발자.
post-custom-banner

0개의 댓글