DEV/JAVA

02. JAVA의 키워드와 연산자

wooki4307 2020. 2. 25. 12:11

JAVA에는 키워드와 연산자라는 것이 존재한다. 이번 글에서는 중요한 키워드와 연산자만 다룰 예정이다.

 

1. 추상클래스(Abstract Class)

추상클래스는 구현부가 없는 메소드가 1개이상 존재하는 클래스를 지칭한다. 이런 경우를 선언부만 있는 메소드라고 한다. 해당 경우를 예를들어 동물에 대한 울음소리에 대해 클래스를 만든다고 가정을 하면 다음과 같을 것이다.

public class Calf extends Animal {
    void cry(){
        System.out.println("나는 송아지! 음메~ 음메~");
    }
}
public class Dog extends Animal {
    void cry(){
        System.out.println("나는 강아지! 멍! 멍!");
    }
}

 

두 클래스 모두 Animal이라는 클래스를 상속 받는다. 그렇다면 Animal클래스의 cry라는 메소드를 만들떄에 어떤 식으로 구현을 해야하나?

정답은 구현 자체를 하지 않고 상속받는 클래스에서 재정의 하는 방법이 하나의 방법이라고 볼 수 있다. 각기 동물마다 서로 다른 소리를 내는데 굳이 상위 클래스에서 울음소리를 구현할 필요가 있을까? 이런 경우에 추상 클래스라는 개념을 사용한다. 추상클래스는 다음과 같이 구현이 된다.

public abstract class Animal {
    abstract void cry();
}

Animal 클래스 자체에서 선언만 진행을 한 이후 상속받는 클래스에서 재정의 즉, 오버라이딩 할 수 있도록 구현하는 것이다.

이처럼 추상 메소드가 1개 이상 존재하는 경우 무조건 클래스도 Abstract라는 키워드를 사용해야한다. 

추상클래스는 인스턴스, 즉, 생성자를 생성할 수 없는 특징이 있다. 한마디로 new 키워드로 생성을 못한다는 건데, 개인적인 생각으로는 추상클래스는 오버라이딩에 강제성을 부여하기 때문에 굳이 할 필요가 없어서 라고 생각한다.

추상클래스를 요약하면 다음과 같은 특징을 갖는다.

  • 인스턴스를 생성할 수 없다.
  • 오버라이딩에 강제성이 부여된다.
  • 추상메소드가 1개 이상 포함하는 클래스는 추상클래스가 된다.

 

2. 인터페이스(Interface)

실무를 접해봤거나, 혹은 실무를 접해보지 않은 사람들도 대학교에서도 인터페이스가 실무에서 얼마나 쓰이는지에 대해 말을 많이 듣는다. 다음 예시를 보면 더 의혹이 생길 것이다. 인터페이스를 간단히 구현해보면 다음과 같다.

interface Speakable{
    double PI = 3.14159;
    final double absoluteZeroPoint=-275.15;

    void sayYes();
}

1번에서 설명한 추상 클래스와 별다른 차이는 없어보인다. 정수 선언 유무와 메소드에 구현부가 없다는 정도? 일것이다. JAVA에서는 기본으로 붙는 키워드를 제외해도 무방한데, 그 제외된 키워드를 같이 넣어보면 다음과 같다.

interface Speakable{
    public static final double PI=3.141592;
    public static final absoluteZeroPoint=-275.15;

    abstract void sayYes();
}

여기서 차이가 확연히 들어난다. 변수(속성)에는 static키워드와 final키워드가 붙는 다는 점, 메소드에는 abstract키워드가 붙어 선언만 가능한 점이다. 추상 클래스에서는 구현부가 있는 메소드 생성이 가능하지만 인터페이스에서는 구현부가 있는 메소드가 존재가 불가능하다.

또한 추상클래스에서는 변수의 형태에 자유가 있지만 인터페이스에서는 자유가 없는 final연산자가 붙는다. 해당 연산자는 5번에서 다루도록 하겠다. 

 

3. 생성자(Constructor)

생성자는 모든 클래스가 가지는 인스턴스이다. 자바는 기본적으로 클래스를 생성하면 기본 인스턴스가 다음과 같이 생긴다.

public class Animal {
    public Animal(){}
}

하지만 기본 생성자는 생략을 해도 JAVA가 컴파일 시 자동으로 생성을 해준다. 하지만 주의해야 할점은 다음과 같다.

public class Animal {
    public Animal(String name){
        System.out.println(name);
    }
}

기본 생성자에 인수를 부여하면 기본 생성자는 생성이 될까?

더보기

정답

생기지 않는다. 인자 목록이 추가될때 새로 생성되어야 하는데 이 경우에는 인자가 없는 기본 생성자를 같이 생성해주어야 기본 인스턴스도 생성이 가능하다. 밑의 클래스처럼말이다.

public class Animal {
    public Animal(){}
    public Animal(String name){
        System.out.println(name);
    }
}

4. 스태틱 블록(Static Block)

어쩌면 생소할 수 있는 리스트 목록이다. static블록은 클래스의 생성자(?)와 매우 흡사한 영역이라고 할 수 있다. 실제로 클래스자체에 대한 생성자는 존재하지 않는다. 스태틱 블록은 JVM이 프로그램 실행시 크게 Static 프레임, Stack 프레임, Heap프레임의 큰 틀로 알맞은 코드를 주입한다. Static프레임은 클래스를 저장하는 영역, Stack프레임은 실행시 지역변수 및 메소드가 저장되는 영역, Heap은 인스턴스를 지정하는 영역이다. Static프레임은 클래스를 사용하는 시점에서 메모리에 올라가는 구조이다. 클래스를 처음 시작하는 부분은 다음과 같다.

  • 인스턴스 생성 시점
  • static 변수 사용 시점
  • static 메소드 사용 시점

이 Static블록은 처음 한번 실행한 이후에는 실행하지 않는다. 

public class Animal {
    static{
        System.out.println("Animal Class Ready...");
    }
}

아직 실무에서 사용은 해보지 않았지만 메모리가 올라가는 시점에서 사용하는 영역이니 Boot에서 설정값등이 메모리에 올라가는 구조에서 사용이 가능할 것으로 보인다.

 

5. final 연산자

앞서 2번 인터페이스 소개글에서 인터페이스의 변수에는 모두 final연산자가 붙는다. 이 final연산자는 다음과 같은 특징만 기억하면 된다.

처음 초기화 이후 더이상 수정이나 값 변경이 불가능한 영역

예를 들면, PI(3.141592...) 와 같은 정해진 영역, 동물의 꼬리 수(보편적인 예이다... 이건 태클걸지 말아주세요...)등과 같이 고정되어 있는 연산자가 될 수 있다. 메소드, 클래스, 속성 모두 사용이 가능하다.

public class Cat {
    final static int staticInt01=1;
    final static int staticInt02;

    final int objectInt01=1;
    final int objectInt02;

    static {
        staticInt02=2;

    }

    Cat(){
        objectInt02=4;

        final int localValue01=1;
        final int localValue02;

        localValue02=2;
    }
    
    final void breathe(){
        System.out.println("Breathing...");
    }
}

static은 static영역에서만 사용 가능한것은 동일하다. 주의해야 할 점은 final클래스에서 나온다.

public final class Cat {
}

고양이에 대한 클래스가 final클래스일때 이 클래스는 상속이 불가능하다. 필자가 생각하기에는 final연산자를 사용하면 가공처리가 불가능하여 해당 final클래스의 내용만 사용가능하기 때문이 아닐까 생각한다.

 

6. instanceOf 연산자

해당 연산자는 만들어진 객체가 특정 클래스의 인스턴스 인지 확인하는 연산자이다. 해당 사용법은 다음과 같다.

사용법: 객체_참조_변수 instanceOf 클래스명

객체의 타입에 대한 참, 거짓 값으로, 필자도 이해가 안되는 부분이 있어서 예시코드를 삽입해봤다.

class Animal{}

class Bird extends Animal{}

class Penguin extends Bird{}

public class Driver {
    public static void main(String[] args){
        Animal animalObj=new Animal();
        Bird birdObj=new Bird();
        Penguin penguinObj=new Penguin();

        System.out.println(animalObj instanceof Animal);

        System.out.println(birdObj instanceof Animal);
        System.out.println(birdObj instanceof Bird);

        System.out.println(penguinObj instanceof Animal);
        System.out.println(penguinObj instanceof Bird);
        System.out.println(penguinObj instanceof Penguin);

        System.out.println(penguinObj instanceof Object);
    }
}

birdObj, penguinObj는 타입이 각각 Bird, Penguin이 된다. 해당 클래스는 각각 Bird의 인스턴스도 되고, Animal의 인스턴스도 되므로 모두 true라는 결과값이 나온다. 그러면 다음과 같은 코드는?

class Animal{}

class Bird extends Animal{}

class Penguin extends Bird{}

public class Driver {
    public static void main(String[] args){
        Animal animalObj=new Animal();
        Animal birdObj=new Bird();
        Animal penguinObj=new Penguin();

        System.out.println(animalObj instanceof Animal);

        System.out.println(birdObj instanceof Animal);
        System.out.println(birdObj instanceof Bird);

        System.out.println(penguinObj instanceof Animal);
        System.out.println(penguinObj instanceof Bird);
        System.out.println(penguinObj instanceof Penguin);

        System.out.println(penguinObj instanceof Object);
    }
}

모두 타입은 Animal인데 해당 결과값은 어떻게 나올것인가?

더보기

정답

true

이유는 모두 타입 자체는 Animal에서 상속받은 Bird, Penguin이 된다. 사용 가능한 영역이 Animal만 사용 가능할뿐 객체 생성 자체는 Animal을 상속받은 클래스들이기 때문이다.

쉽게말해 birdObj는 Animal객체지만 생성은 Bird로 하지 않았나? penguinObj는 Animal객체지만 인스턴스는 Animal을 상속받은 Bird, Bird를 상속받은 Penguin으로 생성하였기 때문에 해당 인스턴스는 true라고 나온다. 이는 interface사용시에도 동일하다.

interface flying{}

class Bat implements flying{}

class Sparrow implements flying{}

public class Driver {
    public static void main(String[] args){
        flying batObj=new Bat();
        flying sparrowObj=new Sparrow();

        System.out.println(batObj instanceof flying);
        System.out.println(batObj instanceof Bat);

        System.out.println(sparrowObj instanceof flying);
        System.out.println(sparrowObj instanceof Sparrow);

    }
}

7. this 연산자

this는 객체 참조 변수 자신을 지칭하는 연산자이다. 예를 들어 다음과 같은 클래스가 있다.

class Penguin {
    int var =10;

    void test(){
        int var=20;

        System.out.println(var);        
        System.out.println(this.var);   
    }
}

해당 클래스를 실행하는 다음과 같은 클래스가 있다고 할때

public class Driver{
    public static void main(String[] args){
        Penguin pororo=new Penguin();
        pororo.test();
    }
}

결과는 어떻게 나올것인가?

더보기

정답

20

10

그 이유는 test메소드에서의 var과 this.var는 차이가 있기 때문이다. var는 지역변수의 값이지만 this.var는 Penguin클래스의 var를 지칭하기 때문이다. 요약하면 다음과 같이 정리가 된다.

  • 동일한 변수명으로 사용시 지역변수가 우선시 사용이 된다.
  • this키워드는 클래스의 변수의 값에 해당된다.

8. super 연산자

해당 super연산자는 바로 위의 상위클래스의 메소드나 변수를 사용하기 위해 사용되는 키워드이다.

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

class Bird extends Animal{
    void method(){
        super.method();
        System.out.println("Bird");
    }
}

class Penguin extends Bird{
    void method(){
        super.method();
        System.out.println("Penguin");
    }
}

해당 클래스에서 method라는 메소드를 오버라이딩 했다고 가정했을때 이를 실행하는 코드는 다음과 같다.

public class Driver {
    public static void main (String[] args){
        Penguin pororo=new Penguin();
        pororo.method();
    }
}

그러면 결과값은 어떻게 나올 것인가?

더보기

결과값

Animal

Bird

Penguin

super 키워드는 처음 실행하는 우선순위를 가지기 때문이다. 그렇다면 만약 Penguin클래스에서 Animal클래스의 method()를 바로 사용이 가능할까? 답은 불가능하다. 해당 키워드를 사용하기 위해서는 super.super.method()와 같이 사용되어야 하는데 JAVA에서는 이를 허용하지 않고 있다.

 

해당 실행 소스 코드 및 요약은 다음 Git에 올려놨습니다. 참고하시는데 도움되길 바랍니다.

(계속 업데이트 예정입니다.)

https://github.com/Chaeson/studyJava_FROG

 

Chaeson/studyJava_FROG

자바 개구리책 공부. Contribute to Chaeson/studyJava_FROG development by creating an account on GitHub.

github.com

* chapter4 패키지 내

1. 추상클래스(Abstract Class) : abstractMethod01~02

2. 인터페이스(Interface): Interface

3. 생성자(Constructor): constructor01~03

4. 스태틱 블록(Static Block): staticBlock

5. final 연산자: finalClass/ finalValue/ finalMethod

6. instanceOf 연산자: instanceOf01~03

7. this 연산자: This

8. super 연산자: Super

* 참고자료: 스프링 입문을 위한 자바 객체지향의 원리와 이해