211028(1) Java - 접근제한자와 캡슐화
접근제한자
멤버 또는 클래스에 사용되어 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 한다. (접근을 하든가 못 하든가) 왜? 외부로부터 데이터 보호하고, 내부적으로 사용되는 부분을 감추기 위해서 사용한다.
private - 같은 클래스 내에서만 접근 가능
- "내가 알아서 처리할 거니까 값만 던져주면 돼" (안에는 안 보이게 보호를 한다)
default - 같은 패키지 내에서만 접근 가능 (그래서 다른 패키지에서 클래스를 여러개 만들어도 가능함)
protected - 같은 패키지 내에서와 다른 패키지의 자손 클래스에서 접근 가능(상속 받았다면 가능함)
public - 접근제한이 없음(가장 많이 사용)
지금까지 접근제한자를 하지 않은 이유는 자동으로 default가 지정되기 때문임. 내부적으로 디폴트가 자동 저장 됨
대상에 따라 사용할 수 있는 접근 제어자가 정해져있다.
클래스 - public, (default)
메서드, 멤버변수(인스턴스) - 다 쓸 수 있음!! private > default > potected > public
지역변수 - 없음 (지역 변수는 나가면 사라지니까!)
제너레이트 get, set 만드는 건 당분간 하지 않고 손으로 코딩을 해야 프로그램이 실행되는 과정들을 알 수 있다. 앞으로 jsp 등 다 이 구조를 이용해서 만들게 됨
class Time
{
private int hour, minute, second;
Time(int hour, int minute, int second)
{
setHour(hour);
setMinute(minute);
setSecond(second);
}
public void setHour(int hour)
{
if(hour < 0 || hour > 23) return;
this.hour = hour;
}
public int getHour()
{
return hour;
}
public void setMinute(int minute)
{
if(minute < 0 || minute > 59) return;
this.minute = minute;
}
public int getMinute()
{
return minute;
}
public void setSecond(int second)
{
if(second < 0 || second > 59) return;
this.second = second;
}
public int getSecond()
{
return second;
}
//toString 오버라이딩
public String toString()
{
return hour + ":" + minute + ":" + second;
}
}
public class Sample1028 {
public static void main(String[] args) {
Time t = new Time(12, 35, 30);
System.out.println(t);
t.setHour(t.getHour() + 1);
System.out.println(t);
//11:12:35 출력
//원래는 이 방법을 쓴다
t.setHour(11);
t.setMinute(12);
t.setSecond(35);
System.out.println(t);
//11:12:35 출력 이런 방법도 있다.
t.setHour(t.getHour()-2);
t.setMinute(t.getMinute()-23);
t.setSecond(t.getSecond()+5);
System.out.println(t);
}
}
12:35:30 13:35:30 11:12:35 11:12:35 |
시간 클래스를 만들려고 한다. private 접근지정자를 해주는 이유는 인자값만 받아오고 안에서 어떻게 실행이 되는지 숨기기 위해서이다(보호 목적). 메인에서 객체생성을 하면서 시, 분, 초를 넣어주면 알아서 초기화가 되는 클래스 라는 것을 알아두면 된다.
Time 클래스를 뜯어보게 되면 클래스에는 default가 생략되어 있지만 지정되어 있다. 다만 변수를 private로 지정해두었다. private로 지정된 변수를 사용하기 위해서는 그 무엇도 아닌 Time클래스 내에서 정의 돼 있는 메소드를 통해서만 값을 전달해 줄 수 있음 그 말인즉슨 geter(), seter()를 사용해서만 초기화를 시킬 수 있다.
hour만 놓고 보자면...
//외부에서 타입 클래스를
public void setHour(int hour)
//seter는 매개변수를 받는다.
//seter에서만
{
if(hour < 0 || hour > 23) return;
this.hour = hour;
}
public int getHour()
//매개변수 받을 필요가 없다.
{
return hour;
}
get set은 어디서든지 접근이 가능하도록 public으로 지정해주어야 한다.
외부에서 타임 클래스에 접근하기 위해서 값을 던져주면 set은 그 인자값을 받아주고, get은 받은 인자값을 리턴해주는 기능을 하고 있다. 각자의 역할이 있기 때문에 set에서는 매개변수로 받아서 this.hour(내 클래스의 인스턴스 변수) = hour(전달 받은 매개변수) 이 문으로 매개변수 값을 세팅시켜준다.
이렇게 세팅시킨 값은 get으로 리턴을 시켜주는 것임. 인자값을 이미 받았기 때문에 매개변수가 없어야 하고(하지만 있는 경우도 종종 있지만 보통은 없다), return을 통해서 전달을 해줘야하기 때문에 리턴타입을 지정해주어야 한다. 매개변수 타입과 리턴 타입이 같아야 한다. 변수값을 던지는 역할만 한다.
set에서 반환타입이 void 라고 해서 리턴을 못 쓰나? 쓸 수 있음
if(hour < 0 || hour > 23) return;
다만, 여기서 쓰는 return의 경우 값을 전달하는 의미로 사용되는 게 아니라 if를 처리하고 나서, 나를 호출한 곳으로 돌아가려는 목적으로 사용하는 것이다. 한 가지 기능만 실행하기 때문에 {}가 필요 없음. 아래 것이 실행되지 않고 호출한 곳으로 돌아간다.
Time(int hour, int minute, int second)
{
setHour(hour);
setMinute(minute);
setSecond(second);
//이미 초기화가 돼 있으니까 부르기만 하면 됨
}
하나하나 변수 설정을 하지 않고 메소드를 통해서 값을 넣는 이유는 코드의 중복을 최소화 하기 위해서이다.
public static void main(String[] args) {
Time t = new Time(12, 35, 30);
//t.hour = 12;
System.out.println(t);
t.setHour(t.getHour()-2);
t.setMinute(t.getMinute()-23);
t.setSecond(t.getSecond()+5);
System.out.println(t);
t.setHour(11);
t.setMinute(12);
t.setSecond(35);
System.out.println(t);
}
Time 클래스를 접근지정 private로 하지 않고 int로 했을 경우에는 객체 생성 후에 t.hour = 12; 이런 식으로 시간을 직접 넣는 것이 가능하지만 private의 경우 같은 클래스 내에서만 접근이 가능하기 때문에 main 클래스에서는 안 된다. 그래서 사용하는 것이 Time 클래스에서 지정한 get(), set() 메소드이다. 대신에서 값을 받고 그 값을 또 다른 애가 대신해서 전달해주기 위해서 이런 번거로운 작업을 하는 이유는 안전성을 높이기 위해서이다.
객체생성을 할 때 인자값을 전해주면 매개변수 3개 있는 생성자로 들어가서 초기화된 곳으로 알아서 옮겨진다. 그 뒤로 바꾸기 위해서 t.setHour(11); 이런식으로 인자값을 주어 바꿔줄 수 있다.
여기서 잠깐! 원래 참조변수 값을 print로 넘길 경우 Object 클래스에 있는 .toString 메소드가 실행이 되면서 주소값이 출력된다. 하지만 Time 클래스에서 오버라이딩을 해주면 내가 원하는 값으로 출력할 수 있다. 덕분에 여기서는 12:35:30 이런 값으로 출력이 되는 것이다.