[Java] 09. 접근 제한자와 static


Study/Java  2020. 5. 6. 16:27

안녕하세요. 명월입니다.


이 글은 Java애서의 접근 제한자와 static에 대한 글입니다.


이전에 제가 클래스에 대한 글을 작성한 적이 있습니다.

링크 - [Java] 07. 클래스를 사용하는 방법 (생성자를 사용하는 방법)


접근 제한자

클래스는 변수와 함수, 생성자로 이루어져 있고 그 세가지 속성을 접근하기 위해서는 접근 제한자가 필요하다고 설명했습니다.

이 접근 제한자는 객체 지향(OOP)의 4대 특성 중 캡슐화와 관계가 있는 키워드입니다.


캡슐화는 클래스를 객체에 하나의 목적과 역할을 설정하고 데이터와 기능을 하나로 묶는다는 뜻을 의미합니다.

그 중 이 접근 제한자는 함수나 변수에 접근하는 방식에 대한 것을 설정하는 것입니다.


Java에서의 접근 제한자는 아래와 같습니다.

접근자 설명
public 모든 클래스에서 접근이 가능함을 의미한다.
private 클래스 내에서만 접근이 가능하다
protected 동일 패키지에 속하는 클래스와 하위 클래스 관계의 클래스에 의해 접근이 가능하다.

접근 제한자의 접근 권한은 아래와 같습니다.

종류 내부 클래스 하위 클래스 동일 패키지 모든 클래스
private O X X X
(default) O X O X
protected O O O X
public O O O O

먼저 public과 private, protected의 차이는 외부 접근 가능과 불가능의 차이입니다.

// 클래스
public class Program {
  // private로 설정된 함수
  private void func() {
    // 콘솔 출력
    System.out.println("func");
  }
  // public로 설정된 함수
  public void print() {
    // func함수 호출
    this.func();
    // 콘솔 출력
    System.out.println("hello world");
  }

  // main 함수
  public static void main(String... args) {
    // Program 클래스 생성
    Program p = new Program();
    // Program 클래스의 print함수  호출
    p.print();
  }
}

main함수는 Program의 내부에 있지만 static이 붙어 있기 때문에 우선 외부에 있는 것으로 생각합시다. (사실 내부에 있는 것인데.. 이 부분을 아래에 다시 설명하겠습니다)

main 함수에서 Program를 new로 해서 p의 변수에 할당을 했습니다.

여기서 p의 변수를 통해 print함수를 호출했습니다. 여기서 클래스를 할당해서 호출하는 부분을 class로 입장으로 보면 외부라고 생각하면 됩니다.

즉 변수 p에서는 func함수를 호출할 수 없고 print함수만 호출 가능한 것입니다.


다시 print함수 안을 보면 func함수를 호출했습니다. 특히 this라는 키워드를 붙였습니다.

즉, 클래스 내부에서 호출하는 것을 클래스 내부라고 판단합니다.


다시 정리하면 public은 클래스 외부에서만 호출되고 private와 protected는 클래스 내부에서만 선언됩니다.


다시 private와 protected의 차이는 클래스 내부에도 차이가 있습니다.

// 상위 클래스 (SuperProgram)
class SuperProgram {
  // protected로 설정된 함수
  protected void print() {
    // 콘솔 출력
    System.out.println("SuperProgram print");
  }
}
// 클래스 , SuperProgram를 상속 받는다.
public class Program extends SuperProgram{
  // private로 설정된 함수
  protected void print() {
    // 콘솔 출력
    System.out.println("Program print");
  }
  // public으로 설정된 함수
  public void run() {
    // 상위 클래스의 print 호출
    super.print();
    // 내부 클래스의 print 호출
    this.print();
  }

  // main 함수
  public static void main(String... args) {
    // Program 클래스 생성
    Program p = new Program();
    // run 함수 호출
    p.run();
  }
}

private와 protected는 상속하는 클래스에서 참조가 가능하게 하는지에 대한 차이입니다.

즉, SuperProgram 클래스에서 print함수를 protected의 제한자가 아닌 private라고 하면 상속 받은 Program 클래스의 run 함수에서 super로 호출할 수 없습니다. protected는 main 함수에서 new Program한 p변수에서 SuperProgram.print를 참조할 수는 없지만 상속받은 Program 클래스에서는 SuperProgram의 print 함수가 호출이 가능하게 설정하는 것입니다.


이것이 private, protected, public의 차이입니다.

위 표의 접근 권한에 (default)가 있습니다. (default)는 명시적으로 private, protected, public를 설정하지 않는 경우 입니다. 기본적으로 private와 같은 설정입니다만 동일 패키지 안에서는 접근이 가능하게 되어있습니다.

즉, new Program이 동일 패키지에서 선언된 경우에는 참조가 가능하다는 뜻입니다만, Java 코딩 규약에서는 비명시적 (default)를 권장하지 않으니 되도록이면 명시적으로 private, protected, public를 설정하는 게 좋습니다.

static

기본적으로 클래스는 new 라는 키워드를 사용해서 메모리에 할당(인스턴스 생성)을 하고 사용하는 것이 기본입니다.

그런데 이 클래스를 할당하는 개념은 사실 멤버 변수에 관한 것입니다. 즉, 멤버 변수가 int형 두개라면 클래스는 8byte의 크기의 메모리가 할당되는 것입니다. 함수는 여기서 그런 멤버 변수를 다루기 위한 보조적인 기능이라고 생각하면 됩니다.

즉, Java에서는 new를 사용하지 않으면 클래스 내부의 함수도 사용하지 못한다는 뜻입니다. 왜냐하면 함수 내부에서는 맴버 변수를 참조하여 계산하고 다시 저장하는 방법을 사용하기 때문입니다. 즉, 클래스가 생성되지 않으면 함수를 사용할 수 없다는 뜻이네요.


그렇다면 함수라는 것은 꼭 클래스 맴버 변수를 이용해서 사용하는 함수만 있는 것인가? 생각해보면 꼭 그렇지만은 않습니다. 변수를 사용하지 않고 단순히 계산을 위한 함수도 있을 것이라고 생각합니다. 즉, 인스턴스와 관계없는 함수도 있다는 뜻입니다.

그렇게 static을 사용하여 인스턴스 생성과 관계없는 함수를 만들 수 있습니다.

위 main 함수의 경우는 Program 클래스 내부에 있지만 Program 인스턴스와 상관이 없다라는 뜻입니다.


static 키워드는 함수만 사용할 수 있을까라고 생각하면, 변수와 클래스에도 사용할 수 있습니다.

변수는 자료형을 설정하고 메모리에 그 크기를 할당해야 하지만, static을 설정하게 된다면 어느 시점에 그것을 할당하게 되는 것일까?

프로그램이 시작할 때 가장 먼저하는 작업이 프로그램 내의 static 변수와 함수를 찾는 것입니다. 즉, static 변수의 경우는 변수 내에 new가 있으면 할당하고 main함수를 찾아서 실행하는 것이빈다.

static을 클래스에 사용하는 경우는 클래스 내부의 클래스, 즉 인라인 클래스의 경에 사용할 수 있습니다.

// 기본 class에는 static을 붙일 수 없다.
public class Program {
  // static이 키워드가 있기 떄문에 맴버 변수가 아닌 정적 변수라고 한다.
  private static int test;
  // 내부(inline) 클래스에 static를 붙일 수 있다.
  static class Node {
    // 생성자
    public Node() {
      // 콘솔 출력
      System.out.println("Alloc Node class");
    }
  }
  // 이 함수는 static으로 Program의 할당(new)과 관계없이 호출 가능
  public static void print() {
    // 
    System.out.println("Program.test = " + test);
  }

  // main 함수
  public static void main(String... args) {
    // Program 클래스 할당
    Program p = new Program();
    // 10 설정
    p.test = 10;
    // Program 클래스 할당
    Program p1 = new Program();
    // p.test의 값을 20 설정
    p1.test = 20;
    // Program 클래스의 print함수 호출
    // p 변수의 test 변수의 값은 10이 나올까? 20이 나올까?
    p.print();
    // 이건 10이 나올까? 20이 날까?
    Program.print();
    // static를 설정하면 Inline 클래스를 외부에서도 사용할 수 있다.
    // 그러나 이런 방식은 사용하지 않는 편이 좋다.
    Program.Node node = new Program.Node();
  }
}

먼저 결과를 보면 p의 test 변수의 값은 20으로 변경되어 있습니다. 즉, Program의 test 변수는 인스턴스와 관계가 없기 때문에 인스턴스 p와 p1을 할당하는 것과 관계가 없습니다. 즉, 동일한 변수인 셈입니다.


여기서 조금 위 접근 제한자에 대한 설명에서 private의 설명을 수정해야 합니다.

위에서 public은 외부에서 접근 가능하기 때문에 private는 main에서 접근이 불가능하다고 설명했습니다. 사실 이 main은 static 형식이지만 Program 클래스 안에 있습니다.

즉, private와 public은 할당된 인스턴스의 개념으로 외부 내부를 구분하는 것이 아니고 중괄호({})로 되어 있는 스택 영역에 따라 외부와 내부를 구분합니다.

static main함수도 곧 Program 클래스의 내부로 판단하기 때문에 private가 접근이 가능합니다. 그러나 main이 Program 내부에 없으면, Program 클래스의 private 객체는 접근이 불가능합니다.


그럼 다시 static으로 돌아와서 static에서는 할당된 변수를 참조할 수 있을까?

위 이미지는 Program의 data는 static이 붙지 않았기 때문에 static 함수에서는 data를 참조할 수 없습니다.

data는 Program의 클래스가 할당(new)과 동시에 생성되는 변수입니다만, static 함수의 경우는 할당으로 생긴 변수가 아니기 때문에 참조할 수 없는 것입니다.

이 할당과 인스턴스의 관계는 설명해야 할 내용이 많으니 따로 글을 작성하겠습니다.


여기까지 Java에서의 접근 제한자와 static에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.