필요에 의해 클래스를 만들고, 사용할 때는 생성자를 통해 객체로 만들어서 사용을 한다.
이러한 방식으로 모든 것을 처리하다 보면, 문제가 생긴다..
예를 들어 집안에 아빠, 엄마, 그리고 세 쌍둥이가 있다.
아이들이 아빠 지갑에 100원짜리 2개가 있다고 알고 있다.
이러한 상황에서 아이들이 아빠한테 100원을 달라고 한다.
당연히 아빠지갑에는 100원 동전 2개가 있으니, 줄 수 있다.
첫째 아이가 먼저 아빠 지갑 객체를 생성하여 100원을 받았다.
그리고 둘째 아이가 아빠 지갑 객체를 생성하여 100원을 받았다.
마지막으로 셋째 아이가 아빠 지갑 객체를 생성하여 100원을 받았다.
여기까지 보면, 뭔가 이상 하다. 분명 집안에 아빠도 한 명이고, 지갑도 한 개일 텐데, 아이들 모두 아빠 지갑을 생성하여 100원씩을 받았다.
200원 밖에 없는데 아이들이 받은 돈은 모두 300원 이다. 버그다!!
그럼 이런 상황에서 생각할 수 있는 방법은 무엇일까?
아주 쉽게 생각해 보면 아빠 지갑 객체가 세 아이들 모두에게 공유된다.
그러면 첫째 아이가 100원 받고, 둘째 아이가 100원을 받으면, 지갑에는 동전이 없고, 셋째 아이는 100원을 받을 수 없다.
셋째 아이한테는 서운한 일이지만, 어째든 프로그램적으로는 이상이 없이 정상 동작 할 것이다!
그러면 어떻게 아빠의 지갑을 모든 아이들이 공유할 수 있을까?
바로 ‘static’ 키워드를 사용하는 것 이다. ‘static’ 키워드를 ‘공유’ 라고 생각하면 편하다.
예제를 보자.
ackage com.javalec.papa;
public class PapaPouch {
public static int MONEY = 200;
public PapaPouch() {
}
}
먼저 papa패키지에 papapouch라는 메소드를 만들었다. 여기엔 200원이 있다.
package com.javalec.children;
import com.javalec.papa.PapaPouch;
public class FirstChild {
public FirstChild() {
}
public void takeMoney(int money){
PapaPouch.MONEY = PapaPouch.MONEY - money;
if(PapaPouch.MONEY < 0) System.out.println("첫째는 돈이 없어 못 받았어요.ㅜㅜ");
//첫째 -> 둘째 -> 셋째
}
}
children패키지에 첫째, 둘째, 셋째 아이들 객체를 만든다. 여기선 첫번째 아이 코드만 입력해놨다. 코드가 똑같기 때문이다.
package com.javalec.staticex;
import com.javalec.children.FirstChild;
import com.javalec.children.SecondChild;
import com.javalec.children.ThirdChild;
import com.javalec.papa.PapaPouch;
public class MainClass {
public static void main(String[] args) {
FirstChild firstChild = new FirstChild();
firstChild.takeMoney(100);
SecondChild secondChild = new SecondChild();
secondChild.takeMoney(100);
ThirdChild thirdChild = new ThirdChild();
thirdChild.takeMoney(100);
System.out.println("PapaPouch.money : " + PapaPouch.MONEY);
}
}
여기서 한명씩 아버지 지갑에서 100원씩 가져간다. 이때 두번째 아이까지 가져가면 아빠 지갑은 0원이 된다. 그렇기 때문에 셋째 아이가 가져가려한다면 위 아이 로직에 의해 "셋째는 돈이 없어 못 받았어요.ㅜㅜ"라는 문구가 출력된다. 그리고 아빠의 돈은 0원이라고 출력된다.
static 키워드를 사용한 변수는 클래스 변수 이다. 객체는 클래스에서 생성(복사)되어 진다고 배웠다. 클래스 하나에서 무한대로 객체를 생성할 수 있고 그러한 객체는 모양은 동일할지 모르지만, 전혀 다른 덩어리 이다. 하지만 static이 붙은 변수는 객체 변수가 아닌 클래스 변수로써 객체가 생성되기 전에 이미 존재한다.
메모리를 보면 클래스 들이 모여있는 곳이 데이터 영역, 객체들이 모여있는 곳이 heap영역이다. 가비지 콜렉터는 heap영역에서만 돌아간다.
static 키워드를 사용한 변수는 객체생성과는 상관없이 클래스와 함께 존재 하게 된다. 즉 객체 생성을 하지 않아도 메모리에 상주하게 된다. 이렇게 생성을 하지 않고도 존재한다는 것은 그만큼 메모리를 사용하지 않아도 된다는 것이다. 지금은 간단한 예제를 살펴보고 있기 때문에 객체생성에 따른 메모리의 부하를 느낄 수 없지만, 프로젝트가 커지면 객체생성에 따른 메모리 부족현상이 있을 수 있다. 물론 가비지콜렉터가 열심히 일을 하고는 있지만.. 객체 생성을 하지 않고도 사용할 수 있는다는 것은 분명 장점이지만, 한편으로 생각해 보면 가비지콜렉터의 관리 밖에 있기 때문에 항상 메모리에 상주해 있다. 즉 프로젝트가 커지고, 시스템이 오랜 시간 동안 돌아가게 되면 시스템 운영속도가 점차 느려지다가 급기야 큰 재앙이 올 수도 있다.. 따라서 static의 사용은 신중해야 한다..! 이때 사용할 수 있는 것이 final이다.
예를들어 파이 값을 정해야 할때 그냥 선언하면 무한에 가까운 변숫값이기 때문에 미리 final로 정하고 가는 것이다. 상수(변하지 않는 수)로 저장을 하고 가는 것이다.
package com.javalex.staticfinal;
public class PiClass {
public static final double pi = 3.14D;
}
package com.javalex.staticfinal;
public class MainClass {
public static void main(String[] args) {
System.out.println("원주율 : " + PiClass.pi);
}
}
위처럼 final을 통해 pi값을 상수로 저장해줬다.
static과 final을 살펴보았는데, 이것들도 매우 유용하게 사용되고 중요한 것이라는 것을 다시 느낀다.. 개발에서 많이 사용되기 때문이다.