프로그래밍 언어/JAVA

[JAVA] 자바의 Static

Rateye 2021. 1. 19. 14:51
728x90
반응형

static

정적(static) 멤버 변수 = 클래스 멤버 변수

  • 클래스가 메모리(메서드 영역)에 로딩될 때 함께 로딩되는 멤버변수
  • 인스턴스 생성과 상관없음 인스턴스 생성 없이 클래스명만으로 접근 가능 클래스명.정적멤버변수명 또는 클래스명.정적메서드명()
  • 정적 멤버변수의 경우 모든 인스턴스에서 하나의 값을 공유함 공유 메모리 공간을 사용하므로 모든 인스턴스가 주소값 공유
  • 로컬변수는 메서드 호출 및 종료와 관계있고 인스턴스변수는 인스턴스 생성 및 소멸과 관계있고, 클래스변수는 클래스 로딩(프로그램 시작) 및 클래스 제거(프로그램 종료)와 관계있다.
class StaticEx {
	static int a = 10; // 클래스(static = 정적) 멤버변수 선언
	int b = 20; // 인스턴스 멤버변수 선언
}
		// StaticEx 클래스의 클래스(정적) 변수 a 는
		// 클래스가 메모리에 로딩될 때 함께 로딩되므로
		// 인스턴스 생성 전에 메모리에 로딩되며, 인스턴스 생성 없이도
		// 클래스명만으로 접근 가능한 멤버이다!
		System.out.println("StaticEx.a = " + StaticEx.a); // 접근 가능
		//		System.out.println("StaticEx.b = " + StaticEx.b); // 오류 발생!
		// => 인스턴스 멤버변수 b는 반드시 인스턴스를 통해서만 접근 가능!
		
		// StaticEx 클래스의 인스턴스 생성
		StaticEx se = new StaticEx();
		// => 인스턴스 생성 시 Heap 영역에 인스턴스 멤버도 함께 로딩됨 
		System.out.println("se.a = " + se.a);
		// => 정적 멤버변수도 일반 인스턴스 멤버변수처럼 접근할 수 있으나
		//    가급적 static 접근 방식(클래스명.변수명)으로 접근하도록 하자!
		
		System.out.println("se.b = " + se.b); // 반드시 인스턴스를 통해 접근
		
		// 변수 a의 값을 100으로 변경, b의 값을 200으로 변경 후 출력
		//		se.a = 100;
		StaticEx.a = 100;
		se.b = 200;
		
		System.out.println("변경 후 StaticEx.a = " + StaticEx.a);
		System.out.println("변경 후 se.a = " + se.a);
		System.out.println("변경 후 se.b = " + se.b);
		System.out.println("==============================");
		
		StaticEx se2 = new StaticEx();
		// 인스턴스를 새로 생성하더라도 정적 멤버변수는
		// 하나의 메모리 공간을 모든 인스턴스에서 공유하므로
		// 하나의 인스턴스에서 값을 변경하면 모든 인스턴스가 변경된 값을 공유
		System.out.println("se2.a = " + se2.a); // 변경된 100 이 출력됨
		System.out.println("se2.b = " + se2.b); // 새 인스턴스 값 20 출력됨
		
		System.out.println("==============================");
		
		// 대표적인 static 멤버변수의 예
		// 수학관련 기능을 제공하는 Math 클래스의 멤버변수는 모두 static
		double pi = 3.1415926535;
		System.out.println("파이값 : " + pi);
		
		// Math 클래스에 상수 PI 가 제공되며 static 멤버변수로 제공됨
		// => 별도의 인스턴스 생성없이 클래스명만으로 접근 가능
		System.out.println("파이값 : " + Math.PI);
		
		// 정수를 다루는 Integer 클래스의 멤버변수는 모두 static
		System.out.println("INT형 표현범위 최소값 : " + Integer.MIN_VALUE);
		System.out.println("INT형 표현범위 최대값 : " + Integer.MAX_VALUE);

정적(static) 메서드

  • 메모리 로딩 시점은 정적 멤버변수와 동일함
  • 호출 방법도 정적 멤버변수와 동일함
  • 메서드 정의 시 리턴타입 앞에 static 키워드를 붙여서 정의
  • static 메서드 내에서는 레퍼런스 this 사용 불가!
    레퍼런스 this 에 저장되는 인스턴스 주소는 인스턴스가 생성되는 시점에서 만들어지므로 static 멤버가 로딩되는 시점에서는 접근 불가능하다!
  • static 메서드 내에서는 인스턴스 변수 사용 불가!
    레퍼런스 this 사용 불가 사유와 동일함
class StaticEx2 {
	
	public void normalMethod() {
		System.out.println("일반 메서드 normalMethod()");
	}
	
	public static void staticMethod() {
		System.out.println("정적 메서드 staticMethod()");
	}
	
}

class StaticEx3 {
	// static 멤버변수와 static 메서드 정의 시 주의사항!
	private static int a = 10; // 클래스 로딩 시(인스턴스 생성 전) 함께 로딩됨
	private int b = 20; // 인스턴스 생성 시 로딩됨
	
	// ====================================
	// static 멤버변수 a 를 리턴하는 Getter 메서드 정의
	public static int getA() {
		return a;
	}
	
	// static 멤버변수 a의 값을 전달받아 초기화하는 Setter 메서드 정의
	public static void setA(int a) {
		// static 메서드 내에서는 레퍼런스 this 사용 불가!
		// => 레퍼런스 this 에 저장되는 인스턴스 주소는
		//    인스턴스가 생성되는 시점에서 만들어지므로
		//    static 멤버가 로딩되는 시점에서는 접근 불가능하다!
//		this.a = a; // 오류 발생!
		
		// 레퍼런스 this 대신 클래스명을 통해 접근하면 된다!
		StaticEx3.a = a;
	}
	
	public static void print() {
		// static 메서드 내에서는 인스턴스 멤버변수 사용 불가
		// => 클래스(static 멤버) 로딩 시점에서는
		//    인스턴스 멤버는 생성되기 전이기 때문(this 와 이유가 동일)
		System.out.println("a = " + a);
//		System.out.println("b = " + b); // 오류 발생! 접근 불가!
		
		StaticEx3 ex = new StaticEx3();
		System.out.println("ex.b = " + ex.b);
	}
	
	// ====================================
	public int getB() {
		return b;
	}
	public void setB(int b) {
		this.b = b;
	}
	
}
		// 인스턴스 생성 전 클래스명을 사용하여 멤버메서드 호출
		StaticEx2.staticMethod(); // 정적 멤버 메서드 호출 가능
		//		StaticEx2.normalMethod(); // 인스턴스 멤버 메서드는 호출 불가능
		
		// 인스턴스 생성 후 멤버메서드 호출
		StaticEx2 se = new StaticEx2();
		se.normalMethod();
		se.staticMethod(); // 인스턴스 통해 접근해서 메서드 호출도 가능하나
		                   // 클래스명으로 접근하는 것을 권장함
		
		System.out.println("=============================");
		// static 멤버도 private 접근제한자 사용 시 외부클래스에서 접근 불가
		//		System.out.println("StaticEx3.a = " + StaticEx3.a); // 오류 발생
		
		// 따라서, Getter/Setter 를 사용하여 static 멤버에 접근해야함
		// => 만약, 일반메서드로 정의할 경우 클래스명만으로 접근 불가능하며
		//    인스턴스 생성을 통해서만 접근해야함
		StaticEx3 ex3 = new StaticEx3();
		System.out.println("ex3.getA() = " + ex3.getA());
		
		// static 메서드로 정의 시 클래스명만으로 접근이 가능해진다!
		System.out.println("StaticEx3.getA() = " + StaticEx3.getA());
		
		
		System.out.println("========================================");
		
		// static 메서드 활용 예
		// => Math 클래스의 모든 메서드는 static 메서드로 제공됨
		//    수학관련 기능이 필요할 경우 클래스명만으로 메서드 호출 가능
		System.out.println("-5 의 절대값 : " + Math.abs(-5));

static 멤버(클래스 멤버)와 인스턴스 멤버의 생성 시점 차이

  • static 멤버는 클래스 내에서 위치와 상관없이 순차적으로 로딩됨
  • static 멤버(main() 메서드 포함) 로딩이 끝난 후 main() 메서드 호출됨
  • main() 메서드 내에서 인스턴스 생성 시 인스턴스 멤버가 로딩
  • 인스턴스 멤버 로딩이 끝난 후 생성자 호출됨
public class Ex {

	public int b = callB();
	// => 인스턴스 생성 시점에 로딩되어 callB() 메서드를 호출(4번)
	
	public static int a = callA();
	// => 클래스 로딩 시점에 로딩되어 callA() 메서드를 호출(1번)

	public Ex() { // => 인스턴스 생성 시점에 로딩
		// 모든 인스턴스 멤버가 로딩된 후 호출됨(5번)
		System.out.println("Ex 클래스의 인스턴스 생성됨!");
	}
	
	public static int callA() { // => 클래스 로딩 시점에 로딩
		System.out.println("static 변수 a 로딩!");
		return 0;
	}
	
	public int callB() { // => 인스턴스 생성 시점에 로딩
		System.out.println("인스턴스 변수 b 로딩!");
		return 0;
	}

	public static void main(String[] args) { // => 클래스 로딩 시점에 로딩
		// => 단, 로딩시점에 호출되지는 않는다!
		// => 모든 static 멤버의 로딩이 끝난 후 자동으로 main() 메서드 호출
		
		
		System.out.println("main() 메서드 호출됨!"); // 3번
		
		Ex ex = new Ex(); // 인스턴스 생성
		
	}
	
	public static int c = callC();
	// => 클래스 로딩 시점에 로딩되어 callC() 메서드를 호출(2번)
	
	public static int callC() { // => 클래스 로딩 시점에 로딩
		System.out.println("static 변수 c 로딩!");
		return 0;
	}

}

싱글톤 디자인 패턴 Singleton Design Patter

  • 프로그램에서 단 하나뿐인 유일한 객체(인스턴스) = 싱글톤 객체
  • 싱글톤 객체를 사용하여 프로그램을 작성하는 기법을 싱글톤 디자인 패턴이라고 한다.
  • 새로운 인스턴스 생성이 불가능하게 하며, 미리 생성된 하나의 인스턴스를 모든 참조변수에서 공유해서 사용

싱글톤 패턴 작성 순서

  1. 객체가 생성되면 안되기 때문에 외부에서 생성자 호출을 못하도록 생성자 정의 시 접근제한자를 private 으로 선언
  2. 자신의 클래스 내에서 직접 객체를 생성하여 참조변수에 저장 참조변수의 접근제한자를 private 으로 선언하여 접근 제한 참조변수를 static 변수로 선언하여 객체 생성 없이 로딩
  3. 생성된 인스턴스를 외부로 리턴하는 Getter 를 정의 static 변수를 리턴하므로 Getter 메서드도 static 으로 선언
class SingletonClass {
	
	// 1. 생성자의 접근제한자를 private 으로 선언하여
	//    외부에서 인스턴스 생성(생성자 호출)을 못하도록 제한
	private SingletonClass() {}
	
	// 2. 자신의 클래스 내에서 직접 인스턴스를 생성하여 참조 변수에 저장
	//    => 이 때, 인스턴스 생성이 불가능하므로 인스턴스 변수 접근 불가능
	//       따라서, 인스턴스 생성 없이도 접근 가능하도록 static 으로 선언
	//    => 또한, 외부에서 변수에 접근하여 함부로 값을 변경하지 못하도록
	//       접근제한자를 private 으로 선언
	private static SingletonClass instance = new SingletonClass();
	
	// 3. 인스턴스 생성 후 인스턴스가 저장된 멤버변수도
	//    접근제한자로 인해 외부에서 접근이 불가능하므로
	//    대신 인스턴스를 리턴해주는 Getter 메서드 정의(Setter 불필요)
	//    => 이 때, 인스턴스 생성 없이도 호출 가능하도록 static 으로 선언
	public static SingletonClass getInstance() {
		return instance;
	}
	
}
		// 접근제한자가 private 으로 선언된 생성자 호출이 불가능하므로
		// SingleClass 의 인스턴스 생성이 불가능해짐
		//		SingletonClass sc = new SingletonClass();
		//		SingletonClass sc2 = new SingletonClass();
		
		// static 으로 선언된 정적 멤버변수 instance 에 접근하여
		// 미리 생성되어 있는 인스턴스를 가져올 수 있다!
		// => 외부에서 함부로 값을 변경할 수 없도록 private 접근제한자 적용
		//		SingletonClass sc = SingletonClass.instance;
		//		SingletonClass.instance = null;
		
		// 외부에서 접근할 수 있도록 Getter 메서드를 제공하므로
		// Getter 를 호출하여 생성된 인스턴스를 전달받아 사용할 수 있음
		// => static 메서드인 getInstance() 메서드를 호출하여 인스턴스 리턴
		SingletonClass sc = SingletonClass.getInstance();
		
		// 참조변수 sc2 에도 동일한 인스턴스를 리턴받기
		SingletonClass sc2 = SingletonClass.getInstance();

싱글톤디자인패턴 예제

public class Test4 {

	public static void main(String[] args) {
		// 생성된 인스턴스를 두 번 가져오기
		//		SingletonTest st = new SingletonTest(); // 인스턴스 생성 불가
		SingletonTest st = SingletonTest.getInstance();
		SingletonTest st2 = SingletonTest.getInstance();
		
		System.out.println(st.num + ", " + st2.num); // 10, 10 출력
		
		// 인스턴스 내의 인스턴스 변수 값을 변경하면 나머지도 공유됨
		st.num = 100;
		System.out.println(st.num + ", " + st2.num); // 100, 100 출력
		
		System.out.println("=============================");
		
		JavaTeacher jt = JavaTeacher.getInstance();
		JavaTeacher jt2 = JavaTeacher.getInstance();
		
		jt2.teacherName2 = "이순신";
		System.out.println(jt.teacherName1 + ", " + jt2.teacherName1);
		System.out.println(jt.teacherName2 + ", " + jt2.teacherName2);
		
	}

}

// 1. SingletonTest 클래스 정의 => 싱글톤 디자인 패턴 적용
class SingletonTest {
	
	// 1. 생성자 정의
	private SingletonTest() {}
	
	// 2. 인스턴스 생성
	private static SingletonTest instance = new SingletonTest();

	// 3. Getter 정의
	public static SingletonTest getInstance() {
		return instance;
	}
	
	// ==============================================================
	// 싱글톤 패턴으로 생성된 객체 확인을 위한 인스턴스 변수 1개 선언
	int num = 10;
	
}

// 2. JavaTeacher 클래스 정의 => 싱글톤 패턴 적용
class JavaTeacher {
	String teacherName1 = "홍길동";
	String teacherName2 = "강감찬";
	
	private JavaTeacher() {}
	
	private static JavaTeacher instance = new JavaTeacher();

	public static JavaTeacher getInstance() {
		return instance;
	}
	
}
728x90
반응형