static 이란?
static 이란 '정적인, 고정된' 이라는 사전적 의미를 가진다.
static 키워드는 주로 멤버 변수와 메서드에 사용하며 다른 용어로는 정적 필드, 정적 변수, 클래스 변수, 정적 메서드라고도 하며 이 둘을 정적 멤버(클래스 멤버)라고 한다.
--> 객체(인스턴스)에 소속 X, 클래스에 고정되어 있다.
static 변수
클래스를 통해서 생성된 객체의 수를 세는 프로그램
// Data1 클래스
public class Data1 {
public String name;
// 인스턴스 멤버 변수에 카운트 저장
public int count;
public Data1(String name) {
this.name = name;
count++;
}
}
===========================================================
// DataCountMain1 클래스
public class DataCountMain1 {
public static void main(String[] args) {
Data1 data1 = new Data1("A");
System.out.println("A count = " + data1.count);
Data1 data2 = new Data1("B");
System.out.println("B count = " + data2.count);
}
}
===========================================================
출력값
A count = 1
B count = 1
위의 코드로는 객체를 생성할 때마다 count가 증가하지 않을 것이다.
-> 객체를 생성할 때마다 Data1 인스턴스는 새로 만들어지고, 인스턴스에 포함된 count 변수도 새로 만들어 진다.
- Data1("A") 생성 -> count 초기화 -> count++ -> count = 1
- Data1("B") 생성 -> count 초기화 -> count++ -> count = 1
-> 인스턴스에 사용되는 멤버 변수 count는 인스턴스끼리 서로 공유되지 않는다!!
이제 count값을 증가 시키기 위해서 위 코드를 유지보수 해보도록 하겠다.
// Data2 클래스
public class Data2 {
public String name;
public static int count; // static 변수
public Data2(String name) {
this.name = name;
count++;
}
}
static 이 붙은 멤버 변수 count 는 인스턴스 영역에 생성 X -> 메서드 영역에서 static 멤버 변수 관리함
name, count는 둘다 멤버 변수이다
** 멤버 변수(필드) 종류
- 인스턴스 변수 : static이 붙지 않은 멤버 변수 ex) name
static 이 붙지 않은 멤버 변수는 인스턴스를 생성해야 사용할 수 있고, 인스턴스 변수라 한다.
인스턴스 변수는 인스턴스를 만들 때 마다 새로 만들어진다
- 클래스 변수 : static이 붙은 멤버 변수 ex) count
클래스 변수, 정적 변수, static 변수등 용어를 모두 사용하니 주의!
static 이 붙은 멤버 변수는 인스턴스와 무관하게 클래스에 바로 접근해서 사용할 수 있고, 클래스 자체에 소속되어 있다.
따라서 클래스 변수라 한다.
// DataCountMain2 클래스
public class DataCountMain2 {
public static void main(String[] args) {
Data2 data1 = new Data2("A");
System.out.println("A count = " + Data2.count); // 클래스명에 .사용
Data2 data2 = new Data2("B");
System.out.println("B count = " + Data2.count);
}
}
=========================================================
출력값
A count = 1
B count = 2
- 인스턴스 생성 및 생성자 호출:
- Data3("A") 인스턴스를 생성하면 생성자가 호출됩니다. 이 생성자에는 count++ 코드가 포함되어 있어, 인스턴스가 생성될 때마다 count 값이 1씩 증가합니다.
- 정적 변수에 대한 접근:
- 정적 변수인 count는 클래스 수준에서 공용으로 관리됩니다. 따라서 Data3.count와 같이 클래스 이름을 통해 접근하는 것이 명확하고 권장됩니다.
- 정적 변수의 특성:
- count는 static이 붙은 정적 변수입니다. 정적 변수는 인스턴스 영역이 아니라 메서드 영역에서 관리되므로, 인스턴스를 생성하지 않고도 클래스 이름을 통해 직접 접근할 수 있습니다.
- 이 경우, 메서드 영역에 위치한 count의 값이 인스턴스 생성 시마다 증가합니다.
이와 같이 static 변수를 활용하여 객체를 생성할 때마다 count 값을 증가 시킬수 있다.
static 메서드
public class DecoUtil1 {
// 인스턴스가 필요한 이유는 멤버 변수(인스턴스 변수)등을 사용하는 목적이 크다.
// 현재 아래의 메서드는 사용하는 인스턴스 변수도 없고 단순히 기능만 제공함
public String deco(String str) {
return "*" + str + "*";
}
}
=============================================================================
public class DecoMain1 {
public static void main(String[] args) {
String s = "hello java";
//DecoUtil1 인스턴스 생성
DecoUtil1 util1 = new DecoUtil1();
String deco = util1.deco(s);
// DecoUtil1 인스턴스 생성 -> 위 코드와 달라지는게 없다!!!
// 단순한 메서드(기능)만 있는 인스턴스(객체)이기 때문에!
DecoUtil1 util2 = new DecoUtil1();
String deco = util2.deco(s);
System.out.println("before: " + s);
System.out.println("after: " + deco);
}
}
=============================================================================
출력값
before: hello java
after: *hello java*
위 코드를 보면 deco() 메서드를 호출하기 위해서는 DecoUtil1의 인스턴스(객체)를 먼저 생성해야 한다.
-> deco() 라는 메서드는 멤버 변수 X, 단순히 기능만 제공!! 즉, 불필요한 객체를 생성하고 있다.
유지보수한 코드
public class DecoUtil2 {
// static 메서드 (정적 메서드)
public static String deco(String str) {
return "*" + str + "*";
}
}
=======================================================================================
public class DecoMain2 {
public static void main(String[] args) {
String s = "hello java";
// 정적 메서드 사용 -> 객체 생성 없이 클래스명 + . (dot) + 메서드 명으로 바로 호출
// 불필요한 객체 생성 없이 메서드 사용
String deco = DecoUtil2.deco(s);
System.out.println("before: " + s);
System.out.println("after: " + deco);
}
}
=======================================================================================
출력값
before: hello java
after: *hello java*
- 클래스 메서드(정적 메서드)
클래스 메서드 = 인스턴스(객체)생성 없이 마치 클래스에 있는 메서드를 바로 호출하는 것처럼 느껴지기 때문, 즉 클래스에 바로 접근해서 사용할 수 있고, 클래스 자체에 소속되어 있다
정적 메서드 = ststic 이란 뜻 자체가 정적이라는 뜻
- 인스턴스 메서드
static이 붙지 않은 메서드는 인스턴스를 생성해야 호출할 수 있다.
static 메서드 사용법
정적 메서드는 언제나 사용할 수 있는 건 아니다.
- static 메서드는 static만 사용할 수 있다
- 클래스 내부의 기능을 사용할 때, 정적 메서드는 static이 붙은 정적 메서드나 정적 변수만 사용
- 클래스 내부의 기능을 사용할 때, 정적 메서드는 인스턴스 변수나, 인스턴스 메서드를 사용 X
- 반대로 모든 곳에서 static을 호출할 수 있다.
- 정적 메서드는 공용 기능이다. 따라서 접근 제어자만 허락한다면 클래스를 통해 모든 곳에서 static 호출O
public class DecoData {
// 인스턴스 변수
private int instanceValue;
// 정적 변수(= 클래스 변수)
private static int staticValue;
public static void staticCall() {
// instanceValue; // 인스턴스 변수 접근, compile error
// instanceMethod(); // 인스턴스 메서드 접근, compile error
staticValue++; // 정적 변수 접근 DecoData.staticValue++ : 자신의 클래스에서는 클래스 명 생략 가능
staticMethod(); // 정적 메서드 접근 DecoData.staticMethod() : 자신의 클래스에서는 클래스 명 생략 가능
}
public void instanceCall() {
instanceValue++; // 인스턴스 변수 접근
instanceMethod(); // 인스턴스 메서드 접근
staticValue++; // 정적 변수 접근
staticMethod(); // 정적 메서드 접근
}
// 인스턴스 메서드
private void instanceMethod() {
System.out.println("instanceValue = " + instanceValue);
}
// 정적 메서드(= 클래스 메서드)
private static void staticMethod() {
System.out.println("staticValue = " + staticValue);
}
}
- staticCall() 정적 메서드 -> static 변수, static 메서드 접근 가능!! But, 인스턴스 변수 & 인스턴스 메서드 접근 불가!
- instanceCall() 인스턴스 메서드 -> 모든 곳에서 공용인 static 호출 가능! 인스턴스 변수 & 인스턴스 메서드 또한 가능!
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();
}
}
=======================================================
출력 결과
1. 정적 호출
staticValue = 1
2. 인스턴스 호출1
instanceValue = 1
staticValue = 2
3. 인스턴스 호출2
instanceValue = 1
staticValue = 3
- 정적 메서드(static 메서드)는 클래스의 이름을 통해 바로 호출된다.
- 인스턴스처럼 참조값의 개념이 없다.
- 인스턴스 변수나 인스턴스 메서드에 접근하려면 참조값이 필요하지만, 정적 메서드는 참조값을 사용하지 않기 때문에 인스턴스 변수와 인스턴스 메서드에 접근할 수 없다.
- static 변수나 static 메서드는 메서드 영역의 static 영역에 존재하며, 클래스 이름을 통해 직접 호출할 수 있다.
- 참조값을 통해 호출하는 것이 아니라 클래스명을 사용한다는 점이 중요한 차이점이다.
- 인스턴스 변수나 인스턴스 메서드는 객체를 생성하고 참조값을 통해 접근한다.
- 이들은 힙 영역에 존재하며, 참조값이 있어야만 호출이 가능하다.
따라서 위 이유로 인해 정적 메서드는 인스턴스의 기능을 사용할 수 없다!!
** 아래와 같이 객체의 참조값을 직접 매개변수로 전달하면 정적 메서드도 인스턴스의 변수나 메서드를 호출할 수 있다.
public static void staticCall(DecoData data) {
data.instanceValue++;
data.instanceMethod();
}