백엔드/JAVA

접근제어자 및 객체 지향 프로그래밍 (2)

두개의 문 2023. 5. 8. 22:07
접근 제어자

- 접근 제어자의 목적 : 멤버 또는 클래스에 사용 → 해당 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할
- 접근 범위가 넓은 쪽에서 좁은 쪽의 순
  : public > protected > (default) > private
 ① public : 접근 제한 없음
 ② (default) : 같은 패키지 내에서만 접근 가능 → 접근 제어자가 지정되어 있지 않은 경우에 해당
 ③ protected : 같은 패키지 내 또는 다른 패키지의 자손 클래스에서 접근 가능
 ④ private : 같은 클래스 내에서만 접근 가능
 
 


캡슐화 ( encapsulation )

- 외부로부터 데이터를 보호하기 위해 멤버 변수의 값에 직접적으로 접근하지 않은 것이 보안상 좋음
- 데이터가 유효한 값을 유지하도록, 외부로부터의 접근을 제한하는 것이 필요
→ 멤버변수를 private나 protected로 제한하고,
    메서드를 public으로 지정해 메서드를 통해 간접적으로 멤버 변수의 값을 읽고 변경하는 것이 바람직
- 속성( property ) 함수
  ① getter : get멤버변수이름
    → 멤버변수의 값을 읽는 메서드
  ② setter : set멤버변수이름
    → 멤버변수의 값을 변경하는 메서드
  · 오른쪽 클릭 → Source → Generate Getter / Setter → 외부에서 접근하게 할 변수만 체크 후, Finish
 

 
 

▷ 첫번째 예시

< 데이터 클래스 >

package human;
public class Human {
	// 1. 멤버(필드, 속성) : 키, 몸무게, 성별 
	double height;
	double weight;
	char gender; 
	
    	// 2. 속성함수 이용 -> 데이터에 직접적인 접근 제한하기
	// 데이터의 값을 변경하는 set메소드
	void setheight(double height) {
		this.height = height;	// 인스턴스 변수와 지역변수를 구분하기 위해 객체 자신을 의미하는 this 사용
	}
	// 데이터의 값을 읽어오는 get메소드
	double getheight() {
		return this.height;
	}
	
	void setweight(double weight) {
		this.weight = weight;
	}
	double getweight() {
		return this.weight;
	}
	
	void setgender(char gender) {
		this.gender = gender;
	}
	char getgender() {
		return this.gender;
	}
    
    	// 3. 기본생성자 생성 : 객체 초기화 시, default값으로 설정
    	Human() {
		this.height = 150;
		this.weight = 50;
		this.gender = '여';
	}

❶ 멤버 변수 선언
❷ 속성함수 ( set메소드, get메소드 )를 이용해 데이터에 직접적인 접근을 제한 → 외부로부터 데이터 보호
❸ 기본생성자 생성 → 객체가 생성될 때마다 생성자가 호출되어 객체의 초기화 담당
 


< 실행 클래스 >

package human;
public class HumanApp {
	public static void main(String[] args) {
		// 1. 실제 객체(Object) 탄생
		Human human = new Human();
		
		// 2. 현재 human 객체의 상태를 알아보자. (=정보출력)
		System.out.printf("human의 키 %.1f cm", human.getheight());
		System.out.printf("\nhuman의 몸무게 %.1f kg", human.getweight());
		System.out.printf("\nhuman의 성별 %c", human.getgender());
		System.out.println("\n입니다.");
		
		System.out.println("--------------------------------------");
		// 3. 외부 클래스에서 참조변수를 이용해 간접적으로 데이터 값 변경하기
        	// 키 설정
		human.setheight(180);
		// 몸무게 설정
		human.setweight(80);
		// 성별 설정
		human.setgender('남');
        
        	// 4. 변경한 데이터 값 출력하기
        	System.out.printf("human의 키 %.1f cm", human.getheight());
		System.out.printf("\nhuman의 몸무게 %.1f kg", human.getweight());
		System.out.printf("\nhuman의 성별 %c", human.getgender());
		System.out.println("\n입니다.");

❶ 객체 생성
❷ 객체의 현재 데이터 출력
❸ set메소드를 이용해 간접적으로 데이터의 값을 변경
❹ get메소드를 이용해 데이터의 값을 불러 출력하기

< 개선할 점 >
실행 클래스에서 데이터의 값을 변경 후, 출력할 때마다 동일한 출력 메소드를 반복해야 함
→ 데이터 클래스에 출력 메서드를 따로 생성시킨 후, 메서드 호출을 통해 출력시키자.

 


< 데이터 클래스 >

    // 3. 출력 메소드 printInfo 생성
    void printInfo(){
		System.out.printf("human의 키 %.1f cm", this.getheight());
		System.out.printf("\nhuman의 몸무게 %.1f kg", this.getweight());
		System.out.printf("\nhuman의 성별 %c", this.getgender());
		System.out.println("\n입니다.");
	}

 


< 실행 클래스 >

	// printInfo() 메서드 호출로 새로운 데이터 값 출력
        human.printInfo();

 


< 출력 결과 >

human의 키 150.0 cm
human의 몸무게 50.0 kg
human의 성별 여
입니다.
--------------------------------------
human의 키 180.0 cm
human의 몸무게 80.0 kg
human의 성별 남
입니다.

∴ 메소드를 통해 데이터에 간접적으로 접근하므로 외부로부터 데이터를 보호할 수 있음
   따로 출력하는 메소드를 생성해서 메소드 호출로 객체 정보를 출력하니 코드가 훨씬 간결해짐을 알 수 있음
 
 

 

▷ 두번째 예시

< 데이터 클래스 >

package human;
public class Me {
	// 1. 멤버변수 선언
	double height;
	double weight;
	char gender;
	String name;
	String phone;
	
	// 2. 속성함수 이용 - setter, getter
	void setheight(double height) {
		this.height = height;
	}
	double getheight() {
		return this.height;
	}
	
	void setweight(double weight) {
		this.weight = weight;
	}
	double getweight() {
		return this.weight;
	}
	
	void setgender(char gender) {
		this.gender = gender;
	}
	char getgender() {
		return this.gender;
	}
	
	void setname(String name) {
		this.name = name;
	}
	String getname() {
		return this.name;
	}
	
	void setphone(String phone) {
		this.phone = phone;
	}
	String getphone() {
		return this.phone;
	}
    
	// 3. 사용자 정의 기본생성자 생성
	private Me (){
		this.height = 150;
		this.weight = 50;
		this.gender = '여'; 
		this.name = "";
		this.phone = "";
	}

❶ 멤버 변수 선언 : Human 클래스에 필수 데이터인 이름과 폰번호의 변수를 추가함
    ( 필수 데이터 : name, phone / 기본 데이터 : height, weight, gender )
❷ 속성함수 ( set메소드, get메소드 )를 이용해 데이터에 직접적인 접근을 제한 → 외부로부터 데이터 보호
❸ 기본생성자 생성 : 접근제어자 private로 제한 ( Me 클래스에서만 접근 가능 )
    ∵ 환상 데이터이므로 외부에서 호출 금지 
    명시적으로 객체를 생성한 후, 비어있음이란 뜻으로 해석 → 나중에 데이터 내용 설정 가능
 
 
< 실행 데이터 >

package human;
public class MeApp {

	public static void main(String[] args) {
		// 1. 객체 생성
		Me me = new Me();
		
	}
Error ) The constructor Me() is not visible.
∵ 데이터 클래스에서 기본생성자 Me( )가 private로 설정되었기 때문에 외부 클래스인 MeApp에서 호출할 수 없음

 
 
▶ 필수 데이터만 출력하기
< 데이터 클래스 >

	// 4. 필수 필드용 생성자
	Me(String name, String phone){
		this();
		this.name = name;
		this.phone = phone;
	}
   
   	// 5. 필수 데이터만 출력하는 메서드
    	void printInfo() {
			System.out.printf("\n나의 이름 %s ", this.getname());
			System.out.printf("\n나의 전화번호 %s ", this.getphone());
	}

❹ 필수 필드용 생성자 생성
❺ 필수 데이터만 출력하는 메서드 생성
 


< 실행 데이터 >

		// 2. 필수 필드용 생성자 호출   
		Me me = new Me("홍길동", "010-1234-5678"); 
		me.printInfo();

 

< 출력 결과 >

나의 이름 홍길동
나의 전화번호 010-1234-5678
4-1. 필수 필드용 생성자에서 첫번째 this(); 는 3. 사용자 정의 기본생성자 호출을 의미
→ 3. 사용자 정의 기본생성자의 접근제어자가 private로 지정되어 있기 때문에 기본 데이터가 출력되지 않음.

 
 
▶ 필수 데이터는 항상 출력되고, 기본 데이터는 선택적으로 출력해보기
    → 메소드를 따로 생성해 해당 메소드를 출력하면 되지만, 프로그래밍의 경우 최대한 코드의 중복을 제거하고자 함
    ∴  메소드 오버로딩 이용해 중복없이 코드를 더 간결하게 작성


- 메소드 오버로딩 ( overloading )
 : 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것
 · 조건
 ① 메서드 이름이 같아야 함
 ② 매개변수의 개수 또는 타입이 달라야 함
 ③ 반환 타입은 관계없음

· 오버로딩된 메소드를 호출할 경우, JVM이 매개값의 타입을 보고 호출할 메서드 선택

  JVM은 반환타입을 보고 메소드를 선택하지 않기 때문에 반환타입은 고려 사항이 아님
 


< 데이터 클래스 >

	// 6. 조건문이 존재하는 printInfo 메서드 생성 
	private void printInfo(boolean isPrint) {
		if (isPrint) {
			System.out.printf("나의 키 %.1f cm", this.getheight());
			System.out.printf("\n나의 몸무게 %.1f kg", this.getweight());
			System.out.printf("\n나의 성별 %c", this.getgender());
		}
	}
	
	// 7. 필수 데이터 출력 및 기본 데이터 선택적 출력 
	void printInfo() {
		
		// 필수 항목 출력
		System.out.printf("\n나의 이름 %s ", this.getname());
		System.out.printf("\n나의 전화번호 %s ", this.getphone());
		
		// 선택 항목 출력
		printInfo(true);
		System.out.println("\n입니다.");
	}
	
}

❻ 기본 데이터를 출력하는 메서드 : 접근 제어자 private으로 지정 → 외부에서 접근 제한됨
   논리형 매개변수를 통해 true일 때만 출력되게 설정 
❼ 필수 데이터는 항상 출력하고, 기본데이터는 true일 때만 출력되는 메서드 생성
 


< 실행 클래스 >

		// 2. 필수 필드용 생성자 호출   
		Me me = new Me("홍길동", "010-1234-5678"); 
		me.printInfo();

 
< 출력 결과 >

isPrint == true 일때
  나의 이름 홍길동
  나의 전화번호 010-1234-5678
  나의 키 150.0 cm
  나의 몸무게 50.0 kg
  나의 성별 여
  입니다.
isPrint == false 일때
  나의 이름 홍길동
  나의 전화번호 010-1234-5678
  입니다.

 
 
▶ 모든 필드를 포함하는 생성자 만든 후, 데이터 변경하고 출력하기
< 데이터 클래스 >

	// 8. 모든 필드용 생성자
	Me(String name, String phone, double height, double weight, char gender){
		this.name = name;
		this.phone = phone;
		this.height = height;
		this.weight = weight;
		this.gender = gender;
	}

 
< 실행 데이터 >

		// 3. 모든 필드용 생성자 호출
       		me = new Me("백두산", "010-9876-5432", 190, 70, '남');
		me.printInfo();

 
< 출력 결과 >

isPrint == true 일때
  나의 이름 백두산
  나의 전화번호 010-9876-5432
  나의 키 190.0 cm
  나의 몸무게 70.0 kg
  나의 성별 남
  입니다.
isPrint == false 일때
  나의 이름 백두산
  나의 전화번호 010-9876-5432
  입니다.

 
 
 

▷ 세번째 예시
 

< 데이터 클래스 >

package book;

public class Book {
	// 1. 필드 선언 : 접근제어자 private 이용
	private String title;
	private String author;
	private int price;

    	// 2. 사용자 정의 생성자 
	public Book(String title, String author, int price) {
		this.title = title;
		this.author = author;
		this.price = price;
	}
    
    	// 4-1. 명시적으로 객체를 생성하고, 비어있음이란 뜻으로 해석하려는 목적
	// -> 나중에 데이터 내용을 설정 가능
	public Book() {
		this.title = "";	// 타이틀 없음
		this.author = "";	// 저자 없음
		this.price = 0;		// 가격 없음
	}

	public String getTitle() {
		return title;
	}

	// 책 제목 한번 정해지면 변경 불가하니까 주석 처리 -> book. 했을 때 안뜸
	public void setTitle(String title) {
		this.title = title;
	}

	public String getAuthor() {
		return author;
	}

	// 책 저자는 변경 불가하니까 주석 처리
	public void setAuthor(String author) {
		this.author = author;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

1. 필드 선언 : 접근제어자로 private 지정 → 같은 클래스 내에서만 접근 가능

2. 사용자 정의 생성자 생성 → 외부에서 생성자를 통해 데이터 값 변경 가

 

 

< 실행 클래스 >

package book;

public class BookApp {

	public static void main(String[] args) {
		// book1, book2 = 로컬변수
		// 2. 객체 생성
		Book book1 = new Book("이것이 자바다","신용권",35000);
		// 2-1. 외부에서 멤버변수에 직접 접근해서 초기화
//		book1.title = "이것이 자바다";
//		book1.author = "신용권";
//	    book1.price = 35000;	    
		// -> 4. 접근제어자 : private 사용했으므로 직접 접근 안됨