제어자
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은 함께 사용할 필요가 없음. 둘다 오버라이딩이 불가능하므로 둘 중 하나만 쓰면 된다.