●제어자(modifier)
제어자는 영어 문법에서 형용사와 같은 역할을 한다. 제어자는 클래스, 변수 또는 선언부에 함께 사용되며 부가적인 의미를 부여한다. 제어자는 접근 제어자와 그 외의 제어자로 나눌 수 있다.
접근 제어자 public, protected, (default), private
그 외 static, final, abstract, native, transient, sychronized, volatile, strictfp
제어자는 클래스나 멤버변수, 메서드에 주로 사용되며 , 하나의 대상에 대해서 여러 제어자를 조합하여 사용하는 것이 가능하다. 단, 접근 제어자는 한 번에 네 가지 중 하나만 선택해서 사용할 수 있다. 접근 제어자 이외에도 여러 제외자가 있지만 'static, final, abstract'제어자를 주로 사용한다. 앞으로 접근 제어자와 주요 세 제어자에 대해 알아보겠다.
●static - 클래스의, 공통적인
static은 '클래스의' 또는 '공통적인'의 의미를 갖는다. static이 멤버변수에 쓰인다면, 모든 객체에 공통적으로 사용되는 클래스 변수가 된다. 또한 클래스 변수는 객체를 생성하지 않고도 사용가능하다. 마지막으로 클래스가 메모리에 로드 될 때 생성된다. 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은 문자 의미 그대로 '마지막의', '변경될 수 없는'을 의미한다. 변수에 사용되면 값을 변경할 수 없는 상수가 되며, 메서드에 사용되면 오버라이딩을 할 수 없게된다. 또한 클래스에 사용되면 자신을 확장하는 자손클래스를 정의하지 못하게 된다.
final이 사용될 수 있는 곳 - 클래스, 메서드, 멤버변수, 지역변수
대상 | 의미 |
클래스 | 변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다. 또한 다른 클래스의 조상이 될 수 없다. |
메서드 | 변경될 수 없는 메서드, final로 지정된 메서드는 오버라이딩을 통해 재정의 될 수 없다. |
멤버변수 | 변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다. |
지역변수 | 멤버변수에 쓰인 final과 같은 의미를 가진다. |
final class FinalTest { // 조상이 될 수 없는 클래스
final int MAX_SIZE = 10; // 값을 변경할 수 없는 멤버변수(상수)
final void getMaxSize() { // 오버라이딩할 수 없는 메서드(변경불가)
final int LV = MAX_SIZE; // 값을 변경할 수 없는 지역변수(상수)
return MAX_SIZE;
}
}
●abstract - 추상의, 미완성의
abstract는 '미완성'의 의미를 가지고 있다. 메서드의 선언부만 작성하고 실제 수행내용은 구현하지 않은 추상메서드를 선언하는데 사용된다. 그리고 클래스에 사용되어 클래스 내에 추상메서드가 존재한다는 것을 쉽게 알 수 있게 한다.
abstract가 사용될 수 있는 곳 - 클래스, 메서드
대상 | 의미 |
클래스 | 클래스 내에 추상 메서드가 선언되어 있음을 의미한다. |
메서드 | 선언부만 작성하고 구현부가 작성하지 않은 추상 메서드임을 알린다. |
abstract class AbstractTest { // 추상 클래스(추상 메서드를 포함한 클래스)
abstract void move(); // 추상 메서드(구현부가 없는 메서드)
}
●접근 제어자(access modifier)
접근 제어자는 멤버 또는 클래스에 사용되어, 해당 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 한다. 나중에 소개할 캡슐화개념과 연관있다.
접근 제어자가 사용될 수 있는 곳 - 클래스, 멤버변수, 메서드 , 생성자
private 같은 클래스 내에서만 접근이 가능하다.
(default) 같은 패키지 내에서만 접근이 가능하다.
protected 같은 패키지 내에서, 그리고 다른 패키지의 자손클래스에서 접근이 가능하다.
public 접근 제한이 전혀 없다.
접근 범위의 순서를 따져보면 다음과 같다
public > protected > (default) > private
제어자 | 같은 클래스 | 같은 패키지 | 자손클래스 | 전체 |
public | O | O | O | O |
protected | O | O | O | |
(defalut) | O | O | ||
private | O |
●캡슐화와 접근 제어자
클래스나 멤버는 접근 제어자를 이용하여 두 가지 효과를 보고자 한다. 접근 제어자를 사용하는 첫 번째 이유는 클래스의 내부에 선언된 데이터를 보호하기 위해서다. 만약 외부클래스로부터 내부클래스가 영향을 받게된다면, 외부클래스로 인해 내부클래스가 변경 될 수 있다. 그렇다면 비밀번호와 닽은 데이터를 외부에서 함부로 변경하는 불상사가 발생 할 수 있다. 이를 위해 접근 제어자를 사용한다. 접근 제어자를 사용하는 두 번째 이유는 클래스 내에서만 사용되는 작업들을 불필요하게 외부에 노출시킬 필요가 없기 때문이다. 이를 통해 코드의 복잡성을 줄일 수 있다. 접근 제어자를 통해 내부의 클래스를 외부로부터 영향을 받지 않게 하거나, 외부 노출에 불필요한 부분을 감춤으로 코드의 복잡성 줄이는 것을 객체지향에서는 '캡슐화(encapsulation)'라 한다.
접근 제어자를 사용하는 이유
- 외부로부터 데이터를 보호하기 위해서
- 외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해서
public class Time {
public int hour;
public int minute;
public int second;
}
Time t = new Time();
t.hour = 25; //멤버변수에 직접 접근
Time클래스를 우리가 일반적으로 사용하는 시간이라고 정의해보자. 그렇다면 시간은 0~24시가 되어야 한다. 그런데 Time클래스 내부에 있는 시간을 나타내는 'hour'의 접근제어자가 외부에서 접근할 수 있는 'public'으로 정의되어 있다. 그래서 외부에서 hour멤버변수에 접근할 수 있고 클래스 밖에서 내부의 멤버변수에 직접 접근했다. 우리가 예상했던 0~24의 값이 아닌 25의 값으로 정의되어 문제가 발생했다. 이 문제를 해결하기위해서 접근제어자를 private으로 하는 캡슐화 과정이 필요하다.
public class Time {
private int hour;
private int minute;
private int second;
public int getHour() { 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 > 59) return;
this.minute = minute;
}
public int getSecond() { return second; }
public void setSecond(int second) {
if (second < 0 || second > 59) return;
this.second = second;
}
}
앞선 코드와 달리 이번 코드에서는 Time클래스의 멤버변수를 private를 사용하여 캡슐화시켰다. 캡슐화를 통해 외부 클래스에서는 함부로 값을 변경할 수 없게 만듦으로써 앞선 코드에서의 문제를 해결했다.
출처: 자바의 정석 기초편(저자: 남궁성, 출판사: 도우출판)
'java > 객체지향' 카테고리의 다른 글
[객체지향]여러 종류의 객체를 배열로 다루기 (0) | 2021.07.16 |
---|---|
[객체지향] 다형성(polymorphism) (0) | 2021.07.15 |
[객체지향] super와 super() (0) | 2021.07.14 |
[객체지향]오버라이딩(overriding) (0) | 2021.07.13 |
[객체지향] 상속 (0) | 2021.07.13 |