Transcript java3_two

6 ███████████
6
6장 배열, 제너릭, 컬렉션
소설같은
3
자바
rd
Edition
since 2001
최영관
Powered by http://www.jabook.org
6.1 배경
6.1.1 데이터 저장 공간
▣
프로그래밍의 기본
◈
▣
데이터 저장공간
◈
◈
▣
기본 데이터 타입의 변수
일반적인 클래스의 객체
배열(Array) 클래스
◈
▣
데이터 저장 공간을 이해하는 것
단순 용량적인 측면에서의 데이터 저장공간
컬렉션(Collection)류나 맵(Map)류의 클래스
◈
자료구조적인 측면에서의 데이터 저장공간
6.1.2 용량적인 저장 공간
▣
10개의 int형 변수 선언
◈
▣
배열을 이용한 10개의 int형 변수 선언
◈
▣
int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9;
int[] ar = new int[10];
Top 클래스
a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]
◈
class Top {
◈
//...클래스의 내용
◈ }
▣
Top 클래스 객체 10개 선언
◈
▣
Top t0, t1, t2, t3, t4, t5, t6, t7, t8, t9;
t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9]
배열을 이용한 Top 클래스 객체 10개 선언
◈
Top[] t = new Top[10];
6.1.3 자료구조적인 저장 공간
▣
단순 저장공간의 문제
◈
◈
◈
▣
배열
◈
▣
양적인 저장 공간
배열의 검색
◈
▣
자료를 처리하는 방식의 문제(자료구조적인 측면)
동적인 공간의 문제(메모리 공간)
빠른 검색의 문제
사용자가 직접 전체 데이터를 확인해서 검색해야 한다.
해결책
◈
컬렉션류나 맵류의 클래스 사용
6.2 배열
6.2.1 배열의 소개
▣
배열의 종류
◈
◈
▣
배열의 특징
◈
▣
같은 데이터 타입의 변수를 한꺼번에 여러 개 생성할 수 있다.
배열의 검색
◈
▣
기본 데이터 타입 배열
객체 배열
배열은 첨자를 통해서 배열 내에 존재하는 모든 변수를 검색할 수
있다.
배열의 단점
◈
배열을 생성할 때 첨자를 이용해서 배열의 크기를 정해버리면 배열
의 크기를 변경할 수 없다.
6.2.2 배열의 특징
▣
배열의 특징 I
◈
◈
◈
▣
배열의 특징 II
◈
◈
▣
◈
배열의 이름은 연속된 변수들을 참조하기 위한 참조값이다.
배열 자체는 객체이다.
int[] ar = new int[10];
배열을 위한 데이터 타입
◈
◈
▣
기본 데이터 타입을 이용
한 배열 클래스의 예
int형 변수 10개를 만드는 구문(int형 배열의 생성)
◈
▣
▣
배열의 요소는 변수이다.
배열의 메모리는 연속적으로 잡히게 된다.
배열의 특징 III
◈
▣
같은 데이터 타입의 변수를 한꺼번에 여러 개 생성할 수 있다.
배열의 크기는 배열의 첨자로 결정된다.
첨자에 해당하는 만큼의 같은 데이터 타입을 가진 변수가 생성된다.
int[]는 데이터 타입이다.
int[]는 배열 클래스형이다.
배열의 데이터 타입
◈
배열 데이터 타입 = 기존의 데이터 타입 + []
▣
◈
byte[]
◈
int[]
◈
long[]
◈
float[]
◈
double[]
클래스를 이용한 배열 클래스의 예
◈
String[]
◈
Image[]
6.2.3 배열의 분석
▣
배열의 생성
◈
▣
기본 데이터 타입 배열
◈
◈
▣
배열의 분석
int[] ar = new int[10];
기본 데이터 타입으로 배열을 선언하면 메모리의 생성을 의
미한다.
기본 데이터 타입은 변수의 선언과 동시에 메모리가 생성되
기 때문에 기본 데이터 타입으로 배열을 선언하면 배열의
전체 메모리가 생성된다.
배열 클래스
참조 변수
int[]
ar
데이터 타입
배열의 정의
◈
참조 변수
배열은 객체다.
int[]
▣
배열의 특징
◈
◈
◈
▣
배열의 첨자는 배열의 크기를 의미한다.
배열의 이름은 참조값이다.
배열끼리의 할당은 참조값 값복사다.
배열의 개수를 확인하는 멤버
◈
◈
◈
배열의 개수는 length 멤버를 이용해서 알 수 있다.
int[] ar = new int[10];
int size = ar.length; // size는 10이 된다.
클래스명
ar
배열
메모리 생성 연산자
=
배열 개수 지정
new int[10];
배열의 이름
배열의 생성
ar[0]
변수
ar[1]
변수
ar[2]
변수
ar[3]
변수
ar[4]
변수
ar[5]
변수
ar[6]
변수
ar[7]
변수
ar[8]
변수
ar[9]
변수
첨자
배열의 개수
= 첨자
6.2.4 배열의 초기화
▣
배열의 생성과 배열의 접근을 테스트하는 예제
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
배열의 선언과 할당의 분리
public class ArrayMain{
public static void main(String[] args){
int[] ar = new int [10];
int[] ap = new int[]{0,1,2,3,4,5,6,7,8,9};
int[] aw = {10,11,12,13,14,15,16,17,18,19};
ar[0] = 100;
int[] ad = new int[10]; //선언과 할당의 분리
ar[1] = 200;
ad[0] = 20; ad[1] = 21; ad[2] = 22; ad[3] = 23;
ar[2] = 343;
ar[3] = 325;
각각의 변수에
ad[4] = 24; ad[5] = 25; ad[6] = 26; ad[7] = 27;
ar[4] = 5;
하나씩 값할당
ar[5] = 2;
ad[8] = 28; ad[9] = 29;
ar[6] = 7;
//1. int[] ap 출력
ar[7] = 30;
ar[8] = 35;
for(int i=0; i<ap.length; i++){
ar[9] = 43;
System.out.print(ap[i] + "\t");
}
//2. int[] aw 출력
for(int i=0; i<aw.length; i++){
배열의 선언과 동시에 초기화
System.out.print(aw[i] + "\t");
}
1
//3. int[] ad 출력
int[] ar = new int[] {100, 200, 343, 325, 5, 2, 7, 30, 35, 43};
for(int i=0; i<ad.length; i++){
System.out.print(ad[i] + "\t"); 2
}
int[] ar = {100, 200, 343, 325, 5, 2, 7, 30, 35, 43};
}
이 방법들은 선언과 초기화를 동시에 하는 방법이다.
}
위의 방법 둘 다 사용할 수 있다.
6.2.5 배열의 복사
배열의 참조값 값복사
3
int[]
ar1
= new int[]{0,1,2,3,4,5,6,7,8,9};
int[]
ar2
= ar1;
메모리 영역
4
new int[]{0,1,2,3,4,5,6,7,8,9}
Index Table
실제 메모리 주소
객체의 실제 주소
2
▣
10001
FFFF:07CA
참조값
(해시코드)
실제 주소
부분 배열 복사를 위한 System.arraycopy()
◈
◈
◈
◈
◈
◈
◈
int[] source = new int[]{5, 4, 6, 9, 7, 9};
int[] target = {100, 200, 300, 400, 500, 600,700};
//부분 배열 복사의 예
System.arraycopy(source, 2, target, 3, 4);
for(int i=0; i<target.length; i++) {
System.out.println("target["+i+"]:" + target[i]);
}
▣
1
ar1[0] = 0;
ar1[1] = 1;
ar1[2] = 2;
ar1[3] = 3;
ar1[4] = 4;
ar1[5] = 5;
ar1[6] = 6;
ar1[7] = 7;
ar1[8] = 8;
ar1[9] = 9;
배열의 실제 메모리
참조값
clone()을 이용한 메모리 차원의 배열복사
◈
◈
◈
◈
◈
◈
int[] source = new int[]{5, 4, 6, 9, 7, 9};
//clone()을 이용한 메모리 복사
int[] target = (int[])source.clone();
for(int i=0; i<target.length; i++){
System.out.println("target["+i+"] : " + target[i]);
}
6.2.6 객체 배열
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class Dog {
private String name;
private String kind;
public Dog(String name, String kind) {
this.name = name;
this.kind = kind;
}
public String toString() {
return kind + ":" + name;
}
}
▣
객체 배열
◈객체
▣
▣
▣
public class ObjectArrayMain {
public static void main(String[] args) {
//1. 객체배열의 생성
참조 변수에 대한 객체의 메모리는 생성되지 않는다.
기본 데이터 타입 배열 vs 객체 배열
◈기본
▣
배열을 선언했을 때 첨자의 수만큼 참조 변수가 만들어진다.
◈각각의
데이터 타입 배열은 배열 생성과 동시에 메모리가 생성된다.
◈객체
배열은 객체 변수(참조 변수)의 이름들만 생성되고, 객체들의 실제 메모리
는 생성되지 않는다.
▣
Dog[] dog = new Dog[5];
▣
▣
for(int i=0; i<dog.length; i++)
System.out.println("Dog[" + i + "] : " + dog[i]);
//2. 객체배열의 참조 변수에 메모리할당
▣
dog[0] = new Dog("쫑쫑이","불독");
dog[1] = new Dog("워리","똥개");
dog[2] = new Dog("개똥이","진도개");
dog[3] = new Dog("발리","푸들");
dog[4] = new Dog("투덜이","잡종");
▣
▣
▣
▣
▣
▣
▣
▣
▣
for(int i=0; i<dog.length; i++)
System.out.println("Dog[" + i + "] : " + dog[i]);
}
}
6.2.7 결론
▣
자바의 배열
배열의 이름은 참조 변수다.
◈ 자바에서 배열은 객체이다.
◈ int[]까지가 배열 데이터 타입이다.
◈
▣
자바의 배열 표기법
◈
int[] ar = new int[10];
◈ int ar[] = new int[10];
▣
배열의 복사
System.arraycopy()를 이용한 부분 배열 복사
◈ clone()을 이용한 배열 통복사
◈
▣
NullPointerException
◈
객체 변수에 메모리가 없다.
6.3 배열의 참조
6.3.1 배열의 참조
매개변수의 참조값 값복사
int[]
ar = new int[]{0,1,2,3,4,5,6,7,8,9};
배열 매개변수로 메서드 호출
4
참조 값복사가 발생
3
메모리 영역
sum(ar);
ar 에 10001의 참조값
temp 에 10001의 참조값
new int[]{0,1,2,3,4,5,6,7,8,9}
Index Table
실제 메모리 주소
▣
배열끼리의 할당
◈
◈
▣
실제 주소
1
= 0;
= 1;
= 2;
= 3;
= 4;
= 5;
= 6;
= 7;
= 8;
= 9;
int[] ar1 = new int[]{0,1,2,3,4,5,6,7,8,9};
int[] ar2 = ar1;
객체의 참조값
◈
객체의 참조값만 있으면 해당 객체를 핸들할 수 있다.
참조값 값복사 발생
5
메서드의 원형
참조값
FFFF:07CA
객체의 실제 주소
2
10001
ar[0]
ar[1]
ar[2]
ar[3]
ar[4]
ar[5]
ar[6]
ar[7]
ar[8]
ar[9]
배열의 실제 메모리
참조값
int[] temp = ar;
int sum ( int[] temp ) {
//배열의 총합계산
}
6.3.2 배열 할당의 참조
직접할당을 이용한 참조값 값복사
int[] aref = new int[]{0,1,2,3};
메모리 영역
3
10001
참조값
int[] cref = bref;
실제 메모리 주소
2
FFFF:07CA
실제 주소
5
▣
int[]형의 배열 생성
◈
▣
참조값 값복사(참조값 할당)
◈
◈
▣
int[] aref = new int[]{0,1,2,3};
int[] bref = aref;
int[] cref = bref;
주의할 점
◈
◈
◈
배열의 이름은 참조 변수다.
배열끼리 할당할 때 데이터 타입은 같아야 한다.
위의 배열들은 데이터 타입이 모두 int[]이다.(aref, bref, cref)
1
ar[0] = 0;
ar[1] = 1;
ar[2] = 2;
ar[3] = 3;
배열의 실제 메모리
4
참조값
new int[]{0,1,2,3}
객체의 실제 주소
int[] bref = aref;
Index Table
6.3.3 객체의 참조
▣
▣
▣
▣
▣
▣
class Baby{
private String name;
public void setName(String name){
this.name = name;
}
public void cry() {
▣
System.out.println(name + " 가(이) 응애응애");
▣
}
▣ }
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
public class BabyRefMain {
public static void main(String[] args) {
Baby b = new Baby();
b.setName("둘리");
b.cry();
Baby s = b; //참조값 값복사 발생
s.setName("아기공룡");
s.cry();
b.cry();
}
}
6.3.4 매개변수로서의 배열
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class ArrayParam {
public int[] copyArray( int[] src) {//배열 복사를 위한 메모리를 메서드 내에서 생성
int[] des = new int[src.length]; //새로운 배열 생성
for(int i=0; i<src.length; i++)
des[i] = src[i]; //값복사
return des;
}
public void copyArray (int[] src, int[] des) {//배열 복사를 위한 메모리를 매개변수로 받음
for(int i=0; i<src.length; i++)
des[i] = src[i]; //값복사
}
}
▣
▣
▣
int[] source = new int[]{1,2,3,4,5}; //배열 생성
ArrayParam p = new ArrayParam(); //객체 생성
1. copyArray(int[] src) 호출
◈
◈
◈
◈
▣
int[] result = p.copyArray(source);
for(int i=0; i<result.length; i++){
System.out.println("result["+i+"] : " + result[i]);
}
2. copyArray(int[] src, int[] des) 호출
◈
◈
◈
◈
◈
int[] target = new int[source.length];
p.copyArray(source, target);
for(int i=0; i<target.length; i++) {
System.out.println("target["+i+"] : " + target[i]);
}
6.3.5 다차원 배열
▣
▣
2차원 배열의 선언과 초기화를 동시에
int[][] src = new int[][]{{100,200,300}, {400,500,600}};
int[][] tar = {{701,702,703}, {704,705,706}};
1
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
//int[][] tar의 정보출력
System.out.print("tar.length:" + tar.length + "\t");
System.out.print("tar[0].length:" + tar[0].length + "\t");
System.out.println("tar[1].length:" + tar[1].length);
선언과 동시에 초기화 (배열의 개수는 자동으로 [2][3]이 된다)
int[][] ar = new int[][]{{100, 200, 300}, {400,500,600}};
클래스명
2
선언과 동시에 초기화 (배열의 개수는 자동으로 [2][3]이 된다)
int[][] ar = {{100, 200, 300}, {400,500,600}};
클래스명
이 방법들은 선언과 초기화를 동시에 하는 방법
입니다. 위의 방법 둘 다 사용할 수 있습니다.
//2차원 배열의 출력
for(int i=0; i<src.length; i++)
for(int j=0; j<src[i].length; j++){
System.out.print("src[" + i + "][" + j + "]=" + src[i][j] + "\t");
System.out.print("tar[" + i + "][" + j + "]=" + tar[i][j] + "\n");
}
//for문에 블록이 없으면 한 줄에 영향을 준다.
2차원 배열의 선언과 할당의 분리
배열 클래스
참조 변수
int[][] ar
메모리 생성 연산자 배열 개수 지정
=
new int[2][3];
ar[0][0] = 100
변수
참조 변수
ar[0][1] = 200
변수
int[][]
ar
ar[0][2] = 300
변수
클래스명
배열
ar[1][0] = 400
변수
ar[1][1] = 500
변수
ar[1][2] = 600
변수
배열의 개수
=2X3
▣
//int[][] src의 정보출력
System.out.print("src.length:" + src.length + "\t");
System.out.print("src[0].length:" + src[0].length + "\t");
System.out.println("src[1].length:" + src[1].length);
2차원 배열의 분석(배열의 배열)
2차원 배열의 분석(배열의 배열)
배열 클래스
참조변수
메모리 생성 연산자
int[][] ar
데이터 타입
배열
ar
ar[0]
1차원 배열
첨자
ar[0][0] = 100
변수
ar[0][1] = 200
변수
ar[0][2] = 300
변수
1차원 배열
원소는 3개
ar[1][0] = 400
ar[1][1] = 500
ar[1][2] = 600
변수
변수
변수
배열의 개수
=2X3
원소는 3개
참조변수
첨자
1차원 배열
원소는 2개
배열
ar[1]
배열의 배열
2차원 배열은 배열의 배열입니다.
ar의 원소는 ar[0], ar[1]
new int[2][3];
배열의 생성
참조변수
참조변수
배열의 배열
=
배열의 이름
배열 개수 지정
6.3.6 배열의 배열과 참조의 참조
▣
배열끼리의 직접할당 & 객체끼리의 직접할당
◈
◈
▣
배열의 메모리가 복사되지 않고 참조값 값복사가 이루어진다.
객체의 메모리가 복사되지 않고 참조값 값복사가 이루어진다.
Top 클래스
◈
class Top{
◈
public String s;
◈
public String p;
◈ }
▣
참조 변수의 참조 변수
◈
◈
◈
▣
Top t = new Top();
Hello
String s
String p
Top t = new Top();
t.s = new String("Hello");
t.p = new String("Hi");
Hi
참조 변수의 참조 변수와 배열의 배열
◈
참조 변수의 참조 변수의 원리는 배열의 배열과 유사한 원리로 되어 있다.
6.4 Generic(자바 5.0)
6.4.1 제너릭(Generic)이란?
▣
Generic 이란
◈
▣
Object형을 매개변수로 사용한 예
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
클래스에 사용할 타입을 디자인시에 지정하는 것이 아니라 클래스를 사용할 때 지정한 후 사용
하는 기술을 말한다.
class Top{
private Object data = null;
public void setData(Object data){
this.data = data;
}
public Object getData(){
return this.data;
}
}
Object형은 어떠한 형으로도 캐스팅가능하
기 때문에 형을 알 수 없을 때에는 Object형
을 사용하였다.
String str = "Hello Object 방식";
Top t = new Top();
t.setData(str);
String str2 = (String)t.getData();
사용할 형을 컴파일러에게 알려준다.
Generic을 매개변수로 사용한 예
◈
◈
◈
◈
◈
◈
◈
◈
◈
class Top<T>{
private T data = null;
public void setData(T data){
this.data = data;
}
public T getData(){
return this.data;
}
T라는 타입은 아직 정해지지 않았으
며 사용할 때 결정한 후 사용한다.
}
String str = "Hello Generic 방식";
Top<String> t = new Top<String>();
t.setData(str);
String str2 = t.getData();
Top<String>을 사용했기 때문에
getData() 메서드는 String형을 리턴
한다.
6.4.2 C++의 제너릭의 원리
▣
C++의 경우
◈
◈
◈
제너릭을 사용하는 순간 컴파일러는 해당 제너릭에 대한 형을 새로
만든다.
Top<String> t1 = new Top<String>();
Top<Integer> t2 = new Top<Integer>();
Top<String>
class Top<String>{
Top<Integer>
class Top<Integer>{
private String data = null;
private Integer data = null;
public void setData(String data){
public void setData(Integer data){
this.data = data;
this.data = data;
}
}
public String getData(){
public Integer getData(){
return this.data;
return this.data;
}
}
}
}
Top<String>과 Top<Integer>를 사용했기 때문에 컴파일러는 이것을 보고 Top<String>과 Top<Integer>에 해당하는 형을 통째로 만든다.
6.4.3 Java의 제너릭의 원리
▣
자바의 제너릭
◈
▣
코드 내에서 Top<String>형 클래스의 사용
◈
◈
◈
▣
자바 컴파일러는 Top<String>이나 Top<Integer>를 만나게 되면
<String>과 <Integer>를 삭제(Erasure)한 후, 컴파일러는 적절한 곳에
캐스팅(casting)으로 대체한다.
Top<String> t = new Top<String>();
t.setData(str);
String str2 = t.getData();
자바 컴파일러가 제너릭을 처리하는 방식
◈
◈
◈
Top<String>은 클래스의
형이 아니라 캐스팅을 판
단하기 위한 표시이다.
Top t = new Top();
자동으로 제거된다.
t.setData(str);
제너릭이 자동으로 붙여준다.
String str2 = (String)t.getData();
C++에서는 제너릭이 하나의 형이지만 자바에서는 캐스팅을 판단하기 위한 표시이다. 그렇기 때문에 자바
의 제너릭은 리플렉션이 적용되지 않는다. 이것은 자바의 제너릭이 타입(형)이 아니기 때문이다.
6.4.4 제너릭의 Collection 적용
▣
제너릭의 표현
◈
class Vector <E>
– 요소(Element)를 제너릭 <E>로 표현한다.
◈
class Hashtable <K, V>
– 키(Key)를 제너릭 <K>로, 값(Value)을 제너릭 <V>로 표현한다.
▣
자바 5.0 이전 버전의 Vector 사용
◈
◈
◈
Vector v1 = new Vector();
v1.add(new String("Hello World"));
String str1 = (String)v1.elementAt(0);
사용자가 직접 캐스팅을 해주어야 한다.
▣
자바 5.0 이상 버전의 Vector 사용
◈
◈
◈
두개 중 어느것을 사용해도 상관 없다.
Vector<String> v2 = new Vector<String>();
v2.add(new String("Hello World"));
String str2 = v2.elementAt(0);
캐스팅을 해주지 않아도 된다.(컴파일러가 알아서 캐스팅 해줌)
이전 버전과의 호환성이 중요하다면
제너릭을 사용하기 보다는 사용자가
직접 캐스팅하는 방법을 이용하기
바란다.
6.5 Collection과 Map
6.5.1 배열의 발전된 형태
▣
배열의 발전된 모델
◈
▣
배열과 컬렉션이나 맵계열의 클래스와의 차이점
◈
▣
배열은 크기를 동적으로 늘릴 수 없지만 컬렉션이나 맵 계열의 클래스는 동
적으로 메모리를 확장할 수 있다.
Collection류와 Map류의 클래스
◈
▣
컬렉션(Collection)과 맵(Map) 계열의 클래스
자료구조적인 기능의 클래스
Collection과 Map 인터페이스
Collection과 Map은 인터페이스이기 때문에 메서드의 프로토타입만 존재한
다.
◈ Collection을 구현해서 사용하면 집합적인 저장공간으로서의 기능을 구현
한 클래스가 된다.
◈ Map을 구현해서 사용하면 검색적인 저장공간으로서의 기능을 구현한 클
래스가 된다.
◈
6.5.2 Collection과 Map의 특징
▣
Collection과 Map 유형이 배열과 구분되는 점
◈
◈
▣
동적 메모리 확장
Collection과 Map류는 객체만을 저장의 대상으로 한다.
Collection의 삽입과 삭제를 위한 추상 메서드
◈
◈
boolean add(Object o)
boolean remove(Object o)
boolean add(E o)
boolean remove(Object o)
자바 1.4까지
▣
자바 5.0(자바 1.5)부터
Map의 삽입과 삭제를 위한 추상 메서드
◈
◈
Object put(Object key, Object value)
Object remove(Object key)
자바 1.4까지
V put(K key, V value)
V remove(Object key)
자바 5.0(자바 1.5)부터
6.5.3 Collection과 Map의 상속구조
List
Collection
순서가 있는 저장 공간
순서나 집합적인 저장공간
Set
Map
키와 값으로 데이터 핸들
LinkedList
링크드리스트
Stack
스택자료구조
Vector
동기화 보장
ArrayList
동기화 보장
하지 않음
HashSet
Set계열의
대표클래스
집합적인 저장 공간
SortedSet
Hashtable
정렬을 위한 Set 계열의 클래스
동기화 보장하는 Map 계열의 클래스
HashMap
TreeSet
동기화 보장하지 않는 Map 계열의 클래스
SortedMap
정렬을 위한 Map 계열의 클래스
TreeMap
6.5.4 Collection 인터페이스
▣
Collection 인테페이스의 특징
◈
▣
Collection 인터페이스를 구현하고 있는 클래스
◈
▣
이것을 구현한 클래스들은 모두 집합적인 저장공간으로서의 기능을 가진다.
Stack, Vector, LinkedList, TreeSet, HashSet
Collection 인터페이스의 데이터 삽입 삭제를 위한 추상 메서드
◈
boolean add(Object o);
–
◈
객체를 삽입하는 메서드
boolean remove(Object o);
–
특정 객체를 삭제하는 메서드
자바 1.4까지
▣
boolean remove(Object o)
자바 5.0(자바 1.5)부터
Collection 인터페이스의 데이터 확인을 위한 추상 메서드(5.0이전 이후 동일)
◈
boolean isEmpty();
–
◈
◈
비어 있는지 확인하는 메서드
boolean contains(Object o);
–
특정 객체가 포함되어 있는지 확인하는 메서드
int size();
–
▣
boolean add(E o)
포함된 객체의 수를 리턴하는 메서드
Collection 인터페이스의 데이터를 배열로 얻기 위한 추상 메서드
◈
Object[] toArray();
–
포함된 모든 객체들을 배열 형태로 리턴하는 메서드
자바 1.4까지
T[] toArray()
자바 5.0(자바 1.5)부터
6.5.5 Map 인터페이스
▣
Map 인터페이스의 특징
◈
▣
Map으로 구현된 클래스
◈
▣
Collection과 달리 Map은 검색적인 개념을 담고 있는 인터페이스
Attributes, HashMap, Hashtable, TreeMap
Map 인터페이스의 데이터 삽입 삭제를 위한 추상 메서드
◈
Object put(Object key, Object value);
–
◈
Object remove(Object key);
–
◈
데이터를 삽입하는 메서드
키(key)를 이용해서 데이터를 제거하는 메서드
Object get(Object key);
– 키(key)를 이용해서 데이터를 검색하는 메서드
자바 1.4까지
▣
V put(K key, V value);
V remove(Object key);
V get(Object key);
자바 5.0(자바 1.5)부터
Map 인터페이스의 데이터 확인을 위한 추상 메서드(자바 5.0 이전 이후 동일)
◈
boolean isEmpty();
–
◈
boolean containsKey(Object key);
–
◈
비어 있는지 확인하는 메서드
특정 키가 있는지 확인하는 메서드
boolean containsValue(Object value);
– 특정 데이터가 있는지 확인하는 메서드
◈
int size();
–
포함된 객체가 몇 개인지 확인하는 메서드
6.6 컬렉션 클래스 비교
6.6.1 Vector와 ArrayList의 비교
▣
Vector와 ArrayList의 공통점
순서가 있는 Collection이다.
List 인터페이스를 구현하고 있다.
◈ 데이터를 중복해서 포함할 수 있다.
◈
◈
LinkedList
Stack
Collection
▣
List
Vector
Vector와 ArrayList의 차이점
◈
◈
Vector는 자동으로 동기화를 보장해준다.
ArrayList는 동기화를 보장해주지 않는다.
동기화보장
ArrayList
동기화보장하지 않음
▣
ArrayList의 동기화 지원 방법
◈
▣
List list = Collections.synchronizedList(new ArrayList(...));
Vector와 ArrayList의 기능
◈
◈
ArrayList는 배열에 동적 메모리 증가 기능을 구현한 클래스이다.
Vector는 ArrayList에 동기화가 보장되도록 최적화한 클래스이다.
6.6.2 Hashtable, HashMap의 비교
▣
Hashtable, HashMap의 공통점
내부적으로 모두 Hash 기법을 이용한다.
Map 인터페이스를 구현하고 있다.
◈ 키(Key)와 값(Value)을 이용해서 데이터를 관리한다.
◈
◈
▣
Hashtable과 HashMap
◈
Hashtable은 동기화가 보장 되지만 HashMap은 동기화가 보장되지 않는다.
Map
▣
HashMap의 동기화 지원 방법
◈
▣
Map m = Collections.synchronizedMap(new HashMap(...));
Hashtable, HashMap과 HashSet과의 관계
Hashtable
동기화보장
HashMap
동기화보장하지 않음
SortedMap
Hashtable과 HashMap은 둘다 Map 인터페이스를 구현하고 있다.
◈ HashSet은 내부적으로 Hash기법을 사용하지만 Set 인터페이스를 구현하고
있다.
◈
6.6.3 ArrayList와 HashSet의 비교
▣
ArrayList와 HashSet의 공통점
객체의 저장공간이다.
◈ 동기화를 보장하지 않는다.
LinkedList
◈
Collection
List
Stack
Vector
▣
ArrayList와 HashSet의 차이점
◈
ArrayList
ArrayList
순서의개념, 중복허용
– List 인터페이스를 구현하는 클래스이다.
– 순서의 개념이 있다.
– 데이터의 중복을 허용한다.
◈
– Set 인터페이스를 구현하는 클래스이다.
– 순서의 개념이 아니라 집합의 개념이 있다.
– 데이터의 중복을 허용하지 않는다.
▣
집합의개념, 중복불허
HashSet
Set
HashSet
SortedSet
ArrayList와 HashSet의 기능
◈
순서가 중요한 의미를 가진다면 ArrayList를, 순서는 중요하지 않고 데이터
가 중복되지 않기를 원한다면 HashSet을 이용하면 된다.
6.6.4 Sorted와 Not Sorted의 비교
▣
Sorted를 지원하지 않음
◈
▣
Sorted를 지원하는 함
◈
▣
Collection
Set
HashSet
SortedSet
Map
Hashtable
HashMap
SortedMap
TreeSet
TreeMap
Key와 Value로 데이터 관리
Key를 기준으로 오름차순으로 정렬된다.
Map 인터페이스를 상속한 SortedMap 인터페이스를 구현한 클래스
TreeSet
◈
◈
▣
TreeSet과 TreeMap
TreeMap
◈
◈
◈
▣
HashSet, HashMap
Set을 인터페이스를 상속한 SortedSet 인터페이스를 구현한 클래스
데이터들이 자동으로 오름차순으로 정렬된다.
Comparator 인터페이스
◈
◈
TreeSet과 TreeMap은 사용자가 직접 정렬의 방식을 지정할 수 있다.
TreeSet과 TreeMap은 정렬을 위한 Comparator 인터페이스를 구현하면 된다.
6.7 Collection 계열 클래스
6.7.1 Stack
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
import java.util.*;
Collection
List
public class StackMain{
public static void main(String[] args) {
Stack<String> stack = new Stack<String>();
System.out.println(stack.empty());
stack.push(new String("jabook"));
stack.push(new String("java"));
stack.push(new String("소설같은 자바"));
System.out.println(stack.empty());
System.out.println(stack.peek());
System.out.println(stack.pop());
System.out.println(stack.peek());
System.out.println(stack.search("jabook"));
}
}
LinkedList
Stack
Vector
ArrayList
6.7.2 ArrayList
LinkedList
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
import java.util.*;
List
Collection
public class ArrayListMain{
public static void main(String args[]) {
ArrayList<String> list = new ArrayList<String>();
list.add("홍길동");
list.add("김삿갓");
list.add("이몽룡");
list.add("성춘향");
list.add("변사또");
System.out.println(list);
System.out.println("Index 2 : " + list.get(2));
System.out.println("Index 0 : " + list.get(0));
String[] arList = new String[list.size()];
list.toArray(arList);
System.out.println("Index 1 : " + arList[1]);
System.out.println("Index 3 : " + arList[3]);
}
}
Stack
Vector
ArrayList
6.7.3 HashSet
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
import java.util.*;
public class HashSetMain{
public static void main(String args[]) {
Set<String> set = new HashSet<String>();
set.add("김삿갓");
set.add("홍길동");
Collection
set.add("춘향이");
set.add("이도령");
set.add("향단이");
System.out.println("HashSet : " + set);
set.remove("이도령");
System.out.println(set.contains("홍길동"));
Iterator<String> iter = set.iterator();
while(iter.hasNext()){
String temp = iter.next();
System.out.print(temp + ", ");
}
System.out.println();
}
}
HashSet
Set
SortedSet
6.7.4 Vector I
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
public class VectorMain {
public static void main(String[] args) {
Vector<String> v = new Vector<String>(); //Vector 객체 생성
System.out.println("Vector 생성 직후의 size : " + v.size());
v.addElement(new String("망아지"));
v.addElement(new String("송아지"));
List
Collection
v.addElement(new String("강아지"));
v.addElement(new String("병아리"));
System.out.println("Vector에 데이터 삽입 후의 size : " + v.size());
for(int i=0; i< v.size(); i++){
String temp = v.elementAt(i);
System.out.println("Vector v의 " + i + "번째 :" + temp);
}
}
Vector v에 데이터가 삽입된
}
Vector v = new Vector();
인덱스 : 0
new String("망아지")
LinkedList
Stack
Vector
ArrayList
순서
v.addElement(new String(”망아지")); v.addElement(new String("강아지"));
v.addElement(new String("송아지")); v.addElement(new String("병아리"));
인덱스 : 1
new String(”송아지")
인덱스 : 2
new String(”강아지")
인덱스 : 3
new String(”병아리")
6.7.5 Vector II
▣
Vector<Object> v = new Vector<Object>(); //객체 생성
▣
▣
▣
v.addElement(new Character('A')); //Wrapper 클래스의 사용
v.addElement(new String("굼뱅이"));
v.addElement(new Integer(100)); //Wrapper 클래스의 사용
v.addElement(new Integer(200)); //Wrapper 클래스의 사용
System.out.println("Vector의 size():" + v.size());
▣
▣
▣
Vector v에 객체삽입
▣
v.insertElementAt(new Float(3.141592), 1);
System.out.println("insertElementAt()-size():" + v.size());
▣
▣
v.setElementAt("Hello", 3);
System.out.println("setElement()-size():" + v.size());
▣
System.out.println("v의0번째:" + (Character)v.elementAt(0));
System.out.println("v의1번째:" + (Float)v.elementAt(1));
System.out.println("v의2번째:" + (String)v.elementAt(2));
System.out.println("v의3번째:" + (String)v.elementAt(3));
System.out.println("v의4번째:" + (Integer)v.elementAt(4));
▣
▣
▣
▣
▣
▣
▣
▣
▣
1번째에 중간삽입, 1번째에 있던 데이터는 2번째가 된다.
3번째 존재하는 데이터를 수정
삽입한 데이터 타입이 다르기 때문
에 하나씩 추출한 뒤 형변환 후 사용
if(v.contains("Hello")){ //데이터가 있는지 확인
int find = v.indexOf("Hello"); //데이터 위치 확인
//위치(인덱스)를 이용한 데이터의 추출
System.out.println("v의" + find + "번째:" + (String)v.elementAt(find));
}
"Hello" 검색
6.7.6 AutoBoxing(자바 5.0)
▣
자바 1.4까지
◈
◈
◈
◈
◈
Vector v = new Vector(); //객체 생성
v.addElement(new Integer(100)); //Wrapper 클래스의 사용
v.addElement(new Integer(200)); //Wrapper 클래스의 사용
Integer t0 = (Integer)v.elementAt(0);
5.0 이상에서는 컴파일러
Integer t1 = (Integer)v.elementAt(1); 가자바
내부에서 자동으로 객체를 적
절한 상수값으로 변경시켜 준다.
▣
자바 5.0 이후
◈
◈
◈
◈
◈
Vector<Integer> v = new Vector<Integer>();
v.addElement(100); //AutoBoxing 발생
v.addElement(200); //AutoBoxing 발생
int a0 = v.elementAt(0);//AutoUnBoxing 발생
int a1 = v.elementAt(1);//AutoUnBoxing 발생
자바 5.0 이상에서는 컴파일러
가 내부에서 자동으로 Wrapper
클래스의 객체로 변경시켜 준다.
6.8 Map 계열 클래스
6.8.1 HashMap
▣
▣
▣
▣
▣
▣
▣
▣
▣
import java.util.*;
public class HashMapMain{
public static void main(String args[]) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("홍길동", new Integer(1));
map.put("김삿갓", new Integer(2));
map.put("이도령", new Integer(3));
Key
Value
map.put("춘향이", new Integer(4));
map.put("향단이", new Integer(5));
▣
System.out.println(map.get("홍길동"));
System.out.println(map.get("김삿갓"));
System.out.println(map.get("이도령"));
System.out.println(map.get("춘향이"));
System.out.println(map.get("향단이"));
▣
▣
▣
▣
▣
▣
}
}
Map
Hashtable
HashMap
SortedMap
6.8.2 Hashtable
▣
import java.util.Hashtable;
▣ public class HashtableMain {
▣
public static void main(String[] args) {
▣
Hashtable<String, Object> h = new Hashtable<String, Object>();
▣
//Hashtable에 키와 데이터의 삽입
h.put("Name", new String("홍길동"));
h.put("Age", new Integer(27));
h.put("Tel", new String("02-1111-2222"));
h.put("Handphone", new String("017-777-9999"));
h.put("Etc", new String("I'm a boy"));
▣
▣
▣
▣
▣
//키 값을 이용해서 객체 추출
String name = (String)h.get("Name");
Integer age = (Integer)h.get("Age");
▣
▣
▣
▣
▣
Value
Hashtable
HashMap
}
}
Key
Map
SortedMap
6.9 Sorted 계열 클래스
6.9.1 TreeSet과 TreeMap
▣
HashSet을 이용한 TreeSet 생성
◈
◈
◈
◈
◈
◈
◈
◈
▣
Set<String> set = new HashSet<String>();
set.add(new String("김삿갓"));
set.add(new String("홍길동"));
set.add(new String("춘향이"));
Set
set.add(new String("이도령"));
set.add(new String("향단이"));
HashSet
sorted 기능을
이용하기 위해서
TreeSet
TreeSet<String> ts = new TreeSet<String>();
ts.addAll(set);
HashMap을 이용해서 TreeMap 생성
◈
◈
◈
◈
◈
◈
◈
◈
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("홍길동", new Integer(1));
map.put("김삿갓", new Integer(2));
map.put("이도령", new Integer(3));
map.put("춘향이", new Integer(4));
Map
HashMap
map.put("향단이", new Integer(5));
Map<String, Integer>sortedMap = new TreeMap<String, Integer>();
sortedMap.putAll(map);
sorted 기능을
이용하기 위해서
TreeMap
6.9.2 TreeSet의 Comparator 구현
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class Score{
private int korea=0;
private int math=0;
public Score(int korea, int math){
this.korea = korea;
TreeSet<Score> tset = new TreeSet<Score>(new MyComparator<Score>());
this.math = math;
비교법지정
}
public int getSum(){
return this.korea + this.math;
}
데이터 삽입
public String toString(){
return "국어:" + korea + " 수학:" + math;
tset.add(new Score(21, 22));
}
tset.add(new Score(61, 62));
}
class MyComparator<T> implements Comparator<T>{
public int compare(T o1, T o2){
Score s1 = (Score)o1;
Score s2 = (Score)o2;
int r = s1.getSum() - s2.getSum();
if(r>0){
return 1; //오름차순 정렬
}else if(r==0){
return 0;
}else {
return -1; //내림차운 정렬
}
}
}
Comparator는 Sorting을 위한 기준을 제시한다.
tset.add(new Score(81, 82));
tset.add(new Score(11, 12));
tset.add(new Score(31, 32));
사용자 정의 Comparator
삽입과 동시에 자동으로 정렬된다.
6.9.3 TreeMap의 Comparator 구현
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
TreeMap<Score, String> tset = new TreeMap<Score, String>(new MyComparator<Score>());
class Score{
private int korea=0;
private int math=0;
비교법지정
public Score(int korea, int math){
this.korea = korea;
this.math = math;
데이터 삽입
}
public int getSum(){
return this.korea + this.math;
}
public String toString(){
return "국어:" + korea + " 수학:" + math;
}
tset.put(new Score(21, 22), "홍길동1");
}
tset.put(new Score(61, 62), "홍길동2");
class MyComparator<T> implements Comparator<T>
public int compare(T o1, T o2){
Score s1 = (Score)o1;
Score s2 = (Score)o2;
int r = s1.getSum() - s2.getSum();
if(r>0){
return 1; //오름차순 정렬
}else if(r==0){
return 0;
}else {
return -1; //내림차운 정렬
}
}
}
Comparator는 Sorting을 위한 기준을 제시한다.
tset.put(new Score(81, 82), "홍길동3");
tset.put(new Score(11, 12), "홍길동4");
tset.put(new Score(31, 32), "홍길동5");
Comparator
삽입과 동시에 자동으로 소팅된다.
6.10 Enumeration과 Iterator
6.10.1 컬렉션 검색
▣
컬렉션 검색을 위한 인터페이스
◈
◈
▣
Enumeration과 Iterator의 특징
◈
◈
▣
Enumeration과 Iterator
Advanaced for(foreach)
데이터의 마지막에 상관하지 않고 모든 데이터에 접근할 수 있다.
Iterator는 자바 1.2에서 제공, Enumeration의 대체용
컬렉션 검색을 위한 제어문(자바 5.0)
◈
Advanced for(일명 foreach)
– 자바 5.0에 새롭게 추가된 컬렉션을 위한 반복 제어문
– 데이터의 마지막에 상관하지 않고 검색하기 위한 제어문
◈
Advanced for의 예
– String[] ar = new String[]{"안녕1", "안녕2", "안녕3"};
– for (String tmp : ar ){
–
System.out.println( tmp );
– }
반복변수
6.10.2 Vector에서의 Enumeration
Enumeration 키를 움직이는 방법
첫위치는 어떠한
데이터도 가르키지
않는다.
실제 데이터
데이터가 있는지 확인
hasMoreElements()
hasMoreElements()
1
0
1
true
true
nextElement()
nextElement() 할 때마다
키가 움직임
▣
hasMoreElements()
3
4
5
6
true
nextElement()
nextElement()는 데이터가
있는지 확인한 후 사용
Vector 생성과 데이터 삽입
◈ Vector<String> v = new Vector<String>();
◈ v.addElement(new String("망아지"));
◈ v.addElement(new String("송아지"));
◈ v.addElement(new String("강아지"));
◈ v.addElement(new String("병아리"));
2
▣
▣
nextElement()
데이가 있는지 확인할 때
hasMoreElements() 사용
Enumeration 얻어내기
◈ Enumeration<String> en = v.elements();
Enumeration을 이용해서 전체 데이터 추출
◈ while(en.hasMoreElements()){
◈
String temp = en.nextElement();
◈
System.out.println(temp);
◈ }
6.10.3 Hashtable에서의 Enumeration
▣
hashtable에 객체의 삽입
◈
◈
◈
◈
◈
▣
Enumeration 얻기
◈
▣
Enumeration en = h.elements();
Hashtable의 값(Value)을 Enumeration 얻기
◈
◈
◈
◈
◈
◈
▣
Hashtable<String, String> h = new Hashtable<String, String>();
h.put("1", new String("홍길동"));
h.put("2", new String("안녕하세요"));
h.put("3", new String("02-1111-2222"));
h.put("4", new String("017-777-9999"));
Enumeration<String> en = h.elements();
while(en.hasMoreElements()){
//데이터 얻기(다운캐스팅 필요)
String temp = en.nextElement();
System.out.println(temp);
}
Hashtable의 키(Key)에 해당하는 Enumeration 얻기
◈
◈
◈
◈
◈
Enumeration<String> en2 = h.keys();
while(en2.hasMoreElements()){
String temp = en2.nextElement();
System.out.println(temp);
}
6.10.4 Iterator(자바 1.2)
▣
Vector의 Iterator
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
Vector<String> v = new Vector<String>();
v.addElement(new String("망아지"));
v.addElement(new String("송아지"));
v.addElement(new String("강아지"));
v.addElement(new String("병아리"));
Iterator<String> iter = v.iterator();
while(iter.hasNext()){
String temp = iter.next();
System.out.println(temp);
}
Hashtable의 Iterator
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
Hashtable<String,String> h = new Hashtable<String,String>();
h.put("1", new String("홍길동"));
h.put("2", new String("안녕하세요"));
h.put("3", new String("02-1111-2222"));
h.put("4", new String("017-777-9999"));
Iterator<String> iter2 = h.values().iterator();
while(iter2.hasNext()){
String temp = iter2.next();
System.out.println(temp);
}
6.10.5 Enumeration과 Iterator의 차이
▣
Fail-Fast 방식
◈
Iterator를 이용해서 순차적으로 접근하고 있는 도중 다른 곳에서 해
당 컬렉션에 데이터를 추가하거나 삭제하는 등의 작업이 일어난다
면 ConcurrentModificationException이 발생하게 하는 방식
– 자바 1.2부터 지원
◈
▣
Enumeration은 컬렉션의 집합을 통째로 복사해서(SnapShot) 사용하
기 때문에 Fail-Fast를 지원하지 않는다.
Enumeration과 Iterator의 차이점
◈
메서드의 이름이 다르다
– hasMoreElements()가 hasNext()로 대체
– nextElement()가 next()로 대체
◈
◈
대체적으로 Enumeration보다는 Iterator
를 사용하기를 권장한다.
Iterator는 remove() 메서드를 제공한다.
Fail-Fast 지원 여부
– Iterator는 Fail-Fast방식을 지원한다.
– Enumeration은 Fail-Fast 방식을 지원하지 않는다.
6.10.6 for each 문(자바 5.0)의 예
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
import java.util.*;
public class ForEachMain{
public static void main(String[] args){
ArrayList<String> ar = new ArrayList<String>();
ar.add("안녕1");
ar.add("안녕2");
ar.add("안녕3");
//1. 일반적인 For문
for (Iterator<String> i = ar.iterator(); i.hasNext(); ) {
String tmp = i.next();
System.out.println(tmp);
}
//2. For Each문 Java SE 5.0의 코드
for (String tmp : ar){
System.out.println(tmp);
}
}
}
7 ███████████
7
7장 Exception, 문자열, 자바 5.0
소설같은
3
자바
rd
Edition
since 2001
최영관
Powered by http://www.jabook.org
7.1 예외처리(Exception Handling)
7.1.1 개요
▣
자바의 에러
◈
◈
▣
컴파일 타임 에러
◈
▣
자바의 문법적인 에러이기 때문에 아주 쉽게 처리할 수 있다.
실행 타임 에러
◈
◈
▣
컴파일 타임 에러(Compile-Time Error)
실행 타임 에러(Run-Time Error)
디버깅의 절차를 거치지 않으면 거의 잡을 수 없는 심각한 버그(Bug)
인 경우가 많다.
실행 타임의 잘못된 에러는 프로그램 자체를 멈추게 하는 원인이 된
다.
자바의 예외처리(Exception Handling)
◈
try, catch, finally의 사용
7.1.2 에러 이벤트(Error Event)
▣
컴파일 타임 오류(Compile-Time Error)
◈
▣
실행 타임 오류(Run-Time Error)
◈
▣
컴파일은 되지만 실행이 되지 않는 로직(Logic)상의 오류
에러 이벤트(Error Event)
◈
▣
자바의 문법적 오류로 컴파일이 되지 않는 구문상의 오류
자바에서는 에러가 발생했을 때 에러 이벤트(Error Event)라는 것이
발생한다.
Exception의 정의
◈
Exception은 실행 타임에 발생하는 에러 이벤트(Error Event)를 말한
다.
7.1.3 예외 처리
▣
에러처리 방법 I
◈
▣
에러처리 방법 II
◈
▣
try와 catch를 이용해서 프로그래머가 에러를 감지해서 처리
try와 catch 블록의 사용
◈
◈
◈
◈
◈
◈
▣
프로그램적으로 에러가 발생하지 않도록 완벽하게 코딩
try{
//파일로딩
//try 블록은 에러 감지블록
}catch(FileNotFoundException e){
//catch 블록은 에러 처리블록
}
참고
위의 경우에 설명을 위해서 사용자가 파일의 유
무를 확인하는 에러처리 기법을 소개하고 있지만,
자바에서는 파일을 다룰 때 try, catch를 의무적으
로 사용해야 한다.
에러처리의 의무화
◈
자바에서는 에러가 발생할 가능성이 높은 곳에 try와 catch의 사용이 의무적
이다.
7.1.4 에러 처리의 예
▣
FileReader는 의무적으로 FileNotFoundException을 처리해주어야 하지만 예외처리를 하
지 않은 경우
◈
◈
◈
◈
◈
◈
▣
import java.io.*;
public class ErrorMain{
컴파일 타임 에러
public static void main(String[] args) {
FileReader fr = new FileReader("ErrorMain.java");
}
에러가 자주 발생하는 곳에는 컴파일러 차원에서 에러처
}
리 루틴을 요구하기 때문에 의무적으로 에러처리 루틴을
넣어야 한다.
try, catch를 이용해서 FileNotFoundException을 처리한 경우
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
import java.io.*;
public class ErrorMain2{
public static void main(String[] args){
try{
FileReader fr = new FileReader("ErrorMain.java");
//fr을 이용해서 작업
}catch(FileNotFoundException e){
System.out.println(e);
컴파일 OK
}
}
}
7.1.5 try-catch-finally
다중 catch를 이용한 에러처리 과정
try {
error
String s = null;
System.out.println(s.length());
.......
1
NullPointerException x 발생
2
3
이벤트 할당 후 catch 호출
} catch(NullPointerException e1) {
//에러 처리
4
작업처리
매개변수가 일치하는
catch 검색
x를 e1에 할당한 후 catch 호출
} catch(Exception e2) {
//그외의 모든 에러대한 처리
} finally{
// 최종적으로 무조건적으로 처리해야 하는 작업
5
finally 작업처리
}
6
▣
끝
try catch의 원리
try 구문 내에서 에러가 발생하면 에러 이벤트가 생성된다. 이 에러 이벤트를 매개변
수로 해서 catch 메서드를 호출하는 방식이다.
◈ catch 메서드는 내부에서 에러가 발생했을 때 자동으로 호출된다.
◈
7.1.6 try-catch-finally의 예
▣
에러처리 루틴이 삽입된 예(실행 시에 에러처리 루틴에 의해 에러가 처리된다.)
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
public class TryCatchMain{
public static void main(String[] args) {
try{
String str = null;
System.out.println(str.length());
}catch(NullPointerException e){
System.out.println(e.toString() + " 에러가 발생했습니다");
System.out.println("에러처리 루틴 실행");
}
실행 시에 NullPointerException 에러가 발생하는 예
System.out.println("프로그램의 종료");
}
public class NullErrorMain {
}
public static void main(String[] args) {
String str = null;
에러처리 루틴(try, catch, finally)이 삽입된 예
System.out.println(str.length()); //에러발생
◈ public class TryCatchFinallyMain{
◈
public static void main(String[] args) {
System.out.println("프로그램의 종료");
◈
try{
}
◈
String str = null;
}
◈
System.out.println(str.length());
◈
}catch(NullPointerException e){
◈
System.out.println(e.toString() + " 에러가 발생했습니다");
◈
System.out.println("에러처리 루틴 실행");
◈
}finally{
◈
//에러가 나든 나지 않든 무조건 실행되는 블록
◈
System.out.println("finally 구문 실행");
◈
}
◈
System.out.println("프로그램의 종료");
◈
}
◈ }
7.2 예외 처리 실례
7.2.1 사용자 필요에 의한 에러처리
▣
사용자가 필요하다고 생각해서 에러처리 구문을 사용하는 경우
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
public class BasicException {
public static void main(String args[]) {
try{
고위로 에러유발 : 배열의
범위를 벗어나도록 한다.
int[] ar = new int[]{0, 100, 200, 300};
for(int i=0; i<ar.length+1; i++){
System.out.println("ar["+i+"]=" + ar[i]);
}
}catch(ArrayIndexOutOfBoundsException e) {
System.out.println("e.getMessage(): " + e.getMessage());
System.out.println("e.toString(): " + e.toString());
e.printStackTrace();
return;
}finally{
System.out.println("finally: 결국이리로 오는군요");
}
}
}
7.2.2 throw
▣
수동으로 사용자가 직접 에러 이벤트 발생시키기
◈
▣
throw new Exception();
사용자가 에러 이벤트를 직접 발생시키는 경우를 테스트하는 예
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
public class UseThrowMain {
public static void main(String args[]) {
try {
throw new Exception("이것이 에러 메시지");
1
} catch(Exception e) {
System.out.println("--Exception 발생구문--");
System.out.println("정보:e.getMessage(): " + e.getMessage());
System.out.println("정보:e.toString(): " + e.toString());
e.printStackTrace();
return;
2
} finally{
System.out.println("finally: 결국이리로 오는군요");
}
}
}
7.2.3 다단계 catch
▣
catch 블록으로 처리하지 못한 경우
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
import java.io.*;
public class LevelErrorMain{
public static void main(String[] args){
try{
FileReader f = new FileReader("LevelErrorMain.java");
String s = null;
System.out.println(s.toString()); //NullPointerException 발생
}catch(FileNotFoundException e1){
System.out.println("FileNotFoundException:" + e1);
}catch(ArrayIndexOutOfBoundsException e2){
System.out.println("ArrayIndexOutOfBoundsException:" + e2);
}
}
}
NullPointerException 발생
catch에 NullPointerException이 없다.
Exception으로 모든 에러 처리
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
import java.io.*;
public class LevelCatchMain{
public static void main(String[] args){
try{
FileReader f = new FileReader("LevelCatchMain.java");
String s = null;
System.out.println(s.toString()); //NullPointerException 발생
}catch(FileNotFoundException e1){
System.out.println("FileNotFoundException:" + e1);
}catch(ArrayIndexOutOfBoundsException e2){
System.out.println("ArrayIndexOutOfBoundsException:" + e2);
}catch(Exception e3){
System.out.println("Exception:" + e3);
}
}
}
NullPointerException 발생
예외의 최상위 클래스인 Excpeiton을 이용해서
NullPointerException 처리
7.2.4 의무적인 에러처리
▣
파일 입출력을 위해 에러처리 루틴을 삽입한 예
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
public class NeedCatchMain {
FileNotFoundException
public static void main(String args[]) {
System.out.println("프로그램 시작");
try{
FileReader f = new FileReader("NeedCatchMain.java");
//...파일 입출력 작업
f.close();//파일 닫기
}catch(FileNotFoundException e1){
IOException
e1.printStackTrace();
}catch(IOException e2){
e2.printStackTrace();
}
IOException을 상속해서 FileNotFoundException이
System.out.println("프로그램 종료");
되기 때문에 IOException으로 모두 처리할 수 있다.
}
}
하나의 try문 내에서 여러 개의 catch를 사용할 때
catch의 매개변수명을 다르게 지정해야 한다.
▣
URL 객체 생성에 필요한 에러 루틴을 추가한 예
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
public class NeedNetCatchMain {
public static void main(String args[]) {
System.out.println("프로그램 시작");
try{
URL url = new URL("http://www.yahoo.co.kr");
System.out.println("URL:" + url.toString());
} catch(MalformedURLException e) {
e.printStackTrace();
} finally{
System.out.println("finally: 결국이리로 오는군요");
}
System.out.println("프로그램 종료");
}
}
MalformedURLException
7.2.5 에러처리 미루기
▣
에러 처리 미루기의 예(에러 발생)-makeURL()을 사용할 때 에러처리 필요
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
import java.net.*;
public class ShiftError {
MalformedURLException 처리를 미룬다.
//throws를 이용해서 에러 처리를 미무는 메서드
2
public URL makeURL(String urlstr) throws MalformedURLException{
return new URL(urlstr);
}
1
의무적으로 MalformedURLException을 처리해야 함
public static void main(String args[]) {
ShiftError s = new ShiftError();
//에러처리 루틴 필요
URL url = s.makeURL("http://www.yahoo.co.kr");
}
3
}
에러 처리 미루기의 예(에러처리 미루기를 직접 구현하는 예)
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
makeURL()을 호출할 때 미룬 예외처리를 해주어야 한다.
import java.net.*;
public class ShiftCatch {
public URL makeURL(String urlstr) throws MalformedURLException{
return new URL(urlstr);
}
public static void main(String args[]) {
ShiftCatch p = new ShiftCatch();
4 에러 처리 루틴을 추가한다.
try{
//정확한 URL을 입력하지 않았기 때문에 에러발생
URL url = p.makeURL("htttttp://www.yahoo.co.kr");
} catch(MalformedURLException e) {
e.printStackTrace();
} finally{
System.out.println("finally: 결국이리로 오는군요");
}
}
}
7.2.6 try, catch의 문제점
▣
폭발물 처리 컴퓨터
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
public class BombComputer {
boolean power = false;
public void powerOn() {
power = true;
System.out.println("폭발물 처리 컴퓨터 전원 ON!");
}
public void powerOff() {
power = false;
System.out.println("폭발물 처리 컴퓨터 전원 OFF!");
}
public void process() throws Exception{
System.out.println("작업처리 1");
System.out.println("작업처리 2");
//무조건 에러가 발생하도록 디자인
throw new Exception("작업처리 3 오류발생 곧 폭발합니다.!@#$");
}
}
폭발
BombComputer를 동작시키는 프로그램(try, catch만 사용한 경우)
◈
◈
◈
◈
◈
public class BombComputerErrorMain {
public static void main(String args[]) {
BombComputer bc = new BombComputer();
try{
bc.powerOn();
bc.process();
◈
◈
◈
◈
◈
◈
◈
bc.powerOff();
}catch(Exception e) {
e.printStackTrace();
}
}
}
process()를 호출할 때 무조건 에러 발생
문제는 powerOff()를 호출하지 못하고 catch문
으로 제어권이 넘어간다는데 있다.
7.2.7 try, catch의 해결책
▣
finally를 추가한 예
◈
◈
◈
◈
◈
◈
◈
◈
public class BombComputerMain {
public static void main(String args[]) {
BombComputer bc = new BombComputer();
try{
bc.powerOn();
bc.process();
1
}catch(Exception e) {
2
e.printStackTrace();
처리
}finally {
◈
◈
bc.powerOff();
}
◈
◈
◈
3
}
}
전원차단
폭발
7.3 문자열
"Hello World!"
7.3.1 문자열
▣
문자열
◈
◈
▣
문자열을 생성하는 방법 I
◈
◈
▣
◈
▣
str2
String ntr1 = new String("Hello World!");
String ntr2 = new String("Hello World!");
ntr1
ntr2
문자열 결합하기
◈
▣
String str1 = "Hello World!";
String str2 = "Hello World!";
str1
문자열 생성하는 방법 II
◈
상수 풀 영역
문자열은 문자상수의 집합이다.
자바에서 문자열은 String 클래스의 객체이다.
String ntr3 = "Hello" + " " + "World!";
new String("Hello World!");
"Hello World!"
문자열 내에 에스케이프(Escape) 문자의 사용
◈
String myString = "c:\\javasrc\\chap07";
new String("Hello World!");
"Hello World!"
Heap 영역의 메모리 공간
7.3.2 문자열의 주요 메서드
▣
문자열의 길이
◈
▣
문자열 결합
◈
▣
concat() 메서드나 + 연산자를 이용해서 문자열을 결합시킨다.
문자열 비교
◈
▣
문자열의 length() 메서드를 이용한다.
equals() 또는 compareTo() 메서드 문자열 비교를 할 수 있다.
문자열 검색
indexOf()는 문자열을 첫 부분부터 검색하여 입력한 문자열을 제일 먼저 만나는 위치
를 리턴
◈ lastIndexOf()는 문자열을 마지막 부분부터 검색하여 입력한 문자열을 제일 먼저 만
나는 위치를 리턴
◈
▣
문자열의 잘라내기
◈
▣
substring()는 시작 위치와 끝위치를 매개변수로 주면 해단 구간을 추출해서 문자열
로 리턴
문자열 교체(자바 5.0)
◈
replace()는 문자열 내에 특정 문자열을 모두 교체한다.
7.3.3 문자열 메서드 예제(자바 5.0)
▣
import static java.lang.System.out; 자바 5.0 static import 기능 사용
▣
▣
▣
▣
public class StringMethodMain{
public static void main(String[] args){
String str1 = new String("www.jabook.org");
String str2 = new String("www.jabook.org");
System.out.println("str1의 길이:" + str1.length());
System.out.println("str2의 길이:" + str1.length());
System.out.println("str1.equals(str2):" + str1.equals(str2));
System.out.println("str1.compareTo(str2):" + str1.compareTo(str2));
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
out.println(
out.println(
out.println(
out.println(
out.println(
out.println(
}
}
"str1.concat(str2):" + str1.concat(str2));
"str1+str2:" + str1+str2);
"str1.indexOf(\"jabook\"):" + str1.indexOf("jabook"));
"str1.indexOf(\"jabook\"):" + str1.lastIndexOf("o"));
"str1.substring(4,10):" + str1.substring(4,10));
"str1.replace(\"o\", \"t\"):" + str1.replace("o", "t"));
7.3.4 StringBuffer 클래스
▣
String 클래스는 수정이 불가능한(Immutable) 클래스이다.
◈
String str1 = "www.";
◈ String str2 = "jabook.";
◈ String str3 = "org";
◈ String str4 = str1 + str2 + str3; //새로운 문자열을 만들어서 리턴한다.
▣
StringBuffer 클래스
문자열의 수정이 가능한 클래스
◈ StringBuffer 클래스를 이용해 생성한 객체는 문자열의 추가, 삭제, 수정, 검색 등의 기
능을 가지고 있다.
◈
▣
StringBuffer 클래스는 수정이 가능한 클래스이다.
◈
◈
◈
◈
◈
◈
◈
StringBuffer sb = new StringBuffer();
sb.append("www.");
sb.append("jabook.");
sb.append("org");
sb.replace(11, 14, "net");
String str = sb.toString();
System.out.println(str);
7.3.5 StringBuffer의 속도
▣
속도 측정 변수
◈
◈
▣
long startTime = 0L;
long elapsedTime = 0L;
String으로 문자열 만들기의 속도 측정
◈
◈
◈
String str1 = "";
startTime = System.currentTimeMillis();
for(int i=0; i<50000; i++){
◈
str1 += "H"; //새로운 문자열 생성하기
◈ }
◈ elapsedTime = System.currentTimeMillis() - startTime;
▣
String의 속도측정
StringBuffer로 문자열 만들기의 속도 측정
◈
◈
◈
StringBuffer sb = new StringBuffer();
startTime = System.currentTimeMillis();
for(int i=0; i<50000; i++){
◈
sb.append("H"); //새로운 문자열 추가하기
◈ }
◈ elapsedTime = System.currentTimeMillis() - startTime;
StringBuffer의 속도측정
7.3.6 StringBuilder 클래스(자바 5.0)
▣
속도 측정 변수
◈
◈
▣
long startTime = 0L;
long elapsedTime = 0L;
동기화가 지원되는 StringBuffer의 속도측정
◈
◈
◈
◈
String str1 = "";
StringBuffer sb = new StringBuffer();
startTime = System.currentTimeMillis();
for(int i=0; i<500000; i++){
◈
sb.append("H"); //새로운 문자열 추가하기
◈ }
◈ elapsedTime = System.currentTimeMillis() - startTime;
▣
StringBuffer의 속도 측정
동기화가 지원되지 않는 StringBuilder의 속도 측정
◈
◈
◈
StringBuilder sbr = new StringBuilder();
startTime = System.currentTimeMillis();
for(int i=0; i<500000; i++){
◈
sbr.append("H"); //새로운 문자열 추가하기
◈ }
◈ elapsedTime = System.currentTimeMillis() - startTime;
StringBuilder의 속도 측정
7.3.7 Formatting 클래스(자바 5.0)
▣
포멧팅(Formatting) 기능
◈
▣
포멧팅의 예
◈
▣
자바 5.0 이전에는 Formatter 클래스를 이용해서 포멧된 문자열을 생성했지만,
자바 5.0부터는 String.format() 메서드를 이용해서 포멧된 문자열을 만들 수 있
다.
String s1 = String.format("%s %d %f %o %h", "Hello", 100, 3.14F, 100, 100);
숫자 포멧팅의 예
◈
String s2 = String.format("%,d", 10000000);
– 콤마(,)가 포함된 숫자
◈
String s3 = String.format("%.3f", 42.000000);
– 소수점 3째 자리까지 표현
◈
String s4 = String.format("%,.2f ", 12345.678901);
– 콤마가 포함되며, 소수점 2째 자리까지 표현
7.4 자바 5.0(enum, Varargs)
7.4.1 자바의 상수
▣
자바에서 상수를 만드는 방법
◈
class Top{
public static final double PI = 3.14D;
◈
◈
}
`
자바 5.0 이전에는
▣
클래스에서의 상수 선언
◈
◈
◈
▣
double d = 5.0D * Top.PI; `
인터페이스에서의 상수 선언
◈
◈
◈
◈
▣
final 상수를 이용했다.
상수의 사용
◈
▣
final 키워드를 상수를 선언한다.
선언할 때 단 한번 초기화를 한다.
static final로 선언하면 전역적인 상수가 된다.
interface ITop{
double PI = 3.14D;
//public static final PI = 3.14D; //동일한 표현
}
인터페이스에서의 상수 선언
◈
◈
인터페이스에서는 멤버변수는 상수만 가능하다.
멤버변수를 선언하면 자동으로 public static final이 된다.
자바 5.0부터
열거형 상수(enum)를 지원한다.
7.4.2 enum(자바 5.0)
▣
열거형
◈
▣
독특한 형태의 데이터 타입 생성기
일반적인 데이터 타입의 경우
◈
▣
데이터 타입 생성기
데이터 타입을 이용해서 변수를 만든 후 형에 맞는 값을 넣을 수 있다.
열거형(enum)의 경우
열거형으로 만든 데이터 타입을 이용해서 변수를 만드는 것은 일반 데이터
타입과 동일하다.
◈ 열거형으로 변수를 만들었다면 이 변수에는 열거형을 선언할 때 명시한 값
을 넣을 수 있다.
◈
▣
열거형으로 데이터 타입 만들기
데이터 타입
◈
SignFlag형 변수를 선언한 후 할당할 수 있는 상수
SignFlag s1;
s1 = SignFlag.black;
enum SignFlag {black, yellow, green, blue, red};
– 열거형을 선언할 때 사용할 값들을 함께 지정할 수 있다.
– black, yellow, green, blue, red는 열거형 변수에 할당될 수 있는 상수들이다.
– black, yellow, green, blue, red에는 0,1,2,3,4가 순서대로 자동 할당된다.
7.4.3 enum(자바 5.0)의 사용
▣
▣
//Type Safe Enumerations
enum PaintStyle{ DASH, DB_DASH, DOT, DB_DOT};
▣
▣
▣
public class EnumSampleMain{
public static void main(String[] args){
PaintStyle ps = PaintStyle.DB_DASH;
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
switch( ps ){
case DASH: System.out.println("---------------");
break;
case DB_DASH:
System.out.println("================");
break;
case DOT:
System.out.println("................");
break;
case DB_DOT:
System.out.println("::::::::::::::::::::");
break;
default:
break;
}
}
}
7.4.4 enum(자바 5.0)의 실체
▣
enum PaintStyle{ DASH, DB_DASH, DOT, DB_DOT};
컴파일러가 다음과 같이 변환한다.
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
final class PaintStyle extends Enum{
public static PaintStyle[] values() {
return (PaintStyle[])$VALUES.clone();
}
public static PaintStyle valueOf(String s) {
return (PaintStyle)Enum.valueOf(PaintStyle, s);
}
private PaintStyle(String s, int i) {
super(s, i);
}
public static final PaintStyle DASH;
public static final PaintStyle DB_DASH;
public static final PaintStyle DOT;
public static final PaintStyle DB_DOT;
private static final PaintStyle $VALUES[];
static {
DASH = new PaintStyle("DASH", 0);
DB_DASH = new PaintStyle("DB_DASH", 1);
DOT = new PaintStyle("DOT", 2);
DB_DOT = new PaintStyle("DB_DOT", 3);
$VALUES = (new PaintStyle[] {
DASH, DB_DASH, DOT, DB_DOT
});
}
}
enum도 결국에는 클래스가 된다.
.class파일을 .java 파일로 변환하는 방법
c:\javasrc\chap07>jad.exe -s .java PaintStyle.class
[참고]역컴파일러 jad.exe는 http://www.kpdus.com/jad.html
에서 다운로드 받을 수 있다.
7.4.5 Varargs(자바 5.0)
▣
▣
▣
public class VarArgsMain {
public static void display(Vector v) {
for (Object s : v) {
자바 5.0 이전의
▣
System.out.println("컬렉션형태:" + s);
매개변수
▣
}
▣
}
가변인수가 없는 자바 5.0 이전에는 메서드의 인수를 컬렉션이나 배열로 처리하였다.
▣
▣
public static void display(String... strs) {
for (String s : strs) {
▣
System.out.println("가변인수형태:" + s);
▣
}
▣
}
자바 5.0부터는 가변인수(VarArgs)를 갖는 메서드를 작성할때는 "…"를 사용한다.
▣
public static void main(String[] args) {
▣
Vector vec = new Vector ();
컴파일러는 가변인수를 다음과 같이 해석한다.
▣
vec.add("Hello");
public static void display(String as[])
▣
vec.add("World");
자바
▣ 5.0부터
vec.add("Korea"); VarArgsMain.display(new String[] {"Hello", "World", "Korea" });
▣
VarArgsMain.display(vec);
▣
▣
▣
VarArgsMain.display("Hello" , "World", "Korea");
}
}
인수를 가변적으로 조정할 수 있다.
8 ███████████
8
8장 자바 Thread
소설같은
3
자바
rd
Edition
since 2001
최영관
Powered by http://www.jabook.org
8.1 스레드 기본
8.1.1 프로세스와 스레드
▣
프로세스(Process)
◈
▣
하나의 프로그램(Program)은 하나의 프로세스(Process)에 해당한다.
멀티 태스킹(Multi-Tasking)
프로세스(Process)의 경우 운영체제에서 자동으로 관리해준다.
◈ 운영체제 차원의 프로세스(Process) 관리를 멀티 태스킹(Multitasking)이라
고 한다.
◈
▣
프로세스(Process)와 스레드(Thread)
◈
▣
하나의 프로세스(Process) 내에는 여러 개의 스레드(Thread)가 존재할 수 있
다.
스레드(Thread)란?
◈
◈
하나의 프로그램 내에서 실행되는 메서드
같은 순간에 두 개의 메서드가 동시에 실행되면 두 개의 스레드가 동작하는
것이다.
8.1.2 스레드(Thread)란
▣
시퀀셜(Sequential)하게 동작한다.
◈
▣
스레드의 기본
◈
▣
한 순간에 두 개의 메서드가 동시에 실행되었을 때, 실행된 메서드
들을 스레드(Thread)라고 한다.
스레드란?
◈
▣
한 순간에 하나의 메서드가 동작하는 것을 '시퀀셜하게 동작한다'라
고 한다.
프로그램에서 독립적으로 실행되는 메서드
스레드에서의 중요 사항
◈
◈
스레드가 어떻게 만드는가?
왜 스레드를 메서드라고 하는가?
8.1.3 Runnable로 스레드 만들기
▣
Top 클래스 내에 run() 메서드 작성
◈
◈
◈
◈
◈
▣
◈
◈
Runnable 인터페이스의 구현
◈
◈
public class Top{
public void run(){
//스레드의 세부 내용
}
}
스레드의 메서드를 구현하기 위한 인터페이스
◈
▣
◈
◈
◈
▣
진짜 스레드에 Runnable 장착
◈
public interface Runnable{
void run();
}
◈
▣
public class Top implements Runnable{
public void run(){
//...작업 내용
}
}
Top t = new Top();
Thread thd = new Thread(t);
스레드 동작시키기
◈
◈
◈
Topt t = new Top(); //가짜 스레드
Thread thd = new Thread(t); //진짜 스레드
thd.start(); //스레드 동작시키기
스레드의 실행 순서
main()
System.out.println("프로그램 시작");
Top t = new Top();
Thread thd = new Thread(t);
thd.start();
System.out.println("프로그램 종료");
Runnable run()
스레드 실행 start()
start()는 run() 호출
동시에 작업 진행
public void run(){
for(int i=0; i<50; i++)
System.out.print(i+"\t");
}
8.1.4 Runnable로 두 개의 스레드 만들기
두 개의 스레드 실행 순서
main()
Thread thd1
Runnable run()
public void run(){
for(int i=0; i<50; i++)
System.out.print(i+"\t");
}
System.out.println("프로그램 시작");
Top t = new Top();
Thread thd1 = new Thread(t);
Thread thd2 = new Thread(t);
thd1.start();
thd2.start();
System.out.println("프로그램 종료");
동시에 작업 진행
▣
2개의 스레드를 동시에 실행
◈
1. Runnable을 구현하는 객체 만들기
–
◈
2. Runnable을 장착한 후 진짜 스레드 만들기
–
–
◈
Top t = new Top();
Thread thd1 = new Thread(t);
Thread thd2 = new Thread(t);
3. 스레드 동작 시키기
–
–
thd1.start();
thd2.start();
Thread thd2
Runnable run()
public void run(){
for(int i=0; i<50; i++)
System.out.print(i+"\t");
}
8.1.5 Thread 상속으로 스레드 만들기
▣
Thread를 상속받는 방법(상속을 통해서 스레드를 만드는 방법)
◈
◈
◈
◈
◈
▣
스레드 생성 및 동작시키기
◈
◈
▣
public class DerivedThread extends Thread{
public void run(){
//...스레드 내의 작업
}
}
DerivedThread d = new DerivedThread();
d.start();
Thread를 상속하는 두 개의 스레드를 생성한 후 실행시키기(Thread 상속)
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
class DerivedThread extends Thread{
public void run(){
for(int i=0; i<50; i++)
System.out.print(i+"\t");
}
}
public class DerivedThreadMain2{
public static void main(String[] args){
System.out.println("프로그램 시작");
DerivedThread d1 = new DerivedThread();
DerivedThread d2 = new DerivedThread();
d1.start();
d2.start();
System.out.println("프로그램 종료");
}
}
8.1.6 Runnable을 사용하는 이유
▣
Frame을 상속했기 때문에 Thread를 상속할 수 없는 경우
◈
◈
◈
▣
Frame은 상속을 하고 Runnable은 구현을 한 상태
◈
◈
◈
◈
◈
▣
class RunFrame extends Frame implements Runnable{
public void run(){
//...스레드 내의 작업
}
}
Runnable을 사용하는 이유
◈
▣
class RunFrame extends Frame{
//...작업
}
Thread를 상속하기 이전에 다른 클래스를 상속한 경우 Runnable을 이용해서 스레드를 만든다.
창 띄우기와 스레드 동작 시키기
◈
◈
◈
◈
◈
RunFrame r = new RunFrame();
r.setSize(300, 100);
r.setVisible(true);
Thread t = new Thread(r);
t.start();
8.1.7 Frame 내부에서 스레드 동작 시키기
▣
▣
▣
▣
▣
import java.awt.*;
class RunnableFrame extends Frame implements Runnable {
public RunnableFrame() {
new Thread(this).start();
}
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
생성과 동시에 스레드 시작
public void run() {
int i = 0;
System.out.println("스레드 시작!");
while(i<20) {
System.out.print(i + "\t");
this.setTitle("스레드 동작중" + i++);
try{
Thread.sleep(300);
}catch(InterruptedException e){System.out.println(e);}
}
System.out.println("스레드 종료!");
}
▣
▣
▣
▣
▣
▣
Frame을 상속하면서 Runnable을 구현
}
public class RunnableFrameMain{
public static void main(String args[]){
RunnableFrame r = new RunnableFrame();
r.setSize(300, 100);
r.setVisible(true);
}
}
프레임 생성 및 띄우기
Runnable의 run() 메서드 구현
8.1.8 Frame과 Thread 분리시켜서 구현
▣
▣
▣
▣
▣
▣
class SoloFrame extends Frame{
public SoloFrame(){
SoloThread t = new SoloThread(this);
t.start();
}
}
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class SoloThread extends Thread{
private Frame f = null;
public SoloThread(Frame f){
this.f = f;//SoloFrame의 참조값 챙겨두기
}
public void run() {
int i = 0;
System.out.println("스레드 시작!");
while(i<20) {
System.out.print(i + "\t");
f.setTitle("스레드 동작중" + i++);
try{
this.sleep(300);
}catch(InterruptedException e){System.out.println(e);}
}
System.out.println("스레드 종료!");
}
}
8.2 스레드의 상태와 제어
8.2.1 스레드의 상태
스레드(Thread)의 상태 구조도
sleep(시간)
Run
스레드의 우선권에 따라서
Run을 차지할 권한이 달라진다.
Thread
일정시간 동안만 NotRunanble 상태가 된다.
시간이 지나면 자동으로 Runnable 상태가 된다.
NotRunnable
sleep(시간), wait()
Runnable
start()를 호출했을 때
Thread
Start
Thread
Thread
sleep(시간) -> 시간지나면
wait() -> notify()
Thread
Thread
Thread
Thread
Thread
Thread
Thread
자동진입
Thread
Thread
Thread
Thread
wait()
run()의 종료는 스레드의 종료
wait()하면 NotRunnable 상태가 되지만 notify()
해주어야만 Runnable 상태로 되돌아 올 수 있다.
Dead
▣
NotRunnable 상태를 만드는 방법
sleep()은 일정 시간 동안만 NotRunnable 상태로 만든다.
◈ wait()와 notify()는 수동으로 NotRunnable 상태로 들어가고 나오는 것을 제
어할 수 있다.
◈
8.2.2 스레드의 우선권
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class PriorityThread extends Thread {
public void run() {
int i = 0;
System.out.print(this.getName()); //스레드의 이름 출력
System.out.println("[우선권:" + this.getPriority() + "] 시작\t");
while(i < 10000) {
i = i + 1;
try{
this.sleep(1);
}catch(Exception e){System.out.println(e);}
}
System.out.print(this.getName()); //스레드의 이름 출력
System.out.println("[우선권:" + this.getPriority() + "] 종료\t");
}
}
Thread 클래스의 스태틱 우선권 상수
public static final int MIN_PRIORITY = 1;
public static final int NORM_PRIORITY = 5;
public static final int MAX_PRIORITY = 10;
public class PriorityThreadMain {
public static void main(String[] args) {
System.out.println("Main메서드 시작");
for(int i=1; i<=10; i++){
//for(int i=Thread.MIN_PRIORITY; i<=Thread.MAX_PRIORITY; i++){
PriorityThread s = new PriorityThread();
s.setPriority(i);
스레드의 우선권 설정
s.start();
}
System.out.println("Main메서드종료");
}
}
8.2.3 NotRunnable 상태 만들기 I
▣
스레드를 NotRunnable 상태로 만드는 방법
◈
◈
▣
▣
sleep()을 이용해서 일정시간 동안만 대기시키는 방법(자동)
wait()와 notify()를 이용해서 대기와 복귀를 제어하는 방법(수동)
sleep()의 사용
◈
try{
◈
◈
Thread.sleep(1000); //시간의 단위는 1/1000초
}catch(InterruptedException e){e.printStackTrace();}
sleep()을 이용한 작업의 일시 중단 - main()에서의 Thread.sleep()
◈
◈
◈
◈
public class NotRunnableMain{
public static void main(String[] args){
long current = System.currentTimeMillis();
System.out.println("프로그램 시작");
◈
try{
◈
Thread.sleep(5000);
NotRunnable 상태 만들기
}catch(InterruptedException e){e.printStackTrace();}
◈
System.out.println("프로그램 종료");
System.out.println("시간: " + (System.currentTimeMillis()-current));
◈
◈
◈
◈
}
}
8.2.4 NotRunnable 상태 만들기 II
▣
▣
▣
▣
▣
import java.util.*;
class NotRunnableThread extends Thread {
public void run() {
int i = 0;
while(i < 10) {
▣
System.out.println(i + "회:" + System.currentTimeMillis() + "\t");
▣
i = i + 1;
▣
try{
this.sleep(1000);
▣
▣
▣
▣
▣
}
▣
▣
▣
▣
▣
▣
public class NotRunnableThreadMain {
public static void main(String args[] ) {
NotRunnableThread s = new NotRunnableThread();
s.start();
}
}
}catch(Exception e){System.out.println(e);}
}
}
NotRunnable 상태 만들기
8.2.5 스레드 죽이기 I
▣
while문의 조건을 이용한 스레드의 종료를 테스트하는 예
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
class TerminateThread extends Thread {
//스레드의 종료를 제어하는 플래그
▣ 스레드 테스트하기
private boolean flag = false;
◈ TerminateThread a = new TerminateThread();
public void run() {
◈ TerminateThread b = new TerminateThread();
◈ TerminateThread c = new TerminateThread();
int count = 0;
b.start();
c.start();
System.out.println(this.getName() +"시작"); ◈ a.start();
◈ int i;
while(!flag) {
◈ System.out.print("종료할 스레드를 입력! A, B, C, M?\n");
try {
◈ while(true){
◈
i = System.in.read();
//작업
◈
if(i == 'A'){
this.sleep(100);
◈
a.setFlag(true);
} catch(InterruptedException e) { }
◈
}else if(i == 'B'){
◈
b.setFlag(true);
}
◈
}else
if(i
== 'C'){
System.out.println(this.getName() +"종료");
◈
c.setFlag(true);
}
◈
}else if(i == 'M'){
public void setFlag(boolean flag){
◈
a.setFlag(true);
◈
b.setFlag(true);
this.flag = flag;
◈
c.setFlag(true);
}
◈
System.out.println("main종료");
}
◈
break;
NotRunnable
상태 만들기
◈
}
◈
}
8.2.6 스레드 죽이기 II
▣
두개의 조건을 이용한 스레드의 종료
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
두개의 조건을 이용한 스레드의 종료 테스트
class ControlThread extends Thread {
◈
//모든 스레드의 종료를 제어하는 플래그
◈
public static boolean all_exit = false;
◈
//스레드의 종료를 제어하는 플래그
◈
private boolean flag = false;
◈
public void run() {
◈
int count = 0;
◈
System.out.println(this.getName() +"시작");
◈
while(!flag && !all_exit) {
◈
try {
◈
//작업
◈
this.sleep(100);
◈
} catch(InterruptedException e) { } ◈
}
◈
System.out.println(this.getName() +"종료");
◈
}
◈
public void setFlag(boolean flag){
◈
this.flag = flag;
◈
}
◈
}
◈
◈
◈
◈
◈
ControlThread a = new ControlThread();
ControlThread b = new ControlThread();
ControlThread c = new ControlThread();
a.start();
b.start();
c.start();
Thread.sleep(100);
int i;
System.out.print("종료할 스레드를 입력! A, B, C, M?\n");
while(true){
i = System.in.read();
if(i == 'A'){
a.setFlag(true);
}else if(i == 'B'){
b.setFlag(true);
}else if(i == 'C'){
c.setFlag(true);
}else if(i == 'M'){
//모든 스레드를 종료시킨다.
ControlThread.all_exit = true;
System.out.println("main종료");
break;
}
}
8.2.7 스레드의 Resume, Suspend
▣
public final void suspend() Deprecated
◈
▣
public final void resume() Deprecated
◈
▣
스레드의 작업을 대기시킨다.
스레드의 대기 상태를 해제하고 작업을 재개한다.
스레드의 제어를 위한 도구
◈
setPriority()
– 스레드가 Run 상태에 들어갈 수 있는 우선권을 결정하게 된다.
◈
sleep()
– 일정 시간 동안 작업을 멈추게 하는 기능이 있다.
◈
wait()
– 스레드를 대기상태(NotRunnable)로 보내게 된다.
◈
notify()
– 대기상태에 있는 스레드를 Runnable 상태로 복귀시켜서 작업을 재개하게 한다.
8.3 동기화
8.3.1 멀티 스레드와 문제점
▣
스레드의 문제점
◈
▣
스레드들이 여러 개 동시에 작업을 진행하기 때문에 공유자원의 문제가 발생한다.
동기화 문제가 발생하는 상황
A, B, C라는 세 사람이 화장실을 사용하려고 한다.
◈ 화장실이 하나밖에 없다.
◈ 세사람이 동시에 화장실을 사용하려 한다.
◈
▣
화장실의 동기화 기법(Lock)
◈
▣
동기화(Synchronization)의 정의
◈
▣
A가 사용하고 있을 때 문을 잠그고(Lock), 그리고 다 사용하고 나면 다른 사람이 사
용하면서 문을 잠그는 기법을 이용하면 된다.
줄서기(번갈아 가면서 순서대로 공유자원 사용하기)
동기화(Synchronization)의 기법
synchronized 블록(자원을 사용할 때 자원에 락(Lock)을 거는 방식)
◈ wait()와 notify()
◈
8.3.2 동기화가 보장되지 않는 예제
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class Tom extends Thread{
public void run(){
for (int i=0;i<100000; i++){
NotSyncDataMain.data.i++;
}
System.out.println("Tom :" + NotSyncDataMain.data.i);
}
}
class Jane extends Thread{
public void run(){
for (int i=0;i<100000; i++){
NotSyncDataMain.data.i++;
}
System.out.println("Jane:" + NotSyncDataMain.data.i);
}
}
Tom이 100000번 증가시킨다.
동기화 보장
Jane이 100000번 증가시킨다.
동기화 보장
공유자원으로 사용할 클래스
class NotSyncDataMain{
public static Data data = new Data();
class Data{
public static void main(String[] args){
public int i = 0;
System.out.println("main 시작");
}
Tom t = new Tom();
Tom과 Jane이 동시에 수를 증가시킨다.
Jane j = new Jane();
t.start();
j.start();
System.out.println("main 종료");
}
}
예상 결과는 200000, 하지만 동기화가 보장되지 않기 때문에 어느 순간에는 동
시에 수를 증가시킨다.
8.3.3 동기화 보장 예제
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class Tom extends Thread{
public void run(){
Tom이 100000번 증가시킨다.
for (int i=0;i<100000; i++){
synchronized(SyncDataMain.data){
SyncDataMain.data.i++;
}
}
동기화 보장
System.out.println("Tom :" + SyncDataMain.data.i);
}
}
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class Jane extends Thread{
public void run(){
Tom이 100000번 증가시킨다.
for (int i=0;i<100000; i++){
synchronized(SyncDataMain.data){
SyncDataMain.data.i++;
}
동기화 보장
}
System.out.println("Jane:" + SyncDataMain.data.i);
}
}
공유자원으로 사용할 클래스
class SyncDataMain{
class Data{
public static Data data = new Data();
public int i = 0;
public static void main(String[] args){
}
System.out.println("동기화 보장 예제 시작");
Tom t = new Tom();
Jane j = new Jane();
Tom과 Jane이 동시에 수를 센다.
t.start();
j.start();
Tom과 Jane은 동기화가 보장된
System.out.println("동기화 보장 예제 종료");
상태에서 수를 증가시킨다.
}
}
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
8.3.4 공유자원의 접근 실전 예제
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class Bank{
private int money = 10000; //예금 잔액
public int getMoney(){
return this.money;
}
public void setMoney(int money){
this.money = money;
}
public void saveMoney(int save){
int m = this.getMoney();
try{
Thread.sleep(3000);
}catch(InterruptedException e){e.printStackTrace();}
this.setMoney(m + save);
}
public void minusMoney(int minus){
int m = this.money;
try{
Thread.sleep(200);
}catch(InterruptedException e){e.printStackTrace();}
this.setMoney(m - minus);
}
}
▣
▣
class Park extends Thread{
public void run(){
▣
NotSyncMain.myBank.saveMoney(3000);
▣
System.out.println("saveMoney(3000):" +
NotSyncMain.myBank.getMoney());
▣
▣
}
▣
}
▣
class ParkWife extends Thread{
public void run(){
▣
▣
NotSyncMain.myBank.minusMoney(1000);
▣
▣
System.out.println("minusMoney(1000):" +
NotSyncMain.myBank.getMoney());
▣
}
▣
}
▣
▣
public class NotSyncMain{
public static Bank myBank = new Bank();
public static void main(String[] args) throws Exception{
System.out.println("원금:" + myBank.getMoney());
Park p = new Park();
ParkWife w = new ParkWife();
p.start();
try{
Thread.sleep(200);
}catch(InterruptedException e){e.printStackTrace();}
w.start();
}
}
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
8.3.5 synchronized
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class Bank{
private int money = 10000; //예금 잔액
public int getMoney(){
return this.money;
}
public void setMoney(int money){
this.money = money;
}
public synchronized void saveMoney(int save){
int m = this.getMoney();
synchronized 메서드
try{
Thread.sleep(3000);
}catch(InterruptedException e){e.printStackTrace();}
this.setMoney(m + save);
}
public void minusMoney(int minus){
synchronized(this){
int m = this.money;
synchronized 블록
try{
Thread.sleep(200);
}catch(InterruptedException e){e.printStackTrace();}
this.setMoney(m - minus);
}
}
}
8.3.6 synchronized의 활용
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class Bank{
//....중간 생략
public void saveMoney(int save){
int m = this.getMoney();
try{
Thread.sleep(3000);
}catch(InterruptedException e){e.printStackTrace();}
this.setMoney(m + save);
}
public void minusMoney(int minus){
int m = this.money;
try{
Thread.sleep(200);
}catch(InterruptedException e){e.printStackTrace();}
this.setMoney(m - minus);
}
}
class Park extends Thread{
public void run(){
synchronized(SyncMain2.myBank){
synchronized 블록
SyncMain2.myBank.saveMoney(3000);
}
System.out.println("saveMoney(3000):" + SyncMain2.myBank.getMoney());
}
}
class ParkWife extends Thread{
public void run(){
synchronized(SyncMain2.myBank){
synchronized 블록
SyncMain2.myBank.minusMoney(1000);
}
System.out.println("minusMoney(3000):" + SyncMain2.myBank.getMoney());
}
}
8.3.7 synchronized의 한계
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class VideoShop{
private Vector buffer = new Vector();
public VideoShop(){
System.out.println("프로그램 시작");
buffer.addElement("은하철도999-0");
Person p1 = new Person();
buffer.addElement("은하철도999-1");
buffer.addElement("은하철도999-2");
Person p2 = new Person();
buffer.addElement("은하철도999-3");
Person p3 = new Person();
}
public String lendVideo(){
Person p4 = new Person();
String v = (String)this.buffer.remove(buffer.size()-1);
p1.start();
return v;
}
p2.start();
public void returnVideo(String video){
p3.start();
this.buffer.addElement(video);
}
p4.start();
}
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class Person extends Thread{
public void run(){
synchronized(VideoShopMain.vShop){
//5초동안 VideoShopMain.vShop은 락(Lock)에 걸리게 된다.
불필요한 동기화
try{
String v = VideoShopMain.vShop.lendVideo();
System.out.println(this.getName() + ":" + v + " 대여");
System.out.println(this.getName() + ":" + v + " 보는중");
this.sleep(5000);
System.out.println(this.getName() + ":" + v + " 반납");
VideoShopMain.vShop.returnVideo(v);
}catch(InterruptedException e){e.printStackTrace();}
}
}
}
8.3.8 synchronized의 개선
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class VideoShop{
private Vector buffer = new Vector();
public VideoShop(){
buffer.addElement("은하철도999-0");
buffer.addElement("은하철도999-1");
buffer.addElement("은하철도999-2");
buffer.addElement("은하철도999-3");
}
public synchronized String lendVideo(){
String v = (String)this.buffer.remove(buffer.size()-1);
return v;
}
public synchronized void returnVideo(String video){
this.buffer.addElement(video);
}
}
필요한 곳에 동기화 보장
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
Person p4 = new Person();
p1.start();
p2.start();
p3.start();
p4.start();
System.out.println("프로그램 종료");
class Person extends Thread{
public void run(){
try{
String v = VideoShopMain2.vShop.lendVideo();
System.out.println(this.getName() + ":" + v + " 대여");
System.out.println(this.getName() + ":" + v + " 보는중");
this.sleep(5000);
System.out.println(this.getName() + ":" + v + " 반납");
VideoShopMain2.vShop.returnVideo(v);
}catch(InterruptedException e){e.printStackTrace();}
▣
▣
▣
▣
▣
▣
▣
▣
System.out.println("프로그램 시작");
}
}
8.3.9 synchronized의 개선의 한계
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class VideoShop{
private Vector buffer = new Vector();
public VideoShop(){
buffer.addElement("은하철도999-0");
buffer.addElement("은하철도999-1");
buffer.addElement("은하철도999-2");
buffer.addElement("은하철도999-3");
}
public synchronized String lendVideo(){
if(buffer.size()>0){
String v = (String)this.buffer.remove(buffer.size()-1);
return v;
▣
▣
System.out.println("프로그램 시작");
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
Person p4 = new Person();
Person p5 = new Person();
p1.start();
p2.start();
p3.start();
}else{
return null;
p4.start();
▣
▣
▣
▣
▣
▣
▣
}
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
class Person extends Thread{
public void run(){
String v = VideoShopMain3.vShop.lendVideo();
if( v == null){
데이터가 없으면 스레드가 요청작업을 포기하고 스레를 끝낸다.
return;
}
try{
this.sleep(5000);
VideoShopMain3.vShop.returnVideo(v);
}catch(InterruptedException e){e.printStackTrace();}
}
}
}
}
public synchronized void returnVideo(String video){
this.buffer.addElement(video);
}
p5.start();
System.out.println("프로그램 종료");
9 ███████████
9
9장 자바 Stream
소설같은
3
자바
rd
Edition
since 2001
최영관
Powered by http://www.jabook.org
9.1 스트림
9.1.1 스트림의 개요
▣
입출력을 위한 공통된 방법
◈
▣
입출력을 위한 공통된 방법이 없다면?
◈
▣
각각의 하드웨어 장치를 잘 알고 있다는 가정하에, 각각의 하드웨어 장치에
직접 접근해야 한다.
스트림(Stream)의 정의
◈
▣
다양한 장소에 존재하는 데이터들을 핸들하기 위해서는 입출력 데이터를
처리하는 공통된 방법이 있어야 한다.
자료의 입출력을 도와주는 중간 매개체
입출력 장치
파일 디스크(파일)
◈ 키보드, 모니터, 마우스
◈ 메모리
◈ 네트워크
◈
9.1.2 스트림이란?
▣
입출력 장치의 일반적인 특징
◈
▣
스트림의 역할
◈
▣
◈
FileInputStream fis = new FileInputStream(파일);
//fis.read()를 이용해서 데이터 읽기
File 출력 스트림의 연결과 작업
◈
◈
▣
장치마다 연결할 수 있는 각각의 스트림이 존재
File 입력 스트림의 연결과 작업
◈
▣
스트림은 데이터를 읽고 쓰기 위한 공통된 방법을 제공
장치(Device)와 스트림(Stream)
◈
▣
일반적인 입출력 장치는 대부분 데이터를 읽고 기록한다는 특징이 있다.
FileOutputStream fos = new FileOutputStream(파일);
//fos.write()를 이용해서 데이터 쓰기
스트림의 내부 동작원리는 몰라도 된다. 사용할 줄만 알면 된다.
◈
◈
사용자는 스트림이 어떻게 장치와 연결되고 작업이 되는지 몰라도 된다.
단지 스트림을 어떻게 생성하며 어떻게 데이터를 읽고 쓰는지만 알면 된다.
9.1.3 스트림의 원리
▣
스트림이란?
◈
▣
스트림으로부터 데이터 읽기
◈
◈
◈
◈
▣
◈
◈
입력 스트림
적절한 빨대를 음료수에 꽂는다.
빨대로부터 음료수를 흡입한다.
빨대로부터 마시고 싶은 만큼 계속해서 음료수를 흡입한다.
빨대를 제거한다.
출력 스트림
스트림에 데이터 기록하기
◈
◈
◈
◈
▣
목표지점에 적절한 입력용 스트림을 생성한다.
스트림으로부터 데이터를 읽는다.
스트림으로부터 필요한 만큼 계속해서 데이터를 읽는다.
스트림을 닫는다.
빨대로부터 음료수 마시기
◈
◈
▣
스트림이란 빨대다.
목표지점에 적절한 출력용 스트림을 생성한다.
스트림에 데이터를 기록한다.
스트림에 필요한 만큼 계속해서 데이터를 기록한다.
스트림을 닫는다.
빨대로 음료수 내뱉기
◈
◈
◈
◈
적절한 빨대를 컵에 꽂는다.
빨대로 입안에 있는 음료수를 내뱉는다.
빨대로 내뱉고 싶은 만큼 음료수를 내뱉는다.
빨대를 제거한다.
스트림의 기본적인 종류
입력용 스트림과 출력용 스트림이 있다.
음료수를 내뱉고 다시 마시려면 빨대가 2개 필요하다.
9.1.4 스트림의 종류
▣
바이트 단위의 read() 메서드
◈
◈
◈
▣
기본적인 분류
◈
◈
▣
◈
◈
▣
▣
◈
◈
▣
◈
◈
스트림의 종류 II
◈
◈
▣
문자 스트림: Reader, Writer(문자 단위로 처리)
바이트 스트림 : InputStream, OutputStream(바이트 단위로 처리)
입력 스트림 : Reader나 InputStream
출력 스트림 : Writer나 OutputStream
스트림의 규칙
◈
◈
InputStream과 Reader는 읽어 들이는 메서드를 포함하고 있어야 한다.
OutputStream과 Writer는 기록하는 메서드를 포함하고 있어야 한다.
int write(int c)
int write(byte cbuf[])
int write(byte cbuf[], int offset, int length)
문자 단위의 write() 메서드
◈
◈
◈
InputStream는 입력용 바이트 스트림이다.
OutputStream는 출력용 바이트 스트림이다.
'InputStream'나 'OutputStream'이라는 단어가 붙어 있다면 바이트 스트림이다.
PrintStream은 출력용만 존재하기 때문에 약간 특이하다.
스트림의 종류 I
◈
◈
▣
▣
바이트 스트림의 패턴
◈
◈
▣
Reader는 입력용 문자 스트림이다.
Writer는 출력용 문자 스트림이다.
Reader나 Writer가 붙는다면 문자 스트림이다.
int read()
int read(char cbuf[])
int read(char cbuf[], int offset, int length)
바이트 단위의 write() 메서드
◈
◈
◈
문자 스트림의 패턴
◈
문자 단위의 read() 메서드
◈
입력 스트림(예: FileInputStream, ObjectInputStream)
출력 스트림(예: FileOutputStream, ObjectOutputStream)
int read()
int read(byte cbuf[])
int read(byte cbuf[], int offset, int length)
int write(int c)
int write(char cbuf[])
int write(char int offset, int length)
9.1.5 문자 스트림과 바이트 스트림
스트림의 종류와 방향성
입력 스트림의 방향
나를 기준으로 데이터가 들어온
다면 무조건 입력 스트림
나
바이트스트림
바이트 그 자체를 핸들하는 스트림
바이트
문자스트림
문자 인코딩
바이트 저장소
문자
출력 스트림의 방향
나를 기준으로 데이터가 밖으로 나간
다면 무조건 출력 스트림
바이트를 문자로 인코딩하여 핸들
한다. 인코딩은 기본적으로 스트림
이 자동으로 판단한다.
스트림이 빨
대랬지!
9.2 표준 스트림과 File 클래스
9.2.1 표준 입출력
▣
System 클래스의 표준 입출력 스트림 멤버
◈
◈
◈
◈
◈
package java.lang;
public class System{
public static PrintStream out;
public static InputStream in;
public static PrintStream err;
◈
//중간 생략
◈ }
▣
System.out
◈
◈
▣
System.in
◈
◈
▣
표준 출력(모니터) 스트림
System.out.println("에러메시지");
표준 입력(키보드) 스트림
int d = System.in.read(); //한 바이트 읽어내기
System.err
◈
◈
표준 에러 출력(모니터) 스트림
System.err.println("데이터");
모니터(표준출력)
키보드(표준입력)
모니터(표준에러출력)
9.2.2 System.in
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
import java.io.*;
public class SystemInMain3{
public static void main(String[] args) throws IOException{
System.out.print("문자를 입력한 후 엔터를 누르세요? ");
int i;
while( (i = System.in.read()) != '\n' ){
System.out.println((char)i +":" + i);
}
}
}
▣
실행결과
◈
◈
◈
◈
◈
◈
◈
◈
◈
c:\javasrc\chap09>javac SystemInMain3.java
c:\javasrc\chap09>java SystemInMain3
문자를 입력한 후 엔터를 누르세요? Hello
H:72
e:101
l:108
l:108
o:111
:13
엔터
9.2.3 System.out과 System.err의 차이
▣
Standard Out과 Standard Error의 차이
◈
◈
◈
◈
◈
◈
◈
◈
▣
실행결과
◈
◈
◈
◈
◈
◈
▣
public class SystemOutErrMain{
public static void main(String[] args){
System.out.println("out : Hello ");
System.out.println("out : World");
System.err.println("err : It's eror stream");
System.out.println("out : World!");
}
}
c:\javasrc\chap09>javac SystemOutErrMain.java
c:\javasrc\chap09>java SystemOutErrMain
out : Hello
System.out과 System.err의 일반적 차이
out : World
err : It's eror stream
1. out과 err는 별개의 스트림이며, 독립적으로 리다이렉트된다.
out : World!
2. out은 버퍼링 지원, err는 버퍼링 지원하지 않음(자바는 모두 버퍼링지원)
리다이렉트시 실행 결과
◈
◈
c:\javasrc\chap09>java SystemOutErrMain > a.txt
err : It's eror stream
out의 결과는 a.txt로 리다이렉트 되었다.
err의 결과는 실행 콘솔창에 바로 출력된다.
9.2.4 File 클래스
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
File f = new File("FileMain.java");
PrintStream out = System.out;
out.println("isFile(): " + f.isFile()); //파일인지 아닌지
out.println("isDirectory(): " + f.isDirectory()); //디렉터리인지 아닌지
out.println("isHidden(): " + f.isHidden()); //숨김파일인지
out.println("lastModified(): " + f.lastModified()); //마지막에 수정된 날짜
out.println("canRead(): " + f.canRead()); //읽기 속성을 가진 파일인지
out.println("canWrite(): " + f.canWrite()); //쓰기 속성을 가진 파일인지
out.println("getPath(): " + f.getPath()); //상대 경로
out.println("getAbsolutePath(): "+ f.getAbsolutePath()); //절대 경로
out.println("getName(): " + f.getName()); //디렉터리 또는 파일의 이름
out.println("toURL(): " + f.toURL()); //URL형식의 경로
out.println("exists(): " + f.exists()); //파일이 존재하는지
out.println("length(): " + f.length()); //파일의 길이
9.2.5 파일 목록 출력하기
▣
현재 디렉터리와 상위 디렉터리의 경로 출력
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
File dir1 = new File(".");
File dir2 = new File("..");
System.out.println("AbsolutePath");//AbsolutePath
System.out.println ("Current dir-> " + dir1.getAbsolutePath());
System.out.println ("Parent dir-> " + dir2.getAbsolutePath());
System.out.println("CanonicalPath");//CanonicalPath
System.out.println ("Current dir-> " + dir1.getCanonicalPath());
System.out.println ("Parent dir-> " + dir2.getCanonicalPath());
File dir3 = new File("c:\\");//절대경로지정
System.out.println ("c:\\ -> " + dir3.getAbsolutePath());
File 클래스의 listFiles() - 디렉터리 목록 보기
◈
◈
◈
File f = new File("c:\\");
File[] fs = f.listFiles();
for(int i=0; i<fs.length; i++){
◈
System.out.println(fs[i].getName());
9.2.6 디렉터리 생성, 삭제, 파일이름 변경
▣
디렉터리의 생성
◈
◈
◈
◈
▣
디렉터리를 삭제하는 예
◈
◈
◈
◈
▣
File f = new File("NewFolder"); //디렉터리의 경우 비어 있어야 한다.
if(f.exists()){ //디렉터리가 존재하는지 확인
f.delete(); //디렉터리 삭제
}
파일을 삭제하는 예
◈
◈
◈
◈
▣
File f = new File("디렉터리명");
if(!f.exist()){
f.mkdir();
}
File f = new File("파일명");
if(f.exists()){
f.delete();
}
파일의 이름을 변경하는 예
◈
◈
◈
◈
◈
File f = new File("원본파일명");
File t = new File("변경할파일명");
if(f.exists()){
f.renameTo(t);
}
9.3 File 스트림
9.3.1 파일 입출력의 종류
▣
파일 입출력을 위한 스트림
◈
바이트 단위
– FileInputStream
– FileOutputStream
◈
문자 단위
– FileReader
– FileWriter
▣
바이트와 문자 스트림
바이트 단위의 파일을 핸들할 때에는 FileInputStream과 FileOutputStream을
사용한다.
◈ 문자 단위의 파일을 핸들하고자 할 때에는 FileReader와 FileWriter를 사용한
다.
◈
▣
입력과 출력 스트림
FileInputStream을 생성하는 이유는 파일을 읽기 위해서이다.
FileOutputStream을 생성하는 이유는 파일을 기록하기 위해서이다.
◈ FileWriter와 FileReader에서도 이것은 마찬가지이다.
◈
◈
9.3.2 바이트 단위의 출력 파일 스트림 I
▣
바이트 단위의 File 출력 스트림의 생성
◈
FileOutputStream fos = new FileOutputStream("a.dat");
◈ //fos로 쓰기 작업
◈ fos.close(); //스트림 닫기
▣
바이트 단위의 File 입력 스트림의 생성
◈
FileInputStream fis = new FileInputStream("b.dat");
◈ //fis로 읽기 작업
◈ fis.close(); //스트림 닫기
▣
파일 기록하기
◈
◈
◈
◈
◈
◈
◈
FileOutputStream fos = new FileOutputStream("a.dat");
fos.write(72);
fos.write(101);
fos.write(108);
fos.write(108);
fos.write(111);
fos.close();
◈ System.out.println("a.dat 파일 기록완료");
9.3.3 바이트 단위의 출력 파일 스트림 II
▣
FileOutputStream으로 데이터 기록하기 II
◈
◈
◈
◈
◈
▣
FileOutputStream fos = new FileOutputStream("a1.dat");
byte[] b = new byte[]{72, 101, 108, 108, 111};
fos.write(b);
fos.close();
System.out.println("a1.dat 파일 기록완료");
존재하는 파일의 끝에 덧붙여쓰기
◈
◈
File f = new File("a.dat");
if(f.exists()){
FileOutputStream fos = new FileOutputStream("a.dat", true);
byte[] b = new byte[]{72, 101, 108, 108, 111};
fos.write(b);
fos.close();
System.out.println("a.dat 파일의 끝부분에 데이터 추가하기 완료");
◈
◈
◈
◈
◈
◈
}
9.3.4 바이트 단위의 입력 파일 스트림 I
▣
FileInputStream을 이용해서 파일 읽기
◈
◈
◈
◈
◈
◈
▣
FileInputStream fis = new FileInputStream("a.dat");
int i;
while( (i=fis.read()) != -1 ){
System.out.print((char)i);
}
fis.close();
FileInputStream의 read(byte[] b) - FileInputStream을 이용한 여러 바이트 읽기
◈
◈
◈
◈
◈
◈
◈
◈
◈
FileInputStream fis = new FileInputStream("FileStreamMain5.java");
int count;
byte[] b = new byte[10];
while( (count=fis.read(b)) != -1 ){
for(int i=0; i<count; i++){
System.out.print((char)b[i]);
}
}
fis.close();
9.3.5 바이트 단위의 입력 파일 스트림 II
▣
1. 파일 사이즈 알아내기
◈
◈
◈
▣
2. 파일 사이즈에 해당하는 배열 만들기
◈
▣
byte[] b = new byte[fileSize];
3. 스트림을 이용해서 배열에 데이터 채우기
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
File f = new File("FileStreamMain6.java");
int fileSize = (int)f.length();
System.out.println("파일의 사이즈:" + fileSize);
FileInputStream fis = new FileInputStream("FileStreamMain6.java");
int pos = 0;
int size = 10;
int temp;
while( (size=fis.read(b, pos, size)) > 0 ){
pos += size;
temp = b.length - pos;
if(temp < 10){
size = temp;
}
}
fis.close();
System.out.println("읽은 바이트수:" + pos);
4. 배열을 통째로 파일에 기록하기
◈
◈
◈
FileOutputStream fos = new FileOutputStream("test.txt");
fos.write(b);
fos.close();
9.3.6 문자 단위의 출력 파일 스트림
▣
FileWriter로 문자 기록하기
◈
◈
◈
◈
◈
◈
◈
▣
FileWriter에 문자열과 문자배열 기록하기
◈
◈
◈
◈
◈
◈
▣
FileWriter fos = new FileWriter("writer.dat");
fos.write(72);
fos.write(101);
fos.write(108);
fos.write(108);
fos.write(111);
fos.close();
char[] content = new char[]{72, 101, 108, 108, 111};
String str = new String("Hello World!");
FileWriter fos = new FileWriter("writer2.dat");
fos.write(content); //문자배열의 기록
fos.write(str); //문자열의 기록
fos.close();
파일의 마지막 부분에 덧붙여넣기
◈
◈
◈
◈
String str = new String("New Hello World!");
FileWriter fos = new FileWriter("writer2.dat", true);
fos.write(str);
fos.close();
9.3.7 문자 단위의 입력 파일 스트림
▣
FileReader를 생성하는 방법
◈
◈
◈
▣
FileReader의 read() 계열의 메서드
◈
◈
▣
FileReader fr = new FileReader("파일명");
//fr을 이용해서 문자 읽기
fr.close(); //스트림 닫기
int read()
int read(char[] cbuf, int offset, int length)
FileReader를 이용해서 파일 읽기
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
import java.io.*;
public class FileReaderMain{
public static void main(String[] args) throws IOException{
FileReader fr = new FileReader("writer.dat");
int i;
while( (i=fr.read()) != -1 ){
System.out.print((char)i);
}
fr.close();
}
}
9.3.8 파일복사
▣
File 스트림을 이용한 파일 복사 예제(한 바이트 단위)
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
int i, len=0;
FileInputStream fis = new FileInputStream(args[0]);
FileOutputStream fos = new FileOutputStream(args[1]);
long psecond = System.currentTimeMillis();
while((i=fis.read()) != -1) {
fos.write(i);
len++;
}
fis.close();
fos.close();
psecond = System.currentTimeMillis() - psecond;
File 스트림
버퍼링지원하지 않음
Buffered 스트림을 이용한 파일 복사 예제(한 바이트 단위)
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
int i, len=0;
변환
FileInputStream fis = new FileInputStream(args[0]);
FileOutputStream fos = new FileOutputStream(args[1]);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
long psecond = System.currentTimeMillis();
while((i=bis.read()) != -1) {
bos.write(i);
Buffered
len++;
}
bis.close();
bos.close();
psecond = System.currentTimeMillis() - psecond;
스트림
버퍼링지원
9.3.9 RandomAccessFile
▣
임의의 접근(Random Access)
◈
▣
스트림을 사용할 때 순차적으로 read()를 호출하게 되며, reset()을 이용하면 처음부터 다시 읽을
수 있다. 하지만 사용자가 원하는 위치로 이동하거나 되돌릴 수는 없다. 그렇기 때문에 일반적인
스트림에서는 데이터를 랜덤하게 접근할 수 없다.
임의 접근방식으로 파일 접근-RandomAccessFile을 테스트하는 예제
◈
◈
◈
◈
◈
◈
◈
public class RandomAccessFileMain{
public static void main(String[] args) throws IOException{
String s = "I love normal java";
String q = "jabook";
RandomAccessFile rf = new RandomAccessFile("raccess.txt", "rw");
rf.writeBytes(s);
데이터 기록하기
rf.close();
RandomAccessFile rf2 = new RandomAccessFile("raccess.txt", "rw");
rf2.seek(7);
특정 부분에 데이터 기록하기
rf2.writeBytes(q);
rf2.close();
◈
◈
◈
◈
RandomAccessFile rf3 = new RandomAccessFile("raccess.txt", "r");
rf3.seek(2);
특정 부분 읽어내기
System.out.println(rf3.readLine());
rf3.close();
◈
◈
◈
◈
}
◈
◈
}
9.4 Memory 스트림
9.4.1 메모리 스트림이란
장소에 따른 파일 스트림의 분류
목표지점에 직접 연결되는 스트림
File에 직접 연결되는 스트림
File 스트림
바이트 배열을 이용한
스트림
Memory에 직접 연결되는 스트림
Network에 직접 연결되는 스트림
Memory 스트림
Network 스트림
문자 배열을 이용한 스트림
문자열을 목표지점으로 하는
스트림
ByteArrayInputStream
CharArrayReader
StringReader
ByteArrayOutputStream
CharArrayWriter
StringWriter
9.4.2 ByteArray 스트림
▣
ByteArray 스트림
◈
▣
바이트 배열을 스트림처럼 다룰 수 있게 도와주는 스트림
바이트 배열 스트림을 테스트하는 예
◈
◈
int i;
byte[] arr = {'j', 'a', 'b', 'o', 'o', 'k'};
◈
◈
◈
◈
◈
◈
ByteArrayInputStream in = new ByteArrayInputStream(arr);
ByteArrayOutputStream out = new ByteArrayOutputStream();
//ByteArrayInputStream에서 읽고 ByteArrayOutputStream에 기록
while((i = in.read()) != -1) {
out.write(i);
}
◈
◈
◈
◈
◈
◈
byte[] result = out.toByteArray();
for(i=0; i<result.length; i++){
System.out.print((char)result[i]);
}
in.close();
out.close();
ByteArray 스트림 생성
ByteArrayOutputStream으로부터 바이트 배열 얻기
9.4.3 CharArray 스트림
▣
CharArray 스트림
◈
▣
문자 배열을 스트림처럼 다룰 수 있게 도와주는 스트림
CharArray 스트림을 이용한 문자 배열 다루기
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
int ch;
char arr[] = {'j','a','b','o','o','k'};
CharArrayReader car = new CharArrayReader(arr);
CharArrayWriter caw = new CharArrayWriter();
while((ch=car.read()) != -1) {
caw.write(ch);
}
String str = caw.toString();
System.out.println(str); //jabook 출력
//결과출력
char[] result = caw.toCharArray();
for(int i=0; i<result.length; i++){
System.out.print(result[i]); //jabook 출력
}
car.close();
caw.close();
CharArray 스트림 생성
9.4.4 String 스트림
▣
String 스트림
◈
▣
문자열을 스트림 형태로 다룰 수 있게 도와주는 스트림
String 스트림을 사용하는 예
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
int ch;
String str = "Hello!";
StringReader sr = new StringReader(str);
StringWriter sw = new StringWriter();
while((ch=sr.read()) != -1) {
sw.write(ch);
}
String result = sw.toString();
System.out.println(result);
StringBuffer sb = sw.getBuffer();
System.out.println(sb.toString());
String 스트림 생성
9.5 2차 스트림
9.5.1 2차 스트림이란
▣
목표지점에 직접 연결되는 스트림
◈
◈
◈
▣
File 스트림 : File에 직접 연결되는 스트림
Memory 스트림 : Memory에 직접 연결되는 스트림
Network 스트림 : Network에 직접 연결되는 스트림
FileInputStream을 BufferedInputStream으로 변환
◈
◈
FileInputStream fis = new FileInputStream("파일명");
BufferedInputStream bis = new BufferedInputStream(fis);
대표적인 1차 스트림들
대표적인 2차 스트림들
InputStream, OutputStream
InputStreamReader, OutputStreamWriter
FileInputStream, FileOutputStream
FileReader, FileWriter
변환
ByteArrayInputStream, ByteArrayOutputStream
CharArrayReader, CharArrayWriter
StringReader, StringWriter
▣
DataInputStream, DataOutputStream
BufferedInputStream, BufferedOutputStream
BufferedReader, BufferedWriter
ObjectInputStream, ObjectOutputStream
FileReader를 BufferedReader로 변환(1차 스트림을 2차 스트림으로 가공)
◈
◈
◈
FileReader fr = new FileReader("파일명");
BufferedReader br = new BufferedReader(fr);
String str = br.readLine();
9.5.2 스트림 변환법
▣
스트림 변환 규칙 I
◈
◈
▣
스트림 변환 규칙 II
◈
◈
▣
◈
◈
FileInputStream -> BufferedInputStream
FileOutputStream -> BufferedOutputStream
다른 계열끼리의 변환(특수한 경우)
◈
▣
FileReader -> BufferedReader
FileWriter -> BufferedWriter
바이트 스트림끼리의 변환
◈
▣
바이트 스트림은 바이트 스트림 계열로 변환
문자 스트림은 문자 스트림 계열로 변환
문자 스트림끼리의 변환
◈
▣
입력 스트림은 입력 스트림 계열로 변환
출력 스트림은 출력 스트림 계열로 변환
바이트 트림 -> 문자 스트림
바이트 스트림에서 문자 스트림으로 변환
◈
◈
InputStream -> InputStreamReader
OutputStream -> OutputStreamWriter
9.5.3 문자 스트림으로 변환
▣
바이트 스트림을 문자 스트림으로 변환하는 예제
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
바이트 스트림으로 문자를 핸들할 때 발생하는 문제를 보여주는 예제
◈
◈
◈
◈
◈
◈
▣
FileInputStream fis = new FileInputStream (args[0]);
FileOutputStream fos = new FileOutputStream (args[1]);
InputStreamReader isr = new InputStreamReader (fis);
OutputStreamWriter osw = new OutputStreamWriter (fos);
int i;
while((i = isr.read()) != -1){
osw.write (i);
}
osw.close();
isr.close();
int i;
System.out.println("종료하기 위해서는 '끝'을 입력하시오");
System.out.println("출력할 데이터를 입력하시오? ");
while((i = System.in.read()) != '끝') {
System.out.print((char)i);
}
문제발생
System.in.read()는 1바이트를
읽는 메서드이며 '끝'이라는 문
자는 2바이트이다.
바이트스트림
문자 스트림을 이용한 문제해결
◈
◈
◈
◈
◈
◈
◈
int i;
InputStreamReader isr = new InputStreamReader(System.in);
System.out.println("종료하기 위해서는 '끝'을 입력하시오");
System.out.println("출력할 데이터를 입력하시오? ");
while((i = isr.read()) != '끝') {
System.out.print((char)i);
}
변환
해결책
문자스트림
9.5.4 Buffered 바이트 스트림
▣
File 스트림을 이용한 파일 복사 예제(한 바이트 단위)
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
int i, len=0;
FileInputStream fis = new FileInputStream(args[0]);
FileOutputStream fos = new FileOutputStream(args[1]);
long psecond = System.currentTimeMillis();
while((i=fis.read()) != -1) {
fos.write(i);
len++;
}
fis.close();
fos.close();
psecond = System.currentTimeMillis() - psecond;
Buffered 스트림을 이용한 파일 복사 예제(한 바이트 단위)
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
int i, len=0;
FileInputStream fis = new FileInputStream(args[0]);
FileOutputStream fos = new FileOutputStream(args[1]);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
long psecond = System.currentTimeMillis();
while((i=bis.read()) != -1) {
bos.write(i);
len++;
}
bis.close();
bos.close();
psecond = System.currentTimeMillis() - psecond;
버퍼링지원하지 않음
File 스트림
변환
Buffered 스트림
버퍼링지원
9.5.5 Buffered 문자 스트림
▣
Buffered 스트림을 이용한 문자 단위의 파일 복사의 예
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
int i, len=0;
FileReader fr = new FileReader(args[0]);
FileWriter fw = new FileWriter(args[1]);
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw);
long psecond = System.currentTimeMillis();
while((i=br.read()) != -1) {
bw.write(i);
len++;
}
br.close();
bw.close();
psecond = System.currentTimeMillis() - psecond;
▣
BufferedReader로 파일읽기
◈
◈
◈
◈
◈
◈
◈
FileReader fr = new FileReader(args[0]);
BufferedReader br = new BufferedReader(fr);
String temp;
while((temp=br.readLine())!= null ){
System.out.println(temp);
}
br.close();
BufferedReader를 이용해서 줄 단위로 표준 입력 읽어내기
◈
◈
◈
◈
◈
◈
◈
◈
int i, len=0;
InputStream in = System.in;
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String temp;
if((temp=br.readLine())!= null ){
System.out.println(temp);
}
9.5.6 Data 스트림
public class DataInputOutputMain{
public static void main(String[] args) throws IOException{
//DataOutputStream을 이용해서 데이터 타입 단위로 데이터 기록하기
FileOutputStream fos = new FileOutputStream("data.dat");
DataOutputStream dos = new DataOutputStream(fos);
dos.write(100); //byte형으로 기록하기(1바이트)
dos.writeInt(100); //int형으로 기록하기(4바이트)
dos.writeFloat(3.14f); //float형으로 기록하기(4바이트)
dos.writeChar('A'); //char형으로 기록하기(2바이트)
dos.close();
File 스트림
변환
}
}
//DataInputStream을 이용해서 데이터 타입 단위로 데이터 읽어오기
FileInputStream fis = new FileInputStream("data.dat");
DataInputStream dis = new DataInputStream(fis);
int i = dis.read(); //1바이트 읽기
int i2 = dis.readInt(); //4바이트 읽기
float f = dis.readFloat(); //4바이트를 float형식으로 읽기
Data 스트림
char c = dis.readChar(); //2바이트 읽기
dis.close();
데이터 타입 단위로 읽기 및 저장
System.out.println( i + "," + i2 + "," + f + "," + c);
버퍼링 지원
9.5.7 수작업 직렬화
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
▣
public class MemData{
객체의 정보를 파일에 저장한 후 다시 복원하기
private int id;
MemData m = new MemData();
private long type;
private float weight;
m.setMemData(100, 200000, 3.14159F);
public void setMemData(int id, long type, float weight){ System.out.println(m.toString() + " : " + m.getMemData());
this.id = id;
m.saveMemData("memdata.dat"); //파일에 객체 저장
this.type = type;
this.weight = weight;
}
MemData r = MemData.restoreMemData("memdata.dat");
public String getMemData(){
System.out.println(r.toString() + " : " + r.getMemData());
return id + ", " + type + ", " + weight;
}
public void saveMemData(String filename) throws IOException{
FileWriter fw = new FileWriter(filename);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("MemData\n");
MemData 저장을 위한 메서드
bw.write(id + "\n");
bw.write(type + "\n");
bw.write(weight + "\n");
bw.close();
}
public static MemData restoreMemData(String filename) throws IOException{
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
String temp = br.readLine();
MemData memObj = new MemData();
MemData를 읽어내기 위한 메서드
if(temp.equals("MemData")){
int i = Integer.parseInt(br.readLine());
long t = Long.parseLong(br.readLine());
float w = Float.parseFloat(br.readLine());
memObj.setMemData(i, t, w);
}
br.close();
return memObj;
}
}
9.5.8 Object 스트림
▣
직렬화(Serialization)
◈
◈
◈
◈
◈
◈
◈
◈
◈
▣
FileOutputStream fos = new FileOutputStream("serial.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
SampleData s1 = new SampleData("홍길동", 1004);
▣ 직렬화를 위한 클래스
SampleData s2 = new SampleData("김삿갓", 1005);
◈ class SampleData implements Serializable {
◈
public String str;
oos.writeObject(s1);
객체 저장
◈
public int id;
oos.writeObject(s2);
◈
public SampleData(String str, int id) {
oos.close();
◈
this.str = str;
◈
this.id = id;
System.out.println(s1 + ":" + s1.getSampleData());
◈
}
System.out.println(s2 + ":" + s2.getSampleData());
◈
◈
역직렬화(Deserialization)
◈
◈
◈
◈
◈
◈
◈
◈
◈
public String getSampleData(){
return id + "=" + str;
}
}
FileInputStream fis = new FileInputStream("serial.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
SampleData sd1 = (SampleData)ois.readObject();
객체 복원
SampleData sd2 = (SampleData)ois.readObject();
ois.close();
System.out.println(sd1 + ":" + sd1.getSampleData());
System.out.println(sd2 + ":" + sd2.getSampleData());
End ███████████
End