예외처리는 프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료되는 경우가 있다. 이러한 결과를 초래하는 원인을 프로그램 에러 또는 오류라고 한다.

이를 발생 시점에 따라 '컴파일 에러(compile-ime error)'와 '런타임 에러(runtime error)'로 나눌 수 있는데, 글자 그대로 컴파일 에러는 컴파일할 때 발생하는 에러이고 프로그램의 실행 도중에 발생하는 에러를 '런타임 에러'라고 한다. 이 외에도 '논리적 에러(logical error)'가 있는데, 컴파일도 잘되고 실행도 잘되지만 의도한 것과 다르게 동작하는 것을 말한다.

컴파일 에러 : 컴파일 시에 발생하는 에러
런타임 에러 : 실행 시에 발생하는 에러
논리적 에러 : 실행은 되지만, 의도와 다르게 동작하는 것

소스코드를 하면 컴파일러가 컴파일 에러는 잡아주지만 일부 런타임 에러와 논리적 에러는 실행하기 전까지 인지하기 힘들거나 실행시켜도 잠재적인 오류를 검사할 수 없기 때문에 실행 중에 에러에 의해 잘못된 결과를 얻거나 프로그램이 비정상적으로 종료될 수 있다.

따라서 프로그래머는 가급적 프로그램의 실행 도중 발생할 수 있는 모든 경우의 수를 고려하여 이에 대한 대비를 하는 것이 필요하다.

자바에서 에러는 메모리 부족(OutOfMemoryError)이나 스택오버플로우(StackOverflowError)와 같이 일단 발생하면 복구할 수 없는 심각한 오류와, 예외는 발생하더라도 수습될 수 있는 비교적 덜 심각한 것이다.

에러(error)	: 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
예외(exception)	: 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류

 

 

예외 클래스의 계층구도

 

예외 클래스들은 다음과 같이 두 그룹으로 나눠질 수 있다.

1 Exception클래스와 그 자손들 (Exception 클래스들)
2 RuntimeException클래스와 그 자손들 (RuntimeException 클래스들)

 

RuntimeException클래스들은 주로 프로그래머의 실수에 의해서 발생될 수 있는 예외들로 자바의 프로그래밍 요소들과 관계가 깊다.

 

Exception클래스들은 주로 외부의 영향으로 발생할 수 있는 것들로서, 프로그램의 사용자들의 동작에 의해서 발생하는 경우가 많다.

Exception클래스들	사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외
RuntimeException클래스들	프로그래머의 실수로 발생하는 예외

 

 

 

예외 처리하기 try catch문

프로그램의 실행 도중에 발생하는 에러는 어쩔 수 없지만, 예외는 프로그래머가 이에 대한 처리를 미리 해주어야 한다.

예외처리(exception handling)란, 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것이며, 예외처리의 목적은 예외의 발생으로 인한 실행 중인 프로그램의 갑작스러운 비정상 종료를 막고, 정상적인 실행상태를 유지할 수 있도록 하는 것이다.

예외처리(exception handling)의
	정의 - 프로그램 실행 시 발생할 수 있는 예외에 대비한 코드를 작성하는 것
    목적 - 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것

 

 

public class ExceptionEx03 {
	public static void main(String[] args) {
		int number = 100;
		int result = 0;
		
		for(int i=0; i < 10; i++) {
			try {
				result = number / (int)(Math.random()*10);
				System.out.println(result);
			} catch (ArithmeticException e) {
				System.out.println("0");
			}
		}
	}
}
25
16
50
33
25
0
20
0
16
12

위의 예제는 ArithmeticException이 발생했을 경우에는 0을 화면에 출력하도록 했다.

만약 예외처리가 없었다면 0이 출력되는 위치에서 프로그램이 비정상적 종료가 됐을 것이다.

 

 

try-catch문에서의 흐름

try-catch문에서, 예외가 발생한 경우와 발생하지 않았을 때의 흐름(문장의 실행 순서)이 달라진다.

-try블럭 내에서 예외가 발생한 경우,
1. 발생한 예외와 일치하는 catch블럭이 있는지 확인한다.
2. 일치하는 catch블럭을 찾게 되면, 그 catch블럭 내의 문장들을 수행하고 전체try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다.
만일 일치하는 catch블럭을 찾지 못하면, 예외는 처리되지 못한다.

-try블럭 내에서 예외가 발생하지 않은 경우,
1. catch블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.

 

 

public class ExceptionEx05 {
	public static void main(String[] args) {
		System.out.println(1);
		System.out.println(2);
		try {
			System.out.println(3);
			System.out.println(0 / 0);
			System.out.println(4);
		} catch (ArithmeticException ae) {
			System.out.println(5);
		}
		System.out.println(6);
	}
}

위의 예제의 결과를 보면 1,2,3을 출력한 다음 try블럭에서 예외가 발생했기 때문에 try블록을 바로 벗어나서 System.out.print(4);는 실행되지 않는다. 그리고는 발생한 예외에 해당하는 catch블록으로 이동하여 문장들을 수행한다. 다음엔 전체 try-catch문을 벗어나서 그 다음 문장을 실행하여 6을 화면에 출력한다.

try블럭에서 예외가 발생하면, 예외가 발생한 위치 이후에 있는 try블럭의 문장들은 수행되지 않으므로, try블럭에 포함시킬 코드의 범위를 잘 선택해야 한다.

 

 

예외의 발생과 catch블럭

catch블럭은 괄호()와 블럭{} 두 부분으로 나눠져 있는데, 괄호()내에는 처리하고자 하는 예외와 같은 타입의 참조변수를 하나를 선언해야 한다.

예외가 발생하면, 발생한 예외에 해당하는 클래스의 인스턴스가 만들어진다. try부터 내려가다가 검사결과가 true인 catch블럭을 찾게 되면, 블럭에 있는 문장들을 모두 수행한 후에 try-catch문을 빠져나가고 예외는 처리되지만, 검사결과가 true인 catch블럭이 하나도 없으면 예외는 처리되지 않는다.

모든 예외 클래스는 Exception클래스의 자손이므로, catch블럭의 괄호()안에 Exception클래스 타입의 참조변수를 선언해 놓으면 어떤 종류의 예외가 발생하더라도 이 catch블럭에 의해서 처리된다.

 

 

 

PrintStackTrace()와 getMssage()

예외가 발생햇을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨져 있으며,  getMessage()와 printStackTrace()를 통해서 이 정보들을 얻을 수 있다.

catch블럭의 괄호()에 선언된 참조변수를 통해 이 인스턴스에 접근할 수 있다. 이 참조변수는 선언된 catch블럭 내에서만 사용 가능하며, 자주 사용되는 메서드는 다음과 같다.

printStackeTrace() 예외발생 당시의 호출스택(Call Stack)에 있었던 메서드의 정보와 예외 메세지를 화면에 출력한다
getMessage() 발생한 예외클래스의 인스턴스에 저장된 메세지를 얻을 수 있다.

 

 

 

멀티 catch블럭

JDK.1.7부터 여러 catch블럭을 '|'기호를 이용해서, 하나의 catch블럭으로 합칠 수 있게 되었으며, 이를 '멀티 catch블럭'이라 한다. 아래의 코드에서 알 수 있듯이 '멀티 catch블럭'을 이용하면 중복된 코드를 줄일 수 있다. 그리고'|'기호로 연결할 수 있는 예외 클래스의 개수에는 제한이 없다.

멀티 catch블럭에 사용되는 '|'는 논리연산자가 아니라 기호이다.

내부 클래스는 클래스 내에 선언된다는 점을 제외하고는 일반 클래스와 다르지 않다.

 

내부 클래스는 클래스 내에 선언된 클래스이다. 클래스에 다른 클래스를 선언하는 이유는 두 클래스가 긴밀한 관계에 있기 때문이다.

한 클래스를 다른 클래스의 내부 클래스로 선언하면 두 클래스의 멤버들 간에 서로 쉽게 접근할 수 있다는 장점과 외부에는 불필요한 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다는 장점을 얻을 수 있다.

내부 클래스의 장점
 - 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
 - 코드의 복잡성을 줄일 수 있다(캡슐화).

내부 클래스는 바로 바깥에 있는 외부 클래스를 제외하고는 다른 클래스에서 잘 사용되지 않는 것이어야 한다.

 

 

 

내부 클래스의 종류와 특징

내부 클래스의 종류는 변수의 선언위치에 따른 종류와 같다. 내부 클래스는 마치 변수를 선언하는 것과 같은 위치에 선언할 수 있으며, 변수의 선언 위치에 따라 인스턴스 변수, 클래스 변수(static변수), 지역변수로 구분되는 것과 같이 내부 클래스도 선언 위치에 따라 다음과 같이 구분되어진다.

 

내부 클래스 특 징
인스턴스 클래스
(instance class)
외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 인스턴스멤버처럼 다루어진다. 주로 외부 클래스의 인스턴스멤버들과 관련된 작업에 사용될 목적으로 선언된다.
스태틱 클래스
(static class)
외부 클래스의 멤버변수 선언위치에 선언하며 외부 클래스의 static멤버처럼 다루어진다. 주로 외부 클래스의 static멤버, 특히 static메서드에서 사용될 목적으로 선언된다.
지역 클래스
(local class)
외부 클래스의 메서드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있다.
익명 클래스
(anonymous class)
클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용)

 

 

 

내부 클래스의 선언

변수가 선언된 위치에 따라 인스턴스 변수, 클래스 변수(static변수), 지역변수로 나뉘듯이 내부 클래스도 이와 마찬가지로 선언된 위치에 따라 나뉜다. 그리고, 각 내부 클래스의 선언 위치에 따라 같은 선언 위치의 변수와 동일한 유효 범위(scope)와 접근성(accessibility)을 갖는다.

 

 

 

내부 클래스의 제어자와 접근성

아래 코드에서 인스턴스 클래스(InstanceInner)와 스태틱 클래스(StaticInner)는 외부 클래스(Outer)의 멤버 변수(i인스턴스 변수와 클래스 변수)와 같은 위치에 선언되며, 또한 멤버 변수와 같은 성질을 갖는다. 따라서 내부 클래스가 외부 클래스의 멤버와 같이 간주되고, 인스턴스 멤버와 static멤버 간의 규칙이 내부 클래스에도 똑같이 적용된다.

내부 클래스 중에서 스태틱 클래스(StaticInner)만 static멤버를 가질 수 있다. 드문 경우지만 내부 클래스에 static변수를 선언해야 한다면 스태틱 클래스로 선언해야 한다.

다만 final과 static이 동시에 붙은 변수는 상수(constant)이므로 모든 내부 클래스에서 정의가 가능하다.

인스턴스 멤버는 같은 클래스에 있는 인스턴스 멤버와 static멤버 모두 직접 호출이 가능하지만, static멤버는 인스턴스 멤버를 직접 호출할 수 없는 것처럼, 인스턴스클래스는 외부 클래스의 인스턴스멤버를 객체 생성 없이 바로 사용할 수 있지만, 스태틱 클래스는 외부클래스의 인스턴스멤버를 객체생성 없이 사용할 수 있다.

 

 

익명 클래스(anonymous class)

익명 클래스는 특이하게도 다른 내부 클래스들과는 달리 이름이 없다. 클래스의 선언과 객체의 생성을 동시에 하기 때문에 단 한 번만 사용될 수 있고 오직 하나의 객체만을 생성할 수 있는 일회용 클래스이다.

public class InnerEx6 {
	Object iv = new Object () { void method() {} };
	static Object cv = new Object() { void method() {} };
	
	void myMethod() {
		Object lv = new Object() { void method() {} };
	}
}

이 예제를 컴파일하면 다음과 같이 4개의 클래스 파일이 생성된다.

InnerEx6.class

InnerEx6$1.class > 익명 클래스

InnerEx6$2.class > 익명 클래스

InnerEx6$3.class > 익명 클래스

익명 클래스는 이름이 없기 때문에 '외부 클래스명$숫자.class'의 형식으로 클래스파일명이 결정된다.

트리 구조 그래프의 일종으로, 여러 노드가 한 노드를 가리킬 수 없는 구조이다. 간단하게는 회로가 없고, 서로 다른 두 노드를 잇는 길이 하나뿐인 그래프를 트리라고 부른다.

트리에서 최상위 노드 : 루트 노드(root node 뿌리 노드[*]) 
노드 A가 노드 B를 가리킬 때 : A를 부모 노드(parent node)
노드 A가 노드 B를 가리킬 때 : B를 자식 노드(child node) 
자식 노드가 없는 노드(최하단 노드) : 리프노드(leaf node) 
잎 노드가 아닌 노드 : 내부 노드(internal node)

 

 

 

 

 

 

 

 

레드-블랙 트리(Red-black tree)

레드-블랙 트리(Red-black tree) 자가 균형 이진 탐색 트리(self-balancing binary search tree)로서, 대표적으로는 연관 배열 등을 구현하는 데 쓰이는 자료구조다. 실 사용에서 효율적이고, 최악의 경우에도 상당히 우수한 실행 시간을 보인다: 트리에 n개의 원소가 있을 때 O(log n)의 시간복잡도로 삽입, 삭제, 검색을 할 수 있다.

 

용도와 장점

레드-블랙 트리는 자료의 삽입과 삭제, 검색에서 최악의 경우에도 일정한 실행 시간을 보장한다(worst-case guarantees). 이는 실시간 처리와 같은 실행시간이 중요한 경우에 유용하게 쓰일 뿐만 아니라, 일정한 실행 시간을 보장하는 또 다른 자료구조를 만드는 데에도 쓸모가 있다. 예를 들면, 각종 기하학 계산에 쓰이는 많은 자료 구조들이 레드-블랙 트리를 기반으로 만들어져 있다.

AVL 트리는 레드-블랙 트리보다 더 엄격하게 균형이 잡혀 있기 때문에, 삽입과 삭제를 할 때 최악의 경우에는 더 많은 회전(rotations)이 필요하다.

레드-블랙 트리는 함수형 프로그래밍에서 특히 유용한데, 함수형 프로그래밍에서 쓰이는 연관 배열이나 집합(set)등을 내부적으로 레드-블랙 트리로 구현해 놓은 경우가 많다. 이런 구현에는 삽입, 삭제시 O(log n)만큼의 시간이 필요하다.

레드-블랙 트리는 2-3-4 트리 등장변환이 가능하다(isometry). 다시 말해서, 모든 2-3-4 트리에는 구성 원소와 그 순서(order)가 같은 레드-블랙 트리가 최소한 하나 이상 존재한다는 말이다. 2-3-4 트리에서의 삽입, 삭제 과정은 레드-블랙 트리에서의 색 전환(color-flipping)과 회전(rotation)과 같은 개념이다. 그러므로 실제로는 잘 쓰이지 않지만 2-3-4 트리는 레드-블랙 트리의 동작 과정(logic)을 이해하는 데 많은 도움을 주기 때문에 많은 알고리즘 교과서들이 레드-블랙 트리가 나오기 바로 전에 2-3-4 트리를 소개하고 있다.

 

특성(Properties)

1. 노드는 레드 혹은 블랙 중의 하나이다.
2. 루트 노드는 블랙이다.
3. 모든 리프 노드들(NIL)은 블랙이다.
4. 레드 노드의 자식노드 양쪽은 언제나 모두 블랙이다. 
(즉, 레드 노드는 연달아 나타날 수 없으며, 블랙 노드만이 레드 노드의 부모 노드가 될 수 있다)
5. 어떤 노드로부터 시작되어 그에 속한 하위 리프 노드에 도달하는 모든 경로에는 리프 노드를 제외하면 
모두 같은 개수의 블랙 노드가 있다.

 

출처 : 위키백과

https://ko.wikipedia.org/wiki/%EB%A0%88%EB%93%9C-%EB%B8%94%EB%9E%99_%ED%8A%B8%EB%A6%AC

 

레드-블랙 트리 - 위키백과, 우리 모두의 백과사전

레드-블랙 트리(Red-black tree)는 자가 균형 이진 탐색 트리(self-balancing binary search tree)로서, 대표적으로는 연관 배열 등을 구현하는 데 쓰이는 자료구조다. 1978년 레오 귀바스(Leo J. Guibas)와 로버트

ko.wikipedia.org

 

 

'JAVA준비 > 배경지식' 카테고리의 다른 글

php jsp asp asp.net(c#)  (0) 2021.07.28
엑셀파일을 txt로 여는법+(XML, JSON, CSV)  (0) 2021.07.28
파일 할당 테이블fat  (0) 2021.07.23
MVC모델-뷰-컨트롤러  (0) 2021.07.15
트래픽 사이트)스탯카운터  (0) 2021.07.08

예제 7-1

public class CaptionTvTest {
	public static void main(String[] args) {
		CaptionTv ctv = new CaptionTv();
		ctv.power();
		ctv.channel = 10;
		ctv.channelUp();
		System.out.println(ctv.channel);
		ctv.display("Hello World");
		ctv.caption = true;
		ctv.display("Hello World2");
	}

}

class Tv {
	boolean power;
	int channel;
	
	void power() {power = !power;}
	void channelUp() {channel++;}
	void channelDown() {channel--;}
}
class CaptionTv extends Tv {
	boolean caption;
	void display(String text) {
		if(caption) {
			System.out.println(text);
		}
	}
}
11
Hello World2

 

 

 

예제7-2

public class DrawShape {
	public static void main(String[] args) {
		Point[] p = {	
				new Point(100, 100),
				new Point(140, 50),
				new Point(200, 100)
		};
		Triangle t = new Triangle(p);
		Circle c = new Circle(new Point(150, 150), 50);
		
		t.draw();
		c.draw();
	}

}

class Shape {
	String color = "black";
	void draw() {
		System.out.printf("[color=%s]%n", color);
	}
}

class Point {
	int x;
	int y;

	Point(int x, int y) {
		this.x = x;
		this.y = y;
	}

	Point() {
		this(0, 0);
	}

	String getXY() {
		return "(" + x + "," + y + ")"; // x와 y의 값을 문자열로 반환
	}
}

class Circle extends Shape {
	Point center;
	int r;

	Circle() {
		this(new Point(0, 0), 100);
}
	Circle(Point center, int r) {
		this.center = center;
		this.r = r;
	}
	
	void draw() {
		System.out.printf("[center=(%d, %d), r=%d, color=%s]%n", center.x, center.y, r, color);
	}
}

class Triangle extends Shape {
	Point[] p = new Point[3];
	
	Triangle(Point[] p) {
		this.p = p;
	}
	
	void draw() {
		System.out.printf("[p1=%s, p2=%s, p3=%s, color=%s]%n",
				p[0].getXY(),p[1].getXY(), p[2].getXY(), color);
	}
}
[p1=(100,100), p2=(140,50), p3=(200,100), color=black]
[center=(150, 150), r=50, color=black]

 

 

 

예제7-3

public class DeckTest {
	public static void main(String[] args) {
		Deck d = new Deck();
		Card c = d.pick();
		System.out.println(c);

		d.shuffle();
		c = d.pick(0);
		System.out.println(c);

	}
}

class Deck {
	final int CARD_NUM = 52;
	Card cardArr[] = new Card[CARD_NUM];

	Deck() {
		int i = 0;
		for (int k = Card.KIND_MAX; k > 0; k--) {	// 4
			for (int n = 0; n < Card.NUM_MAX; n++) {	//13
				cardArr[i++] = new Card(k, n + 1);
			}
		}
		
//		for(int i =0 ; i < CARD_NUM ; i++) {
//			cardArr[i++] = new Card(Card.KIND_MAX-i/Card.NUM_MAX, i%Card.NUM_MAX+1);
//		}
	}
		// cardArr[10] ?? 무늬, 숫자
		// diamond J 23
		// heart J 36
		//Clover J 49 50 51

	Card pick(int index) {
		return cardArr[index];
	}

	Card pick() {
		return pick((int) (Math.random() * CARD_NUM));
	}

	void shuffle() {
		for (int i = 0; i < cardArr.length; i++) {
			int r = (int) (Math.random() * CARD_NUM);

			Card temp = cardArr[i];
			cardArr[i] = cardArr[r];
			cardArr[r] = temp;

		}
	}
}

class Card {
	static final int KIND_MAX = 4;
	static final int NUM_MAX = 13;

	static final int SPADE = 4;
	static final int DIAMOND = 3;
	static final int HEART = 2;
	static final int CLOVER = 1;

	int kind;
	int number;

	Card() {
		this(SPADE, 1);
	}

	Card(int kind, int number) {
		this.kind = kind;
		this.number = number;
	}

	public String toString() {
		String[] kinds = { "", "CLOVER", "HEART", "DIAMOND", "SPADE" };
		String numbers = "0123456789XJQK"; // 숫자 10은 X로 표현
		return "kind : " + kinds[this.kind] + ", number : " + numbers.charAt(this.number);
	} // toString()의 끝
} // Card클래스의 끝
kind : SPADE, number : X
kind : DIAMOND, number : K

 

 

 

예제 7-4

class Tv {
	boolean power;
	int channel;
	
	void power()	{ power = !power; }
	void channelUP() { ++channel; }
	void channelDown() { --channel; }
}

class VCR {
	boolean power;
	int counter = 0;
	void power() {	power = !power; }
		void play() { }
		void stop() { }
		void rew() { }
		void ff() { }
	}

class TVCR extends Tv {
	VCR vcr = new VCR();
	
	void play() {
		vcr.play();
	}
	
	void stop() {
		vcr.stop();
	}
	void rew() {
		vcr.rew();
	}
	void ff() {
		vcr.ff();
	}
}

 

 

 

예제7-5

public class SuperTest {
	public static void main(String[] args) {
		Child c = new Child();
		c.method();
	}
}

class Parent {
	int x=10;
}

class Child extends Parent {
	void method() {
		System.out.println("x=" + x);
		System.out.println("this.x=" + this.x);
		System.out.println("super.x="+ super.x);
	}
}
x=10
this.x=10
super.x=10

 

 

 

예제7-6

public class SuperTest2 {
	public static void main(String[] args) {
		Child c = new Child();
		c.method();
	}
}

class Parent {
	int x=10;
}

class Child extends Parent {
	int x=20;
	void method() {
		System.out.println("x=" + x);
		System.out.println("this.x=" + this.x);
		System.out.println("super.x="+ super.x);
	}
}
x=20
this.x=20
super.x=10

 

 

 

예제7-7

public class PointTest {
	public static void main(String[] args) {
		Point3D p3 = new Point3D(1,2,3);
	}
}

class Point {
	int x, y;
	
	Point(int x, int y) {
		this.x = x;
		this.y = y;
	}
	
	String getLocation() {
		return "x :" + x + ", y :"+ y;
	}
}

class Point3D extends Point {
	int z;
	
	Point3D(int x, int y, int z) {
		
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	String getLocation() {
		return "x :" + x + ", y :" + y + ", z :" + z;
	}
}
Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	Implicit super constructor Point() is undefined. Must explicitly invoke another constructor

	at mypack.Point3D.<init>(PointTest.java:25)
	at mypack.PointTest.main(PointTest.java:5)

 

 

 

예제7-8

class PointTest2 {
	public static void main(String[] args) {
		Point3D p3 = new Point3D();
		System.out.println("p3.x="+ p3.x);
		System.out.println("p3.y="+ p3.y);
		System.out.println("p3.z="+ p3.z);

	}

}

class Point {
	int x = 10;
	int y = 20;

	Point(int x, int y) {

		this.x = x;
		this.y = y;
	}
}

class Point3D extends Point {
	int z = 30;

	Point3D() {
		this(100, 200, 300);
	}

	Point3D(int x, int y, int z) {
		super(x, y);
		this.z = z;
	}
}
p3.x=100
p3.y=200
p3.z=300

 

 

 

예제7-9

pacakge com.codechobo.book;

public class PackageTest {
	public static void main(String[] args) {
		System.out.println("Hello World!");
	}
}

 

 

 

예제7-10

public class ImportTest {
	public static void main(String[] args) {
		Date today = new Date();
		
		SimpleDateFormat date = new SimpleDateFormat("yyyy/mm/dd");
		SimpleDateFormat time = new SimpleDateFormat("hh:mm:ss a");
		
		System.out.println("오늘 날짜는 " + date.format(today));
		System.out.println("현재 시간은 " + time.format(today));
	}
}
오늘 날짜는 2021/30/26
현재 시간은 05:30:19 오전

 

 

 

예제7-11

import static java.lang.System.out;
import static java.lang.Math.*;

public class StaticImportEx1 {
	public static void main(String[] args) {
//		System.out.println(Math.random());
		out.println(random());
		
//		System.out.println("Math.PI :"+Math.PI);
		out.println("Math.PI :" + PI);
	}
}
0.16729015202127595
Math.PI :3.141592653589793

 

 

 

예제7-12

class Card {
	final int NUMBER;
	final String KIND;
	static int width = 100;
	static int height = 250;

	Card(String kind, int num) {
		KIND = kind;
		NUMBER = num;

	}

	Card() {
		this("HEART", 1);
	}

	public String toString() {
		return KIND + " " + NUMBER;
	}
}

class FinalCardTest {
	public static void main(String[] args) {
		Card c = new Card("HEART", 10);

//	c.NUMBER = 5;
		System.out.println(c.KIND);
		System.out.println(c.NUMBER);
		System.out.println(c);
	}
}
HEART
10
HEART 10

 

 

 

예제7-13

public class TimeTest {
	public static void main(String[] args) {
		Time t = new Time(12, 35, 30);
		System.out.println(t);
//		t.hour = 13;
		t.setHour(t.getHour() + 1); // 13을들고오고싶으면 getHour 바꾸고싶으면 setHour
		System.out.println(t);
	}
}

// VO, Component
class Time {
	private int hour, minute, second;
	boolean power;

	public Time(int hour, int minute, int second) {
		super();
		this.hour = hour;
		this.minute = minute;
		this.second = second;
	}
	public boolean isPower() {
		return power;
	}
	public void setPower(boolean power) {
		this.power = power;
	}
	public int getHour() {	return hour; }
	public void setHour(int hour) {
		
		if(hour < 0 || hour > 23) return;
		this.hour = hour;
	}
	
	public int getMinute() {	return minute; }
	public void setMinute(int minute) {
		
		if(minute < 0 || minute > 59) return;
		this.minute = minute;
	}
	public int getSecond() {	return second;}
	public void setSecond(int second) {
		if (second < 0 || second > 59) return;
		this.second = second;
	}
	public String toString() {
		return hour + ":" + minute + ":" + second;
	}

	}
12:35:30
13:35:30

 

 

 

예제7-14

final class Singleton {
	private static Singleton s = new Singleton();
	
	private Singleton() {
		//...
	}
	
	public static Singleton getInstance() {
		if(s==null)
			s = new Singleton();
		
		return s;
	}
}

class SingletonTest{
	public static void main(String[] args) {
//		Singleton s = new Singleton();
		Singleton s = Singleton.getInstance();
	}
}

 

 

 

예제7-15

public class CastingTest1 {
	public static void main(String[] args) {
		Car car = null;
		FireEngine fe = new FireEngine();
		FireEngine fe2 = null;

		fe.water();
		car = (Car)fe;	//Car타입의 참조형변수라고 명시하지 않아도 된다.
//		car.water();
		fe2 = (FireEngine) car;
		fe2.water();
	}
}

class Car {
	String color;
	int door;

	void drive() {
		System.out.println("drive, brrrr~");
	}

	void stop() {
		System.out.println("stop!!!");
	}
}

class FireEngine extends Car {
	void water() {
		System.out.println("water!!!");
	}
}
water!!!
water!!!

 

예제7-16

class CastingTest2 {
	public static void main(String[] args) {
		Car car = new Car();
		Car car2 = null;
		FireEngine fe = null;
		
		car.drive();
		fe = (FireEngine)car;
		fe.drive();
		car2 = fe;
		car2.drive();
	}
}
drive, brrrr~
Exception in thread "main" java.lang.ClassCastException: a210719.Car cannot be cast to a210719.FireEngine
	at a210719.CastingTest2.main(CastingTest2.java:10)

 

 

예제7-17

public class InstanceofTest {
	public static void main(String[] args) {
		FireEngine fe = new FireEngine();
		
		if(fe instanceof FireEngine) {
			System.out.println("This is a FireEngine instance.");
		}
		
		if(fe instanceof Car) {
			System.out.println("This is a Car instance.");
		}
		
		if(fe instanceof Object) {
			System.out.println("This is an Object instance.");
		}
		System.out.println(fe.getClass().getName());
	}
}// class
class Car {}
class FireEngine extends Car{}
This is a FireEngine instance.
This is a Car instance.
This is an Object instance.
FireEngine

 

 

 

예제7-18

public class BindingTest {
	public static void main(String[] args) {
		Parent p = new Child();
		Child c = new Child();
		
		System.out.println("p.x = " + p.x);
		p.method();
		
		System.out.println("c.x = " + c.x);
		c.method();
	}

}

class Parent {
	int x = 100;
	
	void method() {
		System.out.println("Parent Method");
	}
}

class Child extends Parent {
	int x = 200;
	
	void method() {
		System.out.println("Child Method");
	}
}
p.x = 100
Child Method
c.x = 200
Child Method

 

 

 

예제7-19

class BindingTest2 {
	public static void main(String[] args) {
		Parent p = new Child();
		Child c = new Child();

		System.out.println("p.x = " + p.x);
		p.method();

		System.out.println("c.x = " + c.x);
		c.method();
	}

}

class Parent {
	int x = 100;

	void method() {
		System.out.println("Parent Method");
	}
}
class Child extends Parent {
}
p.x = 100
Parent Method
c.x = 100
Parent Method

 

 

 

예제7-20

class BindingTest3 {
	public static void main(String[] args) {
		Parent p = new Child();
		Child c = new Child();

		System.out.println("p.x = " + p.x);
		p.method();
		System.out.println();
		System.out.println("c.x = " + c.x);
		c.method();
	}
}

class Parent {
	int x = 100;

	void method() {
		System.out.println("Parent Method");
	}
}

class Child extends Parent {
	int x = 200;

	void metohd() {
		System.out.println("x=" + x);
		System.out.println("super.x=" + super.x);
		System.out.println("this.x=" + this.x);
	}
}
p.x = 100
Parent Method

c.x = 200
Parent Method

 

 

 

예제7-21

class Product{
	int price;
	int bonusPoint;
	
	Product(int price) {
		this.price = price;
		bonusPoint = (int)(price/10.0);
	}
}
class Tv extends Product {
	Tv() {
		super(100);
	}
	public String toString() { return "Tv"; }
}
class Computer extends Product {
	Computer() { super(200); }
	
	public String toString() { return "Computer"; }
}

class Buyer {
	int money = 1000;
	int bonusPoint = 0;
	
	void buy(Product p) {
		if(money < p.price) {
			System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
			return;
		}
		
		money -= p.price;
		bonusPoint += p.bonusPoint;
		System.out.println(p + "을/를 구입하셨습니다.");
	}
}
class PolyArgumentTest {
	public static void main(String[] args) {
		Buyer b = new Buyer();
		
		b.buy(new Tv());
		b.buy(new Computer());
		
		System.out.println("현재 남은 돈은 " + b.money + "만원입니다.");
		System.out.println("현재 보너스 점수는" + b.bonusPoint + "점입니다.");
		
	}
}
Tv을/를 구입하셨습니다.
computer을/를 구입하셨습니다.
현재 남은 돈은 700만원입니다.
현재 보너스 점수는30점입니다.

 

 

예제7-22

class Product {
	int price;
	int bonusPoint;

	Product(int price) {
		this.price = price;
		bonusPoint = (int) (price / 10.0);
	}

	Product() {
	}
}

class Tv extends Product {
	Tv() {
		super(100);
	}

	public String toString() {
		return "Tv";
	}
}

class Computer extends Product {
	Computer() {
		super(200);
	}

	public String toString() {
		return "Computer";
	}
}

class Audio extends Product {
	Audio() {
		super(50);
	}

	public String toString() {
		return "Audio";
	}
}

class Buyer {
	int money = 1000;
	int bonusPoint = 0;
	Product[] item = new Product[10];
	int i = 0;

	void buy(Product p) {
		if (money < p.price) {
			System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
			return;
		}

		money -= p.price;
		bonusPoint += p.bonusPoint;
		item[i++] = p;
		System.out.println(p + "을/를 구입하셨습니다.");
	}

	void summary() {
		int sum = 0;
		String itemList = "";

		for (int i = 0; i < item.length; i++) {
			if (item[i] == null)
				break;
			sum += item[i].price;
			itemList += item[i] + ", ";
		}

		System.out.println("구입하신 물품의 총금액은 " + sum + "만원입니다.");
		System.out.println("구입하신 제품은 " + itemList + "입니다.");
	}
}

class PolyArgumentTest2 {
	public static void main(String args[]) {
		Buyer b = new Buyer();

		b.buy(new Tv());
		b.buy(new Computer());
		b.buy(new Audio());
		b.summary();
	}
}
Tv을/를 구입하셨습니다.
Computer을/를 구입하셨습니다.
Audio을/를 구입하셨습니다.
구입하신 물품의 총금액은 350만원입니다.
구입하신 제품은 Tv, Computer, Audio, 입니다.

 

 

 

예제7-23

import java.util.*;
	class Product {
		int price;
		int bonusPoint;
		
		Product(int price) {
			this.price = price;
			bonusPoint = (int)(price/10.0);
		}
		
		Product() {
			price = 0;
			bonusPoint = 0;
		}
	}



class Tv extends Product {
	Tv() { super(100); }
	public String toString() { return "Tv"; }
}

class Computer extends Product {
	Computer() { super(200); }
	public String toString() { return "computer";	}
}

class Audio extends Product {
	Audio() {	super(50);	}
	public String toString() {	return "Audio";	}
}

class Buyer {
	int money = 1000;
	int bonusPoint = 0;
	Vector<Product> item = new Vector<Product>();
	
	void buy(Product p) {
		if(money < p.price) {
			System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
			return;
		}
		money -= p.price;
		bonusPoint += p.bonusPoint;
		item.add(p);
		System.out.println(p + "을/를 구입하셨습니다.");
	}
	
	void refund(Product p) {
		if(item.remove(p)) {
			bonusPoint -= p.bonusPoint;
			System.out.println(p + "을/를 반품하셨습니다.");
		} else { System.out.println("구입하신 제품 중 해당 제품이 없습니다.");
	}
}
	
void summary() {
	int sum = 0;
	String itemList ="";
	
	if(item.isEmpty()) {
		System.out.println("구입하신 제품이 없습니다.");
		return;
	}
	
	for(int i = 0; i < item.size();i++) {
		Product p = (Product)item.get(i);
		sum += p.price;
		itemList += (i==0) ? "" + p : ", " + p;
	}
	System.out.println("구입하신 물품의 총금액은 " + sum + "만원입니다.");
	System.out.println("구입하신 제품은 "+ itemList + "입니다.");
}
}

public class PolyArgumentTest3 {
	public static void main(String[] args) {
		Buyer b = new Buyer();
		Tv tv = new Tv();
		Computer com = new Computer();
		Audio audio = new Audio();

		b.buy(tv);
		b.buy(com);
		b.buy(audio);
		b.summary();
		System.out.println();
		b.refund(com);
		b.summary();
	}
}
Tv을/를 구입하셨습니다.
computer을/를 구입하셨습니다.
Audio을/를 구입하셨습니다.
구입하신 물품의 총금액은 350만원입니다.
구입하신 제품은 Tv, computer, Audio입니다.

computer을/를 반품하셨습니다.
구입하신 물품의 총금액은 150만원입니다.
구입하신 제품은 Tv, Audio입니다.

 

 

 

예제7-24

public class FighterTest {
	public static void main(String[] args) {
		Fighter f = new Fighter();
		Fightable fightable = new Fighter();
				
		if(f instanceof Unit) {
			System.out.println("f는 Unit의 자손입니다");
		}
		if(f instanceof Fightable) {
			System.out.println("f는 Fightable인터페이스를 구현했습니다");
		}
		if(f instanceof Attackable) {
			System.out.println("f는 Attackable인터페이스를 구현했습니다");
		}
		if(f instanceof Movable) {
			System.out.println("f는 Movable인터페이스를 구현했습니다");
		}
		if(f instanceof Object) {
			System.out.println("f는 Object클래스의 자손입니다");
		}
		
	}
}

class Fighter extends Unit implements Fightable {
	public void attack(Unit u) {
		// 내용생략
	}
	public void move(int x, int y) {
		// 내용생략
	}
}
class Unit {
	int currentHP;
	int x;
	int y;
	
}
interface Fightable extends Movable, Attackable{}
interface Movable { void move(int x, int y); }
interface Attackable { void attack(Unit u); }
f는 Unit의 자손입니다
f는 Fightable인터페이스를 구현했습니다
f는 Attackable인터페이스를 구현했습니다
f는 Movable인터페이스를 구현했습니다
f는 Object클래스의 자손입니다

 

 

 

예제7-25

interface Parseable {
	public abstract void parse(String fileName);
}

class ParserManager {
	public static Parseable getParser(String type) {
		if (type.equals("XML")) {
			return new XMLParser();
		} else {
			Parseable p = new HTMLParser();
			return p;
		}

	}
}

class XMLParser implements Parseable {
	@Override
	public void parse(String fileName) {
		System.out.println(fileName + "- XML parsing completed.");
	}
}

class HTMLParser implements Parseable {
	@Override
	public void parse(String fileName) {
		System.out.println(fileName + "-HTML parsing completed.");

	}
}

class parserTest {
	public static void main(String args[]) {
		Parseable parser = ParserManager.getParser("XML");
		parser.parse("document.xmml");
		parser = ParserManager.getParser("HTML");
		parser.parse("document2.html");
	}
}

 

 

 

예제7-26

public class RepairableTest {
	public static void main(String[] args) {
		Tank tank = new Tank();
		Dropship dropship = new Dropship();
		
		Marine marine = new Marine();
		SCV scv = new SCV();
		scv.repair(tank);
		scv.repair(dropship);
//		scv.repair(marine);
	}
}

interface Repairable{}

class Unit {
	int hitPoint;
	final int MAX_HP;
	Unit(int hp) {
		MAX_HP = hp;
	}
	//...
}

class GroundUnit extends Unit {
	GroundUnit(int hp) {
		super(hp);
	}
}

class AirUnit extends Unit {
	AirUnit(int hp) {
		super(hp);
	}
}


class Tank extends GroundUnit implements Repairable {
	Tank() {
		super(150);
		hitPoint = MAX_HP;
	}
	
	public String toString() {
		return "Tank";
	}
	//...
}

class Dropship extends AirUnit implements Repairable {
	Dropship() {
		super(125);
		hitPoint = MAX_HP;
	}
	
	public String toString() {
		return "Dropship";
	}
	//...
}

class Marine extends GroundUnit {
	Marine() {
		super(40);
		hitPoint = MAX_HP;
	}
	//...
}

class SCV extends GroundUnit implements Repairable{
	SCV() {
		super(60);
		hitPoint = MAX_HP;
	}
	
	void repair(Repairable r) {
		if (r instanceof Unit) {
			Unit u = (Unit)r;
			while(u.hitPoint!=u.MAX_HP) {
				u.hitPoint++;
			}
			System.out.println( u.toString() + "의 수리가 끝났습니다.");
		}
		//...
	}
}
Tank의 수리가 끝났습니다.
Dropship의 수리가 끝났습니다.

 

 

 

예제7-27

class A {
	public void methodA(B b) {
		b.methodB();
	}
}

class B  {
	public void methodB() {
		System.out.println("methodB()");
	}
}

public class InterfaceTest {
	public static void main(String[] args) {
		A a = new A();
		a.methodA(new B());
	}
}
methodB()

 

 

예제7-28

class A {
	void autoPlay(I i) {
		i.play();
	}
}

interface I {
	public abstract void play();
}

class B implements I {
	public void play() {
		System.out.println("play in B class");
	}
}

class C implements I {
	public void play() {
		System.out.println("play in C class");
	}
}

class InterfaceTest2 {
	public static void main(String[] args) {
		A a = new A();
		a.autoPlay(new B());
		a.autoPlay(new C());
	}
}
play in B class
play in C class

 

 

 

예제7-29

public class InterfaceTest3 {
	public static void main(String[] args) {
		A a = new A();
		a.methodA();
	}
}

class A {
	void methodA() {
		I i = InstanceManager.getInstance();
		i.methodB();
		System.out.println(i.toString());
	}
}

interface I {
	public abstract void methodB();
}

class B implements I {
	public void methodB() {
		System.out.println("methodB in B class");
	}

	public String toString() {
		return "class B";
	}
}

class InstanceManager {
	public static I getInstance() {
		return new B();
	}
}
methodB in B class
class B

 

 

예제7-30

public class DefaultMethodTest {
	public static void main(String[] args) {
		Child c = new Child();
		c.method1();
		c.method2();
		MyInterface.staticMethod();
		MyInterface2.staticMethod();
	}
}

class Child extends Parent implements MyInterface, MyInterface2 {
	public void method1() {
		System.out.println("method1() in Child"); // 오버라이딩
	}
}

class Parent {
	public void method2() {
		System.out.println("method2() in Parent");
	}
}

interface MyInterface {
	default void method1() {
		System.out.println("method1() in MyInterface");
	}

	default void method2() {
		System.out.println("method2() in MyInterface");
	}

	static void staticMethod() {
		System.out.println("staticMethod() in MyInterface");
	}
}

interface MyInterface2 {
	default void method1() {
		System.out.println("method1() in MyInterface2");
	}

	static void staticMethod() {
		System.out.println("staticMethod() in MyInterface2");
	}
}
method1() in Child
method2() in Parent
staticMethod() in MyInterface
staticMethod() in MyInterface2

 

 

 

예제 7-31

class InnerEx1 {
	class InstanceInner {
		int iv = 100;
//		static int cv = 100;
		final static int CONST = 100;
	}

	static class StaticIneer {
		int iv = 200;
		static int cv = 200;
	}

	void myMethod() {
		class LocalInner {
			int iv = 300;
//			static int cv = 300;
			final static int CONST = 300;
		}
	}

	public static void main(String[] args) {
		System.out.println(InstanceInner.CONST);
		System.out.println(StaticIneer.cv);
	}
}
100
200

 

 

 

예제 7-32

class InnerEx2 {
	class InstanceInner {
	}

	static class StaticInner {
	}

	InstanceInner iv = new InstanceInner();
	static StaticInner cv = new StaticInner();

	static void staticMethod() {
//		InstanceInner obj1 = new InstanceInner();
		StaticInner obj2 = new StaticInner();

		InnerEx2 outer = new InnerEx2();
		InstanceInner obj1 = outer.new InstanceInner();
	}

	void instanceMethod() {
		InstanceInner obj1 = new InstanceInner();
		StaticInner obj2 = new StaticInner();
//		LocalInner lv = new LocalInner();
	}

	void myMethod() {
		class LocalInner {
		}
		LocalInner lv = new LocalInner();
	}
}

 

 

 

예제7-33

class InnerEx3 {
	private int outerIv = 0;
	static int outerCv = 0;
	
	class InstanceInner {
		int iiv = outerIv;
		int iiv2 = outerCv;
	}
	
	static class StaticInner {
//		int siv = outerIv;
		static int scv = outerCv;
	}
	
	void myMethod() {
		int lv = 0;
		final int LV = 0;
		
		class LocalInner {
			int liv = outerIv;
			int liv2 = outerCv;
			int liv3 = lv;
			int liv4 = LV;
		}
	}

}

 

 

예제7-34

class Outer {
	class InstanceInner {
		int iv = 100;
	}
	
	static class StaticInner{
		int iv = 200;
		static int cv = 300;
	}
	
	void myMethod() {
		class LocalInner {
			int iv = 400;
		}
	}

}

class InnerEx4 {
	public static void main(String[] args) {
		Outer oc = new Outer();
		Outer.InstanceInner ii = oc.new InstanceInner();
		
		System.out.println("ii.iv : "+ ii.iv);
		System.out.println("Outer.StaticInner.cv : "+ Outer.StaticInner.cv);
		
		Outer.StaticInner si = new Outer.StaticInner();
		System.out.println("si.iv : "+ si.iv);
	}
}
ii.iv : 100
Outer.StaticInner.cv : 300
si.iv : 200

 

 

 

예제 7-35

class Outer {
	int value = 10;

	class Inner {
		int value = 20;

		void method1() {
			int value = 30;
			System.out.println("value :" + value);
			System.out.println("this.value :" + this.value);
			System.out.println("Outer.this.value :" + Outer.this.value);
		}
	}

}

class InnerEx5 {
	public static void main(String[] args) {
		Outer outer = new Outer();
		Outer.Inner inner = outer.new Inner();
		inner.method1();
	}
}
value :30
this.value :20
Outer.this.value :10

 

 

 

예제7-36

public class InnerEx6 {
	Object iv = new Object () { void method() {} };
	static Object cv = new Object() { void method() {} };
	
	void myMethod() {
		Object lv = new Object() { void method() {} };
	}
}

 

 

 

예제7-37

import java.awt.*;
import java.awt.event.*;

class InnerEx7 {
	public static void main(String[] args) {
		Button b = new Button("Start");
		b.addActionListener(new EventHandler());
	}
}
class EventHandler implements ActionListener {
	public void actionPerformed(ActionEvent e) {
		System.out.println("ActionEvent occurred!!!");
	}
}

 

 

 

예제7-38

import java.awt.*;
import java.awt.event.*;

class InnerEx8 {
	public static void main(String[] args) {
		Button b = new Button("Start");
		b.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.out.println("ActionEvent occurred!!!");
			}
		});
	}
}

인터페이스는 일종의 추상 클래스이다.

인터페이스는 추상 클래스처럼 추상 메서드를 갖지만 추상 클래스보다 추상화 정도가 높아서 추상 클래스와 달리 몸통을 갖춘 일반 메스도 또는 멤버 변수를 구성원으로 가질 수 없다. 오직 추상 메서드와 상수만을 멤버로 가질 수 있으며, 그 외의 다른 어떠한 요소도 허용하지 않는다.

추상 클래스를 부분적으로만 완성된 미완성 설계도라고 한다면, 인터페이스는 구현된 것은 아무것도 없고 밑그림만 그려져 있는 기본 설계도라 할 수 있다.

인터페이스도 다른 클래스를 작성하는데 도움을 줄 목적으로 작성된다.

 

 

인터페이스의 작성

인터페이스를 작성하는 것은 클래스를 작성하는 것과 같다. 다만 키워드로 클래스 대신 interface를 사용한다는 것만 다르다. interface에도 클래스와 같이 접근제어자로 public 또는 default를 사용할 수 있다.

interface 인터페이스이름 {
	public static fianl 타입 상수이름 = 값;
	public abstract 매서드이름(매개변수목록);
}

일반적인 클래스의 멤버들과 달리 인터페이스의 멤버들은 다음과 같은 제약사항이 있다.

- 모든 멤버변수는 bublic static final 이어야 하며, 이를 생략할 수 있다.
- 모든 메서드는 public abstract이어야 하며, 이를 생략할 수 있다.
단, static메서드와 default메서드는 예외

생략된 제어자는 컴파일 시에 컴파일러가 자동적으로 추가해준다.

 

 

 

인터페이스의 상속

인ㅌ너 페이스는 인터페이스로부터만 상속받을 수 있으며, 클래스와는 달리 다중 상속, 즉 여러 개의 인터페이스로부터 상속을 받는 것이 가능하다.

클래스의 상속과 마찬가지로 자손 인터페이스는 조상 인터페이스에 정의된 멤버를 모두 상속받는다.

 

 

인터페이스의 구현

인터페이스도 추상 클래스처럼 그 자체로는 인스턴스를 생성할 수 어 ㅄ으며, 추상 클래스가 상속을 통해 추상메서드를 완성하는 것처럼, 인터페이스도 자신에 정의된 추상메서드의 몸통을 만들어주는 클래스를 작성해야하는데, 그 방법은 추상클래스가 자신을 상속받는 클래스를 정의하는 것과 다르지 않다.

다만 클래스는 확장한다는 의미의 키워드 extends를 사용하지만 인터페이스는 구현한다는 의미의 키워드 implements를 사용할 뿐이다.

만일 구현하려는 인터페이스의 케서드 중 일부만 구현한다면, abstract를 붙여서 추상 클래스로 선언해야 한다.

또한 인터페이스는 상속과 구현을 동시에 할 수 있다(다중 상속이 가능하므로)

 

인터페이스의 이름에는 주로 able로 끝나는 것들이 많은데, 그 이유는 어떠한 기능 또는 행위를 하는데 필요한 메서드를 제공한다는 의미를 강조하기 위해서이다. 또한 그 인터페이스를 구현한 클래스는 ~를 할 수 있는 능력을 갖추었다는 의미이기도 하다.

인터페이스는 상속 대신 구현이라는 용어를 사용하지만, 인터페이스로부터 상속받은 추상 메서드를 구현하는 것이기 때문에 인터페이스도 조금은 다른 의미의 조상이라고 할 수 있다. 

오버 라이딩할 때는 조상의 메서드보다 넓은 범위의 접근 제어자를 지정해야 한다. 이는 인터페이스도 마찬가지로 적용된다.

 

 

인터페이스를 이용한 다중 상속

자바는 일반적으로 다중 상속을 허용하지 않는다. 인터페이스가 이와 다르게 다중상속이 가능해 다중상속을 위한 것으로 오해를 사게 된다.

인터페이스는 static상수만 정의할 수 있으므로 조상 클래스의 멤버변수와 충돌하는 경우는 거의 없고 충돌된다 하더라도 클래스 이름을 붙여서 구분이 가능하다. 추상메서드는 구현내용이 없으므로 조상클래스의 메서드와 선언부가 일치하는 경우에는 조상 클래스 쪽의 메서드를 상속받으면 되므로 문제 되지 않는다.

그러나, 이렇게 하면 상속받는 멤버의 충돌을 피할 수 있지만, 다중 상속의 장점을 잃게 된다. 만약 두 클래스로부터 상속을 받아야 할 상황이라면, 두 조상 클래스 중에서 비중이 높은 쪽을 선택하고 다른 한쪽은 클래스 내부에 멤버로 표함 시키는 방법으로 처리하거나 어느 한쪽의 필요한 부분을 뽑아서 인터페이스로 만든 다음 구현하도록 한다.

 

 

인터페이스를 이용한 다형성

자손 클래스의 인스턴스를 조상 타입의 참조 변수로 참조하는것이 가능하다. 인터페이스 역시 이를 구현한 클래스의 조상이라 할 수 있으므로 해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로의 형 변환도 가능하다. 인터페이스는 매서드의 매개변수의 타입으로 사용될 수 있다.

인터페이스 타입의 매개변수가 갖는 의미는 메서드 호출 시 해당 인터페이스를 구현한 클래스의 인스턴스를 매개변수로 제공해야 한다는 것이다.

리턴 타입이 인터페이스라는 것은 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 것을 의미한다.

 

 

인터페이스의 장점

- 개발시간을 단축시킬 수 있다.
- 표준화가 가능하다.
- 서로 관계없는 클래스들에게 관계를 맺어줄 수 있다.
- 독립적인 프로그래밍이 가능하다.

1. 개발 시간을 단축시킬 수 있다.

인터페이스가 작성되면 메서드를 호출하는 쪽에서 메서드의 내용과 관계없이 선언 부만 알면 프로그램을 작성하는 것이 가능하다.

그리고 동시에 다른 한쪽에서는 인터페이스를 구현하는 클래스를 작성하게 하면, 인터페이스를 구현하는 클래스가 작성될 때까지 기다리지 않고도 양쪽에서 동시에 개발을 진행할 수 있다.

 

2. 표준화가 가능하다.

프로젝트에 사용되는 기본 틀을 인터페이스로 작성된 다음, 개발자들에게 인터페이스를 구현하여 프로그램을 작성하도록 함으로써 보다 일관되고 정형화된 프로그램의 개발이 가능하다.

 

3. 서로 관계없는 클래스들에게 관계를 맺어줄 수 있다.

서로 상속관계에 있지도 않고, 같은 조상 클래스를 가지고 있지 않은 서로 아무런 관계도 없는 클래스들에게 하나의 인터페이스를 공통적으로 구현함으로써 관계를 맺어 줄 수 있다.

 

4. 독립적인 프로그래밍이 가능하다.

인터페이스를 이용하면 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제 구현에 독립적인 프로그램을 작성하는 것이 가능하다. 클래스와 클래스 간의 직접적인 관계를 인터페이스를 이용해서 간접적인 관계로 변경하면, 한 클래스의 변경이 다른 클래스에 영향을 미치지 않는 독립적인 프로그래밍이 가능하다.

 

 

 

인터페이스의 이해

인터페이스를 이해하기 위해서는 다음의 두 가지 사항을 염두에 두고 있어야 한다.

- 클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있다.
- 메서드를 사용(호출)하는 쪽(User)에서는 사용하려는 메서드(Provider)의 선언부만 알면 된다.

 

public class InstanceofTest {
	public static void main(String[] args) {
		FireEngine fe = new FireEngine();
		
		if(fe instanceof FireEngine) {
			System.out.println("This is a FireEngine instance.");
		}
		
		if(fe instanceof Car) {
			System.out.println("This is a Car instance.");
		}
		if(fe instanceof Object) {
			System.out.println("This is an Object instance.");
		}
		System.out.println(fe.getClass().getName());
	}

} // class
class Car{}
class FireEngine extends Car{}

이 경우 클래스 A를 작성하려면 클래스 B가 이미 작성되어 있어야 한다. 그리고 클래스 B의 methodB()의 선언부가 변경되면, 이를 사용하는 클래스 A도 변경되어야 한다.

직접적인 관계의 두 클래스는 한쪽(Provider)이 변경되면 다른 한쪽(User)도 변경돼야 한다는 단점이 있다. 그러나 클래스 A가 클래스 B를 직접 호출하지 않고 인터페이스를 매개체로 해서 클래스 A가 인터페이스를 통해서 클래스 B의 메서드에 접근하도록 하면, 클래스 B에 변경사항이 생기거나 클래스 B와 같은 기능의 다른 클래스로 대체되어도 클래스 A는 전혀 영향을 받지 않도록 하는 것이 가능하다.

두 클래스 간의 관계를 간접적으로 변경하기 위해서는 먼저 인터페이스를 이용해서 클래스 B(Provider)의 선언과 구현을 분리해야 한다.

interface I {
	public abstract void methodB();
}
class B implements I {
	public void methodB() {
		System.out.println("methodB in B class");
	}
}

이제 클래스 A는 클래스 B 대신 인터페이스 I를 사용해서 작성할 수 있다.

class A {
	public void methodA(B b) {
		b.method();
	}
}
class A {
	public void methodA(I i)
		i.method();
	}
}

클래스 A를 작성하는 데 있어서 클래스 B가 사용되지 않았다는 점에 주목해서 보면 클래스 A와 클래스 B는 A-B의 직접적인 관계에서 A-I-B의 간접적인 관계로 바뀌었다.

결국 클래스 A는 여전히 클래스 B의 메서드를 호출하지만, 클래스 A는 인터페이스 I 하고만 직접적인 관계에 있기 때문에 클래스 B의 변경에 영향을 받지 않는다.

인터페이스 I는 실제 구현 내용(클래스 B)을 감싸고 있는 껍데기이며, 클래스 A는 껍데기 안에 어떤 알맹이(클래스)가 들어 있는지 몰라도 된다.

 

 

 

디폴트 메서드와 static메서드

static메서드는 인스턴스와 관계가 없는 독립적인 메서드이기 때문에 인터페이스에 추가가 가능하지만 자바의 규칙을 단순화하기 위해 인터페이스의 모든 메서드는 추상 메서드여야 한다는 규칙에 예외를 두지 않았다. 덕분에 인터페이스와 관련된 static메서드는 별도의 클래스에 따로 두어야 했다.

 

디폴트 메서드 

조상 클래스에 새로운 메서드를 추가하는 것은 별 일이 아니지만, 인터페이스의 경우에는 큰 일이다. 인터페이스에 메서드를 추가한다는 것은, 추상 메서드를 구현한다는 것이고, 이 인터페이스를 구현한 기존의 모든 클래스들이 새로 추가된 메서드를 구현해야 하기 때문이다.

이에 default method라는 것을 고안해서 추상 메서드의 기본적인 구현을 제공하는 메서드로, 추상 메서드가 아니기 때문에 디폴트 메서드가 새로 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.

default 메서드는 앞에 default를 붙이며, 추상 메서드와 달리 일반 메서드처럼 몸통{}이 있어야 한다. 디폴트 메서드 역시 접근 제어자가 public이며, 생략 가능하다.

인터페이스 메서드에 디폴트 메서드를 추가하면, 조상 클래스에 새로운 메서드를 추가한 것과 동일해진다. 그러나 새로 추가된 디폴트 메서드가 기존 메서드와 이름이 중복되어 충돌하는 겨우가 발생하는데, 이 충돌을 해결하는 규칙은 다음과 같다.

1. 여러 인터페이스의 디폴트 메서드 간의 충돌
- 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩해야 한다.
2. 디폴트 메서드와 조상 클래스 메서드 간의 충돌
- 조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다(사용하지 않도록 주의하자)

 

public class DefaultMethodTest {
	public static void main(String[] args) {
		Child c = new Child();
		c.method1();
		c.method2();
		MyInterface.staticMethod();
		MyInterface2.staticMethod();
	}
}

class Child extends Parent implements MyInterface, MyInterface2 {
	public void method1() {
		System.out.println("method1() in Child"); // 오버라이딩
	}
}

class Parent {
	public void method2() {
		System.out.println("method2() in Parent");
	}
}

interface MyInterface {
	default void method1() {
		System.out.println("method1() in MyInterface");
	}

	default void method2() {
		System.out.println("method2() in MyInterface");
	}

	static void staticMethod() {
		System.out.println("staticMethod() in MyInterface");
	}
}

interface MyInterface2 {
	default void method1() {
		System.out.println("method1() in MyInterface2");
	}

	static void staticMethod() {
		System.out.println("staticMethod() in MyInterface2");
	}
}
method1() in Child
method2() in Parent
staticMethod() in MyInterface
staticMethod() in MyInterface2

 

추상 클래스는 미완성 설계도로 볼 수 있다.

즉 추상클래스로 인스턴스는 생성할 수 없다. 추상 클래스는 상속을 통해서 자손 클래스에 의해서만 완성될 수 있다.

추상 클래스 자체로는 클래스로서의 역할을 다 못하지만, 새로운 클래스를 작성하는 데 있어서 바탕이 되는 조상 클래스로서 중요한 의미를 갖는다.

예를 들면 같은 크기의 Tv를 모델의 차이에 따라 나눌 때 Tv의 기본설계도를 두고 옵션에 따라 다른 클래스에 상속해서 사용하는 용도이다. 추상 클래스를 이용하면 중복되는 코드를 줄이고, 보기 편하게 만들 수 있다.

추상 클래스는 추상 메서드를 포함하고 있다는 것을 제외하고는 일반 클래스와 전혀 다르지 않다.

추상 클래스에도 생성자가 있으며, 멤버 변수와 메서드도 가질 수 있다.

추상 메서드를 포함하고 있지 않은 클래스에도 키워드 abstract를 붙여서 추상 클래스로 지정할 수도 있다. 추상메서드가 없는 완성된 클래스라 할지라도 추상클래스로 지정되면 클래스의 인스턴스를 생성할 수 없다.

 

추상 메서드(abstract method)

메서드는 선언부와 구현부(몸통)로 구성되어 있다고 했다. 선언 부만 작성하고 구현부는 작성하지 않은 채로 남겨 둔 것이 추상 메서드이다. 즉, 설계만 해 놓고 실제 수행될 내용은 작성하지 않았기 때문에 미완성 메서드인 것이다.

메서드를 이와 같이 미완성 상태로 남겨 놓는 이유는 메서드의 내용이 상속받는 클래스에 따라 달라질 수 있기 때문에 조상 클래스에서는 선언 부만을 작성하고, 주석을 덧붙여 어떤 기능을 수행할 목적으로 작성되었는지 알려주고, 실제 내용은 상속받는 클래스에서 구현하도록 비워놓는 것이다.

/* 주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명한다 */
abstract 리턴타입 매서드이름();

추상 클래스로부터 상속받는 자손 클래스는 오버 라이딩을 통해 조상인 추상 클래스의 추상 메서드를 모두 구현해주어야 한다. 만일 조상으로부터 상속받은 추상메서드 중 하나라도 구현하지 않는다면, 자손 클래스 역시 추상 클래스로 지정해 주어야 한다.

메서드의 이름과 매개변수, 리턴 타입은 반드시 주석에 넣도록 하자.

 

추상 클래스의 작성

여러 클래스에 공통적으로 사용될 수 있는 클래스를 바로 작성하기도 하고, 기존의 클래스의 공통적인 부분을 뽑아서 추상 클래스로 만들어 상속하도록 하는 경우도 있다.

상속계층도를 따라 내려갈수록 (자손 클래스로 갈수록) 기능이 추가되어 세분화되며, 상속계층도를 따라 올라갈수록(조상 클래스로 갈수록) 공통 요소만 남게 된다.

추상화 : 클래스간의 공통점을 찾아내서 공통의 조상을 만드는 작업
구체화 : 상속을 통해 클래스를 구현, 확장하는 작업

다형성에서 배웠듯이 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하는 것이 가능하기 때문에 이처럼 조상 클래스타입의 배열에 자손 클래스의 인스턴스를 담을 수 있는 것이다.

만일 이들 클래스 간의 공통조상이 없었다면 이처럼 하나의 배열로 다룰 수 없을 것이다.

초기의 신경 시스템 모델 중에서 아마도 가장 잘 알려진 것은 1943년 Warren McCulloch  Walter Pitts 에 의해 제안된 모델일 것이다. 그들은 인간의 두뇌를 논리적 서술을 구현하는 이진 원소들의 결합으로 추측했는데, 이진 원소인 뉴런은 on이나 off 상태를 나타낸다. 맥컬럭과 피츠가 쓴 논문 (A Logical calculus of ideas immanent in nervous activity) 의 요약 부분 중의 첫 단원을 소개한다.

이 논문의 가장 중요한 결과는 그러한 신경망들이 완벽하게 일반적이어서 어떤 유한한 논리적 표현도 실현할 수 있다는 점이다. 이 모델은 명백히 실용적인 의미를 내포하는데 그러한 뉴런들을 합쳐서 강력하고도 범용적인 컴퓨터 장치를 마들 수 있다. 이외에도 1943년 그 당시에 맥컬럭-피츠 모델과 같은 프로젝트가 펜실베니아 대학의 Moore School of Engineering 에서도 진행중 이었다. 맥컬럭과 피츠는 이 논문을 쓰는 데 단지 3 개의 문헌만 참조하였는데 모두가 수학적인 논리를 다룬 책들이다. 맥컬럭과 피츠는 뉴런의 오퍼레이션을 지배하는 5 개의 가정 (assumptions) 을 하였다. 그 가정들은 컴퓨터 과학자들에게 맥컬럭과 피츠의 뉴런으로 알려지게 되었는데 다음과 같다 ............ (김대수 1992)

McCulloch-Pitts 모델에서 사용한 가설은 다음과 같다.

McCulloch-Pitts 모델의 구조를 아래에 도시하였다. 뉴런은 인접한 여러 뉴런들로부터 신호를 수신하며, 뉴런간의 시냅스 연결강도에는 흥분성과 억제성의 2 가지 유형이 있다.

연결강도가 

 인 경우는 뉴런을 흥분시키는 역할을 하므로 흥분성 연결강도라 하며, 연결강도가 

 인 경우는 뉴런의 활성화를 억제시키는 역할을 하므로 억제성 연결강도라 한다.

그림 1 에서 뉴런 

 은 연결강도가 

 이므로 뉴런에 흥분 신호를 전달하지만, 뉴런 

 은 연결강도가 

 이므로 뉴런에 억제 신호를 전달한다.

따라서, 뉴런의 가중 입력합 NET 는 다음과 같다. .............. (오창석 1996)

 

 

출처 : http://www.aistudy.co.kr/neural/McCulloch_Pitts.htm

 

McCulloch-Pitts Model

초기의 신경 시스템 모델 중에서 아마도 가장 잘 알려진 것은 1943년 Warren McCulloch 과 Walter Pitts 에 의해 제안된 모델일 것이다. 그들은 인간의 두뇌를 논리적 서술을 구현하는 이진 원소들의 결합

www.aistudy.co.kr

 

객체지향 개념에서 다형성이란 여러 가지 형태를 가질 수 있는 능력을 의미하며, 자바에서는 한 타입의 참조 변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현하였다.

조상 클래스 타입의 참조 변수로 자손 클래스의 인스턴스를 참조할 수 있도록 하였다.

보통 인스턴스의 타입과 참조변수의 타입이 일치하는 것이 보통이지만, 조상 클래스와 자손 클래스처럼 상속관계에 있을 경우, 조상 클 클래스 타입의 참조 변수로 자손 클래스의 인스턴스를 참조하도록 하는 것도 가능하다.

하지만 반대로 자손타입의 참조 변수로 조상 타입의 인스턴스를 참조하는 것은 불가능하다. 

그 이유는 실제 인스턴스인 조상클래스의 멤버 개수보다 자손 타입의 참조 변수가 사용할 수 있는 멤버 개수가 더 많기 때문이다.

클래스는 상속을 통해서 확장될 수는 있어도 축소될 수는 없어서, 조상 인스턴스의 멤버 개수는 자손 인스턴스의 멤버 개수보다 항상 적거나 같다.

조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 없다.
반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.

 

 

 

참조 변수의 형 변환

기본형 변수와 같이 참조 변수도 형 변환이 가능하다. 단, 서로 상속관계에 있는 클래스사이에서만 가능하기 때문에 자손타입의 참조변수를 조상타입의 참조변수로, 조상타입의 참조변수를 자손타입의 참조변수로 형변환만 가능하다.

바로 윗 조상이나 자손이 아닌, 조상의 조상으로도 형 변환이 가능하다. 따라서 모든 참조변수는 모든 클래스의 조상인 Object클래스 타입으로 형변환이 가능하다.

기본형 변수의 형 변환에서 작은 자료형에서 큰 자료형의 형 변환은 생략이 가능하듯이, 참조형 변수의 형 변환에서는 자손 타입의 참조 변수를 조상 타입으로 형 변환하는 경우에는 형 변환을 생략할 수 있다.

자손타입 > 조상타입(Up casting) : 형변환 생략가능
자손타입 < 조상타입(down castring) : 형변환 생략불가

참조 변수 간의 형 변환 역시 캐스트 연산자를 사용하며, 괄호() 안에 변환하고자 하는 타입의 이름(클래스명)을 적어주면 된다.

class Car {
	String color;
	int door;
	void drive() {
		System.out.println("drive, brrr~");
	}
	void stop() {
		System.out.println("Stop!!!");
	}
}
class FireEngine extends Car {
	void water() {
		System.out.println("water!!");
	}
}
class Ambulance extends Car {
	void siren() {
		System.out.println("siren~~");
	}
}

Car클래스는 FireEngine클래스와 Ambulance클래스의 조상이다.

하지만 FireEngine클래스와 Ambulance클래스는 서로 아무런 관계가 없다.

따라서 FireEngine클래스와 Ambulance 간의 형 변환은 불가능하다.

 

형 변환은 참조 변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것은 아니기 때문에 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다.

 단지 참조변수의 형 변환을 통해서, 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(개수)를 조절하는 것뿐이다.

서로 상속관계에 있는 타입간의 형변환은 양방향으로 자유롭게 수행될 수 있으나, 
참조변수가 가리키는 인스턴스의 자손타입으로 형변환은 허용되지 않는다.
그래서 참조변수가 가리키는 인스턴스의 타입이 무엇인지 확인하는 것이 중요하다.

 

 

instanceof연산자

참조 변수가 참조하고 있는 인스턴스의 실재 타입을 알아보기 위해 instanceof연산자를 사용한다. 주로 조건문에 사용되며, instanceof의 왼쪽에는 참조 변수를 오른쪽에는 타입(클래스명)이 피연산자로 위치한다. 그리고 연산의 결과로 boolean값인 true와 false중의 하나를 반환한다.

instanceof를 이용한 연산결과로 true가 나왔다면 참조변수가 검사한 타입으로 형변환이 가능하다는 것을 의미한다.

값이 null인 참조변수에 대해 instanceof연산을 수행하면 false를 결과로 얻는다.

public class InstanceofTest {
	public static void main(String[] args) {
		FireEngine fe = new FireEngine();

		if (fe instanceof FireEngine) {
			System.out.println("This is a FireEngine instance.");
		}
		if (fe instanceof Car) {
			System.out.println("This is a Car instance.");
		}
		if (fe instanceof Object) {
			System.out.println("This is a Object instance.");
		}
		System.out.println(fe.getClass().getName());
		System.out.println(fe.getClass().getSimpleName());
	}
}
This is a FireEngine instance.
This is a Car instance.
This is a Object instance.
a210719.FireEngine
FireEngine

실제 인스턴스와 같은 타입의 instanceof연산 이외에 조상타입의 instanceof연산에도 true를 결과로 얻으며, instanceof연산의 결과가 true라는 것은 검사한 타입으로의 형변환을 해도 아무런 문제가 없다.

어떤 타입에 대한 instanceof연산의 결과가 true라는 것은 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.

 

 

 

참조변수와 인스턴스의 연결

조상타입의 참조변수로 자손 인스턴스를 참조하는 경우와 자손타입의 참조변수로 자손 인스턴스를 참조하는 경우는 다른 결과를 얻는다.

메서드의 경우 조상 클래스의 메서드를 자손의 클래스에서 오버라이딩한 경우에도 참조변수의 타입에 관계없이 항상 실제 인스턴스의 메서드(오버라이딩된 메서드)가 호출되지만, 멤버변수의 경우 참조변수의 타입에 따라 달라진다.

static메서드는 static변수처럼 참조변수의 타입에 영향을 받는다. 참조변수의 타입에 영향을 받지 않는 것은 인스턴스메서드 뿐이다. 그래서 static메서드는 반드시 참조변수가 아닌 클래스이름.메서드()로 호출해야 한다.

 

멤버변수가 조상 클래스와 자손 클래스에 중복으로 정의된 경우, 조상타입의 참조변수를 사용했을 때는 조상 클래스에 선언된 멤버변수가 사용되고, 자손타입의 참조변수를 사용했을 때는 자손 클래스에 선언된 멤버변수가 사용된다.

 중복 저의되지 않은 경우, 조상타입의 참조변수를 사용했을 때와 자손타입의 참조변수를 사용했을 때의 차이는 없다.

 

 

 

매개변수의 다형성

참조변수의 다형적인 특징은 매개변수에도 적용된다. 

class Product{
	int price;
	int bonusPoint;
	
	Product(int price) {
		this.price = price;
		bonusPoint = (int)(price/10.0);
	}
}
class Tv extends Product {
	Tv() {
		super(100);
	}
	public String toString() { return "Tv"; }
}
class Computer extends Product {
	Computer() { super(200); }
	
	public String toString() { return "Computer"; }
}

class Buyer {
	int money = 1000;
	int bonusPoint = 0;
	
	void buy(Product p) {
		if(money < p.price) {
			System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
			return;
		}
		
		money -= p.price;
		bonusPoint += p.bonusPoint;
		System.out.println(p + "을/를 구입하셨습니다.");
	}
}
class PolyArgumentTest {
	public static void main(String[] args) {
		Buyer b = new Buyer();
		
		b.buy(new Tv());
		b.buy(new Computer());
		
		System.out.println("현재 남은 돈은 " + b.money + "만원입니다.");
		System.out.println("현재 보너스 점수는" + b.bonusPoint + "점입니다.");
		
	}
}
Tv을/를 구입하셨습니다.
Computer을/를 구입하셨습니다.
현재 남은 돈은 700만원입니다.
현재 보너스 점수는30점입니다.

고객(Buyer)이 buy(Product p)메서드를 이용해서 Tv와 Computer를 구입하고, 고객의 잔고와 보너스점수를 출력하는 예제이다.

 

 

 

여러 종류의 객체를 배열로 다루기

조상타입의 참조변수 배열을 사용하면, 공통의 조상을 가진 서로 다른 종류의 객체를 배열로 묶어서 다룰 수 있다. 또는 묶어서 다루고싶은 객체드르이 상속관계를 따져서 가장 가까운 공통조상 클래스 타입의 참조변수 배열을 생성해서 객체들을 저장하면 된다.

import java.util.*;
	class Product {
		int price;
		int bonusPoint;
		
		Product(int price) {
			this.price = price;
			bonusPoint = (int)(price/10.0);
		}
		
		Product() {
			price = 0;
			bonusPoint = 0;
		}
	}



class Tv extends Product {
	Tv() { super(100); }
	public String toString() { return "Tv"; }
}

class Computer extends Product {
	Computer() { super(200); }
	public String toString() { return "computer";	}
}

class Audio extends Product {
	Audio() {	super(50);	}
	public String toString() {	return "Audio";	}
}

class Buyer {
	int money = 1000;
	int bonusPoint = 0;
	Vector<Product> item = new Vector<Product>();
	
	void buy(Product p) {
		if(money < p.price) {
			System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
			return;
		}
		money -= p.price;
		bonusPoint += p.bonusPoint;
		item.add(p);
		System.out.println(p + "을/를 구입하셨습니다.");
	}
	
	void refund(Product p) {
		if(item.remove(p)) {
			bonusPoint -= p.bonusPoint;
			System.out.println(p + "을/를 반품하셨습니다.");
		} else { System.out.println("구입하신 제품 중 해당 제품이 없습니다.");
	}
}
	
void summary() {
	int sum = 0;
	String itemList ="";
	
	if(item.isEmpty()) {
		System.out.println("구입하신 제품이 없습니다.");
		return;
	}
	
	for(int i = 0; i < item.size();i++) {
		Product p = (Product)item.get(i);
		sum += p.price;
		itemList += (i==0) ? "" + p : ", " + p;
	}
	System.out.println("구입하신 물품의 총금액은 " + sum + "만원입니다.");
	System.out.println("구입하신 제품은 "+ itemList + "입니다.");
}
}

public class PolyArgumentTest3 {
	public static void main(String[] args) {
		Buyer b = new Buyer();
		Tv tv = new Tv();
		Computer com = new Computer();
		Audio audio = new Audio();

		b.buy(tv);
		b.buy(com);
		b.buy(audio);
		b.summary();
		System.out.println();
		b.refund(com);
		b.summary();
	}
}
Tv을/를 구입하셨습니다.
computer을/를 구입하셨습니다.
Audio을/를 구입하셨습니다.
구입하신 물품의 총금액은 350만원입니다.
구입하신 제품은 Tv, computer, Audio입니다.

computer을/를 반품하셨습니다.
구입하신 물품의 총금액은 150만원입니다.
구입하신 제품은 Tv, Audio입니다.

문자여과 참조변수의 덧셈(결합연산)은 참조변수에 toString()을 호출해서 문자열을 얻어 결합한다. 그래서 위 예제에 나오는 "+p"는 +p.toString()이 되고, 만일 p.toString()의 결과가 Audio라면 +Audio가 되어 Audio가 추가된다.

+ Recent posts