자바에서 final 키워드는 '마지막', '변경 불가'를 의미합니다.
final은 객체지향의 캡슐화와 불변성을 지켜주고, 상속과 오버라이딩을 고정하여 예측 가능한 동작을 유지하도록 돕습니다.
변수, 메서드, 클래스에 따라 미세하게 다를 수 있지만, 크게는 더이상 변경하지는 못하도록 설정하는 것이라고 이해하면 될 것 같습니다.
final 변수는 단 한 번만 값을 할당할 수 있으며, 그 후에는 값을 변경할 수 없습니다. 상수를 정의하거나, 객체의 불변성을 보장하는 데 사용됩니다.
원시 타입 final 변수는 선언 시 또는 생성자에서 초기화해야 하며, 이후에는 값을 변경할 수 없습니다.
final int age = 30;
// age = 31; // 컴파일 에러!
final double PI = 3.14159;
객체 타입 final 변수는 참조하는 객체를 변경할 수 없지만, 객체 내부의 상태는 변경할 수 있습니다. 즉, person 변수가 다른 Person 객체를 참조하도록 할 수는 없지만, 객체의 age 속성은 변경할 수 있습니다.
불변 객체는 객체 타입 final 변수와 함께 사용하면 객체의 불변성을 완벽하게 보장할 수 있습니다. 불변 객체는 생성 후 내부 상태를 변경할 수 없는 객체를 의미합니다.
final String name = "John Doe";
// name = "Jane Doe"; // 컴파일 에러!
final Person person = new Person("Alice", 25);
// person = new Person("Bob", 30); // 컴파일 에러!
person.setAge(26); // final 변수가 참조하는 객체의 상태는 변경 가능합니다.
클래스 필드에 final을 붙이면 클래스의 모든 인스턴스에서 해당 필드의 값이 동일하게 유지됩니다. 인스턴스 final 변수는 각 인스턴스마다 다른 값을 가질 수 있지만, 한 번 초기화된 후에는 변경할 수 없습니다.
class Circle {
final double PI = 3.14159; // 인스턴스 상수
double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getArea() {
return PI * radius * radius;
}
}
메서드 내에서 인자의 값이 변경되지 않음을 보장하며, 코드의 가독성을 높이는 데 도움이 됩니다.
void printName(final String name) {
// name = "Jane Doe"; // final 인자의 값을 변경할 수 없습니다.
System.out.println("Name: " + name);
}
final 메서드는 하위 클래스에서 오버라이드할 수 없습니다.
메서드의 구현이 변경되는 것을 방지하고, 상속 구조에서 특정 메서드의 동작을 고정하는 데 사용됩니다.
class Animal {
final void eat() {
System.out.println("Animal is eating");
}
}
class Dog extends Animal {
// @Override
// void eat() { // final 메서드를 오버라이드할 수 없습니다.
// System.out.println("Dog is eating");
// }
}
final 클래스는 상속할 수 없으며, 클래스의 기능 확장을 제한하고, 클래스의 구현이 변경되는 것을 방지하는 데 사용됩니다. String 클래스가 대표적인 final 클래스입니다.
final class MathUtils {
// 수학 관련 유틸리티 메서드
}
// class AdvancedMathUtils extends MathUtils { // final 클래스를 상속할 수 없습니다.
// }
static은 클래스의 멤버(변수, 메서드, 블록)에 적용되어, 해당 멤버가 클래스의 인스턴스가 아닌 클래스 자체에 속하도록 만듭니다.
static 변수는 클래스의 모든 인스턴스에서 공유하는 변수입니다. 클래스가 메모리에 로드될 때 한 번 초기화되며, 모든 인스턴스에서 동일한 값을 갖습니다.
class Counter {
static int count = 0; // 클래스 변수
public Counter() {
count++; // 인스턴스가 생성될 때마다 count 증가
}
public static int getCount() {
return count; // static 메서드에서는 static 변수에 접근할 수 있습니다.
}
}
public class Main {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();
System.out.println(Counter.getCount()); // 3 (모든 인스턴스가 공유하는 count 값)
}
}
클래스의 인스턴스 없이 호출할 수 있는 메서드입니다. static 메서드 내부에서는 인스턴스 변수나 인스턴스 메서드에 접근할 수 없습니다.
class MathUtils {
static int add(int a, int b) {
return a + b; // static 메서드에서는 static 변수만 사용할 수 있습니다.
}
}
public class Main {
public static void main(String[] args) {
int sum = MathUtils.add(5, 3); // 객체 생성 없이 static 메서드 호출
System.out.println("Sum: " + sum); // Sum: 8
}
}
static 블록은 클래스가 메모리에 로드될 때 한 번 실행되는 코드 블록입니다. static 변수를 초기화하거나, 클래스 로딩 시 필요한 작업을 수행하는 데 사용됩니다.
class MyClass {
static int value;
static {
System.out.println("Static block is executed");
value = 10; // static 변수 초기화
}
public MyClass() {
System.out.println("Constructor is executed");
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass(); // "Static block is executed"가 먼저 출력됨
System.out.println("Value: " + MyClass.value); // Value: 10
}
}
static 키워드는 객체지향 프로그래밍의 기본적인 원칙과 충돌할 수 있기 때문에, 사용이 적합한 경우에만 쓰는 것이 좋습니다.
클래스 레벨(static)의 상수를 정의하고(final), 모든 인스턴스가 동일한 값을 공유(static)해야 할 때
public class Constants {
// 클래스 상수 (static final)
static final int MAX_USERS = 1000;
// 인스턴스 상수 (final)
final String instanceId;
public Constants(String id) {
this.instanceId = id; // 생성자에서 초기화
}
}
1. final
메모리 위치는 힙 영역(Heap)입니다.
인스턴스가 생성될 때마다 새로운 메모리가 할당되고, 객체마다 별도로 저장되므로 메모리 효율성이 낮을 수 있습니다.
2. static
메모리 위치는 메서드 영역(Method Area)이고, 클래스 로딩 시 한 번만 저장되니다. 프로그램 종료 시까지 메모리에 유지되며, Garbage Collector의 관리 대상이 아닙니다.
3. static final
메모리 위치는 메서드 영역(Method Area)이고, 클래스 로딩 시 한 번만 저장됩니다. 메모리 효율성과 데이터 일관성을 보장합니다.
final, static, static final..... 어렵지 않다!!
전 어렵던데... 대단하시네요 ^^ static에 대해 알 수 있어서 좋았습니다 ^^ 감사합니다 😊👍