Java/객체 지향
제어자와 캡슐화
늦은산책
2023. 5. 9. 15:00
제어자
- 클래스와 클래스의 멤버(멤버 변수, 메소드)에 부가적인 의미 부여
- 접근 제어자 = public, protected, (default), private
- 그 외 = static, final, abstract, native, transient, synchronized, volatile, strictfp
- 하나의 대상에 여러 제어자를 같이 사용 가능하다.(접근 제어자는 하나만 사용 가능하다)
public class ModifierTest {
public static final int WIDTH = 200;
public static void main(String[] args) {
System.out.println("WIDTH="+WIDTH);
}
}
그 외 접근자 종류
제어자 | 대상 | 의미 |
static | 멤버변수 | - 모든 인스턴스에 공통적으로 사용되는 클래스 변수가 된다 - 클래스 변수는 인스턴스를 생성하지 않고도 사용 가능하다. - 클래스가 메모리에 로드될 때 생성된다. |
메소드 | - 인스턴스를 생성하지 않고도 호출이 가능한 static 메소드가 된다. - static 메소드 내에서는 인스턴스 멤버들을 직접 사용할 수 없다. |
|
final | 클래스 | - 변경 될 수 없는 클래스, 확장 될 수 없는 클래스가 된다. - final로 지정된 클래스는 다른 클래스의 조상이 될 수 없다 |
메소드 | 변경 될 수 없는 메소드, final로 지정된 메소드는 오버라이딩을 통해 재정의 될 수 없다. | |
멤버 변수 | 면수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다. | |
지역 변수 | ||
abstract | 클래스 | 클래스 내에 추상 메소드가 선언되어 있음을 의미한다. |
메소드 | 선언부만 작성하고 구현부는 작성하지 않은 추상 메소드임을 알린다. |
class StaticTest {
static int width = 200;
static int height = 120; 클래스 변수(static 변수)
static {
// 복잡한 static 변수의 초기화
}
static int max(int a, int b) { 클래스 메소드(static 메소드)
return a > b ? a : b;
}
}
객체 생성 없이 사용이 가능하다. 하지만 iv와 인스턴스메소드는 사용할 수 없다.
-------------------------------------------------------------------------------------
class FinalTest { 조상이 될 수 없는 클래스
final int MAX_SIZE = 10; 값을 변경 할 수 없는 멤버변수(상수)
final void getMaxSize() { 오버라이딩 할 수 없는 메소드(변경이 불가능하다)
final int LV = MAX_SIZE; 값을 변경 할 수 없는 지역변수(상수)
return MAX_SIZE;
}
}
-------------------------------------------------------------------------------------
abstract class AbstractTest { 추상 클래스(추상 메서드를 포함한 클래스)
abstract void move(); 추상 메서드(구현부가 없는 메소드)
}
미완성 메소드를 가진 미완성 클래스가 된다.
Abstract = 미완성 이란 뜻이다. 추상 메소드를 가지고 있다면 무조건 추상 클래스 이다.
AbstractTest a = new AbstractTest(); 미완성된 클래스(설계도)이기 때문에 객체 생성이 안된다.
그렇다면 abstract 클래스는 어떻게 사용하는것인가?
상속(extends)를 받아서 완전한 구상클래스를 만든후에 객체 생성을 한다.
접근 제어자
- private 같은 클래스 내에서만 접근이 가능하다.
- (default) 같은 패키지 내에서만 접근이 가능하다.
- protected 같은 패키지 내에서, 그리고 다른 패키지의 자손 클래스에서 접근이 가능하다.
- public 접근 제한이 전혀 없다.
제어자 | 같은 클래스 | 같은 패키지 | 자손클래스 | 전체 |
public | O | O | O | O |
protected | O | O | O | |
(default) | O | O | ||
private | O |
- 4가지 모두다 메소드에 붙이는것이 가능하지만 클래스에서는 public 과 (default)만이 붙일수 있다.
캡슐화와 접근 제어자
- 우리가 기본적으로 접근 제어자를 사용하는 이유가 뭘까?
- 1. 외부로부터 데이터를 보호하기 위한것이다.
우리가 시간을 다룰 수 있는 class가 있다고 생각해보자
public class Time {
public int hour;
public int minute;
public int second;
}
그럼 다른곳에서
Time t = new Time();
t.hour = 35;
이런식으로 직접적으로 접근할 수가 있다.
하지만 상식적으로 우리는 시간을 다루고 싶은데 35시 라는건 있을수가 없다.
즉, 시간의 데이터들이 보호가 안된다는 것이다.
그래서 접근 제어자로 외부의 접근을 막는다.
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;
}
}
이런식으로 변수들을 private로 막아내고 메소드로 적용을 할 수 있게 만드는것이다.
그러면 hour를 바꾸기 위해 setHour라는 메소드를 거쳐야 하는데 메소드에는 조건이 있다.
그래서 35라고 해봤자 조건에 부딫혀 숫자가 나타나지 않는다. 이것을 통해 hour를 직접 접근하거나
숫자를 마음대로 바꿀수 없게 만들면서 데이터를 외부로 부터 보호하게 되는것이다.
- 2. 외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해서 사용한다.
class Time {
private int hour;
private int minute;
private int second;
public void setHour(int hour) {
if(isValidHour(hour)) return;
this.hour = hour;
}
private boolean isValidHour(int hour) {
return hour < 0 || hour > 23;
}
public int getHour() { return hour; }
}
public class TimeTest {
public static void main(String[] args) {
Time t = new Time();
t.setHour(21);
System.out.println(t.getHour());
t.setHour(100);
}
}
가장 크게 바뀐곳이 isValidHour메소드의 유무이다. t.setTime의 메소드가 하는 일조차 숨기고 싶었다.
그래서 Time class에 isValidHour의 메소드를 private로 접근을 제한하고 Time의 범위를 숨겨놓고 사용할 수 있다.
그리고 우리가 나중에 확인할때는 해당 메소드만 확인하면 되는것이다. 이것 또한 캡슐화의 장점이라고 볼 수있다.