개발자로 후회없는 삶 살기

[문법] 접근 지정자와 static 동작 원리 본문

[백엔드]/[Java | 학습기록]

[문법] 접근 지정자와 static 동작 원리

몽이장쥰 2024. 5. 17. 23:37

서론

※ 과거에 공부한 내용에서 중요한 부분만 발췌하여 모두가 이해하기 쉽게 다시 서술한다.

 

본론

- 접근 지정자

-> 접근 지정자의 목적

public class A {
    public int a;
    protected int b;
    int c;
    private int d;
}

자바는 객체 지향 언어로 캡슐화, 추상화, 상속화, 다형화라는 특징을 가진다. 그 중 캡슐화를 만족하기 위해 접근 지정자를 사용한다. 캡슐화란 클래스를 하나의 캡슐처럼 외부와 독립된 공간으로 보고 외부와 적절히 상호작용하는 것을 목표로 한다. 외부와 적절히 상호 작용을 하기 위해선 데이터의 I/O가 있어야 하는데 접근 지정자를 사용하면 클래스 내부 데이터의 접근 권한을 조절하여 데이터의 흐름을 제어할 수 있다.

 

-> 종류

public / protected / default / private

왼쪽으로 갈 수록 데이터의 개방 범위가 넓어진다. default는 접근 지정자를 생략하면 적용된다.

 

-> 관습

1. 필드는 private

public class Bus {
    private int passengerCount;
    private int money;
}

필드는 private로 하여 공개 범위를 강화한다. 공개 범위를 강화하지 않으면, 어디서나 해당 필드에 접근할 수 있기에, 해당 필드의 데이터가 의도치 않게 수정되거나 삭제될 수도 있다. 이를 사전에 방지한다.

 

public class Bus {
    private int passengerCount;
    private int money;
    
    public int getPassengerCount() {
        return passengerCount;
    }

    public int getMoney() {
        return money;
    }
}

private로 비공개한 필드는 동일 클래스가 아니면 접근이 불가하여, getter()로 접근한다. 무분별한 setter()는 의도치 않은 데이터 변경을 유도할 수 있기에 지양한다.

 

public class Bus {
    private int passengerCount;
    private int money;

    public void changeMoney(int money) {
        this.money = money;
    }
}

대신 편의 메서드를 만들어 코드 가독성과 개발자의 의도를 확실히 한다.

 

- static 키워드

=> static 키워드의 특징

static 키워드를 이해하기 전에 메모리 구조를 이해해야 한다. 우리가 작성한 class는 static 영역에 생성되고 객체는 heap 영역에 생성된다.

 

-> static 영역

1. 작성한 클래스가 할당됨
2. GC의 관여 X
3. 모든 요소가 메모리를 공유함

 

-> heap 영역

1. new로 생성된 객체가 할당됨
2. GC의 관여 O
3. 메모리를 공유하지 않음

static 키워드가 붙은 필드와 메서드는 static 영역에 생성되며, 메모리를 공유한다.

 

-> 장점

1. 메모리의 효율적 사용

static이 붙지 않은 필드와 메서드를 사용하기 위해선 객체를 생성해야 한다. 객체는 생성되는 만큼 heap 영역을 차지하기 때문에 메모리 사용량이 증가하고 이는 프로그램의 성능에 안 좋은 영향을 준다.

 

2. 시간적 이점

클래스를 작성하고 메모리에 로드되는 순서를 살펴보면 클래스 로더를 통해 읽고 메모리에 로드된다. 이때 초기화 과정이 메모리에 static 영역에 static 멤버를 할당한다. 따라서 객체를 생성하기 전에 클래스가 메모리에 로딩되는 순간부터 필드와 메서드를 사용할 수 있다.

 

-> 단점

1. 프로그램 종료 시까지 메모리에 할당된 채로 존재

GC를 통해 수시로 관리 받는 heap 영역과 다르게 static 영역은 GC의 관리를 받지 않는다. static 키워드가 붙은 요소는 생성된 순간부터 메모리를 차지하고, 프로그램이 종료되기 전까지 소멸되지 않는다. 만약 static을 많이 사용하면, 메모리를 많이 차지하여 프로그램 연산 속도에 안 좋은 영향을 준다.

 

2. 캡슐화를 위반한다.

캡슐화는 클래스라는 독립 공간에 필드와 메서드를 고립시키고, 이를 활용하기 위해서는 객체를 생성하는 것을 원칙으로 한다. 하지만 static을 붙이면 객체를 생성하지 않아도 멤버를 사용할 수 있다.

 

-> 🚨 static을 사용하면 안 될까?

지금까지, static의 동작 방식과 장단점을 알아보았는데 고정 메모리로 인해 프로그램에 악영향을 주니, 사용하면 안 될 것 같은 생각이 든다. 하지만 상황에 따라 장점을 가진다.

 

1. 객체 생성이 필요 없는 상황에서는 메모리의 낭비를 막을 수 있음
2. 무분별하게 사용하지 않으면 단점도 커버할 수 있음

위와 같은 이유로 컴퓨팅 파워(RAM의 크기)나 코드를 작성할 상황에 맞게 적절히 사용할 경우 가독성 높고 효율적인 코딩을 할 수 있다.

 

-> static의 제약

1. static 메서드에 static이 아닌 필드를 사용할 수 없다. (static이 아닌 메서드에 static 변수는 사용 가능)
2. static 메서드에 this를 사용할 수 없다.

static이 붙은 메서드는 static 영역에 로딩되는 순간부터 사용할 수 있다. 하지만 객체 레벨의 필드나 this가 정의되어 있다면, static 영역에 로딩된 시점에는 객체 레벨의 데이터를 알 수 없기 때문에 static 메서드에는 클래스 레벨의 멤버만 사용할 수 있다.

 

-> 🚨 static 변수를 멤버 변수처럼 사용하면 안 된다.

static 변수는 공통으로 사용하는 전역 변수로, 해당 클래스로 인해 생성된 객체들이 모두 공유할 수 있다. 학생 객체를 만들 때 학번을 1씩 증가하며 학번을 부여하는 상황을 가정해보자

 

public class Studentt {
    public static int id = 1000;

    public Studentt(String name) {
        id++;
        this.name = name;
    }
}

학번은 모든 학생에게 공통으로 적용되어야 하기 때문에 static 변수로 지정할 수 있다. (예제에서 패키지 명과 중복되어 클래스 명을 Studentt로 설정)

 

만약 이 상황에서 학번을 멤버 변수처럼 사용한다면 언제 어느 상황에서 값이 변경될지 알 수 없다. 위 예제에서는 학생을 추가하였더니 이미 만들어 두었던 student1의 학번도 증가하게 된다.

 

public class Studentt {
    public static int id = 1000L;
    private Long num;

	public Studentt(String name) {
        id++;
        num = id;
        this.name = name;
    }
}

따라서, static 변수를 직접 사용하기보다는 static 변수의 값을 멤버 변수에 대입해서 사용해야 한다.

 

그러면 원하는 대로 동작함을 확인할 수 있다.

Comments