📗 self-study/📗 KH정보교육원 당산지원

[Java] 06_객체(캡슐화, 클래스 실습, 필드, 접근 제한자)

천재강쥐 2022. 8. 4. 09:38

06_객체(캡슐화)

*setter 메소드

 

Student 클래스

package com.kh.chap02.encapsulation.model.vo;

// 캡슐화 과정을 거친 Student 클래스
public class Student {
	
	// 필드부
	/*
	 * 필드: 클래스 안에 바로 선언해 두는 변수
	 * 		(== 멤버변수 == 인스턴스 변수)
	 * 
	 * [ 표현법 ]
	 * 접근제한자 자료형 필드명;
	 */
	
	private String name;
	private int age;
	private double height;
	
	// 생성자부
	
	// 메소드부
	/*
	 * 각 기능(기능 단위 == 메소드)을 구현하는 부분
	 * 
	 * [ 표현법 ]
	 * 접근제한자 반환형 메소드명(매개변수 => 생략 가능하나 적을 때는 짝 맞춰서 값 잘 넣어 줘야 함) {
	 * 
	 * 		기능 구현 코드;
	 * 
	 * 		반환형이 있다면 return 돌려줄값;
	 * }
	 * 
	 * 반환형: 출력되는(가지고 오는) 자료형의 결과값
	 * (반환형과 돌려줄값은 일치해야 함)
	 * 매개변수: 입력되는(들어오는) 값
	 */
	
	// setter 메소드: 데이터를 기록 및 수정하는 용도의 메소드
	// getter 메소드: 데이터를 반환해 주는 기능의 메소드
	// => setter / getter 메소드는 항상 접근 가능해야 하기 때문에 public으로 써야 함
	// => 필드 한 개당 setter와 getter 메소드를 1개씩 !꼭! 만들어 줘야 함
	
	// setter 메소드: 주로 대입이 일어남
	// 각각 String name, int age, double height에 대하여 만들기
	// 이름값을 기록 및 수정할 수 있는 메소드(name 필드에 대입하는 용도)
	
	public void setName(String name) {
	// setter는 기록/수정용이기 때문에 반환할 반환형이 없음! 항상 void를 씀
	
		// name = "홍길동";
		// 이렇게 쓰면 setName 메소드에 무조건 홍길동만 입력되기 때문에 이러면 안 됨!
		// 매개변수에 입력받을 값을 넣은 뒤
		
		this.name = name;
		
		// this에는 현재 나의 주소값이 담김
	}
	
	/*
	 * [ 표현법 ]
	 * public void set필드명(해당필드의자료형 해당필드명과동일한매개변수명) {
	 * 
	 * 		this.필드명 = 매개변수명;
	 * }
	 * 
	 * => 필드명 앞에는 항상 this. (나의 주소값)을 붙여 줘야 함
	 * => 메소드 영역 안에서는 이름이 같을 때 매개변수 (일종의 지역변수 개념) 의 우선순위가 높게 지정되기 때문에
	 * 	    구분 용도로 필드명 앞에는 this.을 붙여야 함 반드시!
	 */
	
	
	// 나이값을 기록 및 수정할 수 있는 메소드(age 필드에 대입하는 용도)
	public void setAge(int age) {
		
		this.age = age;
		
	}
	
	// 키값을 기록 및 수정할 수 있는 메소드(height 필드에 대입하는 용도)
	public void setHeight(double height) {
		
		this.height = height;
		
	}
	
	

}

메소드 흐름... 이라는데 아직 아리까리
setter 메소드에 입력한 값의 흐름
setter 메소드 규칙 - 관례상 매개변수를 필드명과 똑같이 지정하고, 메소드 안의 변수 이름은 this.으로 주소값을 추가하여 구분해 줌

Run 클래스

package com.kh.chap02.encapsulation.run;

import com.kh.chap02.encapsulation.model.vo.Student;

public class Run {
	
	/*
	 * 7. 캡슐화 작업을 통해서 완벽한 클래스의 형태를 갖추게 하자!
	 * 
	 * 캡슐화를 하지 않으면: 외부로부터 직접 접근이 가능하기 때문에 함부로 값이 조회/변경될 수 있음
	 * 					=> 이 이슈를 막기 위해 캡슐화 작업을 해야 함!
	 * 
	 * *객체지향 설계 원칙 중 하나가 "정보은닉" => 캡슐화 작업을 통해 진행
	 * *데이터의 직접 접근을 막을 것! 단, 간접적으로 접근할 수 있게끔은 해 줘야 함
	 *  => 메소드를 활용하여 간접적으로 접근할 수 있게 하자!
	 * 
	 * 	캡슐화 작업
	 *	1) 정보 은닉 단계: 필드들의 접근제한자를 private으로 변경하기
	 *				      외부로부터 필드들의 직접 접근을 막기 위해 항상 필드들의 접근제한자는 public이 아닌 private를 쓴다!
	 *	2) setter / getter 메소드 만들기: 간접적으로나마 접근해서 값을 대입하거나 (setter: 세팅하는 애)
	 *									그 값을 가지고 올 수 있는 메소드를 (getter: 가지고 오는 애) 만들어야 한다!
	 */

	public static void main(String[] args) {
		
		Student hong = new Student(); // 객체를 생성한다 (== 인스턴스화 한다)
		
		/*
		 *직접 대입
		hong.name = "홍길동"; // is not visible 오류 발생
		hong.age = 20;
		hong.height = 168.7;
		 
		 *직접 조회
		System.out.println(hong.name);
		System.out.println(hong.age);
		System.out.println(hong.height);
		*/
		
		// 위와 같이 대입을 직접적으로 했을 경우 또는 조회를 직접적으로 했을 경우
		// 우리가 private으로 접근 제한을 막아 버렸기 때문에
		// .(직접접근연산자)를 통해 필드에 접근이 불가능해짐
		// => 간접적으로나마 메소드를 통해 접근이 가능하도록 해야 함

		hong.setName("홍길동");
		hong.setAge(20);
		hong.setHeight(168.7);
		
		

	}

 

*getter 메소드

 

Student 클래스

	// getter 메소드: 필드에 들은 값을 외부로 반출시키는 용도 (가지고 나가겠다)
	//				 주로 필드값을 반환시키는 구문히 작성됨
	// String name, int age, double height에 대해서 작성
	
	// name 필드에 들어 있는 값을 돌려주는 용도의 메소드
	public String getName() {
		
		return name; // name 값을 들고 돌아가겠다( == 결과값을 돌려주겠다) 
		
	}
	
	/*
	 * [ 표현법 ]
	 * public 해당필드의자료형 get필드명() {
	 * 
	 * 		return 필드명;
	 * }
	 * 
	 * => getter 메소드에서는 필드명 앞에 this. 을 붙이지 않음
	 * 	   굳이 이름을 구분할 매개변수가 없기 때문
	 */
	
	// age 필드에 들은 값을 돌려주는 용도의 메소드
	public int getAge() {
		
		return age;
		
	}
	
	// height 필드에 들은 값을 돌려주는 용도의 메소드
	public double getHeight() {
		
		return height;
	}
	
	// => setter와 getter 메소드까지 만들어 주는 과정까지가 캡슐화이다!

getter 메소드에 담긴 값의 흐름

Run 클래스

	public static void main(String[] args) {
		
		Student hong = new Student(); // 객체를 생성한다 (== 인스턴스화 한다)
		
		/*
		 *직접 대입
		hong.name = "홍길동"; // is not visible 오류 발생
		hong.age = 20;
		hong.height = 168.7;
		 
		 *직접 조회
		System.out.println(hong.name);
		System.out.println(hong.age);
		System.out.println(hong.height);
		*/
		
		// 위와 같이 대입을 직접적으로 했을 경우 또는 조회를 직접적으로 했을 경우
		// 우리가 private으로 접근 제한을 막아 버렸기 때문에
		// .(직접접근연산자)를 통해 필드에 접근이 불가능해짐
		// => 간접적으로나마 메소드를 통해 접근이 가능하도록 해야 함

		hong.setName("홍길동");
		hong.setAge(20);
		hong.setHeight(168.7);
		
		// 간접적으로 조회할 수 있게 도와주는 getter 메소드를 호출
		// String name = hong.getName();
		
		System.out.println(hong.getName());
		System.out.println(hong.getAge());
		System.out.println(hong.getHeight());
		
		// xxx님의 나이는 xx살이고, 키는 xxxcm입니다.
		System.out.println(hong.getName() +" 님의 나이는 " + hong.getAge() + "살이고, 키는 " + hong.getHeight() + "cm입니다.");
	

	}

*setter와 getter처럼 필수는 아니지만 편의상 이용하기 좋은 (원하는 구조의) 출력문 메소드 만들기

 

Student 클래스

// 모든 필드값들을 하나의 문자열로 연이어서 돌려 주는 용도의 메소드
	// => 캡슐화 과정은 아니기 때문에 필수는 아님! 있으면 편리하니까 꼭 만들자
	
	// 접근제한자 반환형 메소드명(매개변수) {
	//		return 리턴값;
	// }
	
	public String information() {
		
		// return name, age, height; // 한 번에 한 개의 값만 리턴 가능함
		return name + " 님의 나이는" + age + "살이고, 키는 " + height + "cm입니다.";
		// 그래서 하나의 문자열로 만든 후에 리턴!
		
	}

Run 클래스

public static void main(String[] args) {
		
		Student hong = new Student(); // 객체를 생성한다 (== 인스턴스화 한다)

		hong.setName("홍길동");
		hong.setAge(20);
		hong.setHeight(168.7);
		
		System.out.println(hong.getName());
		System.out.println(hong.getAge());
		System.out.println(hong.getHeight());
		
		// xxx님의 나이는 xx살이고, 키는 xxxcm입니다.
		// System.out.println(hong.getName() +" 님의 나이는 " + hong.getAge() + "살이고, 키는 " + hong.getHeight() + "cm입니다.");
		
        System.out.println(hong.information());
		
		Student kim = new Student();
		
		kim.setName("김영희");
		kim.setAge(21);
		kim.setHeight(180.4);
		
		//System.out.println(kim.getName() + " 님의 나이는 " + kim.getAge() + "살이고, 키는 " + kim.getHeight() + "cm입니다.");
		
        System.out.println(kim.information());
	

	}

 

클래스 다이어그램을 보고 클래스를 작성할 줄 알아야 함

Person 클래스

package com.kh.chap03.class_.model.vo;

public class Person {
	
	// 필드부
	// 필드 == 멤버변수 == 인스턴스변수
	
	// 1. 필드 만들기
	private String id;
	private String pwd;
	private String name;
	private int age;
	private char gender;
	private String phone;
	private String email;
	
	// 생성자부
	
	// 메소드부
	// 2. setter, getter 메소드 만들기
	// 각 필드에 대입시키고자 하는 값을 전달받아서 해당 필드에 대입시켜 주는 setter 메소드 7개ㅑ
	// setter 메소드명: setXXX (낙타표기법)
	
	public void setId(String id) {
		
		this.id = id;
		
	}
	
	public void setPwd(String pwd) {
		
		this.pwd = pwd;
		
	}
	
	public void setName(String name) {
		
		this.name = name;
	}
	
	public void setAge(int age) {
		
		this.age = age;
		
	}
	
	public void setGender(char gender) {
		
		this.gender = gender;
		
	}
	
	public void setPhone(String phone) {
		
		this.phone = phone;
		
	}
	
	public void setEmail(String email) {
		
		this.email = email;
		
	}
	
	
	// 각 필드값을 돌려 주는 getter 메소드 7개
	// getter 메소드명: getXXX (낙타표기법)
	
	public String getId() {
		
		return id;
		
	}
	
	public String getPwd() {
		
		return pwd;
		
	}
	
	public String getName() {
		
		return name;
		
	}
	
	public int getAge() {
		
		return age;
		
	}
	
	public char getGender() {
		
		return gender;
		
	}
	
	public String getPhone() {
		
		return phone;
		
	}
	
	public String getemail() {
		
		return email;
		
	}
	
	// => 캡슐화 작업 끝!
	
	// 3. information 메소드 만들기
	// 모든 필드값을 하나의 문자열로 연이어서 반환해 주는 information 메소드
	
	public String information() {
		
		return "id: " + id + ", pwd: " + pwd + ", name: " + name + ", age: " + age
				+ ", gender: " + gender + ", phone: " + phone + ", email: " + email;
	}

	

}

Run 클래스

package com.kh.chap03.class_.run;

import com.kh.chap03.class_.model.vo.Person;

public class Run {

	public static void main(String[] args) {
		
		// Person이라는 클래스
		// == 사람의 정보를 담고자 내가 만든 나의 자료형
		// 	  (여러 개의 자료형, 여러 개의 값들을 보관 가능)
		// == 사용자 정의 자료형 (커스터마이징)
		
		Person p = new Person(); // 객체 생성 == 인스턴스화
		
		System.out.println(p); // 주소값
		System.out.println(p.information()); // 초기값, JVM에 의해 채워진 기본값들이 출력

		// => 값 대입을 안 해서 기본값이 출력된 것이므로 메소드가 잘 만들어진 것임을 알 수 있음!
		
		// 값 넣기 => setter 메소드
		p.setId("user01");
		p.setPwd("pass01");
		p.setName("홍길동");
		
		System.out.println(p.information()); // id, pwd, name만 입력되고 나머지는 기본값 출력됨
		
		p.setAge(25);
		p.setGender('남');
		p.setPhone("010-1111-2222");
		p.setEmail("hong@naver.com");
		
		System.out.println(p.information());

 


*변수 구분
- 전역변수(== 필드): 클래스 영역에 바로 선언하는 변수 => 클래스 내에서면 어디서든 사용 가능
- 지역변수: 클래스 영역 내의 어떤 특정한 구역 ({})에 선언한 변수 => 메소드 역역 안, if문 블럭 안, for문 블럭 안, for문 초기식,  매개변수, ...
 
1. 전역변수
- 멤버변수(== 필드 == 인스턴스변수): 일반 필드의 개념
  생성 시점: new 연산자를 통해 해당 객체를 생성하는 순간 메모리의 heap 영역에 할당됨
  소멸 시점: 연결이 끊긴 후 가비지 컬렉션이 일어나는 순간 소멸(객체가 소멸되는 순간)
 
- 클래스변수(== static변수): static이라는 예약어가 붙은 변수
   생성 시점: 프로그램 시작과 동시에 메모리의 static 영역에 생성됨 (해당 객체가 생성이 되지 않더라도 바로 가져다 쓸 수 있는 개념)
   소멸 시점: 프로그램 종료 시 소멸
 
2. 지역변수
    생성 시점: 특정한 구역 ({}) 실행 중 메모리 stack 영역에 할당
    소멸 시점: 특정한 구역({}) 종료 시 값이 소멸
 

필드 테스트

FieldTest1 클래스

package com.kh.chap04.field.model.vo;

// 변수 선언 위치에 따른 구분 (전역변수, 지역변수, 매개변수)
public class FieldTest1 {	

	// 멤버변수( == 필드 == 인스턴스 변수)
	public int global;
	
					// 매개변수: 일종의 지역변수 개념
	public void test(int num) {
		
		// 지역변수: 반드시 초기화
		int local = 0; // local이 생성 및 초기화
		
		// 멤버변수 출력
		System.out.println(global); // public int global; 시점에 JVM에서 알아서 초기화시킴
		
		// 지역변수 출력
		System.out.println(local); // 지역변수는 int local = 0;과 같이 항상 생성 및 초기화 해 주어야 함
		
		// 매개변수 출력
		System.out.println(num); // Run 클래스에서 f1.test(10);을 실행할 때 알아서 그 시점에서 대입된 후 실행됨
		
	}

}

FieldRun 클래스

package com.kh.chap04.field.run;

import com.kh.chap04.field.model.vo.FieldTest1; // (2) import 하기

public class FieldRun {

	public static void main(String[] args) {
		
		// 1. FieldTest1 클래스 테스트
		
		FieldTest1 f1 = new FieldTest1(); // (1) 객체 만들기
		// 이 시점에서 객체가 생성 시 f1의 global이 생성, 초기화(대입)된 것
		
		f1.test(10); // test 메소드 호출 시 num이라는 매개변수에 10이 대입된 것
		
		//System.out.println(num); // 메소드가 종료되었기 때문에 소멸
		//System.out.println(local); // 메소드가 종료되었기 때문에 소멸
		System.out.println(f1.global); // 아직 소멸되지 않음
		
		// 연결고리를 끊겠다 == 객체를 소멸했다
		
		f1 = null;
		
		System.out.println(f1.global); // NullPointerException 발생
		
		// 이 시점에서 가비지 컬렉션이 일어나면 global 값도 소멸될 예정임!
 
	}

}

변수 생성 시점

 

접근제한자

* (+)public: 어디서든(같은 패키지/클래스, 다른 패키지/클래스 모두) 접근 가능
* (#)protected: 같은 패키지 내부에서는 무조건 접근 가능,
 다른 패키지에 있는 클래스에서 접근하고 싶다면 적어도 그 클래스는 가져다 쓰고 싶은 클래스와 상속 구조를 이루어야 함
  (상속은 다음 시간에 배움!)
 * (~)default: 오직 같은 패키지 내에서만 접근 가능
 * (-)private: 오직 해당 클래스 내에서만 접근 가능
 
 => 위에서부터 아래로 내려갈수록 접근할 수 있는 제한 범위가 좁아짐
 => +, #, ~, -: 클래스 다이어그램에서의 기호

 

패키지와 클래스 상황

FieldTest2 클래스

package com.kh.chap04.field.model.vo;

// 필드에서 사용 가능한 접근제한자 종류 4가지
public class FieldTest2 {
	
	/*
	 * (+)public: 어디서든(같은 패키지/클래스, 다른 패키지/클래스 모두) 접근 가능
	 * (#)protected: 같은 패키지 내부에서는 무조건 접근 가능,
	 * 				  다른 패키지에 있는 클래스에서 접근하고 싶다면
	 * 				  적어도 그 클래스는 가져다 쓰고 싶은 클래스와 상속 구조를 이루어야 함
	 * 				 (상속은 다음 시간에 배움!)
	 * (~)default: 오직 같은 패키지 내에서만 접근 가능
	 * (-)private: 오직 해당 클래스 내에서만 접근 가능
	 * 
	 * => 위에서부터 아래로 내려갈수록 접근할 수 있는 제한 범위가 좁아짐
	 * => +, #, ~, -: 클래스 다이어그램에서의 기호
	 */
	
	public String pub = "public";
	protected String pro = "protected";
	String df = "default"; // default를 쓰면 오류 남! 없는 게 기본값...!
	private String pri = "private";
	
	

}

FieldRun 클래스

package com.kh.chap04.field.run;

import com.kh.chap04.field.model.vo.FieldTest2;

public class FieldRun {

	public static void main(String[] args) {
    
    		FieldTest2 f2 = new FieldTest2();
		
		System.out.println(f2.pub); // public: 어디서든 직접 접근 가능
		
		// 이 아래부터는 is not visible 오류 발생
		// System.out.println(f2.pro); // protected: 같은 패키지에서는 직접 접근 가능
		  							   //	   		    다른 패키지에서는 상속 관계일 때만 가능
		// System.out.println(f2.df); // default: 같은 패키지에서만 직접 접근 가능
		// System.out.println(f2.pri); // private: 해당 클래스 내에서만 직접 접근 가능
		
		
	}

}

Test 클래스

package com.kh.chap04.field.model.vo;

public class Test {

	public static void main(String[] args) {
		
		FieldTest2 f2 = new FieldTest2();
		System.out.println(f2.pub); // public: 직접 접근 가능
		System.out.println(f2.pro); // protected: 같은 패키지에 있기 때문에 직접 접근 가능
		System.out.println(f2.df); // default: 직접 접근 가능
		
		// is not visible 오류 발생
		// System.out.println(f2.pri);

	}

}


*클래스 변수(static 변수)

[ 표현법 ]
접근제한자 static 자료형 변수명;

 

FieldTest3 클래스

package com.kh.chap04.field.model.vo;

// 클래스변수(static 변수)와 상수필드(static final)에 대해 테스트
public class FieldTest3 {

	public static String sta = "static 변수";
	// 생성 시점: 프로그램 시작과 동시에 static 영역에 올라가면서 생성
	//		   (굳이 이 객체를 생성하지 않아도 가져다 쓸 수 있다)
	// 소멸 시점: 프로그램 종료와 동시에 소멸됨
	
	// static: "공유"의 목적, 개념이 강함
	// => 프로그램 시작과 동시에 메모리 영역에 박스를 만들어 두고 끝날 때까지 공유해서 쓰자
	// => 주로 어디서든지 자주 쓰이는 것들에 대해서만 static 붙여서 쓰는 게 가장 이득임
	
	// 참고) 해당 클래스의 모든 메소드와 모든 필드가 static인 경우
	// => Math 클래스
	// => "싱글톤패턴"
	
	/*
	 * 상수 필드
	 * [ 표현법 ]
	 * public static final 자료형 상수필드명 = 값;
	 * 
	 * 한 번 지정된 값을 고정해서 계속 가져다 쓰기 때문에 처음부터 무조건 초기화를 해 줘야 함
	 * static final 예약어의 순서는 바꿔서 써도 됨 (final static도 가능)
	 * 
	 * static: 공유의 개념
	 * final: 한 번 지정된 값은 고정의 개념(상수)
	 * 
	 * => 값이 변경되어서는 안 되는 고정적인 값을 메모리 static 영역에 올려 두고
	 * 	    두고두고 공유할 목적으로 사용
	 * 
	 * + 상수명은 항상 대문자임!
	 */
	
	public static final int NUM = 10;
	
	public static void test() {
		System.out.println("나는 static 메소드야");
	}

}

FieldRun 클래스

package com.kh.chap04.field.run;

import com.kh.chap04.field.model.vo.FieldTest3;

public class FieldRun {

	public static void main(String[] args) {
    
    // 3. FieldTest3 클래스 테스트
		
		// FieldTest3 f3 = new FieldTest3();
		// => 싱글톤 패턴: 모든 필드가 static인 상황이므로 객체 생성할 필요 없음!
		
		System.out.println(FieldTest3.sta);
		System.out.println(FieldTest2.sta);
		
		// 항상 Static을 찾을 때에는 해당 클래스명은 앞에 꼭 붙여야 함
		// => 이름이 중복될 수 있기 때문에!
		
		// 상수 필드에 값을 대입하기!
		//FieldTest3.NUM = 20; // 불가능! 상수는 바꿀 수 없음!
		
		FieldTest3.sta = "FieldTest3의 static"; // 가능!
		
		System.out.println(FieldTest3.sta);
		System.out.println(FieldTest3.NUM);
		
		// static 메소드도 호출해 보기
		
		FieldTest3.test();
		// => 메소드도 마찬가지로 객체를 생성할 필요 없이 클래스명만으로 호출 가능
		
		System.out.println(Math.PI); // 원주율
			}

}

이클립스에서 control 클릭 뒤에 함수 위에 마우스 올려 보면 정의나 코딩창을 확인 가능함!