728x90
반응형
static 변수

 

package package_01;

class HouseHong{
	String lastname = "홍";
}

public class TestClass{
	public static void main(String[] args) {
		HouseHong hong1 = new HouseHong();
		HouseHong hong2 = new HouseHong();
	}
}

 

이와 같은 클래스를 만들고 객체를 생성하면 객체마다 객체변수 lastname 을 저장하기 위한 메모리가 별도로 할당된다.

하지만 HouseHong 클래스의 lastname은 어떤 객체이던지 동일한 값인 "홍" 이어야 한다.

이렇게 항상 값이 변하지 않는 경우라면 static 사용시 메모리의 이점을 얻을 수 있다.

class HouseHong{
	static String lastname = "홍";
}

 

위와 같이 lastname 변수에 static 키워드를 붙이면 자바는 메모리 할당을 딱 한번만 하게 되어 메모리 사용에 이점이 있다.

 

만약 HouseHong 클래스의 lastname 값이 변경되지 않기를 바란다면 static 키워드 앞에 final (c의 const) 이라는
키워드를 붙이면 된다. final 키워드는 한번 설정되면 그 값을 변경할 수 없다. 변경 시도시 오류가 발생한다.

 

static을 사용하는 또 한가지 이유로는 공유 개념을 들 수 있다.

static으로 설정하면 같은 곳의 메모리 주소만을 바라보기 때문에 static 변수의 값을 공유하게 되는 것이다.

 

다음의 예를 보면 더욱 명확하게 파악할 수 있다.

웹 사이트 방문시마다 조회수를 증가시키는 Counter 클래스가 다음과 같다고 가정한다.

class Counter{
	int count = 0;
	
	Counter(){
		this.count++;
		System.out.println(this.count);
	}
}

public class TestClass{
	public static void main(String[] args) {
		Counter c1 = new Counter();		// 1
		Counter c2 = new Counter();		// 1
	}
}

 

c1, c2 객체 생성시 생성자에 멤버변수인 count 의 값을 1 씩 증가하더라도
c1과 c2의 count는 서로 다른 메모리를 가리키고 있기 때문에 원하던 결과(카운터가 증가된 결과)가 나오지 않는다.

멤버변수는 항상 독립적인 값을 갖기 때문이다.

class Counter{
	static int count = 0;
	
	Counter(){
		count++;
		System.out.println(count);
	}
}

public class TestClass{
	public static void main(String[] args) {
		Counter c1 = new Counter();		// 1
		Counter c2 = new Counter();		// 2
	}
}

 

Counter 클래스의 count 멤버변수에 static 키워드를 붙이며 count 값이 공유되어 위처럼 증가되어 출력된다.

보통 변수의 static 키워드는 프로그래밍시 메모리의 효율보다는 공유의 목적으로 훨씬 더 많이 사용한다.

 


static 메서드

 

static 이라는 키워드가 메서드 앞에 붙으면 스태틱 메서드가 된다.

class Counter{
	static int count = 0;
	
	Counter(){
		count++;
		System.out.println(count);
	}
	
	/*
	 * Counter 클래스의 
	 * 접근제어자 public 권한의
	 * int형 타입의 getCount 메서드에
	 * static 키워드로 static method로 선언
	*/
	public static int getCount() {
		return count;
	}
}

public class TestClass{
	public static void main(String[] args) {
		Counter c1 = new Counter();		// 1
		Counter c2 = new Counter();		// 2
		
		// static method 는 클래스를 이용하여 호출한다.
		System.out.println(Counter.getCount());
	}
}

 

스태틱 메서드 안에서는 멤버변수 접근이 불가하다.
위 예에서는 count 멤버 변수가 static 변수이기 때문에 스태틱 메서드에서 접근이 가능한 것이다.

- 클래스내 static 멤버변수 == static 메서드 : 조건 성립시에만 접근 가능!

 

보통 스태틱 메서드는 유틸리티성 메서드를 작성할 때 많이 사용된다.

예를 들어 "오늘의 날짜 구하기", "숫자에 콤마 추가하기" 등의 메서드를 작성할 때에는 클래스 메서드를 사용한다.

 

다음은 "날짜"를 구하는 Util 클래스의 예이다.

import java.text.SimpleDateFormat;
import java.util.Date;

class Util{
	public static String getCurrentDate(String fmt) {
		SimpleDateFormat sdf = new SimpleDateFormat(fmt);
		return sdf.format(new Date());
	}
}

public class TestClass{
	public static void main(String[] args) {
		System.out.println(Util.getCurrentDate("yyyyMMdd"));	// 20230327
	}
}

 


싱글톤 패턴 (single pattern)

 

static에 대한 개념이 생겼기 때문에 싱글톤을 이해하는 것이 어렵지 않다.

싱글톤은 단 하나의 객체만을 생성하게 강제하는 패턴이다.

즉 클래스를 통해 생성할 수 있는 객체는 Only One, 즉, 한 개만 되도록 만드는 것이 싱글톤이다.

 

다음의 예를 보자.

class Singleton{
	private Singleton() {
	}
}

public class TestClass{
	public static void main(String[] args) {
		Singleton singleton = new Singleton(); // Compile Error
		// The constructor Singleton() is not visible
	}
}

 

위와 같은 코드를 작성하면 컴파일 에러가 발생한다.

왜냐하면 Singleton 클래스의 생성자에 private 키워드로 다른 클래스에서 Sigleton 클래스의 생성자로서의 접근을
막았기 때문이다. 이렇게 생성자를 private 으로 만들어 버리면 다른 클래스에서 Singleton 클래스를 new를 이용하여
생성할 수 없게 된다.

 

new 를 이용하여 무수히 많은 객체를 생성한다면 싱글톤에 정의에 맞지 않는다.

그래서 일단 new 객체를 생성할 수 없도록 막은 것이다.

 

그렇다면 Singleton 클래스의 객체는 어떻게 생성할 수 있을까.

class Singleton{
	private Singleton() {
	}
	
	public static Singleton getInstance() {
		return new Singleton();		// 같은 클래스 이므로 생성자 호출 가능
	}
}

public class TestClass{
	public static void main(String[] args) {
//		Singleton singleton = new Singleton(); // Compile Error
		Singleton singleton = Singleton.getInstance();
	}
}

 

위와 같이 코드를 변경하면 이제 getInstace 라는 static 메서드를 이용하여 Singleton 클래스의 객체를 생성할 수 있다.

하지만 getInstance를 호출할 때마다 새로운 객체가 생성된다. 그렇다면 싱글톤이 아니다. 해결방법은 무엇일까?

 

class Singleton{
	private static Singleton one;
	private Singleton() {
	}
	
	public static Singleton getInstance() {
		if(one == null) {
			one = new Singleton();
		}
		return one;
	}
}

public class TestClass{
	public static void main(String[] args) {
//		Singleton singleton = new Singleton(); // Compile Error
		Singleton singleton1 = Singleton.getInstance();
		Singleton singleton2 = Singleton.getInstance();
		System.out.println(singleton1 == singleton2);	// true
	}
}

 

Singleton 클래스에 one이라는 static 변수를 두고 getInstance 메서드에서 one 값이 null 인 경우에만

객체를 생성하도록 하여 one 객체가 딱 한번만 만들어지도록 만들었다.

 

getInstance 메서드의 동작원리는

최초 getInstance가 호출되면 one이 null이므로 new에 의해서 객체가 생성이 된다.

이렇게 한번 생성이 되면 one은 static 변수이기 때문에 그 이후로는 null 이 아니게 된다.

그런 후에 getInstance 메서드가 호출되면 이제 one은 null이 아니므로 이미 만들어진 싱글톤 객체인 one을 항상 리턴한다.

 

main 메서드에서 getInstance를 두 번 호출하여 각각 얻은 객체가 같은 객체인지 조사해 보았다.

예상대로 "true"가 출력되어 같은 객체임을 확인할 수 있다.

 

위 예제의 Singleton 예제는 Thread Safe 하지 않다.

728x90
반응형

'Language > Java' 카테고리의 다른 글

Java - Code Sample  (0) 2023.04.13
Java - Exception  (0) 2023.03.27
Java - Access Modifier  (0) 2023.03.27
Java - Abstract Class  (0) 2023.03.27
Java - Polymorphism  (0) 2023.03.27

+ Recent posts