데이터를 저장하는 공간에서 움직이는 연습이 필요하다.
컴퓨터의 RAM 내 데이터는 휘발성이 있어서 별도의 저장소(HDD나 SDD)가 필요한데, 저장소에 있는 데이터를 RAM에 저장하는 것을 Input / RAM의 데이터를 저장소로 옮기는 것을 Output이라고 한다. (RAM 기준)
- Input은 JAVA Scanner를 통해 System.in
- Output은 System.out
장치와 입출력을 위해서는 하드웨어장치에 직접 접근이 필요한데 다양한 매체에 존재하는 데이터들을 사용하기 위해 공통적인 방법으로 스트림을 이용한다.
1. 스트림(Stream) 클래스
입출력 장치에서 데이터 IO를 위해 자바에서 제공하는 클래스 (최상위 추상 클래스들을 상속받은 하위 클래스들을 사용함)
단방향이며 각 장치마다 연결가능한 별도의 스트림이 존재하다. 즉, 동시에 입출력을 수행하려면 각 2개의 스트림이 필요하다. (InputStream, OutputStram)
스트림을 사용한 뒤에는 반드시 close()를 통해 종료를 해줘야한다.(에러가 생기더라도) → try-catch-source 구문을 이용함이 편리하다.
바이트단위 처리와 문자단위 처리는 별도로 구별되어 있다.
각 최상위 클래스를 먼저 null로 초기화한 후에 스트림 객체를 사용하면 된다.
수업에서는 File 클래스에 맞춰서 사용되었으나, Byte, StringBuffered 등 다양한 종류가 Stream명앞에 붙을 수 있다.
1) 바이트 단위
바이트는 자바에서 기본적으로 int 처리를 하기 때문에 반환형은 int이다.
문자열은 용량이 달라서 오버플로우가 발생할 수 있기 때문에 사용치 않고, 이미지나 동영상 등을 보낼 때 사용하는 편이다.
(1) 불러오기 : InputStream(최상위)
- 객체 생성시 지정된 파일이 실존하지 않으면 에러가 발생함 → FileNotFoundException 예외처리 필수
- read()를 통해 1바이트 또는 바이트 배열의 값들을 읽고 return;
- int형으로 가져오고 반환값이 -1이 되는 순간 완료된 것임
- 지속적으로 read를 호출해야 알 수 있기 때문에 read 값이 -1이 되면 종료하는 반복문을 사용함
- int형으로 가져오고 반환값이 -1이 되는 순간 완료된 것임
- 자원반납 후 close()로 종료
- FileInputStream: 파일에서 바이트 단위로 읽을 때 사용(모든 종류 파일 가능)
FileInputStream fis = null;
try {
fis = new FileInputStream("test.txt");
//데이터를 가져올 때는 read 메소드 호출
//int형으로 가져오고, 반환값이 -1이 되는 순간 파일 내용을 다 가져온 것임.
int data = 0; //반복문 밖에 미리 선언
while ((data = fis.read()) != -1) { //결과가 -1이 되면 종료
System.out.println(data);
}
System.out.println("파일 내용을 모두 읽어옴");
} catch (IOException e) {
e.printStackTrace();
} finally { //닫아주는거 까먹지 말기
try {
if( fis != null) fis.close();
} catch(IOException e) {
e.printStackTrace();
}
}
(2) 입력하기 : OutputStream(최상위)
- byte 쓰기
- 파일이 없어도 새로 생성을 해버림 → 에러 미발생
- write()를 통해 입력을 버퍼에 저장
- flush()를 통해 버퍼에 있는 모든 바이트 출력
- close()로 종료
- 까먹지 않도록 도와주는 코드구조가 이미 있음
: try ~ with ~ resource 구조- try 조건문 부분에 반납할 자원을 생성하면 됨
- 까먹지 않도록 도와주는 코드구조가 이미 있음
- 활용예시
FileOutputStream fos = null; //조상 클래스를 먼저 선언
//기본적으로 자바에서 throws가 명시되어있기 때문에 예외처리 구문을 사용해줘야
//에러가 사라진다.
//예외처리
try {
//객체 연결과 메소드를 호출해와야함
// fos = new FileOutputStream(new File("test.txt"));
fos = new FileOutputStream(new File("test2"));
//byte 기반 통신이고 메모장 에디터가 자체적으로 숫자를 해석해서 문자로 표출해서 ABC가 적혀있
fos.write(65);
fos.write(66);
fos.write(67);
//오버플로우 발생으로 임의값이 출력됨
String name = "유병승";
fos.write(name.charAt(0));
fos.write(name.charAt(1));
fos.write(name.charAt(2));
name = "banana";
for(int i = 0; i < name.length(); i++) {
fos.write(name.charAt(i));
}
System.out.println("저장완료");
//해당 파일에 위 바이트 단위가 저장되는 것
//저장 이후에는 반드시 스트림 객체를 반환해줘야 한다.
} catch(FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e ) {
e.printStackTrace();
} finally {
try {
if(fos != null) {fos.close();} //throws 되어 있으므로 한번더 예외처리 진행
} catch(IOException e) {
e.printStackTrace();
}
}
2) 문자 단위
(1) In : Reader(최상위)
- read()를 통해 한개 또는 문자 배열의 문자값들을 읽고 return;
- 자원반납 후 close()
- FileReader: 텍스트파일에서 문자 읽을 때 사용 가능
public void readFile() {
try(FileReader fr = new FileReader("strdata.bs");) {
int data = 0;
StringBuffer sb = new StringBuffer(); //계속 누적출력해야되서 버퍼사용
while ((data = fr.read()) != -1) {
// System.out.println((char)data);
//문자열 기반이기 때문에 숫자로 나오더라도 바이트형과 결과가 다름
//char로 변환 안해주면 2바이트를 기반으로 한글문자열을 의미하는 숫자가 나옴
sb.append((char)(data)); //문자열 마지막에 매개변수값을 추가(누적)
}
System.out.println(sb);
} catch(IOException e) {
e.printStackTrace();
}
}
(2) OUT: Writter(최상위)
- FileWriter: 텍스트 파일에서 문자 단위 저장할 때 사용
- FileWriter를 BufferedWriter와 함께 사용하면 버퍼를 사용할 수 있기 때문에 더 효율적이므로 같이 써주기!
- public void fileSave(String file, String s) { try(FileWriter fw = new FileWriter("./" + file); BufferedWriter bw = new BufferedWriter(fw);) { fw.write(s); } catch(IOException e) { e.printStackTrace(); } }
write()를 통해 매개값이 주어진 한 문자 또는 문자 배열의 모든 문자를 버퍼로 보냄
flush()를 통해 버퍼에 있는 모든 문자열을 출력
close()를 통해 시스템 자원 반납후 출력 스트림을 닫음
public void writeFile() {
//문자열을 파일에 저장할 때 사용: Writer 추상클래스 이용 -> 구현체 이용
//FileWriter class를 생성해서 이용, 매개변수 있는 생성자 이용해서 생성
FileWriter writer = null;
try {
writer = new FileWriter("strdata.bs");
writer.write("유병승");
writer.write("점심먹고 졸릴시간이에요 졸면안돼요");
writer.write(19); //출력안됨
String name= "졸지마";
for(int i = 0; i < name.length(); i++) {
writer.write(name.charAt(i));
}
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
if(writer != null) writer.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
3. File 클래스
파일 시스템의 파일을 표현하는 클래스로 파일 크기, 속성, 이름 등의 정보와 파일 생성 및 삭제 기능 제공
- 매개변수 있는 생성자로 사용, 클래스이므로 new연산자 사용 가능
- 매개변수 : [파일경로+]파일명
File file = new File("파일경로");
- 만들 예정이라면 아직 실존하지 않는 파일이어도 괜찮다. 다만, 아직 생성되지 않아 없는 경로면 에러가 뜸 → 폴더 생성자로 만든뒤 실행
- 디폴트 경로 = 프로젝트 경로
- 절대 경로: root부터 최종 파일 경로까지 쭉 다 적는 경로
- 윈도우의 경우 보통 c:\\등 저장소 명으로 시작
- 맥은 바로 \로 시작됨 → /Users/pjh/coding/JavaStudy/13_파일입출력
- 상대 경로: 코드를 실행하는 경로를 기반으로 설정된 경로
- . : 현재 위치
- .. : 1개 상위 폴더
- 예) ../ ../test.txt : 2개 상위 폴더에 txt 파일 생성됨
1) 파일 / 디렉토리 생성 및 삭제 메소드
반환타입 | 메소드명 | 설명 |
boolean | createNewFile(”파일경로”) | 해당 경로에 새로운 파일 생성, 입력없을시 디폴트인 프로젝트 경로에 생성 |
mkdir() | 새로운 폴더 생성 | |
mkdirs() | 새로운 폴더들 생성 | |
delete() | 파일 또는 폴더 삭제 |
2) 파일 / 디렉토리 정보 리턴 메소드
리턴타입 | 메소드명 | 설명 |
boolean | canExcute() | 실행가능한지 |
canRead() | 읽을 수 있는지 | |
canWrite() | 수정 및 저장 가능한지 | |
isDirectory() | 폴더인지 | |
isHidden() | 숨김 파일인지 확인 | |
String | getName() | 파일이름 |
getParent() | 부모 경로 리턴 | |
getPath() | 전체 경로 리턴 | |
File | getParentFile() | 부모 디렉토리를 객체로 리턴 |
long | lastModified() | 마지막 수정 날짜 및 시간 리턴 |
length() | 파일 크기 리턴 | |
String[] | list(), lists() | 폴더에 포함된 파일목록을 String 배열로 리턴 |
list(필터명 필터), lists(필터명 필터) | 필터에 맞는 배열만 리턴 | |
File[] | listFiles() | 디렉토리에 포함된 파일 및 서브 디렉토리 목록 전부 File 배열로 리턴 |
listFile(필터명 필터) | 혹은 필터에 맞는 것만 File 배열로 리턴 |
3) 파일 생성 :: .createNewFile(”파일경로/파일명.확장자”)
- file 클래스 메소드를 이용해서 매개변수 파일을 생성함
- 이미 존재하는 파일이라면 false가 리턴됨
- 덮어쓰기 처리가 되지 않기 때문
- 확장자가 없어도 상관은 없다(파일을 오픈할 프로그램이 명시되지 않은 것 뿐임) 다만, 없는 경로면 에러 발생
- 파일이 없을 수도 있기 때문에 예외처리 해줘야한다.
- try-catch 사용한 예
try {
boolean result = f.createNewFile();
System.out.println(result? "생성 성공" : "생성 실패");
} catch(IOException e) {
e.printStackTrace(); //에러 메세지 출력하기
}
이하 생략
- 생성된 파일은 자바 익스플로러에는 자동으로 추가되진 않으나 파일 경로에 실제로 생성됨
- 프로젝트 F5 새로고침을 하면 익스플로러에도 추가됨 (내용이 없는 빈 파일)
4) 폴더 생성 :: .mkdir() / .mkdirs()
- 폴더 하나
File dir = new File("./test");
dir.mkdir();
- 폴더 여러개 생성
File dir = new File("./test/test1/test2");
dir.mkdirs();
5) 파일 및 폴더 삭제 :: delete()
폴더의 경우 빈 폴더여야 삭제 가능 → 파일이 있으면 false
휴지통에도 안생겨서 복구 불가하므로 주의
6) 정보 확인 :: 그외
- getAbsolutePath() : 파일 절대경로 출력
- 파일명 제외한 경로 출력하려면 substring으로 잘라내면 된다
System.out.println(f.getAbsolutePath().substring(0,f.getAbsolutePath().indexOf(".\\\\")
- 그외 메소드 설명 자세히보기
- getName(): 파일명
- getParent() : 파일의 부모파일 String으로 확인하기
- 폴더는 . 출력
- getParentFile(): 부모를 file class로 가져오기
- getPath() : 파일 상대경로 출력
- 파일인지 폴더인지 확인: isDirectory(), isFile()
- 파일크기 확인: length()
- 숨김여부: isHidden()
- 읽기 가능인지: canRead()
- 쓰기 가능인지: canWrite()
- 실행 가능인지: canExecute()
- 수정날짜: lastModified()
- 파일이나 폴더가 존재하는지: exists()
- 특정 파일이 있는지: 특정파일.exists()
- 파일이나 폴더가 존재하는지: exists()
- 특정 파일이 있는지: 특정파일.exists()
- list(), listFiles() : 저장된 파일들을 가져옴
4. 보조 스트림
기반 스트림이 먼저 생성된 후 선언 가능
//주 스트림
FileInputStream fis = new FileInputStream("animals.bs");
//보조 스트림: FileInputStream 이 없으면 생성 불가
InputStreamReader isr = new InputStreamReader(fis);
//데이터 읽어오기
isr.read();
스트림의 기능 향상 및 새로운 기능 추가를 위해 사용됨 → 실제 데이터를 주고 받지 않음
- 입출력은 X
@ 네트워크 통신으로 데이터를 가져오기
: 바이트 기반으로 스트림처리가 되어서 파일이 깨지게됨
-> 문자열로 처리를거쳐줘야함
//메인스트림
InputStream input = null;
//보조스트림
InputStreamReader isr = null;
try {
//아래 주소로 코드를 보냄
URL url = new URL("<https://naver.com>");
//추상화 객체라서 임포트, 연결 후 가져오기
HttpURLConnection connect = (HttpURLConnection) url.openConnection();
input = connect.getInputStream();
//문자열 형태로 전환해주는 보조스트림
isr = new InputStreamReader(input);
int data = 0;
StringBuffer sb = new StringBuffer();
while((data = isr.read()) != -1) {
sb.append((char)data);
}
System.out.println(sb);
//출력시 홈페이지 html코드가 가져와짐(한글깨짐) 바이트 단위로 가져와서
} catch(IOException e) {
e.printStackTrace();
} //편의상 close() 생략
1) 문자변환 보조 스트림
바이트 기반 스트림 이용시 데이터가 문자일 때 사용(문자 깨질 때)
- 네트워크 통신 등으로 데이터를 가져올 때 바이트로 스트림이 진행되게 되어있음
Reader, Writer가 문자단위로 입출력 하므로 바이트 기반 스트림보다 편리하게 사용가능
InputStreamReader
바이트 → InputStream → InputStreamReader → 문자
OutputStreamWriter
문자 → OutputStreamWriter
InputStream input = null;
//보조스트림 선언
InputStreamReader isr = null;
try {
//아래 주소로 코드를 보냄
URL url = new URL("<https://naver.com>");
//추상화 객체라서 임포트, 연결 후 가져오기
HttpURLConnection connect = (HttpURLConnection) url.openConnection();
input = connect.getInputStream();
//문자열 형태로 전환해주는 보조스트림
isr = new InputStreamReader(input);
int data = 0;
StringBuffer sb = new StringBuffer();
while((data = isr.read()) != -1) {
sb.append((char)data);
}
System.out.println(sb);
//출력시 홈페이지 html코드가 가져와짐(한글깨짐) 바이트 단위로 가져와서
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
input.close();
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2) 성능향상 보조 스트림
입출력 성능에 영향을 미치는 입출력 소스를 이용할 때 사용
입출력소스와 직접 작업하지 않고 버퍼에 데이터를 모았다가 한꺼번에 작업하는 방식으로 실행 성능을 향상한 것임(입출력 횟수가 적어서 속도 저하 방지)
BufferdInputStream/Reader
BufferdOutputStream/Writer
//성능향상 보조스트림
public void bufferedSave() {
try(FileOutputStream fos = new FileOutputStream("testfile");
BufferedOutputStream bos = new BufferedOutputStream(fos);){
String msg = "오늘은 금요일 아이유 만나는 날!";
bos.write(msg.getBytes());
} catch(IOException e) {
e.printStackTrace();
}
}
public void bufferedLoad() {
try(FileInputStream ifs = new FileInputStream("testfile");
BufferedInputStream bis = new BufferedInputStream(ifs)){
InputStreamReader isr = new InputStreamReader(bis);
int data = 0;
while((data = bis.read()) != -1) {
System.out.println((char)data);
}
} catch(IOException e) {
e.printStackTrace();
}
}
3) 기본 타입 입출력 보조 스트림
DataOutputStream / DataInputStream
- 기본 자료형 별 데이터 읽고 쓰기가 가능하도록 기능 제공
- 단, 입력된 자료형의 순서와 출력될 자료형의 순서 일치
- String 형만 readUTF(), writeUTF로 바뀌고 나머지는 메소드명 동일
//데이터 타입별로 저장하는 스트림
public void saveDataStream() {
try(FileOutputStream fos = new FileOutputStream("aniData.bs");
//dataoutputSteam은 자료형별 저장하는 보조스트림
DataOutputStream dos = new DataOutputStream(fos);){
Animal a = new Animal("도토리", 3, 0.3);
//데이터를 타입별로 저장할 수 있도록 하기 -> 불러온 순서를 지켜서 내보내야되므로 순서 기억하기
dos.writeUTF(a.getName()); //String 전용
dos.writeInt(a.getAge());
dos.writeDouble(a.getWeight());
}catch(IOException e) {
e.printStackTrace();
}
}
public void loadDataStream() {
try(FileInputStream ifs = new FileInputStream("aniData.bs");
DataInputStream dis = new DataInputStream(ifs)) {
String name = dis.readUTF();
//순서대로 안하면 에러남
int age = dis.readInt();
double weight = dis.readDouble();
Animal a = new Animal(name, age, weight);
System.out.println(a);
} catch(IOException e) {
e.printStackTrace();
}
}
4) 객체 입출력 보조 스트림
객체데이터는 직렬화 필수
직렬화, 역직렬화 메커니즘들은 객체를 저장하거나 전송하는데 사용됩니다
ObjectInputStream
public void animalIn() {
try(ObjectInputStream ois =
new ObjectInputStream(
new FileInputStream("animalsObj.bs"))){
Animal[] result = (Animal[])ois.readObject();
for(Animal a : result) {
System.out.println(a.toString());
}
} catch(ClassNotFoundException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
}
}
ObjectOutputStream
객체 자체를 불러오는 것이라서 append 불가능하니 주의
public void animalOut(Animal[] animals) {
try(ObjectOutputStream oos =
new ObjectOutputStream(
new FileOutputStream("animalsObj.bs")))
// ObjectOutputStream oos = new ObjectOutputStream(animal);
{
oos.writeObject(animals);
}catch(IOException e) {
e.printStackTrace();
}
}
5. 직렬화(Serialization)
: 객체를 바이트 기반 스트림으로 변경하는 것
즉, 객체를 바이트로 저장하려면 직렬화 필수 ⇒ 객체 클래스에 가서 implement Serializable 추가해서 인터페이스 구현
- 객체 직렬화시 private필드를 포함한 모든 필드를 바이트로 변환하지만 transient키워드를 사용한 필드는 직렬화에서 제외됨직렬화한 클래스와 같은 클래스임을 알려주는 식별자 역할
- 자동생성시 역직렬화에서 예상치못한 InvalidClassException을 유발할 수 있어서 명시 권장됨
private static final long serialVersionUID = -98765434567L;
- JVM자동으로 추가해서 오류는 안나지만 명시하는 것을 권장함
- serialVersionUID 필드
6. 역직렬화(Deserealization)
바이트 스트림을 실제 자바 객체로 재생성하는 것
- 직렬화할 때 썼던 클래스를 그대로 사용한다(클래스명 동일)
- 🚨 단, 클래스 내용이 변경되면 실패됨
'JAVA' 카테고리의 다른 글
14-2. 자바(java) : 컬렉션(Collection) - Set, Map 개념과 활용 정리 (0) | 2024.04.24 |
---|---|
14-1. 자바(java) : 컬렉션(Collection) - List 개념과 활용 정리 (0) | 2024.04.09 |
12. 자바(java) : 예외(Exception) 처리, try-catch문 간단 정리 (0) | 2024.04.09 |
11. 자바(java) : 다형성(Polymorphism)과 캐스팅(casting), 추상(substract) 개념 정리 (0) | 2024.04.01 |
10. 자바(java) : 상속(Inherit), super, 오버라이딩(overriding) 간단 정리 (0) | 2024.04.01 |