1. 접근 제어자
- 접근 제어자는 Java 에서 변수나 메소드의 사용 권한을 설정하기 위해 사용하는 방법이다.
- 접근 제어자는 변수나 메서드 앞에 올 수 있으며 아래 표에 있는 4가지를 사용할 수 있다.
- 단, 클래스의 경우 public 과 default 만 올 수 있다.
- 단, 지역 변수의 경우 어떤 것도 올 수 없다. 이는 메소드나 반복문 등 특정 모듈안에서만 사용되고 사라지는 지역 변수의 특성상 굳이 올 필요가 없기 때문이라고 할 수 있다.
- 위에서 아래로 pivate -> defalult -> protected -> public 순으로 많은 접근을 허용한다.
- 이러한 접근 제어자를 통해서 데이터를 감추고 보고하는 것을 객체지향개념에서 캡슐화(encapsulation)라고 한다.
접근제어자 종류 | 내용 |
private | 같은 클래스에서만 접근 가능 - 외부에서는 getter , setter 사용해서 접근 |
defalult | 같은 패키지에서만 접근 가능 - 접근 제어자를 생략하는 경우 기본은 DEFAULT |
protected | 같은 패키지 & 상속관계에 있는 곳에서만 접근 가능 |
public | 어디에서나 접근 가능 |
2. 접근제어자에 따른 접근 방법 - SETTER & GETTER
1) Private
- 사실 접근자에서 가장 중요한 것은 바로 private 가 아닐까 생각한다.
- Spring 을 공부하거나 다른 곳을 찾아보면서 보았을 때 가장 많이 중요한 곳에 사용하는 것은 바로 private 였다고 생각한다.
- private 가 public 와 엑세스 범위가 다르다는 점도 있지만 둘의 가장 큰 차이점은 바로 외부에서 private 접근제어자가 달린 변수에 접근하기 위해서 GETTER 와 SETTER 메서드를 사용한다는 점일 것이다.
- GETTER 와 SETTER 메서드는 PRIVATE 으로 정해진 변수에 외부에서 값을 할당하거나 외부에서 해당 값을 가져오기 위해 사용된다 => 기본형은 아래와 같다.
// 변수값 지정
public void set변수명(자료형 변수명){
this.필드변수명 = 매개변수명;
}
// 변수값 호출
public 자료형 get변수명(){
return 필드변수명; // 혹은 this.필드변수명 을 사용해도 무방하다
}
- 아래의 ATM 클래스가 있다. 해당 클래스에는 현재 잔액 balance 가 선언되어 있고, private 으로 접근제어자를 지정해주었다. 이후 외부에서 balance 에 값을 넣을때 어떻게 나오는지 확인해보겠다.
- 보면 아래 사진과 같이 'THE FIELD ATM.BALANCE IS NOT VISIBLE' 라는 에러를 내보내고 있다. 즉 balance 라는 필드 변수를 확인 할 수 없다는 의미이다.
- 이는 private 으로 감춰진 변수는 외부에서 일반적으로 대입하는 방식으로는 값을 넣을 수 없다는 의미라고 할 수 있다.
public class ATM {
private int balance;
String name;
void deposit(int a) {
balance +=a;
System.out.println(a+" 원 입금 완료");
}
}
### 외부 클래스 ###
public static void main(String args[]) {
ATM atm = new ATM();
atm.balance = 50; // private 으로 설정된 변수에 바로 값을 넣을 때
}
- 다음으로 balance 변수에 getter 와 setter 메서드를 만들고 해당 메서드를 통해 값을 삽입해보겠다.
- 먼저 setter 를 사용해서 매개변수에 값을 넣고, getter 를 이용해서 필드변수의 값을 출력해보았다.
- 이렇게 하니 이전과는 다르게 정상적으로 동작하는 것을 알 수 있다.
public class ATM {
private int balance;
String name;
void deposit(int a) {
balance +=a;
System.out.println(a+" 원 입금 완료");
}
// getter, setter 메서드
public int getBalance() {
// private 대상 변수를 외부에서 접근하기 위해서 사용
// balance 즉 필드변수를 리턴함
return balance;
}
public void setBalance(int balance) {
// private 대상 변수를 외부에서 접근하기 위해서 사용
// 해당 매개 변수로 들어오는 값이 this.balance 즉, 필드변수에 대입됨
this.balance = balance;
}
}
### 외부 클래스 ###
public static void main(String args[]) {
ATM atm = new ATM();
atm.setBalance(5000);
System.out.println("balance : "+atm.getBalance());
}
3. 상속 Extends
- 부모 클래스의 속성과 메서드를 보다 확장하여 자식 클래스에서 사용하기 위한 방법
- [자식 클래스명] extends [부모 클래스명]
- 상속받은 자식 클래스에서 부모 클래스의 생성자와 부모 잠조변수 호출
- 부모 클래스 생성자 호출 : super()
- 부모 클래스를 가리키는 참조변수 : super.
- 자식 클래스에서 사용되는 super() 는 따로 정의해놓지 않더라도 마치 기본생성자처럼 인스턴스 생성 시 항상 실행됨
- 다중 상속도 가능!! ⇒ person 을 상속받는 superman을 상속받는 hero 가 있다면 Hero는 person 과 superman 의 멤버 변수와 메서드 모두 사용 가능하다
// 자식 클래스 superMan에서 부모 클래스 Person 으로부터 메서드와 속성을 상속받아서 사용함
// 1. 부모클래스
public class Person{
public Person(){
name = "홍길동";
age = 20;
System.out.println("Person 클래스의 기본 생성자");
}
void eating(String str) {
System.out.println(str+"을/를 냠냠");
}
// 2. 자식 클래스
public class SuperMan extends Person {
SuperMan(){
super(); // 이 부분은 부모 클래스의 기본 생성자를 부르는 생성자로 생략 하더라도 무조건 항상 실행됨!!!!
System.out.println("SuperMan 클래스의 기본 생성자");
}
public void razer() {
System.out.println("레이저 발싸!");
}
public void fly() {
System.out.println("날아라!");
}
}
## 메인 메서드 ##
SuperMan sm = new SuperMan();
sm.eating("사과");
System.out.println(sm.age);
sm.fly();
sm.razer();
## 출력 결과 ##
Person 클래스의 기본 생성자
SuperMan 클래스의 기본 생성자
사과을/를 냠냠
20
날아라!
레이저 발싸!
2) 상속과 클래스 형변환
- 자바에서의 상속은 매우 중요하다. 특히나 상속은 자바에서의 클래스 형변환과도 관련이 있다.
- 상속에서 중요한 점 중 하나는 상속한 부모 클래스의 참조변수에 자식 클래스의 참조값을 담을 수 있다는 것이다. 반면 자식 클래스의 잠조 변수에는 부모 클래스의 참조값을 담을 수는 없다.
- 이는 큰 것에는 작은 것을 담을 수 있으나, 작은 것에는 큰 것을 담을 수 없다는 것을 의미한다.
- 이전의 type 별 형변환도 비슷한 맥락이다 => float 에 int 를 담을 수 있으나, int 에 float 를 담으려면 반드시 형변환이 필요하다.
int a = 10;
float b = a; // 묵시적 형변환 => 큰 것에 작은 것을 담는 것은 따로 형변환 필요없음
float c = 20;
int d = (int)c; // 명시적 형변환 => 작은 것에 큰 것을 담을때는 반드시 명시적으로 표시해야함
- 이를 확인하기 위해서 StarCraft 클래스와 Tank 클래스를 보자
- StarCraft 클래스
public class StarCraft {
String name;
String weapon;
int x, y; // 위치
int hp, at_point, de_point; // hp, 공격력, 방어력
int mv_spd, at_spd; // 이동속도, 공격속도
// 정보 출력을 위한 메소드
// 메소드에서는 StarCfaft 객체를 매개변수로 갖는다.
public void status(StarCraft unit) {
System.out.println(unit+" 의 현재 정보를 출력합니다");
System.out.println("이름 : "+unit.name);
System.out.println("hp : "+unit.hp);
System.out.println("공격력 : "+unit.at_point);
System.out.println("방어력 : "+unit.de_point);
System.out.println("이동 속도 : "+unit.mv_spd);
System.out.println("공격 속도 : "+unit.at_spd);
}
}
- Tank 클래스
public class Tank extends StarCraft{
boolean mod; // 시즈모드는 boolean 을 사용하여 true면 공성모드 false면 일반모드
Tank(){ // 기본생정자를 통해서 Tank 인스턴스 생성시 변수 값 초기화
System.out.println("시즈 탱크 등장!");
this.name = "시즈 탱크";
this.weapon = "아크라이트 포 && 아크라이트 중격포";
this.x = 50;
this.y = 50;
this.hp = 150;
this.at_point = 40;
this.de_point = 1;
this.mv_spd = 2;
this.at_spd = 50;
boolean mod = false;
}
}
- 이제 아래의 Game 클래스에서 상속에 따른 형변환을 확인해보자
- 부모 클래스인 StarCraft 의 참조변수인 star 안에는 tank 를 담을 수 있다.
- 반대로 자식 클래스의 tank 에는 부모 클래스의 참조변수인 StarCraft 를 담을 수 없다
- 이 때문에 자식 클래스안에 부모클래스를 담는 경우 명시적으로 형변환을 해줘야 한다.
Tank tank = new Tank();
StarCraft star = new StarCraft();
// 부모클래스의 잠조변수인 star 에는 자식클래스의 인스턴스를 담을 수 있다
star = tank;
// 아래처럼 묵시적 형변환이 된 것이다
star = (StarCraft)tank;
// 위의 내용을 최종적으로 아래처럼 사용 가능하다
// 이는 부모클래스의 참조변수에 자식클래스의 참조값을 담을 수 있기 때문에 가능하다
StarCraft star2 = new Tank();
// 반대로 자식클래스에 부모 클래스를 담는 것은 불가능!!!
// tank = star;
// 때문에 명시적으로 작은 타입을 형변환해줘야 작은 클래스의 참조변수에 큰 값을 담을 수 있다
tank = (Tank)star
- 부모 클래스에 자식 클래스를 담을 수 있기 때문에 아래 status 메소드가 사용 될 수 있다.
- 부모 클래스 StarCraft 를 매개변수로 갖는 status 메소드가 있다.
- 부모 클래스의 객체 안에 자식 클래스의 참조값을 담을 수 있다는 것을 활용하면 부모 클래스의 객체를 매개변수로 갖는 메소드의 매개변수안에 자식 클래스의 객체가 담겨도 괜찮다는 것이다!!
public class StarCraft {
String name;
String weapon;
int x, y; // 위치
int hp, at_point, de_point; // hp, 공격력, 방어력
int mv_spd, at_spd; // 이동속도, 공격속도
// 정보 출력을 위한 메소드
// 메소드에서는 StarCfaft 객체를 매개변수로 갖는다.
public void status(StarCraft unit) {
System.out.println(unit+" 의 현재 정보를 출력합니다");
System.out.println("이름 : "+unit.name);
System.out.println("hp : "+unit.hp);
System.out.println("공격력 : "+unit.at_point);
System.out.println("방어력 : "+unit.de_point);
System.out.println("이동 속도 : "+unit.mv_spd);
System.out.println("공격 속도 : "+unit.at_spd);
}
public static void main(String[] args) {
Tank tank = new Tank();
StarCraft star = new StarCraft();
// 부모 클래스인 StarCraft 에는 StarCraft 를 담을 수 있다.
// 즉, 매개변수가 StarCraft 인 경우 자식 클래스인 Tank 의 객체가 들어와도 괜찮다!!
tank.status(tank);
}
3) 오버라이딩 - 상속의 꽃
- 개인적으로 상속의 꽃은 오버라이드가 아닐까하고 생각한다. 뭔가 이름은 어딘가 게임의 스킬에서 나올법하다
- 오버라이드는 부모클래스의 메소드의 이름, 매개변수, 순서를 그대로 유지하되 메소드 내용 - method body - 만 다르게 재정의하는 것이다.
- 즉 오버라이드 된 메소드는 이름, 매개변수, 순서는 동일하지만 메서드의 기능은 완전히 다르게 변할 수 있다.
- 오버라이드 된 메소드에는 @Override 어노테이션이 붙는다.
- 다시 한번 StarCraft 로 예시를 보겠다
- 오버라이딩 예시를 위해 attack 메소드를 만들었다. 이때 StarCraft 는 실제로 객체가 생성되어서 StarCraft 로 뭔가를 할 일은 없으니 method body 는 빈 내용을 두었다.
public class StarCraft {
String name;
String weapon;
int x, y; // 위치
int hp, at_point, de_point; // hp, 공격력, 방어력
int mv_spd, at_spd; // 이동속도, 공격속도
// 정보 출력을 위한 메소드
// 메소드에서는 StarCfaft 객체를 매개변수로 갖는다.
public void status(StarCraft unit) {
System.out.println(unit+" 의 현재 정보를 출력합니다");
System.out.println("이름 : "+unit.name);
System.out.println("hp : "+unit.hp);
System.out.println("공격력 : "+unit.at_point);
System.out.println("방어력 : "+unit.de_point);
System.out.println("이동 속도 : "+unit.mv_spd);
System.out.println("공격 속도 : "+unit.at_spd);
}
// 공격을 위한 메서드 정의.
// StarCraft 클래스에는 사실 쓸 필요가 없음으로 내용은 -method body는- 빈 내용을 두었다.
public void attack(StarCraft unit) {};
}
- Tank 클래스
- 아래의 attack 메소드를 보자. 분명 상위 클래스의 메소드와 메소드명, 매개변수 갯수, 타입 모두 일치한다.
- 다만 메소드를 재정의 - overriding - 하였고, 이를 통해 해당 메소드의 기능이 완전히 달라지게 되었다.
public class Tank extends StarCraft{
boolean mod; // 시즈모드는 boolean 을 사용하여 true면 공성모드 false면 일반모드
Tank(){ // 기본생정자를 통해서 Tank 인스턴스 생성시 변수 값 초기화
System.out.println("시즈 탱크 등장!");
this.name = "시즈 탱크";
this.weapon = "아크라이트 포 && 아크라이트 중격포";
this.x = 50;
this.y = 50;
this.hp = 150;
this.at_point = 40;
this.de_point = 1;
this.mv_spd = 2;
this.at_spd = 50;
boolean mod = false;
}
@Override // 오버라이드 메소드는 @Override 어노테이션을 붙인다
// 메소드 내용이 달라졌다...!! => 재정의
public void attack(StarCraft unit) {
System.out.println(unit+" 를 공격합니다!");
unit.hp -=at_point; // unit 의 hp에서 전차의 공격력만큼 감소
if(mod == true) {
System.out.println("시즈 모드로 공격!! 일부는 펑펑펑");
}else {
System.out.println("일반 모드로 공격!! 일부는 퉁퉁퉁");
}
}
4. 메소드 오버로딩과 오버라이딩 : Overloading , Overriding
사실 오버로딩은 옆집 친구인 오버라이딩과 이름도 비슷하고 헷갈린다.
이를 확실히 하기 위해 표로 정리해두었다.
overriding - 재정의 | overloading - 다중정의 - |
재정의 | 다중정의 |
method명 동일 | method명 동일 |
매개변수 순서, 자료형, 갯수 모두 일치 | 매개변수 순서, 자료형, 갯수 다르게 |
상속받은 경우에만 가능! | 상속과는 관련 없다 |
부모의 접근지정자보다 자식의 접근지정자가 더 허용적이여야만 오버라이딩 가능 |
- 참고자료
Chapter 5-4. 객체 지향 : 클래스 형변환(is, as)
'Java - 기본기' 카테고리의 다른 글
22. 추상 클래스, 인터페이스, static, final (0) | 2022.03.05 |
---|---|
클래스, 메서드, 생성자, 상속 정리 : 나만의 StarCraft (0) | 2022.03.01 |
18-2 메소드 : 메소드 반환 타입, 오버로딩, 매개변수, 호출방식 (0) | 2022.02.24 |
20. 컬렉션 프레임워크 : list, set, map 간단 하게 알아보기 (0) | 2022.02.07 |
19. 오버로딩, 생성자, 기본 생성자, 생성자 this(), 참조변수 this (0) | 2021.12.09 |
댓글