만약 당신의 지갑이 누구에게나 공개되어있다면..? 생각만 해도 끔찍하다.
프로그램에서 또한 마찬가지다. 어떤 건 공개해도 되지만, 어떠한 것은 감추어야만 한다. 이를 위한 개념을 배워보자.
접근 제한자란
public과 private
게터와 세터
Access modifier
access modifier >> 접근 제한자란, 접근 권한을 제한하는 키워드다.
why use? >> 여러분의 지갑이 모두에게 공개돼있다면? 또는 여러분의 개인 정보가 외부에 모두 알려졌다면 어떨까? 안될 것이다. 이를 해결하기 위한 키워드. 바로 접근 제한자다.
type >> 접근 제한자의 종류는 총 4가지로, 외부 접근을 거부하는 private(비공개)에서부터 모든 접근을 허용하는 public(완전 공개)까지 존재한다.
Public and private
public and private >> public 키워드는 완전한 공개를 의미한다. 모두가 사용할 수 있는 공공의 산물이 되는 것이다. 이와 반면 private은 비공개를 의미한다. 오직 해당 클래스 내부에서만 사용할 수 있게 된다.
apply and benefit >> 접근 제한자는 필드와 메소드에 적용될 수 있다. public 선언되면 외부의 접근이 가능하나, private의 경우 외부 접근이 불가능하다.
이를 통해 외부의 악의적 접근을 예방할 수 있다.
Getter and setter
why need? >> private 선언된 필드는 외부 접근이 불가능하다. 심지어 정상적인 사용자도 직접 접근할 수 없다. 이를 해결하기 위해 게터와 세터가 필요하다.
rule >> 게터와 세터를 사용하면, private 필드를 우회하여 가져오거나 변경할 수 있다.
객체의 정보를 외부로부터 보호하지 않는 경우, 데이터가 함부로 변경될 수 있습니다.
이러한 문제를 예방하기 위해, 적절한 접근 제한자가 필요합니다. 접근 제한자의 종류는 크게 4가지이며, 필드와 메소드에 적용할 수 있습니다.class AAA { /* 필드 */ private String aaa; // 비공개 int bbb; // 디폴트 공개 protected double ccc; // 상속 공개 public boolean ddd; // 완전 공개 /* 메소드 */ private void eee() {} // 비공개 int fff() {} // 디폴트 공개 protected double ggg() {} // 상속 공개 public boolean hhh() {} // 비공개 }
CODE
public class Main { public static void main (String[] args) { // 1. 계좌 생성 Account myAccount = new Account(1000000); System.out.println(myAccount.toString()); // 2. 계좌 해킹 Hacker.malcious(myAccount); // 3. 결과 출력 System.out.println(myAccount.toString()); } } class Account { // 필드 int balance; // 생성자 public Account(int balance) { this.balance = balance; } // 메소드 public String toString() { return String.format("Account { balance: %d }", balance); } } class Hacker { // 클래스 메소드 - 계좌의 잔액을 0원으로 변경 public static void malcious(Account account) { /* 1. 파라미터 계좌의 잔액을 0으로 변경하세요. */ account.balance = 0; } }
private >> private 키워드는 외부 접근을 차단합니다
example code >> 위 그림을 코드로 나타내면 아래와 같습니다. 외부 클래스에서 해당 내용을 사용할 수 없습니다.class Account { public int number; // 완전 공개 필드 private String password; // 비공개 필드 private int balance; // 비공개 필드 // 비공개 메소드 - 외부 호출 불가 private void somthing() {} }
CODE
public class Theif { // 메인 메소드 public static void main(String[] args) { // 지갑 객체 생성 Wallet wallet = new Wallet(30000); System.out.print(wallet.toString()); // 지갑에서 돈을 모두 꺼냄 Theif.steal(wallet); // 지갑 확인 System.out.println(" -> " + wallet.toString()); } // 클래스 메소드 public static void steal(Wallet target) { target.money = 0; } } class Wallet { // 필드(인스턴스 변수) private int money; /* 1. 주어진 필드를 외부로부터 감추세요. */ // 생성자 public Wallet(int i) { money = i; } // 메소드 public String toString() { return String.format("Wallet { money: %d }", money); } }
private field >> private 필드는 외부에서 직접 값을 가져올(read) 수 없습니다. 이를 외부에서 읽기 위해서는 게터 메소드가 필요합니다.
getter >> 게터(getter)는, private 필드를 우회적으로 접근(read)하게 합니다.class SmartPhone { // private 필드 - 외부 접근 불가 private int number; // 게터 메소드 - number를 우회하여 반환 public int getNumber() { return number; } }
getter character >> 게터 메소드는 아래의 특징을 갖습니다.
private 필드를 반환한다.
public 이다.
메소드명은 get + 필드명으로 한다
CODEpublic class PersonTest { public static void main(String[] args) { // 객체 생성 Person park = new Person("홍팍", "010-1234-5678"); /* 1. 게터를 호출하여 필드값을 얻으시오. */ System.out.printf("이 름: %s\n", park.getName()); System.out.printf("연락처: %s\n", park.getPhonenumber()); } } class Person { // 필드 private String name; private String phoneNumber; // 생성자 public Person(String n, String p) { name = n; phoneNumber = p; } // 게터 public String getName() { return name; } /* 2. phoneNumber 필드의 게터를 추가하시오. */ public String getPhonenumber() { return phoneNumber; } }
need setter >> private 필드는 외부에서 직접 값을 변경(write)할 수 없습니다. 이를 해결하기 위해서 세터 메소드가 필요합니다.
example >> 세터(setter)는, private 필드를 우회적으로 변경(write)합니다.class SmartPhone { // private 필드 private int number; // 세터 메소드 public void setNumber(int n) { number = n; } }
setter character >> 세터 메소드는 아래의 특징을 갖습니다.
private 필드를 변경한다.
public 이다.
메소드명은 반환하려는 private 필드명 앞쪽에 set을 붙인다.
CODEpublic class LockTest { public static void main(String[] args) { // 객체 생성 Lock lock = new Lock("123!@#"); /* 1. 세터를 호출하여, 비밀번호를 변경하시오. */ lock.setPassword("654#@!"); // 객체 정보 출력 System.out.println(lock.toString()); } } class Lock { // 필드 private String password; // 생성자 public Lock(String p) { password = p; } // 메소드 public String toString() { return String.format("Lock { password: %s }", password); } /* 2. 비밀번호 변경을 위한, 세터 메소드를 추가하시오. */ public void setPassword(String pw){ password = pw; } }
scope >> 스코프(scope)란, 변수의 활동 영역이었습니다.
method scope vs class scope >> 이러한 스코프는 크게 두 종류로, 메소드 스코프와 클래스 스코프로 나뉘었습니다.
메소드 스코프: 메소드 내부에서 활동 (예 - 파라미터, 지역변수)
클래스 스코프: 클래스 전역에서 활동 (예 - 필드)
notice of scope! >> 스코프 관련하여 주의할 점은, 변수의 이름이 같은 경우입니다.class Whatever { public void cool(int score) { String result = ""; ... } public void great(int score) { String result = ""; ... } }
위 코드를 보면 cool() 메소드와 great() 메소드 내부에 둘 다 score 및 result 변수를 가지고 있습니다. 이들은 같은 이름이지만 서로 스코프가 다르기에 각각 개별적인 변수입니다. 서울 철수와 부산 철수가 다른 것처럼 말이죠.
CODEpublic class MethodScope { public static void main(String[] args) { // main 메소드의 지역변수 score int score = 88; System.out.printf("score = %d in main()\n", score); // Record 클래스의 메소드 수행 Record.foo(score); // main 메소드의 지역변수 score 출력 System.out.printf("score = %d in main()\n", score); } } class Record { // 클래스 메소드 public static void foo(int score) { // printMinus10 메소드의 파라미터 score의 값을 10 감소 score -= 10; // printMinus10 메소드의 파라미터 score의 값을 출력 System.out.printf("score = %d in foo()\n", score); } }
problem from same variable name >> 클래스의 필드와 메소드의 파라미터 이름이 같은 경우, 문제가 생길 수 있습니다.
아래 코드의 생성자는 잘 동작할까요? 잘 안됩니다. 필드의 값은 변경되지 않고, 파라미터 자신의 값을 그대로 대입할 뿐입니다. 왜일까요? 스코프가 중복되었기 때문입니다.// 호출 영역 Cookie c = new Cookie("버터링", 1500); // 정의 영역 class Cookie { private String name; private int price; public Cookie(String name, int price) { name = name; // 인스턴스 변수 name 초기화(X) price = price; // 인스턴스 변수 price 초기화(X) } }
this >> 파라미터와 필드의 이름이 같은 경우, this 키워드 통해 이를 해결할 수 있습니다. this 키워드는 메소드 수행의 주체 객체를 가리키기 때문입니다. 이를 통해 같은 이름의 파라미터 name과 구분할 수 있게 되었습니다.
// 호출 영역 Cookie c = new Cookie("버터링", 1500); c.setPrice(2000); // 정의 영역 class Cookie { private String name; private int price; // 생성자 public Cookie(String name, int price) { this.name = name; // this.name -> 인스턴스 변수 this.price = price; // this.price -> 인스턴스 변수 } // 세터 public void setName(String name) { this.name = name; // this.name -> 인스턴스 변수 } }
CODE
public class CoffeeTest { public static void main(String[] args) { // 커피 객체 생성 Coffee americano = new Coffee("아메리카노", 1500); System.out.printf("커피값 인상 전 => %s\n", americano.toString()); // 커피 값 인상: 1500 -> 1800 americano.setPrice(1800); System.out.printf("커피값 인상 후 => %s\n", americano.toString()); } } class Coffee { // 필드(인스턴스 변수) private String name; private int price; // 생성자 public Coffee(String name, int price) { /* 1. this 키워드를 사용하여 필드를 초기화하세요.*/ this.name = name; this.price = price; } // 세터 public void setPrice(int price) { /* 2. this 키워드를 사용하여 필드를 변경하세요.*/ this.price = price; } // 메소드 public String toString() { return String.format("Coffee { name: %s, price: %d }", name, price); } }
CODE 1public class KnightTest { public static void main(String[] args) { // 객체 생성 Knight k = new Knight("돈키호테", 30); // 정보 출력 System.out.println("[객체 생성]"); System.out.printf(" %s\n",k.toStr()); // 체력 증가: 기존 체력 + 30 k.setHp(k.getHp() + 30); // 결과 출력 System.out.println("[체력 증가 +30]"); System.out.printf(" %s\n",k.toStr()); } } class Knight { // 필드 private String name; private int hp; // 생성자 Knight(String name, int hp){ this.name = name; this.hp = hp; } // 게터 public int getHp(){ return hp; } public String getName(){ return name; } // 세터 public void setHp(int hp){ this.hp = hp; } public String toStr(){ return String.format("Knight { name: %s, hp: %d }", this.name, this.hp); } }
CODE 2
public class KnightTest { public static void main(String[] args) { // 객체 생성 Knight k = new Knight("돈키호테", 30); // 정보 출력 System.out.println("[객체 생성]"); System.out.printf(" Knight { name: %s, hp: %d }\n",k.getName(), k.getHp()); // 체력 증가: 기존 체력 + 30 k.setHp(30); // 결과 출력 System.out.println("[체력 증가 +30]"); System.out.printf(" Knight { name: %s, hp: %d }",k.getName(), k.getHp()); } } class Knight { // 필드 private String name; private int hp; // 생성자 Knight(String name, int hp){ this.name = name; this.hp = hp; } // 게터 public int getHp(){ return hp; } public String getName(){ return name; } // 세터 public void setHp(int hp){ this.hp += hp; } }