728x90
static 변수가 필요한 상황은?
- 이전에 만들었던 학생 클래스를 다시 보겠습니다.
public class Student {
public int studentID;
public String studentName;
public int grade;
public String address;
}
- 해당 클래스를 통해 생성되는 학생 인스턴스들은 학생마다 고유한 학번 studentID, 이름 studentName, 주소 address 를 가집니다.
- 그런데, 학생 인스턴스가 생성될 때마다 학번이 순차적으로 증가하면서 자동으로 부여되도록 하고 싶습니다. 어떻게 해야 할까요? 학생 마다 일일이 다른 학번을 부여할 수는 있지만, 학생의 수가 급격하게 증가하면 엄청 힘든 일이 될 겁니다.
- 이 경우에 각 인스턴스마다 따로 생성되는 변수가 아닌, 클래스 전반에서 공통으로 사용할 수 있는 기준 변수가 있어야 합니다.
- 그리고 학생 클래스를 통해 학생 인스턴스가 한 명 생성될 때마다 기준 변수 값을 하나씩 증가시켜 각 학생 인스턴스의 학번 변수에 대입해 주면 됩니다.
- 이럴때 클래스에서 공통으로 사용하는 기준 변수를 'static 변수'로 선언하게 됩니다.
static 변수란?
- static 변수를 다른 말로 '정적 변수' 라고도 합니다.
- static 변수는 자바 뿐만 아니라 다른 언어에서도 비슷한 개념으로 사용하고 있는 변수입니다.
- 자바에서는 다른 멤버 변수처럼 클래스 내부에 선언합니다.
- static 정적 변수를 선언할 때는 다음과 같이 자료형 앞에 static 예약어를 사용합니다.

- static 변수는 클래스 내부에 선언하지만, 다른 멤버 변수처럼 인스턴스가 생성될 때마다 새로 생성되는 변수가 아닙니다.
- static 변수는 프로그램이 실행되어 스택 메모리에 올라갔을 때 딱 한 번 메모리 공간이 할당됩니다.
- static 변수의 메모리 주소값은 해당 클래스에서 생성되는 모든 인스턴스가 공유합니다.
- 다시 정리해보자면, 일반 멤버 변수는 인스턴스가 생성될 때마다 새로 생성되어 각각 다른 값을 가지게 되지만, static 으로 선언한 변수는 인스턴스 생성과 상관없이 먼저 생성되고 그 값을 모든 인스턴스가 공유하게 되는 것입니다.
- 이런 이유 때문에 static 변수를 클래스에 기반한 특성 때문에 '클래스 변수' 라고도 합니다.
static 변수 사용방법 예제
- 학생이 새로 생성되면 학번을 차례로 부여하는 예제를 통해 static 변수를 사용하는 방법을 알아보겠습니다.
- 우선, 기준 변수(클래스 변수, 정적 변수)를 모든 인스턴스가 공유하는지 확인하는 프로그램을 먼저 작성하겠습니다.
package staticex;
public class Student {
public static int serialNum = 1000; // static 변수는 인스턴스 생성 이전에 먼저 생성한다.
public int studentID;
public String studentName;
public int grade;
public String address;
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
}
package staticex;
public class StudentTest1 {
public static void main(String[] args) {
Student studentLee = new Student();
studentLee.setStudentName("이지원");
System.out.println(studentLee.serialNum);
studentLee.serialNum++;
Student studentSon = new Student();
studentSon.setStudentName("손수경");
System.out.println(studentSon.serialNum);
System.out.println(studentLee.serialNum);
}
}

- Student 클래스에서 기준 변수(클래스 변수, 정적 변수) 역할을 하는 serialNum 변수를 선언하고 기준 값으로 1,000을 대입했습니다.
- Student 클래스의 인스턴스를 2개 만들고 serialNum 이 증가했을 때 증가된 값을 각 인스턴스가 공유하는지 살펴보는 코드를 작성하였는데, 출력된 값을 보면 증가한 기준변수(클래스 변수, 정적 변수)의 값을 각 인스턴스들이 공유하고 있는 것을 확인하였습니다.

- static 으로 선언한 serialNum 변수는 모든 인스턴스가 공유하기 때문입니다.
- 즉, 두 개의 참조 변수가 동일한 변수의 메모리를 가리키고 있다는 것을 알 수 있습니다.
학번 생성하기
- 이제 처음에 하려고 했던 학생이 한 명 생성될 때마다 학번을 자동으로 부여하는 프로그램을 완성시켜 보겠습니다.
package staticex;
public class Student1 {
public static int serialNum = 1000; // static 변수는 인스턴스 생성 이전에 먼저 생성한다.
public int studentID;
public String studentName;
public int grade;
public String address;
public Student1() {
serialNum++;
studentID = serialNum;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
}
package staticex;
public class StudentTest2 {
public static void main(String[] args) {
Student1 studentLee = new Student1();
studentLee.setStudentName("이지원");
System.out.println(studentLee.serialNum);
System.out.println(studentLee.studentName + " 학번 : " + studentLee.studentID);
Student1 studentSon = new Student1();
studentSon.setStudentName("손수경");
System.out.println(studentSon.studentName + " 학번 : " + studentSon.studentID);
}
}
- 이 프로그램에서 주의할 점은 static 변수로 선언된 serialNum 은 모든 인스턴스가 공유하는 변수이므로 이 변수를 바로 학번으로 사용하면 모든 학생이 동일한 학번 값을 가지게 됩니다.
- 그래서 student 클래스의 인스턴스가 생성될 때 serialNum++ 을 해준 다음 studentID 에 대입해주어야 순차적으로 증가하는 serialNum 을 모든 인스턴스가 각자 갖게 됩니다.

- 출력된 값을 보면 정상적으로 적용된 것을 알 수 있습니다.
- 이처럼 static 변수는 같은 클래스에서 생성된 인스턴스들이 같은 값을 공유할 수 있으므로, 인스턴스 간에 공통으로 사용할 값이 필요한 경우 유용하게 사용할 수 있습니다.
클래스 변수
- 앞에서 본 것처럼 static 변수는 인스턴스를 생성할 때마다 만들어지는 것이 아니고 클래스를 선언할 때 특정 메모리에 저장되어 모든 인스턴스가 공유하는 변수입니다.
- static 변수는 인스턴스보다 먼저 생성됩니다.
- 그러므로 인스턴스가 아닌 클래스 이름으로 static 변수를 참조하여 사용할 수 있습니다.
package staticex;
public class StudentTest2 {
public static void main(String[] args) {
Student1 studentLee = new Student1();
studentLee.setStudentName("이지원");
System.out.println(Student1.serialNum); // 인스턴스로 참조하는 것이 아닌 클래스로 참조 할 수 있는 static 변수
}
}
- 위의 예시처럼 static 변수는 클래스의 인스턴스로 참조하지 않고 클래스로 참조하여 사용할 수 있습니다.
- 그렇기 때문에 보통은 static 변수는 인스턴스가 생성되지 않아도 사용할 수 있기 때문에 보통은 Student.serialNum 과 같이 클래스 이름과 함께 사용합니다.
- studentLee.serialNum 으로 사용하면 이클립스는 자동으로 노란 줄을 칠해 주는데, 오류는 아니지만 클래스에서 직접 참조하라는 것을 의미합니다.
- 또한 이클립스에서 static 변수와 메서드는 이탤릭체로 나타납니다.
- static 변수, 정적 변수, 클래스 변수라는 3가지 용어가 동시에 사용 됩니다. 자바에서 static 변수를 클래스 변수라고 하는 이유는 인스턴스마다 생성되는 변수가 아니라 클래스에 속해 한 번만 생성되는 변수이고 이를 여러 인스턴스가 공유하기 때문입니다.
클래스 메서드
- 일반 멤버 변수를 위한 메서드가 존재하듯이 static 변수를 위한 메서드도 있습니다.
- 이런 메서드를 'static 메서드' 또는 '클래스 메서드 class method' 라고 합니다.
- 위에서 보았던 static 변수인 serialNum 변수를 사용하는 메서드를 만들어보겠습니다.
package staticex;
public class Student2 {
private static int serialNum = 1000;
int studentID;
String studentName;
int grade;
String address;
public Student2() {
serialNum++;
studentID = serialNum;
}
public static int getSerialNum() {
int i = 10;
return serialNum;
}
public static void setSerialNum(int serialNum) {
Student2.serialNum = serialNum;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
}
- static 변수인 getSerialNum 의 getter 와 setter 함수는 static 을 포함한 클래스 메서드입니다.
- static 변수를 클래스 이름으로 호출했던 것처럼 static 함수(클래스 메서드)를 클래스 이름으로 호출하여 사용해보겠습니다.
package staticex;
public class StuddntTest4 {
public static void main(String[] args) {
Student2 studentLee = new Student2();
studentLee.setStudentName("이지원");
System.out.println(Student2.getSerialNum());
System.out.println(studentLee.studentName + " 학번 : " + studentLee.studentID);
Student2 studentSon = new Student2();
studentSon.setStudentName("손수경");
System.out.println(Student2.getSerialNum());
System.out.println(studentSon.studentName + " 학번 : " + studentSon.studentID);
}
}
- serialNum 을 Student 의 인스턴스인 studentLee 와 studentSon 을 통해 참조하지 않고, 클래스를 통해 getSerialNum( ) 메서드를 호출하여 참조하였습니다.
- static 메서드 또한 static 변수처럼 인스턴스 참조 변수가 아닌 클래스 이름으로 직접 호출할 수 있습니다.
클래스 메서드와 인스턴스 변수
- 클래스 메서드 내부에서는 인스턴스 변수를 사용할 수 없습니다.
- 다음 예제 코드를 보면서 이해해 보겠습니다.
package staticex;
public class Student2 {
private static int serialNum = 1000;
int studentID;
String studentName;
int grade;
String address;
public Student2() {
serialNum++;
studentID = serialNum;
}
public static int getSerialNum() {
int i = 10;
studentName = "이지원"; // 오류 발생
return serialNum;
}
public static void setSerialNum(int serialNum) {
Student2.serialNum = serialNum;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
}
- getSerialNum( ) 메서드는 static 변수인 serialNum 의 getter 로 static 예약어를 붙인 클래스 메서드입니다.
- getSerialNum( ) 이 메서드는 세 종류의 변수를 사용하고 있습니다.
- int i 이 변수는 메서드 내부에서 선언하였습니다. 이렇게 메서드 내부에서 선언한 변수를 그 지역에서만 사용한다고 해서 지역 변수(local variable)라고 합니다.
- 지역 변수는 우선 간단히 정리하여 말하자면, 메서드가 호출될 때 메모리에 생성되어 메서드가 끝나면 사라지는 변수입니다.
- 따라서 int i 이 변수는 getSerialNum( ) 메서드 내부에서만 사용할 수 있습니다.
- return serialNum 에서 사용된 serialNum 변수는 static 변수입니다.
- 그러므로 클래스 메서드인 getSerialNum( ) 메서드 내부에서도 사용할 수 있습니다.
- 그런데 studentName = "이지원" 이 부분에서 오류가 발생합니다.
- studentName 변수는 Student2 클래스의 멤버 변수로, 인스턴스가 생성될 때 만들어지는 인스턴스 변수이기 때문입니다.
- 클래스 메서드와 클래스 변수는 인스턴스가 생성되지 않아도 사용할 수 있습니다. (그 말은 반대로, 멤버 변수와 멤버 함수들은 인스턴스를 만들지 않으면 사용할 수 없습니다)
- 예제로 확인해보겠습니다.
package staticex;
public class StudentTest5 {
public static void main(String[] args) {
System.out.println(Student2.getSerialNum());
}
}

- 위의 예제처럼, 클래스 메서드(static 메서드)는 Student2.getSerialNum( ) 과 같이 인스턴스가 생성되지 않아도 언제든 호출할 수 있습니다.
정리하자면
- 클래스 메서드 내부에서 지역 변수와 클래스 변수는 사용할 수 있지만, 인스턴스 변수는 사용할 수 없습니다.
- 클래스 메서드에서 인스턴스 변수를 사용할 수는 없지만, 반대로 일반 메서드에서 클래스 변수를 사용하는 것은 전혀 문제가 되지 않습니다.
- 왜냐하면, 일반 메서드 & 클래스 메서드, 인스턴스 변수 & 클래스 변수 는 생성이 되는 시점이 각각 인스턴스 생성시 & 인스턴스 생성 전이기 때문이다.
- 무슨 말이냐면, 클래스 메서드와 클래스 변수는 인스턴스가 생성되기 이전부터 존재하기 때문에 인스턴스를 생성한 이후 인스턴스를 통해서 클래스 메서드와 클래스 변수를 사용해도 문제가 없다. 하지만 반대로 일반 메서드와 인스턴스 변수는 인스턴스가 생성된 이후에 생성되므로, 인스턴스 생성 전의 상태인 클래스 메서드안에서 인스턴스 변수를 사용하면 생성되지도 않은 것을 사용하는 것이 되므로 오류가 발생하게 됩니다.
연습하기
- 우리가 이전에 보았던 예제를 참조하여 학생마다 카드 번호를 부여하는데, 카드번호는 학번에 100을 더한 값입니다. 구현해봅시다!
package staticex;
public class Student3 {
String name;
int studentID;
int studentCard;
private static int serialNum = 1000;
public Student3(String name) {
this.name = name;
Student3.serialNum++;
this.studentID = Student3.serialNum;
this.studentCard = studentID + 100;
}
@Override
public String toString() {
return "Student3 [name=" + name + ", studentID=" + studentID + ", studentCard=" + studentCard + "]";
}
}
package staticex;
public class StudentTest6 {
public static void main(String[] args) {
Student3 kim1 = new Student3("kim1");
System.out.println(kim1.toString());
Student3 kim2 = new Student3("kim2");
System.out.println(kim2.toString());
}
}
