선언위치에 따른 변수의 종류 3가지 1. 클래스 변수, 2. 인스턴스 변수, 3. 지역변수에 대해 알아봅시다.
자바에서는 선언위치에 따라 변수의 종류가 달라집니다.
class Calculator {
// 1. 클래스 변수 - 원주율
static double PI = 3.14;
// 2. 인스턴스 변수 - 반지름
double radius = 0;
public double getAreaOfCircle() {
// 3. 지역 변수
double result = this.PI * this.radius * this.radius;
return result;
}
// 생성자 - 반지름을 넣어줍니다.
Calculator(int radius) {
this.radius = radius;
}
}
static 지시자
를 붙이며, 클래스가 메모리에 로딩될 때 생성됩니다.JVM 메모리 영역 중 method 영역
에 로드됩니다.
모든 클래스의 객체가 공통된 변수
를 공유하게됩니다.
이미 메모리에 올라가 있기 때문에 클래스의 객체를 생성하지 않고도 사용
가능합니다.
class Main {
public static void main(String[] args) {
// 클래스 변수는 클래스의 객체 생성없이 바로 사용가능합니다. - 3.14
System.out.println(Calculator.PI);
}
}
실제로 자바의 Math 클래스
의 PI는 static
으로 선언되어 있습니다.
이처럼 클래스 변수의 객체를 안만들어도 사용가능하다는 특징으로 인해 유틸성 라이브러리들을 static으로 선언후 클래스의 객체를 생성하지 않고 사용하고 있습니다.
클래스의 객체를 생성할 때 만들어집니다. JVM 메모리 영역중 Heap 영역
에 로드됩니다.
클래스의 객체마다 독립적인 값을 가질 수 있습니다.
블럭 내부에서 변수 선언문이 실행되었을 때 생성됩니다. 블럭이 종료되면 소멸됩니다.
JVM 메모리 영역중 Heap
영역에 로드됩니다.
사실, 이제부터가 진짜
HOXY 그런 생각안드시나요? 오 클래스 변수는 공유도 되고 전역으로 사용할 수 있고 객체 안만들고도 사용할 수 있는데 전부다 static 붙여서 클래스 변수로 사용하지뭐
결론은 안됩니다.
자바가 구동되는 JVM의 Runtime Data Area
는 총 5개로 구분됩니다.
1) PC Register - 현재 수행중인 JVM 명령어 저장
2) JVM stack - 호출된 메소드의 매겨변수, 지역변수, 리턴정보 저장
3) Native Method stack - 자바 외 C, C++로 구현된 정보 저장
4) Heap: 런타임 중 생성되는 객체들이 저장
5) Method: 전역변수, 정적변수, 메소드 정보 저장
문제는 Method 영역
에 저장된 정보는 가비지 컬렉션
의 대상이 되지 않으며, 프로그램이 종료될때 해제됩니다.
가비지 컬렉션에 대해 알아봅시다.
자바에서는 Garbage Collector가 주기적으로 더 이상 사용하지 않는 메모리를 수집하고 해제하는 역할을 합니다.
문제는 Heap영역에
런타임시 동적으로 할당되는 객체들은 해제가되는데, Method 영역
의 정보들은 계속해서 메모리를 점유하고 있다는 것입니다.
그래서 정리하면, 정말 필요한것만 static을 붙여서 클래스 변수로 선언합시다.
final
지시자는 자바에서 변할 수 없다는 의미로 사용됩니다.
만약 아래처럼 누군가가 PI(원주율)
의 값을 222로 바꾼다면, 원의 넓이는 잘못 계산됩니다.
class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator(2);
// 원의 넓이 계산 - 12.56
System.out.println(calculator.getAreaOfCircle());
// 클래스 변수 PI 값 변경
Calculator.PI = 222;
// 원의 넓이 계산 - 888
System.out.println(calculator.getAreaOfCircle());
}
}
class Calculator {
// 1. 클래스 변수 - 원주율
static double PI = 3.14;
// 2. 인스턴스 변수 - 반지름
double radius = 0;
public double getAreaOfCircle() {
// 3. 지역 변수
double result = this.PI * this.radius * this.radius;
return result;
}
// 생성자 - 반지름을 넣어줍니다.
Calculator(int radius) {
this.radius = radius;
}
}
변하지 않는 값이라면 final을 붙여 생성합니다.
static final double PI = 3.14;
정적 메소드는 클래스 변수만 접근 할 수 있다는 특징이 있습니다.
왜냐하면 접근하려고 해도 인스턴스 변수는 메모리에 올라와 있지 않을 수 있기 때문에 접근이 막혀있습니다.
그래서 좋은 방법은 함수의 파라미터 변수
과 클래스 변수
만 사용하는 것입니다.
자바 Math 유틸에서 각도를 라디안으로 바꾸는 함수 파라미터와, 클래스변수만 사용합니다.
메인 메소드는 가장 자주보는 정적 메소드중 하나입니다.
public static void main(String []args) {
}
그래서 메인메소드를 제출하는 알고리즘 시험에서는 static으로 전역변수 선언해서 클래스 내부에서 사용했습니다.
그리고,
여러가지 이유가 있겠지만, 제 생각에 메인 메소드를 정적 메소드로 만든 이유는 생성자 때문입니다.
왠만해서 메인 클래스에 생성자를 안넣지만, 정적 메소드가 아니라면, 생성자를 통해 객체를 생성해야하는데, 프로그래머가 생성자를 여러개 만들면 어떤것을 선택해야할지 모르기 때문입니다.
그냥 그렇다구
Math.PI
그렇지 않은 경우 static을 사용하면 쓸데없이 메모리를 점유한다.