1일 1개념정리 24.08.09.금 ~
큰 결정에 큰 동기가 따르지 않을 때도 있다. 하지만 큰 결심이 따라야 이뤄낼 수 있다.
무조건 무조건 1일 1개의 개념 정리하기 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#16. 자바 final
자바에서 보이는 이 final은 변수에서만 사용된다고 오해할 수 있는데, 아니다. 변수, 메소드, 클래스에 적용될 수 있으며, 각각 다른 의미를 가진다. 오늘은 final에 대해 알아봅시다.
일단 final은 한마디로 변경이 불가능하도록 만들어주는 키워드고 상황에 따라 다르게 활용된다 !!

일반 변수 final
일반변수에 final을 쓰면 그 변수의 값을 한 번 할당하면 이후에는 변경할 수 없다. 마치 상수로 취급할 때 유용하다.
- 로컬 변수 : 메소드 내부에서 정의된 변수에 final을 사용할 수 있다.
- 필드 : 클래스의 멤버 변수(필드)에도 final을 사용할 수 있다.
public class ex {
public static void main(String[] args) {
final int NUMBER = 10; // 상수 선언 및 초기화
// NUMBER = 20; // 오류 : NUMBER는 final로 선언되어 값을 변경할 수 없음
}
}
참조 변수 final
객체에 대한 참조 변수를 final로 선언하면, 참조 자체는 변경할 수 없지만, 참조된 객체의 상태는 변경할 수 있다. (중요)
class MyClass {
int value = 10;
}
public class ex {
public static void main(String[] args) {
final MyClass obj = new MyClass();
obj.value = 20; // 객체의 상태는 변경 가능
// obj = new MyClass(); // 오류 : 참조를 다른 객체로 변경할 수 없음
}
}
메소드 final
final을 "메소드에" 쓰면 그 메소드는 서브클래스에서 오버라이딩(재정의)할 수 없다. 이는 메소드의 동작이 변경되지 않도록 보장할 때 유용하다.
class ParentClass {
public final void showMessage() // final 설정
{
System.out.println("This is a final method.");
}
}
class ChildClass extends ParentClass {
// public void showMessage() {
// System.out.println("Attempting to override.");
// }
// 오버라이딩 못함
}
클래스 final
final을 "클래스에" 쓰면 그 클래스를 상속할 수 없다. 즉, 이 클래스는 서브클래스를 가질 수 없다. 특정 클래스의 기능을 변경하지 않도록 보장할 때 사용된다.
final class FinalClass {
public void showMessage() {
System.out.println("This is a final class.");
}
}
// class ExtendedClass extends FinalClass {
// // 오류 : FinalClass는 final로 선언되어 상속할 수 없음
// }
정리
- 변수에 final : 변수의 값이 한 번 초기화된 이후 변경 불가능
- 메소드에 final : 해당 메소드는 서브클래스에서 오버라이딩 불가능 (재정의 X)
- 클래스에 final : 해당 클래스는 상속될 수 없게 된다.
그래서 다음과 같이 활용하면 좋다.
- 상수 값을 선언할 때 final을 사용하여 코드의 가독성과 유지보수성을 높인다.
- 변경하면 안 되는 메소드나 클래스를 final로 선언해, 코드의 안정성을 확보한다.
- 참조의 불변성을 보장하기 위해 final 참조 변수를 사용하는 것도 좋다.
C언어 const VS Java final 비교
읽다보면 C언어에서 말하는 const랑 비슷한 느낌이 든다. 비교해봅시다. 변수 사용에 있어서는 비슷한 맥락인데, Java에서 좀 더 다채롭게 사용하는 느낌이다.
final
- 변수 : 한 번 초기화된 후에는 값이 변경되지 않음을 보장
- 메소드 : 오버라이딩을 금지
- 클래스 : 상속을 금지
const
- 변수 : 해당 변수의 값이 수정되지 않음을 보장
- 포인터 : 포인터 자체가 가리키는 값을 수정하지 못하게 하거나, 포인터가 가리키는 주소 자체를 변경하지 못하게 할 수 있다.
case 1. 포인터가 가리키는 값이 변경되지 않게
const int *ptr = &number;
// *ptr = 20; // 오류
case 2. 포인터 자체를 변경하지 못하게
int *const ptr = &number;
// ptr = &otherNumber;
case 3. 포인터도 변경하지 못하고, 가리키는 값도 변경하지 못하게
const int *const ptr = &number;
// *ptr = 20; // 오류
// ptr = &otherNumber; // 오류
주의사항
- final로 선언된 일반변수는 반드시 초기화되어야 함. ★ ★ ★
- final로 선언된 참조변수는 객체의 필드는 변경될 수 있음을 유의해야 함
- final로 선언된 메소드는 상속받는 클래스에서 오버라이딩할 수 없다.
- final 클래스는 상속할 수 없으므로, 그 클래스를 확장하려는 시도 자체를 피해야함
여담 - Spring에선 왜 필드를 맨날 private final로 초기화하는 것 ??
예를들어.... 우린 다음과 같은 구조의 코드를 자주 본다.
@Service
public class MyService {
private final MyRepository myRepository;
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
Spring에서 위처럼 final로 쓰는 이유는 DI와 관련있다. 일단 기본적으로 앞선 맥락과 같다. final을 쓰면 해당 필드는 한 번 초기화된 후 변경할 수 없다. 이는 객체의 상태를 불변으로 만들어 의도치 않은 변경을 방지하고, 코드의 안정성을 높여준다. 그리고 DI 측면도 있다.
의존성 주입 에서의 안전성
- Spring에서 DI로 의존성을 주입받을 때, final로 선언된 필드를 사용하면 주입된 객체가 변경되지 않음을 보장할 수 있다.
- 위에 주의사항에서 언급했듯이 final은 반드시 초기화되어야한다. 그래서 생성자 주입을 써서 final 필드를 초기화하면 해당 필드가 반드시 생성자 호출시 초기화 되므로 NullPointerException을 피할 수 있다.
→ 결론적으로 안전한 DI가 가능해진 것이다 !!!!!
'1일 1개념정리 (24년 8월~12월) > Java' 카테고리의 다른 글
1일1개 (19) - static (0) | 2024.08.28 |
---|---|
1일1개 (18) - 상속과 구현 (0) | 2024.08.27 |
1일1개 (17) - 오버라이딩 vs 오버로딩 (0) | 2024.08.26 |
1일1개 (5) - 인터페이스 (2) | 2024.08.13 |
1일1개 (4) - JVM (2) | 2024.08.12 |