< 자바 프로그램 실행 과정 >
- 소스 코드 작성 및 컴파일 후 클래스 실행
- 클래스 로딩(static 변수 및 메서드 메모리에 로딩)
- main() 메서드 호출
- 인스턴스 생성(인스턴스 변수 및 메서드 메모리에 로딩)
- 인스턴스 메서드 호출(로컬 변수 메모리에 로딩)
- 결과 출력
- 프로그램 종료
클래스, 메서드, 변수의 제한자로 사용
메서드 또는 변수에 static 키워드 사용할 경우 인스턴스 생성과 관계없이 클래스가 로딩되는 시점에 메모리에 로딩됨
-> 참조변수 없이 클래스명만으로 멤버에 접근 가능
(인스턴스 생성 없이도 접근 가능한 멤버)
인스턴스 멤버가 아닌 클래스 멤버로 취급
< 기본 문법 >
클래스명.변수명
클래스명.메서드명()
멤버변수에 static 키워드 사용할 경우 클래스 멤버로 취급
인스턴스 생성 전, 클래스가 메모리에 로딩될 때 함께 로딩
-> 모든 인스턴스에 하나의 변수 공유
-> 클래스 당 하나만 생성됨
-> 인스턴스 생성 없이도 사용 가능
< 기본 문법 >
클래스명.멤버변수명
public static void main(String[] args) {
NormalMeber n1 = new NormalMeber(); // 인스턴스 생성
NormalMeber n2 = new NormalMeber(); // 인스턴스 생성
system.out.println("n1.a : " + n1.a + ", n2.a : " + n2.a);
// n1.a = 10 / n2.a = 10 출력됨
system.out.println("n1.b : " + n1.b + ", n2.b : " + n2.b);
// n1.b = 20 / n2.b = 20 출력됨
n1.a = 99; // n1 인스턴스의 인스턴스 멤버변수 a 값 변경
n2.b = 99; // n1 인스턴스의 인스턴스 멤버변수 b 값 변경
system.out.println("n1.a : " + n1.a + ", n2.a : " + n2.a);
// n1.a = 99 / n2.a = 10 출력됨
system.out.println("n1.b : " + n1.b + ", n2.b : " + n2.b);
// n1.b = 99 / n2.b = 20 출력됨
// n1의 값만 바뀌고 n2 인스턴스의 값은 여전히 10, 20으로 동일함
// 하나의 인스턴스에서 인스턴스 멤버변수 값을 바꾸더라도
// 다른 인스턴스 멤버변수에는 아무런 영향이 없음!!!!
//=======================================================
StaticMember s1 = new StaticMember(); // 인스턴스 생성
StaticMember s2 = new StaticMember(); // 인스턴스 생성
system.out.println("s1.a : " + s1.a + ", s2.a : " + s2.a);
// s1.a = 10 / s2.a = 10 출력됨
system.out.println("s1.b : " + s1.b + ", s2.b : " + s2.b);
// s1.b = 20 / s2.b = 20 출력됨
s1.a = 88; // static 멤버변수 a 값 변경
s1.b = 88; // 인스턴스 멤버변수 b 값 변경
system.out.println("s1.a : " + s1.a + ", s2.a : " + s2.a);
// s1.a = 88 / s2.a = 88 출력됨
system.out.println("s1.b : " + s1.b + ", s2.b : " + s2.b);
// s1.b = 88 / s2.b = 20 출력됨
// static 멤버변수 값을 변경하면 다른 인스턴스 멤버변수 값도 바뀜
// 인스턴스 멤버변수는 값 그대로(b)
// 따라서 하나의 인스턴스에서 값을 변경하면 모든 인스턴스가 영향 받음
}
class NormalMember {
int a = 10;
int b = 20;
// 둘 다 인스턴스 멤버변수
}
class StaticMember {
static int a = 10; // static 멤버변수
// 이클립스에 적으면 기울어져서 적힘
int b = 20; // 인스턴스 멤버변수
}
static 멤버변수는 참조변수명 대신 클래스명만으로도 접근이 가능하다!
S1.a = 10;
이 일반적인 방법이지만
StaticMember.a = 20;
으로도 접근이 가능!
정적 메서드
메서드 정의 시 리턴타입 앞에 static 키워드 붙여서 정의
클래스가 메모리에 로딩될 때 static 변수와 함께 메모리에 로딩되므로 인스턴스 생성과 무관
클래스명만으로 접근 가능
< 기본 문법 >
클래스명.메서드명()
- 인스턴스 변수 사용 불가
원인
-> static 메서드가 로딩 시점 : 클래스 로딩 시점
-> 인스턴스 변수 로딩 시점: 인스턴스 생성 시점
=> static 메서드가 로딩되는 시점에서 인스턴스 변수는 존재하지 않음
- 레퍼런스 this 또는 super 사용 불가
원인
-> static 메서드가 로딩 시점 : 클래스 로딩 시점
-> 레퍼런스 this 생성 시점: 인스턴스 생성 시점
=> static 메서드가 로딩되는 시점에서는 this나 super가 존재하지 않음
해결책
-> this.XXX
또는 super.XXX
대신
=> 클래스명.XXX
형식으로 접근
- 메서드 오버라이딩 불가
public static void main(String[] args) {
StaticMethod.staticMethod();
// static 메서드도 static 멤버변수처럼 인스턴스 생성 없이 클래스명만으로 접근 가능
StaticMethod.setStaticVar(99);
// private 접근제한자를 적용했기 때문에 set으로 접근해야함
StaticMethod sm = new StaticMethod();
// 인스턴스 생성해야지 일반 메서드 호출 가능
sm.normalMethod(); // 일반 메서드 호출
sm.staticMethod(); // 정적 메서드 호출 (올바른 접근 방법X)
StaticMethod.staticMethod(); // 정적 메서드 올바른 접근
}
class StaticMethod {
private int normalVar = 10;
// 인스턴스 멤버변수 선언 및 초기화
private static int staticVar = 20;
// static 멤버변수(정적 멤버변수) 선언 및 초기화
public void normalMethod() { // 일반 메서드
System.out.println(normarlVar);
System.out.println(staticVar);
// 일반 메서드에서는 인스턴스 멤버변수와 정적 멤버변수 모두 접근 가능
staticMethod();
// 일반메서드에서 static 메서드도 호출 가능!
}
public static void staticMethod() {
System.out.println(normarlVar);
// !!!컴파일 에러!!!
// 인스턴스 변수는 아직 로딩되기 전이기 때문에 접근 불가
System.out.println(staticVar);
// static 멤버변수는 접근 가능
normalMethod(); // !!!컴파일 에러!!!
// 위와 마찬가지로 아직 일반 메서드는 로딩 전이므로 호출 불가
}
public void setNormalVar(int normalVar) {
this.normalVar = normalVar;
// 인스턴스 변수 normalVar에 대한 Setter 정의
}
public static void setStaticVar(int staticVar) {
StaticMethod.staticVar = staticVar;
// static 메서드는 this 사용 불가!
// 레퍼런스 this 대신 클래스명으로 변수 접근 가능!
}
< static 멤버와 인스턴스 멤버의 메모리 할당 순서 >
- Ex 클래스가 메모리에 로딩됨
- static 키워드가 선언된 모든 멤버가 메모리에 로딩됨
- static 멤버가 메모리에 로딩될 때 변수 a와 c에 check() 메서드 리턴값이 전달되어야 하므로 static 메서드인 check() 메서드 호출됨
- main() 메서드 자동으로 호출됨
- main() 메서드 내에서 Ex 인스턴스 생성됨
- Ex 인스턴스 생성 시 인스턴스 변수 b가 메모리에 로딩되며 인스턴스 변수 b가 로딩될 때 check() 메서드 호출됨
- Ex 인스턴스 생성 후 main() 메서드의 다른 코드 실행
⭐ 입력
public class Ex {
int b = check("인스턴스 변수 b");
static int a = check("static 변수 a");
public static int check(String str) {
System.out.println("check() 호출 : " + str);
return 0;
// 리턴값 주는 이유?
// 인스턴스 변수 b랑 static 변수 a, c 모두 int 타입!
// 변수에 check(String str)을 대입하고 있는데
// int 타입에는 int 대입해야함
// 그래서 return 해서 int 타입 대입하는 것!
}
public static void main(String[] args) {
System.out.println("main() 메서드 호출");
Ex ex = new Ex();
System.out.println("Ex 인스턴스 생성 완료");
}
static int c = check("static 변수 c");
}
📌 출력
check() 호출 : static 변수 a
check() 호출 : static 변수 c
main() 메서드 호출
check() 호출 : 인스턴스 변수 b
Ex 인스턴스 생성 완료
check() 호출 : static 변수 a
check() 호출 : static 변수 c
-> static 변수 먼저 로딩됨!
-> 위에서부터 순서대로 a 먼저 출력되고 c가 출력됨
-> static int a = check("static 변수 a")
은 check()
를 a
에 대입하겠다는 말이니까 check()가 실행되는 것!!
-> 리턴값 출력하고 싶으면 System.out.println(a);
출력하면 됨
-> 리턴값은 저장은 되어 있으나 출력하지 않아서 안 나오는 것뿐...
main() 메서드 호출
-> static 변수 먼저 출력하고 이제 main() 로딩됨
check() 호출 : 인스턴스 변수 b
-> Ex ex = new Ex();
이 코드의 출력문
-> 왜 인스턴스 생성밖에 안했는데 출력됐지 했는데
=> Ex 인스턴스를 생성하는 순간 int b
가 동시에 로딩되는 거래
그래서 check() 실행되서 출력문이 나오는 거라고 함!!!!
Ex 인스턴스 생성 완료
-> 얘는 그냥 순서대로 제일 마지막 코드 실행된 거임