TIL-23-02-21
Updated:
제어자(modifier)
- 제어자는 클래스, 변수, 메서드의 선언부에 함께 사용되어 부가적인 의미를 부여한다.
- 제어자의 종류는 크게 접근 제어제와 그 외 제어자로 나뉜다.
접근 제어자 public, proteced, default, private
그 외 static, final, abstract, native, transient, synchronized, stricffp
- 하나의 대상에 여러 제어자를 조합하여 사용할 수 있다. 단, 접근 제어자는 하나만 사용할 수 있다.
public class 클래스명 { public static final int WIDTH = 200; // static, final 동시에 사용. 접근제어자는 public 사용 public static void main(String[] args){ // 제어자들 간의 순서는 관계없지만 주로 접근 제어자를 제일 먼저 놓는다. system.out.print("WIDTH=" + WIDTH); } }
static - 클래스의, 공통적인
- static이 붙은 멤버변수와 메서드, 그리고 초기화 블럭은 인스턴스가 아닌 클래스에 관계된 것이므로 인스턴스를 생성하지 않고도 사용할 수 있다.
- 인스턴스멤버를 사용하지 않는 메서드는 static을 붙여서 static 메서드로 선언하는 것을 고려하자. 가능하다면 static 메서드로 호출하는 것이 인스턴스를 생성하지 않고도 호출이 가능해서 더 편리하고 속도도 빠르다.
static이 사용될 수 있는 곳 - 멤버변수, 메서드, 초기화 블럭
class StaticTest {
static int width = 200; //클래스 변수(static 변수) 간단(명시적) 초기화
static int height = 120; //클래스 변수(static 변수)
static { //클래스 초기화 블럭 (클래스가 메모리에 로드될 때 한번 수행되며,클래스 변수를 초기화하는데 사용된다.)
// static 변수의 복잡한 초기화 수행
}
static int max(int a, int b){ //클래스 메서드(static 메서드)
return a > b ? a : b ;
}
}
final - 마지막의, 변경될 수 없는
- 변수에 사용하면 값을 변경할 수 없는 상수가 되고, 메서드에 사용하면 오버라이딩 할 수 없게 되고, 클래스에 사용하면 자신을 확장하는 자손클래스를 정의할 수 없다.(조상클래스가 될 수 없다.)
- 대표적인 final 클래스로는 String과 math가 있다.
final이 사용될 수 있는 곳 - 클래스, 메서드, 멤버변수, 지역변수
생성자를 이용한 final 멤버 변수의 초기화
- final이 붙은 변수는 상수이므로 일반적으로 선언과 초기화를 동시에 하지만, 인스턴스변수의 경우 생성자에서 초기화가 가능하다.
class Card {
final int NUMBER; //상수지만 선언과 함께 초기화 하지 않고 생성자에서 단 한번 초기화할 수 있다.
final String KIND;
static int width = 100;
static int height = 250;
Card(String kind, int num) { //매개변수를 갖는 생성자를 선언하여, final 멤버변수를 초기화하는데 필요한 값을
KIND = kind; //생성자의 매개변수로부터 받는다.
NUMBER = num;
}
Card() {
this("HEART", 1)
}
public String toString() {
return KIND + " " + NUMBER;
}
}
class FinalCardTest {
public static void main(String args[]) {
Card c = new Card("HEART", 10);
// c.NUMBER = 5; //에러. cannot assign a value fo final variable NUMBER
System.out.printle(c.KIND);
System.out.printle(c.NUMBER);
System.out.printle(c);
}
}
//실행결과
HEART
10
HEART 10
abstract - 추상의, 미완성의
- 메서드의 선언부만 작성하고 실제 수행내용은 구현하지 않는 추상 메서드를 선언하는데 사용된다.
- 클래스에 사용되면 클래스 내에 추상 메서드가 존재함을 의미한다.
- 추상메서드는 아직 완성되지 않은 메서드가 존재하는 ‘미완성 설계도’이므로 인스턴스 생성이 불가하다.
abstract가 사용될 수 있는 곳 - 클래스, 메서드
abstract class AbstractTest { //추상 클래스(추상 메서드를 포함한 클래스
abstract void move(); //추상 메서드(구현부가 없는 메서드)
}
접근 제어자
- 접근 제어자는 멤버 또는 클래스에 사용되어 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한한다.
접근 제어자를 이용한 캡슐화
- 접근 제어자를 사용하는 이유는
- 외부로부터 데이터를 보호하기 위해서(데이터 감추기)
- 외부에서 접근할 필요없는, 내부적으로만 사용되는 부분을 감추기 위해서
- 기능 변경 후 테스트 시, 메서드의 접근 제어자가 default나 private이라면 테스트 범위가 좁아지므로 테스트 시간을 줄일 수 있다.
public class Time {
private int hour; //접근 제어자를 private으로 하여 외부에서 직접 접근하지 못하도록 한다.
private int minute;
private int second;
public int getHour() { //멤버변수의 값을 일고 변경할 수 있는 public 메서드를 제공하여
return hour; //간접적으로 멤버변수의 값을 다룰 수 있도록 함
}
public void setHour(int hour) {
if (hour < 0 || hour > 23) return;
this.hour = hour;
}
public int getMinute() {
return minute;
}
public void setMinute(int minute) {
if (minute < 0 || minute > 23) return;
this.minute = minute;
}
public int getSecond() {
return second;
}
public void setSecond(int second) {
if (second < 0 || second > 23) return;
this.second = second;
}
}
생성자의 접근 제어자
- 생성자에 접근 제어자를 사용함으로써 인스턴스 생성을 제한할 수 있다.
- 대신 인스턴스를 생성해서 반환해주는 public 메서드를 제공하여 외부에서 이 클래스의 인스턴스를 사용하도록 할 수 있다.
- 이 메서드는 인스턴스를 생성하지 않고도 호출할 수 있어야 하므로 public인 동시에 static이어야 한다.
class Singleton {
...
private static Singleton s = new Singleton(); //getInstance()에서 사용될 수 있도록 인스턴스가 미리 생성되어야 하므로 static이어야 한다.
private Singleton() {
...
}
//인스턴스를 생성하지 않고도 호출할 수 있어야 하므로 static이어야 한다.
public static Singleton getInstance() {
return s;
}
class SingletonTest {
public static void main(String args[]) {
// Singleton s = new Singleton(); //에러. Singleton() has private access in Singleton
Singleton s = Singleton.getInstance();
}
}
}
- 생성자가 private인 클래스는 다른 클래스의 조상이 될 수 없다.
- 클래스 앞에 final을 추가하여 상속불가한 클래스란 것을 알려야 한다.
public final class Math {
private Math() {}
...
}
다형성(polymorphism)
- 여러가지 형태를 가질 수 있는 능력
- 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 한다.
- 인스턴스 타입과 참조변수의 타입이 일치하는 것이 보통이지만, 두 클래스가 서로 상속관계에 있는 경우 타입 불일치가 가능하다.
TV t = new CaptionTV(); //CaptionTV가 TV를 상속받은 자손클래스이면 타입 불일치 가능
- 같은 타입의 인스턴스라도, 참조변수의 타입에 따라 사용할 수 있는 멤버의 갯수가 달라진다.
-
Tv타입의 참조변수로는 Tv클래스에 정의 되지 않은 멤버, text와 caption()은 참조변수 t로 사용이 불가능하다. ```java class Tv{ boolean power; int channel;
void power(){ power = !power; } void chnnelUp(){ ++channel; } void channelDown(){ –channel; } }
class CaptionTV extends Tv { String text; void caption(){} }
```java
CaptionTv c = new CaptionTv(); //인스턴스타입과 같은 참조변수
Tv t = new CaptionTv(); //조상타입의 참조변수
- 반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 순 없다.
- 참조변수(자손)가 사용할 수 있는 멤버의 개수가 인스턴스 멤버(부모)의 개수보다 많으면 존재하지 않는 멤버를 사용하고자 할 가능성이 있으므로 허용하지 않는다.
CaptionTv c = new Tv(); //허용 불가