Study Web Development

11월 8일

이전으로

자바

7. 객체의 활용

7-3 super와 super()

  1. 새 패키지 kr.s05.supertest 생성하고(super는 예약어라 패키지명으로 사용 불가) 새 클래스 SuperMain01 생성
package kr.s05.supertest;
// 부모 클래스
class Dad {
	public String getLunch() {
		return "밥";
	}
}

// 자식 클래스
class Daughter extends Dad {
	@Override
	public String getLunch() {
		return "빵";
	}
	// 부모 클래스 영역의 getLunch 메서드를 우회적으로 호출
	public String getRice() {
		return super.getLunch();
	}
}

public class SuperMain01 {
	public static void main(String[] args) {
		Daughter d = new Daughter();
		System.out.println("딸은 " + d.getLunch() + "을 먹는다.");
		System.out.println("딸은 월요일에는 " + d.getRice() + "을 먹는다.");
	}
}
  1. 새 클래스 SuperMain02 생성
package kr.s05.supertest;
// 부모 클래스
class People {
	int a = 100;
	public People() {
		super(); // 부모 클래스인 Object의 기본 생성자 호출을 먼저 수행하여 Object 영역을 만들고, 이후 자식 클래스인 People을 초기화
	}
}

// 자식 클래스
class Student extends People {
	int b = 200;
	public Student() {
		super(); // 부모 클래스인 People의 기본 생성자 호출을 먼저 수행하여 People 영역을 만들고, 이후 자식 클래스인 Student를 초기화
	}
}

public class SuperMain02 {
	public static void main(String[] args) {
		Student s = new Student();
		System.out.println(s.a);
		System.out.println(s.b);
		System.out.println(s.toString());
	}
}
  1. 새 클래스 SuperMain03 생성
package kr.s05.supertest;
// 부모 클래스
class People2 {
	int a;

	public People2(int a) {
		this.a = a;
	}
}

// 자식 클래스
class Student2 extends People2 { // 부모 클래스의 생성자를 명시하지 않을 경우, 부모 클래스에 기본 생성자가 없어 오류 발생
	public Student2() {
		super(100); // 자식 클래스의 객체를 생성하기 위해서는 부모 클래스의 인자 자료형이 int인 생성자를 호출해야 함
	}
}

public class SuperMain03 {
	public static void main(String[] args) {
		
	}
}
  1. 새 클래스 SuperMain04 생성
package kr.s05.supertest;
// 부모 클래스
class Point {
	int x;
	int y;
	
	public Point(int x, int y) {
		this.x = x;
		this.y = y;
	}
	
	public String getLocation() {
		return "x : " + x + ", y : " + y;
	}
}

// 자식 클래스
class Point3D extends Point {
	int z;
	
	public Point3D() { // 인자가 없는 생성자
		super(10, 20); // 부모 클래스에 기본 생성자가 없으므로, 인자가 있는 생성자를 호출해야 함
		z = 7;
	}
	public Point3D(int x, int y, int z) { // 인자가 있는 생성자
		super(x, y);
		this.z = z;
	}
	
	@Override
	public String getLocation() {
		return "x : " + x + ", y : " + y + ", z : " + z;
	}
}

public class SuperMain04 {
	public static void main(String[] args) {
		Point3D p3 = new Point3D(); // 자식 클래스의 인자가 없는 생성자 호출
		System.out.println(p3.getLocation());
		
		Point3D p3new = new Point3D(10, 20, 30); // 자식 클래스의 인자가 있는 생성자 호출
		System.out.println(p3new.getLocation());
	}
}

7-4 참조 자료형 형변환 및 다형성

  1. 새 패키지 kr.s06.poly 생성하고 새 클래스 PolyMain01 생성
package kr.s06.poly;
// 부모 클래스
class Parent {
	int a = 100;
}

// 자식 클래스
class Child extends Parent {
	 int b = 200;
}

public class PolyMain01 {
	public static void main(String[] args) {
		Child ch = new Child();
		System.out.println(ch.a);
		System.out.println(ch.b);
		
		Parent p = ch; // 자식 클래스 타입에서 부모 클래스 타입으로 자동 형변환
		System.out.println(p.a);
		// System.out.println(p.b); // Parent 타입은 Parent 영역만 호출 가능(=Child 영역은 호출 범위를 벗어나 호출 불가); p는 ch를 복사했기 때문에 메모리에 있는 ch의 멤버에 접근 가능하지만, 자료형이 멤버 호출 범위를 설정하기 때문에 a에만 접근 가능하고 b에는 접근 못함

		// Child ch2 = p;
		Child ch2 = (Child)p; // 부모 클래스 타입에서 자식 클래스 타입으로 강제 형변환
		System.out.println(ch2.a);
		System.out.println(ch2.b);
	}
}
  1. 새 클래스 PolyMain02 생성
package kr.s06.poly;
// 부모 클래스
class A {
	public void make() {
		System.out.println("make 메서드");
	}
}

// 자식 클래스
class B extends A {
	public void play() {
		System.out.println("play 메서드");
	}
}

public class PolyMain02 {
	public static void main(String[] args) {
		B bp = new B();
		bp.make();
		bp.play();
		
		A ap = bp; // bp의 주소를 복사해서 ap에 전달; 업캐스팅
		ap.make(); // ap는 bp의 멤버에 접근 가능하지만, 호출 범위가 부모 영역으로 제한됨
		// ap.play(); // 호출 범위를 벗어나 호출 불가
		
		B bp2 = (B)ap; // ap의 주소를 복사해서 bp2에 전달; 다운캐스팅
		bp2.make();
		bp2.play(); // 자료형을 통해 호출 범위를 늘리거나 줄일 수 있음
	}
}
  1. 새 클래스 PolyMain03 생성
package kr.s06.poly;
// 부모 클래스
class Parent2 {
	public void make() {
		System.out.println("부모 클래스의 make 메서드");
	}
}

// 자식 클래스
class Child2 extends Parent2 {
	@Override public void make() {
		System.out.println("자식 클래스의 make 메서드");
	}
}

public class PolyMain03 {
	public static void main(String[] args) {
		Child2 ch = new Child2();
		ch.make(); // 재정의된 메서드가 호출됨
		
		Parent2 p = ch; // 자동적으로 형변환
		p.make(); // 부모 클래스 타입으로 형변환해도 자식 클래스에 메서드가 재정의되어 있으면 재정의된 메서드가 호출됨; 메서드 재정의 후 부모 클래스의 메서드를 호출하는 유일한 방법은 super.를 이용하는 것
	}
}
  1. 새 클래스 PolyMain04 생성
package kr.s06.poly;
// 부모 클래스
class Car {
	public void drive() {
		System.out.println("주행");
	}
	public void stop() {
		System.out.println("멈춤");
	}
	public void getPower() {
		System.out.println("일반 자동차");
	}
}

// 자식 클래스
class FireEngine extends Car {
	public void getWater() {
		System.out.println("물 뿌리기");
	}
	@Override
	public void getPower() {
		System.out.println("소방 설비를 갖춘 자동차");
	}
}

public class PolyMain04 {
	public static void main(String[] args) {
		FireEngine fe = new FireEngine();
		fe.drive();
		fe.stop();
		fe.getWater();
		fe.getPower();
		
		System.out.println();
		
		Car ca = new FireEngine(); // 부모 클래스 타입으로 FireEngine 객체를 생성
		ca.drive();
		ca.stop();
		// ca.getWater(); // 메모리에는 만들어져 있지만, Car 타입이라 호출 범위가 제한되어 있어 호출 불가
		ca.getPower(); // Car 타입이라도 재정의된 메서드는 FireEngine의 메서드가 호출됨
	}
}
  1. 새 클래스 PolyMain05 생성
package kr.s06.poly;
// 부모 클래스
class Product {
	int price; // 제품의 가격
	int bonusPoint; // 제품 구매시 제공하는 보너스 점수
	public Product(int price) {
		this.price = price;
		this.bonusPoint = price/10; // 보너스 점수는 제품 가격의 10%
	}
	public String getName() {
		return "상품";
	}
}

// 자식 클래스
class Tv extends Product {
	public Tv() {
		super(200);
	}
	@Override public String getName() {
		return "Tv";
	}
}

// 자식 클래스
class Computer extends Product {
	public Computer() {
		super(300);
	}
	@Override public String getName() {
		return "Computer";
	}
}

class Buyer {
	int money = 1000; // 보유 금액
	int bonusPoint; // 보유 보너스 점수
	public void buy(Product p) { // 객체의 주소를 받으려면 자료형을 명시해야 하는데, 제품 종류가 많을 경우 자료형마다 메서드 오버로딩하는 것은 번거로움; 인자 자료형을 부모 클래스 타입으로 지정시, Tv 객체나 Computer 객체 전달받을 때 Product 클래스 타입으로 자동 형변환이 일어남
		if(money < p.price) {
			System.out.println("잔액이 부족하여 물건을 구매할 수 없습니다.");
			return; // buy 메서드를 빠져나감; 기본적으로 return은 생략시 메서드 제일 끝에 컴파일러에 의해 자동으로 포함되어 메서드 종료 역할; void형 메서드에서는 if문 내에 return을 명시하여 if~else문 효과를 낼 수 있음
		}
		money -= p.price;
		bonusPoint += p.bonusPoint;
		System.out.println(p.getName() + "을/를 구매했습니다."); // getName은 재정의된 메서드이기 때문에 다시 형변환할 필요 없음
		System.out.println("현재 남은 돈은 " + money + "만원입니다.");
		System.out.println("현재 보너스 점수는 " + bonusPoint + "점입니다.");
	}
}

public class PolyMain05 {
	public static void main(String[] args) {
		Buyer b = new Buyer(); // 고객 객체 생성
		Tv tv = new Tv(); // Tv 객체 생성
		Computer pc = new Computer(); // Computer 객체 생성
		b.buy(tv); // Tv 클래스 타입에서 Product 클래스 타입으로 형변환(=업캐스팅)
		b.buy(pc); // Computer 클래스 타입에서 Product 클래스 타입으로 형변환(=업캐스팅)
		Audio au = new Audio(); // Audio 객체 생성
		b.buy(au); // Audio 클래스 타입에서 Product 클래스 타입으로 형변환(=업캐스팅)
	}
}

//자식 클래스
class Audio extends Product { // 새 클래스를 추가해도 Product를 상속받기만 하면 구매 행위 메서드를 별도로 만들 필요 없음
	public Audio() {
		super(400);
	}
	@Override
	public String getName() {
		return "Audio";
	}
}
instanceof
  1. 새 패키지 kr.s07.instanceoftest 생성하고(instanceof는 예약어라 패키지명으로 사용 불가) 새 클래스 InstanceofMain01 생성
package kr.s07.instanceoftest;

class Parent {
	@Override
	public String toString() {
		return "Parent 클래스";
	}
}

class Child extends Parent {
	@Override
	public String toString() {
		return "Child 클래스";
	}
}

public class InstanceofMain01 {
	public static void main(String[] args) {
		Parent p = new Parent();
		
		// 컴파일시에는 오류가 없으나, 실행시 오류 발생
		// Child ch = (Child)p;
		
		// 객체 p가 Child 자료형으로 캐스트할 수 있는지를 instanceof 연산자로 검증
		if(p instanceof Child) {
			Child ch2 = (Child)p;
			System.out.println(ch2);
		}
		else {
			System.out.println(p);
		}
	}
}
  1. 새 클래스 InstanceofMain02 생성
package kr.s07.instanceoftest;
// 부모 클래스
class Car {
	public void drive() {
		System.out.println("주행");
	}
	public void stop() {
		System.out.println("멈춤");
	}
}

// 자식 클래스
class FireEngine extends Car {
	public void getWater() {
		System.out.println("물 뿌리기");
	}
}

public class InstanceofMain02 {
	public static void main(String[] args) {
		FireEngine fe = new FireEngine();
		
		if(fe instanceof FireEngine) { // fe는 FireEngine 타입 사용 가능
			System.out.println("This is a FireEngine instance");
		}
		
		if(fe instanceof Car) { // fe는 Car 타입 사용 가능
			System.out.println("This is a Car instance");
		}
		
		if(fe instanceof Object) { // fe는 Object 타입 사용 가능; 모든 클래스는 Object 타입으로 형변환 가능
			System.out.println("This is an Object instance");
		}
	}
}

7-5 final

  1. 새 패키지 kr.s07.finaltest 생성하고(final은 예약어라 패키지명으로 사용 불가) 새 클래스 FinalMain01 생성
package kr.s07.finaltest;

public class FinalMain01 {
	// 멤버 상수
	final int NUM = 10;
	
	// static 상수
	public static final int NUMBER = 20;
	
	public static void main(String[] args) {
		// 지역적 상수
		final int NO = 30;
		System.out.println(NO);
		
		// 상수는 값 변경 불가
		// NO = 100;
		
		// 클래스 영역에 명시한 상수는 객체 생성 후 사용 가능
		// System.out.println(NUM);
		FinalMain01 fm = new FinalMain01();
		System.out.println(fm.NUM);
		
		// static 상수 호출; main과 NUMBER가 같은 클래스이므로 클래스명을 생략 가능
		System.out.println(NUMBER);
	}
}
  1. 새 클래스 FinalMain02 생성
package kr.s07.finaltest;

class FinalMe {
	int var = 100;
	
	// 메서드에 final을 지정하면 자식 클래스에서 메서드 오버라이딩을 할 수 없음
	public final void setVar(int var) {
		this.var = var;
	}
}

public class FinalMain02 extends FinalMe {
	/*
	@Override public void setVar(int var) {
		System.out.println(var);
	}
	*/
	public static void main(String[] args) {
		FinalMe fm = new FinalMe();
		fm.setVar(300);
		System.out.println(fm.var);
	}
}
  1. 새 클래스 FinalMain03 생성
package kr.s07.finaltest;
/* 클래스에 final을 명시하면 상속이 불가능함
final class Me2 {
	int num = 200;
}
*/
class Me2 {
	int num = 200;
}

public class FinalMain03 extends Me2 {
	public static void main(String[] args) {
		FinalMain03 fm = new FinalMain03();
		System.out.println(fm.num);
	}
}
  1. 새 패키지 kr.s08.bank 생성 후 새 클래스 BankAccount 생성
package kr.s08.bank;

public class BankAccount { // 일반 계좌
	/*
	 * [실습]
	 * 계좌번호(number), 예금주(name), 잔고(balance)
	 * 생성자를 이용하여 계좌 번호, 예금주, 잔고를 세팅
	 * 입금하기(deposit), 출금하기(withdraw : 잔고 부족 확인), 계좌 정보 보기(printAccount : 일반 계좌 번호, 예금주, 계좌 잔액)
	 */
	protected String number; // 상속을 고려하여 접근 제한자를 protected로 지정
	protected String name;
	protected int balance; // 은행 프로그램은 long을 쓰지만 예제에서 큰 수를 사용하지 않으므로 int 자료형 사용
	
	public BankAccount(String number, String name, int balance) {
		this.number=number;
		this.name=name;
		this.balance=balance;
		System.out.println(number+" 계좌가 개설되었습니다.\n");
	}
	
	public void deposit(int money) {
		if(money>=0) {
			balance+=money;
			System.out.printf("%,d원을 예금하였습니다.\n", money);
			return;
		}
		System.out.println("0원 이상을 예금하세요.");
	}
	
	public void withdraw(int money) {
		if(balance>=money) {
			balance-=money;
			System.out.printf("%,d원을 출금하였습니다.\n", money);
			return;
		}
		System.out.println("잔고가 부족합니다.");
	}
	
	public void printAccount() {
		System.out.println("계좌 번호 : 일반 "+number);
		System.out.println("예금주 : "+name);
		System.out.printf("계좌 잔액 : %,d원\n", balance);
		System.out.println();
	}
}
  1. 새 클래스 MinusAccount 생성
package kr.s08.bank;

public class MinusAccount extends BankAccount {
	/*
	 * BankAccount 상속
	 * 마이너스 한도(minusLimit)
	 * 한도를 적용한 출금하기(withdraw) 재정의
	 * 마이너스 한도 정보가 포함된 계좌 정보 보기(printAccount) 재정의
	 */
	private int minusLimit; // printAccount가 get 메서드 역할을 대신하니 별도로 Getter & Setter 만들지 않아도 됨

	public MinusAccount(String number, String name, int balance, int minusLimit) {
		super(number, name, balance);
		if(minusLimit<0) {
			this.minusLimit=-minusLimit;
		}
		else {
			this.minusLimit=minusLimit;
		}
	}

	@Override public void withdraw(int money) {
		if(balance+minusLimit>=money) {
			balance-=money;
			System.out.printf("%,d원을 출금하였습니다.\n", money);
			return;
		}
		System.out.println("마이너스 한도 초과로 출금되지 않습니다.");
	}

	@Override public void printAccount() {
		System.out.println("계좌 번호 : 마이너스 "+number);
		System.out.println("예금주 : "+name);
		System.out.printf("계좌 잔액 : %,d원\n", balance);
		System.out.printf("마이너스 한도 : %,d원\n", minusLimit);
		System.out.println();
	}
}
  1. 새 클래스 BankMain 생성
package kr.s08.bank;

public class BankMain {
	public static void main(String[] args) {
		/*
		 * 마이너스 계좌 생성
		 * 입출금 및 정보 출력
		 */
		/* 입력을 받아 마이너스 계좌를 생성
		java.util.Scanner input = new java.util.Scanner(System.in);
		
		System.out.println("계좌 기본 정보를 입력하세요.");
		System.out.print("계좌 번호 > ");
		String number = input.nextLine();
		System.out.print("예금주명 > ");
		String name = input.nextLine();
		System.out.print("계좌 잔액 > ");
		int balance = input.nextInt();
		System.out.print("마이너스 한도 > ");
		int minusLimit = input.nextInt();
		
		input.close();
		
		MinusAccount ma = new MinusAccount(number, name, balance, minusLimit);
		*/
		MinusAccount ma = new MinusAccount("110-1234", "홍길동", 1000, 500);
		
		ma.printAccount();
		
		ma.deposit(2000);
		ma.printAccount();
		
		ma.withdraw(3100); // 계좌 잔고 -500이 될 때까지는 출금 가능
		ma.printAccount();
		
		ma.withdraw(500); // 한도 초과로 출금 불가
		ma.printAccount();
	}
}

7-6 추상 클래스

  1. 새 패키지 kr.s09.abstracttest 생성하고(abstract는 예약어라 패키지명에 사용 불가) 새 클래스 AbstractMain01 생성
package kr.s09.abstracttest;
// 추상 클래스
abstract class A {
	private int x;
	public void setX(int x) {
		this.x = x;
	}
	public int getX() {
		return x;
	}
}

// 추상 클래스가 부모 클래스 역할을 수행
class B extends A {
	int b = 200;
}

public class AbstractMain01 {
	public static void main(String[] args) {
		// 추상 클래스는 객체 생성이 불가능
		// A ap = new A();
		
		B bp = new B();
		bp.setX(100);
		System.out.println(bp.getX());
		System.out.println(bp.b);
	}
}
  1. 새 클래스 AbstractMain02 생성
package kr.s09.abstracttest;
// 일반 클래스에서는 추상 메서드를 만들 수 없음
abstract class A2 {
	// 추상 메서드
	public abstract void getA();
	
	// 일반 메서드; {} 블럭이 있으면 (수행문이 없더라도) 메서드가 구현된 것
	public void make() {
		System.out.println("make 메서드");
	}
}

// 추상 클래스를 상속받으면 추상 메서드를 자식 클래스에 구현해야 함
class B2 extends A2 {
	// 재정의를 통해 부모 클래스의 미구현된 추상 메서드가 호출되지 않도록 함
	@Override public void getA() {
		System.out.println("getA 메서드");
	}
}

public class AbstractMain02 {
	public static void main(String[] args) {
		B2 bp = new B2();
		bp.getA();
		bp.make();
	}
}

다음으로