static 키워드
- 클래스, 메서드, 변수의 제한자로 사용
- 메서드 또는 변수에 static 키워드를 사용할 경우 인스턴스 생성과 상관없이 클래스가 로딩되는 시점에 메모리에 로딩됨 (클래스가 로딩되는 Method Area에 함께 로딩됨) => 따라서, 참조변수 없이 클래스명만으로 멤버에 접근 가능함 (= 인스턴스 생성 없어도 접근 가능한 멤버) => 기본문법 : 클래스명.변수명 또는 클래스명.메서드명() 형태로 접근
- 인스턴스 멤버변수가 아닌 클래스가(= 정적변수·메서드 = static변수·메서드) 멤버로 취급됨
static 변수
- 멤버변수에 static 키워드를 사용할 경우 클래스(정적) 멤버로 취급됨
- 인스턴스 생성 전, 클래스가 메모리에 로딩될 때 함께 로딩됨 => 모든 인스턴스에서 하나의 변수를 공유함 => 즉, 클래스 당 하나만 생성됨(클래스 영역 = Method Area에 생성됨) => 인스턴스 생성 없이도 사용 가능
- 클래스명.멤버변수명 형태로 접근
System.out.println("NormalMember : " + NormalMember.a);
// => Cannot make a static reference to the non-static field NormalMember.a
} //main() 메서드 끝
=> Static이 정의되어 있지 않은 인스턴스 멤버변수 a에 접근하려고 하면 에러발생
NormalMember n1 = new NormalMember(); //인스턴스 생성
NormalMember n2 = new NormalMember(); //인스턴스 생성
// => 두개의 인스턴스가 생성되며, 인스턴스 멤버변수도 각각 두개씩 생성됨
System.out.printf("n1.a:%d, n2.a:%d\\n", n1.a, n2.a);
System.out.printf("n1.b:%d, n2.b:%d\\n", n1.b, n2.b);
// n1 인스턴스의 인스턴스 멤버변수 a값을 99로 변경
// n2 인스턴스의 인스턴스 멤버변수 a값을 999로 변경
n1.a = 99;
n2.a = 999;
System.out.printf("n1.a:%d, n2.a:%d\\n", n1.a, n2.a);
System.out.printf("n1.b:%d, n2.b:%d\\n", n1.b, n2.b);
} //main() 메서드 끝
인스턴스 멤버변수인 a와 b는 인스턴스마다 생성되므로 하나의 인스턴스에서 인스턴스 멤버변수 값을 바꾸더라도 다른 인스턴스 멤버변수에는 아무런 영향이 없다. => 생성된 메모리 공간이 다르기 때문
System.out.println("StaticMember.a: "+StaticMember.a);
StaticMember s1 = new StaticMember(); //인스턴스 생성
StaticMember s2 = new StaticMember(); //인스턴스 생성
System.out.printf("s1.a: %d, s2.a: %d\\n", s1.a, s2.a);
System.out.printf("s1.b: %d, s2.b: %d\\n", s1.b, s2.b);
System.out.println();
s1.a = 99; // Static(클래스 =정적) 멤버변수 값을 변경
s1.b = 999; // 인스턴스 멤버변수 값을 변경
System.out.printf("s1.a: %d, s2.a: %d\\n", s1.a, s2.a);
System.out.printf("s1.b: %d, s2.b: %d\\n", s1.b, s2.b);
} //main() 메서드 끝
static 멤버변수 값(a)을 s1 인스턴스에서 변경하게 되면 s2 인스턴스도 동일한 변수 값을 공유하므로 변경된 값이 두 인스턴스 모두 적용
즉, 하나의 인스턴스에서 값을 변경하면 모든 인스턴스가 영향을 받음
s2.a = 1000;
System.out.printf("s1.a: %d, s2.a: %d\\n", s1.a, s2.a);
Static 멤버는 참조변수명 대신 클래스명만으로도 접근 가능
StaticMember.a = 500;
// StaticMember.b = 500; //컴파일 에러발생! 클래스명만으로는 접근 불가!
System.out.printf("s1.a: %d, s2.a: %d\\n", s1.a, s2.a);
Static 멤버변수의 대표적인 예
=> java.lang.Math 클래스의 PI 변수 -String처럼 import없이 바로 쓸 수 있음
System.out.println("PI 값: " +Math.PI);
static 메서드(=정적 메서드)
- 메서드 정의시 리턴타입 앞에 static 키워드를 붙여서 정의
- 클래스가 메모리에 로딩될 때 static 변수와 함께 메모리에 로딩되므로 인스턴스 생성과는 무관함
- 클래스명만으로 접근 가능(클래스명.메서드명() 형태로 호출)
static 메서드 정의 시 주의 사항
- 인스턴스 생성 시점에서 생성되는 것은 static 메서드 내에서 접근 불가!
- 인스턴스 변수 사용 불가 => 원인 : static 메서드가 로딩되는 시점은 클래스가 로딩되는 시점이며 인스턴스 변수는 인스턴스 생성 시점에 로딩되므로 static 메서드가 로딩되는 시점에서는 존재하지 않는다.
- 레퍼런스 this 또는 super 사용 불가 => 원인 : static 메서드가 로딩되는 시점은 클래스가 로딩되는 시점이며 레퍼런스 this는 인스턴스 생성 시점에 생성되므로 static 메서드가 로딩되는 시점에서는 존재하지 않는다. => 해결책 : this.XXX 또는 super.XXX 대신 클래스명.XXX형식으로 접근
3. 메서드 오버라이딩 불가(상속 단원에서 배움)
1. 인스턴스 변수 사용 불가
} // Ex2 클래스 끝
class StaticMethod {
// 인스턴스 멤버변수 normalVar 선언(int형) - 10 초기화
private int normalVar = 10;
// 정적 멤버변수 staticVar 선언(int형) - 20 초기화
private static int staticVar = 20;
// 일반 메서드 normalMethod() 메서드 정의 - 파라미터, 리턴값 없음
public void normalMethod() {
System.out.println("일반 메서드 normalMethod()! ");
// 일반 메서드는 인스턴스가 생성되는 시점에 메모리에 로딩됨
System.out.println("일반 메서드에서 인스턴스 변수 접근 : " + normalVar);
System.out.println("일반 메서드에서 static 변수 접근 : " + staticVar);
// 일반 메서드(normalMethod())에서는 static 메서드 호출 가능!
staticMethod();
}
// static 메서드, staticMethod() 메서드 정의 - 파라미터, 리턴값 없음
public static void staticMethod() {
System.out.println("정적 메서드 staticMethod()! ");
// static 메서드는 클래스가 메모리에 로딩되는 시점에 함께 로딩됨
// System.out.println("static 메서드에 인스턴스 변수 접근 : " + normalVar);
// => 컴파일 에러 발생! Cannot make a static reference to the non-static field normalVar
// => 인스턴스 변수는 아직 메모리에 로딩되기 전이므로 접근 불가능
// 즉, new키워드로 StaticMethod() 하지않아서 오류남
System.out.println("static 메서드에서 static 변수접근 : " + staticVar);
// static 메서드에서 일반 메서드 호출 불가! (변수의 원인과 동일함)
// normalMethod();
// => 오류! Cannot make a static reference to the non-static method normalMethod() from the type StaticMethod
//즉, 일반 클래스는 static 가능~ static 클래스는 staic메서드만 가능
} // staticMethod()메서드 끝
} // StaticMethod 클래스 끝
2. 레퍼런스 this 또는 super 사용 불가
레퍼런스 this가 만들어지는 setter만 체크
// Getter/Setter - alt shift s -> r
// 인스턴스 변수 normalVar에 대한 Setter 정의
public void setNormalVar(int normalVar) {
this.normalVar = normalVar;
}
// static 변수 staticVar에 대한 Setter 정의
public static void setStaticVar(int staticVar) {
// this.staticVar = staticVar;
// static 메서드 내에서 레퍼런스 this 사용 불가!
// => Cannot use this in a static context
// => 레퍼런스 this는 인스턴스 생성 시점에 생성되지만, static 메서드가 로딩되는 시점에 레퍼런스 this는 존재하지 않음
// 레퍼런스 this 대신 클래스명으로 static 변수 접근 가능함
StaticMethod.staticVar = staticVar;
}
} // StaticMethod 클래스 끝
static 멤버와 인스턴스 멤버의 메모리 할당 순서
int b = check("인스턴스 변수 b!");
static int a = check("인스턴스 변수 a!");
public static int check(String str) {
System.out.println("check() 호출 : " + str);
return 0;
}
public static void main(String[] args) {
System.out.println("main() 메서드 호출!");
Ex3 ex = new Ex3();
System.out.println("Ex3 인스턴스 생성 완료!");
} //main() 메서드 끝
static int c = check("인스턴스 변수 c!");
} // Ex3 클래스 끝
- Ex3 클래스가 메모리에 로딩됨
- static 키워드가 선언된 모든 멤버가 메모리에 로딩됨
- static 멤버가 메모리에 로딩될 때 static 변수 a와 c에 check() 메서드 리턴값이 전달되어야 하므로 static 메서드인 check() 메서드가 호출됨 => 2-1. "static 변수 a" 출력(static 변수 a 로딩됨) => 2-1. "static 변수 c" 출력(static 변수 c 로딩됨)
- main() 메서드가 자동으로 호출됨 => "main() 메서드 호출!" 출력
- main() 메서드 내에서 Ex3 인스턴스 생성됨
- Ex3 인스턴스 생성 시 인스턴스 변수 b가 메모리에 로딩되며 인스턴스 변수 b가 로딩될때 check() 메서드가 호출됨 => "인스턴스 변수 b!" 출력
- Ex3 인스턴스 생성 후 main() 메서드의 다른 코드 실행 => "Ex3 인스턴스 생성 완료!" 출력
'JAVA' 카테고리의 다른 글
[JAVA] 상속(inheritance) (0) | 2023.09.18 |
---|---|
[JAVA] 싱글톤 디자인 패턴(Singleton Design pattern) (0) | 2023.09.13 |
[JAVA] import 키워드 (1) | 2023.09.13 |
[JAVA] package(패키지) (0) | 2023.09.13 |
[JAVA] 레퍼런스 this, 생성자 this (0) | 2023.09.13 |