Theme. static
클래스는 타입이다. 이전에 집이 아니라 집의 설계도라고 언급한 바 있다. 즉, 실체가 아니라는 것이다.
따라서 클래스 안에서 클래스의 데이터 필드에 데이터를 저장할 수는 없고, 클래스의 멤버 메서드를 실행할 수도 없다.
단지 new 명령으로 해당 클래스 타입의 객체를 만든 후, 그 객체에 데이터를 저장하고, 그 객체의 멤버 메서드를 실행하는 것이다.
여기에는 하나의 예외가 존재하는데 그것이 static 멤버이다.
static 멤버는 클래스 안에 실제로 존재하며(클래스 안에서 실체가 있다), 객체에는 존재하지 않는다.
아래 그림을 통해 자세히 설명하도록 한다.
먼저, 가장 중요한 것은 static 멤버는 class 멤버라는 것과 non-static 멤버는 object 멤버라는 것이다.
non-static 멤버부터 알아보자.
희미하게 적혀있는
public int t = 0;
public void print2() {
System.out.println("t= " + t);
}
부분은 클래스 내에서 실제로 변수 t가 존재하거나 print2() 메서드를 실행할 수 있는 것이 아니다는 것을 나타낸다.
t와 print2()는 new 명령으로 Test 타입의 객체를 생성하였을 때, 그 객체 안에 존재하는 것이다.
즉, Test test1 = new Test(); 를 작성하고,
test1.t = 100;
test1.print2();
와 같이 사용해야 하는 것이다.
만약 Test test2 = new Test();를 통해 새로운 객체를 하나 더 만들었다면,
각각의 객체마다 서로 다른 t, print2()가 존재하는 것이다.
(각각의 t는 다른 변수이므로 t에 서로 다른 값을 저장할 수 있다.)
반면에, static 멤버는 class 멤버로 class안에 존재하는 것이지 생성된 객체에 존재하는 것이 아니다.
그래서 static 멤버는 하나의 클래스 안에 유일하게 존재한다. 생성된 객체마다 어떤 멤버가 존재하는 등의 일은 발생할 수 없다.
몇 가지 질문을 통해 static에 대해 좀 더 알아보자.
Q. 왜 main 메서드는 반드시 static이어야 하는가?
먼저, 자바는 클래스들의 집합임을 기억하자. 클래스 외부에 존재할 수 있는 것은 아무것도 없다.
따라서, main 메서드조차 클래스 안에 존재하는 것이다.
여기서 문제가 발생한다. 클래스 안에서는 어떠한 메서드들도 실행할 수 없는데, 그 메서드들은 어디서 실행할 수 있는가?
메서드를 실행하기 위해서는 그 메서드가 포 함된 클래스 타입의 객체를 생성하고, 그 객체 내의 메서드를 실행해야 한다. 이를 프로그램의 출발점인 main에서 실행시키고자 한다면, main 메서드는 static이어야만 한다.
프로그램의 시작점인 main 메서드가 포함된 클래스의 객체를 만드는 것은 불가능하다. 따라서 main 메서드 만큼은 static으로 두어서 그 안에서 다른 클래스들의 객체를 생성하고, 객체 내 데이터를 사용하거나 메서드를 실행하는 등의 작업을 수행할 수 있는 것이다.
Q. 왜 static 메서드에서 같은 클래스의 non-static 멤버를 엑세스 할 수 없는가?
결론부터 말하자면, static은 class의 멤버이고, non-static 멤버는 object 멤버이기 때문이다.
non-object 멤버는 object 멤버이기 때문에 클래스 내에서는 허상일 뿐이다. 객체를 생성해주고 그 객체 안에서 어떠한 값을 가지거나 메서드를 실행할 수 있다. 따라서, 어떤 클래스 내의 static 메서드에서 non-static 멤버를 엑세스할 수 없는 것이다.
이를 확인할 수 있는 아래 코드를 살펴보자.
1
2
3
4
5
6
7
8
9
10
11
12
|
package section3;
public class Test {
static int s = 0;
int t = 0;
// 어떤 클래스 안에 있는 static 메서드 안에서
public static void printStatic() {
System.out.println("s= " + s); // static 멤버에 access하는 것은 문제가 없다.
System.out.println("t= " + t); // non-static 멤버에 access할 수 없다.
}
|
cs |
그런데, 어찌보면 당연하게 넘겼을 지 모르는 아래 코드를 살펴보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package section3;
public class Test {
static int s = 0;
int t = 0;
/*
// 어떤 클래스 안에 있는 static 메서드 안에서
public static void printStatic() {
System.out.println("s= " + s); // static 멤버에 access하는 것은 문제가 없다.
System.out.println("t= " + t); // non-static 멤버에 access할 수 없다.
}
*/
public void printNonStatic() {
System.out.println("s= " + s);
System.out.println("t= " + t);
}
|
cs |
printNonStatic() 메서드에서
Test 타입의 객체를 생성해 준 적도 없는데, t에 어떻게 접근할 수 있을까?
물론, Test test1 = new Test(); 와 같이 Test 타입의 객체를 생성하는 코드가 눈에 보이지는 않지만,
클래스 Test 내에 printNonStatic이라는 메서드를 생성할 때, 그 메서드는 non-static 멤버이므로 클래스가 아닌 객체 안에 존재하게 되고, 메서드가 생성되었다는 것은 곧 객체도 생성되어 있다는 것과 같다.
따라서, 메서드의 생성과 동시에 존재하는 그 객체 안에는 t도 존재할 것이므로 위와 같이 접근할 수 있는 것이다.
즉, 위 메서드에서 접근하는 t는 클래스 안에 있는 t가 아니라 객체 내의 t이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
package section3;
public class Test {
static int s = 0;
int t = 0;
// 어떤 클래스 안에 있는 static 메서드 안에서
public static void printStatic() {
System.out.println("s= " + s); // static 멤버에 access하는 것은 문제가 없다.
// System.out.println("t= " + t); // non-static 멤버에 access할 수 없다.
}
public void printNonStatic() {
System.out.println("s= " + s);
System.out.println("t= " + t);
}
public static void main(String[] args) {
s = 100; // s는 static이므로 이상 없음
// t = 100; // t는 non-static이므로 에러 발생
printStatic(); // static
// printNonStatic(); // non-static
// non-static 멤버들을 사용하기 위해서는 객체를 생성해야 함
Test test1 = new Test();
test1.t = 100;
test1.printNonStatic();
}
}
|
cs |
Q. 다른 클래스에 속한 static 멤버는 어떻게 엑세스하는가?
위에서 생성한 Test class에 추가적으로 TestTest class를 생성하였다.
간단하게 말하면, static 멤버는 class 멤버이므로 "클래스명.static멤버" 의 형식으로 접근하면 된다.
이는 마치 non-static 멤버가 object 멤버이므로 "객체를 참조하는 변수명.object멤버"의 형식으로 접근하는 것과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package section3;
public class TestTest {
public static void main(String[] args) {
Test test1 = new Test();
test1.t = 10; // non-static 멤버(객체에 접근)
test1.printNonStatic(); // non-static 멤버
Test.s = 100; // static 멤버(클래스에 접근)
Test.printStatic(); // static 멤버
/* 아래 코드는 오류는 나지 않지만, 잘못된 코드
// static 멤버인데, 속해 있지도 않은 객체에서 접근하려고 하고 있다.
test1.s = 100; // static 멤버
test1.printStatic(); // static 멤버
*/
}
}
|
cs |
Q. static 메서드/필드의 용도는 무엇인가?
물론, 자바에서 static을 필수적으로 사용해야 하는 경우는 main 메서드뿐이다.
하지만, static 멤버를 사용하는 것이 더 유용한 경우가 있다.
예컨대, 상수 혹은 클래스 당 하나만 유지하고 있으면 되는 값(혹은 객체)의 경우가 있다.
Math.PI 가 3.141592....의 값을 가질 때, 프로그램 전체에서 값을 바꿀 이유가 없기 때문에 static 멤버로 만들어 두고 사용하면 된다.
또한 순수하게 기능만으로 정의되는 메서드. 대표적인 예로 수학함수들의 경우 static을 사용하면 유용하다.
Math.abs(k), Math.sqrt(n), Math.min(a,b)와 같은 메서드들은 어느 클래스에 존재하든 독립적으로 매개변수에 따라서만 메서드의 결과가 달라지므로 static 멤버로 사용할 수 있다.
Theme. public
접근 제어: public, private, default, protected 에 대하여 간략하게 정리하면,
- public: 클래스 외부에서 접근이 가능하다.
- private: 클래스 내부에서만 접근이 가능하다.(클래서 외부에서 접근 불가능)
- default: 동일 패키지에 있는 다른 클래스에서 접근 가능하다.
- protected: 동일 패키지의 다른 클래스와 다른 패키지의 하위클래스에서도 접근 가능하다.
이중 public 과 private에 대해 좀 더 자세히 알기 위해,
"데이터 캡슐화"에 대해 먼저 알아보자.
결론부터 말하자면, 어떤 클래스 안에 있는 데이터 멤버들을 모두 private으로 만들어서 다른 클래스에서는 접근하지 못하게 만들어 놓고, 대신 필요한 경우에 접근할 수 있도록 하는 것이다. 접근할 수 있는 방법은 해당 클래스에서 public한 get/set 메서드를 제공하는 것이다.
즉, 다른 클래스에서 접근하고자 할 때, public 한 get/set 메서드를 통해 private한 데이터 멤버들에 접근하는 것이다. 그러한 메서드를 getter, setter 메서드라고도 한다.
클래스 내부에 있는 데이터가 무분별하게 접근되는 것을 방지함으로써 의도하지 않은 프로그램 오류 등을 방지하기 위해 private를 사용하고, 전형적인 객체지향 프로그래밍에서는 모든 데이터 멤버들을 private으로 만들고 필요한 경우에 getter, setter를 제공한다.
'Java > Java로 배우는 자료구조' 카테고리의 다른 글
자료구조 3.4 추상클래스와 인터페이스 (0) | 2023.02.26 |
---|---|
자료구조 3.1 상속(Inheritance) (0) | 2023.02.18 |
자료구조 2-2. 메서드와 생성자 (0) | 2023.02.13 |
자료구조 2-1. 클래스, 객체, 참조변수(2) (0) | 2023.02.13 |
자료구조 2-1. 클래스, 객체, 참조변수(1) (0) | 2023.02.08 |