
기존의 내 코드들을 살펴보면 캡슐화가 하나도 이루어지지 않은 것을 알 수 있다. 가독성과 재사용면에서 캡슐화되지 않은 코드는 보기가 좋지 않고, 이는 내가 실무에 가서도 마찬가지일 것이다. 따라서 앞으로는 최대한 코드를 캡슐화해서 적을 것인데, 그러려면 static으로 변수를 선언해야한다. 따라서 static을 한번 정리하고 가려고한다.
다음과 같은 HouseLee 클래스갈있다고 하자.
class HouseLee {
String lastname = "이";
}
public class Sample {
public static void main(String[] args) {
HouseLee lee1 = new HouseLee();
HouseLee lee2 = new HouseLee();
}
}
HouseLee 클래스를 만들고 객체를 생성하면 객체마다 객체 변수 lastname을 저장하기 위한 메모리가 별도로 할당된다.
하지만 가만히 생각해보면 HouseLee 클래스의 lastname은 어떤 객체이든지 동일한 값인 '이'이어야 할 것 같지 않나? 이렇게 항상 값이 변하지 않는다면 static을 사용해 메모리 낭비를 줄일 수 있다. 앞서 작성한 예제를 수정해보자.
class HouseLee {
static String lastname = "이";
}
public class Sample {
public static void main(String[] args) {
HouseLee lee1 = new HouseLee();
HouseLee lee2 = new HouseLee();
}
}
lastname 변수에 static 키워드를 붙이면 자바는 메모리 할당을 딱 한 번만 하게 되어 메모리를 적게 사용할 수 있다.
여기서 만약 HouseLee클래스의 lastname값이 변경되지 않기를 바란다면 static 키워드 앞에
final이라는 키워드를 붙이면 된다.
final 키워드는 한 번 설정되면 그 값을 변경할 수 없다. 만약 변경하려고 하면 오류가 발생한다.
static을 사용하는 또 다른 이유는 값을 공유할 수 있기 때문이다.
static으로 설정하면 같은 메모리 주소만을 바라보기 때문에 static 변수의 값을 공유하게 된다.
다음의 예시를 보자. 객체를 생성할 때마다 숫자를 증가시키는 Counter 클래스가 다음과 같다.
class Counter {
int count = 0;
Counter() {
this.count++;
System.out.println(this.count);
}
}
public class Sample {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
}
}
위의 코드를 실행하면
1
1
의 결과가 나온다.
객체 c1,c2를 생성할 때 생성자에서 객체 변수인 count의 값을 1씩 증가시키더라도 c1,c2와 count는 서로 다른 메모리를 가리키고 있기 떄문에 원하던 결과(count가 증가된 결과)가 나오지 않는다. 객체 변수는 항상 독립적인 값을 갖기 때문이다.
이번에는 다음 예제를 살펴보자.
class Counter {
static int count = 0;
Counter() {
count++; // count는 더이상 객체변수가 아니므로 this를 제거하는 것이 좋다.
System.out.println(count); // this 제거
}
}
public class Sample {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
}
}
위 코드의 결과는
1
2
이렇게 원하는 결과가 출력되는 것을 알 수 있다.
int count = 0 앞에 static 키워드를 붙였더니 count값이 공유되어 count가 증가되어 출력된다.
보통 변수에 사용되는 static은 메모리의 효율을 높이기 위한 목적보다는 공유의 목적으로 훨씬 더 많이 사용된다.
static이라는 키워드가 메서드 앞에 붙을 수도 있다.
class Counter {
static int count = 0;
Counter() {
count++;
System.out.println(count);
}
public static int getCount() {
return count;
}
}
public class Sample {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
System.out.println(Counter.getCount()); // 스태틱 메서드는 클래스를 이용하여 호출
}
}
1
2
2
Counter 클래스에 getCount()라는 스태틱 메서드를 추가했다. 메서드 앞에 static을 추가하면 Counter.getCount() 와 같이 객체 생성 없이도 클래스를 통해 메서드를 직접 호출할 수 있다.
스태틱 메서드 안에서는
객체 변수 접근이 불가능하다. 이 예에서 count변수가 static변수이기 때문에 접근이 가능한 것이다.
특히 스태틱 메서드는 유틸리티성 메서드를 작성할 때 많이 사용된다. 예를 들어 "숫자에 콤마붙이기" 등의 메서드를 작성할 때 스태틱 메서드를 사용하는 것이 유리하다.
유틸리티성 메서드는 특정 클래스나 인스턴스에 종속되지 않고, 재사용이 가능하고 범용 기능을 제공한다. 특히 코드의 중복을 줄이고 가독성을 향상시킨다.
싱글톤 패턴은 자바의 디자인 패턴 중 하나이다. static에 대한 개념이 생겼기 때문에 싱글톤패턴은 어렵지 않다. 싱글톤 패턴은 단 하나의 객체만을 생성하게 강제하는 디자인 패턴이다. 다시 말해, 클래스를 통해 생성할 수 있는 객체가 한개만 되도록 만드는 것이 싱글톤이다.
class Singleton {
private Singleton() {
}
}
public class Sample {
public static void main(String[] args) {
Singleton singleton = new Singleton(); // 컴파일 오류가 발생한다.
}
}
이같이 코드를 작성하면 컴파일 오류가 발생한다. 왜냐하면 Singleton 클래스의 생성자에 private 접근제어자를 설정하여 다른 클래스에서 Singleton 클래스의 생성자로의 접근을 막았기 때문이다. 이렇게 생성자를 private으로 만들면 Singleton클래스를 다른 클래스에서 new 를 이용하여 생성할 수 없게 된다.
new 를 이용하여 무수히 많은 객체를 생성한다면 싱글톤의 정의에 어긋나지 않겠나? 그래서 일단 new로 객체생성이 안되게 막은 것이다.
그렇다면 Singleton 클래스의 객체는 어떻게 생성할까? 아래 코드를 보자
class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return new Singleton(); // 같은 클래스이므로 생성자 호출이 가능하다.
}
}
public class Sample {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
}
}
이같이 getInstance라는 스태틱 메서드를 이용하여 Singleton 클래스의 객체를 생성할 수 있다. 하지만 getInstance를 호출할 때마다 새로운 객체가 생성되기 때문에 이 역시 싱글톤이 아니다. 아래 수정된 코드를 보자
class Singleton {
private static Singleton one;
private Singleton() {
}
public static Singleton getInstance() {
if(one==null) {
one = new Singleton();
}
return one;
}
}
public class Sample {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2); // true 출력
}
}
Singleton 클래스에 one이라는 static 변수를 작성하고, getInstance 메서드에서 one값이 null인 경우에만 객체를 생성하도록하여 one 객체가 딱 한 번만 만들어지도록 했다.
처음 getInstance가 호출되면 one이 null이므로 new에 의해서 one 객체가 생성된다. 이렇게 한 번 생성되면 one은 static변수이기 때문에 그 이후로는 null이 아니다. 이어서 다시 getInstance 메서드가 호출되면 이미 만들어진 싱글톤 객체인 one을 항상 리턴한다. 따라서 main메소드에서 만든 singleton1와 singleton2는 같은 객체이다.