package chap15;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class FileEx3 { // 필드 2개, 메서드 2개
static int totalFiles; // 클래스변수 (클래스를 호출하는 시점에 초기화됨 생성. / 프로그램 종료될 때 죽음.)
static int totalDirs;
public static void printFileList(File dir) {
System.out.println(dir.getAbsolutePath() + " 디렉토리");
File[] files = dir.listFiles();
ArrayList<String> subDir = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd kk:mm");
for(int i = 0 ; i < files.length ; i++) {
String fileName = files[i].getName();
String attr="";
String size = "";
if(files[i].isDirectory()) {
subDir.add(i+"");
attr = "DIR";
}
else {
size = files[i].length() + "";
attr += files[i].canRead() ? "R" : " ";
attr += files[i].canWrite() ? "W" : " ";
attr += files[i].isHidden() ? "H" : " ";
}
System.out.println(
String.format("%s %3s %6s %s", sdf.format(new Date(files[i].lastModified())), attr, size, fileName)
);
}
int dirNum = subDir.size();
int fileNum = files.length - dirNum;
totalDirs += dirNum; // 값을 계속해서 유지함.(불변이 아니라)
totalFiles += fileNum;
System.out.println(fileNum + "개의 파일, " + dirNum + "개의 디렉토리");
System.out.println();
// 내가 나를 호출 rescursive call
// call stack
for(int i = 0 ; i < subDir.size() ; i++) {
int idx = Integer.parseInt(subDir.get(i));
printFileList(files[idx]);
}
}
public static void main(String[] args) {
String dirName = "C:\\Program Files\\Java\\jdk1.8.0_271";
File dir = new File(dirName);
if(!dir.exists() || !dir.isDirectory()) {
System.out.println("유효하지 않은 디렉토리");
System.exit(0);
}
printFileList(dir);
System.out.println("총 " + totalFiles + "개의 파일");
System.out.println("총 " + totalDirs + "개의 디렉토리");
}
}
- 파일 목록을 출력하고, 디렉토리인 경우 ArrayList에 담았다가 각 디렉토리에 대해 pringFileList(File dir)를 재귀호출함.
package chap15;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class TmpFileCreator {
public static void main(String[] args) throws Exception {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("tmp.dat"));
for(int i = 0 ; i < 4_881_080 ; i++) { // 횟수 = 바이트숫자
bos.write(0);
}
bos.close();
}
}
- 크기가 4,881,080 바이트인 .dat 파일 생성.
package chap15;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileSplit {
public static void main(String[] args) throws IOException {
int unit = 1000;
final int VOLUMN = unit * 1000; // 백만바이트
String fileName = "tmp.dat";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName));
BufferedOutputStream bos = null;
int data = 0;
int i = 0;
int number = 0;
while ((data = bis.read()) != -1) { // 1바이트씩 읽은 걸 data에 담음. -1이 아닐때까지
if (i % VOLUMN == 0) {
if (i != 0) {
bos.close();
}
bos = new BufferedOutputStream(new FileOutputStream(fileName + "_." + ++number));
}
bos.write(data);
i++;
}
bis.close();
bos.close();
}
}
- 위에서 만든 tmp.dat 파일을 unit * 1000 바이트 크기인 파일로 쪼갬. tmp.dat_.5는 881,080 바이트. tmp.dat는 0 바이트.
package chap15;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class FileMerge {
public static void main(String[] args) throws Exception {
String mergeFilename = "tmp.dat";
File tmpFile = File.createTempFile("~mergetmp", ".tmp");
tmpFile.deleteOnExit();
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tmpFile));
BufferedInputStream bis = null;
int number = 1;
File f = new File(mergeFilename + "_." + number);
while (f.exists()) {
f.setReadOnly();
bis = new BufferedInputStream(new FileInputStream(f));
int data = 0;
while ((data = bis.read()) != -1) {
bos.write(data);
}
bis.close();
f = new File(mergeFilename + "_." + ++number);
}
bos.close();
File oldFile = new File(mergeFilename);
if (oldFile.exists()) {
oldFile.delete();
}
tmpFile.renameTo(oldFile);
}
}
- 위에 나눈 파일을 다시 합침.
- 작업할 임시파일을 새로 만들고 프로그램 종료시 자동 삭제 되도록 함.
- 합치는 작업이 온전히 끝나면, 기존 파일을 학제하고 임시파일의 이름을 기존 파일의 이름으로 변경.
- 0 바이트였던 tmp.dat가 다시 4,881,080 바이트가 됨.
7. 직렬화(Serialization) ★★
7.1 직렬화란?
- 객체를 컴퓨터에 저장했다가 다음에 다시 꺼내 쓰거나, 네드웤을 통해 컴퓨터 간에 서로 객체를 주고받을 수 있게 해 줌.
- 객체를 데이터 스트림으로 만드는 것. 즉, 객체에 저장된 데이터를 스트림에 쓰기(write) 위해 연속적인(serial) 데이터로 변환하는 것.
- 반대로 스트림으로부터 데이터를 읽어서 객체를 만드는 것은 역직렬화.
- 객체는 클래스에 정의된 인스턴스변수의 집합. (클래스변수나 메서드가 객체에 포함되지 않음. > 직렬화 제외)
7.2 ObjectInputStream, ObjectOutputStream
7.3 직렬화가 가능한 클래스 만들기 - Serializable, transient
- 직렬화하고자 하는 클래스가 java.io.Serializable인터페이스를 구현하도록 하면 됨.
- 직렬화하고자 하는 객체의 클래스에 직렬화가 안 되는 객체에 대한 참조를 포함하고 있다면, 제어자 transient를 붙여서 직렬화 대상에서 제외되도록 할 수 있음.
package chap15.serial;
import java.io.Serializable;
public class UserInfo implements Serializable {
/**
* 직렬화시 포함된 필드 (문서주석화)
* name, password, age
*/
private static final long serialVersionUID = 1348362370342145473L;
String name;
String password;
int age;
public UserInfo() {
this("Unknown", "1111", 0);
}
public UserInfo(String name, String password, int age) {
this.name = name;
this.password = password;
this.age = age;
}
@Override
public String toString() {
return "UserInfo [name=" + name + ", password=" + password + ", age=" + age + "]";
}
}
- 아래 예시들을 하기 위한 UserInfo클래스의 소스.
package chap15.serial;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class SerialEx1 {
public static void main(String[] args) throws Exception {
String fileName = "UserInfo.ser";
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
UserInfo u1 = new UserInfo("javaman", "1234", 30);
UserInfo u2 = new UserInfo("javawoman", "4321", 20);
ArrayList<UserInfo> list = new ArrayList<>();
list.add(u1);
list.add(u2);
// 객체를 직렬화 함.
oos.writeObject(u1);
oos.writeObject(u2);
oos.writeObject(list);
oos.close();
System.out.println("직렬화가 잘 끝났습니다.");
}
}
- 생성한 객체를 직렬화하여 UserInfo.ser에 저장.
- ArrayList와 같은 객체를 직렬화하면 ArrayList에 저장된 모든 객체들과 각 객체의 인스턴스변수가 참조하고 있는 객체들까지 모두 직렬화 됨.
package chap15.serial;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;
public class SerialEx2 {
public static void main(String[] args) throws Exception {
String fileName = "UserInfo.ser";
ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(fileName)));
UserInfo u1 = (UserInfo)ois.readObject();
UserInfo u2 = (UserInfo)ois.readObject();
ArrayList list = (ArrayList)ois.readObject();
System.out.println(u1);
System.out.println(u2);
System.out.println(list);
ois.close();
}
}
- 직렬화한 객체를 역직렬화함. (OujectInputStream의 readObject() 사용)
- readObject()의 리턴타입이 Object이므로 원래의 타입으로 형변환을 해줘야 함.
- 객체를 역직렬화할 때는 직렬화할 때의 순서와 일치해야 함. (u1, u2, list의 순서로 직렬화 했기 때문에 u1, u2, list의 순서로 역직렬화!)
-> 직렬화할 객체가 많을 때는 ArrayList와 같은 컬렉션에 저장해서 ArrayList 하나만 역직렬화함. (객체의 순서를 고려하지 않아도 되기 때문)
7.4 직렬화가능한 클래스의 버전관리
- 직렬화된 객체를 역직렬화할 때는 직렬화 했을 때와 같은 클래스를 사용해야 함.
- serialVersionUID
'JAVA' 카테고리의 다른 글
06장- 객체지향 프로그래밍 (생성자) 21. 02. 18. (0) | 2021.02.18 |
---|---|
16장- 네트워킹 21. 02. 16. (0) | 2021.02.17 |
15장- 입출력 I/O 21. 02. 15. (0) | 2021.02.15 |
11장- 컬렉션 프레임웍 21 . 02. 08. (0) | 2021.02.09 |
04장- 조건문과 반복문 21. 02. 03. (0) | 2021.02.04 |