본문 바로가기
ICIA 수업일지

2021.06.22 수업일지

by 주성씨 2021. 6. 26.

- 전화번호부 3단계 다시 보기

public class PhoneBook {
	//필드
	public static Scanner sc=new Scanner(System.in);
	//컨트롤 타워
	public static void main(String[] args) {
		PhoneBookManager manager=new PhoneBookManager(); //핵심, 제어 클래스

+ PhoneBookManager는 핵심 제어 클래스다.
메인 메서드에 올림으로써 프로그램 시작과 종료 시까지 사용 가능하다. 필드에 올릴 필요 없다.
지역변수에 넣으면 관리하기 쉽기 때문이지 필드에 넣어도 상관은 없다.

package phoneBook;

public class PhoneBookManager {
	public final int MAX = 100;
	private PhoneInfo[] phoneList = new PhoneInfo[MAX];
	private int cnt = 0;

+ 제어 클래스(구현이 어렵다)

	public void searchData() {
		System.out.println("검색시작합니다.");
		System.out.print("이름:");
		String name = PhoneBook.sc.next();
		int i;
		for (i = 0; i < cnt; i++) {
			if (name.equals(phoneList[i].getName())) {
				phoneList[i].showPhoneInfo();
				break; // 이름이 동명이인 없다는 전제하에
			}
		}
		if (i == cnt) {
			System.out.println("검색할 데이터가 없습니다.");
		} //검색 후 일치하는 데이터가 없는 경우
	}

+ 검색 인스턴스 메서드 다시 보기

	public void deleteData() {
		System.out.println("삭제를 시작합니다.");
		System.out.print("이름:");
		String name = PhoneBook.sc.next();
		int i;
		for (i = 0; i < cnt; i++) {
			if (name.equals(phoneList[i].getName())) {
				phoneList[i] = phoneList[cnt - 1];
				System.out.println("삭제 완료");
				break; // 이름이 동명이인 없다는 전제하에
			}
		}
		if (i == cnt) {
			System.out.println("삭제할 데이터가 없습니다.");
		}
		cnt--;
	}

+ 삭제 인스턴스 메서드 다시 보기

 


+ 검색 삭제 메서드에서 각각 쓰던 검색 메서드를 하나로 통합하게 만드는 메서드를 만들어 각각에 적용해보기
idx는 왜 <0인가? 배열의 인덱스는 0부터 시작하기 때문에 다 찾아봐서 만족하는 값이 없으면 0에서 -1 하여 반환되기 때문에 검색 삭제 메서드에서 idx <0이라는 조건문을 만드는 것이다.

	public void searchData() {
		System.out.println("검색시작합니다.");
		System.out.print("이름:");
		String name = PhoneBook.sc.next();

		// 검색 삭제 메서드에서 각각 쓰던 검색 메서드를 하나로 통합하게 만드는 반복 메서드
		int idx = searchIdx(name);
		if (idx < 0) {
			System.out.println("검색할 데이터가 없습니다.");
		} else {
			phoneList[idx].showPhoneInfo();
			System.out.println("검색 완료");
		}

	}

	public void deleteData() {
		System.out.println("삭제를 시작합니다.");
		System.out.print("이름:");
		String name = PhoneBook.sc.next();

		// 검색 삭제 메서드에서 각각 쓰던 검색 메서드를 하나로 통합하게 만드는 반복 메서드
		int idx = searchIdx(name);
		if (idx < 0) {
			System.out.println("삭제할 데이터가 없습니다.");
		} else {
			phoneList[idx] = phoneList[cnt - 1];
			cnt--;
			System.out.println("삭제 완료");
		}
	}

+ 인덱스 검색 메서드를 만든다.

	public int searchIdx(String name) {
		for (int i = 0; i < cnt; i++) {
			if (name.equals(phoneList[i].getName())) {
				return i;
			}
		}
		return -1; // 검색 실패시
	}

 

- 상속 다시 보기

★ 상속 < 정보은닉

- 상속 실행 과정
1. 메모리 할당
2. 부모 생성자 실행
3. 자식 생성자 실행
4. 인스턴스 생성

- 상속을 위한 기본 조건
1. 상속관계에 있는 두 클래스 사이에는 is A 관계가 성립해야 한다.
2. is A 이외에 has A 관계도 상속으로 표현 가능하다.

 is A 상속 관계도


	public Student(String name, int age, int schoolNum) {
		super(name,age); //생략 되어 있으며 부모 생성자 호출하라는 의미
		this.schoolNum  = schoolNum;
	}

ㄴ Student 클래스는 Person을 상속하고 있기에 super(name, age)는 Person(name, age);을 나타낸다고 할 수 있다.

	public void showStudentInfo() {
		showPersonInfo();
		System.out.println("schoolNum = "+schoolNum);
	}

ㄴ Student의 정보를 하나하나 Sysout 할 필요 없이 showPersonInfo();를 통해서 출력이 가능하다.

+ Person -> Strudent -> PartTumeStd 순으로 상속되어 코드를 효율적으로 작성할 수 있다.


- Car로 상속자 공부하기

+ 틀 만들기

CarMain class

package ex1;

public class CarMain {

	public static void main(String[] args) {
		
		HybridWaterCar car1 = new HybridWaterCar(40,20,30);
		car1.showeHybridWaterGaugeInfo();
		System.out.println("-----------");
		
		HybridWaterCar car2 = new HybridWaterCar(20,30,30);
		car2.showeHybridWaterGaugeInfo();
		System.out.println("-----------");
		
	} // main method end

} // class end

Car class (서브 클래스)

package ex1;

public class Car {
	private int gasoline;
	// 생성자 도는 메서드 정의

} // class end

HybridCar class(서브 클래스)

package ex1;

public class HybridCar extends Car {
	private int electric;
	// 생성자 또는 메서드
	
} // class end

HybridWaterCar class(서브 클래스)

package ex1;

public class HybridWaterCar extends HybridCar {
	private int water;
	// 생성자 메서드
	
} // class end

ㄴ 필드, 생성자, 메서드를 만들어서 각 서브 클래스를 채워보자.

결과)

Car class (서브 클래스)

package ex1;

public class Car {
	private int gasoline;

	public Car(int gasoline) {
		this.gasoline=gasoline;
	}
	
	public void showGasGaugeInfo() {
		System.out.println("gasoline Gauge : "+gasoline+"Liter");
	}
	
} // class end

HybridCar class(서브 클래스)

package ex1;

public class HybridCar extends Car {
	private int electric;

	public HybridCar(int gasoline, int electric) {
		super(gasoline);
		this.electric=electric;
	}
	
	public void showeHybridGaugeInfo() {
		showGasGaugeInfo();
		System.out.println("electric Gauge : "+electric+"kWh");
	}
	
} // class end

HybridWaterCar class(서브 클래스)

package ex1;

public class HybridWaterCar extends HybridCar {

	private int water;
	
	public HybridWaterCar(int gasoline, int electric, int water) {
		super(gasoline, electric);
		this.water=water;
	}
	
	public void showeHybridWaterGaugeInfo() {
		showeHybridGaugeInfo();
		System.out.println("water : "+water+"Liter");
	}
	
} // class end

- Stackoverflow

+ 만약 메서드를 정보 출력 메서드를 모두 동일하게 한다면?

Exception in thread "main" java.lang.StackOverflowError
	at ex1.HybridWaterCar.showGasGaugeInfo(HybridWaterCar.java:13)
	at ex1.HybridWaterCar.showGasGaugeInfo(HybridWaterCar.java:13)
	at ex1.HybridWaterCar.showGasGaugeInfo(HybridWaterCar.java:13)
    .
    .
    .

ㄴ showGasGaugeInfo(); 메서드가 자신을 호출하면서 정보가 스택에 쌓이고 쌓이고 쌓여서 넘치고 에러가 발생하면 StackoverflowError가 발생하게 된다.

ㄴ 이를 방지하기 위해서 아래와 같이 입력한다.

	public void showGasGaugeInfo() {
		super.showGasGaugeInfo();
		System.out.println("water : "+water+"Liter");
	}

ㄴ super.showGasGaugeInfo();와 같이 입력하여 부모의 Car class의 멤버에 접근하는 참조 변수 super를 입력하면 해결된다. 이와 같이 각 클래스별 출력 메서드를 이름을 다르게 할 필요 없이 super 클래스의 매서드를 이용하여 서브 메서드에 super만 입력하면 해결된다.

참고)

지시자에 따른 클래스내 접근 허용 범위


- 메서드 오버 라이딩

- 오버 로딩? 오버 라이딩?
- 메서드 오버 로딩(overloading)(다중 정의) : 이름이 동일한 여러 메서드를 중복 정의하여 다른 파라미터에 대응할 수 있게 한다.
- 메서드 오버 라이딩(overriding)(재정의) : 상위 클래스에 정의된 메서드의 이름, 반환형, 매개변수 선언까지 완전히 동일한 메서드를 하위 클래스에서 다시 정의하는 것. 상위 클래서로부터 상속받은 메서드의 내용을 상속받는 클래스에 맞게 변경하는 것이다. 하위 클래스에 정의된 메서드에 의해 상위 클래스의 메서드는 가려진다.

예제 1)

package ex1_0621;

public class Main {

	public static void main(String[] args) {
		
		Student p1 = new Student("Kim",20,11);
		p1.sleep();
		
		
	}// main method end

}// class end

결과)

사람은 보통 8시간 잔다.

ㄴ 학생 인스턴스에 맞게 p1.sleep();을 재정의, 오버 라이딩해보자.
ㄴ 오른쪽 마우스 -> 소스 -> 오버라이드 임플리먼트 클릭


결과)

package ex1_0621;

public class Student extends Person {
	private int schoolNum;

	public Student(String name, int age, int schoolNum) {
		super(name,age); 
		this.schoolNum  = schoolNum;
	}
	// sleep(){8시간}
	@Override
	public void sleep() {
		// TODO Auto-generated method stub
		super.sleep();
	}

ㄴ 다음과 같이 입력한다.

	// sleep(){8시간}
	@Override //재정의
	public void sleep() {
		System.out.println("학생은 6시간 잔다.");
	}

+ 파트타임 학생이 일도 공부도 잠도 자야 한다면

package ex1_0621;

public class Main {

	public static void main(String[] args) {

		PartTimeStd p1 = new PartTimeStd("Kim", 20, 11, 8, 8000);
		p1.study();
		p1.work();
		p1.sleep(); // 재정의, 오버라이딩

	}// main method end

}// class end

ㄴ 파트타임 학생 클래스의 슬립 메서드를 오버 라이딩하고 워크 인스턴스 메서드를 추가한다.

	@Override
	public void sleep() {
		System.out.println("파트타임 학생은 4시간 잔다.");
	}

	public void work() {
		System.out.println("열심히 일한다.");
	}

ㄴ 만약 잠을 6시 간자고 TV나 게임을 하지 않는다면

	@Override
	public void sleep() {
		System.out.println("파트타임 학생은 6시간 잔다.");
		System.out.println("tv 또는 game을 하지 않는다.");
	}

ㄴ 그런데 이미 부모의 코드에서 6시간을 자는 인스턴스 메서드가 이미 있다면?

	@Override
	public void sleep() {
		super.sleep();
		System.out.println("tv 또는 game을 하지 않는다.");
	}

ㄴ 이와 같이 작성하면 된다.

+ 메인 클래스의 메인 메서드의 참조 변수를 Student로 바꾼다면?

		//참조변수				//파트타임인스턴스
		Student p1 = new PartTimeStd("Kim", 20, 11, 8, 8000);
		p1.study();
//		p1.work();
		p1.eat();
		p1.sleep(); // 재정의, 오버라이딩

결과)

학생은 공부를 한다.
사람은 보통 세끼 먹는다.
학생은 6시간 잔다.
tv 또는 game을 하지 않는다.

ㄴ 워크 메서드를 제외하고 실행이 된다. why? 상속에서 상위 클래스를 하위 클래스로 가능하다(?)
ㄴ 그런데 왜 워크 메서드는 안될까? 재정의한 sleep은 학생 클래스의 영향을 안 받고 eat, study는 보내 학생 클래스이니 출력이 되지만 work는 학생 클래스의 하위 클래스이기 때문에 에러가 나서 출력이 안 되는 것이다.

+ 그럼 메인 클래스의 메인 메서드의 참조 변수를 Person으로 바꾼다면?

		//참조변수				//파트타임인스턴스
		Person p1 = new PartTimeStd("Kim", 20, 11, 8, 8000);
//		p1.study();
//		p1.work();
		p1.eat();
		p1.sleep(); // 재정의, 오버라이딩

ㄴ 하위 클래스의 메서드인 study, work는 에러가 나서 출력이 안 되는 것이다. 사용할 수 없다.
p.s 진짜 이유는 뭘까? 컴파일러(컴퓨터 언어로 변환시키는 프로그램)의 한계이기 때문이다. 조상 타입(상위 타입)은 후손 타입(하위 타입)을 참조할 수 있다. 하지만 줄이 바뀌면 컴파일러는 어떤 인스턴스를 가리키는지 모르기 때문에 Person의 메서드 까지면 출력되는 것이다. 

+ 다음과 같이 메인 클래스 메인 메서드에 인스턴스 메서드를 만들고 다중 정의를 한다면

		show(new Person("kim", 20));
		show(new Student("Lee", 15, 11));
		show(new PartTimeStd("park", 13, 11, 8, 9000));
		//메소드 오버로딩(다중정의)
	private static void show(Person person) {
	}

	private static void show(Student student) {
	}

	private static void show(PartTimeStd partTimeStd) {
	}

ㄴ 위와 같이 가능하지만 이 예제와 같이 상속관계 에서는 아래 두 개를 지워도 된다.

	private static void show(Person person) {
	}

+ 이를 통해서 다음과 같이 나타낼 수 있다.

	private static void show(Person person) {
		person.showPersonInfo(); // 다형성
	}

ㄴ 상속 -> 오버라이딩(재정의) -> 다형성을 통해서 코드의 간결화가 위와 같이 가능해진다.

출력)

name = kim
age = 20
name = Lee
age = 15
schoolNum = 11
name = park
age = 13
schoolNum = 11
일당은 72000입니다.

- 지금까지 배운거 응용문제풀이

+. java 파일을 package에 단순 드래그 후 넣으면 다음과 같이 된다. 단, 패키지 이름은 수정해야 한다. 아래는 그 파일의 소스이다.

Main class

package ex2;

public class Employee // 洹쇰줈�옄
{
	private String name;

	public Employee(String name) {
		this.name = name;
	}
	public void showYourName() {
		System.out.println("�씠由�:" + name);
	}
    //�삤踰꾨씪�씠�뵫 ���긽
	public int getPay() {
		return 0;
	}
	public void showSalaryInfo() {
	}
}

Permanent class

package ex2;
//�뜲�씠�꽣 �겢�옒�뒪
public class PermanentWorker extends Employee // �젙洹쒖쭅
{
	private int salary; // �뿰遊�

	public PermanentWorker(String name, int money) {
		super(name);
		salary = money;
	}
	@Override
	public int getPay() {
		return salary;//*12-�꽭湲�..;
	}
	@Override
	public void showSalaryInfo() {
		showYourName();  //�씠由�
		System.out.println("�젙洹쒖쭅 湲됱뿬:" + getPay());
	}
}

Sales class

package ex2;

public class SalesWorker extends PermanentWorker {
	private int salesResult; // �썡�뙋留� �떎�쟻
	private double bonusRatio; // �긽�뿬湲� 鍮꾩쑉

	public SalesWorker(String name, int salary, double ratio) {
		super(name, salary);  //�씠由�, �뿰遊�
		salesResult = 0;
		bonusRatio = ratio;
	}

	public void addSalesResult(int value) {
		salesResult += value;
	}
	@Override
	public int getPay(){
		return super.getPay()+(int)(salesResult*bonusRatio);
	}
	@Override
	public void showSalaryInfo() {
		showYourName();
		System.out.println("�쁺�뾽吏� 湲됱뿬: " + getPay());
	}
}

Temp class

package ex2;

public class TemporaryWorker extends Employee // �엫�떆吏�(鍮꾩젙洹쒖쭅)
{
	private int workTime = 0; // 洹쇰Т�븳 �떆媛�
	private int payPerHour; // �떆媛꾨떦 湲됱뿬

	public TemporaryWorker(String name, int pay) {
		super(name);
		payPerHour = pay;
	}

	public void addWorkTime(int time) {
		workTime += time;
	}
	@Override
	public int getPay() {
		return workTime * payPerHour;//...蹂듭옟�븳 怨꾩궛�떇
	}
	@Override
	public void showSalaryInfo() {
		showYourName();
		System.out.println("鍮꾩젙洹쒖쭅 湲됱뿬: " + getPay());
	}
}

ㄴ 단순 드래그하여 넣으면 위와 같이 한글 깨짐이 발생한다. 이럴 때는 어떻게 해야 할까?
ㄴ 이클립스 인코딩 방식이 다르기 때문에 메뉴 상단에
ㄴ 윈도 - 프리퍼런스 - 제너럴 - 워크스페이스 - 텍스트 파일 인코딩 - 아더 - UTF-8
ㄴ 로 변환한다. 그런데도 안된다?
ㄴ 윈도우 - 프리퍼런스 - 제너럴 - 텍스트 검색 - 인코딩에서 전부 UTF-8로 바꿔서 적용한다.
ㄴ 그런데도 안된다? 그냥 메모장으로 열어서 복붙 하시라 ㅎ

수정후)

Main class

package ex2;  //소스에 가장 상단에 1번만 선언가능

public class Main
{
	public static void main(String[] args){
		// 직원관리를 목적으로 설계된 컨트롤 클래스의 인스턴스생성
		EmployeeHandler handler=new EmployeeHandler(100);  //제어 클래스(코딩어렵다)
		//정규직 등록
		//handler.addEmployee(new Employee("KIM"));  //실수-->에러
		handler.addEmployee(new PermanentWorker("LEE", 1500));

		TemporaryWorker alba=new TemporaryWorker("Jung", 700);	
		alba.addWorkTime(5);									
		handler.addEmployee(alba);	//비정규직 등록	
		
		SalesWorker seller=new SalesWorker("Hong", 1000, 0.1);
		seller.addSalesResult(7000); // 영업 실적 7000
		handler.addEmployee(seller);  //영업직 등록

		handler.showAllSalaryInfo();  	//이번 달에 지불해야 할 급여의 정보	
		handler.showTotalSalary();		//이번 달에 지불해야 할 급여의 총합
	}
}

Emp class

package ex2;

public class Employee // 근로자
{
	private String name;

	public Employee(String name) {
		this.name = name;
	}

	public void showYourName() {
		System.out.println("이름:" + name);
	}

	// 오버라이딩 대상
	public int getPay() {
		return 0;
	}

	public void showSalaryInfo() {
	}
}

Permanent class

package ex2;

//데이터 클래스
public class PermanentWorker extends Employee // 정규직
{
	private int salary; // 연봉

	public PermanentWorker(String name, int money) {
		super(name);
		salary = money;
	}

	@Override
	public int getPay() {
		return salary;// *12-세금..;
	}

	@Override
	public void showSalaryInfo() {
		showYourName(); // 이름
		System.out.println("정규직 급여:" + getPay());
	}
}

Sales class

package ex2;

public class SalesWorker extends PermanentWorker {
	private int salesResult; // 월판매 실적
	private double bonusRatio; // 상여금 비율

	public SalesWorker(String name, int salary, double ratio) {
		super(name, salary); // 이름, 연봉
		salesResult = 0;
		bonusRatio = ratio;
	}

	public void addSalesResult(int value) {
		salesResult += value;
	}

	@Override
	public int getPay() {
		return super.getPay() + (int) (salesResult * bonusRatio);
	}

	@Override
	public void showSalaryInfo() {
		showYourName();
		System.out.println("영업직 급여: " + getPay());
	}
}

Temp class

package ex2;

public class TemporaryWorker extends Employee // 임시직(비정규직)
{
	private int workTime = 0; // 근무한 시간
	private int payPerHour; // 시간당 급여

	public TemporaryWorker(String name, int pay) {
		super(name);
		payPerHour = pay;
	}

	public void addWorkTime(int time) {
		workTime += time;
	}

	@Override
	public int getPay() {
		return workTime * payPerHour;// ...복잡한 계산식
	}

	@Override
	public void showSalaryInfo() {
		showYourName();
		System.out.println("비정규직 급여: " + getPay());
	}
}

+ 메인 클래스에서 제어 클래스 역할을 하는 임플로이 핸들러 클래스를 만든다.

package ex2;
//제어 클래스
public class EmployeeHandler {

	}
	
}

ㄴ 필드 생성자 인스턴스 메서드를 만든다.

package ex2;

//제어 클래스
public class EmployeeHandler {
	private Employee[] empList; // =new employee[100];
	private int cnt = 0;

	public EmployeeHandler(int n) {
		empList = new Employee[n];
	}

	public void addEmployee(Employee emp) {
		empList[cnt++] = emp; // 다형성
	}

	public void showAllSalaryInfo() {
		for (int i = 0; i < cnt; i++) {
			empList[i].showSalaryInfo(); // 다형성
			System.out.println("---------------");
		}
	}

	public void showTotalSalary() {
		int sum = 0;
		for (int i = 0; i < cnt; i++) {
			sum += empList[i].getPay(); // 다형성
		}
		System.out.println("salary sum : " + sum);
	}
}// class end

결과)

이름:LEE
정규직 급여:1500
---------------
이름:Jung
비정규직 급여: 3500
---------------
이름:Hong
영업직 급여: 1700
---------------
salary sum : 6700

- 상속 다시 보기

package ex1_0621;

public class Main2 {

	public static void main(String[] args) {

ㄴ 새로운 클래스를 만든다.

	public static void main(String[] args) {
		PartTimeStd p1 = new PartTimeStd("Kim",20,11,8,9000);
		Student s1 = p1; //파트타임이 스투던트 후손 타입이라 성립된다.
		Person p=p1;
		// 위의 경우는 문제가 없음 그러나

ㄴ 위와 같이 상위 클래스에 하위 클래스가 성립은 된다.

		Person p1 = new PartTimeStd("Kim", 20, 11, 8, 9000);
		Student s1 = p1;
		s1.sleep();

ㄴ 그러나 이처럼 하위클래스가 상위 클래스에는 성립이 되지 않는다.

		Person p1 = new PartTimeStd("Kim", 20, 11, 8, 9000);
		Student s1 = (Student)p1; // 형변환을 하면 성립된다.
		s1.sleep();

ㄴ 형 변환을 사용하면 성립된다. 타입을 원하는 클래스로 변환이 된다.

		Person p1=new  Person("kim", 20);
		Student s1 = (PartTimeStd)p1;
		s1.sleep();

ㄴ 그런데 위처럼 하면 어떻게 될까

결과)

Exception in thread "main" java.lang.ClassCastException:
class ex1_0621.Person cannot be cast to class ex1_0621.PartTimeStd 
(ex1_0621.Person and ex1_0621.PartTimeStd are in unnamed module of loader 'app')
at ex1_0621.Main2.main(Main2.java:16)

ㄴ 이처럼 오류가 난다. 왜? 하위 클래스로 상위 클래스 형 변환은 불가능하기 때문이다.

'ICIA 수업일지' 카테고리의 다른 글

2021.06.25 수업일지  (0) 2021.06.26
2021.06.24 수업일지  (0) 2021.06.26
2021.06.23 수업일지  (0) 2021.06.26
2021.06.21 수업일지  (0) 2021.06.26
2021.06.10 수업일지  (0) 2021.06.20