final 키워드는 이름 그대로 끝! 이라는 뜻이다. 변수에 final이 붙으면 더이상 값을 변경할 수 없다. 정의를 모르는게 아니고 약간 헷갈릴 수 있는 부분들이나 알아두면 코드에 더 도움이 되는것들을 적어봤다.
다음 코드를 보자.
Final
public class Final {
private final int value = 10;
public Final(int value) {
this.value = value;
}
}
이 코드는 에러를 발생시킨다. 왜냐하면 'value'라는 변수에 final 키워드가 붙었고 선언과 동시에 초기화를 해줬다. 그럼 그 이후에 어디서도 이 값을 변경할 수 없는데 생성자에서 받은 값을 대입하려고 하고 있기 때문이다. 그래서 저 코드는 다음과 같이 수정할 수 있다.
public class Final {
private final int value;
public Final(int value) {
this.value = value;
}
}
value라는 변수를 final로 선언했지만 초기화하지 않은 경우 생성자를 통해서 초기화하든 어디서든 딱 한번은 초기화를 할 수 있다.
그럼 여기서 다음 코드를 보자.
Final
public class Final {
private final int value = 10;
public int getValue() {
return value;
}
}
FinalMain
public class FinalMain {
public static void main(String[] args) {
Final f = new Final();
Final f2 = new Final();
Final f3 = new Final();
System.out.println("f = " + f.getValue());
System.out.println("f2 = " + f2.getValue());
System.out.println("f3 = " + f3.getValue());
}
}
지금 이렇게 Final 이라는 클래스가 있고 그 클래스의 인스턴스 변수 value는 final로 선언된 변수이다.
이 클래스의 인스턴스를 3개 만들고 해당 인스턴스가 가지는 value값을 출력해보면 다음 결과가 나온다.
실행결과:
f = 10
f2 = 10
f3 = 10
그리고 이 세개의 인스턴스도 마찬가지고 앞으로 생성될 모든 인스턴스 역시 value라는 값은 무조건 10일것이다.
그럼 인스턴스가 새로 생성될 때마다 힙 영역에 그 인스턴스가 올라가는데 메모리 낭비가 되지 않을까? 맞다. 그래서 이럴 때 사용하면 좋은 것이 바로 static이다.
static final
왜냐하면 static으로 선언된 변수를 생각해보면 static 영역에 들어가고 오로지 한 개만 존재한다. 그리고 값은 변하지 않는다 왜냐? final로 선언했으니까.
이 말은 필드에 final + 필드 초기화를 사용하는 경우 static을 붙여서 사용하는 것이 효과적이다.
그리고 이렇게 static final로 선언하면 언제라도 변경되지 않는 유일한 수가 되는데 이를 Constant(상수)라고 한다.
자바에서는 상수를 선언할 때 관례가 있는데 모두 대문자를 사용하고 구분은 _로 한다. 다음 코드가 그 예시가 되겠다.
public class Final {
public static final double PI = 3.14;
public static final int MAX_USER = 10000;
}
참조형에 대한 final
그리고 또 한가지 헷갈리는 것은 참조형 변수에 final 키워드가 붙는 경우에는 해당 변수의 참조값을 더 이상 바꿀 수 없다는 것이지 참조하고 있는 인스턴스, 배열, 리스트 등등의 내부값은 변경이 가능하다.
난 이게 항상 헷갈렸는데 다음 코드를 보자.
Final
public class Final {
private int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
FinalMain
public class FinalMain {
public static void main(String[] args) {
final Final f = new Final();
System.out.println(f.getValue());
f.setValue(20);
System.out.println(f.getValue());
}
}
Final 클래스의 인스턴스 변수 value는 final이 아니다. 그리고 main() 메서드에서 Final 인스턴스를 생성할 때 변수에 final이 붙었다.
그러면 당연히 초기화 한 이후로 f라는 변수는 다른 참조값을 가질 수 없고 딱 저 인스턴스가 가지는 참조값만 가질 수 있다.
그러나, f가 가지는 참조값을 쭉 따라가면 인스턴스가 나올것인데 그 인스턴스의 변수를 못바꾸는게 아니다. 그래서 실제로 실행결과는 다음과 같다.
실행결과:
0
20
다른 예시도 보자. 다음은 ArrayList()에 대한 참조값을 final로 받는 변수다. 이 변수에 당연히 다른 ArrayList() 참조값을 넣으려면 에러가난다. 다음이 그 화면인데 그렇다고 한들 이 list가 참조하는 참조값을 따라가서 List에 원소를 추가하고 빼는것에는 아무런 문제가 없다. (new로 선언하는 것은 참조형 = 힙 영역에 메모리를 차지 = 참조값을 참조 변수에 알려줌)
그저 참조값만 더 이상 변경이 불가능한 것이다. 다음을 보자.
public class FinalMain {
public static void main(String[] args) {
final List<Integer> list = new ArrayList<>();
System.out.println(list.size());
list.add(1);
list.add(2);
list.add(3);
System.out.println(list.size());
}
}
실제로 잘 추가가 되고 사이즈를 찍어보면 실행결과는 다음과 같다.
실행 결과:
0
3
클래스와 메서드에 final
클래스에 final이 붙으면 상속이 끝!
메서드에 final이 붙으면 오버라이딩 끝!
'JAVA의 가장 기본이 되는 내용' 카테고리의 다른 글
상속 (Part.2) (0) | 2024.03.28 |
---|---|
상속 (Part.1) (0) | 2024.03.28 |
자바 메모리 구조 ✨ (0) | 2024.03.27 |
캡슐화 (0) | 2024.03.26 |
Class 레벨의 접근제어자 (0) | 2024.03.26 |