제어자

Updated:

강의 사이트

http://tcpschool.com/

접근 제어자

  • 제어자란 클래스와 클래스 맴버의 선언 시 부가적인 의미를 부여하는 키워드

  • 접근제어자와 기타제어자로 구분

  • 접근제어자는 오직 한개 기타제어자는 여러개 사용 가능

  • 접근제어자와 기타제어자를 혼합하여 사용 가능

  • 메소드 또한 변수와 똑같은 규칙을 지닌다.

1. 접근제어자

  • 외부 클래스로부터 정보은닉을 구체화하기 위해 사용

  • private -> default -> protected -> public 순으로 접근이 쉽다.

1.1 private

  • private을 사용하여 선언된 클래스 맴버는 외부에 공개 or 외부에서 접근이 불가능

  • 해당 객체의 public 메소드를 통해서만 접근이 가능. default, protected도 접근 불가능하면 public 메소드로 가능

1.2 protected

  • 같은 패키지 내의 클래스 또는 해당 클래스를 상속 받은 외부 패키지의 클래스에서 접근이 가능

1.3 default

  • 접근제어자를 별도 설정하지 않는 다면 기본 제어자가 된다.

  • 같은 패키지 내에서만 접근이 가능하다.

1.4 public

  • 그냥 다 쌉 가능

예시

  • test package에 있는 MotherClass
package test;

public class MotherClass {
	private String privateVar = "privateVar 허용";
	String defaultVar = "defaultVar 허용";
	protected String protectedVar = "protectedVar 허용";
	public String publicVar = "publicVar 허용";
	
    public static void main(String[] args) {
    	MotherClass motherClass = new MotherClass();
        System.out.println(motherClass.privateVar);
        System.out.println(motherClass.defaultVar);
        System.out.println(motherClass.protectedVar);
        System.out.println(motherClass.publicVar);
        /* 자기 꺼니까 다 쌉가능 */
    }
}
  • MotherClass와 같이 test package에 있고 상속받은 FirstChildClass
package test;

import test.MotherClass;

public class FirstChildClass extends MotherClass{
    public static void main(String[] args) {
        MotherClass motherClass= new MotherClass();
        //System.out.println(motherClass.privateVar);	 // 직접 접근은 불가능
        System.out.println(motherClass.defaultVar);
        System.out.println(motherClass.protectedVar);
        System.out.println(motherClass.publicVar);
        
    	FirstChildClass firstChildClass= new FirstChildClass();
        //System.out.println(firstChildClass.privateVar); // 직접 접근은 불가능
        System.out.println(firstChildClass.defaultVar);
        System.out.println(firstChildClass.protectedVar);
        System.out.println(firstChildClass.publicVar);
        /* 같은 패키지 안에 있기 때문에 private 빼고 다 쌉가능 */
    }
}
  • MotherClass와 같이 test package에 있으나 상속받지 못한 SecondChildClass
package test;

import test.MotherClass;

public class SecondChildClass{
    public static void main(String[] args) {
        MotherClass motherClass= new MotherClass();
        //System.out.println(motherClass.privateVar);	 // 직접 접근은 불가능하다.
        System.out.println(motherClass.defaultVar);
        System.out.println(motherClass.protectedVar);
        System.out.println(motherClass.publicVar);
        /*상속은 받지 않았으나 같은 패키지이므로 private 제외 다 쌉가능 */
    }
}
  • MotherClass를 상속 받았으나 test.other package에 있는 OtherChildClass
package test.other;

import test.MotherClass;

public class OtherChildClass extends MotherClass{
    public static void main(String[] args) {
        MotherClass motherClass= new MotherClass();
        //System.out.println(motherClass.privateVar);
        //System.out.println(motherClass.defaultVar);
        //System.out.println(motherClass.protectedVar);
        System.out.println(motherClass.publicVar);
        /* 같은 패키지가 아니고 해당 클래스도 아니기 때문에 public 제외 모두 불가능 */
        
    	OtherChildClass otherChildClass= new OtherChildClass();
        //System.out.println(otherChildClass.privateVar);
        //System.out.println(otherChildClass.defaultVar);
        System.out.println(otherChildClass.protectedVar);
        System.out.println(otherChildClass.publicVar);
        /* 같은 패키지가 아니지만 상속을 받은 클래스이므로 protected는 가능 */

    }
}
  • MotherClass를 상속 받지도 않고 test.other package에 있으며 전혀 상관 없는 UnknwonOtherChildClass
package test.other;

import test.ChildClass;
import test.MotherClass;

public class UnknownChildClass {
    public static void main(String[] args) {
        MotherClass motherClass= new MotherClass();
        //System.out.println(motherClass.privateVar);
        //System.out.println(motherClass.defaultVar);
        //System.out.println(motherClass.protectedVar);
        System.out.println(motherClass.publicVar);
        
        FirstChildClass firstchildClass = new FirstChildClass();
        //System.out.println(firstchildClass.privateVar);
        //System.out.println(firstchildClass.defaultVar);
        //System.out.println(firstchildClass.protectedVar);
        System.out.println(firstchildClass.publicVar);
        
    	OtherChildClass otherChildClass= new OtherChildClass();
        //System.out.println(otherChildClass.privateVar);
        //System.out.println(otherChildClass.defaultVar);
        //System.out.println(otherChildClass.protectedVar);
        System.out.println(otherChildClass.publicVar);
        
        /* 같은 패키지도 상속도 아닌 클래스이므로 모두 쌉 불가능 */
        /* 설사 UnknownChildClass가 FirstChildClass를 상속 받아도 public 제외 모두 불가능 */
    }
}

표로 간단히 정리

접근 제어자 같은 클래스의 멤버 같은 패키지의 멤버 자식 클래스의 멤버 그 외의 영역
public
protected X
default X X
private X X X

기타제어자

1. final 제어자

  • final는 entity를 한 번만 할당하기 위해서 사용한다. 즉, 두 번 이상 할당하려고 할 경우 오류가 발생한다.

  • final은 크게 변수, 메소드, 클래스에서 사용한다.

  • 변수에서 사용하면 값 변경이 불가능

  • 메소드에 사용하면 오버라이딩이 불가능

  • 클래스에 사용하면 상속 불가능

1.1 final 변수

1.1.1 원시타입
  • 가장 기본적인 타입. 한 번 초기화된 변수는 변경할 수 없다.
public void test_final_primitive_variables() {
    final int x = 1; //x = 3; 한번 assign되면 변경할 수 없음.
}
1.1.2 객체타입
  • final로 선언하면 변수에 다른 참조 값을 지정할 수 없다.
  • 즉, 재변경(재할당)이 불가능. 단, 객체 자체가 불변이 아니기 때문에 객체의 속성(값)은 변경 가능
public void test_final_reference_variables() {
    final Pet pet = new Pet();
    //pet = new Pet(); //다른 객체로 변경할수 없음
    pet.setWeight(3); //객체 필드는 변경할 수 있음
}
1.1.3 메소드 인자
public class Pet {
    int weight;
    public void setWeight(final int weight) {
		//weight = 1; //final 인자는 메서드안에서 변경할 수 없음
    }
}
1.1.4 멤버변수
  • 멤버변수에 final로 선언하면 상수 값이 된다. 초기화는 딱 한 번 가능하다.

  • 클래스 변수(static)이냐 인스턴스 변수이냐에 따라 초기화 시점은 달라지므로 접근 시점, 방법이 다르다

public class Cat extends Pet {
    final int i_value;
    static int s_value;

    // 인스턴스 블록에서는 인스턴스 변수 static 변수 모두 접근 가능
    {
        System.out.println("instance initializer block");
        i_value = 3;
        System.out.println("i_value: " + i_value);
        System.out.println("s_value: " + s_value);
    }

    // static 블록에서는 인스턴스 변수 접근 불가능
    static {
        System.out.println("static initializer block");
		//System.out.println("i_value: " + i_value); //static 블록에서 필드 접근 안됨
        System.out.println("s_value: " + s_value);
    }

    public Cat() {
        System.out.println("contructor: Cat");
    }
}

1.2 final 메소드

  • 메소드를 final로 선언하면 상속받은 클래스에서 오버라이드가 불가능하다.

  • 따라서 재정의하는 것을 원치 않을 때 사용하면 된다.

public class Pet {
    public final void makeSound() {
        System.out.println("ahaha");
    }
}

public class Dog extends Pet {
    //final로된 메서드는 override할수 없음
     public void makeSound() {
     }
}

1.3 final 클래스

  • 클래스에 final을 선언하면 상속이 안된다. 클래스 그대로 사용해야함.

  • Util 형식의 클래스나 상수 값을 모아놓은 Constants 클래스에 사용

1.3.1 상수클래스
  • 상속해서 사용할 이유가 없음.
public final class Constants {
    public static final int SIZE = 10;
}

public class SubConstants extends Constants { // 이럴 이유가 없다.
}

Contstans.SIZE // 이렇게 사용하면 됨
1.3.2 Util 클래스
  • 아래의 경우 java 라이브러리 정의된 string 클래스. 이같은 것을 상속하면 유지보수가 힘들어질 수도 있음.
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
}

2. static 제어자

  • static을 변수에 쓰면 클래스 변수, 메서드에 쓰면 클래스 메서드
  • 프로그램 시작시 최초에 단 한 번만 생성되고 초기화된다.
  • 인스턴스를 생성하지 않고도 바로 사용할 수 있게 된다.
  • 해당 클래스의 모든 인스턴스가 공유한다.

3. abstract

  • 선언부만 있고 구현부가 없는 추상 클래스 or 메소드에 사용

  • 하나 이상의 추상 메소드가 있다면 클래스 앞에 꼭 붙여야함

  • abstract 클래스를 상속 받으면 반드시 구현해야함 -> abstract의 목적

4. 추가 정리

  • final과 abstract는 당연 같이 사용이 불가. final은 상속 및 오버라이딩이 불가능한데 abstract는 상속해서 오버라이딩해야함

  • static과 abstract 또한 같이 불가능. static은 바로 사용 가능하도록 구현되어야 하는데 abstract는 구현이 없음.

  • private과 abstract 또한 같이 불가능. abstract 메소드는 다른 클래스가 상속하여 오버라이딩해야 사용할 수 있는데 private 메소드는 자식 클래스에서 접근이 불가능하다.

  • 메소드에 private과 final은 함께 사용할 필요가 없음. 둘다 오버라이딩이 불가능하므로 둘 중 하나만 쓰면 된다.

Tags:

Categories:

Updated: