[Java 개념노트 25] final 키워드 이해하기
안녕하세요. Java 개념노트 시리즈 스물다섯 번째 글입니다.
지난 글에서는 클래스가 함께 공유하는 값과 기능을 만들 때 사용하는 static 키워드에 대해 정리했습니다. 이번 글에서는 값을 변경하지 못하게 막을 때 사용하는 final 키워드에 대해 알아보겠습니다.
final은 자바에서 중요한 키워드입니다. 변수에 붙이면 값을 다시 바꿀 수 없고, 메서드에 붙이면 오버라이딩을 막을 수 있고, 클래스에 붙이면 상속을 막을 수 있습니다. 처음에는 “변경 금지”라는 큰 의미로 이해하면 좋습니다.
1. final이란?
final은 한 번 정해진 값을 다시 변경하지 못하게 막는 키워드입니다.
변수, 필드, 메서드, 클래스에 사용할 수 있으며 어디에 붙느냐에 따라 의미가 조금씩 달라집니다.
쉽게 말하면final은 “이제 더 이상 바꾸지 마라”라는 의미를 가진 키워드입니다.
2. final을 사용할 수 있는 곳
final은 여러 위치에 사용할 수 있습니다.
| 사용 위치 | 의미 | 예시 |
| 변수 | 값을 다시 대입할 수 없습니다. | final int number = 10; |
| 필드 | 객체의 필드 값을 한 번만 정할 수 있습니다. | final String name; |
| 메서드 | 자식 클래스에서 오버라이딩할 수 없습니다. | final void print() |
| 클래스 | 다른 클래스가 상속할 수 없습니다. | final class Member |
3. final 변수
변수에 final을 붙이면 한 번 값을 저장한 뒤 다시 변경할 수 없습니다.
public class Main {
public static void main(String[] args) {
final int number = 10;
System.out.println(number);
}
}
실행 결과는 다음과 같습니다.
10
number 변수는 final로 선언되었기 때문에 이후에 다른 값을 다시 넣을 수 없습니다.
4. final 변수 값 변경 시도
final 변수에 값을 다시 대입하려고 하면 오류가 발생합니다.
public class Main {
public static void main(String[] args) {
final int number = 10;
number = 20; // 오류 발생
}
}
number는 이미 10으로 초기화되었습니다. 따라서 20을 다시 저장할 수 없습니다.
주의final 변수는 한 번 값이 정해지면 다시 대입할 수 없습니다.
5. final 변수는 반드시 초기화해야 한다
final 변수는 사용하기 전에 반드시 값이 정해져야 합니다. 선언과 동시에 초기화할 수도 있고, 나중에 한 번만 값을 넣을 수도 있습니다.
선언과 동시에 초기화
final int number = 10;
나중에 한 번 초기화
final int number;
number = 10;
단, 한 번 값이 들어간 뒤에는 다시 값을 변경할 수 없습니다.
6. final과 상수
상수는 한 번 정하면 바뀌지 않는 값입니다. 예를 들어 원주율, 최대 로그인 시도 횟수, 기본 할인율처럼 프로그램 전체에서 고정적으로 사용하는 값을 상수로 만들 수 있습니다.
final double PI = 3.141592;
final int MAX_LOGIN_COUNT = 5;
상수 이름은 보통 대문자와 밑줄을 사용해서 작성합니다.
| 일반 변수 | 상수 |
int count |
final int MAX_COUNT |
double rate |
final double DISCOUNT_RATE |
7. static final 상수
상수를 만들 때는 static final 조합을 자주 사용합니다.
class AppConfig {
static final String APP_NAME = "GWDEVELBlog";
static final int MAX_USER_COUNT = 100;
}
static은 클래스가 공유한다는 의미이고, final은 값을 바꿀 수 없다는 의미입니다. 따라서 static final은 클래스 전체에서 공유하는 변경 불가능한 값이라고 볼 수 있습니다.
System.out.println(AppConfig.APP_NAME);
System.out.println(AppConfig.MAX_USER_COUNT);
핵심 포인트
프로그램 전체에서 함께 사용하는 고정값은 static final 상수로 만드는 경우가 많습니다.
8. final 필드
클래스의 필드에도 final을 붙일 수 있습니다. final 필드는 객체가 생성될 때 한 번 값이 정해지면 이후 변경할 수 없습니다.
class Member {
final String id;
Member(String id) {
this.id = id;
}
}
id는 회원의 고유 아이디라고 가정할 수 있습니다. 회원 객체가 만들어질 때 아이디가 정해지고, 이후에는 바뀌면 안 되므로 final을 붙일 수 있습니다.
9. final 필드는 생성자에서 초기화할 수 있다
final 필드는 선언할 때 바로 초기화하거나, 생성자에서 초기화할 수 있습니다.
public class Main {
public static void main(String[] args) {
Member member = new Member("user01");
System.out.println(member.id);
}
}
class Member {
final String id;
Member(String id) {
this.id = id;
}
}
실행 결과는 다음과 같습니다.
user01
id 필드는 생성자에서 한 번 초기화됩니다. 이후에는 다른 값으로 바꿀 수 없습니다.
10. final 필드 변경 시도
final 필드를 생성 후 다시 바꾸려고 하면 오류가 발생합니다.
Member member = new Member("user01");
member.id = "user02"; // 오류 발생
id는 final 필드이므로 이미 값이 정해진 뒤에는 다시 대입할 수 없습니다.
11. final 참조 변수
객체를 가리키는 참조 변수에도 final을 붙일 수 있습니다. 이 경우 참조 변수는 다른 객체를 다시 가리킬 수 없습니다.
final Member member = new Member("user01");
member = new Member("user02"); // 오류 발생
member 참조 변수는 처음 생성한 객체만 가리킬 수 있습니다. 다른 객체를 새로 대입할 수 없습니다.
12. final 참조 변수의 객체 내부 값은 바꿀 수 있을까?
여기서 중요한 점이 있습니다. final 참조 변수는 다른 객체를 다시 가리킬 수 없다는 뜻이지, 객체 내부의 모든 값이 무조건 바뀌지 않는다는 뜻은 아닙니다.
class Product {
String name;
int price;
Product(String name, int price) {
this.name = name;
this.price = price;
}
}
public class Main {
public static void main(String[] args) {
final Product product = new Product("키보드", 50000);
product.price = 45000;
System.out.println(product.price);
}
}
실행 결과는 다음과 같습니다.
45000
product 변수는 final이므로 다른 객체를 다시 가리킬 수 없습니다. 하지만 Product 객체의 price 필드는 final이 아니므로 값을 변경할 수 있습니다.
주의
참조 변수에 final을 붙이면 참조 대상 변경이 막히는 것이지, 객체 내부 값 변경이 항상 막히는 것은 아닙니다.
13. final 배열
배열도 참조 자료형입니다. 따라서 final 배열은 다른 배열로 다시 대입할 수 없지만, 배열 안의 요소는 변경할 수 있습니다.
public class Main {
public static void main(String[] args) {
final int[] numbers = {1, 2, 3};
numbers[0] = 100;
System.out.println(numbers[0]);
}
}
실행 결과는 다음과 같습니다.
100
배열 변수 numbers는 다른 배열을 가리킬 수 없지만, numbers[0] 값은 변경할 수 있습니다.
final int[] numbers = {1, 2, 3};
numbers = new int[]{4, 5, 6}; // 오류 발생
14. final 매개변수
메서드의 매개변수에도 final을 붙일 수 있습니다. final 매개변수는 메서드 안에서 다시 값을 대입할 수 없습니다.
public static void printNumber(final int number) {
System.out.println(number);
// number = 20; // 오류 발생
}
이 방식은 매개변수 값이 메서드 안에서 실수로 바뀌는 것을 막고 싶을 때 사용할 수 있습니다. 다만 모든 매개변수에 무조건 붙이기보다는 필요한 경우에 사용하는 편이 좋습니다.
15. final 메서드
메서드에 final을 붙이면 자식 클래스에서 해당 메서드를 오버라이딩할 수 없습니다.
class Parent {
final void printMessage() {
System.out.println("변경할 수 없는 메서드입니다.");
}
}
상속과 오버라이딩은 뒤에서 더 자세히 다룰 예정입니다. 지금은 final 메서드는 자식 클래스에서 재정의할 수 없다 정도로 이해하면 됩니다.
16. final 클래스
클래스에 final을 붙이면 다른 클래스가 이 클래스를 상속할 수 없습니다.
final class Member {
String id;
}
final class는 더 이상 확장되지 않도록 막고 싶을 때 사용합니다. 대표적으로 자바의 String 클래스도 final 클래스입니다.
미리 보기
상속을 배운 뒤에는 final class와 final method의 의미가 더 명확해집니다. 이번 글에서는 “상속과 재정의를 막는다” 정도로 기억하면 충분합니다.
17. final과 static final 차이
final과 static final은 비슷해 보이지만 용도가 다릅니다.
| 구분 | 의미 | 예시 |
final |
한 번 정해진 값을 변경할 수 없음 | 객체마다 다른 고유 id |
static final |
클래스가 공유하는 변경 불가능한 값 | 상수, 설정값, 고정 메시지 |
class Member {
final String id;
static final String SERVICE_NAME = "GWDEVELBlog";
Member(String id) {
this.id = id;
}
}
id는 객체마다 다를 수 있지만 한 번 정해지면 바뀌면 안 됩니다. 반면 SERVICE_NAME은 모든 객체가 공유하는 고정값입니다.
18. final 전체 예제
이번에는 final과 static final을 함께 사용하는 예제를 보겠습니다.
public class Main {
public static void main(String[] args) {
User user = new User("user01", "건");
user.printInfo();
System.out.println(User.SERVICE_NAME);
}
}
class User {
static final String SERVICE_NAME = "GWDEVELBlog";
final String id;
String name;
User(String id, String name) {
this.id = id;
this.name = name;
}
void printInfo() {
System.out.println("아이디: " + id);
System.out.println("이름: " + name);
}
}
실행 결과는 다음과 같습니다.
아이디: user01
이름: 건
GWDEVELBlog
id는 사용자마다 다른 고유값이므로 final 필드로 만들었습니다. SERVICE_NAME은 모든 사용자가 공유하는 고정값이므로 static final 상수로 만들었습니다.
19. final 사용 시 자주 하는 실수
final을 처음 배울 때는 아래와 같은 실수를 자주 합니다.
| 실수 | 문제점 | 해결 방법 |
| final 변수에 값을 다시 대입 | 컴파일 오류 발생 | final 값은 한 번만 대입합니다. |
| final 필드를 초기화하지 않음 | 객체 생성 시 값이 정해지지 않음 | 선언 시 또는 생성자에서 초기화합니다. |
| final 참조 변수면 객체 내부도 전부 불변이라고 생각 | 참조 변경과 내부 값 변경을 혼동 | final 참조는 재대입만 막는다고 이해합니다. |
| 상수를 소문자로 작성 | 상수임을 알아보기 어려움 | MAX_COUNT처럼 대문자와 밑줄 사용 |
| 모든 곳에 무조건 final 사용 | 코드 작성이 불필요하게 복잡해질 수 있음 | 변경되면 안 되는 값에 명확히 사용합니다. |
20. 직접 연습해보기
아래 코드를 직접 작성하고 실행해보세요.
public class Main {
public static void main(String[] args) {
Product product = new Product("P001", "키보드", 50000);
product.printInfo();
product.name = "기계식 키보드";
product.price = 45000;
product.printInfo();
System.out.println(Product.STORE_NAME);
System.out.println(Product.MAX_DISCOUNT_RATE);
}
}
class Product {
static final String STORE_NAME = "GWDEVEL Store";
static final double MAX_DISCOUNT_RATE = 0.3;
final String productCode;
String name;
int price;
Product(String productCode, String name, int price) {
this.productCode = productCode;
this.name = name;
this.price = price;
}
void printInfo() {
System.out.println("상품코드: " + productCode);
System.out.println("상품명: " + name);
System.out.println("가격: " + price);
}
}
실행 결과는 다음과 같습니다.
상품코드: P001
상품명: 키보드
가격: 50000
상품코드: P001
상품명: 기계식 키보드
가격: 45000
GWDEVEL Store
0.3
productCode는 final 필드이므로 한 번 정해지면 바뀌지 않습니다. 반면 name과 price는 final이 아니므로 수정할 수 있습니다.
21. 이번 글 정리
이번 글에서는 값을 변경하지 못하게 막는 final 키워드에 대해 정리했습니다. 핵심 내용은 다음과 같습니다.
final은 변경을 막는 키워드이다.- final 변수는 한 번 값이 정해지면 다시 대입할 수 없다.
- final 필드는 선언 시 또는 생성자에서 초기화해야 한다.
- 상수는 보통
static final로 만든다. - 상수 이름은 보통 대문자와 밑줄로 작성한다.
- final 참조 변수는 다른 객체로 재대입할 수 없다.
- final 참조 변수라도 객체 내부 필드가 final이 아니면 값 변경이 가능하다.
- final 배열은 다른 배열로 재대입할 수 없지만 배열 요소는 변경할 수 있다.
- final 메서드는 오버라이딩할 수 없다.
- final 클래스는 상속할 수 없다.
한 줄 요약final은 변수, 필드, 메서드, 클래스에 붙어 변경이나 확장을 제한하는 키워드입니다.
다음 글 예고
다음 글에서는 [Java 개념노트 26] 접근 제어자 이해하기라는 주제로 public, private, protected, default 접근 범위를 정리해보겠습니다.
GWDEVELBlog Java 개념노트 시리즈

'Computer Science > Java' 카테고리의 다른 글
| [Java]개념노트 27 캡슐화와 getter/setter 이해하기 (0) | 2026.06.10 |
|---|---|
| [Java] 개념노트 26 접근 제어자 이해하기 (0) | 2026.06.10 |
| [Java] 개념노트 24 static 키워드 이해하기 (0) | 2026.06.10 |
| [Java ] 개념노트 23 this 키워드 이해하기 (0) | 2026.06.10 |
| [Java ] 개념노트 22 생성자란 무엇인가? 객체를 만들 때 필드 값 초기화하기 (0) | 2026.06.09 |