본문 바로가기

수업내용

[Day11][Java] 캡슐화 / 다형성

Ⅰ. 캡슐화


-- 프로젝트를 진행할 때, A, B, C, D가 각자 맡은 클래스에서 자신의 클래스만 관리해야 한다.

-- 관리하는 과정 중에 누구나 접근이 가능해서 진행하고 있는 코드를 방해하는 것을 방지하기 위해 캡슐화(encapsulation, 은닉화)라는 과정이 필요하다.

 

접근제한자

(접근지정자, accessmodifier)

자기 자신 클래스 내부

동일패키지에있는

다른클래스

다른패키지에있는

하위(자식)클래스

그 외의 영역

public   O O O O
protected  O O O X
default(package) O O X X
private O X X X

 


-- 각 접근제한자마다 어떻게 나타나는지 알아보기 위해 Package 2개(my.day12.b.accessmodifier/my.day12.c.accessmodifier) 와 각 클래스 4개(Gujikja, MainApp, Member, Other), 2개(Gujikja2, MainApp)을 만들었다. 

 

 

 

 

 

▶▶ class Member

package my.day12.b.accessmodifier;

public class Member {
	public String name = "안중근";
	protected String address = "서울시 강남구 도곡동";
	String email = "ahn@gmail.com";
	private String mobile = "01023456789";
}

 

 

 

-- Member 클래스에 name(public), address(protected), email(default), mobile(private) 변수를 선언하였다.

 

-- Gujikja 클래스는 Member 클래스를 부모 클래스로 지정하여 상속 받을 수 있도록 한다.

▷extends Member를 써 주는 방법도 있지만 이름이 같은 클래스가 있기 때문에 클래스를 만들 때 지정해 주었다.

 

 

 

 

▶ class Gujikja

 

 

 

package my.day12.b.accessmodifier;

public class Gujikja extends Member {
	private int hopeMoney = 5000;
}

 

 

-- finish를 눌러 클래스를 생성하면 extends Member가 자동 입력되어 있는 것을 볼 수 있다.

 

 


 

 

package my.day12.b.accessmodifier;

public class MainApp {

	public static void main(String[] args) {
		
		Gujikja guja = new Gujikja();
		guja.
	}
}

 

 

 

 

 

 

-- MainApp에서 Gujikja 객체를 생성하면 field가 name(public), address(protected), email(default)가 나타난다.

-- private으로 지정된 mobile과 hopeMoney는 보이지 않는 것을 볼 수 있다. 왜냐하면 private로 지정된 변수는 자식 클래스라고 하더라도 사용할 수 없고 그 클래스에서만 사용되기 때문이다.

 

 

▶ class MainApp

 

 

package my.day12.b.accessmodifier;

public class MainApp {

	public static void main(String[] args) {
		
		Gujikja guja = new Gujikja();
		System.out.println(guja.name);
		System.out.println(guja.address);
		System.out.println(guja.email);
	}
}

 

 

 

▷결과

안중근
서울시 강남구 도곡동
ahn@gmail.com

 

 private는 자기 자신 클래스 내부에서만 사용 가능하다.

 

 

▶ class Other

-- my.day12.b.accessmodifier에 Other 클래스를 생성한다.

 

 

 

package my.day12.b.accessmodifier;

public class Other {

	void info() {
		Member mem = new Member();
		String result = "";
		result += "성명 : " + mem.name;
		result += "주소 : " + mem.address;
		result += "이메일 : " + mem.email;
		
		System.out.println(result);
	}
}

 

 

 

 동일 패키지에 있는 다른 클래스는 private 빼고 다(public, protected, default) 받아올 수 있다.

 


 

다른 패키지에 클래스를 만들어 보자.

 

 

 

package my.day12.c.accessmodifier;

import my.day12.b.accessmodifier.Gujikja;

public class MainApp {

	public static void main(String[] args) {

		Gujikja guja = new Gujikja();
	}

}

 

 

 

 

 

다른 패키지의 클래스는 name(public)만 불러올 수 있다.

 

 

다른 패키지에 하위(자식)클래스를 만들어 보자.

-- my.day12.b.accessmodifier.Member를 부모 클래스로 두는 my.day12.c.accessmodifier 패키지의 Gujikja2 클래스를 생성한다.

 

 

▶ class Gujikja2

 

 

 

package my.day12.c.accessmodifier;

import my.day12.b.accessmodifier.Member;

public class Gujikja2 extends Member {

	void showInfo() {
		String info = "";
		info += "성명 : " + super.name;
		info += "주소 : " + super.address;
		
		System.out.println(info);
	}
}

 

 

다른 패키지에 하위(자식)클래스는 name(public)과 address(protected)만 받아올 수 있고, email(default)과 mobile(private)은 상속받지 못한다.

-- 부모 클래스를 만드는 이유는 어떠한 변수나 메소드를 상속 받아 오려 하기 때문인데, 접근제한자가 default나 private일 경우에는 부모 클래스여도 상속받지 못한다. 따라서 부모 클래스를 만들 때에는 최소한 접근제한자가 public(누구나), protected(자식클래스만)를 사용하여야 한다.

 


구직자 관련 프로그램을 캡슐화 해 보자.

 

day11.zip
0.01MB

 

-- 구직자 관련 프로그램은 day11 상속(inheritance)에서 했던 코드를 재활용하였다.

 

 

 

package my.day12.d.encapsulation;

public class Member {
	private String id;
	private String passwd;
	private String name;
}

 

 

 

-- 변수의 접근제한자를 private로 바꾼다.

-- Source - Generate getter setter 해서 메소드 생성한다

 

 

 

public void setId(String id) {
		if(id != null &&	// id가 null이 아니어야 한다
		   5 <= id.length() && id.length() <=20)	// id 5글자 이상 20글자 이하
		this.id = id;
		else
			System.out.println(">> ID는 5글자 이상 20글자 이하이어야 합니다.\n");
	}

 

 

 

 

public void setPasswd(String passwd) {
		if(passwd != null && MyUtil.passwdCheck(passwd))
			this.passwd = passwd;
		else
			System.out.println(">> 암호는 8글자 이상 15글자 이하의 영문대문자,"
           + "영문소문자, 숫자, 특수기호가 혼합되어야만 합니다. \n");
	}

 

 

 

 

public void setName(String name) {
		if(name != null)
		this.name = name;
	}

 

 

 

-- Gujikja 클래스를 Member 클래스를 부모 클래스로 지정한다.

 

 

 

public Gujikja(String id, String passwd, String name, String birthday, int gender, int school,
		       int hopeMoney) {
	super.setId(id);
	super.setPasswd(passwd);
	super.setName(name);
	
	this.birthday = birthday;
	this.gender = gender;
	this.school = school;
	this.hopeMoney = hopeMoney;
}

 

 

 

public String getBirthday() {
		return birthday;
	}


	public void setBirthday(String birthday) {
		this.birthday = birthday;
        // H.W
	}


	public int getGender() {
		return gender;
	}


	public void setGender(int gender) {
		if(gender == 1 || gender == 2)
		this.gender = gender;
		else
			System.out.println("남자는 1, 여자는 2로만 입력하세요!\n");
	}


	public int getSchool() {
		return school;
	}


	public void setSchool(int school) {
		if(school == 1 ||
		   school == 2 ||
		   school == 3 ||
		   school == 4)
		this.school = school;
		else
			System.out.println(">> 학력은 대졸이상은 1, 초대졸은 2, 고졸은 3, 고졸미만은 4로 하세요!\n");
	}


	public int getHopeMoney() {
		return hopeMoney;
	}


	public void setHopeMoney(int hopeMoney) {
		if(hopeMoney > 0)
		this.hopeMoney = hopeMoney;
		else
			System.out.println(">> 희망연봉은 0보다 커야 합니다.\n");
	}

 

 

 

숙제 ▶▶ setBirthday() 를 채워라.

 

 

 

public void setBirthday(String birthday) {
		try {
			int intBirth = Integer.parseInt(birthday);
		this.birthday = birthday;
		}catch(NumberFormatException e) {
			System.out.println("숫자로만 입력하세요.");
		}
		System.out.println("8자리 숫자로 입력하세요.");
	}

 

 

 


 

 

-- Company 클래스도 Member 클래스를 부모 클래스로 지정하여 변수들을 private로 바꾸어 준다.

 

 

 

package my.day12.d.encapsulation;

public class Company extends Member{

private long seedMoney;
private String jobType;

 

 

 

-- Source-Generate getter setter 해서 메소드 생성한다.

 

 

 

public long getSeedMoney() {
		return seedMoney;
	}
	public void setSeedMoney(long seedMoney) {
		if(seedMoney > 0) 
		this.seedMoney = seedMoney;
		else
			System.out.println("자본금은 0보다 커야 합니다!\n");
	}
	
	public String getJobType() {
		return jobType;
	}
	public void setJobType(String jobType) {	// null 또는 공백이 아니어야 함
		if(jobType != null &&
		   !jobType.trim().isEmpty()) // 텅 비어야만 참
		this.jobType = jobType;
		else
			System.out.println(">> 업종을 입력하세요!\n");
	}

 

 

 

-- id, passwd, name 변수들이 private이기 때문에 직접 읽지 못한다. 따라서 메소드를 읽어와야 한다.

 

 

 

public Company(String id, String passwd, String name, long seedMoney, String jabType) {
		super.setId(id);
		super.setPasswd(passwd);
		super.setName(name);
		this.seedMoney = seedMoney;
		this.jobType = jabType;
	}

 

 

 

-- MainApp도 클래스이므로 private로 지정된 변수를 쓰지 못한다. 오류를 잡기 위해 고쳐 주어야 한다.

 

 

 

Gujikja guja = new Gujikja();
		guja.setId(id);
		guja.setPasswd(passwd);
		guja.setName(name);
		guja.setBirthday(birthday);
		guja.setGender(Integer.parseInt(strGender));
		guja.setSchool(Integer.parseInt(strSchool));
		guja.setHopeMoney(hopeMoney);

 

 


 

Ⅱ. 다형성

-- Package(my.day13.a.polymorphism)와 클래스 3개(Cat, Dog, Duck)를 생성한다.

-- 변수 private name, birthdayYear를 세 클래스 모두 사용하므로 클래스 Animal을 사용하여 상속 받아서 사용하자.

-- Animal 클래스에 name, birthYear 변수를 private로 만들어 호출할 수 있는 메소드도 만들어 준다.

 

 

 

public class Animal {
		private String name;
		private int birthYear;

		public String getName() {
			return name;
		}
		public void setName(String name) {
			if(name != null)
			this.name = name;
		}
		public int getBirthYear() {
			return birthYear;
		}
		public void setBirthYear(int birthYear) {
			if(birthYear > 0)
			this.birthYear = birthYear;
			else
				System.out.println(">> 생년은 0보다 커야 합니다.");
		}
}

 

 

 

-- birthdayYear : 나이 구하는 메소드는 자주 쓰기 때문에 static메소드를 만들어 사용한다.

 

 

 

package my.util;

import java.util.Calendar;

public class MyUtil {
	// 태어난 년도만 입력해 주면 현재 나이를 알려주는 메소드 //
	public static int currentAge(int birthYear) {
		
		Calendar currentDate = Calendar.getInstance(); // 현재날짜와 시간을 얻어온다.
		int currentYear = currentDate.get(Calendar.YEAR);
		
		return currentYear-birthYear+1;
	}
}

 

 

 

-- 이제 나이를 알고 싶으면 메소드를 호출하기만 하면 된다.

 

 

 

public int getAge() {
			int age = 0;
			age = MyUtil.currentAge(birthYear);
			return age;
		}

 

 

 

-- 위의 코드를 사용하면 빙빙 돌게 되므로 간단한게 한 줄로 입력한다.

 

 

 

public int getAge() {
			return MyUtil.currentAge(birthYear);
		}

 

 

 

-- 기본 생성자를 생성한다.

 

 

 

public Animal() {
			System.out.println(">> Animal 클래스의 기본생성자 호출함.\n");
		}

 


 

 

-- 각 동물들만 가지고 있는 특징을 각 클래스에 코드화 해 보자.

   예> 강아지 weight, 고양이 color, 오리 price

-- 각 변수들은 private로 선언하였으므로 get set으로 받아와야 한다.

 

 

 

public class Dog extends Animal{
	
	private int weight;
	
	public int getWeight() {
		return weight;
	}

	public void setWeight(int weight) {
		if(weight > 0)
		this.weight = weight;
		else
			System.out.println(">> 몸무게는 0 보다 커야 합니다.\n");
	}
	
	public void printDog() {
		// String result = "";
		// result += ""; => 이렇게 사용해도 되지만 메모리 낭비가 심하다
		
		StringBuffer sb = new StringBuffer();
		sb.append("1.강아지명 : " + super.getName() + "\n");
		sb.append("2.나이  : " + super.getAge()+" 살\n");
		sb.append("3.몸무게  : " + weight+" kg\n");
		
		System.out.println(sb.toString());
		// 스트링버퍼를 사용하기를 권장
	}
}

 

 

 

 

public class Cat extends Animal{

	private String color;

	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
	
	public void printCat() {
		StringBuffer sb = new StringBuffer();
		sb.append("1.고양이명 : " + super.getName() + "\n");
		sb.append("2.나이  : " + super.getAge()+" 살\n");
		sb.append("3.피부색  : " + color+" 색\n");
		System.out.println(sb.toString());
	}
}

 

 

 

public class Duck extends Animal{

	private int price;
	
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		if(price > 0)
		this.price = price;
		else
			System.out.println(">> 가격은 0보다 커야 합니다.");
	}
	
	public void printDuck() {
		StringBuffer sb = new StringBuffer();
		sb.append("1.오리명 : " + super.getName() + "\n");
		sb.append("2.나이  : " + super.getAge()+" 살\n");
		sb.append("3.가격  : " + price+" 원\n");
		System.out.println(sb.toString());
	}
}

 

 

 

-- 같은 Package에 MainApp(main method 포함) 클래스를 생성한다.

-- dog, cat, duck 새로운 객체를 생성한다

-- 부모 클래스의 기본 생성자 자동으로 호출된다.

-- 자식 클래스의 객체를 만드는 순간, 자동적으로 자식 클래스에 있는 부모 클래스의 기본 생성자가 호출된다.

-- 부모 클래스와 자식 클래스에 기본 생성자가 둘 다 있는 경우, 부모 클래스의 기본 생성자가 먼저 호출된다.

 

 

 

public Dog() {
		super();	// 부모 클래스의 기본생성자를 호출한다(생략가능)
		System.out.println("▶ Dog 클래스의 기본 생성자 호출함\n");
	}

 

 

 

-- super() 생략가능하다. 단, super을 아래에 위치하도록 하면 오류가 난다.

-- 반드시 아예 쓰지 않거나 쓸 때에는 맨 위에 두어야 한다.

 


 

public static void main(String[] args) {
		
		Dog dg1 = new Dog();
		dg1.setName("뽀삐");
		dg1.setBirthYear(2010);
		dg1.setWeight(10);
		System.out.println("~~~~~~~~~~~~~~~~~~~~");
		
		Cat ct1 = new Cat();
		ct1.setName("톰");
		ct1.setBirthYear(2017);
		ct1.setColor("검정");
		System.out.println("====================");
		
		Duck dk1 = new Duck();
		dk1.setName("도널드");
		dk1.setBirthYear(2018);
		dk1.setPrice(3000);
		System.out.println("####################");
		
		
		System.out.println("\n~~~~~~~~~~~~~~~~~~~다형성~~~~~~~~~~~~~~~~~~~~~~\n");

		dg1.printDog();
		ct1.printCat();
		dk1.printDuck();

		Animal an1 = new Dog();			// 실제는 dog인데 animal처럼 지낸다
		an1.setName("삽살개");
		an1.setBirthYear(2010);
		
		an1.printAnimal();
		
		Animal an2 = new Cat();
		an2.setName("샴고양이");
		an2.setBirthYear(2013);
		Animal an3 = new Duck();
		an3.setName("청둥오리");
		an3.setBirthYear(2019);

 

 

 

-- 다형성이란? 자식클래스로 생성되어진 객체를 부모 클래스 타입으로 받을 수 있다.

 

 

 

 

-- dog로 생성되어진 객체로 부모 클래스(Animal)에 있는 printAnimal 함수를 호출할 수 있다.

 

 

 

-- Animal에는 weight가 없다. 강아지처럼 지내려면 printDog()가 나와야 하고 printDog()나오려면 weight가 나와야 하는데 없으므로 오류가 발생한다.

-- 부모 클래스로 생성되어진 객체를 자식클래스의 타입으로 받을 수 없다. (컴파일시 오류)

 

 

 

Dog dg3 = (Dog)an1;

 

 

 

-- an1은 Animal 처럼 지냈지만 사실은 dog였으므로 casting을 해 주면 dog로 다시 돌아온다.

 

 

 

Dog dg4 = (Dog)anim;

 

 

-- 실제가 animal이기 때문에 dog로 형변환 할 수 없다.

-- 빨간 줄은 뜨지 않지만 실행해 보면 클래스 형을 바꿀 수 없다는 오류가 발생한다. (실행시 오류)