[Java] static과 final 파헤치기

Loopy·2022년 11월 26일
1
post-thumbnail

1️⃣ Static

☁️ static 개념

static 으로 선언한 변수 및 함수, 클래스들은 JVM이 시작될때 Method Area(static 영역)으로 로딩된다. 이 영역은 프로그램이 종료될 때까지 메모리에 사라지지 않고 상주해있다.

참고로 컴파일러에 의해 변환된 클래스 파일( .class )의 바이트 코드들이 로드된다.

🔗 Method Area

Method Area 에서는, 클래스 로더로부터 전달받은 클래스들에서 타입 정보를 빼내서 저장한다.

🔖 메소드 영역 구성
1. Type Information (타입 정보)
2. Runtime Constant Pool (런타임 상수 풀)
3. Field Information (필드 정보)
4. Method Information (메서드 정보)
5. Class Variable (클래스 변수 = static 키워드 변수)

Runtime Constant Pool 에는 클래스의 변수, 함수, 상수, 클래스 자체 등의 물리적 주소가 symbol table 형태로 저장되어 있다.

☁️ static 클래스

static 클래스는 함수나 변수처럼 바이트 코드가 공유되는 개념이랑은 조금 다르며, 매번 다른 인스턴스 즉 다른 주소 공간을 가진 객체들이 생성된다.

단지 내부 클래스에 static을 붙이는 용도는 다음과 같다.

class A {
    private int a;
  
    static class B {  // 정적 내부 클래스 
    	    private int b;  
    }
    
    class B {  // 비정적 내부 클래스 
    	    private int b;  
    }
}

static이 붙지 않은 비정적 내부 클래스의 경우, 바깥 클래스에 대한 참조를 가지게 된다. 아래의 명령어를 통해 결과를 보면 Class BClassA 의 참조를 가지고 있는 것을 볼 수 있을 것이다.

🔖 javap
어떤 바이트코드 파일이 어디서부터 나왔으며, 어떠한 필드와 메소드를 갖고 있는 파일인지를 알려주는 기능을 수행(컴파일 된 클래스 파일의 명령열을 읽어낼 수 있다)

// javap -p build/classes/java/main/com/javabom/nested/Outer\$StaticInner.class

Compiled from "HelloWorld.java"
public class HelloWorld {
  public HelloWorld();
  public static void main(java.lang.String[]);
}

하지만 이로 인해 메모리 누수 관련해서 문제가 발생한다.

메모리 누수 문제

일반적으로 클래스(객체)는 더이상 사용되거나 참조되지 않는다면 가비지 컬렉터에서 힙의 메모리를 수거해간다.

하지만 내부에서 바깥의 참조를 가지고 있다면, 바깥 클래스가 더 이상 사용되지 않는데도 내부 클래스의 참조로 인해 가비지 컬렉터가 수거하지 못해서 바깥 클래스의 메모리 해제를 하지 못하는 경우가 발생하는 것이다.

따라서 내부 클래스와 외부 클래스가 독립적으로 사용된다면 정적 내부 클래스를 사용하도록 하자!

☁️ static 함수 및 변수 장단점

static 이 붙은 함수와 변수들은 처음 클래스가 메모리에 로드될때 생성되기 때문에 인스턴스를 생성하지 않아도 바로 사용할 수 있게 된다.

장점

  1. 객체를 생성하지 않고 바로 사용할 수 있으니, 속도가 빠르고 반복적으로 사용해야 할때 공유가 가능하다는 장점이 존재한다. 같은 기능을 매번 사용해야 하는데 인스턴스를 만들고 있을 필요가 없기 때문이다.

  2. 정적 팩터리 메서드를 활용하면 정적 메소드를 효율적으로 사용할 수도 있다.

단점

  1. 객체 지향에서 멀어진다.
    static 은 객체보다는 절자 지향적인 키워드이다.
    따라서 전역 변수를 선언할 때 사용하는데, 이는 객체 지향 프로그래밍의 캡슐화를 깨트린다. 한 객체가 가지고 있는 데이터들은 외부로 노출되어 의도치 않게 수정되는 일이 없어야 하는 것이 캡슐화의 핵심(데이터 보호)이기 때문이다.
    또한, 프로그램이 살아있는 내내 상주해 있기 때문에 객체의 생명 주기와 아무런 관련이 없어진다.

  2. 메모리 효율이 떨어질 수도 있다.
    자바의 가비지 컬렉터는 런타임 도중 동적으로 할당된 메모리에 대해서만 관리한다.(ex) Heap 영역에 생성된 객체들) 따라서, 만약 static 영역이 차지하는 메모리 공간이 매우 크다면 메모리 부족 현상이 나타날 수 있다. 프로그램이 끝날때 까지 없어지지 않기 때문이다.

☁️ static의 용도

Q. 만일 새로운 클래스를 작성한다고 할 때, 어떤 경우에 static 키워드를
사용해야 할까?

1. 내부 클래스 : 외부 클래스와 관계가 있지 않고 독립적일 경우에 사용한다.
2. 메서드 : 메서드 내에서 인스턴스 변수를 사용하지 않을때 사용한다.
3. 변수 : 여러 인스턴스들에서 공통적으로 같은 값을 유지하고 공유해야 할 때 사용한다.

2️⃣ Final

final이란?

변수나 메소드 또는 클래스를 변경 불가능하게 만드는 예약어이다. 정확히는, 변경 불가능한 객체가 아닌 재할당이 불가능하게 만드는 것을 의미한다.

원시(Primitive) 변수

해당 변수의 값은 변경 불가능하다.

참조(Reference) 변수

참조 변수가 힙 내의 다른 객체를 가리키도록 변경 할 수 없게 된다.
단, 현재 가리키고 있는 객체의 내부의 값은 final의 영향 밖에 있기 때문에 변경이 가능하다.

🔖 주의 사항
따라서 List에 final을 붙히더라도 add(), remove()와 같이 객체 내부의 값은 변경이 되지만 list = new ArrayList<>() 과 같이 재할당은 불가능하다.

메소드

해당 메서드를 오버라이드(overiding) 할 수 없게 된다.
즉, 상속 받은 클래스에서 해당 메서드를 수정해서 사용하지 못하도록 할 수 있다.

클래스

해당 클래스의 하위 클래스를 정의할 수 없다. 즉 상속을 금지할때 키워드를 붙히면 된다.
예를 들어, Integer 와 같은 Wrapper Class가 있다.

참고자료
IT/Java[Java] Static 키워드 바로 알고 사용하자
정적, 비정적 내부 클래스 알고 사용하기
정적 메소드, 너 써도 될까
java static class 의 특징 총정리
[자바 커맨드] javap는 무슨 기능을 하는 명령어일까?
자바(Java) 메모리 구조 / Runtime Data Area
[실무 면접 준비 - 4] 객체지향 & JVM (OOP & JVM)

profile
개인용으로 공부하는 공간입니다. 피드백 환영합니다 🙂

0개의 댓글