Java

[Java] final 키워드

HEY__ 2024. 9. 30. 15:08
728x90

이 글은 공부를 하면서 알게 된 내용들을 기록하는 글 입니다. 오류나 고쳐야 할 사항들이 있다면 지적 부탁드립니다!

✅ final 키워드란

`final` 키워드는 Java에서 세 곳에서 사용될 수 있습니다.

🔥 variable

`final` 키워드를 통해 선언된 변수는 다른 값(value)나 객체(object)로 재할당될 수 없습니다. 즉, 값이 변경될 수 없는 `상수(constants)`가 됩니다.

따라서 `final` 변수를 선언할 때 초기화를 진행해야 하며, 만일 그렇지 않은 경우는 생성자를 통해서만 초기화될 수 있습니다.

class Car {
    private final int fuel;

    Car() {
        this.fuel = 0;
    }
}

 

`final` 변수가 참조형(reference)인 경우, 즉, 객체의 주소를 저장하는 클래스, 배열, 컬렉션을 할당하는 변수인 경우는 조금 다릅니다.

아래와 같이 `ArrayList` 객체를 저장하는 경우, `ArrayList` 객체 자체를 재할당할 수는 없지만! 컬렉션 내에 있는 원소들을 변경할 수 있습니다.

final List<String> USER_LIST = new ArrayList<>();

 

`final` 변수의 네이밍 컨벤션은 대문자와 언더스코어(_)를 사용하여 단어를 구분하는 형식을 사용합니다.

 

🔥 method

`final` 키워드를 통해 메서드로 선언하게 되면 override를 할 수 없게 됩니다. 자식 클래스가 final 메서드를 상속받을 수는 있지만, override는 하지 못합니다.

 

`Object`, `Thread` 클래스에서 이러한 방법을 사용하는데, 클래스 확장 자체를 금지하진 않지만 몇몇 메서드에 대해서만 override를 방지하고 싶을 때 사용합니다.

`Thread` 클래스의 경우, `Thread` 클래스를 상속받아 사용자 정의 스레드 클래스를 만드는 것은 가능하지만, `isAlive()` 는 final로 선언하여 override를 막아 구현부를 수정하지 못하도록 했습니다.

 

클래스의 특정 메서드가 다른 메서드에 의해 호출된다면 `final method`로 선언하는 것을 고려해보는 것을 권장합니다.

그렇지 않으면 해당 메서드를 override 할 수 있게 되어 구현부가 달라지면, 예상하지 못했던 결과를 가져올 수 있기 때문입니다.

 

🔥 class

어떤 클래스던지 `final`로 선언하게 되면, 해당 클래스를 상속할 수 없습니다. 만일 상속을 시도하는 경우, compile time에 오류가 나게 됩니다.

 

final class는 다음과 같은 경우에 주로 사용됩니다. 

 

1️⃣ final 클래스는 상속이 되지 않기 때문에, 의도적으로 상속을 막기 위해서 사용되기도 합니다. Integer, Float과 같은 Wrapper class들을 예로 들 수 있습니다.

 

2️⃣ String과 같은 불변(immutable) 클래스를 만들기 위해서 사용됩니다. final 키워드를 사용하지 않으면 해당 클래스를 불변으로 만들 수 없습니다.

만일 String 클래스가 final 클래스가 아니었다면, String 클래스를 상속받아 메서드를 오버라이드가 가능해질 것입니다.

구현부의 불변성이 보장되지 않기 때문에 다른 곳에서의 String 클래스의 동작이 일정하다고 보장할 수 없게 됩니다.

String 클래스는 Java 코드에서도 많이 사용되기 때문에 final 클래스로 선언함으로서 불변 클래스로 사용합니다.

 

여기에서 유의할 점은 `final class`라고 해서, 그 클래스의 객체가 immutable(불변)인 것은 아닙니다.

객체의 `필드(field)`는 자유롭게 변경할 수 있습니다. 다만, 클래스를 상속할 수 없어 메서드의 구현부를 변경할 수 없다(override 불가)는 뜻입니다.

 


✅ final 키워드의 이점

1️⃣ 불변성(immutability) 보장

변수나 reference(참조)가 final로 선언되어 한 번 할당되면, 그 값은 변경하지 못합니다. 이것은 데이터가 실수로 혹은 악의적인 수정으로부터 보호되어 불변성을 보장할 수 있습니다.

 

2️⃣ 성능 향상

JVM(Java Virtual Machine)이 final을 사용한 곳에 대해 값이나 참조(reference)가 변하지 않음을 알고, 코드를 더 효과적으로 최적화하여 성능을 향상시킬 수 있습니다.

 

3️⃣ 코드를 더 쉽게 이해할 수 있음

변수, 메서드, 클래스를 final로 선언함으로 인해 개발자들이 코드를 더 쉽고 명확하게 이해할 수 있습니다. value(값)나 reference가 final로 표시되어 있으면, 변하지 않음이 명확하기 때문에 코드를 분석하고 디버깅 할 때 더 편해집니다.

 

4️⃣ 코드 재사용 유도

메서드를 final로 선언하면, 자식 클래스에서 override하는 것을 막을 수 있습니다. 이렇게 하면 부모 클래스의 메서드 구현을 재사용하도록 강제함으로써 중복을 줄이고 코드 재사용을 촉진할 수 있습니다.

 

5️⃣ 보안 강화

final을 사용하면 중요한 데이터나 동작이 악의적인 코드에 의해 수정되는 것을 방지하여 보안을 강화할 수 있습니다.

 

 

결론적으로 final keyword는 코드의 퀄리티를 개선하고 프로그램의 특정 부분이 수정되지 않도록 보장하는데에 유용합니다. 변수, 클래스, 메서드를 final로 선언함으로서 안전하고, 견고하며 유지보수가 용이한 코드를 작성할 수 있습니다.


✅ compile 과정에서의 final 키워드

Java 컴파일러는 final 변수에 대해 static 변수와 같은 최적화를 진행하지 않습니다.

다만, final 적용을 통한 규칙이 올바른지, 컴파일러가 적용하는 검증 규칙을 적용합니다.

 

컴파일 과정에서 컴파일러가 final 키워드에 대해 처리하는 방법은 다음과 같습니다.

 

1️⃣ `final` 변수에 대해 상수로 취급

`final`로 선언된 변수는 초기화 된 후 값이 변경되지 않는 상수로 취급됩니다. 컴파일러는 final 변수를 Constant pool(상수 풀)에 넣거나, 상수로 치환하는 최적화를 수행할 수 있습니다.

 

2️⃣ 초기화 여부 검증

`final` 변수는 선언 단계 또는 생성자에서 초기화 되어야 합니다.

컴파일러가 `final 변수`가 제대로 초기화 되었는지 여부를 확인하고, 그렇지 않으면 컴파일 에러를 발생시킵니다.

 

3️⃣ 성능 최적화

컴파일 시 상수로 취급되기 때문에, `final 변수`를 참조하는 곳에서 치환이 이루어질 수 있습니다.

즉, 변수 자체가 아닌 값으로 대체되어 컴파일 된 코드에서 성능 최적화가 일어날 수 있습니다.

 

특히, static final로 선언된 변수는 컴파일 타임 상수(Compile-Time Constant)로 간주되어, 코드 내에서 해당 상수가 사용될 때마다 그 값으로 인라인될 가능성이 큽니다.

 

 

 

 


✅  참고 자료 & 링크

- Final keyword in Java - Java Spring Decoded

- final Keyword in Java - GeeksforGeeks

- the "final" keyword in Java | Baeldung

- Java 변수의 기본형 & 참조형 타입

 

 

 

728x90