앞서 멤버 변수와 지역 변수에 대해 공부했다. 이때 우리가 공부한 개념은 전부 인스턴스 변수였다. 인스턴스(instance)란 실제로 메모리에 올라가 만들어져 있는 객체를 말하며, 인스턴스 변수는 객체가 생성될 때 객체에 포함되는 변수이다.
참고) 멤버 변수와 지역 변수
우리가 지금부터 배워볼 멤버 변수는 static(클래스) 변수이다. static 변수는 여러 개의 객체가 생성될 때 단 하나만 생성되며 모든 객체들이 공유하는 개념으로 사용되는 변수이다.
static 변수를 이해하기 위해 아래 예제를 살펴보자.
(static 역시 예약어이기 때문에 패키지명으로 사용할 수 없다.)
package kr.s18.object.statictest;
public class StaticCount {
//인스턴트 변수 : 객체가 생성될 때 객체에 포함되는 변수
int c;
//static(클래스) 변수 : 객체가 생성될 때 포함되지 않음
//즉, 단독으로 메모리에 올라감
static int count;
//생성자
public StaticCount() {
c++;
count++;
}
}
인스턴스 변수 int c와 static 변수 static int count가 존재한다. 이를 통해 static 변수를 선언할 때에는 자료형 앞에 static을 붙이면 된다는 것을 알 수 있다.
그리고 StaticCount 클래스의 생성자를 호출할 때, 즉 객체가 생성될 때마다 인스턴스 변수 c와 static 변수 count가 1씩 증가하도록 코드를 작성하였다.
package kr.s18.object.statictest;
public class StaticMain01 {
public static void main(String[] args) {
StaticCount sc1 = new StaticCount();
System.out.println("c : " + sc1.c + ", count : " + StaticCount.count);
StaticCount sc2 = new StaticCount();
System.out.println("c : " + sc2.c + ", count : " + StaticCount.count);
StaticCount sc3 = new StaticCount();
System.out.println("c : " + sc3.c + ", count : " + StaticCount.count);
}
}
① StaticCount의 객체 생성, 참조 변수는 sc1. StaticCount의 멤버 변수인 c(인스턴스 변수)와 count(static 변수)를 호출
② StaticCount의 객체 생성, 참조 변수는 sc2. StaticCount의 멤버 변수인 c(인스턴스 변수)와 count(static 변수)를 호출
③ StaticCount의 객체 생성, 참조 변수는 sc3. StaticCount의 멤버 변수인 c(인스턴스 변수)와 count(static 변수)를 호출
출력 결과를 보기 전, 우리는 멤버 변수를 호출하는 과정에서부터 인스턴스 변수와 static 변수의 차이를 알 수 있다.
1) 인스턴스 변수는 다른 클래스에서 호출 시 기존에 알고 있던 것처럼 객체 생성 후 참조 변수를 붙여 호출한다.
ex. sc1.c
2) 반면, static 변수는 StaticMain01처럼 다른 클래스에서 호출 시 클래스명을 붙여야 한다.
ex. StaticCount.count
출력)
c : 1, count : 1
c : 1, count : 2
c : 1, count : 3
출력 결과 또한 위와 같은 차이가 있다.
1) 인스턴스 변수는 '객체가 생성될 때 객체에 포함되는 변수'이다. 따라서 객체 sc1가 생성될 때 int c가 생기고, sc2가 생성될 때 또 하나의 int c가 생기고, sc3가 생성될 때 또 하나의 int c가 생겨난다.
2) 반면, static 변수 int count는 '객체 생성과 무관하게 단독으로 메모리에 올라가는 변수'로 객체 sc1이 생성될 때 만들어져 sc1, sc2, sc3가 공유한다. 따라서 인스턴스 변수와 달리, +1이 계속 하나의 변수에 누적되어 출력되는 것이다.
이를 그림으로 정리하면 다음과 같다.

package kr.s18.object.statictest;
public class StaticMain02 {
//인스턴스 변수
int a;
//static(클래스) 변수
static String s;
public static void main(String[] args) {
//StaticMain02.s = "서울";
s = "서울";
System.out.println(s);
System.out.println("==========");
//인스턴스 변수는 객체 생성 후에 호출해야 사용 가능
//a = 1000;
StaticMain02 sm = new StaticMain02();
sm.a = 1000;
System.out.println(sm.a);
//1. static 변수와 메서드는 다른 클래스에서 호출했을 때
//클래스 명을 붙이고 호출하면 됨.
System.out.println(StaticMain03.s2);
System.out.println(StaticMain03.getString());
//2. 근데 인스턴스 변수는 다른 클래스에서 호출했을 때
//객체 생성 후에 호출해야 함.
StaticMain03 sm2 = new StaticMain03();
System.out.println(sm2.s1);
}
}
package kr.s18.object.statictest;
public class StaticMain03 {
//인스턴스 변수
String s1 = "여름";
//static(클래스) 변수
static String s2 = "겨울";
//static 매서드
public static String getString() {
return s2;
}
public static void main(String[] args) {
System.out.println(getString());
}
}
같은 클래스 내에서 인스턴스 변수와 static 변수를 호출할 때는 어떤 차이가 있을까?
1) static 변수는 같은 클래스에서 호출 시 클래스 명을 붙일 필요가 없다.
ex. StaticMain02.s = "서울" (X) → s = "서울" (O)
2) 하지만 인스턴스 변수는 같은 클래스 내 메인 영역에서 호출 시 객체 생성 후 호출해야 사용이 가능하다.
ex. a = 1000 (X) → sm.a = 1000 (O)
메서드도 static 영역에 단독으로 올릴 수 있다. staticMain03 예제를 보면, String 타입의 데이터를 반환하는 static 메서드 getString()을 객체 생성 없이, 같은 클래스 내 메인 영역에서 호출해 사용하는 것을 알 수 있다.
그리고 static 메서드 역시 다른 클래스(staticMain02)에서 호출 시에는 클래스 이름만 붙이면 된다.
우리가 기존에 사용했던 메서드 중에서도 static 메서드는 존재한다. 대표적인 예로 그동안 '메인 영역'이라고 칭했던 public static void main(String[] args) { 가 그러하며, String s = String.valueOf(n);의 valueOf() 역시 static한 메서드이다.
static String에 있는 valueOf() 메서드를 객체 생성 없이 클래스 명만 붙여 사용한 것이다.

이와 같이 static한 메서드는 객체 생성 없이 호출해서 사용할 수 있으므로 재활용성을 높일 때 좋다.
그리고 객체 사용을 적극 권장하는 자바에서 객체가 필요 없는 static 영역을 만든 이유는 메모리의 부담을 줄이기 위해서다. 객체는 사용할 가능성이 있는 모든 것을 전부 메모리에 올리는 특성을 가지고 있기 때문에 속도가 느려질 수 있다. 이를 방지하기 위해 일회성 메서드가 필요하거나 객체들이 변수를 공유해야 하는 경우 static을 붙인다.