정적(Static)이란?
정적(static)은 고정된이란 의미를 가지고 있습니다. Static이라는 키워드를 사용하여 Static변수와 Static메소드를 만들 수 있는데 다른말로 정적필드와 정적 메소드라고도 하며 이 둘을 합쳐 정적 멤버라고 합니다. (클래스 멤버라고도 합니다.) 정적 필드와 정적 메소드는 객체(인스턴스)에 소속된 멤버가 아니라 클래스에 고정된 멤버입니다. 그렇기에 클래스 로더가 클래스를 로딩해서 메소드 메모리 영역에 적재할때 클래스별로 관리됩니다. 따라서 클래스의 로딩이 끝나는 즉시 바로 사용할 수 있습니다.
정적(Static) 멤버 생성
Static 키워드를 통해 생성된 정적멤버들은 Heap영역이 아닌 Static영역에 할당됩니다. Static 영역에 할당된 메모리는 모든 객체가 공유하여 하나의 멤버를 어디서든지 참조할 수 있는 장점을 가지지만 Garbage Collector의 관리 영역 밖에 존재하기에 Static영역에 있는 멤버들은 프로그램의 종료시까지 메모리가 할당된 채로 존재하게 됩니다. 그렇기에 Static을 너무 남발하게 되면 만들고자 하는 시스템 성능에 악영향을 줄 수 있습니다.
* 사용제한자 - static
-static 제한자는 변수, 메서드에 적용되는 자바의 키워드이다.
-static 메서드나 변수는 해당 클래스의 객체 없이도 참조할 수 있다.
- static블록(static메서드, 정적 초기화자) 안에는 static 변수만 사용해야하고, static 메서드만 호출할 수 있다. 즉, static블록에서 non-static멤버를 객체 생성 없이 직접 참조할 수 없다.
-static 제한자는 지정된 변수와 메서드를 객체와 무관하게 만들어주기 때문에 this를 가질 수 없다.
- static메서드는 non-static메서드로 재정의(Overriding)될 수 없다.
- 대표적인 static 메서드는 애플리케이션의 main() 메서드이다.
* 정적변수 (static field)
- static변수는 모든 객체들이 공유하는 공유변수가 된다.
- 그리고 객체 생성 없이 클래스 이름만으로 참조가 가능하다.
- 정적 변수는 객체를 만들어 참조할 수도 있지만, 객체를 만들지 않고 클래스 이름만으로도 참조가 가능하기 때문에 이를 "클래스 변수"라고도 부른다.
* 정적메서드(static method)
- static 메서드는 static변수와 마찬가지로 해당 클래스의 객체 생성없이도 참조가 가능하게 해준다.
- static메서드에서 멤버를 참조할 때 주의해야 할 사항은 static메서드안에서는 non-static멤버를 객체생성없이 직접 참조할 수 없다.는 것.
- static 메서드 안에서는 static변수를 선언할 수 없다.
* 정적 초기화자(static initializer)
- 정적 초기화자는 static변수들의 초기화에 사용한다. 일반 멤버변수는 생성자에서 초기화하지만 static변수는 객체 생성없이도 사용해야하므로 생성자를 통해 초기화할 수 없다.
- 그래서 static변수는 정적초기화자를 통해 초기화를 한다.
- 정적 초기화자는 클래스가 로딩될때 생성자와 main() 메서드에 앞서 오직 단 한번만 실행되기 때문에 애플리케이션 살행 중 반드시 한번만 실행되어야 할 로직이 있다면 이곳에 기술하여 사용될 수 있다.
* 싱글톤 패턴(Singleton Pattern)
- 싱글톤 패턴은 어떤 클래스의 객체는 오직 하나임을 보장하며, 이 객체에 접근할 수 있는 전역적인 접촉점을 제공하는 패턴이다.
- 클래스 객체를 유일하게 하나만 생성하여 모든 곳에서 하나의 객체에 접근하게 하여, 전역의 개념으로 객체를 사용할 수 있다.
- 싱글톤 패턴은 객체의 생성을 제한하기 위해 사용한다.
실습1. 일반 멤버변수 vs 정적(static) 멤버변수
package: static_field
name: Count
package static_field;
public class Count {
//일반 멤버변수(instance field) 선언.
public int a;
//정적 멤버변수(static field) 선언.
public static int b;
}
package: static_field
name: MainClass
package static_field;
public class MainClass {
public static void main(String[] args) {
Count c1 = new Count();
c1.a++;
c1.b++;
System.out.println("일반 멤버변수 a:" + c1.a); //일반 멤버변수 a:1
System.out.println("정적 멤버변수 b:" + c1.b); //일반 멤버변수 b:1
Count c2 = new Count();
c2.a++;
c2.b++;
System.out.println("일반 멤버변수 a:" + c2.a); //일반 멤버변수 a:1
System.out.println("정적 멤버변수 b:" + c2.b); //일반 멤버변수 b:2
/*
- static멤버는 객체와 무관하기 때문에 클래스 이름만으로 참조하여 사용합니다.
- 원래는 stack에는 c2 실제 데이터는 객체별로 heap영역에 있었지만 static은
Data이라는 영역에 객체별로 분리되서 관리되는것이 아니라 하나의 영역으로 관리된다.
즉, 공유한다고 생각하자.
*/
Count.b++; //그래서 static변수는 c1.b, c2.b가 의미가 없다. 그래서 Count.b로 하면된다.
Count.b++;
System.out.println(c1.b); //4
System.out.println(c2.b); //4
System.out.println(Count.b); //4
}
}
실습 2.
package: static_.method
name: Count
package static_.method;
public class Count {
public int a;
public static int b;
//일반 메서드 선언.
//일반 메서드 안에서는 일반멤버변수와 정적멤버변수를 모두 참조할 수 있음.
public int method1() {
this.a = 10; //여기서 this.은 생략이 가능해서 우리는 지금까지 생략을 해왔었다.
return this.a + b;
}
//정적 메서드 선언.
public static int method2() {
/*
- static 메서드 내부에서는 static이 붙은 변수나 메서드만
참조할 수 있습니다.
- static블록 내부에서 non-static멤버를 참조하려면
객체 생성을 통해 참조해야 합니다.
*/
// a = 10;
Count c = new Count();
c.a = 10;
return c.a + b;
}
}
package: static_.method
name: MainClass
package static_.method;
public class MainClass {
public static void main(String[] args) {
/*
- static이 붙은 멤버는 객체 생성없이 클래스 이름으로
직접 참조가 가능합니다.
*/
System.out.println(Count.method2()); //10
Count.b += 50;
System.out.println(Count.method2()); //60
Count.b -= 20;
System.out.println(Count.method2()); //40
System.out.println("------------------");
// System.out.println(Count.method1()); (X) 객체를 생성해야지 사용가능.
Count c1 = new Count();
c1.a += 30;
System.out.println(c1.method1()); //3.141592653589793
Math.random(); // 그럼 이제 Math.는 정적이기때문에 객체생성없이 사용했던것으로 이해할수 있다.
System.out.println(Math.PI);
}
}
실습3. static은 언제 사용해야할까?
그럼 언제 static을 사용해야할까? 값을 똑같이 사용되어야 할경우
아래 예에서는 pi는 static으로 사용하면된다.
package static_.calc;
public class Calculator {
/*
- 계산기별로 색깔이 다를 수 있기 때문에 color같은 변수는
데이터를 공유시켜서는 안됩니다.
- 하지만 pi같은 원주율값은 계산기마다 동일하기 때문에
공유해서 사용하는 것이 더 바람직합니다.
*/
private String color;
public static double pi;
/*
- 일반 멤버변수를 사용하고 있는 메서드는 정적메서드로 선언하면 안됨!
*/
public void setColor(String color) {
this.color = color;
}
public String getColor() {
return color;
}
/*
- 메서드 내부에서 일반 멤버변수를 참조하지 않고, 범용성 있게
사용되는 메서드는 static키워드를 사용하여 정적 메서드로 선언하는
것이 좋습니다.
*/
public static double areaCircle(int r) { //반지름을 하나 받아서 원의 넓이를 구하는 메서드
return r * r * pi;
}
}
실습 4. 정적 초기화자(static initializer)
package: static_.init
name: Computer
package static_.init;
public class Computer {
public static String company = "LG";
public static String model = "gram";
public static String info;
public int price;
public Computer() { // 기본 생성자
System.out.println("생성자가 호출됨!");
this.price = 1000000;
this.info = company + "-" + model;
}
//정적 변수의 초기화를 위해서는 정적 초기화자를 사용해야 합니다.
static {
System.out.println("정적 초기화자 호출!");
info = company + "-" + model;
// price = 1000000;
}
}
package: static_.init
name: MainClass
package static_.init;
public class MainClass {
public static void main(String[] args) {
// Computer com = new Computer(); static변수는 객체생성하지말고 아래와 같이 사용하라고 했다.
// System.out.println(com.info);
/*
* 그럼 생성자초기화는 어떻게 해야하는가? 매번 초기에 넣어줘야하는가?
* 아니다. 정적 초기화자를 하면된다.
*
- 정적 초기화자를 호출하려면 클래스를 로딩해야 합니다.
- 2가지 클래스 로딩방법 :
1. 객체를 생성
2. 클래스 이름을 통해 정적 멤버에 접근.
*/
System.out.println(Computer.info); // 이 자체가 생성자초기화 로딩이다.
System.out.println(Computer.info);
System.out.println(Computer.info);
Computer com = new Computer();
Computer com2 = new Computer();
Computer com3= new Computer();
}
}
-----------------결과--------------------------
정적 초기화자 호출!
LG-gram
LG-gram
LG-gram
생성자가 호출됨!
생성자가 호출됨!
생성자가 호출됨!
정적초기화자는 처음 한번만 실행된다고 했다. 그래서 "정적 초기화자 호출!" 이 한번만 출력된다.
실습 5. 싱글톤 패턴(Singleton Pattern)
package: static_.singleton
name: Singleton
package static_.singleton;
public class Singleton {
/*
# 싱글톤 패턴 - 객체의 생성을 1개로 제한하기 위한 디자인 패턴.
1. 외부에서 이 클래스의 객체를 생성할 수 없도록
생성자를 단 1개만 선언하고 private 제한을 붙임,
*/
private Singleton() {}
/*
2. 자신의 클래스 내부에서 스스로의 객체를 1개 생성합니다.
*/
private static Singleton instance = new Singleton();
/*
3. 외부에서 이 클래스의 객체 생성을 요구할 경우
2번에서 미리 만들어둔 단 하나의 객체를 공개된 메서드를
통해 제공합니다.
*/
public static Singleton getInstance() {
return instance;
}
}
package: static_.singleton
name: MainClass
package static_.singleton;
public class MainClass {
public static void main(String[] args) {
// Singleton s1 = new Singleton();
// s1.getInstance();
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1); //static_.singleton.Singleton@6f2b958e
System.out.println(s2); //static_.singleton.Singleton@6f2b958e
}
}
두번 객체를 호출해도 같은 주소의 객체를 부르는거다. 즉, 동일한 객체.
'IT' 카테고리의 다른 글
15-2. 자바 - 사용제한자 추상화(abstract) (0) | 2022.09.27 |
---|---|
15-1. 자바 - 사용제한자 final (1) | 2022.09.26 |
13-1. 자바 - 은닉 (1) | 2022.09.25 |
13. 자바 - 접근제한자 (0) | 2022.09.25 |
10-1. SQL - DDL/DML/DCL (1) | 2022.09.22 |