Data Stream은 .dat 파일을 만들 때 사용하는 스트림으로 


특징은 dat 파일을 읽기 위해서는 어떤 타입이 어떤 순서로 들어있는지 알아야 읽을 수 있기 때문에


쉽게 읽어올 수 없다는 특징을 가진다.



package chap11.exam10.data;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class DataOutput {

	public static void main(String[] args) throws Exception {
		// 자료형을 유지해서 파일에 쓰기
		// 1. 파일 위치 설정
		String path = "C:/img/data.dat";
		// 2. 스트림 준비(보조)
		FileOutputStream fos = new FileOutputStream(path);
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		DataOutputStream dos = new DataOutputStream(bos);
		
		// 3. 쓰기
		dos.writeUTF("aaa");
		dos.writeInt(1000);
		dos.writeBoolean(true);
		
		// 4. 비우기
		dos.flush();
		
		// 5. 닫기
		dos.close();
		bos.close();
		fos.close();
	}

}


package chap11.exam10.data;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;

public class DataInput {

	public static void main(String[] args) throws Exception {
		
		// 1. 파일 위치 설정
		String path = "C:/img/data.dat";
		// 2. 스트림 준비(보조)
		FileInputStream fis = new FileInputStream(path);
		BufferedInputStream bis = new BufferedInputStream(fis);
		DataInputStream dis = new DataInputStream(bis);
		
		// 3. 읽기 (파일에 있는 타입들을 알아야 하고
		// 입력한 순서대로 불러야 한다.)
		// 4. 출력
		System.out.println("이름 : " + dis.readUTF());
		System.out.println("급여 : " + dis.readInt());
		System.out.println("승진대상 : " + dis.readBoolean());
		
		
		
		// 5. 닫기
		dis.close();
		bis.close();
		fis.close();
	}

}



다양한 스트림들은 다양한 타입을 다루기에는 적절하지 않고 보통 하나에 특화 되어 있다.


그러나 Object Stream은 데이터 타입의 최상위 객체인 Object를 의미하는 보조 스트림으로


다양한 데이터 타입 뿐만 아니라 객체, 배열, 컬렉션 등 다양한 형태가 들어 갈 수 있다.


하지만 클래스의 경우는 직렬화를 하지 않으면 전달되지 않는다.


직렬화는 전달 할 데이터를 바이트로 바꿔서 나열하는 것이다.


다음 예제는 dat 파일을 만들어 읽는데 Sample이라는 클래스도 함께 전달하는 예제이다.



package chap11.exam11.obj;

import java.io.Serializable;
// 직렬화 인터페이스를 통해 모양을 잡아 준다.
public class Sample implements Serializable {
	/**
	 * 통신 시 사용하는 일련번호 serialVersionUID
	 * 객체 통신을 위한 일련번호
	 */
	private static final long serialVersionUID = 1L;
	int num = 11;
	String team = "edu";
	String job = "manager";
	
	public String method() {
		return "메소드 호출";
	}
}



package chap11.exam11.obj;

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;

public class ObjOutput {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 설정
		String path = "C:/img/temp/obj.dat";
		// 2. 스트림 준비(보조)
		FileOutputStream fos = new FileOutputStream(path);
		DataOutputStream dos = new DataOutputStream(fos);
		ObjectOutputStream oos = new ObjectOutputStream(dos);
		// 3. 쓰기
		// - Map으로
		Map<String, String> map = new HashMap<>();
		map.put("name",  "kim");
		map.put("phone", "010-1234-5678");
		oos.writeObject(map);
		// - Array로
		oos.writeObject(new int[] {90, 95, 90, 100});
		// - String으로
		oos.writeUTF("기본형도 가능");
		// - Instance Object, 객체화하여 넣기
		oos.writeObject(new Sample());
		// 4. 비우기
		oos.flush();
		// 5. 자원 반납
		oos.close();
		dos.close();
		fos.close();
	}

}



package chap11.exam11.obj;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Map;
import java.util.Set;

public class ObjInput {

	public static void main(String[] args) throws Exception {
		// 1. 경로 설정
		String path = "C:/img/temp/obj.dat";
		
		// 2. 스트림 생성
		FileInputStream fis = new FileInputStream(path);
		BufferedInputStream bis = new BufferedInputStream(fis);
		ObjectInputStream ois = new ObjectInputStream(bis);
		
		// 3. 읽기 
		Map<String, String> map = (Map<String, String>)ois.readObject();
		int [] scores = (int[])ois.readObject();
		String msg = (String)ois.readUTF();
		Sample sample = (Sample)ois.readObject();
		
		// 4. 출력하기
		Set<String> set = map.keySet();
		for(String key : set) {
			System.out.println(key + " : " + map.get(key));
		}
		
		for(int i = 0; i < scores.length; i++) { System.out.println(scores[i]); }
		
		System.out.println(msg);
		
		System.out.println("sample : " + sample.num);
		System.out.println("sample : " + sample.job);
		System.out.println("sample : " + sample.team);
		System.out.println("sample : " + sample.method());
		
		// 4. 해제
		ois.close();
		bis.close();
		fis.close();
	}

}





Prop은 Properties의 줄임말로 보통 Java Project 내부에서 읽고 쓰기를 한다.


프로그램의 내부에서 사용하기 때문에 환경 설정, 저장한 데이터 등의 데이터를 저장한다.


Properties는 map 인터페이스를 상속받은 자료 구조이다.


다음 예제를 확인해보자. 예제를 실행 하면 클래스 파일 옆에 Properties 파일이 생성된다.



package chap11.exam12.prop;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.util.Properties;

public class PropWriter {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 지정
		String path = "src/chap11/exam12/prop/profile.properties";
		
		// 2. Properties 객체 준비
		Properties pro = new Properties();
		
		// 3. 객체에 값 추가 (무엇을 넣어도 문자열로 넣어버림
		pro.put("1", "1111");
		pro.put("2", "2222");
		pro.put("3", "3333");
		pro.put("4", "4444");
		pro.put("5", "5555");
		
		// 4. 스트림 준비
		FileOutputStream fos = new FileOutputStream(path);
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		
		// 5. 저장(스트림, 주석)
		pro.store(bos, "simple Comment");
		System.out.println("저장 완료");
		
		// 6. 자원 반납
		bos.close();
		fos.close();
	
	}

}



package chap11.exam12.prop;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

public class PropReader {

	public static void main(String[] args) throws Exception {
		// 1. 불러올 파일 위치 지정
		String path = "";
		// jar로 배포하고나면 사용자마다 경로가 상이하므로
		// 특정 클래스를 기준으로 리소스를 호출해야 한다.
		// 1-1. 경로를 가져오는 방법
//		path = PropReader.class.getResource("profile.properties").getPath();
//		System.out.println(path);
		// 1-2. 스트림으로 직접 불러오는 방
		InputStream is = PropReader.class.getResourceAsStream("profile.properties");
		
		// 2. 프로퍼티 객체 준비
		Properties pro = new Properties();
		
		// 3. 스트림 준비
		BufferedInputStream bis = new BufferedInputStream(is);
		
		// 4. 읽어오기
		pro.load(bis);
		
		// 5. 객체에서 값을 하나씩 추출
		System.out.println(pro.get("1"));
		System.out.println(pro.get("2"));
		System.out.println(pro.get("3"));
		System.out.println(pro.get("4"));
		System.out.println(pro.get("5"));
		
		// 6. 자원 반납
		bis.close();
		is.close();
		
		// 간단한 설정에 좋음, 보안성이 안좋음(전부 String)
		
	}

}



보조 스트림은 다른 스트림에게 스트림을 전달하는 기능이다.


일반적으로 스트림 자체가 기능이 하나만 특화되어 매우 한정적인데


다른 스트림에 전달 함으로써 다른 기능을 사용 할 수 있다.


하지만 Stream 선언이 늘어 난 만큼 close로 해제도 제때 해주어야 한다.


다음 예제는 File Stream을 Input, Output Stream에 전달한 예제이다.



package chap11.exam08.string;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

public class TextFileCopy {

	public static void main(String[] args) throws Exception {
		// 1. 경로 설정
		String ori = "C:/img/asd.txt";
		String target = "C:/img/temp/asd.txt";
				
		// 2. 스트림 준비
		// 보조스트림 - 바이너리 기반의 스트림에 문자열 특화 기능 추가
		FileInputStream fis = new FileInputStream(ori);
		InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
		
		FileOutputStream fos = new FileOutputStream(target);
		OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");
		
		// 3. 읽어오기
		char[] arr = new char[100];
		while(reader.read(arr) != -1) {
			writer.write(arr);	// 4. 내보내기
		}

		// 5. 자원반납(flush, close)
		writer.flush();
		reader.close();
		writer.close();
		fis.close();
		fos.close();
		
	}

}






기존의 스트림이 하나씩 하나씩 데이터를 들고 왔다면


Buffer Stream은 데이터를 한꺼번에 여러개씩 들고, 한꺼번에 전송 할 수 있도록 해 주는 임시 저장소이다.


그만큼 속도도 확연하게 차이나게 된다.


다음 예제는 데이터를 하나씩 받았을 때와 


Buffer Stream을 이용해 Byte 배열 덩어리로 한꺼번에 받았을 때의 속도를 비교하는 코드이다.


TimeChecker chk = new TimeChecker는 클래스 파일을 새로 만들어 선언한 클래스이고, 시간을 체크하는 기능을 간단하게 만들었다.


버퍼를 사용하지 않을 때


package chap11.exam09.buffer;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class BufferNotUse {

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

		TimeChecker chk = new TimeChecker();
		// 1. 파일 위치 지정
		String ori = "C:/img/10multi.pdf";
		String target = "C:/img/temp/NotuseCopy.pdf";
		
		// 2. 스트림 준비
		FileInputStream fis = new FileInputStream(ori);
		FileOutputStream fos = new FileOutputStream(target);
		int data;
		chk.timeStart();
		
		// 3. 읽기
		while((data = fis.read()) != -1) {
			System.out.println("복사중...");
			fos.write(data); // 4. 쓰기(시간 체크)
		}
		
		// 5. 비우고 반납
		fos.flush();
		// 15667ms 걸림, 15.6초
		System.out.println("완료 : " + chk.timeStop() + " ms");
		fis.close();
		fos.close();
	}

}


버퍼를 사용 할 때


package chap11.exam09.buffer;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.Buffer;

public class BufferUse {
	
	// try-with-resource == 트라이문이 끝나면 아래에 소켓 버퍼 등 close를 할 필요가 없음.
	public static void main(String[] args) throws Exception {
		TimeChecker chk = new TimeChecker();
		// 1. 파일 위치 지정
		String ori = "C:/img/10multi.pdf";
		String target = "C:/img/temp/useCopy.pdf";
		
		// 2. 스트림 준비(보조 스트림)
		try(  // 트라이가 끝나면 자동으로 close가 된다.
				FileInputStream fis = new FileInputStream(ori);
				BufferedInputStream bis = new BufferedInputStream(fis);
				
				FileOutputStream fos = new FileOutputStream(target);
				BufferedOutputStream bos = new BufferedOutputStream(fos);
			){
			byte[] arr = new byte[1024];
			chk.timeStart();
			while(bis.read(arr) != -1 ) {
				bos.write(arr);
			}
			// 5. 비우고 반납
			bos.flush();
			// 15667ms 걸림, 15.6초
			// 6044ms 걸림, 6초 (data를 배열로 받지 않았을 때)
			// 4ms 걸림, 0.004초 (byte[]배열로 받았을 때)
			System.out.println("완료 : " + chk.timeStop() + " ms");
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	
//	public static void main(String[] args) throws Exception {
//
//		TimeChecker chk = new TimeChecker();
//		// 1. 파일 위치 지정
//		String ori = "C:/img/10multi.pdf";
//		String target = "C:/img/temp/useCopy.pdf";
//		
//		// 2. 스트림 준비(보조 스트림)
//		FileInputStream fis = new FileInputStream(ori);
//		BufferedInputStream bis = new BufferedInputStream(fis);
//		
//		FileOutputStream fos = new FileOutputStream(target);
//		BufferedOutputStream bos = new BufferedOutputStream(fos);
//		
//		byte[] arr = new byte[1024];
//		chk.timeStart();
//		while(bis.read(arr) != -1 ) {
//			bos.write(arr);
//		}
//		
//		
//		
//		
//		
//		
//		
//		
////		int data;
////		chk.timeStart();
////		
////		// 3. 읽기
////		while((data = bis.read()) != -1) {
////			System.out.println("복사중...");
////			bos.write(data); // 4. 쓰기(시간 체크)
////		}
//		
//		// 5. 비우고 반납
//		bos.flush();
//		// 15667ms 걸림, 15.6초
//		// 6044ms 걸림, 6초 (data를 배열로 받지 않았을 때)
//		// 4ms 걸림, 0.004초 (byte[]배열로 받았을 때)
//		System.out.println("완료 : " + chk.timeStop() + " ms");
//		bos.close();
//		bis.close();
//		fis.close();
//		fos.close();
//	}

}


시간을 체크 할 타이머


package chap11.exam09.buffer;

public class TimeChecker {
	long start = 0;
	long end = 0;
	
	public void timeStart() {
		start = System.currentTimeMillis();
	}
	
	public long timeStop() {
		end = System.currentTimeMillis();
		return end-start;
	}
}



2번째 코드에서 try catch 문의 try 앞에 괄호가 추가 되었는데


괄호 안에 Stream을 선언하였을 경우 


try catch문이 끝났을 때 자동으로 close, 해제가 된다. (아래 복사)



		// 2. 스트림 준비(보조 스트림)
		try(  // 트라이가 끝나면 자동으로 close가 된다.
				FileInputStream fis = new FileInputStream(ori);
				BufferedInputStream bis = new BufferedInputStream(fis);
				
				FileOutputStream fos = new FileOutputStream(target);
				BufferedOutputStream bos = new BufferedOutputStream(fos);
			){
			byte[] arr = new byte[1024];
			chk.timeStart();
			while(bis.read(arr) != -1 ) {
				bos.write(arr);
			}
			// 5. 비우고 반납
			bos.flush();
			// 15667ms 걸림, 15.6초
			// 6044ms 걸림, 6초 (data를 배열로 받지 않았을 때)
			// 4ms 걸림, 0.004초 (byte[]배열로 받았을 때)
			System.out.println("완료 : " + chk.timeStop() + " ms");
			
		} catch (Exception e) {
			e.printStackTrace();
		}





File class는 I/O에 해당하는 클래스가 아니다.


하지만 File을 읽어오거나 쓸 때 사용하며 이 기능을 사용 할 때 Stream과 함께 사용된다.


일반적으로


1. 파일 객체를 만들어 읽어올 경로를 설정하고


2. 읽어올 파일을 Input Stream에 집어 넣고


3. 프로그램에서 Stream을 이용해 작업 한 후


4. 작업한 데이터를 Output Stream에 집어 넣고


5. 파일 객체 만들어 파일을 만들 경로를 설정하여 내보냄


내보낼 때에는 File Input Stream, File Output Stream을 사용한다.



다음 예제는 File 객체의 메소드를 활용한 예제이다.



package chap11.exam04.file;

import java.io.File;

public class FileMain {

	public static void main(String[] args) throws Exception {
		// 폴더 만들기 - File 객체 사용
		File dir = new File("c:/img/temp");
		if(!dir.exists()) { // 폴더가 존재 하지 않을 경우
			System.out.println("폴더 생성");
			dir.mkdirs();
		}		
		
		// 파일 만들기 - File 객체 사용
		 File file = new File("C:/img/temp/test.txt");
		 if(!file.exists()) {
			 System.out.println("파일 생성");;
			 file.createNewFile();
		 }

		 // 폴더 또는 파일에 대한 정보 - File 객체 사용
		 dir = new File("c:/img");
		 // 폴더에 해당하는 파일명
		 String[] fileNames = dir.list();
		 for(String name : fileNames) {
			 System.out.println(name);
		 }
		 
		 // 파일 객체를 뽑아내기
		 File[] files = dir.listFiles();
		 String gubun;
		 for(File f : files) {
			 if(f.isDirectory()) {
				 System.out.println("true");
				 gubun = "[디렉토리]";
			 } else {
				 gubun = "[파일]";
			 }
			 System.out.println(gubun + " " + f.getName() + " / " + f.length() + "byte");
		 }
	}

}


package chap11.exam05.fileinput;

import java.io.File;
import java.io.FileInputStream;

public class FileInputMain {

	public static void main(String[] args) throws Exception {
		// 1. 읽어올 파일 위치 지정
		String path = "C:/img/temp/test.txt";
		
		// 2. 파일 객체 생성
		File file = new File(path);
		
		// 3. 스트림 준비(빨대)
		// FileInputStream은 파일 특화이고 생성자 경로를 문자열로 넣어도 됨
		FileInputStream fis = new FileInputStream(file);
		// fis = new FileInputStream(path);
		
		// 4. 빨아 들인다.
		int data;
		while((data = fis.read())!=-1) { // -1 == EOF
			// 5. 출력(시스템안에서 이루어짐)
			System.out.print((char)data);
		}

		
		// 6. 닫아준다.(빨대 치우기)
		fis.close();
	}

}



package chap11.exam05.fileinput;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class FileOutputMain {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 선정(읽어올 곳, 내보낼 곳)
		String pathInput = "C:/img/001.jpg";
		String pathInput2 = "C:/img/10multi.pdf";
		String pathOutput = "C:/img/temp/food.jpg";
		String pathOutput2 = "C:/img/temp/10multi_copy.pdf";
		// 2. 스트림 준비(input, output)
		FileInputStream fis = new FileInputStream(pathInput);
		FileOutputStream fos = new FileOutputStream(pathOutput);
		FileInputStream fis2 = new FileInputStream(pathInput2);
		FileOutputStream fos2 = new FileOutputStream(pathOutput2);
		// 3. 읽어오기
		int data;
		int i = 0;
		
//		while((data = fis.read())!=-1) { // -1 == EOF
//			// 4. 내보내기
//			fos.write(data);
//			i++;
//			System.out.println("파일 복사중... " + i);
//		}
		
		// 속도 향상을 위해 배열 사용
		byte[] arr = new byte[1024];
		while(fis2.read(arr)!=-1) { // -1 == EOF
			// 4. 내보내기
			fos2.write(arr);
			i++;
			System.out.println("파일 복사중... " + i);
		}
		
		System.out.println("복사 완료");
		// 5. flush, close
		fis.close();
		fos.flush();
		fos.close();
		fis2.close();
		fos2.flush();
		fos2.close();
	}

}



다음 예제는 File Reader와 File Writer을 이용한 예제이다.


Writer와 Reader는 문자형 특화로 txt 파일을 만들 때 자주 쓰인다.





package chap11.exam06.charIO;

import java.io.FileReader;

public class TextInput {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 지정
		String path = "C:/img/asd.txt";
		// 2. 스트림 준비
		FileReader fr = new FileReader(path);
		// 3. 읽어오기
		char[] arr = new char[100];
		
		while(fr.read(arr)!=-1) { // -1 == EOF
			String data = new String(arr);
			// 4. 출력하기
			System.out.println(data);
			// System.out.print(arr);
		}

		
		// 5. 자원닫기
		
	}

}


package chap11.exam06.charIO;

import java.io.FileWriter;

public class TextOutput {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 설정
		String fileName = "C:/img/temp/write.txt";
		// 2. 스트림 준비
		// append 인자 - 이어쓰기 여부
		FileWriter fw = new FileWriter(fileName, true);
		// 3. 쓰기
		System.out.println("저장 시작");
		fw.write("for 문 시작 \r\n");
		for(int i = 1; i <= 10; i++) {
			fw.write("for 문 증가 : " + i + "\r\n");
		}
		fw.write("for 문 종료\r\n");
		// 4. flush, close
		System.out.println("저장 종료");
		fw.flush();
		fw.close();
		
		// 실행을 두 번 해보면 데이터가 새로 쓰이지 않고 축적된다.
		// append를 true값으로 해서
	}

}


package chap11.exam07.test;

import java.io.FileWriter;
import java.util.Scanner;

public class WriteTest {

	public static void main(String[] args) throws Exception {
		// 1. 파일 위치 설정
		String fileName = "C:/img/temp/testkey.txt";
		
		// 2. 스캐너 준비
		Scanner scan = new Scanner(System.in);
		
		// 3. Writer 준비
		// append 인자 - 이어쓰기 여부
		FileWriter fw = new FileWriter(fileName, true);
		
		// 4. 읽어오기
		String temp = "";
		System.out.println("저장 시작");
		while(true) {
			System.out.println("추가할 문장을 입력하세요.(end test 입력 할 경우 끝남)");
			temp = scan.nextLine();
			if (temp.equals("end test")) { break; }
			else {
				// 5. 내보내기
				fw.write(temp);
				fw.write("\r\n");
				fw.flush();
			}
		}
		
		// 6. 자원 종료
		System.out.println("저장 종료");
		fw.close();
	}

}


InputStream과 OutputStream을 이용하여 콘솔에 간단한 입력과 출력을 해보자


package chap11.exam01.sysio;

import java.io.IOException;
import java.io.InputStream;

public class SysInput {

	public static void main(String[] args) {
		System.out.println("아무거나 입력 하세요!");

		InputStream in = System.in;

		try {
			char a = (char)in.read();
			System.out.println(a);
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}



package chap11.exam01.sysio;

import java.io.OutputStream;

public class SysOutput {

	public static void main(String[] args) throws Exception {
		OutputStream os = System.out;
		
		for(byte b = 97; b <= 122; b++) {
			os.write(b); // 바이너리를 내부적으로 처리 (파일 처리용)
//			System.out.println((char)b); // 외부로 출력
		}
		os.flush(); // flush가 없으면 출력이 안 될 수도 있다.
//		os.close(); // 여기서 close를 하면 밑에서 다시 객체화하여도 사용할 수 없음
		
		System.out.println();
		
		
		// 문자열을 배열로 만들어서 출력
		String hangul = "가나다라마바사아자차카타파하";
		
		byte[] aa = hangul.getBytes();
//		for(int i = 0; i < aa.length; i++) {
//			System.out.println((char)aa[i]);
//		}
		os.write(aa);
		// 단일 스레드에서 여러 개의 스트림을 다룰 수 없다.
		os.flush(); // flush가 없으면 출력이 안될 수도 있다.
		os.close();
	}

}


flush는 (변기의)물을 내리다 라는 의미를 가지고 있는데 


Stream을 flush하다 라고 이해하면 쉽다.


현재 메모리에 Stream이 그대로 흘러와 담겨있는데


flush하여 데이터를 뿌려준다고 할 수 있다.


그리고 Stream을 close 한다면 메모리를 반환하게 된다.


close를 한번 한 스트림은 다시 객체화 하여도 사용 할 수 없게 된다.










Console 클래스는 여태껏 사용해온 콘솔창의 입력 받은 문자를 쉽게 읽을 수 있는 클래스이고 cmd(Windows 커맨드 창)에서 실행된다.


Console은 정수, 실수 값을 바로 읽어 들이지 못하기 때문에 Scanner 클래스와 함께 이용하면 쉽게 사용가능하다.


여태껏 사용해온 Scanner scan = new Scanner(System.in);과 같다.




package chap11.exam02.console;

import java.io.Console;

public class Main {

	public static void main(String[] args) {
		// cmd에서만 실행 가능
		// 프로젝트 내 bin 폴더 > java [패키지이름.실행클래스] (경로)
		// 입력 순서 ex
		// d:
		// D:\JAVA\Chapter11\bin
		// java chap11.exam02.console.Main
		
		Console console = System.console();
		
		System.out.print("아이디 : ");
		String id = console.readLine();
		System.out.print("비번 : ");
		char[] pw = console.readPassword();
		String pass = new String(pw);
		System.out.println("---------------------");
		System.out.println("ID : " + id);
		System.out.println("PW : " + pass);
		
	}

}



콘솔 창으로 실행하는 방법은 https://qdgbjsdnb.tistory.com/79?category=715136 에서 확인하자





Scanner은 딱히 설명 할 것이 없으므로 예제만 올려보겠습니다.



package chap11.exam03.scanner;

import java.util.Scanner;

public class ScanMain {

	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		
		System.out.print("문자열 입력>");
		String inputStr = scan.nextLine();
		System.out.println(inputStr);
		
		System.out.print("정수 입력>");
		int inputInt = scan.nextInt();
		System.out.println(inputInt);
		
		System.out.print("실수 입력>");
		double inputDouble = scan.nextDouble();
		System.out.println(inputDouble);
		
	}

}




윈도우키(Ctrl, Alt 사이 윈도우 아이콘)를 누르고 cmd 입력


or


윈도우키 + R을 누르면 실행창이 나오는데 cmd 입력해서 실행



두가지 방법 중 마음에 드는 방법으로 실행 후 다음 사진과 같이 입력한다.







폴더 구조와 함께 이해해 보자.


콘솔창에 cd [Workspace 경로][프로젝트 이름 + \bin]을 입력해서 경로를 수정하고


java [패키지 명].[실행할 java 파일 이름(대소문자 구분)] 하고 입력하면 코드가 실행된다.






Thread 그룹은 프로젝트가 커질수록 그에 맞춰 쓰레드도 많아지게 된다.


몰론 스레드마다 이름을 지정해 관리 할 수 있지만 숫자가 많아지면 한계가 있다.


그러한 이유로 그룹으로 묶어 공통으로 설정하고 제어 할 수 있는 기능이 바로 Thread Group이다


기본적으로 모든 스레드는 Main Group으로 자동적으로 설정이 되고





- 스레드 그룹 이름 얻기 -


Threadgroup group = Thread.currentThread.getThreadGroup();


String groupName = group.getName();



스레드 그룹을 생성 시 부모 그룹과 묶을 수 있는데 만약 묶지 않는다면 현재 스레드의 하위 그룹과 묶인다.


- 스레드 그룹 생성 -


Threadgroup group = new ThreadGroup(String name);


Threadgroup group = new ThreadGroup(ThreadGroup parent, String name);




- 스레드에 생성 시 그룹 부여 방법 4가지 -


Thread th = new Thread(Thread group, Runnable target);


Thread th = new Thread(Thread group, String name);


Thread th = new Thread(Thread group, Runnable target, String name);


Thread th = new Thread(Thread group, Runnable target, String name, long stackSize);




그룹을 통해 묶은 스레드는 메소드로 한번에 통제 할 수 있다.


예를들어 interrupt()로 한꺼번에 정지 시키거나


setDaemon(boolean)으로 데몬 그룹으로 설정 할 수 있다.


다양한 메소드는 직접 Eclipse에서 ThreadGroup이름에서 '.'을 눌러 나오는 자동완성 메소드들을 통해서 확인해보면


익숙한 메소드가 많을 것이다.














'개념 및 코딩 > 08.Thread 스레드' 카테고리의 다른 글

[JAVA]08-07.Thread Pool, Pool Block  (0) 2018.09.06
[JAVA]08-05.Demon Thread  (0) 2018.09.06
[JAVA]08-04.Thread State, Control  (0) 2018.09.06
[JAVA]08-03.Priority, Synchronized  (0) 2018.09.06
[JAVA]08-02.Runnable, Thread  (0) 2018.09.04

Stream이란 사전적 의미로는 '줄줄 흐르다', 즉 흘러가는 흐름을 말하는데


Input Output도 마찬가지다.


데이터를 불러오는데 읽어들어오는 흐름과 내보내는 흐름이라고 이해하면 쉽다.




C:\temp\data.dat


위의 파일을 inputStream으로 불러온다고 하면


C:\temp\data.dat 이 경로에서 나의 작업 공간으로 데이터가 흘러온다.


현실에 비유한다면


산(C:\temp\data.dat)에서 강물이 도시(작업 공간)로 흘러 들어온다.


Output도 역으로 생각하면 된다.





이 흐름은 Byte, 바이트 형식으로 흘러오거나 Character, 문자 형식으로 흘러온다.


Byte형식은 InputStream, OutputStream이고


Character형식은 Reader와 Writer로 사용한다.





InputStream은 Byte 형식의 흐름의 최상위 객체로 FileInputStream, BufferedInputStream, DataInputStream 등


각각 용도에 특화된 InputStream의 자식 객체가 있다.


다음 게시글을 통해 다양한 스트림의 활용을 확인해보자




1.람다


람다식은 익명객체를 선언 할 경우 자주 쓰이는 형식을 간략화 시킨 식을 말한다.


자주 쓰이는 방법은 3가지의 방법이 있으며 람다를 사용하면 코드수가 줄어들고 오버라이드 할 메소드가 생략되어


좀 더 깔끔해진다.


다음 예제를 통해 일반 익명객체와 람다식을 비교해보자



package chap13.exam01.basic;

public class Main {
// 람다식은 다른 환경에서는 잘 쓰이지만 java에서는 잘 쓰이고 있지 않음
	public static void main(String[] args) {
		// 일반적인 익명객체 사용
		Runnable task1 = new Runnable() {
			
			@Override
			public void run() {
				for(int i = 1; i <= 3; i++) {
					System.out.println("익명 객체 스레드 실행");
				}
			}
		};
		// 1번 방법
		Thread th = new Thread(task1);
		th.start();
		
		// 2번 방법 (간소화)
		new Thread(task1).start();
		
		// 람다식 익명객체 사용 -> 강제하는 오버라이드 메소드 생략, 코드 수 절약
		// 강제 생략 할 메소드가 여러개면 안된다.
		// 3번 방법
		Runnable task2 =()->{
			for(int i = 1; i <= 3; i++) {
				System.out.println("람다식 객체 스레드 실행");
			}
		};
		
		new Thread(task2).start(); // 간소화
		
	}

}


람다식 인터페이스도 있는데 람다식의 특징으로 한 방식 정해진 방법을 강제하는 식이므로


메소드가 많으면 안되어 제한하는 코드를 따로 명시하며 메소드는 하나밖에 안쓰이기 때문에


왠만하면 잘 안쓰인다.


package chap13.exam02.lambdaInter;
@FunctionalInterface // 람다식 인터페이스를 만들 때 선언하여 1인터페이스 - 1메소드를 강제시킴
							   // 람다식을 사용 할 때에 메소드가 많으면 사용하기 애매하니 제한하는 코드
public interface LambdaInter {
	
	int operation(int a , int b); // 하나밖에 없기 때문에 사용 시 매개변수에 들어오는 값은
										 // 정해져 있어 자료형을 생략
	
//	void sayMsg(String msg);
}


람다식의 사용법은 3가지인데 객체화시


= new XXXX();


------------------------


= (a, b) -> { 내용 }


= a -> { 내용 }


= ( ) -> { 내용 }


과 같이 사용된다. 다음 예제를 확인해보자

package chap13.exam03.usage;

public class Main {

	public static void main(String[] args) {
		// 생략이 가능한 이유는 이미 인터페이스 내 메소드가 선언되어 있어서
		
		// 매개변수가 여러개 일 경우(매개변수 타입 생략 가능)
		OperationA lambdaA = (a, b)-> {
			int result = a + b;
			System.out.println("답은 : " + result);
		};
		lambdaA.plus(3, 4);
		
		// 매개변수가 하나일 경우(괄호와 타입이 생략)
		OperationB lambdaB = a ->{
			System.out.println("a의 제곱은 : " + (a * a));
		};
		lambdaB.square(50);
		
		// 매개변수가 없을 경우(생략 X) 기본 형태
		// 람다식 자체를 자주 사용하지 않기 때문에 일반적인 사용법인 이것정도만 기억하면 된다.
		OperationC lambdaC = () ->{
			System.out.println("매개변수 없이 실행");
		};
		lambdaC.process();
		
		//실험내용이 반환문 만 있을 경우(코드블록 생략, Return도 생략해주어야 함)
		OperationD lambdaD = msg -> msg;
		System.out.println(lambdaD.echo("반환만 한다."));

	}
	// 사용할 인터페이스 (내부 선언)
//	@FunctionalInterface // 써도 되고 안써도 된다.
	interface OperationA{ void plus(int a, int b); }
	interface OperationB{ void square(int a); }
	interface OperationC{ void process( ); }
	interface OperationD{ String echo(String msg); }
}


Thread Pool은 다수의 스레드 자원 할당을 좀 더 효율적으로 하는 작업이다.


Thread pool을 생성 할 때 스레드 수를 지정하고


Runnable 작업이나 Callable 작업을 받고 실행된다.


단순하게 thread.start를 한다면 os의 작업 스케줄링으로 작업이 진행되는데


pool은 작업 할 Thread 수를 미리 설정해서 스케줄링이 진행된다.


하지만 Thread 수를 잘 못 예측하여 높게 잡는다면 오히려 메모리 공간이 낭비된다.




Runnable 작업은 Return값이 없고


Callable 작업은 Return값이 있는게 특징이다.


생성된 작업은 submit(), execute()로 실행되고


execute는 반환값이 없고 예외가 발생하면 스레드가 제거되는 반면


submit은 Future타입을 반환하고 예외상황에도 스레드를 재사용한다.




Thread Pool은 main Thread가 종료되도 남아있기 때문에 상태를 확인하고 따로 종료를 해주어야 한다.


shutdown()은 현재 작업을 마무리 후 종료시키고 shutdownNow()는 interrupt를 일으켜 강제 종료시킨다.



package chap10.exam13.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPool {

	public static void main(String[] args) {
		// ThreadPool 생성 방법
		// 1개 이상의 스레드가 추가 되었을 때 60초 이상 놀고 있으면 제거
		// 작은 캐쉬 메모리 방에서 오래 붙들고 있으면 낭비가 심하기 때문에 제거
		ExecutorService pool = Executors.newCachedThreadPool();
		
		// 실행환경, 현재 이용할 수 있는 스레드, 프로세서 불러오는 방법
		// 현재 CPU에서 코어 갯수를 추출하는 방법.
		int n = Runtime.getRuntime().availableProcessors(); 
		System.out.println("가용 할 수 있는 스레드 갯수 : " + n);
		// 사용할 스레드, 프로세서 갯수를 지정하는법
		// 기본적으로 n개의 스레드를 유지
		pool = Executors.newFixedThreadPool(n);
	}

}







package chap10.exam14.exec;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExecuteMain {

	public static void main(String[] args) throws InterruptedException {
		// Runnable로 작업 요청
		// execute() : 용서 X, 반환 X // 혼자 실행
		// submit() : 용서 O, 반환 O // 전송에는 답변이 있어야 함
		
		// 스레드 순서가 중요
		// 1. 스레드 풀 생성
		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);
		// 2. 할 일 지정
		Runnable runnable = new Runnable() {
			
			@Override
			public void run() {
				System.out.println("Runnable 처리");
			}
		};
		// 3. 실행
		pool.execute(runnable); // 반환 값 X
		
		
		// 4. 스레드 풀 종료(close)
		// 처리중인 작업이 마무리 되면 닫는다.
		pool.shutdown();
		// 처리중인 작업에 관계없이 강제로 닫는다.
//		pool.shutdownNow();
		// shutdown()이 타임아웃안에 실행 되는지 반환
		// 제한 시간안에 작업 완료시 true를 반환
		// 만약 제한시간을 지날경우 강제로 닫음
//		boolean t = pool.awaitTermination(10L, TimeUnit.SECONDS);
		
		
	}

}


package chap10.exam14.exec;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Submit {

	public static void main(String[] args) throws Exception {
		// 1. 스레드 풀 생성
		ExecutorService pool = Executors.newCachedThreadPool();
		
		// 2. 할 일 만들기
		Callable<String> callable = new Callable<String>() {

			@Override
			public String call() throws Exception {
				System.out.println("Callable 처리");
				return "작업완료";
			}
		};
		// 3. 실행
		Future<String> submit = pool.submit(callable);
		System.out.println(submit.get());
		
		// 4. 스레드 풀 닫기
		pool.shutdown();
		
	}

}


package chap10.exam14.exec;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestPool {

	public static void main(String[] args) throws Exception {
		// Thread Pool 10번 출력하기

		// Runnable 사용, 순차실행
		// 1. 스레드 풀 생성
		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);
		// 2. 할 일 지정
		Runnable runnable = new Runnable() {

			@Override
			public void run() {
				for(int i = 0; i < 10; i++) {
					System.out.println("Thread Pool " + (i + 1) + "번");
				}
				System.out.println("----------------------------");
			}
		};
		// 3. 실행
		pool.execute(runnable); // 반환 값 X

		// 4. 스레드 풀 종료(close)
		pool.shutdown();





		// Callable 사용, 자기 마음대로 실행
		// 1. 스레드 풀 생성
		ExecutorService pool2 = Executors.newCachedThreadPool();

		// 2. 할 일 만들기
		Callable<String> callable = new Callable<String>() {

			@Override
			public String call() throws Exception {

				System.out.println("Thread Pool1");

				return "작업완료";
			}
		};

		// 3. 실행
		Future<String> submit = null;
		for(int i =0; i < 10; i++) {
			System.out.print(i + " : ");
			submit = pool2.submit(callable);
		}
		System.out.println(submit.get());

		// 4. 스레드 풀 닫기
		pool2.shutdown();

	}

}



Thread Pool은 runnable이나 callable에 관계없이 작업이 완료되면 Future객체를 반환한다.


그 Future 객체를 가져오는 get 메소드는 join과 같은 역할을 수행하고


이러한 작업을 Pool Blocking이라고 한다.



package chap10.exam15.block;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class NoResultMain {

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

		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);

		Runnable runnable = new Runnable() {

			@Override
			public void run() {
				for(int i = 0; i < 10; i++) {
					System.out.println("Thread Pool " + (i + 1) + "번");
				}
				System.out.println("----------------------------");
			}
		};
		
		Future future = pool.submit(runnable); // 반환할게 따로 없으므로 제너릭 지워줌
		// 반환값이 없는데 get() 사용 이유
		future.get(); // get() == join() / 이 아래 내용이 반환 후 실행
		System.out.println("작업 완료");
		pool.shutdown();
	}

}


package chap10.exam15.block;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ResultMain {

	public static void main(String[] args) throws Exception {
		// 1. 풀 생성
		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);
		// 2. 해야 할 일
		Callable<Integer> task = new Callable<Integer>() {

			@Override
			public Integer call() throws Exception {
				int sum = 0;
				for(int i = 1; i <= 100; i++) {
					sum += i;
				}
				return sum;
			}
		};
		// 3. 실행
		Future<Integer> future = pool.submit(task);
		int result = future.get();	
		System.out.println("합 : " + result);
		// 4. 풀 닫기
		pool.shutdown();
	}

}





1.데몬 스레드, Demon Thread


Main이 종료 될 때 직접 작성한 스레드들은 따로 끝내지 않으면 계속 돌아가게 되는데


Thread가 많아 질 수록 리소스를 많이 가져가고


꺼지지 않은 Thread가 만약 공유하는 변수값을 수정한다면 프로그램 자체가 꼬이게 된다.


그래서 스레드 start 전에 setDaemon(true) 설정을 하면


스레드를 실행한 메인 스레드가 종료된다면 강제로 같이 종료가 된다.


다음 예를 보자


package chap10.exam12.demon;

public class DemonThread extends Thread {

	@Override
	public void run() {		
		while(true) {
			System.out.println("Demon Thread Running...");
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
}
<


package chap10.exam12.demon;

public class MainThread {

	public static void main(String[] args) throws InterruptedException {
		Thread demon = new DemonThread();
		demon.setDaemon(true);//work -> demon
		demon.start();//데몬 스레드 시작
		Thread.sleep(3000);//3초후
		System.out.println("메인 스레드 종료");
	}

}


+ Recent posts