ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java - final
    Language/Java 2022. 9. 20. 17:34

    final

    final이란?

    위키피디아에 따르면 자바에서 사용하는 final은 다음과 같다.

     

    "자바 언어에서 final은 오직 한 번만 할당할 수 있는 entity를 정의할 때 사용된다. final로 선언된 변수가 할당되면 항상 같은 값은 값을 가진다. 만약 final 변수가 객체를 참조하고 있다면, 해당 객체의 상태가 바뀌어도 final 변수는 매번 동일한 내용을 참조한다."

     

    이러한 final 키워드는 변수(variable), 메서드(method), 클래스(class)에 사용될 수 있다. 이 final 키워드는 어떤 곳에 사용되느냐에 따라 다른 의미를 가진다.

    하지만, final 키워드를 붙이면 무언가를 제한한다는 의미를 가진다는 것이 공통적인 성격이다.

     

    변수(varibale)

    변수에 final을 붙이게 되면, 해당 변수는 수정할 수 없다는 의미를 가진다. 수정될 수 없기 때문에 초기화 값은 필수이다.

    수정할 수 없다는 범위는 그 변수가 가지고 있는 값에 한정한다. 즉, 다른 객체를 참조할 때 참조하는 객체 내부의 값은 변경할 수 있다는 것을 의미한다.

     

    public class Main {
        public static void main(String[] args) {
            final int value = 2;
            final Person person = new Person("홍길동", 20);
            
            System.out.println("value : " + value);
            System.out.println("person01 : " + person);
            
            // value = 10;   // 컴파일 에러
            person.setName("이순신");
            person.setAge(30);
            
            System.out.println("person02 : " + person);
            // person = new Person("강감찬", 40);   // 컴파일 에러
        }
    
    }
    
    class Person {
        private String name;
        private int age;
        
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }

    위 코드는 value라는 기본형 변수와 person이라는 참조형 변수를 하나씩 선언하고 값을 변경해보는 예제이다.

    결과적으로 기본형 변수는 값을 변경하지 못하고 참조형 변수는 참조하고 있는 객체의 내부의 값을 변경할 수 있지만, 가리키는 객체를 변경하지 못한다는 것을 알 수 있다.

     

     

    위 코드를 그림으로 나타내면 위와 같다.

    변수들은 stack 메모리에 저장된다. 이 변수들은 각각 값을 가지는데 기본형 변수인 value는 내부에 2라는 값을 가지고, 참조형 변수인 person은 참조하는 객체의 주소를 가진다.

    final 키워드를 사용한다는 것은 위 그림에 보이는 영역에만 값을 변경할 수 없다는 것을 의미한다. 그렇기 때문에, 기본형 변수라면 값을 변경하지 못하고, 참조형 변수라면 가리키는 객체를 변경하지 못하는 것이다.

    따라서, person 변수가 가리키고 있는 객체의 내부의 값은 final의 영향밖에 있기 때문에 변경이 가능한 것이다.

     

    위 예제는 final을 메서드 내부 변수에 선언했기 때문에 stack 메모리를 기점으로 제한하지만, 객체 내부 변수를 final로 선언한다면 해당 객체 메모리 내부에서 제한하는 것이 가능하다.

    그리고 배열 역시 객체이기 때문에 final을 통해 배열을 선언하더라도 내부의 값을 변경하는 것이 가능하다.

     

    변수를 final로 선언하고 초기화가 되기 전에 사용한다면 컴파일 에러를 발생시킨다. 각 변수 타입별 일반적인 초기화 방법은 아래와 같다.

     

    1. 메서드 내부 변수

    public class Main {
        public static void main(String[] args) {
            final int value01 = 2;   // 선언시 초기화
            fianl int value02;
            
            System.out.println("value01 : " + value01);
            
            value02 = 3;   // 사용하지 전 초기화
            System.out.println("value02 : " + value02);
        }
    }

     

    2. 객체 멤버 변수

    public class Main {
        public static void main(String[] args) {
            Person person = new Person("abc123@naver.com");
            System.out.println("person : " + person);
        }
    }
    
    class Person {
        private final String name = "홍길동";   // 변수 선언시 초기화
        
        private final int age;   // 초기화 block을 이용한 초기화
        {
            age = 10;
        }
        
        private final String email;
        
        public Person(String email) {
            this.email = email;
        }
        
    }

     

    3. static 변수

    class Person {
        private static final String DEFAULT_NAME = "홍길동";   // 변수 선언시 초기화
        
        private static final int DEFAULT_AGE;   // static 초기화 block을 이용한 초기화
        static {
            DEFAULT_AGE = 10;
        }
    
    }

     

    메서드(method)
    class person {
        private final String name;
        
        private final int age;
        
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        public void speech() {
            System.out.println("나는 " + name + "입니다.");
        }
    }
    
    class Korean extends Person {
        
        public Korean(String name, int age) {
            super(name, age);
        }
        
        @Override
        public void speech() {   // 메서드 재정의 불가능
            System.out.println("나는 " + name + " 이며, " + age + "살 입니다.");
        }
    }

    메서드에 final 키워드를 붙이면 override를 제한하게 된다. 특정 클래스를 상속하게 되면, 해당 클래스의 protected, public 접근 제어자를 가진 메서드를 상속해서 재구현을 할 수 있다. 즉, 동일한 메서드지만 다른 동작을 하도록 만들 수 있다는 의미이다.

    위 코드는 보면 Person 클래스에 speech() 메서드가 있다. speech() 메서드에 final 키워드를 사용하게 되면, Person 클래스를 상속받은 자식 클래스인 Korean 클래스에서 speech() 메서드를 재정의 할 수 없다.

    이렇게 자신이 만든 메서드를 변경할 수 없게끔 하고 싶을 때 사용되며, 시스템의 코어 부분에서 변경을 원치 않는 메서드에 많이 사용이 된다.

     

    클래스(class)
    final class Person {
        private final String name;
    
        private final int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    
    class Korean extends Person {   // Peron 클래스 상속 불가능
    }

    클래스에 final 키워드를 사용하게 되면, 해당 클래스는 최종 상태가 되어 더 이상 상속이 불가능하게 된다. 위 코드와 같이 final 키워드로 선언된 Person 클래스는 Korean 클래스에서 상속이 불가능하다.


    출처

    https://coding-factory.tistory.com/525

    https://sabarada.tistory.com/148

     

    728x90

    'Language > Java' 카테고리의 다른 글

    Java - Exception  (0) 2022.09.29
    Java - 추상 클래스 & 인터페이스  (0) 2022.09.21
    Java - '==' 연산자 와 'equals()'를 이용한 문자열 비교  (0) 2022.09.20
    Java - Wrapper Class  (0) 2022.09.20
    Java - Mutex & Semaphore & Monitor  (0) 2022.04.06

    댓글

Designed by Tistory.