파일을 입력, 출력을 다루는 클래스가 JAVA에 존재한다.

InputStream으로 파일 스트림을 받을 수 있는데

이를 이용해서
먼저
간단한 파일 복사 프로그램을 작성해본다.



(파일 복사 코드)




(복사된 화면)



위 코드는 파일을 복사하는 프로그램이다.

InputStream과 OutputStream을 이용해 작성해보았다.

InputStream 과 OutputStream는 한바이트씩 읽고 쓴다.
그렇기 때문이 이 코드를 직접 사용하기에는 성능면에서 문제가 있다.

만약 버퍼를 이용하면 성능을 높일 수 있게 된다.


(버퍼를 이용한 코드)



위처럼 1KB 버퍼를 이용해 옮기는 코드 모습이다.

그러면
InputStream을 이용해 정수형 int 자료형에 데이터를  담을 수 있을까?

스트림 데이터를 int 자료형으로 옮겨야하는데
그러기 위해서는
필터 스트림이 필요하다.

필터 스트림이란 스트림을 받아오는 과정에서 원하는 형태로 가공하기 위한 것이다.
말그대로 필터이다.

필터 스트림에는 InputStream이나 OutputStream의 기능은 없다.
필터이기 때문에 같이 사용하여야 한다.



(필터 스트림 적용)




(출력 화면)



위 코드는 필터 스트림을 적용한 코드이다.

그렇다면 이 코드 역시 버퍼를 적용 시킬 수 있지 않을까?



(버퍼를 적용한 코드)



위는 BufferedOutputStream 클래스를 이용해 버퍼를 이용한 코드이다.

OutputStream을 생성하고 그 참조변수를 BufferedOutputStream인자로 넘겨주고
그 인자를 DataOutputStream으로 연결해준다.

이 순서는 중요하다.

그러면 파일에 직접 입력을 해볼 것이다.

파일에 직접 입력하기 위해서는 문자스트림이 필요하다.
문자스트림은 다양한 운영체제에서도 똑같이 적용시키기 위해서
필요하다. 문자데이터를 입력받아 각 운영체제에 맞게 인코딩이 필요하기 때문이다.

- FileReader, FileWriter

클래스만 달라졌지 역할은 위에서 본것과 같다.

메모장에 입력하는 코드를 만들어보겠다.




(파일 입력 코드)






(출력 화면)



메모장에 문자가 입력된 것을 확인할 수 있다.

객체 또한 파일처럼 입력할 수 있다.

클래스 이름은
ObjectOutputStream , ObjectInputStream이다.
파일입력처럼 객체를 저장해 파일의 데이터처럼 꺼내서 사용할 수 있다.



(객체 입력)




(*참조 - 난 정말 JAVA를 공부한 적이 없다구요, 윤성우)




쓰레드란 프로그램의 흐름을 형성하는 것이다.

지금까지 우리는 main 메소드를 이용해 main 쓰레드 하나가지고 이요했다.

그 말은 하나의 프로세스만 돌렸다는 뜻이다.
쓰레드를 이용하면 여러개의 처리를 동시에 할 수 있다.

?
CPU가 하나인데 여러개의 프로세스를?
우리 운영체제는 보통 시분할 즉 시간을 나누어 프로세스에 분배한다.
우리 눈에는 동시에 일이 처리되는 듯 보이는 것이다.

쓰레드 생성 모습


(Thread를 상속받아 클래스를 만들었다.)




(쓰레드를 생성하고 시작.)




위 코드에서 보는 것과 같이 쓰레드를 이용하려면
Thread 라는 클래스를 상속받아야 한다.
또 Thread를 상속받은 클래스 내에서 run이라는 메소드를 오버라이드 해야한다.

이렇게 만든 클래스를 생성하여 .start()코드로
run() 메소드를 동작시킨다.

위 코드는 st1 클래스의 run과 st2클래스의 run이 동시에 돌아가는 것이다.

만약,
다른 클래스를 상속받는 클래스를 쓰레드로 이용하려면 어떻게해야할까?
위에서 봤듯이 쓰레드를 이용하려면 Thread를 상속받아야한다.
자바에서는 다중상속이 안되기 때문에 다른 방법이 필요하다.
바로 인터페이스로 구현하는 것이다.


(Runnable 인터페이스를 상속받는 코드)



위에서 보듯 Runnable 인터페이스를 상속 받으면 된다. 그리고 run() 을 꼭 채워줘야한다. start()함수로 run()이 실행되기 때문이다.





(생성한 클래스를 Thread 클래스의 인자로 넘긴다.)





위에서 보듯 Runnable 인터페이스를 상속받은 클래스는
실행하려면 결국 Thread 클래스로 돌아가야한다.
다중상속의 기능을 위해 우리는 Runnable 인터페이스를 상속시켜서
그 클래스 인자를 Thread 클래스의 인자로 넘겨서 사용했다.

쓰레드는 우선순위를 가지고 있다.
아래 코드를 실행시켜보면 우선순위에 대한 이해가 될 것이다.




(우선순위 확인 코드)



* 동기화
- 위에서 말했듯이 쓰레드는 각자 독립적으로 실행이된다.
그렇기 때문에 다른 애들이 어떻게 일하는지 신경안쓰고 혼자 신나게 동작한다.

하지만 코드를 작성하는 우리 입장에서는
쓰레드끼리 서로 합이 맞길 바란다.
아래 처럼


(비동기화된 코드)




(실행결과)



결과를 보면 실행 메인코드는 첨부하지 않았지만 300,000,000이 출력되야한다. 하지만 실행한 쓰레드들이 각자 동작하다보니 요상한 값이 출력된 것이다.

동기화 시키는 방법은 간단히 synchronized 메서드를 이용하면 된다.



(동기화 된 코드)





(실행 결과)





이렇게 동기화를 시켜주면
내가 원하는대로 쓰레드들이 합을 맞추어 올바른 결과를 내었다.

synchronized 함수는 여러가지 key를 가지고 돌아갈 수 있다.
다른 식으로 설명하자면 토큰식이라고 생각할 수 있다.
토큰이 돌고 돈다. 이 토큰을 가진 놈들만 일을 할 수 있는 것이다.
그렇기 때문에 synchronized로 동기화를 시켜줄 수 있는 것이다.

이 토큰을 늘릴 수 있는데 Object를 하나 더 이용하는 것이다.



(두개의 키를 이용한 코드)



혹은 쓰레드들을 서로 기다리고 이제 왔으니 일어나라는 등의 명령을 내려줄 수 있다.



(notifyAll 코드)




notifyAll 구문으로 기다리고 있던 다른 쓰레드들을 부를 수 있다.
혹은
wait() 구문으로 다른 애들이 부르기 전까지 기다리는 구문도 있다.

이런 식으로 쓰레드들을 정교하게 동기화 시킬 수도 있다.

(*참조 - 난  정말 JAVA를 공부한 적이 없다구요, 윤성우)





자바에서 컬렉션 프레임워크란
프로그래밍시 유용한 자료구조나 알고리즘을 클래스와 메서드로 정리해 둔것이다.

자료구조로는 대표적으로
배열, 리스트, 스택, 큐, 트리, 해시

알고리즘 몇가지로는
정렬, 탐색, 최대 최소검색이 있다.

리스트표현과 유용한 메서드들을 정리해둔 예를 보자.

(리스트 표현)


< > 표시는 어제 확인한 제네릭이다.
또 리스트는 배열로 만들 수 있고, 연결리스트(LinkedList)로 만들 수 있다.

위 코드는 배열을 이용한 리스트이다.
자세한 자료구조 내용은 언급하지 않겠다.
(필요시 검색해보면 많이 나온다.)




(리스트에 정수를 추가하는 코드)


보면 Integer Wrapper 클래스를 이용한다.


(get() 메서드)



리스트에서 제공하는 get 메서드는 () 괄호 안의 인덱스에 있는 데이터를 얻을 수 있다.




(결과 모습)


배열을 이용하지 않는
연결리스트 표현도 가능하다.

아래는 LinkedList 의 표현코드이다.



(연결리스트 코드)


위 코드를 보면
Iterator 라는 놈이 나왔다.
이 Iterator 라는 놈은 Interface이다.
반복하는 경우에 생성해서 이용하면 좋은 메서드들이 많다.

위 코드에서 보는 것처럼 .hasNext() 함수는 다음에 값이 있는지 확인하는 boolean 반환 메서드이고
next() 는 다음 값으로 넘어가는 함수이다.

*Hash Set
해쉬 알고리즘을 이용하는 집합단위이다.
먼저
코드를 보면




(HashSet 사용 코드)



(실행화면)



실행 화면에 보듯이 해쉬집합은
순서대로 입력되지 않는다.
순서는 신경쓰지않고 저장한다. 또 동일한 값은 저장하지 않는다.

해쉬에 관한 자세한 내용은 언급하지 않겠다.
(자료구조에 관한 내용은 알고있으리라 가정하고 넘어가겠습니다.)



(해쉬 집합)




(실행화면)



화면을 보면 중복된 데이터들은 빠져있는 것을 확인 할 수 있다.


자료구조 트리를 이용한 집합이 있다.
TreeSet이다.



(TreeSet 사용 코드)



(실행화면)



실행화면을 보면 값이 정렬되어 저장되어있음을 확인 할 수 있다.

오름차순인지 내림차순인지 기준은 각자 다르지만
확실한건 숫자의 크기로 순서가 정해져 있다는 것은 분명하다.

그러면 이 순서를 역순으로 하는 방법이 있을까?
-> 반복자 Iterator를 바꿔준다. ( descendingIterator() 함수로 )


(역순, 실행화면)



* TreeMap
Map이란 key-value 방식의 저장방법이다.
트리라는 자료구조로 저장한다. 즉, 크기비교하여 순서있게 저장한다.

아래는 사용 예시이다. 입력을 보면 key와 value가 들어가는 것을 확인할 수 있다.



(TreeMap 사용 코드)



(실행 화면)



실행 화면을 보면 트리의 특성처럼
순서가 있게 저장된것을 확인 할 수 있다.

* 전화번호관리 프로그램 07 단계

-> Ver 07에서는
이러한 프레임워크를 이용한다.
자료구조를 이용해 자료를 관리한다.
해쉬를 이용해 검색기능을 향상 시켰고, 중복된 값은 저장받지 않도록 향상했다.

캡쳐 화면은 다음 08단계에서 한번에 보이겠다.

(*참조 - 난 정말 JAVA를 공부한 적이 없다구요, 윤성우)



자바 5.0 이후로 추가된 문법이다.

자바에서 제공하는 많은 라이브러리를 활용하려면
제네릭에 대해 이해하고 있어야한다.

많은 라이브러리가 제네릭으로 이루어져있기 때문이다.

제네릭이란

매개변수가 어떤 값이 들어와도 성립하도록 그 때 그 때 바뀌게 만든
쉽게 말하면
모든 값에 대해 이루어 질 수 있게 하는 것이다.

예를 들면
AAA class로 참조 받아야하는 클래스가 있다.
이 클래스는 제네릭이 아닌 그냥 설계한다면
AAA class만 받을 수 있다.

그렇기에 BBB class에 관한 그 클래스를 만들고 싶으면
또 만들어야한다.

하지만 이 것을 제네릭으로 만들면 하나만 만들어도 된다.

그 하나가 AAA 일때는 AAA에 맞춰 생성되고
BBB일 때는 BBB에 맞춰 생성되기 때문이다.

표현 방법은


class FruitBox<T>
{ T item;
...
}


위는 과일 상자를 제네릭으로 표현한 것이다. < > 안에 T는 기호이다.
저 클래스에 AAA를 참조하려하면 제네릭 클래스 안에 표시한 T기호가 AAA로 바뀌어서 나타내어진다.

문제 21-1
FruitBox<T>클래스에 생성자를 추가해서 코드를 만들어보자.

(생성자 코드 추가)



위 코드는 생성자를 추가한 코드이다.
제네릭 표현으로 클래스 앞에 < > 기호를 쓴것과 아래 표현하고 싶은 클래스를 기호 T로 쓴것을 제외하면
일반적인 표현 방법과 다르지 않다.

제네릭의 문법은 생각보다 복잡하다.
제네릭을 직접 만들 일은 적겠지만,

대신 우리는 제네릭으로 이루어진 라이브러리를 잘 활용해야할 필요가 있다.

제네릭 표현에 제네릭 메소드도 있다.
이 메소드에 참조되는 값은 제네릭클래스와 마찬가지로 전부 올 수 있는데
이렇게 되면 Object 클래스의 메소드만 호출할 수 있게된다.

왜냐하면 모든 경우에 대비해야되기 때문에..

이런 문제점 때문에

참조변수를 제한하는 방법이 있다.

많이 사용되는 배열을 제한하는 방법으로는

ex)


public static <T> void showArrayData(T[] arr)

{ ....

}

[] 의 표현으로 들어오는 타입을 제한할 수 있고
또 추가적으로 arr의 .length라든가 배열의 메서드를 이용할 수 있게된다.

* 와일드 카드

자료형에 상관없이 FruitBox<T>의 인스턴스를 참조하려면


FruitBox<?> box;

라고 표현하면 된다. 여기서 '?' 기호는 어떤값이 들어와도 된다는 뜻이다.

(* 참조 - 난 JAVA를 공부한 적이 없다구요, 윤성우)


지금까지 배운 내용들로

다양한 클래스들을 이해해보겠다.

자바에는 다양한 클래스들을 지원해주는데
오늘 알아볼 클래스는
 Wrapper, BIgInteger, BigDecimal, 난수 생성 함수, 문자열 토큰 이다.

1. Wrapper 클래스

Wrapper 클래스는 기본 자료형의 데이터를 인스턴스화 시키는 클래스이다.
Wrapper란 말 그대로 감싸고 있다는 것이다.

데이터 클래스를 의미한다.

이런걸 Boxing 이라고 하고 반대는 Unboxing이라고한다.
자바5.0부터는

Auto Boxing, Auto Unboxing이 지원 되서
클래스 생성선언 없이도 바로 생성되어 지원된다.

ex)


Integer iValue = 10; // auto boxing
Double dValue = 3.14;
int num1 = iValue; // auto unboxing
double num2 = dValue;


문제 20-1
static 메소드를 이용하여  Boxing 해보아라.

(valueOf(); 메소드 사용)





(실행 화면)



이러한 Wrapper 클래스안에 valueOf 의 메소드가 있다. 이 값을 이용하면 Wrapper 클래스의 인스턴스를 생성할 수 있다.

그런데 결과 화면을 보면 동일한 인스턴스라고 나온다.

저장되는 데이터는 문자열과 같이 수정이 불가능하다. 그렇기 때문에 메모리 효율 상 같은 인스턴스를 여러개 만들 필요가 없는것이다.
값을 변경하려면 새로운 인스턴스를 만들어 참조값을 변경해주면 된다.

현재 iValue1 과 iValue2는 같은 인스턴스를 참조하고 있다.


2. BigInteger, BigDecimal 클래스
매우 큰 수( BigInteger)
가장 표현범위가 넓은 long형으로도 표현이 불가능한 수를 표현할때 사용한다.
ex)

BigInteger bigValue1 = new BigInteger("10000000000000000000000000000");


왜 입력을 문자열로 넣을까?
그 이유는 정수로 넣게되면 그 정수를 옯겨줄 매개변수가 필요한데
가장큰 long형으로도 커버가 안되기 때문에
문자열로 입력해야하는 것이다.

BigDecimal
-> 정확한 소수점 표현

처음 자료형을 배울때 float나 double의 소수점 표현에
오차가 있다고 말했다.
하지만 오차가 없게 표현하려면
BigDecimal 함수를 사용하면 된다.
BigInteger와 같이 문자열로 입력된다.

문제 20-2
두 개의 실수를 입력받아 두 실수의 차를 절대값으로 계산하시오.




(BigDeciaml 이용 코드)




(실행화면)



결과를 보면 오차없이 계산된 것을 확인 할 수 있다.

3. 난수 생성

JAVA에는 java.util 패키지로 Random 클래스를 제공한다.
- boolean nextBoolean()   : boolean형 난수 반환
- int nextInt() : int형 난수 반환
- long nextLong() : long형 난수 반환
- int nextInt(int n) : 0이상 n미만의 범위 내에있는 int형 난수 반환
- float nextFloat() : 0.0 이상 1.0 미만의 float형 난수 반환
- double nextDouble() : 0.0 이상 1.0 미만의 double형 난수 반환

괄호() 안에는 seed 값이 들어간다.
default 값은 System.currentTimeMillis() 이다.
시간에 관련한 seed 값이 들어간다.

4. 문자열 토큰

- 문자열에서 구분자를 이용해 문자열을 구분하는 방법이다.
ex)
14:99 
위 글자에서 ' : ' 를 구분자로 하여 14와 99를 구분할 수 있다.


StringTokenizer st = new StringTokenizer("14:99", ":");

public String nextToken() // 다음 토큰 반환 메서드




(*참조 - 난 정말 JAVA를 공부한 적이 없다구요, 윤성우)






그렇다면 스택영역과 힙역역을 왜 구지 나누었을까?
-> 그 이유는 인스턴스 소멸시기와 지역변수 소멸시기가 다르기 때문이다.

* Object 클래스

- finalize 메소드
모든 Object 클래스에는 finalize 메소드가 있다. 이 finalize메소드는 함수가 소멸되기 전에 반드시 실행되어야하는 코드다.
인스턴스를 지칭하는 참조변수가 사라진다고해서
바로 사라지는 것은 아니다. 너무 빈번하게 일어나면 프로그램 성능에 문제가 될 수 있기 때문이다.

그래서 finalize를 인스턴스가 사라질때 반드시 실행시키고 싶다면,
System.gc();
System.runFinalization(); 
이 코드를 적어주면 된다.

- equals 메소드
== 연산자는 참조변수의 참조 값을 비교한다.
문제 19-1
equals 메소드를 삽입해 참조 내용을 비교해보자.



(equal 오버라이딩)





(equal 오버라이딩)




(비교 코드)




(실행 화면)



* 인스턴스 복사 clone

인스턴스를 복사하기 위한 메소드가 정해져있다.
protected Object clone() throws CloneNotSupportedException

이란 메소드로 정해져있다.

이 메소드를 쓰기위해서는 Cloneable 인터페이스를 구현해야한다.

왜냐하면 프로그래밍을 하다보면 복사에 민감한, 인스턴스들도 있다. 그런것과 복사해도 되는 것을 구별하기 위해서 표현하기 위해서
Cloneable 인터페이스를 구현하는지를 확인한다.

여기서 중요한건 clone으로 복사되는데
그 안의 변수들도 복사가 된다.
하지만 그 인스턴스 내부의 인스턴스는 복사되지 않으니 같은 참조값으로써 복삭가 된다.

결론적으로 말하면
인스턴스를 완전히 복사하기 위해서는
내부 인스턴스들 또한 같이 복사해줘야한다.

문제 19-2를 보면 이해가 될것이다.


(Business 클래스 복사)




(PersonalInfo를 복사하고 그 내부의 Business 클래스 복사)




이렇게 내부도 복사해주어야

중간에 값을 변경해도 다른 복사본에 영향을 주지 않는다.



(복사 후 내부 문자열을 바꾸는 내용)



(실행화면)



내부 문자열을 변경했어도 복사본에 영향이 없음을 확인 할 수 있다.

(*참조 - 난 정말 JAVA를 공부한 적이 없다구요, 윤성우)



예외처리는

예상가능한 예외 지역에 대비해 처리해두는 것이다.

보통 지금까지 우리는
if문을 이용해 예외처리를 해왔다.

나누기연산에서 분모에 0이 들어가면 에러가 나니까
if문으로 0이들어오면 다시 입력해달라고 할 때
if문을 보통 사용했다.
하지만 JAVA든 모든 프로그래밍 언어에서 if문은 유용하게 사용되는 구문 중 하나이다.

그렇기에 예외처리가 아니더라도 많이 사용될 수 있고
그렇게 되면 예외처리부분을 찾기가 어려운 상황이 생길수 있다.

또, 예외처리를 따로 한다면
코드를 읽는데 더 쉽게 해줄수 있을 것이다.

JAVA에서는 이러한 예외처리를 위해
try-catch 구문이 있다.

try
   {
내용
   }
catch
{}

이런 형식으로 작성할 수 있다.

우리가 예외상황이 일어날 수 있을 것 같은 구문을 try로 감싼다. 그리고 바로 catch구문을 적어
예외상황이 일어나면 이렇게 처리하라는 내용을 적는다.
여기서 catch사이에 다른 구문이 들어갈 수 없다.

try로 감싼 부분이 끝나자마자 catch가 나와야한다.

이렇게 되면 catch영역이 실행되고나면 무조건 예외적인 상황이 적절하게 처리됬다고 판단하고 다음 실행으로 넘어가게 된다.

* 이때 중요한건 오류가 해결됬다는 건 아니다. 오류에대한 보고를 출력하고 그 부분을 넘어가겠다는 말이지 오류에 대한 해결책은 될 수 없다는 것이다.

예외처리는 예외 클래스를 이용해 가능하다.

알고있어야할 메소드
e.getMessage()

여기서 e는 예외처리의 클래스 변수다.
이 메소드는 오류에 대한 내용을 출력한다.

* finally

try catch 구문 바로 뒤에 finally를 쓰고 {}로 내용을 채우면

finally
{ }

이렇게 바로 나오면 try구문에 들어가자마자 무조건 그 finally코드는 실행되는 코드이다.

try영역으로 일단 들어가면 무조건 실행되는 영역
이 바로 finally 구문 안의 영역이다.
중간에 return이 되더라도 finally영역은 실행되고 나서 메소드를 빠져나가게 된다.


이러한 예외 상황을 프로그래머가 직접 정의할 수도 있다.
0으로 나누기를 한다던가 등등
자바가 직접 설정한 예외상황뿐 아니라 프로그래머가 직접 정의할 수도 있다.

예를들면,
나이를 입력하는데 0보다 작은 값을 입력하면
문법적으로 틀리진 않지만 논리적인 오류가 발생한다.

그렇기 때문에 이런예외상황은 자바머신은 인지하지 못하지만 사람들은
알 수 있고, 이걸 따로 처리해주고 싶다면
예외 클래스를 만들어야한다.


(예외 클래스 선언)



예외 클래스 선언이다. extends로 Exception 클래스를 상속받고 있다.
우리가 정의할 예외 클래스들은 모두 Exception클래스를 상속받아야한다.

또 위 코드를 보면 생성자에 문자열을 입력하고 있다.
이 구문은 예외상황이 발생하였을때 호출되어 출력되는 문자열이다.



(throw)



위 코드를 보면 나이를 입력하는 메소드에서 예외처리를 한 상황이 일어날 법하다.

하지만 이 코드에 try-catch구문이 없다.
이 예외상황을 누군가는 처리해야한다. 하지만 try-catch가 없고 대신
메소드 위에 throws 라고 적혀있다.

이 말은 자기는 안할꺼고 이 메소드를 호출한 부분으로 예외처리를 던지겠다는 말이다.
즉, 해야할 일을 넘기는 거다.



(try-catch)



나이를 입력받는 메소드에서 던져진 예외 처리가 바로 이 메소드가 호출된 지점으로 던져진다.
다행히도(?) 던져진 지점이 try구문으로 덮여있다.

그렇게 되면 try구문 안에서 예외처리가 일어났으므로 catch로 잘 넘어가
예외처리가 된다.

만약 try-catch구문이 전혀없고 심지어
main 함수 또한 throws를 한다면?

-> 그렇게 되면 자바머신이 예외처리를 한다.

* printStackTrace
-> 이 메소드는 마치 자바머신이 예외처리를 하는 상황과 비슷해진다.
-> 예외처리가 된 이유를 추적해서 어디서부터 잘못됬는지 화면에 보여준다.

정리하자면
예외처리상황은
try-catch문을 이용해서 예외를 처리하든
throws를 이용해서 던지든
해야한다.



* 전화번호 관리 프로그램 06단계

오늘 공부한 예외처리를 전화번호관리 프로그램에 적용시켜보았다.

1번부터 4번까지의 선택지중 다른걸 선택하였을 때
사용자에게 예외상황임을 알리는 것이다.




(예외 클래스)




먼저 예외 클래스를 정의 하였다. 메뉴선택예외이다. 이때 출력되는 구문은
"잘못된 선택이 이뤄졌습니다." 이다. 또 예외 처리시 선택된 choice를 매개변수로 받아와
어떤 선택은 없다고 다시 말해주는 메소드도 추가해보았다.



(초기 선택)




(예외상황 throws)


(try-catch 구문)



(정상적인 실행)



이로써 오늘 배운
예외처리를 이용하여
전화번호관리 프로그램도 6단계로 업그레이드 하였다.


(*참조 - 난 정말 JAVA를 공부한 적이 없다구요, 윤성우)


오늘은 여러가지 클래스에 관해 공부했다.
먼저, abstrcat 클래스부터 살펴보면

abstract 클래스는 인스턴스화 될 수 없는 클래스이다.
??
그러면 왜 abstract 클래스를 쓰는 것일까?

상속을 목적으로 상위클래스로 선언하고 싶은 경우 abstract를 사용한다.
abstract 선언은 클래스와 메소드에서 사용할 수 있다.

abstract 클래스는 인스턴스 생성이 불가능하다.
그렇기 때문에 다른 클래스에서 상속을 받은 후 abstract 메소드를 오버라이딩 해서 사용한다.

- Interface
: 완벽한 abstract 클래스이다.
완벽한 abstract 클래스란 내부 메소드들이 전부 abstract메소드인 클래스를 말한다.
전부 abstract 인 메소드들을 가지고 있는 클래스 앞에 interface라고 선언을 하면
자동으로 내부 메소드들은 public abstract으로 설정되고 내부에 존재하는 변수는 자동으로 public static final로 선언된다.



(interface 선언)



위 선언을 보면 메소드들의 { } 의 내용이 없는 것을 확인 할 수 있다.

즉 이 메소드들은 오버라이딩 해서 사용해야한다.
이러한 interface는 extends로 받지 않고 implements 라고 받는다.
상속이라는 표현보다 구현이라는 표현을 사용한다. 텅 빈 메소드들을 채워 넣는 개념이다.

- interface 기반의 상수표현
interface를 이용해 상수를 표현하는 방법도 있다.



(interface를 이용해 상수를 표현하는 방법)



* 인터페이스를 이용해 다중상속을 구현할 수 있다.

원칙적으로 자바에서는 다중상속을 못하게 되어있다.

하지만, interface를 이용하면 다중상속과 같은 표현을 할 수 있다.

예를 들어 IPTV는 TV이기도 하고 Computer이기도 하다. 이 것을 표현하고자 한다면
interface를 이용해서



(interface와 TV, Computer 클래스 선언)





(IPTV는 TV와 Computer 다중상속)



정확히 표현하자면 다중상속은 아니지만
다중상속과 같은 표현이다.

* Inner 클래스
클래스 안에 클래스를 선언 할 수 있다.

- Outer 클래스의 인스턴스 생성 후에야 Inner 클래스의 인스턴스 생성이 가능하다.
- Inner 클래스 내에서는 Outer 클래스의 멤버에 직접 접근이 가능하다.
- Inner 클래스의 인스턴스는 자신이 속할 Outer 클래스의 인스턴스를 기반으로 생성된다.

-> Inner 클래스를 사용하면
1. 클래스들을 논리적으로 묶는 수단이된다.
2. 클래스들을 논리적으로 묶다 보니, 캡슐화가 증가하는 효과가 있다.
3. 결과적을 ㅗ코드의 가독성이 향상되고, 유지보수성이 좋아진다.


* 전화번호 관리 프로그램 5단계
출력 내용은 같지만
코드 면에서 가독성을 높이고
효율적으로 만들기 위해 5단계로 업그레이드하였다.

바꾼 내용은 먼저



(case 상수)



case에서 쓰던 상수를 읽기 편하게 interface를 이용해 상수를 선언했다.


(안정성을 높인 코드)


위 코드는 PhoneBookManager가 하나만 생성될 수 있도록
static메소드로 밖에서 호출 할 수 있고 이 메소드를 이용해 PhoneBookManager를 생성 할 수 있도록 하였다.

(* 참조 - 난 정말 JAVA를 공부한 적이 없다구요, 윤성우)




오늘 상속을 이용해
전화번호 관리 프로그램을 4번째 단계로 업그레이드 하였다.

업그레이드 내용은.

전화번호 내용 데이터를
대학, 회사, 일반 정보로 나누는 것이다.

쉽게 말하면 대학, 회사의 클래스 두개를 추가하는 것이다.

만약 상속을 이용하지 않았더라면
좀 더 복잡하게 바꿨어야했을 텐데

상속을 이용했기에 기존에 몇개만 바꾸어서 업그레이드 가능했다.

먼저
기본 상위 클래스인 일반친구들의 정보 클래스를 만들었다.


(상위 클래스)


이 정보를 상속받아 대학, 회사의 정보도 만들었다.


(상속을 이용해 추가한 두개 클래스)


상속을 이용해 PhoneInfo 의 클래스로 규정화 시켜 다룰 것이다.

먼저 입력을 다루기위해 manager 클래스(controller)에서 일반, 대학, 회사의 입력을
나누었다.


(입력을 추가해 놓은 코드)


이렇게 되면 inputData 메소드로 입력을 간단히 완성시킬 수 있다.
보면 들어오는 정보는 PhoneInfo 클래스로 다루어진다.


(입력 코드)


PhoneInfo 클래스인 info 로 한번에 다루어서 저장한다.

그리고

최종 출력화면


(정상적으로 돌아가는 화면)


이로써 상속을 이용하여 4번째 단계로 전화번호 관리 프로그램을 업그레이드 하였다.

:)

(*참조 - 난 정말 JAVA를 공부한 적이 없다구요, 윤성우)


@ 상속의 조건
 
아래 조건과 필요가 충족되지 않으면 상속을 안하는 것이 좋다.
 
* IS-A관계
: IS-A 관계란 하위 클래스를 상위 클래스로 부를 수 있어야한다.
 
예를 들면,
핸드폰 -> 스마트폰
여기서 핸드폰은 상위클래스이고, 스마트폰은 하위클래스이다.
 
이 때, 우리는 스마트폰을 핸드폰이라고 말할 수 있다.
스마트폰 IS A 핸드폰
 
이 관계를 IS-A 관계라고 부른다. 이 조건을 만족하지 않는다면 상속을 하지 않는 것이 더 좋다.
 
* HAS-A 관계
: HAS-A 관계란 하위클래스가 상위클래스를 가지고 있다는 의미이다. 이 경우 상속을 사용할 수 있지만, 왠만하면 쓰지 않는 것이 좋다.
이유는 가지는 게 추가해야될 때 고치기 힘들다. 자바에서는 중복상속이 안되기 때문이다.
그렇기 때문에 HAS-A 관계에서는 복합관계로 대신하는 것이 좋다.
 
@ 메소드 오버라이딩
: 메소드 오버라이딩이란 상위클래스에서 정의한 메소드를 다시 정의한 것이다. 메소드의 이름과 반환형, 매개변수의 선언이 완전이 일치할 때 메소드 오버라이딩이된다.
 
참조 변수를 이용해서, 인스턴스의 오버라이딩 된 메소드를 호출하면, 상위클래스가 아닌 하위 클래스의 메소드가 호출된다.
하위 클래스에서 오버라이딩 된 상위 클래스의 메소드를 호출하려면 키워드 super를 사용한다.
 
쉽게 말하면 오버라이딩이 되면 하위 클래스에서 재정의 된 메소드로 호출이 되고, 그 전의 메소드들은 가려지게 된다. 그 전의 메소드는 호출이 불가능하다.
 
 
 
 
여기서 조금 복잡한 내용인데,
상속에 있어서 인스턴스 참조관계를 일반화 해서 정리하면
 
AAA -> BBB -> CCC ( 상속 관계)
 
AAA ref1 = new BBB();
AAA ref2 = new CCC();
BBB ref3 = new CCC();
 
위를 보면 ref1, ref2는 클래스 AAA로 바라봐진다. ref3는 클래스 BBB로 바라봐진다.
 
ref1BBB로 인스턴스가 생성되지만 AAA 취급을 받는다.
ref1AAA의 메소드만 이용할 수 있다.
 
* XXX 클래스의 참조변수는 XXX 클래스의 이느턴스, 또는 XXX를 상속받는 하위 클래스의 인스턴스를 참조할 수 있다.
* 참조변수의 자료형에 상관없이 마지막으로 오버라이딩을 한 메소드만 호출된다.
(가려지기 때문이다.)

문제 15-1
오버라이딩을 이용해 같은 메소드로 다른 메소드로 연결시킨다.


(코드)


(동작화면)


-> 오버라이딩으로 자료형에 상관없이 같은 메소드로 다른 메소드처럼 동작하였다.

@ 상속의 목적
상속을 통해 연관된 일련의 클래스에 대한 공통적인 규약을 정의할 수 있다.”
 
이 뜻은 상속이 된 클래스들에 대해 일괄적으로 다룰 수 있게 된다는 이야기 이다.
상속을 계획하여 설계하고 이러한 이득을 얻을 수 있다.
 
* Object 클래스
: java.lang 패키지에 묶여있는 클래스이다. 보통 다른 어떤 클래스도 상속하지 않으면 Object 클래스를 상속하게 된다.
Object클래스 안에는
public String toString() 메소드가 정의되어있다.
그렇기 때문에 이 toString을 오버라이딩하여 사용할 수 있다.
 
* final 클래스와 final 메소드
: final 선언이 된 클래스는 상속되지 않는다.
final로 선언된 메소드는 오버라이딩을 허용하지 않겠다는 의미이다.

(*참조 - 난 정말 JAVA를 공부한 적이 없다구요, 윤성우)



+ Recent posts