변수를 여러 클래스에서 공통으로 사용하려면
public class Student{
public int dtudentID;
public String studentName;
public int grade;
public String address;
}
학생 클래스를 사용하면 여러 학생의 인스턴스를 만들 수 있다.
학생마다 고유한 학번(studentID)을 가지는데, 학생이 입학하면(클래스가 생성되면) 학번이 자동으로 생성되도록 만들고 싶다.
생성된 인스턴스는 학번을 순서대로 가져야 한다.
이때 어떻게 학생에게 학번을 부여할 수 있을까? 이 경우에 각 인스턴스마다 따로 생성되는 변수가 아닌, 클래스 전반에서 공통으로 사용할 수 있는 기준 변수가 있어야 한다.
그리고 학생이 한 명 생성될 때마다 기준 변수 값을 하나씩 증가시켜 각 학생 인스턴스의 학번 변수에 대입해 주면 된다.
이 때 클래스에서 공통으로 사용하느 변수를 'static 변수'로 선언한다.
static 변수의 정의와 사용 방법
static 변수는 다른 용어로 '정적 변수'라고도 한다.
static 변수는 자바뿐만 아니라 다른 언어에서도 비슷한 개념으로 사용하고 있는 변수로서 자바에서는 다른 멤버 변수처럼 클래스 내부에 선언한다.
변수를 선언할 때 다음과 같이 자료형 앞에 static 예약어를 사용한다.
static int serialNum;
static 변수는 클래스 내부에 선언하지만, 다른 멤버 변수처럼 인스턴스가 생성될 때마다 새로 생성되는 변수가 아니다.
static 변수는 프로그램이 실행되어 메모리에 올라갔을 때 딱 한번 메모리 공간이 할당된다.
그리고 그 값은 모든 인스턴스가 공유한다.
다시 말하면 일반 멤버 변수는 인스턴스가 생성될 때 마다 새로 생성되어 각각 다른 studentName을 가지게 되지만,
static으로 선언한 변수는 인스턴스 생성과 상관없이 먼저 생성되고 그 값을 모든 인스턴스가 공유하게 되는 것이다.
이런 이유 때문에 static 변수를 클래스에 기반한 변수라고 해서 '클래스 변수'라고도 한다.
- 학생이 새로 생성되면 학번을 차례로 부여하는 예제
예제에 사용할 static 변수는 serialNum이고 변수의 값이 학생이 생성 될 때마다 순서대로 증가할 것이다.
이 증가된 값을 각 학생의 학번에 대입해 주면 학생에게 새로운 학번이 부여된다.
public class staticex {
public class Student {
public static int serialNum = 1000;
public int studentID;
public String studentName;
public int grade;
public String address;
public String getStudentName(){
return studentName;
}
public void setStudentName(String name){
studentName = name;
}
}
}
Student 클래스에서 기준 값 역할을 하는 serialNum 변수를 선언하고 기준 값으로 1000을 대입한다.
다음의 테스트 코드에서 학생을 두 명 생성하고 serialNum이 증가했을 때 두 인스턴스에서 증가된 값이 공유되는지 확인해 본다.
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);
}
}

studentLee를 먼저 생성하고 이 참조 변수를 사용하여 전체 인스턴스에서 공통으로 사용하는 serialNum 변수 값을 1 증가 시킨다.
그리고 studentSon을 생성한다.
생성된 studentSon으로는 아무 연산도 수행하지 않는다.
그다음에 studentSon과 studentLee로 serailNum 변수 값을 출력해 보면 둘다 1001로 증가된 serialNum 값이 출력되는 것을 알 수 있다.
static으로 선언한 serilaNum 변수는 모든 인스턴스가 공유하기 때문이다.
즉 두개의 참조 변수가 동일한 변수의 메모리를 가리키고 있다.

- 학번 생성하기
이제 원래 구현하려 했던 학생이 한 명 생성될 때마다 학번을 자동으로 부여하는 프로그램을 완성
package staticex;
public class Student {
public static int serialNum = 1000;
public int studentID;
public String studentName;
public int grade;
public String address;
public Student(){
serialNum++;
studentID = serialNum;
}
public String getStudentName(){
return studentName;
}
public void setStudentName(String name){
studentName = name;
}
}
static 변수를 하나 선언한다.
학생이 생성될 때마다 이 변수 값이 증가한다.
그런데 이 static 변수를 그냥 바로 학번으로 사용하면 안된다.
앞서본 예제 처럼 static 변수는 모든 인스턴스가 공유하는 변수이므로 이 변수를 바로 학번으로 사용하면 모든 학생이 동일한 학번 값을 가지게 된다.
학번은 학생의 고유 번호이므로 학생의 멤버 변수로 선언해 주고, 학생이 한 명 생성될 때마다 증가한 serialNum 값을 studentID에 대입해 주면 이 문제를 해결할 수 있다.
Student 클래스에 생성자를 추가하고 생성자에서 serialNum 값을 증가 시키고 증가된 값을 studentID 변수에 대입한다.
- 학생이 생성될 때마다 증가된 다른 학번을 가지는지 확인하는 테스트 클래스
package staticex;
public class StudentTest2 {
public static void main(String[] args){
Student studentNana = new Student();
studentNana.setStudentName("나나");
System.out.println(studentNana.serialNum);
System.out.println(studentNana.studentName + " 학번 : " + studentNana.serialNum);
Student studentUna = new Student();
studentUna.setStudentName("웅나나");
System.out.println(studentUna.serialNum);
System.out.println(studentUna.studentName + " 학번 : " + studentUna.serialNum);
}
}

학생 인스턴스를 생성할 때마다 serialNum 변수의 값은 증가한다.
그리고 새로 생성되는 학생마다 가지는 studentID 변수에 증가한 serialNum 값을 복사해 주었으므로, 두 학생의 학번은 다르다.
이처럼 static 변수는 같은 클래스에서 생성된 인스턴스들이 같은 값을 공유할 수 있으므로, 인스턴스 간에 공통으로 사용할 값이 필요한 경우 유용하게 사용할 수 있다.
클래스 변수
static 변수는 인스턴스를 생성할 때마다 만들어지는 것이 아니고 클래스를 선언할 때 특정 메모리에 저장되어 모든 인스턴스가 공유하는 변수이다.
static 변수는 인스턴스 생성과는 별개이므로 인스턴스보다 먼저 생성된다.
그러므로 인스턴스가 아닌 클래스 이름으로도 참조하여 사용할 수 있다.
따라서 자바에서는 static 변수를 클래스 변수라고도 하며 StudentTest2.java 코드는 다음처럼 변경할 수 있다.
package staticex;
public class StudentTset3 {
public static void main(String[] args) {
Student studentNana = new Student();
studentNana.setStudentName("나나");
System.out.println(Student.serialNum);
System.out.println(studentNana.studentName + " 학번 : " + studentNana.serialNum);
Student studentUna = new Student();
studentUna.setStudentName("웅나나");
System.out.println(Student.serialNum);
System.out.println(studentUna.studentName + " 학번 : " + studentUna.serialNum);
}
}
static 변수 serialNum을 studentLee.serialNum과 같이 인스턴스로 참조할 수도 있다.
그러나 static 변수는 인스턴스가 생성되지 않아도 사용할 수 있기 때문에 보통은 student.serialNum과 같이 클래스 이름과 함께 사용한다.
클래스 메서드
일반 멤버 변수를 위한 메서드가 존재하듯이 static 변수를 위한 메서드도 있다.
이런 메서드를 'static 메서드' 또는 '클래스 메서드' 라고 한다.
클래스 메서드 추가한 Student2 코드
package staticex;
public class Student2 {
private static int serialNum = 1000;
int studentID;
String studentName;
int grade;
String addrss;
public Student2(){
serialNum++;
studentID = serialNum;
}
public String getStudentName(){
return studentName;
}
public void setStudentName(String name){
studentName = name;
}
public static int getSerialNum(){
int i = 10;
return serialNum;
}
public static void setSerialNum(int serialNum){
Student2.serialNum = serialNum;
}
}
테스트코드
package staticex;
public class StudentTest4 {
public static void main(String[] args){
Student2 studentNana = new Student2();
studentNana.setStudentName("나나");
System.out.println(Student2.getSerialNum());
System.out.println(studentNana.studentName + " 학번 : " + studentNana.studentID);
Student2 studentUnana = new Student2();
studentUnana.setStudentName("웅나나");
System.out.println(Student2.getSerialNum());
System.out.println(studentUnana.studentName + " 학번 : " + studentUnana.studentID);
}
}
serialNum을 직접 참조하지 않고 getSerialNum() 메서드를 호출하여 참조한다.
static 메서드 또한 static 변수처럼 인스턴스 참조 변수가 아닌 클래스 이름으로 직접 호출할 수 있다.
클래스 메서드와 인스턴스 변수
클래스 메서드 내부에서는 인스턴스 변수를 사용할 수 없다.
public class Student2{
private static int serialNum = 1000;
int studentID;
String studentName;
int grade;
String address;
public static int getSerialNum(){
int i = 10;
studentName = "이지원"; //오류발생
return serialNum;
}
}
getSerialNum() 메서드는 static 예약어를 붙인 클래스 메서드이다.
이 메서드는 세 종류의 변수를 사용하고 있다.
int i 이 변수는 메서드 내부에서 선언하였다.
이렇게 메서드 내부에서 선언한 변수를 그 지역에서만 사용한다고 해서 지역 변수라고 한다.
지역 변수는 메서드가 호출될 때 메모리에 생성되어 메서드가 끝나면 사라지는 변수이다.
따라서 이 변수는 getSerialNum() 메서드 내부에서만 사용할 수 있다.
마지막 return serialNum; 문장을 보면 serialNum 변수는 static 변수이다.
그러므로 클래스 메서드인 getSerialNum() 메서드 내부에서도 사용할 수 있다.
그런데 메서드 내부의 두 번째 줄에 사용한 studentName 변수는 오류가 발생한다.
이 변수는 Student2 클래스의 멤버 변수로, 인스턴스가 생성될 때 만들어지는 인스턴스 변수이기 때문이다.
클래스 메서드와 클래스 변수는 인스턴스가 생성되지 않아도 사용할 수 있다.
package staticex;
public class StudentTest5 {
public static void main(String[] args){
System.out.println(Student2.getSerialNum());
}
}

클래스 메서드는 Student2.getSerialNum()과 같이 인스턴스가 생성되지 않아도 언제든 호출할 수 있다.
따라서 studentName처럼 인스턴스가 생성되어야 메모리가 할당되는 인스턴스 변수는 클래스 메서드에서 사용할 수 없다.
클래스 메서드 내부에서 지역 변수와 클래스 변수는 사용할 수 있지만, 인스턴스 변수는 사용할 수 없다.
또한 클래스 메서드에서 인스턴스 변수를 사용할 수는 없지만, 반대로 일반 메서드에서 클래스 변수를 사용하는 것은 문제 없다.
왜냐하면 일반 메서드는 인스턴스가 생성될 때 호출되는 메서드이고, 클래스 변수는 이미 만들어진 변수이기 때문에 일반 메서드에서도 클래스 변수를 호출할 수 있기 때문이다.