static
static이란? 정적인, 움직이지 않는다는 뜻으로
Java에서 static 키워드를 사용하는 것은 메모리에 한번 할당되어 프로그램이 종료될 때 해제되는 것을 의미한다.
static으로 선언된 메서드나 변수는 자바 버추얼 머신에서 인스턴스 객체의 생성 없이 호출(사용)을 할 수 있다.
자바 프로그램을 실행하면 static으로 지정된 메서드를 찾아서 먼저 메모리에 할당시킨다.
static 키워드를 통해 생성된 정적 멤버들은 heap 영역이 아닌 static 영역에 할당된다.
static 영역에 할당된 메모리는 모든 객체가 공유하여 하나의 멤버를 어디서든지 참조할 수 있지만 GC(Garbarge Collector)의 관리 영역 밖에 존재한다. 따라서 프로그램 종료 시까지 메모리가 할당된 채로 존재하기 때문에 static을 너무 많이 사용하는 것도 성능상 좋지 않다.
1. static 변수
메모리에 고정적으로 할당되어 프로그램이 종료될 때 해제되는 변수이다.
static 변수와 메소드는 static 메모리 영역에 존재하므로 객체가 생성되기 전에 이미 할당이 되어있다.
때문에 객체의 생성없이 바로 변수나 메소드를 호출해 사용할 수 있는것이다.
예를 들어, 다음과 같은 Person 클래스가 있다고 하자.
// Test.java
class Person {
String name = "hhuhuu";
}
public class Test {
public static void main(String[] args) {
// Person 객체를 2번 생성
Person p1 = new Person();
Person p2 = new Person();
}
}
어떤 사람의 이름을 나타내는 Personname 클래스를 만들고 객체를 생성하면 객체마다 객체 변수 name을 저장하기 위한 메모리가 별도로 할당된다. 우리는 Person클래스를 통해 객체를 2번 생성하면 "hhuhuu"라는 같은 값을 갖는 메모리가 2개가 중복되서 생성된다. 3번생성하면 3번, 100번은 100번 중복되서 생성될 것이다. 이러한 경우, static을 사용하여 여러 객체가 하나의 메모리를 참조하도록 하면 메모리의 효율성을 얻을 수 있다.
static을 이용해 코드를 변경해보자.
// Test.java
class Person {
final static String name = "hhuhuu";
}
public class Test {
public static void main(String[] args) {
// Person 객체를 2번 생성
Person p1 = new Person();
Person p2 = new Person();
}
}
위와 같이 name 변수에 static 키워드를 붙이면 자바는 메모리 할당을 한 번만 하게 된다.
만약, name 변수의 "hhuhuu"라는 값이 변경되지 않기를 바란다면 static 키워드 앞에 final 키워드를 붙여주면 된다. final 키워드를 한 번 설정되면 그 값을 변경할 수 없고, 변경하려면 오류가 난다.
static 변수의 대표적인 특징은 '공유' 개념과 메모리의 이점을 꼽을 수 있다.
변수에 static 키워드를 붙이면 자바는 메모리 할당을 딱 한 번만 하게된다. 즉, static으로 설정하면 같은 곳의 메모리의 주소를 가리키기 때문에 static 변수값과 메모리를 공유하게 되고, 메모리 할당을 한 번만 하기 때문에 메모리 사용에 이점이 있다.
공유 개념을 명확히 이해하기 위해 코드를 보자. 카운트를 세는 Counter 클래스이다.
class Counter {
int count = 0;
Counter() {
this.count++;
System.out.println(this.count);
}
}
public class Sample {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
}
}
위 Counter 클래스를 실행하면 다음과 같은 결과값이 나온다.
1
1
c1, c2 객체 생성시 생성자에서 count++;를 실행해도 c1과 c2의 count 변수는 서로 다른 메모리를 가리키고 있기 때문에 둘 다 1이 출력된다. 객체 생성이 될때마다 count값을 공유해서 증가시키고 싶다면 count 변수에 static 키워드를 붙여 count 값을 공유시킨다.
class Counter {
static int count = 0;
Counter() {
count++; // count는 더이상 객체변수가 아니므로 this를 제거
System.out.println(count); // this 제거
}
}
public class Sample {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
}
}
결과
1
2
보통 변수의 static 키워드는 프로그래밍시 메모리의 효율보다는 공유의 목적으로 훨씬 더 많이 사용한다.
2. static 메소드
마찬가지로 static 키워드를 사용해 선언된 메소드는 변수와 크게 다르지 않다. 메모리에 메소드 하나를 고정적으로 두어 사용이 가능하고, 객체 생성없이 해당 메소드를 호출해서 사용할 수 있다.
class Counter {
int count2 = 0;
static int count = 0;
Counter() {
count++;
System.out.println(count);
}
public static int getCount() {
//return count2; 불가능한호출
return count;
}
public static int getCount2(int cnt) {
return cnt+1;
}
}
public class Sample {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
System.out.println(Counter.getCount()); // static 메소드는 객체 생성없이 클래스 이용하여 호출
System.out.println(Counter.getCount2(1));
System.out.println(Counter.getCount2(2));
}
}
주의할 점은, static 메소드 안에서는 객체 변수 접근이 불가능하다는 것이다. (count변수는 static 변수이기 때문에 접근이 가능하지만 count2 변수는 접근이 불가능하다.) 그 이유는 무엇일까?
메모리의 static 변수나 메소드가 동적으로 메모리를 할당하는 시점보다 먼저 메모리에 잡히게 된다. 즉, static 메소드에서는 자신보다 나중에 메모리가 할당되는 멤버 변수나 메소드는 언제 할당되었는지 알 수 없다. 따라서 멤버 변수인 count2는 static 메소드인 getCount에서는 사용이 불가능하다.
결과는 다음과 같다.
1
1
2
2
3
보통 static 메소드는 유틸리티성 메소드를 작성할 때 많이 사용된다. (ex. 오늘 날짜 출력하기위한 Util 클래스..)
'JAVA' 카테고리의 다른 글
[Java] 문자열 비교 ==, equals() 차이점 (0) | 2022.09.03 |
---|---|
[Java] int와 Integer의 차이 (0) | 2022.09.02 |
[Java] 스택(Stack)과 큐(Queue) Java로 구현하기 (0) | 2022.02.27 |