Java - static 키워드, 싱글톤디자인패턴

2020. 6. 14. 12:13Java

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
public class Ex {
 
    public static void main(String[] args) {
        /*
         * static 키워드
         * - 변수와 메서드에 사용 가능
         * - 인스턴스 생성 전, 클래스가 메모리에 로딩되는 시점에 static 멤버도 함께 로딩됨
         *   => 인스턴스 생성과 무관하며, 모든 인스턴스에서 하나의 메모리를 공유함
         * - 인스턴스 주소를 갖는 참조변수 대신 클래스명만으로 접근 가능
         * 
         * static 변수(= 클래스 변수 = 정적 변수)
         * - 변수의 데이터타입 앞에 static 키워드를 사용
         * - 인스턴스 생성 없이 클래스가 메모리에 로딩될 때 변수도 함께 로딩됨
         *   => 클래스명만으로 접근 가능하며, 모든 인스턴스에서 하나의 변수를 공유함
         * 
         * < 기본 사용 문법 >
         * 선언 : [접근제한자] static 데이터타입 변수명;
         * 사용 : 클래스명.변수명
         */
        
        // static 으로 선언된 클래스 변수 사용 시 클래스명만으로 접근 가능하며
        // 인스턴스 생성 없이도 바로 사용 가능
        System.out.println("StaticTest.a : " + StaticTest.a);
        
        // 인스턴스 변수는 static 변수 사용 방법으로 접근할 수 없으며
        // 인스턴스 생성 없이는 사용 불가능
//        System.out.println("StaticTest.b : " + StaticTest.b); // 오류 발생!
        
        StaticTest st1 = new StaticTest();
        StaticTest st2 = new StaticTest();
        
        System.out.println("st1.a : " + st1.a + ", st2.a : " + st2.a);
        // 두 개의 인스턴스 중 하나의 인스턴스에서만 static 변수 a 의 값을 변경
        st1.a = 99;
        // 하나의 인스턴스(st1) 에서만 static 변수의 값을 변경해도 st2 인스턴스에 동일하게 적용
        System.out.println("st1.a : " + st1.a + ", st2.a : " + st2.a);
        
        System.out.println("-----------");
        
        System.out.println("st1.b : " + st1.b + ", st2.b : " + st2.b);
        // 두 개의 인스턴스 중 하나의 인스턴스에서만 static 변수 b 의 값을 변경
        st1.b = 99;
        // 하나의 인스턴스(st1) 에서만 인스턴스 변수의 값을 변경하면
        // 다른 인스턴스(st2) 의 인스턴스 변수에는 아무런 영향이 없다!
        System.out.println("st1.b : " + st1.b + ", st2.b : " + st2.b);
        
        
        System.out.println("=================================");
        
        // static 변수는 클래스명.변수명 형태로 접근한다.
        // 또한, 인스턴스 생성과 무관하므로 인스턴스 생성 없이 클래스명만으로 접근 가능하다!
        Student.schoolName = "아이티윌 부산교육센터";
        
        Student s = new Student();
//        s.schoolName = "아이티윌 부산교육센터";
        
        s.name = "홍길동";
        s.age = 20;
        
//        System.out.println("학교명 : " + s.schoolName);
        System.out.println("학교명 : " + Student.schoolName);
        
        System.out.println("이름 : " + s.name);
        System.out.println("나이 : " + s.age);
        
        System.out.println("------------------");
        
        Student s2 = new Student();
//        s2.schoolName = "아이티윌 부산교육센터";
        
        s2.name = "이순신";
        s2.age = 44;
 
//        System.out.println("학교명 : " + s2.schoolName);
        
        // static 변수로 선언된 멤버변수 값을 다른 인스턴스에서 변경하면
        // 동일한 클래스로 생성된 모든 인스턴스에서 메모리를 공유하므로 동일한 값이 적용됨
        System.out.println("학교명 : " + Student.schoolName);
        
        System.out.println("이름 : " + s2.name);
        System.out.println("나이 : " + s2.age);
        
    }
 
}
 
class StaticTest {
    // static 변수(= 클래스 변수 = 정적 변수) => 클래스 로딩 시 메모리에 로딩됨
    static int a = 10;
    
    // 인스턴스 변수 => 인스턴스가 생성될 때 메모리에 로딩됨
    int b;
}
 
class Student {
    // static 변수(= 클래스 변수 = 정적 변수) => 클래스 로딩 시 메모리에 로딩됨
    static String schoolName; 
    
    // 인스턴스 변수 => 인스턴스가 생성될 때 메모리에 로딩됨
    String name;
    int age;
}
 
public class Ex2 {
 
    public static void main(String[] args) {
        /*
         * static 메서드(= 정적 메서드 = 클래스 메서드)
         * - static 변수와 마찬가지로 클래스가 메모리에 로딩될 때 함께 로딩됨
         *   (인스턴스 생성 전에 로딩됨)
         * - 인스턴스 생성 없이 클래스명만으로 호출 가능
         * - static 메서드 내에서는 인스턴스 변수 사용 불가
         *   => 인스턴스 변수가 생성되기 전에 static 메서드가 로딩되기 때문
         * - static 메서드 내에서는 레퍼런스 this 사용 불가
         *   => 레퍼런스 this 도 인스턴스 생성 시점에 메모리에 로딩되기 때문
         *   => 만약, 로컬변수와 static변수명이 같을 경우
         *      static 변수를 구별하기 위해 클래스명.static변수명 형태로 지정
         */
        
        // static 메서드는 static 변수와 마찬가지로 인스턴스 생성 없이 클래스명만으로 호출 가능
        StaticMethod.staticMethod(); // static 메서드 호출(클래스명.메서드명())
//        StaticMethod.normalMethod(); // 오류 발생! 인스턴스 메서드는 클래스명만으로 호출 불가!
        
        // StaticMethod 인스턴스 생성
        StaticMethod sm = new StaticMethod();
        sm.staticMethod(); // static 메서드 호출
        sm.normalMethod(); // 인스턴스 메서드 호출(인스턴스 생성 필수!)
        
    }
 
}
 
class StaticMethod {
    static int staticMember; // static 변수
    int instanceMember; // 인스턴스 변수
    
    public static void staticMethod() { // static 메서드
        System.out.println("staticMethod() 호출됨!");
        
        System.out.println("staticMember 변수 값 : " + staticMember);
        
//        System.out.println("instanceMember 변수 값 : " + instanceMember); // 오류 발생!
        // static 메서드 로딩 시점은 인스턴스 변수가 생성되기 전이므로 
        // static 메서드 내에서는 인스턴스 변수 접근이 불가능하다!
    }
    
    public void normalMethod() { // 일반 메서드
        System.out.println("normalMethod() 호출됨!");
        
        System.out.println("staticMember 변수 값 : " + staticMember);
        System.out.println("instanceMember 변수 값 : " + instanceMember);
    }
    
    public void setInstanceMember(int instanceMember) {
        // 인스턴스 변수와 로컬 변수의 이름이 동일할 때 인스턴스 변수를 구별하기 위해
        // 인스턴스 변수명 앞에 레퍼런스 this 를 사용하여 this.인스턴스변수명 형태로 지정
        this.instanceMember = instanceMember;
    }
    
    public static void setStaticMember(int staticMember) {
        // static 메서드 내에서 레퍼런스 this 사용 불가능
        // => 레퍼런스 this 는 인스턴스 생성 시점에서 로딩되므로 static 메서드 로딩 시 존재X
//        this.staticMember = staticMember; // 오류 발생!
        
        // Setter 등의 메서드 내의 로컬변수명과 static 변수명이 같을 경우
        // static 변수를 지정하기 위해 클래스명.static변수명 사용
        StaticMethod.staticMember = staticMember;
        
    }
    
}
 
public class Ex3 {
    int b = check(2); // 인스턴스 변수(new Ex3() => 인스턴스 생성 시점에 로딩됨)
    // => 정수 2 출력(4등)
    
    static int a = check(1); // static 변수 => 클래스 로딩 시점에 로딩됨
    // => 이 때, static 메서드 check() 를 호출하여 정수 1 전달하므로
    //    정수 1 출력(1등)
    
    public static int check(int i) { // static 메서드
        System.out.println("call " + i);
        return 0;
    }
    
    public static void main(String[] args) { // 클래스 로딩 시점에 로딩됨
        // static 멤버 로딩이 끝난 후 main() 메서드가 호출됨
        System.out.println("main() 메서드 호출됨!"); // 3등
        
        Ex3 ex = new Ex3(); // 인스턴스 생성
    }
    
    static int c = check(3); // static 변수 => 클래스 로딩 시점에 로딩됨
    // => 이 때, static 메서드 check() 를 호출하여 정수 1 전달하므로
    //    정수 3 출력(2등)
 
}
public class Ex4 {
 
    public static void main(String[] args) {
        /*
         * Singleton Design Pattern(싱글톤 디자인 패턴)
         * - 유일한 인스턴스 하나만 생성해서 공유하는 프로그래밍 작성 패턴
         * 
         * < 작업 순서 >
         * 1. 외부에서 인스턴스 생성이 불가능하도록 생성자를 private 으로 지정
         * 2. 외부에서 인스턴스 생성이 불가능하므로, 클래스 내에서 직접 인스턴스 생성
         * 3. 외부에서 인스턴스를 전달받을 수 있도록 Getter 를 사용하여 생성한 인스턴스 리턴
         */
        
//        SingletonClass sc = new SingletonClass(); // 오류 발생!
        // => 외부에서 생성자 호출이 불가능하므로 인스턴스 생성이 불가능!
        
        // SingletonClass 내에서 생성한 인스턴스를 리턴받기 위해 Getter 메서드 호출
        // => static 메서드이므로 클래스명.메서드명() 형태로 호출
        SingletonClass sc = SingletonClass.getInstance();
        
//        sc.instance = null;
        // => 외부에서 인스턴스가 저장된 변수(instance) 에 접근하는 것을 방지하기 위해
        //    해당 멤버변수도 private 접근제어자를 사용하여 외부로부터 숨겨야 한다!
        
        
        // 리턴받은 인스턴스를 사용하여 인스턴스 내의 멤버에 접근할 수 있다!
        sc.method();
        
        
        // 또 다른 참조변수를 선언하여 인스턴스 리턴받음
        SingletonClass sc2 = SingletonClass.getInstance();
        
        System.out.println("sc, sc2 는 같은 객체인가? " + (sc == sc2));
        
    }
 
}
 
class SingletonClass {
    // 1. 생성자 정의
    // => 외부에서 접근할 수 없도록(인스턴스 생성할 수 없도록) 접근제어자 private 지정
    private SingletonClass() {} 
    
    // 2. 인스턴스 생성
    // 자신의 클래스 내에서 직접 인스턴스 생성
    // => static 메서드인 getInstance() 메서드에서 접근할 수 있도록 static 변수로 선언
    // => 외부로부터 변수 접근을 차단하기 위해 접근제어자 private 지정
    private static SingletonClass instance = new SingletonClass(); 
 
    // 3. Getter 메서드 정의
    // 자신의 클래스 내에서 생성한 인스턴스를 외부로 리턴
    // => 외부에서 인스턴스 생성 없이도 호출할 수 있도록 static 메서드로 정의
    public static SingletonClass getInstance() {
        return instance;
    }
    
 
    // 테스트용 메서드
    public void method() {
        System.out.println("메서드 호출됨!");
    }
    
}
public class Test4 {
 
    public static void main(String[] args) {
//        Account acc = new Account(); // 싱글톤 패턴 클래스는 인스턴스 생성이 불가능
        
        // 생성된 인스턴스 리턴받기
        Account acc = Account.getInstance();
        
        acc.setAccountNo("111-1111-111");
        acc.setOwnerName("홍길동");
        acc.setBalance(50000);
        
        acc.showAccountInfo();
    }
 
}
 
class Account {
    // ------------------------------------------------
    // 싱글톤 패턴 적용
    private Account() {}
    
    private static Account instance = new Account();
    
    public static Account getInstance() {
        return instance;
    }
    // ------------------------------------------------
    
 
    private String accountNo;
    private String ownerName;
    private int balance;
    
    public String getAccountNo() {
        return accountNo;
    }
    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }
    public String getOwnerName() {
        return ownerName;
    }
    public void setOwnerName(String ownerName) {
        this.ownerName = ownerName;
    }
    public int getBalance() {
        return balance;
    }
    public void setBalance(int balance) {
        this.balance = balance;
    }
    
    public void showAccountInfo() {
        System.out.println("계좌번호 : " + accountNo);
        System.out.println("예금주명 : " + ownerName);
        System.out.println("현재잔고 : " + balance);
    }
    
}
cs

이번엔 static 키워드의 정의와 활용범위, 싱글톤 디자인 패턴에 대해서 배워보았다. static 이란 키워드는 첫 시간부터 한 번씩 봐왔던 것 같은데 이번에서야 배우게 되었다. static을 사용함으로 인해서 인스턴스화시킬때 보다 먼저 선언이 되고 인스턴스 변수와는 다른 방법으로 활용할 수 있다는 것도 알게 되었다. 코딩을 할 때 순서를 정함에 있어서 중요할 것 같았다. static을 사용함으로 인해서 인스턴스화를 했을 때 사용이 안되니 클래스명. static변수명을 활용하여 사용해야 한다는 것도 알게 되었다. 좀 복잡한 개념이긴 했지만 여러 번 복습하다 보면 이것 또한 어려울 건 없을 것 같았다. 그리고 싱글톤 디자인 패턴에 대해서도 배우게 되었는데 이건 하나의 인스턴스를 만들어서 여러 번 공유하여 이리저리 사용하기 위해서 사용되는 방법이라고 배웠다. 처음으로 좀 어렵게 다가왔던 개념 같았다. 수업들을 당시에는 명확하게 이해는 가지 않았고 집에 와서 다시 차분하게 보니 어떤 방법으로 사용되고 어떤 구조로 이루어지는지 이해가 되었다. private 접근 제한자를 활용하고 Getter를 활용하여 만들어진 하나의 인스턴스를 외부에서는 접근하지 못하고 불러서 활용만 가능하게 만들어진다는 개념이었는데 나중에 좀 더 심화과정에 들어가면 사용될 일이 많을 것 같아서 좀 더 심도 깊게 복습해나가야 할 것 같다.