static 변수란 다른 용어로 '정적 변수'라고도 한다. 다른 멤버 변수처럼 클래스 내부에 선언하고 자료형 앞에 static 예약어를 사용한다.
static 변수는 클래스 내부에 선언하지만, 다른 멤버 변수처럼 인스턴스가 생성될 때마다 새로 생성되는 변수가 아니다.
프로그램이 실행되어 메모리에 올라갔을 때 딱 한번 메모리 공간이 할당된다. 그리고 그 값은 모든 인스턴스가 공유한다.
다시 말하면 static으로 선언한 변수는 인스턴스 생성과 상관없이 먼저 생성되고 그 값을 모든 인스턴스가 공유하게 된다. 이런 이유로 클래스에 기반한 변수라고 해서 '클래스변수'라고도 한다.
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 name) {
studentName = name;
}
}
public class StudentTest {
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을 생성한다. 그리고 studentLee.serialNum과 studentSon.serialNum을 출력해보면 같은 값이 출력된다. 왜냐하면 static으로 선언한 serialNum 변수는 모든 인스턴스가 공유하기 때문이다. 즉 두개의 참조 변수가 동일한 변수를 가리키고 있다는 것이다.
public class Student {
public static int serialNum = 1000; //static 변수는 인스턴스 생성과 상관없이 먼저 생성됨.
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;
}
}
이렇게 생성자 코드를 따로 더 구현해서 main() 함수를 실행시키면
studentLee.studentID는 1001, studentSon.studentID는 1002가 출력될 것이다.
정적 변수는 인스턴스 생성과 별개이므로 인스턴스보다 먼저 생성된다. 그러므로 인스턴스가 아닌 클래스 이름으로도 참조하여 사용할 수 있다.
public class StudentTest {
public static void main(String[] args) {
Student studentLee = new Student();
studentLee.setStudentName("이지원");
System.out.println(Student.serialNum); // 클래스 이름으로 참조
System.out.println(studentLee.studentName + " 학번 : " + studentLee.studentID);
Student studentSon = new Student();
studentSon.setStudentName("손수경");
System.out.println(Student.serialNum); // 클래스 이름으로 참조
System.out.println(studentSon.studentName + " 학번 : " + studentLee.studentID);
}
}
위 코드를 실행하면
1001
이지원 학번 : 1001
1002
손수경 학번 : 1002
이렇게 출력될 것이다.
일반 멤버 변수를 위한 메서드가 존재하듯 static 변수를 위한 메서드도 있다. 이런 메서드를 static 메서드, 클래스 메서드라고 한다.
위 코드 중 serialNum 변수에 private으로 선언하고 get(), set() 메서드를 만들고 테스트를 해보면 일반 멤버 변수와 똑같이 값이 바뀌고 값을 얻을 수 있는 것을 알 수 있다.
그리고 클래스 메서드 내부에서는 인스턴스 변수를 사용할 수 없다.
public class Student {
private static int serialNum = 1000;
int studentID;
String studentName;
...
public static int getSerialNum() { // 클래스 메서드
int i = 10;
studentName = "이지원"; // 오류 발생
return serialNum;
}
...
위 코드 중 int i를 메서드 내부에 선언했다. 이렇게 메서드 내부에서 선언한 변수를 그 지역에서만 사용하는 변수라고 해서 '지역 변수'라고 한다. 지역 변수는 메서드가 호출될 때 메모리에 생성되어 메서드가 끝나면 사라지는 변수다. 그러므로 이 변수는 getSerialNum() 메서드 내부에서만 사용할 수 있다.
return serialNum 을 보면 serialNum 변수는 static변수다. 그러므로 클래스 메서드 내부에서도 사용할 수 있다.
studentName 변수는 오류가 발생하는데, 이 변수는 Student 클래스의 멤버 변수로, 인스턴스가 생성될 때 만들어지는 변수이기 때문이다.
클래스 변수와 클래스 메서드는 인스턴스가 생성되지 않아도 사용할 수 있지만,
멤버 변수는 인스턴스가 생성되어 메모리가 할당 되었을 때에만 사용할 수 있기 때문에
클래스 메서드 내부에서는 사용할 수 없다.
정리하자면, 클래스 메서드 내부에서 지역 변수와 클래수 변수는 사용할 수 있지만, 인스턴스 변수는 사용할 수 없다.
또한 클래스 메서드에서 인스턴스 변수를 사용할 수는 없지만, 반대로 일반 메서드에서 클래스 변수를 사용하는 것은 문제가 되지 않는다. 왜냐하면 일반 메서드는 인스턴스가 생성될 때 호출되는 메서드이고, 클래스 변수는 이미 만들어진 변수이기 때문에 일반 메서드에서도 클래스 변수를 호출할 수 있기 때문이다.
참고 - Do it ! 자바프로그래밍기초
'자바 기초' 카테고리의 다른 글
자바 배열 복사, arraycopy - Do it! 자바프로그래밍기초 (0) | 2022.07.20 |
---|---|
자바 배열, array, 객체배열 - Do it! 자바프로그래밍기초 (0) | 2022.07.20 |
자바 객체 간 협력 - Do it! 자바프로그래밍기초 (0) | 2022.07.19 |
자바 접근제어자, public, private - Do it ! 자바프로그래밍기초 (0) | 2022.07.19 |
자바 생성자, 생성자 오버로딩, 참조 자료형 - Do it ! 자바프로그래밍기초 (0) | 2022.07.19 |