나와 static의 첫 만남은 PI(3.141592....)였다.
static이란?
"정적인"이란 뜻을 가지고 있으며 static을 붙이면 정적 메소드, 정적 변수가 된다.
메모리적인 측면에서 설명을 하자면 한번 저장이 된 뒤, 프로그램이 종료가 될 때까지 저장된다.
- 메모리는 글을 따로 작성 할 때 자세히 말씀드리겠습니다.
또한 Static을 붙이면 클래스 변수, 클래스 메소드가 되므로 객체를 생성하지 않아도 호출이 가능하게 됩니다.
public class StaticExample {
// 정적 변수
public static int count = 0;
// 정적 메소드
public static void displayCount() {
System.out.println("Count: " + count);
}
public void incrementCount() {
count++;
}
public static void main(String[] args) {
StaticExample se1 = new StaticExample();
se1.incrementCount(); // 1번
StaticExample se2 = new StaticExample();
se2.incrementCount(); // 2번
StaticExample.displayCount();
}
}
간단하게 정적 변수와 메소드를 이용해 클래스를 작성해 봤습니다.
클래스의 실행 흐름을 정확하게 설명하실 수 있다면 정적 메소드 설명으로 넘어가셔도 됩니다. 😊
조금이라도 헷갈리셨다면...! 제 글을 자세히 읽어주세요
코드 설명
- count는 정적 변수이므로 메소드 영역에 저장된다.(프로그램 종료 때까지 저장된다)
- displayCount 메소드는 정적 메소드다. (클래스 변수이므로 객체 생성 없이 호출이 가능하다)
main메소드에서는 StaticExample 클래스의 객체를 두번 생성한 뒤, incrementCount 메소드를 호출했습니다.
만약 count가 정적 변수가 아니었다면..?
우선 결론부터 말하면 컴파일 에러가 일어난다..(정적 메소드에서 인스턴스 변수에 접근할 수 없기 때문에)
위의 에러를 고려하지 않고 말을 하면 count의 값은 1이다.
왜냐하면 인스턴스 변수는 객체마다 가지고 있는 독립적인 변수이기 때문에 그렇다.
다시 본론으로 돌아오면 count는 정적 변수이기 때문에 값이 2가 됩니다.
또한, displayCount 메소드는 정적 메소드 이므로 클래스 단계에서 호출할 수 있습니다.
정답은 Count: 2입니다.
정적 메소드는 언제 사용할까?
"정적 메소드는 객체지향과는 거리가 멀다"
"추상화 단계에서 사용한다"
"인스턴스 멤버가 없는 경우 사용한다"
- 우아한 테크코스 프리코스 멤버들의 의견을 종합한 것이며, 문제가 될 시 바로 수정하겠습니다.
정말 다양한 의견들이 있었다. 객체지향을 예시로 들어주신 분도 있었고, 객체지향과 거리가 멀다며 객체지향으로 예시 드는 것은 틀리다는 분도 계셨다.
테크코스에서 한 가지 지식라도 더 얻고 싶은 간절한 태도로 참여했기에, 블로그에 정리를 하면서 확실히 공부를 해보려고 한다.
1. 기능을 제공하는 경우
public class ExceptionHandler {
public void throwIfNumberLengthNotThree() {
throw new IllegalArgumentException("숫자의 길이는 3이어야 합니다.");
}
public void throwIfNumberHasZero() {
throw new IllegalArgumentException("0이 포함되면 안됩니다.");
}
...
}
이 클래스는 에러를 발생시키는 여러 메소드들을 모아둔 클래스입니다.
정적 메소드가 아닌 일반 메소드 이므로 클래스의 객체를 통해서 호출해야 합니다.
public class ValidationService {
private final ExceptionHandler exceptionHandler = new ExceptionHandler();
public void isThreeDigits(String str) {
if (!(str.length() == 3)) {
exceptionHandler.throwIfNumberLengthNotThree();
}
}
public void hasNoZero(String str) {
if (str.contains("0")) {
exceptionHandler.throwIfNumberHasZero();
}
}
...
}
프로그래머가 에러 처리를 할 때 객체를 생성 한 뒤, 메소드에 접근하는 모습입니다.
정적 메소드로 변경을 하면 어떻게 변할까?
public final class ExceptionHandler {
public static void throwIfNumberLengthNotThree() {
throw new IllegalArgumentException("숫자의 길이는 3이어야 합니다.");
}
public static void throwIfNumberHasZero() {
throw new IllegalArgumentException("0이 포함되면 안됩니다.");
}
...
}
public class ValidationService {
public void isThreeDigits(String str) {
if (!(str.length() == 3)) {
ExceptionHandler.throwIfNumberLengthNotThree();
}
}
public void hasNoZero(String str) {
if (str.contains("0")) {
ExceptionHandler.throwIfNumberHasZero();
}
}
...
}
정적 메소드로 변경했을 때의 변경점
1. 메모리가 추가로 필요하지 않습니다.
static 메소드는 프로그램 실행 시 한번만 메모리에 할당되므로 객체가 추가로 존재하는 일반 메소드에 비해 메모리가 추가로 필요하지 않습니다.
2. 다형성을 활용할 수 없습니다.
정적 메소드는 오버라이딩 할 수 없으므로 다형성을 활용할 수 없습니다.
3. 클래스에서 직접 접근할 수 있습니다.
정적 메소드의 대표 특징 중 하나입니다. 코드를 간결하게 만들 수 있습니다.
2. 싱글톤 패턴
싱글톤 패턴은 클래스의 인스턴스가 하나만 만들어지고, 그 인스턴스를 사용하는 디자인 패턴이다.
public class Singleton {
// 자신의 타입인 정적 필드를 선언합니다.
private static Singleton uniqueInstance;
// 생성자를 private으로 선언하여 외부에서의 접근을 차단합니다.
private Singleton() {}
// 유일한 인스턴스를 반환하는 정적 메소드를 제공합니다.
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
...
}
주로 공통된 객체를 여러 개 생성해서 사용하는 상황에 많이 사용한다.
싱글톤 패턴에서 정적 메소드를 사용해서 얻는 장점
- 메모리 측면에서 이점 (하나의 인스턴스만 생성하기 때문에 이점이 있다)
- 접근이 쉽다. (싱글턴 패턴은 정적 메소드로 접근 점을 제공하기 때문에 접근하는데 이점이 있다)
✅정리
언제나 동일한가, 상태가 없다, 인스턴스를 생성할 필요가 없을 때
프리코스 멤버들의 다양한 의견을 종합해 3가지 주요 포인트로 정리해 보았다.이 글을 처음 작성하며 "왜 이렇게 의견이 다양한 것일까?"라는 생각을 했었다. 하지만 글을 작성하면서 각각의 의견에서 공통적인 부분을 찾아낼 수 있었다. 블로그를 통해 다른 사람들의 견해를 살펴보아도 유틸리티 함수에 대한 부분을 제외하면 각자 조금씩 다른 시각으로 정적 메소드의 사용 상황에 대해 설명하고 있었다.
"이는 각 개발자가 정적 메소드를 활용하는 상황과 관점이 다르기 때문이라고 생각한다."
나의 의견을 한마디로 말하자면 변함없이 같은 값을 반환하며, 불필요한 메모리를 줄이고 싶을 때 정적 메소드를 사용하면 좋을 것 같습니다 이다.
이 글을 읽는 여러분도 필자가 말한 상황에만 정적 메소드를 사용하지 말고 다양한 상황에 부딪치고 의견을 들으면서 자신만의 견해를 가지길 바란다.