GeehDev

[Java] 자바 제어자 - 접근 제어자/static/일반 제어자 본문

Study/Java

[Java] 자바 제어자 - 접근 제어자/static/일반 제어자

geehyun 2024. 9. 15. 15:15

 

velog에서 이관해온 글


 

Java에서는 다양한 제어자가 있으며, 해당 클래스, 변수의 접근 범위를 지정하기도, 특정 상태를 보여주기도 합니다.

접근 제어자

클래스, 멤버(필드, 메서드, 이너클래스), 생성자 앞에 위치하며 사용 범위를 정의하는 역할을합니다.

모든 클래스, 멤버는 접근 제어자를 필수로 가져야합니다.

멤버 및 생성자의 접근제어자

접근 제어자 사용 가능 범위
public 동일 패키지의 모든 클래스 + 다른 패키지의 모든 클래스
protected 동일 패키지의 모든 클래스 + 다른 패키지의 자식클래스
default 동일 패키지의 모든 클래스
private 동일 클래스

 

사용 범위 순 : public > protected > default > private

해당 멤버 및 생성자의 접근제어자로 위 4개 중 하나를 작성해줘야하며, 만약 아무것도 작성하지 않는다면 자동으로 default 제어자로 설정됩니다.

//사용법
접근제어자 자료형 변수명;
접근제어자 리턴타입 함수명() {...};
접근제어자 생성자명() {...};
//사용예시
public int a = 1;           //public
String b = "안녕하세요";      //default
protected void c() {...};   // protected
private D() {...};          //private

클래스의 접근제어자

클래스의 경우 public 또는 default만 접근제어자로 사용할 수 있습니다.

접근 제어자 사용 가능 범위
public 동일 패키지 + 다른 패키지 모두 사용가능
default 동일 패키지 내 사용가능

클래스 또한 아무것도 작성하지 않으면, default로 자동 설정됩니다.

//사용법
접근제어자 class 클래스명 {
    ...;
}
// 사용예시
public class A {   //public 클래스
    ...;
}
class B {         //default 클래스
    ...;
}

💡 클래스의 접근제어자와 생성자
클래스 내부에 생성자를 따로 정의하지 않을 시 컴파일러가 자동으로 생성자를 만들게되는데 이 때 클래스의 접근제어자와 동일하게 자동 생성된 생성자의 접근제어자를 설정합니다.

  • public 클래스 => (자동생성) public 생성자
  • default 클래스 => (자동생성) default 생성자

물론, 사용자 정의 생성자로 직접 각 각 다른 접근제어자를 갖는 생성자를 만들어줄 수도 있습니다.

이 때 중요한 점은 class 자체가 public 이어도 생성자가 default일 경우 다른 패키지에서 해당 클래스를 임포트 해도 생성자를 사용할 수가 없어서 객체 생성을 못한다는 점을 유의해야합니다.


반대로 클래스가 defalt 상태인데 생성자만 public 일 경우는 애초에 다른 패키지에서 해당 클래스를 임포트 할 수 없습니다.

static 제어자

클래스의 멤버(필드, 메서드, 이너클래스)에 사용하는 제어자로 클래스 내 멤버를 객체를 통해서가 아니라 클래스 자체로 사용할 수 있게 해주는 제어자 입니다.

정적멤버와 인스턴스 멤버

class A {
    int a1 = 10;
    static int a2 = 20;

    void a3() {...}
    static void a4() {...}
}
public class Main {
    public static void main(String[] args) {
        A a = new A();
        //인스턴스 멤버의 사용
        System.out.println(a.a1);
        a.a3();
        //정적 멤버의 사용
        System.out.println(A.a2);   //1. 객체 생성없이 클래스명으로 바로 사용 
        A.a4();
        System.out.println(a.a2);   //2. 객체 생성 후 변수명으로 사용
        a.a4();
    }
}
  • 인스턴스 멤버 : static 상태가 아닌 클래스 멤버로, 해당 클래스로 객체를 만들어 해당 참조변수를 통해 사용할 수 있습니다.
  • 정적(static) 멤버 : static 키워드가 붙은 멤버로, 객체 생성 없이 바로 클래스명.필드명 또는 클래스명.메서드명()으로 사용할 수 있습니다.
    인스턴스 멤버처럼 객체 생성 후 참조변수 명을 이용해 사용할 수도 있지만 정적멤버는 클래스명.멤버명으로 정적 멤버 특성을 살리는 방법으로 사용해줌이 더 올바릅니다.

 

정적멤버의 메모리 구조

정적 멤버의 경우 인스턴스 멤버들과 달리 클래스 영역에 그 값 자체가 저장되고 해당 값을 참조해서 사용하는 방식입니다.

정적 필드의 메모리 구조

정적 필드

  • 인스턴스 필드 : 힙에 객체가 생성된 후 해당 객체 내부에 필드가 위치하며 필드 내 실제 값을 갖고 있습니다.
  • 정적 필드 : 힙에 객체가 생성된 후 해당 객체 내부에 필드가 위치하지만 실제 값은 클래스 영역의 클래스 내부에 위치하고있으며 객체 내에는 해당 위치값만 저장하여 참조해서 사용합니다.

즉, 정적 필드의 경우 어떤 객체를 생성하든 실제 값이 있는 클래스 영역 내부를 모두 똑같이 바라보고있기 때문객체 간 공유 변수의 성질이 있습니다.
=> 정적 필드 값을 어디선가 변경한다면 해당 정적필드를 사용 중인 모든 곳에서 값이 변경된다는 의미입니다.

정적 메서드의 메모리 구조

정적 메서드


인스턴스 메서드와 정적 메서드 모두 실제 메서드는 class, static, final, 메서드 영역에 존재합니다. 다만, 상세 위치에서 차이가 있습니다.

  • 인스턴스 메서드 : 메서드 영역 내 위치합니다.
  • 정적 메서드 : 클래스 영역 내 해당 클래스 내부에 위치 합니다.

정적 메서드 내부

정적 메서드는 인스턴스 메서드와 달리 내부에 정적 필드, 다른 정적 메서드만 이용할 수 있습니다.

정적 메서드의 특성 상 객체 생성 없이 클래스명.메서드명()으로 사용할 수 있기 때문에 정적 메서드 내 구성품 또한 객체 생성없이 사용 가능한 구조로 만들어져야하기 때문입니다.

인스턴스 메서드에서는 인스턴스 필드, 정적 필드, 인스턴스 메서드, 정적 메서드 모두 사용 가능합니다!

정적 초기화 블럭

일반적으로 class의 초기화는 객체가 만들어지면서 생성자로 하여금 초기화 하게끔 구성합니다.

그런데 정적멤버의 경우 객체를 생성하기 이전에도 사용할 수 있기 때문에 초기화 필요 시 따로 정적 초기화 블럭으로 초기화 시켜줘야합니다.

class A {
    static {
        //정적 초기화 블럭입니다.
    }
}

static main()메서드

Java는 프로그램을 시작할 때 가장 먼저 main()메서드를 실행해줍니다.

public static void main(String[] args) {
    내용~~;
}

위와 같이 자동으로 작성되며, static 제어자를 포함되어있습니다.

 

이는 Java가 프로그램을 시작할 때 main()메서드를 가장 먼저 실행해줘야하는데 static 제어자가 없다면, 해당 메서드 실행을 위해 객체를 만들어줘야만 해당 메서드를 실행할 수 있기 때문입니다.

 

따라서 main() 메서드를 실행시켜주기 위해서 static 정적 메서드로 구성된 것 입니다.

 

일반 제어자

일반제어자에는 final, abstract가 있습니다.

final 제어자

final제어자의 경우 필드, 지역변수, 메서드, 클래스에 사용할 수 있으며, 각 각 사용의 의미는 조금씩 다르나 가장 중요한 키워드는 최종이라는 키워드 입니다.

final 필드/지역변수

final키워드를 변수 앞에 선언 시 JS의 const 상수와 의미가 유사합니다.
final 제어자를 추가 시 해당 변수에 값이 한번 대입되면 해당 값이 최종 상태가 되는 제어자 입니다.

class A {
    // 선언과 동시에 값 대입
    final int a = 1;
    a = 4;                   //불가능
    A(int a) {
        a = 5;               //불가능
    }
    // 선언과 값 대입을 분리
    final int b;
    A() {
        b = 4;
    }
    b = 5                   //불가능
}

위 처럼 final 제어자를 사용 시 해당 변수에 값이 대입되는 순간 해당 변수는 최종 상태가 되며 그 어떤 방법으로도 새로운 값 대입이 불가 합니다.

💡 final 변수 선언과 값 대입을 분리할 때
final 변수에 대해 선언과 대입을 분리할 경우 해당 변수의 초기화 작업은 생성자 내에서 해줘야합니다.
이유는, final 변수의 경우 Java 자체에서 강제 초기화를 진행하지 않기 때문입니다.
따라서 객체 생성과 동시에 final 변수의 값이 초기화될 수 있도록 생성자에서 초기화 작업을 행해야합니다.

final 변수 메모리 구조


final 변수의 경우 필드냐, 지역변수냐에 따라 해당 변수 자체의 값이 저장되는 곳은 힙 또는 스택으로 다르지만 공통적으로 해당 변수가 원래 저장되는 곳 외에 final 영역에 해당 변수와 값이 한번 더 복사되서 저장된다는 구조적 특징 있습니다.

이는 final 변수를 사용함으로써 해당 원본이 삭제되더라도 final 영역에있는 복사본을 통해 그 값을 활용할 수 있게 해줍니다.

final 메서드와 클래스

메서드나 클래스 앞에 final 제어자가 온다면 각 각 최종 메서드, 최종 클래스가 된다는 의미 입니다.

  • 최종 메서드 : 자식 클래스에서 해당 매서드를 오버라이딩 할 수 없는 상태
class A {
    void abc() {...}
    final void def() {...}
}
class B extends {
    void abc() {수정~}          // 가능
    void def() {수정~)          // 불가능
}
  • 최종 클래스 : 해당 클래스를 누구도 상속 받을 수 없는 상태가 됩니다.
final class A {
    void abc() {...}
    final void def() {...}
}
class B extends {            // 불가능
    ...
}

abstract 제어자

abstract란, 추상적인이란 의미로 해당 제어자가 붙은 메서드를 추사 메서드, 클래스를 추상 클래스라고 부릅니다.

  • 추상 메서드 : {}중괄호가 없는(바디가 없는) 메서드 입니다.
  • 추상 클래스 : 추상 메서드를 1개 이상 포함하고 있는 클래스 입니다.
abstract class A {
    abstract abc();
}

추상클래스를 사용하는 이유는 추상클래스를 상속하여 오버라이딩 해서 사용하는 것이 목적입니다.

참고

Do it! 진짜 개발자가 되는 Java 프로그램 입분서 자바 완전 정복 - 김동형
위 책을 공부하며 작성하고 있습니다!