IT 개발노트
제어자란? 본문
1. 제어자
1.1 제어자란?
: 제어자는 클래스, 변수 또는 메서드의 선언부에 함께 사용되어 부가적인 의미를 부여한다. 제어자의 종류는 크게 접근 제어자와 그 외의 제어자로 나눈다.
- 접근 제어자 : public, protected, default, private
- 그 외 : static, final, abstract, native, transient, synchronized, volatile, strictfp
제어자는 클래스나 멤버변수와 메서드에 주로 사용되며, 하나의 대상에 대해서 여러 제어자를 조합하여 사용하는 것이 가능하다.
단, 접근 제어자는 한번에 네 가지중 하나만 선택해서 사용할 수 있다.
즉, 하나의 대상에 대해서 public과 private을 함께 사용할 수 없다는 것이다.
1.2 static - 클래스의, 공통적인
- 클래스변수는 인스턴스에 관계없이 같은 값을 갖는다. 하나의 변수를 모든 인스턴스가 공유한다.
- static이 붙은 멤버변수, 메서드, 초기화 블록은 인스턴스를 생성하지 않고도 사용가능하다.
- static이 사용될 수 있는 곳은 멤버변수, 메소드, 초기화 블록이다.
- 클래스가 메모리에 로드될 때 같이 로드된다. (Method Area)
- 메서드에 static을 붙이면 static메서드가 되며, 인스턴스 멤버들을 직접 사용 할 수 없다. 메서드 내에 따로 클래스를 인스턴스화하고 사용해야 한다.
- 인스턴스 멤버를 사용하지 않는 메서드는 static을 붙여서 static메서드로 선 언하는 것을 고려해야 한다. 가능하다면 static메서드로 하는 것이 인스턴스 를 생성하지 않고도 호출이 가능해서 더 편리하고 속도도 더 빠르다.
- static메서드는 오버라이드 할 수 없다.
단, @Override annotation을 지우고 Hiding이란 기법으로 오버라이드가 가 능 하지만 객체지향에 있어 바람직한 모습이 아니다.
1.3 final - 마지막의, 변경될 수 없는
- final은 클래스, 메서드, 멤버변수, 지역변수에 사용될 수 있다.
- 클래스에 사용되면 상속될 수 없고, 메서드에 사용되면 오버라이드될 수 없 다. 변수에 붙으면 값을 변경할 수 없는 상수가 된다.
- 대표적인 final클래스로는 Math와 String이 있다. 이들은 상속을 할 필요도 없고, 내부에 있는 멤버변수를 건드릴 필요도 없다.
- final이 붙은 변수는 상수이므로 일반적으로 선언과 초기화를 동시에 하지 만, 인스턴스 변수의 경우 생성자에서 초기화 되도록 할 수 있다.
단, 한번만 초기화 가능하다. 이 기능을 이용하면 각 인스턴스마다 final이 붙 은 멤버변수가 다른 값을 갖도록 하는 것이 가능하다.
1.4 abstract - 추상의, 미완성의
- abstract는 메서드의 선언부만 작성하고 실제 수행내용은 구현하지 않은 추 상 메서드를 선언하는데 사용된다.
- 클래스에 사용되어 클래스 내에 추상메서드가 존재한다는 것을 쉽게 알 수 있게 해준다.
- abstract는 클래스와 메서드에 사용될 수 있다. 메서드에 사용되면 구현부를 작성하지 않은 추상메서드임을 알린다.
- abstract클래스는 메서드 내용이 구현되지 않았으니 당연히 인스턴스화 할 수 없다.
- 가끔은 완성된 클래스(추상 메서드가 없는 클래스)도 abstract를 붙여서 추 상 클래스로 만드는 경우가 있다. 추상 메서드가 없는 클래스를 상속하여 원 하는 메서드만 오버라이딩해도 되는 장점이 있기 때문이다. 만일 이 클래스 가 없다면 아무런 내용도 없는 메서드를 잔뜩 오버라이딩 해야 한다.
1.5 접근제어자
- 접근제어자가 사용될 수 있는 곳 - 클래스, 멤버변수, 메서드, 생성자
- private : 같은 클래스 내에서만 접근이 가능
- default : 같은 패키지 내에서도 접근 가능
- protected : 같은 패키지, 상속받은 자식클래스에서도 접근 가능
- public : 어디서든 접근 가능
- public > protected > default > private
- 접근 제어자를 이용한 캡슐화, 접근 제어자를 사용하는 이유는 클래스 내부 에 선언된 데이터를 보호하기 위해서이다. 데이터가 유효한 값을 유지하도 록, 또는 비밀번호와 같은 데이터를 외부에서 함부로 변경하지 못하도록 하 기 위해서는 외부로부터의 접근을 제한하는 것이 필요하다. 이것을 데이터 감추기(data hiding)이라고 하며, 객체지향개념의 캡슐화에 해당한다.
- 접근 제어자를 사용하는 이유
-> 외부로부터 데이터를 보호하기 위해서
-> 외부에는 불필요한, 내부적으로만 사용되는 부분을 감추기 위해서
- 생성자의 접근 제어자(Singleton 패턴)
-> 생성자에 접근 제어자를 사용함으로써 인스턴스의 생성을 제한할 수 있 다.
-> 보통 생성자의 접근 제어자는 클래스의 접근 제어자와 같지만, 다르게 지 정 할 수도 있다.
-> 생성자의 접근 제어자를 private로 지정하면, 외부에서 생성자에 접근할 수 없으므로 인스턴스를 생성할 수 없게 된다.
* 클래스 내부에서는 인스턴스를 생성할 수 있다.
예시 1
1 2 3 4 5 6 | class Singleton { private Singleton() { //... } //... } |
- 대신 인스턴스를 생성해서 반환해주는 public메서드를 제공함으로써 외부에서 이 클래스의 인스턴스를 사용하도록 할 수 있다. 이 메서드는 public인 동시에 static이어야 한다.
예시 2
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Singleton { //... private Singleton s = new Singleton(); // getInstance()에서 사용될 수 있도록 인스턴스가 미리 생성되어야 하므로 static이어야 한다. private Singleton() { //... } // 인스턴스를 생성하지 않고도 호출할 수 있어야 하므로 static이어야 한다. public static Singleton getInstance() { return s; } //... } |
- 생성자를 통해 직접 인스턴스를 생성하지 못하게 하고 public메서드를 통해 인스턴스에 접근하게 함으로써 사용할 수 있는 인스턴스의 개수를 제한할 수 있다.
- 생성자가 private인 클래스는 다른 클래스의 조상이 될 수 없다.
-> 자손클래스의 인스턴스를 생성할 때 조상클래스의 생성자를 호출해야만 하는데, 생성자의 접근 제어자가 private이므로 자손클래스에서 호출하는 것이 불가능하기 때문이다. 그래서 클래스 앞에 final을 더 추가하여 상속할 수 없는 클래스라는 것을 알리는 것이 좋다.
예제 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | final class Singleton() { private static Singleton s = new Singleton(); private Singleton() { //... } public static Singleton getInstance() { if(s==null) s = new Singleton(); return s; } } class SingletonTest { public static void main(String[] args) { // Singleton s = new singleton(); // 에러 : Singleton() has private access in Singleton Singleton s = Singleton.getInstance(); } } |
* Singleton()을 private로 선언해줬기 때문에 새로 생성할수 없고 getInstance()를 사용해서 받아써야 한다.
1.6 제어자의 조합
대상 |
사용가능한 제어자 |
클래스 |
public, (default), final, abstract |
메서드 |
모든 접근 제어자, final, abstract, static |
멤버변수 |
모든 접근 제어자, final, static |
지역변수 |
final |
- 메서드에 static과 abstract를 함께 사용할 수 없다.
-> static메서드는 몸통이 있는 메서드에만 사용이 가능하기 때문이다.
(오버라이딩이 불가능 하기 때문이다.)
- 클래스에 abstract와 final을 함께 사용할 수 없다.
-> abstract는 추상클래스로 오버라이딩으로 완성해주어야 하는데, final은 더 이상 확장이 불가능하므로 둘을 같이 사용하면 아무 의미가 없어진다.
- abstract메서드는 접근제어자가 private일 수 없다.
-> abstract메서드는 자손클래스에서 구현해주어야 하는데 접근 제어자가 private이면, 자손클래스에서 접근할 수 없기 때문이다.
- 메서드에 private와 final을 같이 사용할 필요는 없다.
-> 접근 제어자가 private인 메서드는 오버라이딩될 수 없기 때문이다. 이 둘 중 하나만 사용해도 의미가 충분하다.