결론적으로 모든 인스턴스가 같은 값을 공유해야 할 경우 static 변수를 사용한다.
1. 처음 프로그램이 로드될 때 단 한번 데이터 영역에 생성된다.
2. 인스턴스의 생성과 상관없이 사용할 수 있으므로 클래스 이름을 참조한다.
3. static은 메모리에 한번 할당되어 프로그램이 종료될 때 까지 해체되지 않는다.
static이 실행되는 시점은 클래스가 메모리상에 올라갈 때이다. 즉, 우리가 프로그램을 실행하면 필요한 클래스가 jvm 메모리상에 로딩되는 과정을 거친다. 그리고 한번 로딩된 클래스는 특별한 일이 발생하지 않는 이상 메모리상에서 객체를 생성할 수 있도록 메모리에 상주한다. static은 이 시점에 메모리에 올라가면서 필요한 동작을 처리한다. 결론적으로 static은 객체의 생성과는 관계없이 클래스가 로딩되는 시점에 단 한번만 필요한 동작을 처리하기 위해 사용한다. 이 때, jvm 의 메소드 영역(클래스 영역)에 클래스의 정보들이 올라가게 된다.
Number이라는 클래스안에 클래스 변수 num과 인스턴스 변수 num2를 생성하였고 두개의 Number인스턴스 number1과 number2를 생성했을때 number1에서 num1과 num2를 각각 1씩 증가시키고 number2에서 num1와 num2를 각각 출력시켰을때는 num1은 1, num2는 0이 출력되었습니다. 왜 이런 현상이 나타났느냐면 인스턴스 변수는 인스턴스가 생성될 때마다 생성되므로 인스턴스마다 각기 다른 값을 가지지만 정적 변수는 모든 인스턴스가 하나의 저장공간을 공유하기에 항상 같은 값을 가지기에 나타난 현상입니다.
정적 메소드는 클래스가 메모리에 올라갈 때 정적 메소드가 자동적으로 생성됩니다. 그렇기에 정적 메소드는 인스턴스를 생성하지 않아도 호출을 할 수 있습니다. 정적 메소드는 유틸리티 함수를 만드는데 유용하게 사용됩니다.
장점이 많은 static임에도 불구하고 객체지향 프로그래밍에서는 static을 지양해야 한다는 이야기가 나온다. 그 이유는 무엇일까?
static 멤버는 사용을 하던 사용하지 않던 프로그램의 시작과 끝까지 메모리(Method Area-Static Area) 내에 존재합니다. 즉, 그 클래스를 이용한 작업을 끝내더라도 static 변수가 점유하고 있는 메모리는 garbage collector에 의해서 회수되지 않게 됩니다. 반대로, 프로그래머가 그 변수를 인스턴스화 해서 main() 함수 내에서 하나의 인스턴스로 생성하고 호출을 시키게 되면, 호출이 끝난 후 인스턴스는 소멸됩니다.(#)
위에 있는 말처럼 인스턴스화 시킨 변수는 garbage collector에 의해서 메모리가 회수됩니다.(#) 이때 static 변수는 회수가 되지 않고 쌓이게 되는데, 이 메서드 혹은 변수에 대한 호출이 많아지면 많아질수록 메모리 문제가 생겨날 수 있습니다.
프로그램 전역에서 사용되기 때문에 모든 스레드에서 static 필드를 공유하게 됩니다. 이때 한 스레드에서 값을 변경할 경우 다른 모든 스레드에서 영향을 받습니다. 따라서 변화가 찾아왔을 때 유연하게 대처하기 힘들 수 있습니다.
더불어서 오버라이딩을 할 수 없는 static 멤버들 때문에 클래스를 확장하는게 어려워질 것입니다.
static 필드는 전역으로 관리되기 때문에 프로그램 전체에서 이 필드에 접근할 수 있고 변경할 수 있으므로 해당 필드를 추론하기 어려워 테스트하기가 까다롭습니다.
참고
https://coding-factory.tistory.com/524
https://guccin.tistory.com/155