[CH11. 컬렉션프레임웍과 유용한 클래스] 컬렉션 프레임워크

1. 컬렉션 프레임웍(Collection Framework)

1.6 Enumeration, Iterator, ListIterator

1.6.1 Iterator

컬렉션에 저장된 각 요소에 접근하는 기능을 가진 Iterator인터페이스를 정의해 Collection인터페이스에 Iterator를 반환하는 iterator()를 정의하고있다.

1
2
3
4
5
6
7
8
9
10
public interface Iterator{

boolean hasNext();
ObjectNext();
void remove();
}

public interface Collection {
public Iterator();
}

iterator()은 Collection 인터페이스에 구현되어있기 때문에 자손 클래스인 List와 Set에도 포함되어 있음.

메서드 설명
boolean hasNext() 읽어올 요소가 남아있는지 확인. 있으면 true, 없으면 false를 반환한다.
Object next() 다음 요소를 읽어온다. next()를 호출하기 전에 hasNext()를 호출해 확인하는것이 안전하다.
void remove() next()로 읽어 온 요소를 삭제. next()호출 한 다음에 remove()를 호출해야 한다.

1.6.2 Enumeration과 ListIterator

  • Enumeration은 컬렉션 프레임웍이 만들어지기 전에 사용하는것 Iterator의 구버전. Iterator를 사용하자.
  • ListIterator는 Iterator를 상속받아 기능을 추가한 것. 양방향으로 이동이 가능함.
    ArrayList나 LinkedList와 같이 List인터페이스를 구현한 컬렉션에서만 사용가능

1.7 HashSet

HashSet은 Set인터페이스를 구현한 가장 대표적 컬렉션.
중복된 요소를 저장하지 않음. 저장순서를 유지하지 않음.
저장순서 유지하려면 LinkedHashSet을 사용하면 됨.

생성자 또는 메서드 설명
HashSet() HashSet객체를 생성한다.
HashSet(Collection c) 주어진 컬렉션을 포함하는 HashSet객체를 생성한다.
HashSet(int initialCapactiy) 주어진 값을 초기용량으로 하는 HashSet객체를 생성한다.
HashSet(int initialCapactiy, float loadFactor) 초기용량과 load factor를 지정하는 생성자
boolean add(Object o) 새로운 객체를 저장한다
boolean addAll(Collection c) 주어진 컬렉션에 모든 객체를 추가한다.
void clear() 저장된 모든 객체를 삭제한다.
Object clone() HashSEt을 복제하여 반환한다.
boolean contains(Object o) 지정된 객체를 포함하고 있는지 알려준다.
boolean containsAll(Collection c) 주어진 컬렉션에 저장된 모든 객체들을 포함하고 있는지 알려준다.
boolean isEmpty() HashSet이 비어있는지 알려준다.
boolean iterator() Iterator 를 반환한다.
boolean remove(Object o) 지정된 객체를 HashSet에서 삭제한다.
boolean removeAll(Collection c) 주어진컬렉션에 저장된 모든 객체와 동일한 것을 HashSet에서 모두 삭제한다(차집합)
boolean retainAll(Collection c) 주어진 컬렉션에 저장된 객체와 동일한 것만 남기고 삭제한다.(교집합)
int size() 저장된 객체의 개수를 반환한다.
Object[] toArray() 저장된 객체들을 객체배열의 형태로 변환한다.
Object[] toArray(Object[] a) 저장된 객체들을 주어진 객체배열 a에 담는다.

1.8 ThreeSet

ThreeSet은 이진검색트리라는 자료구조의 형태로 데이터를 저장하는 컬렉션 클래스.
정렬, 검색, 범위검색에 뛰어난 성능을 보인다.

[CH15. 네트워킹(Networking)] 네트워킹

1.네트워킹

두대 이상의 컴퓨터를 케이블로 연결하여 네트워크를 구성하는것.

1.1 클라이언트/서버(client/server)

서버(server)는 서비스를 제공하는 컴퓨터, 클라이언트(client)는 서비스를 사용하는 컴퓨터.
ex) 파일서버(file server), 메일서버(mail server), 어플래케이션 서버(application server)

서버기반 모델(server-based-model) P2P모델(peer-to-peer model)
- 안정적인서비스 제공이 가능하다.
- 공유데이터의 관리와 보안이 용이하다.
- 서버구축비용과 관리비용이 든다.
- 서버구축 및 운용비용을 절감할 수 있다.
- 자원의 활용을 극대화 할 수 있다.
- 자원의 관리가 어렵다.
- 보안이 취약하다.

1.2 IP주소(IP address)

IP주소는 컴퓨터(호스트, host)를 구별하는데 사용하는 고유한 값.
4 byte(32bit)의 정수로 구성되어있음. a.b.c.d와 같은 형식으로 표현됨.
abcd는 0~255사이의 정수이다.
윈도우 콘솔에서 ifconfig를 확인하면 ip주소를 확인 할 수 있음.
ex)

  • ip주소
    192.168.10 .100
    (네트워크주소).(호스트주소)
  • 서브넷마스크
    255.255.255.0

1.3 InetAddress

자바에서 IP주소를 다루기 위한 클래스로 InetAddress 제공함

1.4 URL(Uniform Resource Location)

URL은 인터넷에 존재하는 여러 서버들이 제공하는 자원에 접근할 수 있는 주소를 표현하기 위한것.
“프로토콜://호스트명:포트번호/경로명/파이명?쿼리스트링#참조”의 형태로 이루워져있음.

  • 프로토콜 : 자원에 접근하기 위해 서버와 통신하는데 사용되는 통신규약(http)
  • 호스트명: 자원을 제공하는 서버의 이름(www.javahome.com)
  • 포트번호 : 통신에 사용되는 서버의 포트번호(80)
  • 경로명 : 접근하려는 자원이 저장된 서버상의 위치(/sample/)
  • 파일명 : 접근하려는 자원의 이름(hello.html)
  • 쿼리 : url에서 ?이후부분
  • 참조 : url에서 # 이후부분

java에서 URL 클래스를 제공함

1.5 URL Connection

어플리케이션과 URL간의 통신연결을 나타내는 클래스의 최상위 추상클래스.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.net.*;
import java.io.*;

public class NetworkEx3{
public static void main(String[] args){
URL url = null;
String address = "http://www.javahome.com/sample/hello.html";
String line ="";

try{
url = new URL(address);
URLConnection conn = url.openConnection();

System.out.println("conn.toSTring(): "+conn);
}catch(Exception e){
e.printStackTrace();
}
}
}
  • 아래코드는 해당 페이지의 내용을 읽어온다..
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.net.*;
import java.io.*;

public class NetworkEx4{
public static void main(String[] args){
URL url = null;
BufferedReader input = null;
String address = "http://www.javahome.com/sample/hello.html";
String line ="";

try{
url = new URL(address);

input = new BufferedReader(new InputStreamReader(url.openStream()));

while((line= input.readline()) != null) {
System.out.println(line);
}
input.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
  • 이진파일을 읽어 저장함.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.net.*;
import java.io.*;

public class NetworkEx5{
public static void main(String[] args){
URL url = null;
InputStream in = null;
FileOutputStream out = null;
String address = "http://www.javahome.com/sample/hello.zip";
int ch = 0;

try{
url = new URL(address);
in = url.openStream();
out = new FileOutputStream("hello.zip");

while((ch= inlread()) != -1) {
out.writy(ch);
}
in.close();
out.close();
}catch(Exception e){
e.printStackTrace();
}
}
}

2. 소켓 프로그래밍

소켓을 이용한 통신프로그래밍.
java.net패키지를 통해 소켓 프로그래밍을 지원함.

2.1 TCP와 UDP

TCP/IP 프로토콜은 이기종 시스템간의 통신을 위한 표준 프로토콜로 프로토콜의 집합이다.
TCP와 UDP모두 TCP/IP에 포함되어 있음, OSI 7계층의 전송계층에 해당하는 프로토콜이다.

항목 TCP UDP
연결방식 .연결기반
- 연결 후 통신(전화기)
- 1:1통신방식
.비연결기반
- 연결없이 통신(소포)
- 1:1, 1:n, n:n 통신방식
특징 .데이터의 경계를 구분안함
.신뢰성 있는 데이터를 전송
- 데이터의 전송순서가 보장됨
- 데이터의 수신여부를 확인함
(데이터가 손실되면 재전송)
- 패킷을 관리할 필요가 없음
.UDP보다 전송속도가 느림
.데이터의 경계를 구분함
.신뢰성 없는 데이터 전송
- 데이터의 전송순서가 바뀔 수 있음
- 데이터의 수신여부를 확인안 함
(데이터가 손실되어도 알 수 없음)
- 패킷을 관리해 주어야 함
.TCP보다 전송속도가 빠름
관련 클래스 .Scoket
.ServerSocket
.DatagramSocket
.DatagramPacket
.MulticastSocket

2.3 TCP 소켓 프로그래밍

서버 프로그램과 클라이언트 프로그램간의 통신과정 단계

  1. 서버프로그램에서는 서버소켓을 사용하여 서버의 컴퓨터의 특정 포트에서 클라이언트의 연결요청을 처리할 준비를 한다.
  2. 클라이언트 프로그램은 접속할 서버의 IP주소와 포트 정보를 가지고 소켓을 생성해서 서버에 연결을 요청한다.
  3. 서버소켓은 클라이언트의 연결요청으 ㄹ받으면 서버에서 새로운 소켓을 생성해서 클라이언트의 소켓과 연결되도록 한다.
  4. 이제 클라이언트 소켓과 새로 생성된 서버의 소켓은 서버소켓과 관계없이 일대일 통신을 한다.

2.4 UDP 소켓 프로그래밍

[CH12. 쓰레드] 프로세스와 쓰레드

1. 프로세스와 쓰레드

프로세스(process)는 간단하게 말하면 실행중인 프로그램이다.
프로그램 –실행–> 프로세스
프로세스는 데이터, 메모리등의 자원과 쓰레드로 구성되어있음.
프로세스의 자원을 이용해서 실제 작업을 수행하는 것.
모든 프로세스는 최소 하나 이상의 쓰레드가 존재. 둘 이상이면 멀티쓰레드 프로세스라한다.

  • 멀티쓰레딩의 장점
    • CPU의 사용률을 향상시킴.
    • 자원을 보다 효울적으로 사용할 수 있음.
    • 사용자에 대한 응답성이 향상됨.
    • 작업이 분리되어 코드가 간결해짐.

동기화(synhronization), 교착상태(deadlock)등을 고려해서 신중히 프로그래밍 해야함.

2. 쓰레드의 구현과 실행

구현 방법

  1. Thread클래스 상속

    1
    2
    3
    4
    5
    class MyThread extends Thread{
    public void run(){
    //Thread의 run()함수를 오버라이딩.
    }
    }
  2. Runnable인터페이스 구현(일반적인 방법)

    1
    2
    3
    4
    5
    class MyThread implements Runnable{
    public void run(){
    //추상메서드 run()을 구현
    }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ThreadEx1{
public static void main(String[] args){
ThreadEx1_1 t1 = new ThreadEx1_1();

Runnable r = new ThreadEx1_2();
Thread t2 = new Thread(r);
}
}

class ThreadEx1_1 extends Thread{
public void run(){
for(int i = 0; i < 5; i++) {
System.out.println(getName());
}
}
}

class ThreadEx1_2 implements Runnable{
public void run(){
for(int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}

쓰레드 생성 후 start()를 호출해야 작업을 시작함.
한번 사용한 쓰레드는 다시 재사용할 수 없다. 하나의 쓰레드에 한번의 start()만 호출 될 수 있음.

1
2
3
4
5
6
7
8
9
10
11
ThreadEx1_1 t1 = new ThreadEx_1();
t1.start();
t.start();//이건 불가능
```


```java
ThreadEx1_1 t1 = new ThreadEx1_1();
t1.start();
t1 = new ThreadEx1_1();
t.start();//이건 가능

3. start()와 run()

  • run()을 호출하는 것은 생성된 쓰레드를 실행하는 것이 아니라 단순히 클래스에속한 메서드를 하나 호출하는것.

call stack

run

main

  • start()을 호출하는 것은 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택을 생성한 후 run()을 호출해서 생성된 호출스택에 run()이 저장되게 한다.
    모든 쓰레드는 독립적인 작업을 수행하기 위해 자신만의 호출스택을 필요로 하기 때문에 새로운 쓰레드를 생성하고 실행시킬때마다 새로운 호출스택이 생성되고 쓰레드가 종료되면 작업에 사용된 호출스택은 소멸된다.
  1. main메서드에서 쓰레드의 start메서드를 호출한다.
  2. start메서드는 쓰레드가 작업을 수행하는데 사용될 새로운 호출 스택을 생성한다.
  3. 생성된 호출스택에 run 메서드를 호출해서 쓰레드가 작업을 수행하도록 한다.
  4. 이제는 호츨스택이 2개이기때문에 스케줄러가 정한 순서에 으해 번갈아 가면서 실행된다.

실행중인 쓰레드가 하나도 없을때 프로그램은 종료된다.

4. 싱글쓰레드와 멀티쓰레드

두개의 작업을 하나의 쓰레드로 하면 한 작업 끝난 후 다른 작업 끝.
두개의 작업을 두개의 쓰레드로 하면 짧은시간동안 쓰레드 2개가 번갈아 가면서 작업을 수행해서 동시에 두 작업이 처리되는것과 같다고 느낌.
CPU만 사용하는 계산 작업이면 멀티쓰레드가 전환하는 시간때문에 오히려 느림.
CPU외 자원을 사용하는 경우 싱글쓰레드 프로세스 보다 멀티쓰레드프로세스가 더 효율적임.
ex)외부기기에서 입출력 받는 경우

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import javax.swing.JOptionPane;

class ThreadEx6{
public static void main(String[] args) throws Exception{
String input = JOptionPanel.showInputDialog("아무 값이나 입력하세요.");
System.out.println("입력하신 값은 "+input+"입니다.");

for(int i = 10; i > 0 ; i--) {
System.out.println(i);
try{
Thread.sleep(1000);
}catch(Exception e){}
}
}
}

위의 예는 입력을 받은 후 출력하기 때문에 사용자가 입력하는 동안에는 출력 작업이 일어나지 않는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import javax.swing.JOptionPane;

class ThreadEx7{
public static void main(String[] args) throws Exception{

ThreadEx7_1 th1 = new ThreadEx7_1();
th1.start();

String input = JOptionPanel.showInputDialog("아무 값이나 입력하세요.");
System.out.println("입력하신 값은 "+input+"입니다.");

}
}
class ThreadEx7_1 extends Thread{
public void run(){
for(int i = 10; i > 0 ; i--) {
System.out.println(i);
try{
Thread.sleep(1000);
}catch(Exception e){}

}
}
}

위의 코드는 입력받는 동안 출력되고 입력된 값이 출력되고 또 출력이 된다.

5. 쓰레드의 우선순위

쓰레드에 우선순위(priority)의 멤버변수가 있다.
우선순위의 범위는 1~10이고 숫자가 높을수록 더 우선순위가 높다.
우선순위의 값은 상대적이다. 1,2와 8,9의 결과 값이 같다.
우선순위는 쓰레드생성한 쓰레드로부터 상속받는다.
main 메서드를 수행하는 쓰레드의 우선순위는 5.
파일 다운로드와 채팅기능 중 채팅에 더 우선순위를 높여야 한다.

1
2
3
4
5
6
void setPripority(int new Priority);
int getPriority();

public static final int MAX_PRIORITY =10;
public static final int MIN_PRIORITY =1;
public static final int NORM_PRIORITY = 5;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ThreadEx9{
public static void main(String[] args) throws Exception{

ThreadEx9_1 th1 = new ThreadEx9_1();
ThreadEx9_2 th2 = new ThreadEx9_2();
th2.setPriority(7);
System.out.println("입력하신 값은 "+input+"입니다.");
th1.start();
th2.start();
}
}

class ThreadEx9_1{
public void run(){
for(int i = 0; i < 300 ; i++) {
System.out.println("-");
for(int j = 0; j < 1000000 ; j++) {}
}
}
}
class ThreadEx9_2{
public void run(){
for(int i = 0; i < 300 ; i++) {
System.out.println("|");
for(int j = 0; j < 1000000 ; j++) {}
}
}
}

위 결과는 |가 먼저 끝나고, - 이 완료된다.

6. 쓰레드 그룹(thread group)

서로 관련된 쓰레드를 그룹으로 다루기 위한것.
폴더를 생성해서 관련된 파일을 묶어 관리하는것처럼 쓰레드도 그룹으로 묶어서 관리.
쓰레드를 쓰레드 그룹에 포함시키려면 Thread생성자를 이용해야함

1
2
Thread(ThreadGroup group, String name)
Thread(ThreadGroup gorup, Runnable target)

7. 데몬쓰레드(deamon thread)

데몬쓰레드는 일반 쓰레드의 작업을 돕는 보조역할.
일반쓰레드 작업이끝나면 데몬 쓰레드는 강제종료됨.
ex) 가비지컬렉터, 위드프로세서 자동저장, 화면자동갱신
무한루프와 조건문을 이용해서 실행 후 대기하고 있다가 특정 조건이 되면 작업 수행하고 다시 대기.

8. 쓰레드의 실행제어

쓰레드 프로그램이 어렵게 느껴지는 건 동기화와 스케줄링때문임.

  • 쓰레드 스케쥴링과 관련된 메서드
생성자/메서드 설명
void interupt() sleep()이나 join()에 의해 일시정지상태인 쓰레드를 실행대기상태로 만든다.
해당 쓰레드에서는 interuptedExcetption이 발생함으로써 일시정지상태를 벗어나게 된다.
void join()
void join(long millis)
void join(long millis, int nanos)
지정된 시간동안 쓰레드가 실행되도록한다. 지정된 시간이 자나거나 작업이 종료되면 join()을 호출한 쓰레드로 다시 돌아와 실행을 계속한다.
void resume() supend()에 의해 일시정지상태에 있는 쓰레드를 실행대기 상태로 만든다.
static void sleep(long millis)
static void sleep(long millis, int nanos)
지정된 시간( 천분의 일초 단위) 동안 쓰레드를 일시정지 시킨다. 지정한 시간이 지나고 나면 자동적으로 다시 실행대기가 된다.
void stop() 쓰레드를 즉시 종료시킨다. 교착상태(dead-lock)에 빠지기 쉽기 때문에 deprecated되었다.
void suspend() 쓰레드를 일시정지시킨다. resume()을 호출하면 다시 실행대기상태가 된다.
satic void yield() 실행중에 다른 쓰레드에게 양보하고 실행대기상태가 된다.
  • 쓰레드의 상태
상태 설명
NEW 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태
RUNNABLE 실행 중 또는 실행 가능한 상태
BLOCKED 동기화 블럭에 의해서 일시정지된 상태(Lock이 풀릴때까지 기다리는 상태)
WATING,
TIMED_WATING
쓰레드의 작업이 종료되지는 않았지만 실행가능하지 않은 일시정지 상태. TIMED_WATEING은 일시정지시간이 지정된 경우를 의미한다.
TERMINATED 쓰레드의 작업이 종료된 상태
  1. 쓰레드 생성하고 start()을 호출하면 실행대기열에 저장. 순서를 기다림. Quequ와같은 구조로 먼저 실행대기열에 들어온 쓰레드가 먼저 실행됨.
  2. 실행대기상태이다가 자기 차례가 오면 실행상태가 됨.
  3. 주어진 실행시간이 다되거나 yeild()를 만나면 다시 실행대기 상태가 되고 다음 차례의 쓰레드가 실행상태가 됨.
  4. 실행중 suspend(), sleep(), wait(), join(), I/O block에 의해 일시정지상태가 딜 수 있음.
  5. 지정된 일시정지 시간이 지나거나 notify(), reusme(), interrup()가 호출되면 일시정지 상태를 벗어나 다시 실행대기열에 들어가 순서를 기다림.
  6. 실행을 모두 마치거나 stop()이 호출되면 쓰레드는 소멸됨.
  • join()을 사용한 예시

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    class ThreadEx13{
    static long startTime=0;
    public static void main(String[] args) throws Exception{

    ThreadEx13_1 th1 = new ThreadEx13_1();
    ThreadEx13_2 th2 = new ThreadEx13_2();
    th1.start();
    th2.start();
    startTime = System.currentTimeMillis();

    try{
    th1.join();//th1의 작업이 끝날때까지 기다린다.
    th2.join();//th2의 작업이 끝날때까지 기다린다.
    }catch (InterruptedException e){}

    System.out.println("소요시간:" + (System.currentTimeMillis()- ThreadEx13.startTime));
    }
    }
    class ThreadEx13_1{
    public void run(){
    for(int i = 0; i < 300 ; i++) {
    System.out.println("-");
    }
    }//run()
    }
    class ThreadEx13_2{
    public void run(){
    for(int i = 0; i < 300 ; i++) {
    System.out.println("|");
    }
    } //run()
    }
    ```

    join()을 사용하지 않으면 main 쓰레드는 바로 종료되지만, join()을 사용해서 th1과 th2의 작업이 마칠때까지 main쓰레드가 기다림
    - 쓰레드가 순차적으로 실행되어야 할때 사용하는 예제.

    ```java
    class ThreadEx14{
    static long startTime=0;
    public static void main(String[] args) throws Exception{

    ThreadEx14_1 th1 = new ThreadEx14_1();
    ThreadEx14_2 th2 = new ThreadEx14_2();
    th1.start();

    try{
    th1.join();
    }catch (InterruptedException e){}
    th2.start();
    }
    }

    class ThreadEx14_1{
    public void run(){
    for(int i = 0; i < 300 ; i++) {
    System.out.println("-");
    }
    }//run()
    }
    class ThreadEx14_2{
    public void run(){
    for(int i = 0; i < 300 ; i++) {
    System.out.println("|");
    }
    }
    }
  • 아래 코드는 th1, th2, Main쓰레드 순으로 종료됨

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class ThreadEx15{
static long startTime=0;
public static void main(String[] args) throws Exception{

ThreadEx15_1 th1 = new ThreadEx15_1();
ThreadEx15_2 th2 = new ThreadEx15_2();
th1.start();
th2.start();
try{
th1.sleep();
}catch (InterruptedException e){}
System.out.println("<<main종료>>");
}
}

class ThreadEx15_1{
public void run(){
for(int i = 0; i < 300 ; i++) {
System.out.println("-");
}
System.out.println("<<TH1종료>>");
}//run()
}
class ThreadEx15_2{
public void run(){
for(int i = 0; i < 300 ; i++) {
System.out.println("|");
}
System.out.println("<<TH2종료>>");
} //run()
}
```

왜 th1이 sleep()으로 잠들어있어도 가장 먼저 종료될까?
sleep()이 항상 현재 실행중인 쓰레드에 대해 작동해서 th1.sleep()호출해도 main메서드를 실행하는 main쓰레드가 영향받는다.
static으로 선언되어 있어서 참조변수로 sleep()을 호출하기 보다는 Thread.sleep()이렇게 호츨해야 함.

## 9. 쓰레드의 동기화
멀티쓰레드는 여러 쓰레드가 같은 프로세스내의 자원을 공유하기 때문에 데이터가 원래 의도했더것과는 다르게 변경 될 수 있음.

### 9.1 synchorized를 이용한 동기화
공유 데이터에 lock을 걸어 먼저 작업중이던 쓰레드가 작업을 완전히 마칠때까지는 다른 쓰레드에게 제어권이 넘어가도 데이터가 변경되지 않도록 보호함.

- synchronized 사용방법 두가지.
가능하면 메서드에 synchronized를 사용하는 메서드 단위 동기화를 권장함.

```java
// 1. 특정한 객체에 lock을 걸고자 할때
sysnchronized(객체의 참조변수){

}
// 2. 메서드에 lock을 걸고자 할때
public void synchronized void calcSum(){

}

synchronized를 이용해서 객체를 동기화 하면 쓰레드가 교착상태에 빠질 수 있다.
교착상태란 구 쓰레드가 lock이 된 상태로 서로 lock가 풀리기를 무한정 기다리게 되는상황.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class ThreadEx21 {
public static void main(String[] args){
RunnbaleImpl r = new RunnableImpl();
Thread th1 = new Thread(r);
Thread th2 = new Thread(r);
th1.start();
th2.start();
}
}

class RunnableTmpl implements Runnable {
int iv = 0;
public void run(){
int lv = 0;
String name = Thread.currentThread().getName();

while(lv<3){
System.out.println(name+"Local var: "+ ++lv);
System.out.println(name+"Instance var: "+ ++iv);
System.out.println();
}
}
}

실행결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Thread-0 Local var: 1
Thread-0 Instance var: 1

Thread-0 Local var: 2
Thread-0 Instance var: 2

Thread-0 Local var: 3
Thread-0 Instance var: 3

Thread-0 Local var: 1
Thread-0 Instance var: 4

Thread-0 Local var: 2
Thread-0 Instance var: 5

Thread-0 Local var: 3
Thread-0 Instance var: 6

여기서 인스턴스변수 iv는 main, th1, th2 쓰레드 모두 접근이 가능함.(쓰레드간의 변수 공유)
lv는 지역변수라 각 쓰레드 스택내에서 생성되어 공유되지 않는다.

  • 다음은 동기화가 잘 되지 않아 데이터 값이 변형된 예제.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    class ThreadEx24{
    public static void main(String[] args){
    RunnbaleImpl r = new RunnableImpl();
    Thread th1 = new Thread(r);
    Thread th2 = new Thread(r);
    th1.start();
    th2.start();
    }
    }
    class Account{
    int balance =1000;
    public void withdraw (int money){
    if(balance>=money){
    try{ Thread.sleep(1000);} catch(Exception e){}
    balance -=money;
    }
    }//withdraw
    }
    class RunnableEx24 implements Runnable{
    Account acc = new Account();

    public void run(){
    while(acc.balance > 0){
    //100, 200, 300중의 한 값을 임의로 선택해서 출금
    int money = (int)(Math.random()*3+1)*100;
    acc.withdraw(money);
    System.out.println("balance:"+acc.balance);
    }
    }//run()
    }

실행 결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
balance:700
balance:400
balance:200
balance:0
balance:-100
```

잔고(balance)가 임의의 출금금액(money)보다 클 경우에만 출금하도록 되어있는데 잔고에 -100이 나왔음.
if조건문 통과하고 출금직전에 다른쓰레드가 끼어들어서 먼저 출금했기때문이다.
if문과 출금하는 기능은 하나로 synchronized되어야 한다.

```java
public synchronized void withdraw (int money){
if(balance>=money){
try{ Thread.sleep(1000);} catch(Exception e){}
balance -=money;
}
}//withdraw
```

```java
public void withdraw (int money){
synchronized(this){
if(balance>=money){
try{ Thread.sleep(1000);} catch(Exception e){}
balance -=money;
}

}
}//withdraw

9.2 wait()과 notify()

쓰레드를 동기화 할때 효율을 높이기 위해 사용할 수 있다.
한쓰레드가 lock걸려 다른 쓰레드는 lock이 풀릴때까지 기다려야 되는 상황이 있음.
쓰레드에 lock을 걸는것 대신에 wait()을 호출해서 다른 쓰레드에 제어권을 넘겨주고 대기상태로 기다리다가 다른쓰레드에 의해 notify()가 호출되면 다시 실행상태가 되도록 함
wait()과 notify()는 Object클래스에서 정의되서 모든 객체에서 호출이 가능함.
동기화 블록 내에서만 사용가능. 쓰레드가 wait()을 호출하면 그때까지 걸어 놓은 lock을 풀고 대기실에 들어가기 됨.
notify()는 객체의 wating pool에 있는 쓰레드 중 하나만 깨움.

  • wait(), notify(), notifyAll()
    • Object에 정의 되어있다.
    • 동기화 블록(synchronized)내에서만 사용할 수 있다.
    • 보다 효율적인 동기화를 가능하게 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Account{
int balance =1000;
public synchronized void withdraw (int money){
if(balance>=money){
try{
wait();
} catch(Exception e){}
balance -=money;
}
}//withdraw
}

public synchronized void desposit(int money){
balance += money;
notify();
}

[CH7. 객체지향프로그래밍2] 인터페이스의

7. 인터페이스

7.1 인터페이스란?

인터페이스는 일종의 추상클래스.
추상클래스처럼 추상메서드를 갖지만 추상클래스보다 추상화 정도가 높아서
추상클래스와 달리 몸통을 갖춘 일반 메서드 또는 멤버변수를 구성원으로 가질 수 없다.

7.2 인터페이스의 작성

1
2
3
4
interface 인터페이스이름{
public static final 타입 상수이름 = 값;
public abstract 메서드이름(매개변수 목록);
}
  • 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
  • 모든 메서드는 public abstract 이어야하며, 이를 생략할 수 있다.

7.3 인터페이스의 상속

인터페이스는 인터페이스로부터만 상속받을 수 있으며, 클래스와는 달리 다중상속이 가능하다.

1
2
3
4
5
6
7
8
interface Movable{
void move(int x, int y);
}
interface Attactable{
void move(Unit u);
}

interface Fightable extends Movable, Attackable{}

7.4 인터페이스의 구현

그 자체로는 인스턴스를 생성할 수 없음. 클래스에서 implements를 사용하여 구현함.

1
2
3
4
5
6
7
8
9
10
11
class 클래스 이름 implements 인터페이스이름{
//인터페이스에 정의된 추상메서드 구현
}
class Figther implements Fightable{
public void move(int x, int y){
//기능 구현
}
public void attack(Unit u){
//기능 구현
}
}

구현하는 인터페이스의 메서드 중 일부만 구현하면 추상메서드로 선언되어야 함.

1
2
3
4
5
abstract class Fighter implements Figtable{
public void move(int x, int y){
//기능 구현
}
}

상속과 구현을 동시에 할 수 있음

1
2
3
4
5
6
7
8
class Figther extends Unit implements Figthable{
public void move(int x, int y){
//기능 구현
}
public void attack(){
//기능 구현
}
}

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

자바에서는 다중상속을 허용하지 않아서 인터페이스로 다중상속을 할 수는 있지만 거의 하지 않는다.
두개의 클래스를 상속받아야 하는 상황이라면 두 클래스중 비중이 높은걸 상속받고 나머지는 클래스 내부 멤버로 포함시키는 방법.
필요한 부분을 뽑아 인터페이슬슬 만든 후 구현함.

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

인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수있고 형변환도 가능함.
인터페이스 : Fightable, 클래스 : Fighter(implements Figtable)일때

1
2
Fightable f = (Fightable) new Figther();
Fightable f = new Fighter();

인터페이스는 매게변수 타입으로도 쓸 수 있음

1
viod attack(Figtable f){}

메서드 호출 시 해당 인터페이스를 구현한 클래스의 인스턴스를 매개변수로 제공해야한다.
여기서는 new Figther();
리턴타입으로 인터페이스 타입을 지정하는것도 가능함.

1
2
3
4
Fightable method(){
// ...
return new Fighter();
}

7.7 인터페이스의 장점

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

7.8 인터페이스의 이해

  • 클래스를 사용하는 쪽과 클래스를 제공하는 쪽이 있다.
  • 메서드 사용하느 쪽에서는 사용하려는 메서드의 선언부만 알면된다.

[CH7. 객체지향프로그래밍2] 추상클래스

6. 추상클래스

6.1 추상클래스란?

미완성된 설계도. 미완성 메서드(추상메서드)를 포함하고 있는 클래스.
추상 클래스는 상속을 통해 자손클래스에 의해서만 완성될 수 있음.

1
2
3
abstract class 클래스이름{

}

6.2 추상메서드

메서드 선언부와 구현부 중 선언부만 작성한것.

1
abstract 리턴타입 메서드이름();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
abstract class Player{
abstract void play(int pos);
abstract void stop();
}

class AudioPlayeer extends Player{
void play(int pos){
// 기능 구현
}
void stop(){
//기능 구현
}
}
abstract class AbstractPlayer extends Player{
void play(int pos) {
// 기능 구현
}
}

6.3 추상클래스의 작성

  • 추상 : 낱낱의 구체적 표상이나 개념에서 공통된 성질을 뽑아 이를 일반적인 개념으로 파악하는 정신 작용.
  • 추상화 : 클래스간의 공통점을 찾아내서 공통의 조상을 만드는 작업.
  • 구체화 : 상속을 통해 클래스를 구현, 확장하는 작업
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
abstract class Player{
boolean pause;
int currentPos;

Player(){
pause = false;
currentPos = 0;
}
abstract void play(int pos);
abstract void stop();

void play(){
play(currentPos);
}

void pause (){
if(pause){
pause = false;
}else {
pause = true;
stop();
}
}
}

위의 Player추상클래스를 조상으로 하는 CDPlayer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class CDPlayer extends player{
void play(int currentPos){
//기능 구현
}

void stop(){
//기능 구현
}

int currentTrace;
void nextTrace(){
currentTrace++;
}
void preTrace(){
if(currentTrace>1){
currentTrace--;
}
}
}

추상메서드로 안 만들고 구현부를 빈 상태로 두어 자손 클래스에서 오버라이드해서 사용할 수 있지만
추상메서드로하면 필수로 구연해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Marine{
int x,y;
void move(int x, int y){}
void stop(){};
void stimPack(){}
}
class Tank{
int x,y;
void move(int x, int y){}
void stop(){};
void changeMode(){}
}
class Dropship{
int x,y;
void move(int x, int y){}
void stop(){};
void load(){}
void unload(){}
}

위에서 공통부분을 뽑아내서 추상클래스 Unit을 만들어 다시 구현.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
abstract class Unit{
int x,y;
abstract void move(int x, int y);
void stop(){};
}
class Marine extends Unit{
void move(int x, int y){}
void stimPack(){}
}
class Tank extends Unit{
void move(int x, int y){}
void changeMode(){}
}
class Dropship extends Unit{
void move(int x, int y){}
void load(){}
void unload(){}
}

위와같이 구현하면 아래처럼 Unit으로 배열로 다룰 수 있음

1
2
3
4
5
Unit[] grop = new Unit[4];
group[0] = new Marine();
group[1] = new Tank();
group[2] = new Marine();
group[4] = new Dropship();

[CH7. 객체지향프로그래밍2] 다형성

5. 다형성

5.1 다형성이란?

여러가지 형태를 가질 수 있는 능력.
조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 함.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Tv{
boolean power;
int channel;

void power(){power = !power;}
void channelUp(){++channel;}
void channelDown(){--channel;}
}

class CaptionTv extends Tv{
String text;
void caption(){};
}

1
2
Tv t = new Tv();
CaptionTv c = new CaptionTv();
1
Tv t = new CaptionTv();
1
2
CaptionTv c = new CaptionTv();
Tv t = new CaptionTv();
1
2
//이렇게는 불가능함
CaptionTv c = new Tv();

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

5.2 참조변수의 형병환

자손타입->조상타입(up-castion) : 형변환 생략 가능
자손타입<-조상타입(Down-castion) : 형변환 생략 불가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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~~!");
}
}

FireEngine타입과 Ambulance타입은 서로 형변환 불가능
Car 타입과 FireEngine 타입 형변환 예시

1
2
3
4
5
6
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;

car = fe; //car = (Car) fe; 형변환이 생략된 형태
fe2 = (FireEngine) car; //형변환 생략 불가.

아래 예제는 컴파일에러는 발생하지 않지만 실행시 에러가 발생함.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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();
}
}
```

car가 Car타입의 인스턴스여서 FireEngine을 받을 수 없음.
- 캐스트연산자를 사용하면 서로 상속관계에 있는 클래스 타입의 참조변수간의 형변환은 양방향으로 자유롭게 수행될 수 있다.
그러나 참조변수가 참조하고 있는 인스턴스의 자손타입으로 형변환을 하는것은 허용되지 않는다.

### 5.3 instanceof연산자
참조변수가 참조하는 인스턴스 실제타입을 확인하기 위해 instanceof연산자 사용.
주로 조건문에사용.
* 값이 ull인 참조변수에 대해 instanceof연산을 하면 false나옴.

```java
void doWork(Car c){
if(c instanceof FireEngine){
FireEngine fe = (FireEngind) c;
fe.water();
} else if( c instance of Ambulance){
Ambulance a = (Ambulance) c;
a.siren();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class InstanceOfTest{
public static void main (String args[]){
FireEngine fe = new FireEngine();

if(fe instanceof FireEngine){
System.out.println("FireEngine instance");
}
if( fe instanceof Car){
System.out.println("Car instance");
}
if( fe instanceof Object){
System.out.println("Object instance");
}
}
}

위의 코드를 실행할 경우

1
2
3
FireEngine instance
Car instance
Object instance

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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{
int x = 200;
void method(){
System.out.println("Child Method");
}
}

실행결과

1
2
3
4
p.x=100
Child Method
c.x=200
Child Method

5.5 매개변수의 다형성

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

[CH7. 객체지향프로그래밍2] Package와 import

3 Package와 import

3.1 패키지(package)

패키지란, 클래스의 묶음. 물리적으로 하나의 디렉터리임.
ex) java.lang.String은 java/lang 디렉터리에 위치한 Strig 클래스이다.

  • 하나의 소스파일에는 첫번째 문장으로 단 한번의 패키지 선언만을 허용한다.
  • 모든 클래스는 반드시 하나의 패키지에 속해야 된다.
  • 패키지는 점(.)을 구분자로 하여 계층구조로 구성할 수 있다.
  • 패키지는 물리적으로 클래스 파일(.class)을 포함하는 하나의 디렉터리다.

3.2 패키지의 선언

1
package 패키지명;

3.3 import 문

import 문은 컴파일러에게 소스파일에 사용된 클래스의 패키지에 대한 정보를 제공함.

3.4 import문의 선언

일반적인 소스 파일(*.java)의 구성은 다음의 순서로 되어있다.

  1. package문
  2. import문
  3. 클래스선언
1
2
import 패키지명.클래스명;
import 패키지명.*;

4 제어자

4.1 제어자란?

클래스, 변수 또는 메서드의 선언부에 함께 사용되어 부가적인 의미를 부여함

  • 접근제어자 - public, protected, default, private
  • 그외 - static, final, abstract, native, transient, synchronized, volatile, strictfp
    제어자는 클래스, 멤버변수, 메서드에 주로 사용됨.
    하나의 대상에 여러 제어자를 조합하는 것은 가능하나 접근제어자는 하나만 쓸 수 있다.

4.2 static - 클래스의, 공통적인

  • static 이 사용될 수 있는곳 : 멤머변수, 메서드, 초기화 블럭

*static

대상 의미
멤버변수 - 모든 인스턴스에 공통적으로 사용되는 클래스 변수가 된다.
- 클래스변수는 인스턴스를 생성하지 않고도 가능하다.
- 클래스가 메모리에 로드될 때 생성된다.
메서드 - 인스턴스를 생성하지 않고도 호룰이 가능한 static 메서드가 된다.
static 메서드 내에선 인스턴스 멤버들을 직접 사용할 수 없다.
1
2
3
4
5
6
7
8
9
10
11
12
class StaticTest{
static int width =200;
static int height = 120;

static {
// static 변수의 초기화 수행
}

static int max(int a, int b){
return a>b?a:b;
}
}

4.3 final - 마지막의, 변경될 수 없는

final이 사용될 수 있는 곳 - 클래스, 메서드, 멤버변수, 지역변수
*final

대상 의미
클래스 변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다.
그래서 final로 지정된 클래스는 다른 클래스의 조상이 될 수 없음
메서드 변경될 수 없는 메서드, final로 지정된 메서드는 오버라이딩을 통해 재정의 될 수 없다.
멤버변수/지역변수 변수 앞에 final이 붙으면 값을 변경할 수 없는 상수가 된다.
1
2
3
4
5
6
7
8
final class FinalTest{
final int MAX_SIZE = 10;

final void getMaxSize(){
final int LV = MAX_SIZE;
return MAX_SIZE;
}
}

4.4 생성자를 이용한 final 멤버변수 초기화

final 붙은 변수는 상수임으로 일반적으로 선언과 초기화를 동시에 하지만, 인스턴스 변수의 경우 생성자에서 초기화 되도록 할 수 있다.
각 인스턴스마다 final 이 붙은 멤버 변수가 다른 값을 갖도록 하는 것이 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}
}

4.5 abstract - 추상의, 미완성의

미완성의 의미를 가지고 있다.
메서드의 선언부만 작성하고 실제 수행내용은 구현하지 않은 추상 메서드를 선언하는데 사용함.

  • abstract가 사용될 수 있는 곳 - 클래스, 메서드

*abstract

대상 의미
클래스 클래스 내에 추상메서드가 선언되어 있음을 의미한다.
메서드 선언부만 작성하고 구현부는 작성하지 않은 추상메서드임을 알린다.
1
2
3
abstract class AbstractTest{
abstract void move();
}

4.6 접근제어자

  • 멤버 또는 클래스에 사용되어 외부에서 접근하지 못하게 제한하는 역할.
  • 접근 제어자가 사용될 수 있는곳- 클래스, 멤버변수, 메서드, 생성자
    • private : 같은 클래스 내에서만 접근이 가능하다.
    • default : 같은 패키지 내에서만 접근이 가은하다.
    • protected : 같은 패키지 내에서, 그리고 다른 패키지의 자손 클래스에서 접근이 가능하다.
    • public : 접근 제한이 없다.

4.7 접근 제어자를 이용한 캡슐화

접근 제어자 사용하는 이유:

  • 외부로 부터 데이터를 보호하기 위해
  • 외부에는 불필요한 내부적으로만 사용되는 부분을 감추기 위해
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Time{
private int hour;
private int minute;
private int second;

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 min){
if(min<0 || hour > 59){
return;
}
this.minute = min;
}
public int getSecond(){return second;}
public void setSecond(int sec){
if(sec <0 || sec > 59){
return;
}
this.second = sec;
}
}

4.9 제어자의 조합

대상 사용가능한 제어자
클래스 public, default, final, abstract
메서드 모든 접근제어자, final, abstract, static
멤버변수 모든 접근제어자, final, static
지역변수 final
  1. 메서드에 static과 abstract를 함께 사용할 수 없다.
  2. 클래스에 abstract와 final을 동시에 사용할 수 없다.
  3. abstract메서드의 접근제어자가 private일 수 없다.
  4. 메서드에 private와 final을 같이 사용할 필요는 없다.(둘중 하나만 사용해도 충분)

[CH7. 객체지향프로그래밍2] 오버라이딩

2. 오버라이딩

2.1 오버라이딩이란?

조상 클래스부터 상속받은 메서드의 내용을 변경하는것

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Point{
int x;
int y;

String getLocation(){
return "x : "+x+", y : " + y;
}
|

class Point3D extends Point{
int z;

String getLocation(){
return "x : "+x+", y : " + y+", z : "+z;
}
}

2.2 오버라이딩의 조건

메서드 선언부는 조상의 것과 완전 일치해야함.
자손 클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와

  • 이름이 같아야 한다.
  • 매개변수가 같아야 한다.
  • 리턴 타입이 같아야 한다.
  1. 접근제어자는 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
    ex) 조상클래스의 메서드의 접근 제어자가 protected라면
    자식클래스에서 오버라이딩 하는 메서드의 접근 제어자는 protected나 public 이어야함.
  2. 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
  • 조상 클래스의 메서드를 자손클래스에서 오버라이딩할 때
    1. 접근제어자를 조상클래스의 메서드보다 좁은 범위로 할 수 없다.
    2. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.
    3. 인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없다.

2.3 오버로딩 VS 오버라이딩

  • 오버로딩(overloading) : 기존에 없는 메서드를 정의하는것(new)
  • 오버라이딩(overriding) : 상속받은 메서드의 내용을 변경하는 것(change, modify)
1
2
3
4
5
6
7
8
9
10
11
12
class Parent{
void parentMethod(){};
}

class Chile extends Parent{
void parentMethod(){} // 오버라이딩;
void parentMethod(int i){} //오버로딩

void childMethod(){}
void childMethod(int i){} //오버로딩
void childMethod(){}// 에러 함수가 중복됨
}

2.4 super

자손클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수.
조상클래스의 멤버와 자손클래스의 멤버가 중복 정의되서 구별해야 되지 않는 상황이면 super대신 this를 써도 됨.
static 메서드(클래스 메서드)에서는 사용 할 수 없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class SuperTest{
public void 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);
}
}

실행 결과

1
2
3
x=20
this.x=20;
super.x=10;

2.5 super() - 조상클래스의 생성자

super()는 조상클래스의 생성자를 호출하는 생성자이다.
Object클래스를 제외한 모든 클래스의 생성자 첫 줄에는 생성자(같은 클래스의 다른 생성자 또는 조상의 생성자)를 호출 해야함.
그렇지 않으면 컴파일러가 super(); 를 자동으로 첫줄에 삽입.

  1. 클래스 - 어떤 클래스의 인스턴스를 생성할것인가?
  2. 생성자 - 선택한 클래스의 어떤 생성자를 이용해서 인스턴스를 생성할 것인가?

[CH7. 객체지향프로그래밍2] 상속

1. 상속

1.1 상속의 정의와 장점

기존의 클래스를 재사용하여 새로운 클래스를 작성하는것

1
2
class Parent{}
class Child extends Parent{}
  • 조상클래스 - 부모클래스, 상위클래스, 기반 클래스
  • 자손클래스 - 자식 클래스, 하위클래스, 파생된 클래스
  • 생성자와 초기화 블럭은 상속되지 않는다. 멤버만 상속된다.
  • 자손 클래스의 멤버개수는 조상 클래스보다 항상 같거나 많다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Tv{
boolean power;
int channel;

void power(){power = !power;}
void channelUp(){++chnannel;}
void channelDown(){--channel;}
}

class CaptionTv extends Tv{
boolean caption;
void displayCaption(String text){
if(caption){
system.out.println(text);
}
}
}

1.2 클래스간의 관계 - 포함관계

1
2
3
4
5
class Circle{
int x;
int y;
int r;
}

이 클래스를

1
2
3
4
5
6
7
8
9
class Point{
int x;
int y;
}

class Circle{
Point point = new Point();
int r;
}

이렇게 표현할 수 있다.
Engine, Door 클래스를 미리 작성 후 아래와 같이 사용 가능.

1
2
3
4
class car{
Engine e = new Engine();
Door[] d = new Door[4];
}

1.3 클래스간 관계 결정하기

어떨때 상속, 어떨때 포함관계를 맺을까?

  • 상속: ~은 ~이다(is-a)
  • 포함 : ~은 ~을 가지고 있다(has-a)

ex) 원은 점(point)이다 (X)
원은 점을 가지고 있다(O) -> 포함관계
SportCar는 Car이다(0) -> 상속 관계

1.4 단일 상속

부모클래스를 두개 이상 상속 받을 수 없다.

1
2
3
4
//불가능한 경우
calss TVCR extends TV, VCR{

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Tv{
boolean power;
int channel;

void power(){power = !power;}
void channelUp(){++chnannel;}
void channelDown(){--channel;}
}

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

class TVCR extends Tv{
VCR vcr = new VCR();
int counter =vcr.counter;

void play(){
vcr.play();
};
void stop(){
vcr.stop();
};
void rew(){
vcr.rew();
};
void ff(){
vcr.ff();
};

1.5 Object클래스 -모든 클래스의 조상

Object는 모든 클래스의 조상이다.

1
class Tv{}

이것은 따지고 보면

1
class Tv extends Object{}

이것임.
toString(), equals() 그냥 쓸 수 있는게 Object클래스에 정의 되어있어서 그렇다.

[CH6. 객체지향프로그래밍1 ] 변수의 초기화

6. 변수의 초기화

6.1 변수의 초기화

변수를 선언하고 처읍으로 값을 저장하는것.
가능하면 선언과 동시에 초기화 하는게 바람직.

  • 멤버변수(클래스변수와 인스턴스 변수)와 배열의 초기화는 선택적이지만,
    지역변수는 반드시 초기화 후 사용해야함.

멤버변수 추기화 방법

  1. 명시적 초기화
  2. 생성자
  3. 초기화 블럭
    • 인스턴스 초기화 블럭: 인스턴스변수를 초기화 하는데 사용
    • 클래스 초기화 블럭: 클래스를 초기화 하는데 사용

6.2 명시적 초기화

변수 선언과 동시에 초기화 하는것

1
2
3
4
class Car{
int door= 4; //기본형 변수 초기화
Engine = new Engine(); // 참조형 변수 초기화
}

6. 3 초기화 블럭

  • 초기화 블럭 - 클래스변수의 복잡한 초기화에 사용
  • 인스턴스 초기화 블럭 - 인스턴스 변수의 복잡한 초기화에 사용
1
2
3
4
class InitBlock{
static{/* 클래스 초기화 블럭*/}
{/*인스턴스 초기화 블럭 */}
}

클래스 초기화 블럭은 클래스가 메모리에 올라가 갈 때 한번만 수행.
인스턴스 초기화는 인스턴스 생성될때 생성자보다 먼저 수행됨.

1
2
3
4
5
6
7
8
9
10
Car(){
System.out.println("Car인스턴스가 생성되었습니다.");
color= "withe";
gearType="auto";
}
Car(String color, String gearType){
System.out.println("Car인스턴스가 생성되었습니다.");
this.color= color;
this.gearType=gearType;
}

동일한 “Car인스턴스가 생성되었습니다.” 처리를 인스턴스 블럭으로 아래와 같이 처리

1
2
3
4
5
6
7
8
9
10
{ System.out.println("Car인스턴스가 생성되었습니다."); }

Car(){
color= "withe";
gearType="auto";
}
Car(String color, String gearType){
this.color= color;
this.gearType=gearType;
}

6.4 멤버변수의 초기화 시기와 순서

  • 클래스 변수 초기화 시점 - 클래스가 처음 로딩될 때 한번 초기화
  • 인스턴스 변수의 초기화 시점 - 인스턴스가 생성될 때마다 각 인스턴스별로 초기화 이루어짐

  • 클래스 변수 초기화 순서 : 기본값 -> 명시적 초기화 -> 클래스 초기화 블럭

  • 인스턴스 변수 초기화 순서 : 기본값 -> 명시적 초기화 -> 인스턴스초기화 블럭 -> 생성자
1
2
3
4
5
6
7
8
9
10
11
class Product{
static int count =0; //생성된 인스턴스 수를 저장하기 위한 변수
int serialNo; //인스턴스 고유 번호

{
++count;
serialNo = count; // Product인스턴스가 생성될 때마다 count 값을 1증가시켜 serialNo에 저장
}

public product(){}
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×