안녕하세요.
https://blog.naver.com/sysysy0302 여니입니다 :)
오늘은 static 키워드에 대해 알아보겠습니다.
자바를 한번쯤 공부해본사람이라면 static키워드를 모르지는 않을 것입니다.
그런데도 이 추상적인 static 혹은 인스턴스의 개념을 명확하게 설명하는 분들이 많지도 않은데요,
이미 무수히 많은 static메서드와 인스턴스 메서드를 사용하고 있지만서도 명확한 개념을 다시 한번 정리하고자 이번 포스팅을 작성하게 되었습니다.
자바경력자를 면접볼 때 static키워드에 대해서 질문하곤 합니다.
면접관 : static키워드에 대해서 설명해보세요.
응시자 : static키워드를 쓰면, 객체를 생성하지 않고도 변수나 함수를 사용할 수 있습니다.
면접관 : 왜 static키워드를 쓰나요?
응시자 : 객체를 생성하지 않아도 되니까 편리하고 속도도 빠릅니다.
면접관 : 그렇다면 모든 변수와 함수에 static을 붙이는 것이 좋겠네요?
응시자 : 가능한한 static을 붙이는 것이 좋다고 생각합니다.
면접관 : 어떤 경우에 static을 붙일 수 있고, 어떤 경우에 static을 붙일 수 없습니까?
응시자 : ...
면접관 : 만일 당신이 새로운 클래스를 작성한다고 할 때, 어떤 경우에 static키워드를
사용해야한다고 생각합니까?
응시자 : ...
위의 응답은 모두 맞기도 하지만, 저는... 아마 3번째 응답에서 아니라고 대답했을 것 같아요.
'인스턴스 변수를 사용할 경우에는 static을 사용하지 않고, 인스턴스 변수를 사용하지 않을 경우에는 static을 사용한다.'
즉, static메서드에서는 인스턴스 객체를 생성한 후 인스턴스 변수를 사용할 수 있기 때문에 필요한 상황에 맞게 static을 붙여야한다고 생각했습니다.
기본 개념
1.클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통적으로 사용해야하는 것에 static을 붙인다.
- 인스턴스를 생성하면, 각 인스턴스들은 서로 독립적기 때문에 서로 다른 값을 유지한다. 경우에 따라서는 각 인스턴스들이 공통적으로 같은 값이 유지되어야 하는 경우 static을 붙인다.
2. static이 붙은 멤버변수는 인스턴스를 생성하지 않아도 사용할 수 있다.
- static이 붙은 멤버변수(클래스변수)는 클래스가 메모리에 올라갈때 이미 자동적으로 생성되기 때문이다.
3. static이 붙은 메서드(함수)에서는 인스턴스 변수를 사용할 수 없다.
- static 메서드는 인스턴스 생성 없이 호출가능한 반면, 인스턴스 변수는 인스턴스를 생성해야만 존재하기 때문에... static이 붙은 메서드(클래스메서드)를 호출할 때 인스턴스가 생성되어있을수도 그렇지 않을 수도 있어서 static이 붙은 메서드에서 인스턴스변수의 사용을 허용하지 않는다. (반대로, 인스턴스변수나 인스턴스메서드에서는 static이 붙은 멤버들을 사용하는 것이 언제나 가능하다. 인스턴스변수가 존재한다는 것은 static이 붙은 변수가 이미 메모리에 존재한다는 것을 의미하기 때문이다.)
4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.
- 메서드의 작업내용중에서 인스턴스 변수를 필요로 한다면, static을 붙일 수 없다. 반대로 인스턴스변수를 필요로 하지 않는다면, 가능하면 static을 붙이는 것이 좋다. 메서드 호출시간이 짧아지기 때문에 효율이 높아진다. (static을 안붙인 메서드는 실행시 호출되어야할 메서드를 찾는 과정이 추가적으로 필요하기 때문에 시간이 더 걸린다.)
5. 클래스 설계시 static의 사용지침
- 먼저 클래스의 멤버변수중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있는지 살펴보고 있으면, static을 붙여준다.
- 작성한 메서드 중에서 인스턴스 변수를 사용하지 않는 메서드에 대해서 static을 붙일 것을 고려한다.
일반적으로 인스턴스변수와 관련된 작업을 하는 메서드는 인스턴스메서드(static이 안붙은 메서드)이고 static변수(클래스변수)와 관련된 작업을 하는 메서드는 클래스메서드static이 붙은 메서드)라고 보면 됩니다.
클래스변수와 인스턴스변수
클래스변수와 인스턴스변수의 차이를 이해하기 위한 예로 카드 게임에 사용되는 카드를 클래스로 정의해보자.
카드 클래스를 작성하기 위해서는 먼저 카드를 분석해서 속성과 기능을 알아 내야한다. 속성으로는 카드의 무늬, 숫자, 폭, 높이 정도를 생각할 수 있을 것이다.
이 중에서 어떤 속성을 클래스 변수로 선언할 것이며, 또 어떤 속성들을 인스턴스 변수로 선언할 것인지 생각해보자.
class Card {
String kind ; // 카드의 무늬 - 인스턴스 변수
int number; // 카드의 숫자 - 인스턴스 변수
static int width = 100 ; // 카드의 폭 - 클래스 변수
static int height = 250 ; // 카드의 높이 - 클래스 변수
}
각 Card인스턴스는 자신만의 무늬(kind)와 숫자(number)를 유지하고 있어야 하므로 이들을 인스턴스변수로 선언하였고, 각 카드들의 폭(width)과 높이(height)는 모든 인스턴스가 공통적으로 같은 값을 유지해야하므로 클래스변수로 선언하였다.
만일 카드의 폭을 변경해야할 필요가 있을 때는 모든 카드의 width값을 변경하지 않고, 한 카드의 width값만 변경해도 모든 카드의 width값이 변경되는 셈이다.
CardTest.java
class CardTest{
public static void main(String args[]) {
// 클래스변수(static 변수)는 객체생성없이 '클래스이름.클래스변수'로 직접 사용 가능하다.
System.out.println("Card.width = " + Card.width);
System.out.println("Card.height = " + Card.height);
Card c1 = new Card();
c1.kind = "Heart";
c1.number = 7;
Card c2 = new Card();
c2.kind = "Spade";
c2.number = 4;
System.out.println("c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")" );
System.out.println("c2는 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")" ); System.out.println("이제 c1의 width와 height를 각각 50, 80으로 변경합니다.");
c1.width = 50;
c1.height = 80;
System.out.println("c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")" );
System.out.println("c2는 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")" );
}
}
class Card {
String kind ; // 카드의 무늬 - 인스턴스 변수
int number; // 카드의 숫자 - 인스턴스 변수
static int width = 100; // 카드의 폭 - 클래스 변수
static int height = 250; // 카드의 높이 - 클래스 변수
}
[실행결과]
Card.width = 100
Card.height = 250
c1은 Heart, 7이며, 크기는 (100, 250)
c2는 Spade, 4이며, 크기는 (100, 250)
이제 c1의 width와 height를 각각 50, 80으로 변경합니다.
c1은 Heart, 7이며, 크기는 (50, 80)
c2는 Spade, 4이며, 크기는 (50, 80)
Card클래스의 클래스변수(static변수)인 width, height 그리고 color는 Card클래스의 인스턴스를 생성하지 않고도 '클래스이름.클래스변수'와 같은 방식으로 사용할 수 있다.
Card인스턴스인 c1과 c2는 클래스 변수인 width와 height를 공유하기 때문에, c1의 width와 height를 변경하면 c2의 width와 height값도 바뀐 것과 같은 결과를 얻는다.
Card.width, c1.width, c2.width는 모두 같은 저장공간을 참조하므로 항상 같은 값을 갖게 된다.
인스턴스 변수는 인스턴스가 생성될 때 마다 생성되므로 인스턴스마다 각기 다른 값을 유지할 수 있지만, 클래스 변수는 모든 인스턴스가 하나의 저장공간을 공유하므로, 항상 공통된 값을 갖는다.
클래스메서드(static메서드)와 인스턴스메서드
변수에서 그랬던 것과 같이, 메서드 앞에 static이 붙어 있으면 클래스메서드이고 붙어 있지 않으면 인스턴스메서드이다.
클래스 메서드는 호출방법 역시 클래스변수처럼, 객체를 생성하지 않고도 '클래스이름.메서드이름(매개변수)'와 같은 식으로 호출이 가능하다.
그렇다면 어느 경우에 static을 사용해서 클래스메서드로 정의해야하는 것일까?
클래스는 '데이터(변수)와 데이터에 관련된 메서드의 집합'이라고 할 수 있다. 같은 클래스 내에 있는 메서드와 멤버변수는 아주 밀접한 관계가 있다. 인스턴스메서드는 인스턴스변수와 관련된 작업을 하는, 즉 메서드의 작업을 수행하는데 인스턴스변수를 필요로 하는 메서드이다.
그래서 인스턴스변수와 관계없거나(메서드 내에서 인스턴스변수를 사용하지 않거나), 클래스변수만을 사용하는 메서드들은 클래스메서드로 정의한다.
물론 인스턴스변수를 사용하지 않는다고 해서 반드시 클래스 메서드로 정의해야하는 것은 아니지만, 그렇게 하는 것이 일반적이다.
참고로 Math클래스의 모든 메서드는 클래스메서드임을 알 수 있다. Math클래스에는 인스턴스변수가 하나도 없거니와 Math클래스의 함수들은 작업을 수행하는데 필요한 값들을 모두 매개변수로 받아서 처리 하기 때문이다. 이처럼, 단순히 함수들만의 집합인 경우에는 클래스메서드로 선언한다.
[참고]
인스턴스 변수 뿐만 아니라 인스턴스 메서드를 호출하는 경우에도 인스턴스 메서드로 선언되어야 한다.
인스턴스 메서드를 호출하는 것 역시 인스턴스 변수를 간접적으로 사용하는 것이기 때문이다..
MyMathTest2.java
class MyMath2 {
long a, b;
// 인스턴스변수 a, b를 이용한 작업을 하므로 매개변수가 필요없다.
long add() { return a + b; } // 인스턴스 메서드, a,b는 인스턴스 변수
long subtract() { return a - b; }
long multiply() { return a * b; }
double divide() { return a / b; }
// 인스턴스변수와 관계없이 매개변수만으로 작업이 가능하다.
static long add(long a, long b) { return a + b; } // static 메서드, a,b는 지역변수
static long subtract(long a, long b) { return a - b; }
static long multiply(long a, long b) { return a * b; }
static double divide(double a, double b) { return a / b; }
}
class MyMathTest2 {
public static void main(String args[]) {
// 클래스메서드 호출
System.out.println(MyMath2.add(200L, 100L));
System.out.println(MyMath2.subtract(200L, 100L));
System.out.println(MyMath2.multiply(200L, 100L));
System.out.println(MyMath2.divide(200.0, 100.0));
MyMath2 mm = new MyMath2(); // 인스턴스 생성 (순서 1)
mm.a = 200L;
mm.b = 100L;
// 인스턴스메서드는 객체생성 후에만 호출이 가능함.
System.out.println(mm.add()); // 인스턴스 호출 (순서 2), 참조변수.메서드() 형식으로 작성하여 사용
System.out.println(mm.subtract());
System.out.println(mm.multiply());
System.out.println(mm.divide());
}
[실행결과]
300
100
20000
2.0
300
100
20000
2.0
인스턴스메서드인 add(), subtract(), multiply(), divide()는 인스턴스변수인 a와 b만으로도 충분히 원하는 작업이 가능하기 때문에, 매개변수를 필요로 하지 않으므로 괄호()에 매개변수를 선언하지 않았다.
반면에 add(long a, long b), subtract(long a, long b) 등은 인스턴스변수 없이 매개변수만으로 작업을 수행하기 때문에 static을 붙여서 클래스메서드로 선언하였다. MyMathTest2의 main메서드에서 보면, 클래스메서드는 객체생성없이 바로 호출이 가능했고, 인스턴스메서드는 MyMath2클래스의 인스턴스를 생성한 후에야 호출이 가능했다.
이 예제를 통해서 어떤 경우 인스턴스메서드로, 또는 클래스메서드로 선언해야하는지, 그리고 그 차이를 이해하는 것은 매우 중요하다.
클래스멤버와 인스턴스멤버간의 참조와 호출
TestClass.java
classTestClass {
// 변수
int iv = 10; // 인스턴스 변수
static int cv = 20; // static 변수
// 메서드
void instaceMethod() {}; // 인스턴스메서드
static void staticMethod() {}; // static메서드
int iv2 = cv;
// static int cv2 = iv; 에러. 클래스변수는 인스턴스 변수를 사용할 수 없음.
static int cv2 = new TestClass().iv; // 굳이 사용하려면 이처럼 객체를 생성해야함.
static void staticMethod1() { // static메서드
System.out.println(cv);
// System.out.println(iv); 에러. static(클래스)메서드에서 인스턴스변수를 바로 사용할 수 없음.
TestClass c = new TestClass();
System.out.println(c.iv); // 객체를 생성한 후에야 인스턴스변수의 참조가 가능함.
}
void instanceMethod1() { // 인스턴스메서드
System.out.println(cv);
System.out.println(iv); // 인스턴스메서드에서는 인스턴스변수를 바로 사용가능.
}
static void staticMethod2() { // static메서드
staticMethod1();
// instanceMethod1(); 에러. static(클래스)메서드에서는 인스턴스메서드를 바로 호출할 수 없음.
TestClass c = new TestClass();
c.instanceMethod1(); // 인스턴스를 생성한 후에야 인스턴스메서드를 호출할 수 있음.
}
void instanceMethod2() { // 인스턴스메서드에서는 인스턴스메서드와 클래스메서드 모두 인스턴스생성없이 바로 호출이 가능하다.
staticMethod1(); // static메서드 호출
instanceMethod1(); // 다른 인스턴스메서드 호출
}
}
Q. static메서드는 static메서드 호출가능?
A. 네
Q. static메서드는 인스턴스 변수 사용가능?
A. 아니요
(static메서드는 객체생성없이 언제나 호출 가능하기 때문에 (객체생성여부가 확실하지 않음) iv(변수)는 객체생성 후에 호출가능하므로, static메서드에서는 인스턴스변수를 사용할 수 없다.)
Q. static메서드는 인스턴스 메서드 호출가능?
A. 아니요
(위와 같이 static메서드는 객체생성없이 언제나 호출 가능하기 때문에 static메서드가 인스턴스메서드를 호출하는 것이 불가능하다.)
Q. 왜 static메서드는 인스턴스 멤버를 쓸 수 없나요?
A. static메서드 호출시 객체(iv묶음)가 없을 수도 있어서
위의 퀴즈가 헷갈리는 분들은 직접 써보면서 익히는 것을 추천드리며, static메서드가 인스턴스메서드(인스턴스변수(iv)사용도) 호출이 안되는 것만 기억하고 나머지는 호출이 된다고 아시면 좀 더 쉽게 생각할 수 있습니다.
class메서드에서는 매개변수를 받지 않습니다. 하지만 static메서드에서는 매개변수를 받아옵니다.
여기서 객체는 변수(iv) 묶음입니다.
인스턴스 객체 생성없이 호출이 가능한 메서드가 static메서드입니다.
(iv)를 사용하지 않을 때 static을 붙입니다.
+ 아래 자료들을 참고하여 작성하였습니다.
'。*:・゚☆・゚schedule・゚*:・゚★・:*:・☆ *:・゚★ > 나의 면접 준비 자료 ✿˘◡˘✿' 카테고리의 다른 글
[취업/면접] MVC패턴 방식 Model1과 Model2 차이점 (0) | 2023.10.01 |
---|---|
[취업/면접] MVC패턴 개념 및 동작 방식, MVC패턴을 사용하는 이유 (0) | 2023.10.01 |
[취업/면접] JSTL 개념 및 사용이유 (0) | 2023.09.29 |
[취업/면접] SI, SM, 솔루션의 차이점 (1) | 2023.09.28 |
[취업/면접] PK와 FK 차이점 (0) | 2023.09.26 |