파일에 데이터를 저장하는 여러 가지 방법을 정리해보았어. 주로 CSV 형식으로 저장하거나, DataOutputStream을 사용하여 데이터를 저장하는 방식이 있어.
CSV 파일은 필드와 컬럼을 구분하여 데이터를 저장하는 방식이야. 데이터를 저장할 때는 필드 구분자와 줄바꿈 기호를 추가해야 해.
import java.io.FileWriter;
public class CSVWriter {
public static void main(String[] args) throws Exception {
String data1 = "홍길동,서울,hong@test.com,30";
String data2 = "길남,부산,kil@hong.com,25";
FileWriter out = new FileWriter("member.csv");
out.write(data1 + "\n"); // 줄바꿈 추가
out.write(data2);
out.close(); // close 하면 자동 저장
System.out.println("파일에 저장됨");
}
}
PrintWriter를 활용한 CSV 저장PrintWriter를 사용하면 println을 통해 줄 단위로 저장할 수 있어.
import java.io.FileOutputStream;
import java.io.PrintWriter;
public class CSVWriter2 {
public static void main(String[] args) throws Exception {
String data1 = "홍길동,서울,hong@test.com,30";
String data2 = "길남,부산,kil@hong.com,25";
FileOutputStream fos = new FileOutputStream("member.csv");
PrintWriter out = new PrintWriter(fos);
out.println(data1);
out.println(data2);
out.close();
System.out.println("파일에 저장됨");
}
}
CSV 방식의 장점과 단점
✅ 범용적이며 다양한 플랫폼에서 사용 가능
❌ 데이터의 순서를 알고 있어야 정확한 처리 가능
DataOutputStream 이용하기DataOutputStream을 사용하면 데이터를 타입에 따라 저장할 수 있어. 단순 텍스트 저장 방식과는 다르게 필드 구분자가 필요 없고, 데이터를 읽을 때 형변환할 필요도 없음
import java.io.DataOutputStream;
import java.io.FileOutputStream;
public class DataWriter {
public static void main(String[] args) {
try (FileOutputStream fos = new FileOutputStream("member.data");
DataOutputStream dos = new DataOutputStream(fos)) {
dos.writeUTF("홍길동");
dos.writeUTF("서울");
dos.writeUTF("hong@test.com");
dos.writeInt(30);
dos.writeUTF("길남");
dos.writeUTF("부산");
dos.writeUTF("kil@hong.com");
dos.writeInt(25);
System.out.println("File Saved");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
DataInputStream을 사용하여 저장된 데이터를 순서대로 읽어야 해.
import java.io.DataInputStream;
import java.io.FileInputStream;
public class DataReader {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("member.data");
DataInputStream dis = new DataInputStream(fis)) {
while (dis.available() > 0) { // 데이터가 남아있는 동안 읽기
System.out.println(dis.readUTF());
System.out.println(dis.readUTF());
System.out.println(dis.readUTF());
System.out.println(dis.readInt());
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
DataOutputStream 방식의 장점과 단점
✅ 필드 구분자를 추가할 필요 없음
✅ 데이터를 읽을 때 형변환이 필요 없음
❌ 저장한 순서대로 읽어야만 올바르게 데이터를 가져올 수 있음
❌ 자바에서만 사용 가능
| 바이트 스트림 | character Stream | |
|---|---|---|
| 소스 스트림 | InputStream(바이트 단위 입력스트림) | Reader(문자 단위 입력) |
| 싱크 스트림 | OutputStream(바이트 단위 출력스트림) | Writer(문자 단위 출력) |
| 바이트 스트림 | character Stream | |
|---|---|---|
| file(파일 대상) | FileInputStream | |
| FileOutputStream | FileReader | |
| FileWriter | ||
| Memory : Array(메모리상의 배열대상) | ByteArrayInputStream | |
| ByteArrayOutputStream | CharArrayReader | |
| CharArrayWriter | ||
| Memory : String(메모리상의 String) | - | StringReader |
| StringWriter | ||
| Pipe(파이프 대상) | PipedInputStream | |
| PipedOutputStream | PipedReader | |
| PipedWriter |
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ReadFileExample {
public static void main(String[] args) {
FileInputStream fis = null;
try {
//프로젝트 파일안에 in.txt가 있거나 만들어야함
fis = new FileInputStream("in.txt"); // 입력 스트림 객체 생성 , 파일을 읽어온다
byte[] buffer = new byte[256]; // 데이터를 저장할 버퍼, 배열을 생성해서 데이터를 저장한다.
int readCount;
while (((readCount = fis.read(buffer))) != -1) { // 파일 끝까지 읽기 , read()메서드를 활용해서 파일의 끝 (0-1)까지 읽어온다
// fis.read(buffer)는 fis라는 파일 입력스트림을 사용해서 읽어오고 이를 buffer에 저장한다. 그리고 fis.read(buffer))는 긁어온 바이트만큼 readCount에 저장한다.
String data = new String(buffer, 0, readCount);
// buffer는 파일에서 읽어온 바이트데이터가 저장된 배열
// 0 : 변환을 시작할 인덱스 즉 buffer의 처음(0번쨰 위치부터 변환한다)
// readCount : buffer에서 실제로 읽어온 데이터의 길이를 의미
// new String(바이트배열, 시작 인덱스, 변환할 길이);
System.out.print(data);
}
} catch (FileNotFoundException e) {
System.out.println("파일이 존재하지 않습니다.");
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
try {
if (fis != null) fis.close(); // 스트림 닫기
} catch (Exception e) {
System.out.println("파일 닫는 중 오류 발생");
}
}
}
}
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class NodeStreamExample {
public static void main(String[] args) {
FileReader input = null; // 입력 스트림 객체 선언 (텍스트 파일을 읽음)
FileWriter output = null; // 출력 스트림 객체 선언 (텍스트 파일을 씀)
try {
// 파일 경로 설정
String inFile = "in.txt"; // 입력할 파일
String outFile = "out.txt"; // 복사할 출력 파일
// 파일을 읽고 쓰기 위한 객체 생성
input = new FileReader(inFile); // 문자 단위 입력 스트림 생성
output = new FileWriter(outFile); // 문자 단위 출력 스트림 생성
// 버퍼 설정: 한번에 128개의 문자씩 읽고 저장
char[] buffer = new char[128];
int readCount; // 읽은 문자 수를 저장할 변수
// 파일 끝까지 반복해서 읽기
while ((readCount = input.read(buffer)) != -1) {
// FileReader의 read() 메서드는 파일에서 읽은 문자 수를 반환함
// 파일 끝에 도달하면 -1을 반환하여 반복문이 종료됨
output.write(buffer, 0, readCount); // 읽어온 데이터를 그대로 출력 파일에 저장
}
System.out.println("파일이 복사되었습니다."); // 파일 복사 완료 메시지 출력
} catch (IOException e) {
// 📌 입출력 예외 발생 시 처리
e.printStackTrace();
} finally {
// 📌 사용한 파일 스트림을 닫아 리소스를 해제
try {
if (input != null) input.close(); // 입력 스트림 닫기
if (output != null) output.close(); // 출력 스트림 닫기
} catch (IOException e) {
System.out.println("파일 닫는 중 오류 발생");
}
}
}
}
예로 FileReader클래스의 노드 스트림은 파일에서 텍스트를 읽을떄 한 문자씩 낮은 수준의 메서드(read())만 가지고 있지만 BufferReader클래스 등의 필터 스트림은 줄단위로 읽어 String으로 반환하는 고급 메서드 readLine()메서드를 포함하므로 좀더 편하게 작업이 가능하다
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FilterStreamExample {
public static void main(String[] args) {
// 파일 입출력 객체 선언
FileReader input = null;
FileWriter output = null;
BufferedReader bufInput = null;
BufferedWriter bufOutput = null;
try {
// 파일 경로 지정
String inFile = "in.txt"; // 입력 파일
String outFile = "out2.txt"; // 출력 파일
// 파일을 읽고 쓰기 위한 객체 생성
input = new FileReader(inFile); // FileReader: 텍스트 파일을 문자 단위로 읽음
output = new FileWriter(outFile); // FileWriter: 텍스트 파일을 문자 단위로 씀
// 버퍼를 활용한 입력 및 출력 객체 생성
bufInput = new BufferedReader(input); // BufferedReader: 더 빠른 텍스트 읽기를 지원
bufOutput = new BufferedWriter(output); // BufferedWriter: 더 빠른 텍스트 쓰기를 지원
// 한 줄씩 파일 읽기
String line;
line = bufInput.readLine(); // 첫 번째 줄 읽기
while (line != null) { // 파일 끝까지 반복
bufOutput.write(line, 0, line.length()); // 현재 줄을 출력 파일에 저장
bufOutput.newLine(); // 줄 바꿈 추가
line = bufInput.readLine(); // 다음 줄 읽기
}
// 파일 복사 완료 메시지 출력
System.out.println(inFile + " >> " + outFile);
} catch (IOException e) {
// 입출력 예외 발생 시 처리
e.printStackTrace();
} finally {
// 파일 닫기 (자원 해제)
try {
if (bufInput != null) bufInput.close();
if (bufOutput != null) bufOutput.close();
} catch (IOException e) {
System.out.println("파일 닫는 중 오류 발생");
}
}
}
}
File 클래스를 사용하면 더 다양한 기능을 활용할 수 있다.import java.io.File;
public class FileExample {
public static void main(String[] args) {
// 파일 객체 생성
File someFile = new File("C:/autoexec.bat"); // 특정 파일을 지정
File someDir = new File("C:/"); // 디렉토리를 지정
File otherFile = new File(someDir, "config.sys"); // 기존 디렉토리에서 파일 생성
// 파일 이름 출력
System.out.println("파일 이름: " + someFile.getName());
System.out.println("파일 경로: " + someFile.getPath());
}
}
| 메서드 | 설명 |
|---|---|
String getName() | 파일 이름 반환 |
String getPath() | 파일 경로 반환 |
String getAbsolutePath() | 파일의 절대 경로 반환 |
String getParent() | 파일이 속한 폴더 경로 반환 |
boolean renameTo(File newName) | 파일명을 변경 (변경 실패 시 false 반환) |
boolean delete() | 파일 삭제 (false 반환 시 삭제 실패) |
| 메서드 | 설명 |
|---|---|
long lastModified() | 마지막으로 수정된 날짜 반환 (long 타입) |
long length() | 파일 크기 반환 (바이트 단위) |
| 메서드 | 설명 |
|---|---|
boolean exists() | 파일 존재 여부 확인 (true/false) |
boolean canWrite() | 파일 쓰기 가능 여부 확인 (true/false) |
boolean canRead() | 파일 읽기 가능 여부 확인 (true/false) |
boolean isFile() | 파일인지 확인 (true: 파일, false: 폴더) |
File 클래스 활용)import java.io.File;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Date;
public class FileExplorerExample {
public static void main(String[] args) {
// 탐색할 디렉토리 지정
File myDir = new File("C:/"); // "C:/" 디렉토리 탐색
// 해당 디렉토리 내 파일 목록 가져오기
File[] listing = myDir.listFiles();
// 파일 목록 출력
for (int i = 0; i < listing.length; ++i) {
File file = listing[i]; // 배열에서 파일 또는 폴더 가져오기
System.out.print(file.getName() + "\t"); // 파일 또는 폴더 이름 출력
if (file.isFile()) { // 파일인지 검사
NumberFormat nf = NumberFormat.getInstance(); // 숫자 포맷 객체 (파일 크기 표시)
DateFormat df = DateFormat.getDateInstance(); // 날짜 포맷 객체 (수정 날짜 표시)
// 파일 크기(KB 단위) 출력
System.out.print(nf.format(file.length() / 1024) + " Kbyte\t");
// 마지막 수정 날짜 출력
System.out.print(df.format(new Date(file.lastModified())));
} else { // 폴더인 경우
System.out.print("폴더");
}
System.out.println(); // 개행
}
}
}
File 클래스를 활용하면 파일뿐만 아니라 폴더도 조작 가능하다.listFiles()를 사용하면 디렉토리 내 파일 목록을 가져올 수 있다.isFile()을 사용해 파일인지 폴더인지 구분할 수 있다.lastModified()와 length()를 활용해 파일 크기 및 수정 날짜 조회 가능하다.java.net.URL 클래스다.import java.net.URL;
public class URLExample {
public static void main(String[] args) {
try {
// URL 객체 생성 (웹 주소 지정)
URL url = new URL("http://www.example.com");
// URL의 주요 정보 출력
System.out.println("프로토콜: " + url.getProtocol()); // http
System.out.println("호스트: " + url.getHost()); // www.example.com
System.out.println("포트: " + url.getPort()); // -1 (지정되지 않음)
System.out.println("기본 포트: " + url.getDefaultPort()); // 80 (HTTP 기본 포트)
System.out.println("파일 경로: " + url.getFile()); // / (루트 경로)
} catch (Exception e) {
System.out.println("URL 형식 오류: " + e.getMessage());
}
}
}
| 메서드 | 설명 |
|---|---|
String getProtocol() | 프로토콜 반환 (예: http, https, ftp 등) |
String getHost() | URL의 호스트 이름 반환 |
int getPort() | URL에 지정된 포트 번호 반환 (없으면 -1) |
int getDefaultPort() | 프로토콜의 기본 포트 반환 (http → 80, https → 443) |
String getPath() | URL의 경로 반환 (예: /index.html) |
String getFile() | URL에서 지정된 파일명 반환 |
Object getContent() | URL의 컨텐츠 반환 |
URLConnection openConnection() | URL과 연결되는 객체 반환 |
InputStream openStream() | URL에서 데이터를 읽는 입력 스트림 객체 반환 |
URL 활용)import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
public class GetHTMLExample {
public static void main(String[] args) {
// 📌 가져올 웹 페이지 주소 설정
String urlStr = "http://www.javaspecialist.co.kr/";
String file = "html.txt"; // 저장할 파일 이름
byte[] inputString = new byte[1024]; // 읽어올 데이터를 저장할 배열
// 📌 URL 객체 생성
URL url = null;
try {
url = new URL(urlStr);
} catch (MalformedURLException e) {
System.out.println("URL 형식 오류");
return;
}
// 📌 URL에서 데이터를 가져올 입력 스트림 생성
InputStream is = null;
try {
is = url.openStream();
} catch (IOException e) {
System.out.println("URL을 열지 못했습니다.");
return;
}
// 📌 HTML을 저장할 파일 출력 스트림 생성
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
// 📌 데이터를 읽어 파일에 저장
while (is.read(inputString, 0, inputString.length) != -1) {
fos.write(inputString);
}
// 📌 URL 정보 출력
System.out.println("Path: " + url.getPath());
System.out.println("Protocol: " + url.getProtocol());
System.out.println("Port: " + url.getPort());
System.out.println("Default Port: " + url.getDefaultPort());
System.out.println("File: " + url.getFile());
System.out.println(file + " 파일을 확인하세요.");
} catch (IOException e) {
System.out.println("파일 저장 오류: " + e.getMessage());
} finally {
try {
if (fos != null) fos.close(); // 출력 스트림 닫기
} catch (IOException e) {
System.out.println("파일 닫는 중 오류 발생");
}
}
}
}
URL url = new URL(urlStr);
new URL(String urlStr) → 웹 주소를 URL 객체로 변환하여 활용할 수 있게 함.openStream())을 통해 HTML 가져오기InputStream is = url.openStream();
url.openStream()을 사용하면 웹 페이지 데이터를 InputStream을 통해 가져올 수 있음.FileOut^putStream을 활용해 가져온 데이터를 파일(html.txt)에 저장FileOutputStream fos = new FileOutputStream(file);
FileOutputStream을 사용.while (is.read(inputString, 0, inputString.length) != -1) {
fos.write(inputString);
}
is.read(buffer, 0, buffer.length) → 웹 페이지 데이터를 읽어 buffer 배열에 저장fos.write()를 이용하여 파일에 저장System.out.println("Path: " + url.getPath());
System.out.println("Protocol: " + url.getProtocol());
System.out.println("Port: " + url.getPort());
System.out.println("Default Port: " + url.getDefaultPort());
System.out.println("File: " + url.getFile());
finally 블록을 활용하여 파일을 안전하게 닫기try {
if (fos != null) fos.close();
} catch (IOException e) {
System.out.println("파일 닫는 중 오류 발생");
}
URL 클래스를 사용하면 웹 주소를 직접 참조하고 데이터를 가져올 수 있다.openStream()을 사용하면 웹 페이지의 HTML을 읽을 수 있다.FileOutputStream을 활용하면 가져온 데이터를 파일로 저장할 수 있다.getProtocol(), getHost(), getPort() 등을 활용해 URL 정보를 쉽게 확인할 수 있다.FileReader, FileWriter, FileInputStream, FileOutputStream)은 순차적으로 데이터를 읽고 쓸 수밖에 없지만, RandomAccessFile을 사용하면 파일의 특정 위치로 이동하여 데이터를 읽거나 쓸 수 있다.| 메서드 | 설명 |
|---|---|
long getFilePointer() | 현재 파일 포인터 위치를 반환 |
void seek(long position) | 원하는 위치로 파일 포인터 이동 |
long length() | 파일의 길이(크기)를 반환 |
void writeUTF(String s) | UTF-8 문자열을 파일에 기록 |
String readUTF() | UTF-8 문자열을 읽어 반환 |
RandomAccessFile 활용)import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Date;
public class LogWriteExample {
public static void main(String[] args) {
// 저장할 로그 파일명 및 모드 설정
String fileName = "runtime.log";
String mode = "rw"; // "rw" → 읽기와 쓰기가 가능하도록 설정
RandomAccessFile raf = null;
try {
// RandomAccessFile 객체 생성
raf = new RandomAccessFile(fileName, mode);
// 파일 포인터를 파일 끝으로 이동 (새로운 로그를 추가할 위치)
raf.seek(raf.length());
// 현재 날짜와 시간을 UTF 형식으로 저장
raf.writeUTF(new Date().toString() + "\n");
System.out.println(fileName + "에 현재 시간이 기록되었습니다.");
} catch (FileNotFoundException e) {
System.out.println("파일이 존재하지 않습니다.");
} catch (IOException e) {
System.out.println("파일 기록 중 오류 발생: " + e.getMessage());
} finally {
try {
if (raf != null) raf.close(); // 파일 스트림 닫기 (리소스 해제)
} catch (IOException e) {
System.out.println("파일 닫는 중 오류 발생");
}
}
}
}
RandomAccessFile 객체 생성raf = new RandomAccessFile(fileName, mode);
fileName → 저장할 파일 이름mode → "rw" 모드를 지정하여 읽기(r)와 쓰기(w)가 가능하도록 설정seek())raf.seek(raf.length());
writeUTF())raf.writeUTF(new Date().toString() + "\n");
new Date().toString() → 현재 날짜와 시간을 문자열로 변환writeUTF() → UTF 형식으로 파일에 문자열을 기록FileNotFoundException, IOException)catch (FileNotFoundException e) {
System.out.println("파일이 존재하지 않습니다.");
} catch (IOException e) {
System.out.println("파일 기록 중 오류 발생: " + e.getMessage());
}
FileNotFoundException 발생IOException 처리close())finally {
try {
if (raf != null) raf.close();
} catch (IOException e) {
System.out.println("파일 닫는 중 오류 발생");
}
}
close()를 호출하여 스트림을 닫아야 함import java.io.Serializable;
public class Customer implements Serializable {
private String name;
private char gender;
private String email;
private int birthYear;
// 생성자: 고객 정보를 설정
public Customer(String name, char gender, String email, int birthYear) {
this.name = name;
this.gender = gender;
this.email = email;
this.birthYear = birthYear;
}
// 객체의 내용을 문자열로 반환
@Override
//toString() 메서드는 객체가 문자열로 출력될떄 자동으로 실행되는 메서드
public String toString() {
return "Customer [name=" + name + ", gender=" + gender + ", email=" + email + ", birthYear=" + birthYear + "]";
}
}
Serializable 인터페이스를 구현하면 객체를 직렬화할 수 있음.import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class WriteCustomerExample {
public static void main(String[] args) {
// 직렬화할 객체 생성
Customer cust1 = new Customer("허현정", 'M', "heojk2@daum.net", 21);
Customer cust2 = new Customer("허현준", 'M', "loid@gmail.com", 20);
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
// 파일 출력 스트림 생성
fos = new FileOutputStream("customer.ser");
oos = new ObjectOutputStream(fos);
// 객체를 파일에 직렬화하여 저장
oos.writeObject(cust1);
oos.writeObject(cust2);
System.out.println("Customer 데이터가 저장되었습니다.");
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
try {
if (oos != null) oos.close();
} catch (IOException e) {
System.out.println("파일 닫는 중 오류 발생");
}
}
}
}
ObjectOutputStream을 사용하여 객체를 직렬화하고 파일에 저장.writeObject()를 호출하면 객체가 바이트 형태로 변환되어 저장됨.import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ReadCustomerExample {
public static void main(String[] args) {
FileInputStream fis = null; // 파일 입력 스트림 객체 선언
ObjectInputStream ois = null; // 객체 입력 스트림 객체 선언
try {
// 📌 직렬화된 파일을 읽을 FileInputStream 생성
fis = new FileInputStream("customer.ser");
// 📌 파일에서 객체를 읽을 ObjectInputStream 생성
ois = new ObjectInputStream(fis);
//ois는 직렬화된 파일을 읽은 것에서 객체를 읽는다.
// 📌 파일에서 객체를 읽어 Customer 객체로 변환
Customer cust1 = (Customer) ois.readObject();
Customer cust2 = (Customer) ois.readObject();
// 📌 읽어온 객체 출력
System.out.println(cust1.toString());
System.out.println(cust2.toString());
} catch (FileNotFoundException e) {
System.out.println("파일이 존재하지 않습니다.");
} catch (IOException e) {
System.out.println("파일을 읽는 중 오류 발생: " + e.getMessage());
} catch (ClassNotFoundException e) {
System.out.println("클래스를 찾을 수 없습니다: " + e.getMessage());
} finally {
// 📌 파일을 안전하게 닫기
try {
if (ois != null) ois.close();
} catch (IOException e) {
System.out.println("파일 닫는 중 오류 발생");
}
}
}
}
transient 키워드는 직렬화에서 특정 변수 제외할 때 사용하는 제한자이다.import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ReadAccountExample {
public static void main(String[] args) throws Exception {
// 파일 입력 스트림을 사용해 직렬화된 객체 읽기
FileInputStream fis = new FileInputStream("account.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
// 객체 복원 및 출력
Account a2 = (Account) ois.readObject();
System.out.println(a2);
ois.close();
}
}
ObjectInputStream을 사용하면 직렬화된 객체를 다시 읽어올 수 있음.transient 키워드로 직렬화에서 제외된 변수는 복원되지 않음.serialVersionUID 값을 유지하면 기존 직렬화된 데이터를 읽을 수 있다.import java.io.Serializable;
public class Account implements Serializable {
private static final long serialVersionUID = 5004258855763033943L;
String accountNo;
String userName;
int balance;
transient String password; // 직렬화 제외
String newField;
// 생성자: 계좌 정보 설정
public Account(String accountNo, String userName, int balance, String password) {
this.accountNo = accountNo;
this.userName = userName;
this.balance = balance;
this.password = password;
}
// 객체 정보를 문자열로 반환
@Override
public String toString() {
return accountNo + "[" + userName + ", " + password + "] : " + balance;
}
}
serialVersionUID를 설정하면 클래스 구조가 변경되더라도 직렬화된 데이터를 불러올 수 있음.transient 키워드가 적용된 변수는 객체 직렬화에서 제외됨.Serializable 인터페이스를 구현하면 객체를 직렬화할 수 있다.transient 키워드를 사용하면 직렬화 과정에서 특정 변수를 제외할 수 있다.serialVersionUID를 설정하면 클래스 변경 후에도 이전 직렬화 데이터를 불러올 수 있다.