Language/Java

[Java] Static 정리 (static 변수, static 메소드, static 블록, import static)

KAispread 2022. 5. 16. 20:39
728x90
반응형

자바는 객체지향 언어로써 모든 변수와 메소드가 객체 안에 저장된다.
따라서, 다른 클래스의 변수와 메소드를 사용하기 위해서 객체를 생성해야 한다.
하지만, static 예약어를 사용하면 객체 생성 없이 바로 변수와 메소드를 사용할 수 있다.


 

static 변수

static 예약어를 사용하면 객체 생성 없이 클래스에 선언된 변수에 접근할 수 있다.
이와 같이 클래스내부, 메소드 바깥에 static으로 선언된 변수를 "클래스 변수" 또는 정적 변수라고도 한다.
(클래스내부, 메소드 바깥에 static으로 선언되지 않은 변수"인스턴스 변수"라고 한다.)
static 변수 선언방법은 다음과 같다.

 

static 변수 선언 방법
접근 제어자 static 자료형 "변수명"
//ex
public static int a = 3;

 


예제 코드를 통해 살펴보자.

public class StaticExample {
  public static void main(String[] args) {
    System.out.println(Calculator.StaticNum); 
    //3 출력
  }
}

class Calculator{
  static int StaticNum = 3;
  int nonStaticNum = 2;
}


Caculator 이라는 클래스에 StaticNum이라는 변수를 static 하게 선언하였다.
객체 생성 없이 "클래스명"."static 변수" 만으로 static 변수에 접근 할 수 있다는 것을 볼 수 있다.
이 static 변수는 static 메소드뿐만 아니라 static으로 선언되지 않은 일반 메소드에서도 사용할 수 있다.

 

 

static 변수 특징

하지만 static 변수는 일반 인스턴스 변수와 다르게 객체마다 고유한 값을 가지지 못한다.
객체가 여러개 생성되어도 static 변수 즉, 클래스 변수는 한가지 값만을 가르키게 된다.
마찬가지로 예시를 통해 살펴보자.

public class StaticExample {
  public static void main(String[] args) {
    Calculator sample1 = new Calculator();
    Calculator sample2 = new Calculator();
    sample1.staticNum = 3;
    sample2.staticNum = 5;
		
    System.out.println("sample1- staticNum = " + sample1.staticNum);
    System.out.println("sample2- staticNum = " + sample2.staticNum);
  }
}

class Calculator{
  static int staticNum = 3;
  int nonStaticNum = 2;
}


서로 다른 Calculator 객체를 생성하고, 각 객체마다 static 변수의 값을 다르게 할당해주었다.
static 변수가 아니라면 당연히 sample1과 sample2 객체의 변수값은 달라야 할 것이다.
하지만 결과는 다음과 같다.

결과

객체마다 static 변수에 다른 값을 넣어줬음에도 똑같은 값이 나오는것을 확인할 수 있다.
static으로 선언되지 않은 인스턴스 변수는 객체가 생성될 때마다 새로운 인스턴스 변수가 메모리에 할당된다.
하지만 static 변수는 메모리 상에 한번 할당되면, 객체가 새로 생성되어도 더 이상 메모리에 할당되지 않고
기존에 존재하는 static 변수를 가리키게 된다.

또한, 인스턴스 변수는 객체가 더이상 사용되지 않을때 자바의 GC(Garbage Collector) 에 의해 소멸되어 메모리를 반환하지만, static 변수는 GC의 관리 밖에 있으므로 한번 생성되면 프로그램이 종료할 때까지 해제되지 않는다.
따라서, static 변수를 남용할 경우 시스템 퍼포먼스에 악영향을 끼칠 수 있다.

 

static 메소드

static 변수와 마찬가지로 static 메소드도 객체 생성 없이 메소드를 실행 할 수 있다.
자바의 출력 메소드인 System.out.print()을 객체 생성 없이 사용할 수 있는 것도
메소드가 static으로 선언되어 있기 때문이다.
static 메소드 선언 방법은 static 변수 선언 방법과 같다.

static 메소드 선언 방법
접근 제어자 static 자료형 "메소드명"
//ex
public static int plusMethod() {

}

 

예제 코드를 통해 살펴보자.

public class StaticExample {
  public static void main(String[] args) {
    Calculator.plusMethod(2, 4);
  }
}

class Calculator{
  public static int staticNum = 3;
  int nonStaticNum = 2;
	
  public static void plusMethod(int a, int b) {
    System.out.println(a + b);
  }
}


위 코드와 같이 메소드를 static으로 선언하면 System.out.print() 메소드처럼
별도의 객체 생성 없이 "클래스명." 뒤에 static 메소드를 호출하면 된다.

 

 

static 메소드 사용시 주의해야 할 점

static 메소드를 선언할때 꼭 알아두어야 할 것은
"static 메소드 내에서는 static 변수만 사용할 수 있다"는 점이다.

public class StaticExample {
  public static void main(String[] args) {
    Calculator.plusMethod(2, 4);
  }
}

class Calculator{
  public static int staticNum = 3;
  int nonStaticNum = 2;
	
  public static void plusMethod(int a, int b) {
    int sum = staticNum + nonStaticNum;
    System.out.println(sum);
  }
}


다음과 같이 static 메소드내에서 인스턴스 변수를 참조하게될 경우 다음과 같은 예외가 발생한다.

non-static field인 nonStaticNum을 사용할 수 없다.


static 메소드에선 static 변수만 사용할 수 있다는 것을 꼭 기억해두어야 한다.

 

 

static 블록

클래스에서 생성자는 객체가 생성될 때마다 한 번씩 실행된다.
하지만, 객체가 여러개 생성되더라도 처음 객체가 생성될때 한번만 실행되는 코드를 만들고 싶다면 어떻게 해야할까?
이 때, static 블록을 사용하면 된다.
static 블록의 선언 방법은 다음과 같다.

 

static 블록 선언 방법
static {
	//코드
}

클래스 내부, 메소드 외부 공간에 단지 static예약어를 쓰고 실행하고 싶은 코드를 중괄호{ }로 묶어주면 된다.

 

public class StaticExample {
  public static void main(String[] args) {
    Calculator sample1 = new Calculator();
    Calculator sample2 = new Calculator();
  }
}

class Calculator{
  public static int staticNum = 3;
  int nonStaticNum = 2;
	
  Calculator(){   //객체가 생성될때마다 실행
    System.out.println("Hello");
  }
	
  static {  //객체가 생성되거나 클래스 참조가 발생하면 실행
    staticNum = 5;
    System.out.println("----- Static Block -----");
  }

  static {  //객체가 생성되거나 클래스 참조가 발생하면 실행
    System.out.println("----- StaticNum is "+staticNum+" -----");
  }
  
  public static void plusMethod(int a, int b) {
    System.out.println(a + b);
  }
}

 

실행 결과

 

static 블록 특징

static 블록의 특징은 다음과 같다. 

  • 생성자가 실행되기도 전에 static 블록이 실행된다.
  • 객체가 생성된 처음 한번만 실행된다.
  • static 블록을 여러 개 선언해도 된다.
  • 변수는 static 변수(클래스 변수)만 사용할 수 있다.
  • 객체가 생성되는것이 아닌, 참조만 되어도 static 블록은 실행된다. ex) static 메소드나 변수에 접근하였을 때


마지막 특징을 코드로 살펴보자면 다음과 같다.

public class StaticExample {
  public static void main(String[] args) {
    Calculator.plusMethod(2, 4);
    //객체 생성 없이 참조만 되어도 static 블록이 실행됨.
  }
}

class Calculator{
  public static int staticNum = 3;
  int nonStaticNum = 2;
	
  Calculator(){
    System.out.println("Hello");
  }
	
  static {
    staticNum = 5;
    System.out.println("----- Static Block -----");
  }

  static {
    System.out.println("----- StaticNum is "+staticNum+" -----");
  }
  
  public static void plusMethod(int a, int b) {
    System.out.println(a + b);
  }
}

실행 결과

 

import static

다른 패키지에 선언된 클래스를 사용하기 위해선 import 예약어를 사용하면 된다.
하지만, 다른 패키지에 선언된 static 메소드, 변수를 보다 편하게 사용할 수 있는 방법이 있다.
바로 import static 이다. (JDK 5에서 추가되었다.)

 

import static 선언 방법.
import static "패키지 이름 . 클래스이름 . static 변수/메소드"
//ex
import static pakage00.Animal.NAME;


"package00" 이라는 패키지에 "Animal"이라는 클래스가 있고,
"NAME"이라는 static 변수와 "staticMethod"라는 static 메소드가 있다고 치자.
"package01" 이라는 패키지의 "StaticExample"클래스에서 "Animal" 클래스 안에 있는 static 변수나 메소드를 가져오려면 어떻게 해야할까? (package00과 package01은 src폴더에 저장되어 있다.)

기본적으로 코드 상단에 import "패키지 이름 . 클래스이름" 을 해주는 방법이 있다.

package package01;

import package00.Animal;

public class StaticExample {
  public static void main(String[] args) {
    Animal.staticMethod();
    System.out.println(Animal.NAME);
    //변수와 메소드명 앞에 "클래스명."을 붙여주어야 함
  }
}


이렇게 import 해주면 static 메소드를 사용할 때 앞에 클래스명. 을 붙여주어야만 한다.


이제 import static 을 했을때는 어떻게 되는지 보자.

package package01;

import static package00.Animal.staticMethod;
import static package00.Animal.NAME;

public class StaticExample {
  public static void main(String[] args) {
    staticMethod();
    System.out.println(NAME);
    //"클래스명."을 붙여주지 않아도 내부에 선언된 것처럼 사용가능
  }
}


다음과 같이 클래스 내부에 메소드/변수가 선언된 것처럼 사용할 수 있다.
(메소드나 변수를 일일이 import해주기 귀찮다면 import .../.*을 해도 된다.)
(만약, 클래스 내부에 import한 것과 동일한 이름의 static 메소드/변수가 있다면 import한 static 메소드/변수는 무시되고 클래스 내부에 선언된 static 메소드/변수가 사용된다.)


결론

필요에 맞게 static 변수/메소드를 적절히 사용하면
객체 생성 없이도 변수/메소드를 사용할 수 있어 편리하고 프로그램의 생산성이 올라간다.
하지만 static으로 선언한 변수/메소드는 자바의 GC(Garbage Collector)의 관리 대상이 아니므로 프로그램이 종료될 때까지 메모리에서 해제되지 않는다. 따라서 무차별한 static 선언은 프로그램의 퍼포먼스에 악 영향을 끼치므로 적절하게 사용하는 것이 중요하다.

 

참고
ㆍ"자바의 신 vol.1", 이상민 저자
728x90
반응형