super
super.
를 통해 super클래스의 메서드를 간접적으로(=별도의 메서드를 정의하여) 호출 가능
super()
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() + "을 먹는다.");
}
}
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());
}
}
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) {
}
}
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());
}
}
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);
}
}
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(); // 자료형을 통해 호출 범위를 늘리거나 줄일 수 있음
}
}
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.를 이용하는 것
}
}
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의 메서드가 호출됨
}
}
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";
}
}
true
, 불가능하면 false
를 반환하는 연산자instanceof
로 검증하는 단계를 만드는 것이 안전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);
}
}
}
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");
}
}
}
final
지정
public
으로 지정final
지정
final
지정
java.lang
패키지의 String
, System
, Integer
등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);
}
}
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);
}
}
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);
}
}
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();
}
}
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();
}
}
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();
}
}
abstract
로 지정해야 함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);
}
}
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();
}
}