코딩 기록들
[Java Programming] 12.2 NIO (Java1.8 이후 File I/O ) , 재귀호출 본문
IO / NIO 차이점
IO | NIO | |
입출력 방식 | 스트림 방식 | 채널 방식 |
버퍼 방식 | non-buffer | buffer (버퍼) |
비동기 방식 | 지원x | 지원 |
블로킹/넌블로킹 방식 | 블로킹 방식만 지원 | 블로킹/넌블로킹 모두 지원 |
스트림 vs 채널
- 스트림 기반(IO) : 데이터를 읽기 위해서 입력스트림 생성 & 출력위해서는 출력스트림 생성
- 채널기반(NIO) : 양방향 입력과 출력이 가능. (입출력을 위한 별도의 채널 만들필요 x)
버퍼 vs 넌버퍼
- 버퍼 사용 (IO) : 보조스트림인 BufferedInput(output)Stream을 연결해서 복수개의 바이트를 한번에 입력받고 출력함
- NIO : 기본적으로 버퍼를 사용해서 입출력 함 - 채널 : 버퍼에 저장된 데이터출력 & 입력된 데이터 버퍼에 저장
NIO의 파일 경로 정의
경로정의 (Path)
-> path 구현객체 얻기 위해서는 get() 메소드 호출
- get메소드의 매개값= 파일의 경로 -> 문자열, URI, 상위 & 하위 디렉토리로 지정할 수 있음
Path path = Paths.get(String first, String...more);
Path path = Paths.get(Uri uri)
아래 코드들은, 굉장히 많이 사용하므로 외워둘것 ~!
Java 1.8버전 이상에서 사용할 수 있는 파일 '읽기'
/**
* Java 1.8버전 이상에서 사용할 수 있는 파일 읽기 예제
* new IO (nio) 사용
* (이 코드 내용은 완전히 외울것)
*/
public class NewIOReadAndPrintExam {
public static void main(String[] args) {
File file = new File("C:\\Java Exam", "Java Exam.txt");
if (file.exists() && file.isFile()) {
List<String> fileLine = new ArrayList<>();
try {
// 파일을 처음부터 끝까지 모두 읽어와 List에 할당
// Path filePath = Paths.get("C:\\JavaExam", "Java Exam.txt");
// Charset utf8 = Charset.forName("UTF-8");
// fileLine.addAll(Files.readAllLines(filePath, utf8));
//위의 3줄을 toPath를 이용해서 아래 한줄로 적어준다
fileLine.addAll(Files.readAllLines(file.toPath(), Charset.forName("UTF-8")));
}
catch( IOException ioe ) {
//파일을 읽다가 예외가 발생했을때 예외 내용만 출력
System.out.println(ioe.getMessage());
}
//읽어온 파일을 모두 출력
for (String line : fileLine) {
System.out.println(line);
}
}
}
}
Java 1.8버전 이상에서 사용할 수 있는 파일 '쓰기'
public class NIOWriteExam {
public static void main(String[] args) {
//이어쓰기 위한 변수 추가
boolean append = true;
//파일이 있는지 물어보는것
File file = new File("C:\\outputs2", "java_output2.txt");
if( ! file.getParentFile().exists()) { //file.getParentFile() = C:\\java\\outputs
file.getParentFile().mkdirs(); // mkdirs : make directories
// mkdir => java만 만들어주고, mkdirs => java, outputs 둘다 만들어줌(이걸사용)
}
//이어서쓰는게 아니라면 파일명 새로 작성하기 위해 아래코드 작성
if( ! append ) {
int index = 2; //이어서 쓰지 않을것이라면.. 영역
while( file.exists()) {
file = new File(file.getParent(), // C:\\java\\outputs = file.getParent()
"java_outputs2 (" + (index++) + ").txt");
// index++ = 2, 해당 반복문 실행 후 그 다음 3이 됨
}
}
//파일에 쓸 내용
List<String> fileDesc = new ArrayList<>();
fileDesc.add("파일을 씁니다1");
fileDesc.add("파일을 씁니다2");
fileDesc.add("파일을 씁니다3");
//파일을 쓴다
try {
if( ! append) {
Files.write(file.toPath(), fileDesc, Charset.forName("UTF-8"));
}
else { //이어쓰기
Files.write(file.toPath(), fileDesc,
Charset.forName("UTF-8"), StandardOpenOption.APPEND);
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
System.out.println(file.getAbsolutePath());
}
}
- 파일에 내용 덧붙이기 : StandardOpenOption.APPEND
- 파일 및 폴더 삭제 : File 객체의 delete 인스턴스 메소드 호출- file.delete()
--> 하지만, 파일이 들어있는 폴더는 지워지지 않음 -> ' 재귀호출' 이용
메소드 재귀호출 예제
public class RecursiveCallExam {
public static File findFile(String fileName, File from) {
if(from == null) {
from = new File("C:\\"); //C부터 찾아라
}
if(from.exists()&&from.isDirectory()) {
File[] itemDir = from.listFiles();
//폴더 확인용 코드
System.out.println(from.getAbsolutePath());
if (itemDir != null) {
for(File item : itemDir) {
if(item.isDirectory()) { //순회하고있는게 폴더라면, findFile 해라
File result = findFile(fileName, item);
if ( result != null) { //****
return result;
}
//return findFile(fileName, item); // item 주면 그걸로 찾아라
}
else if (item.getName().equals(fileName)){ //내가 찾고자하는 파일이름과 같으면
System.out.println(item.getAbsolutePath());
return item;//같으면 반환시켜라
}
}
}
}
// 전달받은게 폴더가 아니고 파일이라면,
else if (from.getName().equals(fileName)) {
System.out.println(from.getAbsolutePath());
return from;
}
//다 뒤져봤는데 없다면, null 반환
return null;
}
/**
* dir아래에 있는 모든 항목들(하위폴더포함)을 출력한다.
* @param dir 탐색을 시작할 (폴더)경로
*/
public static void printAllItems(File dir) {
if(dir.exists()&&dir.isDirectory()) {
File[] itemDir = dir.listFiles();
//폴더 확인용 코드
System.out.println(dir.getAbsolutePath());
if (itemDir != null) {
for(File item : itemDir) {
if(item.isDirectory()) {
printAllItems(item);
}
else {
System.out.println(item.getAbsolutePath());
}
}
}
}
else if (dir.isFile()) {
System.out.println(dir.getAbsolutePath());
}
}
public static String join(String startStr, String joinStr) {
if(startStr.length() >= 100) {
return startStr; //join이 아닌 그냥 startStr을 반환시켜라
// length >100 일때 마지막 starStr값 = 128,
// -> main코드의 String resultString = join("A", "");로 가서 A 128개 출력됨
}
startStr += joinStr;
System.out.println(startStr);
//리턴할때 join을 호출하면 이거또한 '재귀호출'이다
// (자기(join)가 자기(join)를 호출했으므로)
return join(startStr, startStr); //자기가 자기를 호출해서 반환시킨다
}
public static void infinityCall(int value) {
if(value == 3) {
return;
}
System.out.println("infinityCall 호출됨." + value);
//재귀호출 (메소드가 자기자신을 다시 실행함)
//infinityCall 안에서 infinityCall 또 호출
infinityCall(++value);
}
public static void main(String[] args) {
// while 무한 반복
// while(true) {
// infinityCall(); //무한히 반복되며 호출되는 infinityCall
// }
// Exception in thread "main" java.lang.StackOverflowError
//재귀호출
//infinityCall(0);
// String resultString = join("A", "");
// System.out.println(resultString);
printAllItems( new File("C:\\Program Files (x86)"));
File file = findFile("goods.csv", null); //findFile(String fileName, File from)
System.out.println(file.getAbsolutePath());
}
}
IO / NIO 선택 방법
1. IO
- 대용량 데이터를 처리할 경우 (IO는 받은즉시 처리하므로)
- 연결 클라이언트 수가 적고, 전송되는 데이터가 대용량 + 순차적으로 처리될 필요가 있을 경우
2. NIO
- 연결 클라이언트 수가 많고, 하나의 입출력 처리작업이 오래걸리지 않을 경우
--> 불특정다수의 클라이언트, 멀티파일들을 넌블로킹이나 비동기로 처리할수 있기때문에 과도한 스레드생성을 피하고, 스레드를 효과적으로 재사용하는 장점 & 운영체제 버퍼를 이용한 입출력이 가능한 장점(다이렉트 버퍼)
'Java' 카테고리의 다른 글
[Java Programming] 14.1 LocalDateTime (2) | 2024.02.05 |
---|---|
[Java Programming] 13.1 Enum (0) | 2024.02.02 |
[Java Programming] 13.2 Calendar (0) | 2024.02.02 |
[Java Programming] 12.1 File I/O (0) | 2024.02.01 |
[Java Programming] 11.2 컬렉션 (ListArray, Map, HashMap, set) (1) | 2024.02.01 |