클래스에 static을 붙일 경우에는 내부 클래스에 붙이는 경우가 많다. 내부 클래스에 static을 붙이는 이유는 외부 클래스의 인스턴스 생성없이 내부 클래스를 접근하기 위한 용도이다. 그러므로 내부 클래스를 외부에 노출시키려는 용도로 사용하고 싶을 경우에는 내부 클래스에 static을 붙이는게 외부 클래스의 인스턴스 생성 없이 사용가능하므로 사용하기에 유용하다.
publicclassStaticCar {staticString _where="I am a Car from Germany!";Country _country; // object of inner class countryStaticCar(){ _country=newCountry(); // instantiate the inner class }staticclassCountry { // static member inner classStringshowCountry() {return _where; } }publicstaticvoidmain(String[] args) {StaticCar myCar =newStaticCar() ; // instantiated object of class StaticCarSystem.out.print("Access through an Country reference");System.out.println(" created in an object of a StaticCar:");System.out.println(myCar._country.showCountry());// instantiated object of class StaticCar.CountryStaticCar.Country country =new StaticCar.Country();System.out.println("Access through an Country reference that is local:");System.out.println(country.showCountry()); }}
그렇다면 내부 클래스를 여러 개 생성할 경우 두개의 인스턴스는 동일한가?
publicstaticvoidmain(String[] args) {// instantiated object of class StaticCar.CountryStaticCar.Country country =new StaticCar.Country();StaticCar.Country country2 =new StaticCar.Country();System.out.println(country == country2);}
정답은 false이다.
내부 클래스에 static을 붙였다는 의미는 단순히 외부 클래스의 인스턴스 생성 여부에 따라 내부 클래스의 인스턴스를 생성할 수 있는지에 초점을 맞추는 것이다. class에는 static을 붙인다고 해서 인스턴스도 하나만 생성된다고 생각하면 안된다. class에는 외부든 내부든 어디든지 붙일 수 있다. 단지 외부 클래스에는 static을 붙이면 IDE에서 오류가 발생할 것이다. 그것은 단순히 static을 붙인것과 안 붙인거에 큰 의미가 없기 때문이다.
변수(Variable)
클래스 내부 변수에 static을 붙이는 것은 일반적으로 많이 사용되는 사례이다. 보통 static 멤버와 static 함수를 혼합하여 사용하지만 현재는 변수 영역에서 static을 붙이는 사례만 보기로 하자.
아래와 같이 과일이라는 클래스에 씨앗이 2개로 fix되어 있을 경우엔 static을 선언하여 인스턴스 생성없이 해당 변수를 사용할 수 있다. static을 붙일 경우엔 보통 final 키워드를 혼합하여 불변을 보장하도록 사용하는 것을 권장한다. static만 붙였을 경우엔 생성자에서 초기화가 가능하지만 final을 혼합할 경우엔 생성자에서 초기화 할 수 없고 초기값을 지정해야 한다.
클래스 내의 특정 영역에 static으로 지정할 수 있다. 해당 영역은 클래스가 메모리에 올라갈 시점에 호출되는것을 알 수 있다.
호출 순서
static
일반 block
생성자
publicclassStaticTest {privateint count =0;privatestaticString name ="";static{ name ="incheol";System.out.println("static block!!!!"); } { name ="test";System.out.println(count);System.out.println("non static block!!!"); }publicStaticTest(int count) {this.count= count; name ="test22";System.out.println("construct block"); }publicstaticvoidmain(String[] args) {System.out.println(StaticTest.name); }}// 결과값// static block!!!!// non static block!!!// construct block// test22
static 메소드 적용 기준
유틸리티 클래스로 작성되고 불변으로 제공된다.
메소드내에서 인스턴스 변수를 사용하지 않는다.
인스턴스 생성에 의존하지 않는다.
메소드가 공유되고 있다면, 정적 메소드로 추출해낼 수 있다.
메소드가 변화되지 않고, 오버라이딩 되지 않는다.
static 데이터가 적재되는 Method Area란?
Method Area는 Class의 데이터를 저장 한다. Class 의 Method같은 경우 Method Area에 저장 된다. Method은 클래스의 데이터이기 때문이다. 또한 Class 의 Meta 정보를 저장한다.(Field 이름, Field타입, Class 이름 등등) 아래의 Method Area에 저장되는 정보를 자세하게 나타낸 그림이다.
Java 8 이전에 Method Area를 **PermGen(Permanat Generation Space)**에 할당 했다. Java 8 이후에는 PermGen이 완전히 제거 되어 Method Area는 Native Heap에 할당 된다. 과거의 PermGen은 현재 Metaspace 로 불리우는데 Method Area는 Meta 정보를 저장 하기 때문이다. PermGen에서는 Default로 Maximum size를 할당 하였는데 만약 해당 사이즈에 도달 하면 OutOfMemoryErr 가 생성 되었다.
Metaspace에서는 Maximum size가 따로 설정 되 어있지 않고 시스템의 메모리 사이즈가 Maximum size이다. 또한 Garbage Collector 퍼포먼스의 성능을 향상시켰다.
static 제약 조건
static 영역 내에선 static 변수만 사용가능하다.
static 영역 내에서는 인스턴스 변수는 사용할 수 없다. 인스턴스 변수는 인스턴스 생성시 초기화 되므로 사용하는 시점과 초기화 시점이 다르므로 혼용해서 사용할 수 없고 실제로 compile 단계에서 에러를 확인할 수 있다.