클래스 정보 보관 (바이트 코드(.class), 필드, 메서드, 생성자 등 모든 실행 코드)
static 변수 보관
리터럴 변수 보관 (java가 알아서 최적화하여 보관해준다 정도로 알고 넘어가자)
💡 인스턴스의 메서드 코드는 메서드 영역에 보관!
![]()
- A 메서드를 가지고있는 B 클래스로
100개의 인스턴스를 생성하면, 힙 영역에 100개의 인스턴스가 보관된다.- 이때 100개의 인스턴스에는 똑같은 A 메서드가 100개 있다.
이것은 메모리 낭비이기 때문에 클래스의 메서드 코드는 메서드 영역에서 공통으로 보관한다.- 힙 영역의 각 인스턴스에는 변수에 대한 메모리만 할당되고 메서드를 위한 메모리 할당하지 않는다.
- 즉, 인스턴스를 통해 메서드를 호출한다면 메서드 영역의 메서드 코드를 호출하여 실행하는 것이다.
new
를 통해 만들어진 인스턴스 보관package memory;
public class JavaMemoryMain1 {
public static void main(String[] args) {
System.out.println("main start");
method1(10);
System.out.println("main end");
}
static void method1(int m1) {
System.out.println("method1 start");
int cal = m1 * 2;
method2(cal);
System.out.println("method1 end");
}
static void method2(int m2) {
System.out.println("method2 start");
System.out.println("method2 end");
}
}
/* 출력 결과
main start
method1 start
method2 start
method2 end
method1 end
main end
*/
package memory;
public class Data {
private int value;
public Data(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
package memory;
public class JavaMemoryMain2 {
public static void main(String[] args) {
System.out.println("main start");
method1();
System.out.println("main end");
}
static void method1() {
System.out.println("method1 start");
Data data1 = new Data(10);
method2(data1);
System.out.println("method1 end");
}
static void method2(Data data2) {
System.out.println("method2 start");
System.out.println("data.value=" + data2.getValue());
System.out.println("method2 end");
}
}
package static1;
public class Data3 {
public String name;
public static int count; //static
public Data3(String name) {
this.name = name;
count++;
}
}
package static1;
public class DataCountMain3 {
public static void main(String[] args) {
Data3 data1 = new Data3("A");
System.out.println("A count=" + Data3.count);
Data3 data2 = new Data3("B");
System.out.println("B count=" + Data3.count);
Data3 data3 = new Data3("C");
System.out.println("C count=" + Data3.count);
//추가
//인스턴스를 통한 접근
Data3 data4 = new Data3("D");
System.out.println(data4.count);
Data3 data5 = new Data3("E");
System.out.println(data5.count);
// -> static 변수는 공용으로 사용하기 위함인데, 마치 각자 다른 변수처럼 보이게 된다.
//클래스를 통한 접근
System.out.println(Data3.count);
}
}
/* 출력 결과
A count = 1
B count = 2
C count = 3
4
4
*/
static
을 붙이면 된다.클래스명.static변수명
(Data3.coount)⭐ 용어 정리
- 멤버 변수 : Class 내부에 속해있는 변수
- 인스턴스 변수
- static 변수가 아닌 모든 변수
- 인스턴스를 생성해야만 사용 가능
- 인스턴스 생성시마다 새로 생성됨
- 클래스 변수
- = static 변수(또는 정적 변수)
- 자바 프로그램 시작 시, 딱 1개 생성
- 자바 프로그램 종료 시에 삭제됨
⭐ 변수와 생명주기
- 지역 변수(매개변수 포함)
- 지역 변수는 스택 영역에 있는 스택 프레임 안에 보관된다. 메서드가 종료되면 스택 프레임도 제거 되는데 이때 해당 스택 프레임에 포함된 지역 변수도 함께 제거된다. 따라서 지역 변수는 생존 주기가 짧다.
- 인스턴스 변수
- 인스턴스에 있는 멤버 변수를 인스턴스 변수라 한다. 인스턴스 변수는 힙 영역을 사용한다.
힙 영역은 가비지 컬렉션이 발생하기 전까지는 생존하기 때문에 보통 지역 변수보다 생존 주기가 길다.- 클래스 변수
- 클래스 변수는 메서드 영역의 static 영역에 보관되는 변수이다. 메서드 영역은 프로그램 전체에서
사용하는 공용 공간이다. 클래스 변수는 해당 클래스가 JVM에 로딩 되는 순간 생성된다. 그리고 JVM이 종료될 때 까지 생명주기가 이어진다. 따라서 가장 긴 생명주기를 가진다.
💡 개발자는 남이 내 코드를 가져다 쓰기 편하게 코딩해야 한다!
개발은 결국 협업으로 이뤄지기에, 남이 내 코드를 사용할 경우가 많다.
그래서 개발자는 남이 봤을 때 사용하기 편하게 코드를 작성해야 된다!
현재 static 변수 사용의 경우에도 클래스를 통한 접근을 추천하는 이유가
인스턴스로 접근하게되면 남이 보기에 클래스 변수인지, 인스턴스 변수인지 혼동을 줄 수 있기 때문이다.
따라서 static 변수라면, 누가 봐도 확실하게 알 수 있는 클래스를 통한 접근으로(클래스 변수로) 사용하는 것이 좋다!
클래스명.static 메서드명
으로 호출하여 사용할 수 있다.인스턴스명.static 메서드명
으로 호출하지 않는다.⭐ 정적 메서드 활용
- 객체 생성이 필요 없이 메서드의 호출만으로 필요한 기능을 수행할 때 주로 사용한다.
예를 들어 간단한 메서드 하나로 끝나는 유틸리티성 메서드에 자주 사용한다.
수학의 여러가지 기능을 담은 클래스를 만들 수 있는데, 이 경우 인스턴스 변수 없이 입력한 값을
계산하고 반환하는 것이 대부분이다.
- 이럴 때 정적 메서드를 사용해서 유틸리티성 메서드를 만들면 좋다.
⭐ 간단히 컴파일 과정을 생각해보면 된다.
먼저 인스턴스 변수/메서드는 객체가 생성되어야 참조값이 생긴다.
그리고 참조값을 알아야 인스턴수 변수/메서드에 접근이 가능하다.이제 코드가 컴파일 되는 과정을 보자.
1. 메인 메서드 실행 전에 배경 준비를 위해 Class 정보가 메서드 영역에 저장되어야 한다.
2. static 메서드 또한 같이 저장된다.
3. 하지만 이 순간에는 객체가 생성되지도 않기에 static 메서드 내부에선 인스턴스 변수/메서드를 어떤값을 가져와야할지 이해할 수 없다.
4. 그래서 컴파일 에러 발생!
package static2;
public class DecoData {
private int instanceValue;
private static int staticValue;
public static void staticCall() {
//instanceValue++; //인스턴스 변수 접근, compile error
//instanceMethod(); //인스턴스 메서드 접근, compile error
staticValue++; //정적 변수 접근
staticMethod(); //정적 메서드 접근
}
public static void staticCallWithParam(DecoData data1){ // 인스턴스 참조값 전달
data1.instanceValue++; // 인스턴스 변수 접근 가능
data1.instanceCall(); // 인스턴스 메서드 접근 가능
}
public void instanceCall() {
instanceValue++; //인스턴스 변수 접근
instanceMethod(); //인스턴스 메서드 접근
staticValue++; //정적 변수 접근
staticMethod(); //정적 메서드 접근
}
private void instanceMethod() {
System.out.println("instanceValue=" + instanceValue);
}
private static void staticMethod() {
System.out.println("staticValue=" + staticValue);
}
}
package static2;
//import static DecoData.staticCall();
//import static DecoData.*;
public class DecoDataMain {
public static void main(String[] args) {
System.out.println("1. 정적 호출");
DecoData.staticCall();
System.out.println("2. 인스턴스 호출1");
DecoData data1 = new DecoData();
data1.instanceCall();
System.out.println("3. 인스턴스 호출2");
DecoData data2 = new DecoData();
data2.instanceCall();
System.out.println("추가");
//인스턴스를 통한 접근
DecoData data3 = new DecoData();
data3.staticCall(); // static 변수와 마찬가지로 이렇게 쓰지 말자!
//클래스를 통한 접근
DecoData.staticCall();
System.out.println("매개변수로 인스턴스 참조값 넘겨주기");
DecoData.staticCallWithParam(data3);
// 만약 인스턴스 참조값을 전달해 준다면 static 메서드에서 인스턴스 변수/메서드 접근 가능!
}
}
/* 출력 결과
1. 정적 호출
staticValue=1
2. 인스턴스 호출1
instanceValue=1
staticValue=2
3. 인스턴스 호출2
instanceValue=1
staticValue=3
추가
staticValue=4
staticValue=5
매개변수로 인스턴스 참조값 넘겨주기
instanceValue=2
staticValue=6
*/
import static ~
클래스명.
으로 호출하지 않고 바로 메서드/변수명으로 호출하여 사용할 수 있다.DecoData.staticCall(); //기존 static 메서드 호출방법
⬇️
import static DecoData.staticCall();
...
staticCall(); //바로 이렇게 사용 가능!
💡 main() 메서드도 static 메서드
public static void main(String[] args){ ... }
우리가 지금까지 흔히 봐왔던 main() 메서드이다.
main() 메서드도 static 메서드이기에 static 메서드와 static 변수만 호출할 수 있다!
- 즉, main() 메서드 안에서 할 수 있는 것에는 제한이 있다!
- 현재 위치하고 있는 Class의 내부 static 메서드/변수 접근 가능
- 객체 생성 및 객체 멤버 메서드/변수 접근 가능
- 지역 변수 선언 가능
💡 객체 생성을 금지하고 싶다면, 생성자를 private 해주면 된다!
new ~
만 입력하면, 생성자를 기준으로 자료형과 변수를 앞에 만들어준다!클래스명.
으로 호출하는데, 단축키 사용하면 import static
해줌과 동시에클래스명.
을 자동으로 모두 제거해준다!