스트림이란 단일 방향으로 연속적으로 흘러가는 것을 의미하는데, 데이터가 출발지에서 도착지로 흘러가는 것을 의미한다.
입력 스트림(InputStream) : 프로그램으로 데이터를 읽어드리는 스트림. 프로그램이 도착지인 경우 사용
출력 스트림(OutputStream) : 프로그램으로 데이터를 내보내내는 스트림. 프로그램이 출발지인 경우 사용
입출력 스트림의 종류
바이트(Byte)기반 스트림: 그림, 멀티미디어 등의 바이너리 데이터를 읽고 출력할 때 사용
문자(Character)기반 스트림: 문지 데이터를 읽고 출력할 때 사용
구분
바이트 기반 스트림
문자기반 스트림
입력 스트림
출력 스트림
입력 스트림
출력 스트림
최상위 클래스
InPutStream
OutputStream
Reader
Writer
하위 클래스(예)
XXXInPutStream ( FileInPutStream )
XXXOutputStream ( FileOutputStream )
XXXReader ( FileReader )
XXXWriter ( FileWriter )
바이트 출력 스트림 : OutputStream
OutputStream은 바이트 기반 출력 스트림의 최상위 클래스로 추상클래스 이다.
OutputStream의 주요 메소드
리턴타입
메소드
설명
void
write(int b)
1byte를 출력합니다.
void
write(byte[ ] b)
매개값으로 주어진 배열 b의 모든 바이트를 출력합니다.
void
write(byte[ ] b, int off, int len)
매개값으로 주어진 배열 b[off]부터 len개까지의 바이트를 출력합니다.
void
flush()
출력 버퍼에 잔류하는 모든 바이트를 출력합니다.
void
close()
출력 스트림을 닫습니다.
바이트 입력 스트림 : InputStream
InputStream은 바이트 기반 출력 스트림의 최상위 클래스로 추상클래스 이다.
InputStream의 주요 메소드
리턴 타입
메소드
설명
int
read()
1byte를 읽고 읽은 바이트를 리턴합니다.
int
read(byte[ ] b)
읽은 바이트를 매개값으로 주어진 배열에 저장하고 읽은 바이트 수를 리턴합니다.
int
read(byte[ ] b, int off, int len)
len개의 바이트를 읽고 매개값으로 주어진 배열에서 b[off]부터 len개까지 저장합니다. 그리고 읽은 바이트 수를 리턴합니다.
void
close()
입력 스트림을 닫습니다.
문자 출력 스트림 : Writer
Writer는 문자 기반 출력 스트림의 최상위 클래스로 추상 클래스이다.
Writer 클래스의 주요 메소드
리턴 타입
메소드
설명
void
write(int c)
매개값으로 주어진 한 문자를 보냅니다.
void
write(char[ ] cbuf)
매개값으로 주어진 배열의 모든 문자를 보냅니다.
void
write(char[ ] cbuf, int off, int len)
매개값으로 주어진 배열에서 cbuf[off]부터 len까지의 문자를 보냅니다.
void
write(String str)
매개값으로 주어진 문자열 보냅니다.
void
write(String str, int off, int len)
매개값으로 주어진 문자열에서 off 순번부터 len개까지의 문자를 보냅니다.
void
flush()
버퍼에 잔류하는 모든 문자를 출력합니다.
void
close()
출력 스트림을 닫습니다.
문자 입력 스트림 : Reader
Reader는 문자 기반 입력 스트림의 취상위 클래스로 추상클래스 이다.
Reader의 주요 메소드
리턴 타입
메소드
설명
int
read()
1개의 문자를 읽고 리턴합니다.
int
read(char[ ] cbuf)
읽은 문자를 매개값으로 주어진 문자 배열에 저장하고 읽은 문자수를 리턴합니다.
int
read(char[ ] cbuf, int off, int len)
len개의 문자를 읽고 매개값으로 주어진 문자 배열에서 cbuf[off]부터 len개까지 저장합니다. 그리고 읽은 문자 수를 리턴합니다.
void
close()
입력 스트림을 닫습니다.
[ 키워드 정리 ]
입출력 스트림: 자바에서 데이터는 스트림을 통해 입출력됩니다. 프로그램이 출발지냐 또는 도착지냐에 따라서 사용하는 스트림의 종류가 결정됩니다. 프로그램이 도착지이면 흘러온 데이터를 입력받아야 하므로 입력 스트림을 사용합니다. 반대로 프로그램이 출발지면 데이터를 출력해야 하므로 출력 스트림을 사용합니다.
InputStream: 바이트 기반 입력 스트림의 최상위 클래스로 추상 클래스입니다. 모든 바이트 기반 입력 스트림은 InputStream 클래스를 상속받아서 만들어집니다. InputStream 클래스에는 바이트 기반 입력 스트림이 기본적으로 가져야 할 메소드가 정의되어 있습니다.
OutputStream : 바이트 기반 출력 스트림의 최상위 클래스로 추상 클래스입니다. 모든 바이트 기반 출력 스트림 클래스는 OutputStream 클래스를 상속받아서 만들어집니다. OutputStream 클래스에는 모든 바이트 기반 출력 스트림이 기본적으로 가져가야 할 메소드가 정의되어 있습니다.
Reader : 문자 기반 입력 스트림의 최상위 클래스로 추상 클래스입니다. 모든 문자 기반 입력스트림은 Reader 클래스를 상속받아서 만들어집니다. Reader 클래스에는 문자 기반 입력 스트림이 기본적으로 가져야 할 메소드가 정의되어 있습니다.
Writer : 문자 기반 출력 스트림의 최상위 클래스로 추상 클래스입니다. 모든 문자 기반 출력 스트림 클래스는 Writer 클래스를 상속받아서 만들어집니다. Writer 클래스는 모든 문자 기반 출력 스트림이 기본적으로 가져야 할 메소드가 정의되어 있습니다.
컬렉션 프레임워크(Collection Framework) :자료구조를 사용해서 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 인터페이스와 구현 클래스를 java.util 패키지에서 제공하는 것
컬렉션 : 객체의 저장을 의미
프레임워크 : 사용 방법을 정해놓은 라이브러리를 의미
컬렉션 프레임워크의 주요 인터페이스로는 List, Set, Map이 있다.
List 컬렉션
List 컬렉션은 배열과 비슷하게 객체를 인덱스로 관리한다.
배열과의 차이점은 저장 용량(capacity)이 자동으로 증가하며, 객체를 저장할 때 자동 인덱스가 부여된다는 점이다.
객체 자체를 저장하는 것이 아니라 객체의 번지를 참조한다는 특징이 있다.
그렇기 때문에 동일한 객체를 중복 저장할 수 있는데, 이 경우 동일한 번지가 참조된다.
< List 컬력션에서 공통으로 사용 가능한 List 인터페이스의 메소드 >
기능
메소드
설명
객체 추가
boolean add(E e)
주어진 객체를 맨 끝에 추가합니다.
void add(int index, E element)
주어진 인덱스에 객체를 추가합니다.
E set(int index, E element)
주어진 인덱스에 저장된 객체를 주어진 객체로 바꿉니다.
객체 검색
boolean contains(Object o)
주어진 객체가 저장되어 있는지 조사합니다.
E get(int index)
주어진 인덱스에 저장된 객체를 리턴합니다.
boolean isEmpty()
걸렉션이 비어 있는지 조사합니다.
int size()
저장되어 있는 전체 객체 수를 리턴합니다.
객체 삭제
void clear()
저장된 모든 객체를 삭제합니다.
E remove(int index)
주어진 인덱스에 저장된 객체를 삭제합니다.
boolean remove(Object o)
주어진 객체를 삭제합니다.
※ 위의 표에서 E라는 타입 파라미터는 저장되는 개체의 타입을 List 컬렉션을 생성할 때 결정하라는 뜻
ArrayList
List<String> list = new ArrauList< >( );
기본 생성자로 ArrayList 객체를 생성하면 내부에 10개의 객체를 저장할 수 있는 초기 용량을 가지게 된다.
ArrayList에서 특정 인덱스의 객체를 제거하면 바로 뒤 인덱스부터 마지막 인덱스까지 모두 앞으로 1씩 당겨진다. 마찬가지로 특정 인덱스에 객체를 삽입하면 해당 인덱스부터 마지막 인덱스까지 모두 1씩 밀려난다.
따라서 빈번한 객체 삭제와 삽입이 일어나는 곳에서는 ArrayList를 사용하는 것 보다 LinkedList를 사용하는 것이 좋다.
반면, 인덱스를 이용해서 객체를 찾거나 맨 마지막에 객체를 추가하는 경우는 ArrayList가 더 좋은 성능을 발휘한다.
Vector
List<E> list = new Vector<E>( );
ArrayList와 다른 점은 Vector는 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 Vector의 메소드를 실행할 수 없고, 하나의 스레드가 메소드 실행을 완료해야만 다른 스레드가 메소드를 실행할 수 있다는 점이다.
멀티 스레드 환경에서 안전하게 객체를 추가, 삭제할 수 있다. = 스레드 안전(thread safe)
LinkedList
ArrayList는 내부 배열에 객체를 저장해서 관리하지만, LinkedList는 인접 참조를 링크해서 체인처럼 관리한다.
List<E> list = new LinkedList<>( );
특정 인덱스의 객체를 제거하면 앞뒤 링크만 변경되고 나머지 링크는 변경되지 않는다.
끝에서부터(순차적으로) 추가 또는 삭제하는 경우는 ArrayList가 빠르지만, 중간에서 추가나 삭제하는 경우는 앞뒤 링크 정보만 변경되는 LinkedList가 더 빠르다.
Set 컬렉션
List 컬렉션은 객체의 저장 순서를 유지하지만, Set 컬렉션은 저장 순서가 유지되지 않는다.
객체를 중복해서 저장할 수 없고, 하나의 null만 저장할 수 있다.
Set 컬렉션에는 HashSet, LinkedHashSet, TreeSet 등이 있다.
인덱스로 관리하지 않기 때문에 인덱스를 매개값으로 갖는 메소드가 없다.
< Set 인터페이스의 메소드 >
기능
메소드
설명
객체 추가
boolean add(E e)
주어진 객체를 저장합니다. 객체가 성공적으로 저장되면 true를 리턴하고 중복 객체면 false를 리턴합니다.
객체 검색
boolean contains(Object o)
주어진 객체가 저장되어 있는지 조사합니다.
boolean isEmpty()
컬렉션이 비어있는지 조사합니다.
lterator<E> iterator()
저장된 객체를 한 번씩 가져오는 반복자를 리턴합니다.
int size()
저장되어 있는 전체 객체 수를 리턴합니다.
객체 삭제
void clear()
저장된 모든 객체를 삭제합니다.
boolean remove(Object o)
주어진 객체를 삭제합니다.
Set<String> set = ...;
Set 컬렉션은 인덱스로 객체를 검색해서 가져오는 메소드가 없기 때문에, 대신 전체 객체를 대상으로 한 번씩 반복해서 가져오는 반복자 Iterator를 제공한다.
Iterator<String> iterator = set.iterator( );
< Iterator 인터페이스에 선언된 메소드 >
리턴 타입
메소드
설명
boolean
hasNext()
가져올 객체가 있으면 true를 리턴하고 없으면 false를 리턴한다.
E
next()
컬렉션에서 하나의 객체를 가져온다.
void
remove()
Set 컬렉션에서 객체를 제거한다.
while(iterator.hasNext()){ //저장된 객체수 만큼 루핑
String str = iterator.next(); //String 객체 하나를 가져옴
if(str.equals("지니")){
iterator.remove(); //객체를 제거
}
}
HashSet
Set<E> set = new HashSet<E>();
객체들을 순서 없이 저장하고 동일한 객체는 중복 저장하지 않음
객체를 저장하기 전에 먼저 객체의 hashCode()메소드를 호출해서 해쉬코드를 얻어내고, 이미 저장되어 있는 객체들의 해시코드와 비교한다. 만약 동일한 해시코드가 있다면 다시 equals() 메소드로 두 객체를 비교해서 true가 나오면 동일한 객체로 판단하고 중복 저장을 하지 않는다.
문자열을 저장할 경우, 같은 문자열을 갖는 String 객체는 동등한 객체로 간주된다.
Map 컬렉션
key(키) 와 value(값)로 구성된 Map.Entry 객체를 저장하는 구조로 Entry는 Map 인터페이스 내부에 선언된 중첩 인터페이스이다.
키와 값은 모두 객체이다.
키는 중복 저장될 수 있지만, 값은 중복 저장될 수 없다.
만약 기존에 저자된 키와 동일한 키로 값을 저장하면 기존의 값은 없어지고 새로운 값으로 대체된다.
HashMap, Hashtable, LinkedHashMap, Properties, TreeMap 등이 있다.
< Map 컬렉션의 메소드 >
기능
메소드
설명
객체 추가
V put(K ket, V value)
주어진 키로 값을 저장합니다. 새로운 키일 경우 null을 리턴하고 동일한 키가 있을 경우 값을 대체하고 이전 값을 리턴합니다.
객체 검색
boolean containsKey(Object key)
주어진 키가 있는지 여부를 확인합니다.
boolean containsValue(Object value)
주어진 값이 있는지 여부를 확인합니다.
Set<Map.Entry<K,V>> entrySet()
키와 값이 쌍으로 구성된 모든 Map.Entry 객체를 Set에 담아서 리턴합니다.
V get(Object key)
주어진 키가 있는 값을 리턴합니다.
booleab isEmpty()
컬렉션이 비어있는지 여부를 확인합니다.
Set<K> keySet()
모든 키를 Set객체에 담아서 리턴합니다.
int size()
저장된 키의 총 수를 리턴합니다.
Collection<V> value()
저장된 모든 값을 Collection에 담아서 리턴합니다.
객체 삭제
void clear()
모든 Map.Entry(키와 값)를 삭제합니다.
V remove(Object key)
주어진 키와 일치하는 Map.Entry를 삭제하고 값을 리턴합니다.
HashMap
HashMap의 키로 사용할 객체는 hashCode()와 equals()메소드를 재정의해서 동등 객체가 될 조건을 정해야 한다.
객체가 달라도 동등 객체라면 같은 키로 간주하고 중복 저장되지 않도록 하기 위함이다.
키와 값의 타입은 기본타입을 사용할 수 없고 클래스 및 인터페이스 타입만 사용 가능하다.
키로 String 타입을 사용하고 값으로 Integer타입을 사용하는 HashMap은 다음과 같이 생성할 수 있다.
Map<String, Integer> map = new HashMap<>();
Hashtable
HashMap과의 차이점은 Hashtable은 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 Hashtable의 메소드를 실행할 수 없고, 하나의 스레드가 실행을 완료해야만 다른 스레드를 실행할 수 있다는 것
멀티스레드 환경에서 안전하게 객체를 추가, 삭제할 수 있기 때문에 Hashtale은 스레드에 안전하다.
스레드를 생성하고 시작하면 스레드는 다양한 상태를 가지게 된다. 스레드의 상태는 자동으로 변경될 수도 있고, 코드에 의해서 변경될 수도 있다.
스레드 객체를 생성하고 start() 메소드를 호출하면 바로 실행되는 것이 아니라 실행 대기 상태가 된다.
실행 대기 상태란 언제든지 실행할 준비가 되어 있는 상태를 의미한다.
실행 상태의 스레드는 run() 메소드를 모두 실행하기 전에 다시 실행 대기 상태로 돌아갈 수 있고, 실행 대기 상태에 있는 다른 스레드가 선택되어 실행 상태가 되기도 한다.
실행 상태에서 run() 메소드의 내용이 모두 실행되면 스레드의 실행이 멈추고 종료 상태가 된다.
스레드 상태
실행 상태(running): 실행 대기 상태에 있는 스레드 중에서 운영체제는 하나의 스레드를 선택하고 CPU(코어)가 run()메소드를 실행하도록 한다. 이때를 실행 상태라고 한다.
종료(terminated) 상태 : 실행 상태에서 run() 메소드가 종료되면, 더 이상 실행할 코드가 없기 때문에 스레드의 실행이 멈추게 된 상태.
스레드는 실행 대기 상태와 실행 상태로 번갈아 변하면서, 경우에 따라 실행 상태에서 일시 정지 상태로 가기도 한다. 일시 정지 상태는 스레드가 실행할 수 없는 상태이다. 일시 정지 상태에서는 바로 실행 상태로 돌아갈 수 없고, 일시 정지 상태에서 빠져나와 실행 대기 상태로 가야 한다.
스레드 상태 제어
스레드 상태 제어 : 실행 중인 스레드의 상태를 변경하는 것
스레드 상태 제어는 주어진 시간 동안 일시 정지시키는 sleep() 메소드와 스레드를 안전하게 종료시키는 stop 플래그, interrupt()메소드를 사용한다.
아래의 그림에서 취소선으로 표시한 메소드는 스레드의 안전성을 해친다고 하여 더 이상 사용하지 않도록 권장된 Deprecated 메소드 이다.
메소드
설명
interrupt()
일시 정지 상태의 스레드에서 InterruptedException을 발새시켜, 예외 처리 코드(catch)에서 실행 대기 상태로 가거나 종료 상태로 갈 수 있도록 한다.
sleep(long millis)
주어진 시간 동안 스레드를 일시 정지 상태로 만든다. 주어진 시간이 지나면 자동적으로 실행 대기 상태가 된다.
실행 중인 스레드를 일정 시간 멈추게 하고 싶다면 Thread 클래스의 정적 메소드인 sleep()을 사용하면 된다. 다음과 같이 Thread.sleep() 메소드를 호출한 스레드는 주어진 시간 동안 일시 정지 상태가 되고, 다시 실행 대기 상태로 돌아간다.
안전한 종료
Thread는 스레드를 즉시 종료하기 위해서 stop()메소드를 제공하고 있는데, 이 메소드는 deprecated(중요도가 떨어져 이제 사용되지 않음)되었다.
그 이유는 stop()메소드로 스레드를 갑자기 종료하면 스레드가 사용 중이던 자원들이 불안전한 상태로 남겨지기 때문이다.
스레드를 안전하게 종료하기 위해서 stop플래그를 이용하거나 interrupt() 메소드를 이용하는 방법이 있다.
stop 플래그를 이용하는 방법
스레드는 run() 메소드가 끝나면 자동적으로 종료되므로, run() 메소드가 정상적으로 종료되도록 유도하는 것이다. 다음 코드는 stop 풀래그를 이용해서 run() 메소드의 종료를 유도한다.
public class XXXThread extends Thread{
private boolean stop; // stop 플래그
public void run(){
while(!stop){
// 스레드가 반복 실행하는 코드;
}
// 스레드가 사용한 자원 정리
}
}
위 코드에서 stop 필드가 false일 경우에는 while문의 조건식이 true가 되어 반복 실행하지만, stop 필드가 true일 경우에는 while문의 조건식이 false가 되어 while문을 빠져나온다. 그리고 스레드가 사용한 자원을 정리하고, run() 메소드가 끝나게 됨으로써 스레드는 안전하게 종료된다.
데몬 스레드
주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드
주 스레드가 종료되면 데몬 스레드는 강제적으로 자동 종료 되는데, 주 스레드의 보조 역할을 수행하므로 주 스레드가 종료되면 데몬 스레드의 존재 의미가 사라지기 때문이다.