Java 접근제한자, final, static, 블럭, 캡슐화(Encapsulation)
Java 접근제한자, final, static, 블럭, 캡슐화(Encapsulation)
자바는 객체지향 프로그래밍 언어라서 캡슐화의 개념을 사용한다. 캡슐화는 외부 객체가 객체의 내부 구조를 알지 못하며 객체가 노출해서 제공하는 필드와 메소드만 이용하는 것을 의미한다. 이 캡슐화를 위해서 접근제한자를 사용한다.
접근제한자
프로그래밍 도구의 기본적인 목표는 생각하는 것을 자유롭게 표현할 수 있도록 하는 것이다. 하지만 자유 같은 변화를 수용하기 위해서는 다양한 규제가 필요해지게 된다.
접근 제한자는 데이터에 직접 접근하는 것에 대한 방지책이다.
접근제한자의 종류
- Public - 외부에서 직접 접근하거나 호출할 수 있다.
- Protected - 현재 클래스와 동일 패키지이거나 다른 패키지이더라도 상속 시에는 접근하거나 호출할 수 있다.
- Default - 현재 클래스와 동일한 패키지 내에서만 접근하거나 호출할 수 있다.
- Private - 현재 클래스의 {} 바깥쪽에서는 절대로 보이지 않는다. 자기 클래스가 아닌 바깥에서 호출할 수 없다.
접근제한자를 어디에 쓸까?
- 클래스 : public(외부에 클래스를 노출), default(현재 패키지 내에서만 보이겠다)
- 인스턴스 : public, protected, default, private(protected, default는 메소드를 이용해서 접근한다.)
- 메소드 : public, protected, default, private(추상 메소드의 형태로 메소드를 만들 때는 private으로 선언할 수 없다)
final
- 메소드 선언 시에 넣으면 현재의 메소드를 수정할 수 없게 한다. 오버라이드해서 스스로 재정의하는 작업을 막는다.
- 클래스 선언 시에 넣으면 상속하지 못하게 된다.
- 변수 선언 시에 넣으면 변수의 값을 변경할 수 없게 한다.
- 생성자에는 붙일 수 없다.
static
Static을 사용하면 ‘모든 객체가 공유’한다는 의미다.
인스턴스가 자신의 인스턴스 변수를 한 인스턴스에서 바꿔봤자 새로운 인스턴스를 만들면 새 인스턴스 변수로 나오니까 공유가 되지않는다. 그 값 자체를 공유하고 싶을 때 쓴다. 즉, 인스턴스 간에 공유된다.
static을 사용할 때 인스턴스는 필요없다. 그냥 클래스이름.메소드, 클래스이름.변수로 쓸 수 있다.
- static 변수(클래스 변수)
- Static은 인스턴스와 묶이는 데이터가 아니라 클래스와 묶이는 데이터라서 static이 붙은 변수를 클래스 변수라고 한다.
- 인스턴스가 아닌 클래스의 변수이기 때문에 별도의 인스턴스를 생성할 필요 없이 그냥 사용할 수 있다.
- static이 붙은 변수는 당연히 일반 메소드에 쓰일 수 있다. 변수가 먼저 메모리에 올라가니까
- static 메소드(클래스 메소드)
- 마찬가지로 클래스에서 나온 인스턴스의 데이터에 영향을 받지 않고 완벽히 동일하게 동작하는 메소드.
- 모든 인스턴스가 클래스에 정의된 방법대로 사용하게 된다.
- Ex) Integer.parseInt()의 경우, 문자열을 int 값으로 변경시키는데 어느 상황이든 인스턴스의 데이터에 영향을 받지 않고 같은 기능만 제공한다.
- Static이 붙은 메소드에서는 클래스의 인스턴스 변수나 인스턴스의 메소드를 사용할 수 없다. 왜냐하면 클래스의 인스턴스 변수를 사용할 때 인스턴스화 되어서 인스턴스로 생성되어야 하는데 그 전에 static 메소드가 먼저 메모리에 할당 되어 쓰일 수 없기 때문.
- 그래서 static은 this를 못 쓴다. this 자체가 객체에서 찾는 건데 static은 객체 없이 만들어질 수 있으므로.
- 물론 static이 붙은 메소드에 static이 붙은 인스턴스 변수라면 상관없다.
그러면 언제 static을 쓸까?
데이터를 보관할 필요가 없을 때 쓴다. 데이터를 활용해야 되는 경우인지, 아닌지.
- 예를 들어, 랜덤 seed를 가지게 하는 방법 중에 Math.random()과 Random클래스의 nextDouble()이 있다. Random클래스를 이용하면 Random r1 = new Random(11)으로 각 인스턴스가 자신만의 Seed 값을 갖게 할 수 있다. 그러나 Math.random()은 static으로 매번 동일한 방법으로 동작한다.
- 즉, 어떤 메소드가 완전히 인스턴스의 데이터와 독립적인 결과를 만들어내야 하는 상황에서 static을 쓴다.
- static을 쓸 때 되는 지 안 되는 지 헷갈린다면 이것만 잘 기억하면 될 것 같다. static과 클래스는 메모리에 먼저 할당되기 때문에 인스턴스를 이용해서 static과 클래스에 접근하는 건 상관이 없다. 왜냐면 인스턴스를 이용해서(다 생성되고 난 후에) 이미 할당 된 곳(클래스와 static)에 접근하는 거니까. 하지만 static과 클래스를 이용해서 인스턴스에 접근하는 건 아직 인스턴스가 생성되지 않았음에도 불구하고 static과 클래스가 이용하려는 것과 같다.
- Static은 속도는 빠르지만 GC의 대상이 아니라서 메모리에 계속 상주해있기 때문에 주의해야 한다.
블럭
public class Person {
// 그냥 블럭에 static을 붙이면 클래스가 메모리에 로드될 때 딱 한 번 찍힌다. 딱 한 번만 할 일에 쓴다.
static {
System.out.println("static initializer.......");
totalCount = 100;
}
// 그냥 블럭이라면 객체가 생성될 때마다 실행되는 블럭이다. 근데 어차피 생성자에서 해도 되니까 굳이 만들 필요가 없다. 모든 생성자들 앞에 뭔가 실행해주고 싶을 때 쓴다.
{
System.out.println("instance initalizer......");
}
캡슐화(Encapsulation)
인스턴스 변수를 private으로 선언하는 이유는 캡슐화, 즉 정보은닉을 위해서이다.
그러나 데이터를 숨겨버리니 사용하기가 어려워졌다.
이에 따라서 조회만 할 수 있게 하는 메소드 getter와 데이터를 세팅할 수 있는 메소드 setter를 만들어 두고 사용하자는 게 나왔다.
private int val;
...
getVal(){ // 권한 체크에 쓰인다
return val;
}
setVal(int val){ // 입력 값 체크에 쓰인다
this.val = val;
}
AccessTest at = new AccessTest();
at.setVal(100);
int value = at.getVal();
- Getter 메소드(get??라고 이름을 지은 메소드) : 보안을 위해서 인스턴스의 데이터를 private으로 처리하고 외부에서 필요하면 getter 메소드를 통해 알 수 있도록 한다. 메소드를 통해 인스턴스가 가진 데이터를 가져온다. 리턴한 값을 다른 변수에 저장하면 복사되기 때문에 원본을 알 수 없다.
- Setter 메소드 : 데이터를 바꿀 때(저장할 때) Setter 메소드를 이용한다. 이렇게 했을 때 데이터가 손상되지 않는 이유는 at.getVal();로 데이터를 가져오지만 return값으로 하고 =으로 할당되어 복사되기 때문.