업 캐스팅, 다운 캐스팅은 서로 상속관계에 있는 타입간의 형변환만 가능하다.
Account 클래스
package ex09_03_upcasting;
class Account {
String accountNo;
String ownerName;
int balance;
Account(String accountNo, String ownerName, int balance) {
this.accountNo = accountNo;
this.ownerName = ownerName;
this.balance = balance;
}
void deposit(int amount) {
this.balance += amount;
}
int withdraw(int amount) throws Exception {
if (balance < amount)
throw new Exception("잔액이 부족합니다.");
balance -= amount;
return amount;
}
}
package ex09_03_upcasting;
public class CheckingAccount extends Account {
String cardNo;
CheckingAccount(String accountNo, String ownerName,
int balance, String cardNo) {
//파라미터 3개의 슈퍼 클래스 생성자 호출문
//반드시 생성자의 첫번째 명령문이어야 한다.
//예전에 this()로 생성자 호출했던 것도 연결해서 공부
super(accountNo, ownerName, balance);
this.cardNo = cardNo; //클래스 안에 선언된 필드 초기화
}
int pay(String cardNo, int amount) throws Exception {
if (!cardNo.equals(this.cardNo) || (balance < amount))
throw new Exception("지불이 불가능합니다.");
return super.withdraw(amount); //super.은 생략할 수 있다.
}
}
public class RefTypeExample6 {
public static void main(String[] args) {
Account obj =
new CheckingAccount("111-22-33333333","홍길동",10,"4444-5555-6666-7777");
// (Account) 묵시적 형변환
try {
int amount = obj.pay("4444-5555-6666-7777",47000);
System.out.println("인출액: "+amount);
System.out.println("카드번호: "+obj.cardNo);
} catch (Exception e) {
System.out.println(e.getMessage());
//(1)obj.pay (2)obj.cardNo 컴파일 오류가 생기는 이유는 뭘까?
//자바 컴파일러는 컴파일 할 때 변수의 타입만 보고
//메소드나 필드의 존재 여부를 체크하기 때문에
//Account 클래스에 pay, cardNo메소드가 있어야 컴파일 에러가 없다
//but!! 컴파일 후 실행할 때는 override된 메소드를 호출한다!!!!!!!!!!!
}
}
}
public class RefTypeExample7 {
public static void main(String[] args) {
Account obj1 =
new CheckingAccount("111-22-33333333","홍길동",100000,"5555-6666-7777-8888");
CheckingAccount obj2 = obj1;
//부모형을 자식형에 대입할 수 없음
//컴파일러는 변수의 타입만 보고 대입 가능성을 검사하기 때문에 에러발생
try {
int amount = obj2.pay("5555-6666-7777-8888",47000);
System.out.println("인출액: "+amount);
System.out.println("카드번호: "+obj2.cardNo);
System.out.println("잔액: "+obj2.balance);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
import java.io.*;
class ExceptionExample8_Filereader {
public static void main(String[] args) {
FileReader reader = new FileReader("some.txt");
}
}
import java.io.FileReader;
class ExceptionExample8_Filereader_trycatch {
public static void main(String[] args) {
try {
FileReader reader = new FileReader("some.txt");
reader.close();
//close 메소드에서도 IOException을 던지고 있으니,
//이것에 대한 예외처리를 해줘야 한다
} catch (Exception e) {
System.out.println("파일을 찾을 수 없습니다.");
}
//try-catch문은 하위클래스에서 상위클래스로 작성한다.
//상위 클래스로 정의하면 굳이 하위 클래스로 작성할 이유가 없기 때문이다. (다형성 개념과 연결됨).
/* catch (FileNotFoundException e) {
System.out.println("파일을 찾을 수 없습니다.") }
catch (IOException e) {
System.out.println("입출력 오류") }
*/
}
}
//상속 관계에 있는 두 종류의 Exception을 처리하는 try문
//catch문 예외 순서가 부모가 자식보다 먼저오면 에러 발생
//-부모 먼저 예외를 정의하면 자식에서 예외를 정의할 필요가 없어짐 (다형성)
//FileNotFoundException -> IOException
import java.io.FileReader;
import java.io.IOException;
class ExceptionExample8_Filereader_trycatch_finally {
public static void main(String[] args) {
FileReader reader = null; // (finally) 쓰려면 main에 정의해줘야 함
try {
reader = new FileReader("some.txt");
}
catch (Exception e) {
System.out.println("파일을 찾을 수 없습니다.2");
} finally { //IOException을 try, catch문으로 한번 더 처리해야 함
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/* catch (FileNotFoundException e) {
System.out.println("파일을 찾을 수 없습니다.") }
catch (IOException e) {
System.out.println("입출력 오류") }
*/
}
}
하나의 참조변수로 여러 타입의 객체를 참조할 수 있는 것
Account 클래스
class Account {
String accountNo; //계좌번호
String ownerName; //예금주 이름
int balance; //잔액
Account(String accountNo, String ownerName, int balance) {//생성자
this.accountNo = accountNo;
this.ownerName = ownerName;
this.balance = balance;
}
void deposit(int amount) {
this.balance += amount;
}
int withdraw(int amount) throws Exception {
if (balance < amount)
throw new Exception("잔액이 부족합니다.");
balance -= amount;
return amount;
}
}
public class BonusPointAccount extends Account {
int bonusPoint; //누적 포인트 필드
BonusPointAccount(String accountNo, String ownerName,
int balance, int bonusPoint) {
super(accountNo, ownerName, balance);
this.bonusPoint = bonusPoint;
}
//예금한다 기능을 다시 구현(메소드 오버라이딩)
//상속 받은 메소드의 로직을 단순히 확장
//super는 자식 클래스에서 부모 클래스로 상속 받은 필드와 메서드를 참조하는데 사용한다.
void deposit(int amount) {
balance += amount; //super.balance += amount;
//super.deposit(amount);
bonusPoint += (int)(amount * 0.001); //실수의 값을 정수형으로 형변환casting
//bonusPoint += amount * (1.0/1000); //bonuspoint는 int형임.
}
}
class InheritanceExample2 {
public static void main(String[] args) {
Account obj1 = new Account("111-22-333333", "임꺽정", 10000);
printAccountInfo(obj1);
CheckingAccount obj2 = new CheckingAccount("444-55-666666","홍길동",20000,"5555-6666-7777");
printAccountInfo(obj2);
CreditLineAccount obj3 = new CreditLineAccount("777-88-999999","김선달",30000,2000000);
printAccountInfo(obj3);
// 다양한 타입의 객체를 가지고 있는 메소드를 호출한다.
BonusPointAccount obj4 = new BonusPointAccount("000-00-000000", "김미영",0,0);
printAccountInfo(obj4);
}
//static을 안 붙이면, printAccountInfo가 포함된 클래스를 인스턴스화 한 후에야 이 메소드를 사용할 수 있게 됨
//다양한 타입의 인스턴스 참조변수를 부모 클래스 하나의 파라미터로 받을 수 있다. (부모는 자식을 다 감싼다)
static void printAccountInfo (Account obj) {
System.out.println("계좌번호:"+obj.accountNo);
System.out.println("예금주 이름:"+obj.ownerName);
System.out.println("잔액:"+obj.balance);
System.out.println();
}
}
public abstract class MessageSender {
String title;
String senderName;
MessageSender (String title, String senderName) {
this.title = title;
this.senderName = senderName;
}
abstract void sendMessage(String recipient);
}
class SMSSender extends MessageSender {
String returnPhoneNo;
String message;
SMSSender(String title, String senderName,
String returnPhoneNo,String message) {
super(title, senderName);
this.returnPhoneNo = returnPhoneNo;
this.message = message;
}
// 슈퍼 클래스의 메서드를 오버라이드하는 메서드
// 추상 클래스를 상속받은 클래스는 추상 메서드를 구현(implement)해야만 한다!!! (override)
void sendMessage(String recipient) {
System.out.println("------------------------------");
System.out.println("제목: " + title);
System.out.println("보내는 사람: " + senderName);
System.out.println("전화번호: " + recipient);
System.out.println("회신 전화번호: " + returnPhoneNo);
System.out.println("메시지 내용: " + message);
}
}
class EMailSender extends MessageSender {
String senderAddr;
String emailBody;
EMailSender(String title, String senderName,
String senderAddr, String emailBody) {
super(title, senderName);
this.senderAddr = senderAddr;
this.emailBody = emailBody;
}
// 슈퍼 클래스의 메서드를 오버라이드하는 메서드
// 추상 클래스를 상속받은 class는
// 추상 메서드를 구현(implement)해야만 한다!!! (override)
void sendMessage(String recipient) {
System.out.println("------------------------------");
System.out.println("제목: " + title);
System.out.println("보내는 사람: " + senderName + "\n"
+ "보낸 주소 : " + senderAddr);
System.out.println("받는 사람: " + recipient);
System.out.println("내용: " + emailBody);
}
}
public class InheritanceExample8 {
public static void main(String[] args) {
EMailSender obj1 = new EMailSender("생일을 축하합니다", "고객센터",
"admin@dukeeshop.co.kr",
"10% 할인쿠폰이 발행되었습니다.");
SMSSender obj2 = new SMSSender("생일을 축하합니다", "고객센터",
"02-000-0000", "10% 할인쿠폰이 발행되었습니다.");
//서브 클래스 참조변수 obj1, obj2를 가지고 메소드를 호출
send(obj1, "hatman@yeyeye.com");
send(obj1, "stickman@hahaha.com");
send(obj2, "010-000-0000");
}
/*
1.MessageSender obj : 슈퍼 클래스 타입의 파라미터
2.MessageSender 클래스에서 sendMessage 메소드를 주석 처리할 경우 에러가 발생한다.
3.자바 컴파일러는 컴파일 과정에서 해당 클래스에 그 메소드가 있는지 없는지 체크한다.
주석처리를 하면 MessageSender 클래스에 sendMessage()메소드가 존재하지 않아서
The method sendMessage(String) is undefined for the type
에러가 발생한다.
4.JVM은 객체의 메소드를 호출할 때 변수 타입에 상관없이 객체가 속하는 클래스의 메소드를 호출한다.
*/
//MessageSender obj : 슈퍼 클래스 타입의 파라미터
static void send(MessageSender obj, String recipient) {
//어느 클래스의 sendMessage메소드가 호출될까?
//EmailSendr(obj1) 또는 SMSSender(obj2)에, 즉 객체에 정의된 overriding된 메서드를 호출한다 (동적바인딩)
- 동적바인딩 개념과 연결됨
obj.sendMessage(recipient);
}
}
추상클래스보다 추상화 정도가 높다. (실제 구현된 것이 전혀 없는 기본 설계도)
추상메서드와 상수만을 멤버로 가질 수 있다.
인스턴스를 생성할 수 없다.
다중 상속을 지원하지 않지만, 다중상속효과를 간접적으로 얻을 수 있다.
Lendable 인터페이스
public interface Lendable {
//대출한다.
public abstract void checkOut(String borrower, String date);
//반납한다.
public abstract void checkIn();
}
//대출 가능 인터페이스를 구현하는 단행본 클래스
//SeparateVolume 클래스는 Lendable 인터페이스를 '구현한다' 라고 한다.
public class SeparateVolume implements Lendable {
String requestNo;
String bookTitle;
String writer;
String borrower;
String checkOutDate;
byte state;
//필드에 선언된 값을 변수를 통해 들어온 값으로 변경 하도록
SeparateVolume(String requestNo, String bookTitle, String writer) {
this.requestNo = requestNo;
this.bookTitle = bookTitle;
this.writer = writer;
}
//인터페이스에서 abstract 메소드를 구현하는 클래스에서는 메소드를 public으로 해야 함
public void checkOut(String borrower, String date) {
if (state != 0)
return;
this.borrower = borrower;
this.checkOutDate = date;
this.state = 1; // 필드의 state를 바꿔야 함
System.out.println("*" + bookTitle + " 이(가) 대출되었습니다.");
System.out.println("저 자 :"+writer); //추가된 부분
System.out.println("대출인:" + borrower);
System.out.println("대출일:" + date + "\n");
}
public void checkIn() {
borrower = null;
checkOutDate = null;
state = 0;
System.out.println("*" + bookTitle + " 이(가) 반납되었습니다.\n");
}
}
/* 출력된 결과
*푸코의 진자 이(가) 대출되었습니다.
저 자 :에코
대출인:홍길동
대출일:2024-01-09
*서양미술사 이(가) 대출되었습니다.
저 자 :곰브리치
대출인:홍길동
대출일:2024-01-09
*마이크로서비스를 위한 자바 프로그래밍 이(가) 대출되었습니다.
저 자 :유시진
대출인:홍길동
대출일:2024-01-09
*/
public class InterfaceExample {
public static void main(String[] args) {
// int[] a = new int[3];
// String[] a = new String[3];
Lendable arr[] = new Lendable[3]; // 인터페이스 타입의 배열
// 배열에 SeparateVolume 타입의 여러 객체 주소 저장
arr[0] = new SeparateVolume("883ㅇ", "푸코의 진자", "에코");
arr[1] = new SeparateVolume("609.2", "서양미술사", "곰브리치");
arr[2] = new SeparateVolume("457.9", "마이크로서비스를 위한 자바 프로그래밍", "유시진");
checkOutAll(arr, "홍길동", "2024-01-09");
}
static void checkOutAll(Lendable[] arr, String borrower, String date) {
for(Lendable a : arr) {
a.checkOut(borrower, date);
}
}
}
1. 인터페이스 Movable
2. 추상 메서드
// 절대 위치로 이동한다
void moveTo(int x, int y);
// 상대 위치만큼 이동한다.
void moveBy(int xOffset, int yOffset);
==============================================================
1. interface 이름 : Transformable(부모 Movable)
2. 추상 메서드
// 크기를 변경한다
void resize(int width, int height);
==========================================================
1. 클래스 이름 : Rectangle(부모 : Transformable)
2. 생성자 : 필드 초기화(int x, int y, int width, int height)
3. 필드 :
int x, y, width, height;
4. 메서드 - 오버라이딩
resize(int width, int height)
기능) 두 개의 매개변수를 이용해 필드 초기화
5. 메서드 -오버라이딩
moveTo(int x, int y)
기능) 두 개의 매개변수를 이용해 필드 초기화
6. 메서드 -오버라이딩
moveBy(int xOffset, int yOffset)
기능) 기존 x값에 xOffset을 더해 x를 구한다.
기존 y값에 yOffset을 더해 y를 구한다.
======================================
다음 조건을 만족하는 printRectangle()메서드 완성하세요.
public static void main(String args[]) {
Rectangle obj = new Rectangle(100, 200, 10, 10);
printRectangle(obj);
obj.moveTo(25, 35);
printRectangle(obj);
obj.moveBy(-5, -5);
printRectangle(obj);
obj.resize(30, 30);
printRectangle(obj);
}
출력결과)
사각형: 위치(100, 200) 크기( 10 x 10)
사각형: 위치( 25, 35) 크기( 10 x 10)
사각형: 위치( 20, 30) 크기( 10 x 10)
사각형: 위치( 20, 30) 크기( 30 x 30)
public interface Movable {
abstract void moveTo(int x, int y);
abstract void moveBy(int xOffset, int yOffset);
}
public interface Transformable extends Movable {
abstract void resize(int width, int height);
}
public class Rectangle implements Transformable {
int x, y, width, height;
Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void moveTo(int x, int y) {
this.x = x;
this.y = y;
}
public void moveBy(int xOffset, int yOffset) {
this.x += xOffset;
this.y += yOffset;
}
public void resize(int width, int height) {
this.width = width;
this.height = height;
}
}
public class Rectangle_Main {
public static void main(String args[]) {
Rectangle obj = new Rectangle(100, 200, 10, 10);
printRectangle(obj);
obj.moveTo(25, 35);
printRectangle(obj);
obj.moveBy(-5, -5);
printRectangle(obj);
obj.resize(30, 30);
printRectangle(obj);
}
static void printRectangle(Rectangle obj) {
System.out.println("사각형: 위치(" + obj.x + ", " + obj.y + ") 크기(" + obj.width + " x " + obj.height + ")");
}
}