열아홉번째날[IO기반 입출력 및 네트워킹(1) - IO 패키지 소개 / 입력 스트림 / 출력 스트림 / 콘솔 / 파일 입출력 / 보조 스트림 ]
목차
- IO 패키지 소개
- 입력 스트림과 출력 스트림 : 여기만 잘 해도 앞으로 별 문제X
- 콘솔(Console) 입출력
- 파일(File) 입출력
- 보조 스트림
IO 패키지 소개
* IO : Input / Output
* 프로그램에서 데이터를 외부에서 읽고, 다시 외부로 출력하는 작업 빈번함
* 자바에서 데이터는 스트림(Stream)을 통해 입출력됨
- 스트림 : 단일 방향으로 연속적으로 흘러가는 것
- 출발지에서 나와 도착지로 들어간다는 개념
* 남의 컴퓨터에서 읽어서, 내 컴퓨터에 씀 // 내 컴퓨터에서 읽어서, 남의 컴퓨터에 씀
* Java.io 패키지 : 자바의 기본적인 데이터 입출력 API 제공
- InputStream / OutputStream : 바이트 단위 (1바이트를 읽고 쓰는 애들)
- Reader / Writer : 문자 단위 (2바이트를 읽고 쓰는 애들)
- Buffered : 임시 메모리 저장이기 때문에, 다른 애들보다 성능이 느려짐
입력 스트림과 출력 스트림
* 입력 스트림과 출력 스트림의 개념
- 입력과 출력을 따로따로 하나씩 만들어서 사용해야 함
- 입력스트림 : 프로그램이 데이터를 입력받을 때
- 출력스트림 : 프로그램이 데이터를 보낼 때
* 바이트 기반 스트림 : 1바이트 읽고 씀 , 많이 사용됨
- 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 받고 보내는 것 가능
* 문자 기반 스트림 : 2바이트 읽고 씀 (char 타입)
- 문자만 받고 보낼 수 있도록 특화
InputStream
* 바이트 기반 입력 스트림의 최상위 클래스
* 추상 클래스 : 상속받아서 만들어야 함
* 읽어 들이는 입력 스트림 : 무언가를 읽는다고 생각해야 함 : 메소드가 read
리턴 타입 | 메소드 | 설명 |
int | read() | 입력 스트림으로부터 1바이트를 읽고 읽은 바이트를 리턴한다. |
int | read(byte[] b) | 입력 스트림으로부터 읽은 바이트들을 매개값으로 주어진 바이트 배열 b에 저장하고 실제로 읽은 바이트 수를 리턴한다. |
int | read(byte[] b, int off, int len) | 배열의 b[off] 부터, len개까지 저장 |
void | close() | 시스템 자원을 반납 입력 스트림을 닫음 |
* read() 메소드
InputStream is = new FileInputStream("/Users/user/Desktop/국비/test.txt");
int readByte;
while((readByte = is.read()) != -1) {...}
* read(byte[] b) 메소드
InputStream is = new FileInputStream("/Users/user/Desktop/국비/test.txt");
int readByteNo;
byte[] readBytes = new byte[100];
while((readByteNo = is.read(readBytes)) != -1) {...}
* read(byte[] b, int off, int len)
InputStream is = new FileInputStream("/Users/user/Desktop/국비/test.txt");
int readByteNo;
byte[] readBytes = new byte[100];
int readByteNo = is.read(readBytes, 0 , 20); //인덱스 0부터 길이20
while((readByte = is.read()) != -1) {...}
OutputStream
* 바이트 기반 출력 스트림의 최상위 클래스
* 추상 클래스
* 쓰는 출력 스트림 : 무언가를 쓴다고 생각해야 함 : 메소드가 write
* BufferdOiutputStream : 프로그램에서 읽어들인 내용을 쓰기 위해 '버퍼'라는 메모리 공간에 집어넣고 그게 꽉 차야 화면으로 밀어서 보여줌 , ... 버퍼링을 생각하면 됨 (동영상 버퍼링 : 네트웤이 불안정해도 라는 이유도 있지만, 계속 네트워크를 통해 날라오는 데이터를 받기 때문. 버퍼가 꽉 차야 데이터를 전송해서 내 화면에 영상으로 전송해서 볼 수가 있음) ➡️ 버퍼가 꽉 차지 않았는데도 데이터를 날려보내기 위해 사용하는 것이 flush()가 됨 / 스캐너 객체보다 버퍼 쓰는게 훨 나음 . 성능 면에서.
리턴타입 | 메소드 | 설명 |
void | write(int b) | 출력 스트림으로 1바이트를 보냄 |
void | write(byte[] b) | 출력 스트림에 매개값으로 주어진 바이트 배열 b의 모든 바이트를 보냄 |
void | write(byte[]b, int off, int len) | b[off] 부터 len개까지의 바이트를 보냄 |
void | flush() | 버퍼에 잔류하는 모든 바이트 출력 |
void | close() | 사용한 시스템 자원 반납 출력 스트립 닫음 |
Reader
* input, output이랑 다르게 byte가 아니라 char 로 씀
* 문자 기반 입력 스트림의 최상위 클래스
* 추상 클래스
리턴타입 | 메소드 | 설명 |
int | read() | 입력 스트림으로부터 한개의 문자 읽고 리턴 |
int | read(char[] cbuf) | 읽은 문자들을 매개값으로 주어진 문자 배열 cbuf에 저장 실제로 읽은 문자수 리턴 |
int | read(char[] cbuf, int off, int len) | cbuf[off] 부터 len개까지 저장 실제로 읽은 문자 len개 리턴 |
void | close() | 사용한 시스템 자원 반납 입력 스트림 닫음 |
Writer
* 문자 기반 출력 스트림의 최상위 클래스
* 추상 클래스
리턴타입 | 메소드 | 설명 |
void | write(int c) | 매개값으로 주어진 한 문자 보냄 |
void | write(char[] cbuf) | 매개값으로 주어진 문자 배열 cbuf의 모든 문자 보냄 |
void | write(char[] cbuf, int off, int len) | cbuf[off]부터 len개까지 문자 보냄 |
void | write(String str) | 매개값으로 주어진 모든 문자열 보냄 |
void | write(String str, int off, int len) | 매개값으로 주어진 문자열 off번부터 len개까지 문자 보냄 |
void | flush() | 버퍼에 잔류하는 모든 문자열 출력 |
void | close() | 사용한 시스템 자원 반납 출력 스트림 닫음 |
* 거의 다 필요없고 write(String str)을 제일 많이 사용함
* inputstream 의 리더는 아스키 코드로 : 영어만 읽을 수 잇음
* 뒤의 Reader는 유니코드 값 : 전 세계 언어를 다 쓸 수 있음
콘솔(Console) 입출력
* 콘솔 : 시스템을 사용하기 위해 키보드로 입력 받고, 화면으로 출력하는 소프트웨어
- Unix, Linux : 터미널
- Windows 운영체제 : 명령 프롬프트
- 이클립스 : Console 뷰
* System.in : InputStream의 객체
* System.out : OutputStream 의 객체
* System.err : OutputStream 의 객체
System.in 필드
* InputStream 타입의 입력 스트립 : InputStream 변수 대입 가능
InputStream is = System.in;
* 읽은 byte는 아스키코드(ascii code)
* 아스키코드로부터 문자 변환
* InputStream의 read() 메소드 : 1바이트만 읽기 때문에, 한글과 같은 2바이트는 읽을 수 없음 ➡️ 오류 발생
- 키보드로 입력된 한글을 읽기 위해선 ➡️ read(byte[] b) 또는 read(byte[]b, int off, int len) 사용
➡️ 전체 입력된 내용을 바이트 배열로 받음 ➡️ 배열을 이용해 String 객체 생성
byte[] byteData = new byte[15];
int readByteNo = System.in.read(byteData);
String strData = new String(byteData, 0, readByteNo-2);
System.out 필드
* PrintStream 타입의 출력 스트림 : OutputStream으로 타입 변환 가능
* 아스키 코드를 출력하면 콘솔에는 문자가 출력됨
byte b = 97;
os.write(b)
</>
a
* 문자열을 출력하기 위해선, 바이트 배열을 얻어야 함
String name = "홍길동"
byte[] nameBytes = name.getBytes();
os.write(nameBytes);
os.flush();
Console 클래스
* 자바6부터 콘솔에서 입력된 문자열을 쉽게 읽을 수 있도록 제공함
* 이클립스에서 System.console()은 null을 리턴 ➡️ 명령 프롬프트에서 실행
*Console 클래스 읽기 메소드
리턴타입 | 메소드 | 설명 |
String | readLine() | 엔터키를 입력하기 전의 모든 문자열 읽음 |
char[] | readPassword() | 키보드 입력 문자를 콘솔에 보여주지 않고 문자열 읽음 |
Scanner 클래스
* Console 클래스 단점: 콘솔로부터 문자열은 읽을 수 있음 / 기본타입(정수, 실수)는 바로 읽을 수 없음
* Scanner 클래스 : 콘솔로부터 기본 타입의 값을 바로 읽을 수 있음
* 단점 : 저장되어 있는 타입이 아닌경우 오류 발생
파일(File) 입출력
File 클래스
* 파일 시스템의 파일을 표현하는 클래스
- 파일 크기, 파일 속성, 파일 이름 등의 정보 제공
- 파일 생성 및 삭제 기능 제공
- 디렉토리 생성, 디렉토리에 존재하는 파일 리스트 얻어내는 기능 제공
* 파일 객체 생성
File file = new File("파일경로")
* 파일 또는 디렉토리 존재 유무 확인 메소드
boolean isExist = file.exists();
* 파일 및 디렉토리 생성 및 삭제 메소드
* 파일 및 디렉토리의 정보를 리턴하는 메소드
FileInputStream
* 파일로부터 바이트 단위로 읽어들일 때 사용
- 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 파일을 읽을 수 있음
* 객체 생성 방법
- FileInputStream 객체가 생성될 때 파일과 직접 연결
- 만약 파일이 존재하지 않으면 FileNotFoundException 발생 : try-catch문으로 예외 처리
* InputStream 하위 클래스 : 사용방법은 InputStream과 동일
FileOutputStream
* 파일로부터 바이트 단위로 데이터를 저장할 때 사용
- 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 파일을 읽을 수 있음
* 객체 생성 방법
- 파일이 이미 존재할 경우, 데이터를 출력하게 되면 파일을 덮어쓰게 되는 단점
* InputStream 하위 클래스 : 사용방법은 InputStream과 동일
FileReader
* 텍스트 파일로부터 데이터를 읽어들일 때 사용 : 문자 단위로 읽음
- 텍스트가 아닌 그림, 오디오, 비디오 등의 파일은 읽을 수 없음
* 객체 생성 방법
- FileReader 객체가 생성될 때 파일과 직접 연결
- 만약 파일이 존재하지 않으면 FileNotFoundException 발생 : try-catch문으로 예외 처리
FileReader fr = new FileReader("파일경로");
File file = new File("파일경로");
FileReader fr = new FileReader(file);
* Reader 하위 클래스 : 사용 방법은 Reader와 동일함
FileWriter
* 텍스트 파일에 문자 데이터를 저장할 때 사용
- 텍스트가 아닌 그림, 오디오, 비디오 등의 데이터를 파일로 저장 불가함
* 객체 생성 방법
- 파일에 이미 존재할 경우, 데이터 출력 : 덮어쓰게 됨
- 기존 파일 끝에 데이터를 추가할 경우
FileWriter fw = new FileWriter("경로", true);
FileWriter fw = new FileWriter(file, true);
* Writer 하위 클래스 : 사용 방법은 Writer와 동일함
보조 스트림
* 읽고 쓰는 또 다른 클래스 라고 생각하기
* 다른 스트림과 연결 되어 여러 가지 편리한 기능을 제공해주는 스트림
- 문자 변환, 입출력 성능 향상, 기본 데이터 타입 입출력, 객체 입출력 등의 기능을 제공
- 앞에서 만든 입력, 출력 스트림이 있어야 보조 스트림도 사용할 수 있음
* 보조 스트림 생성
보조스트림 변수 = new 보조스트림(연결스트림)
*보조 스트림 체인 : 다른 보조 스트림과 연결되어 역할 수행
문자 변환 보조 스트림
* 소스 스트림이 바이트 기반 스트림이지만 데이터가 문자일경우 사용 : 1바이트지만, 2바이트로 바꿔서 출력 할 수 있다
* InputStreamReader
InputStream is = System.in;
InputStreamReader reader = new InputStreamReader(is);
FileInputStream fis = new FileInputStrea("파일경로");
Reader reader = new InputStreamReader(fis);
* OutputStreamReader
OutputStream is = System.out;
OutputStreamWriter reader = new OutputStreamWriter(is);
FileOutputStream fos = new FileOutputStrea("파일경로");
Writer writer = new OutputStreamReader(fos);
성능 향상 보조 스트림
* 입출력 성능에 영향을 미치는 것
- 하드디스크 : CD 같은 디스크 여러개를 집어넣고 저장 ➡️ SSD로 바꾸면 성능 향상
- 느린 네트워크
* 버퍼를 이용한 해결
- 입출력 소스와 직접 작업하지 않고 버퍼(buffer)와 작업 : 실행 성능 향상
- 프로그램은 쓰기 속도 향상
- 버퍼가 꽉 차게 되면 데이터를 한번에 하드 디스크를 보내 출력 횟수를 줄여줌
* BufferedInputStream, BufferedReader
* BufferedOutputStream, BufferedWriter
기본 타입 입출력 보조 스트림
* 잘 안씀 귀찬귀찬
* 바이트 스트림은 바이트 단위로 입출력하기 때문에, 자바 기본 타입 단위로 출력할 수 없음
* 입출력 순서를 맞춰서 사용
* DataInputStreamr과 DataOutputStream 보조스트림 연결하면 기본 데이터 단위로 입출력 가능
DataInputStream dis = new DataInputStream(바이트입력스트림);
DataOutputStream dos = new DataOutputStream(바이트출력스트림);
* 메소드
프린터 보조 스트림
* 파일에 타입 상관 없이 사용 가능하기 때문에, 많이 유용함
PrintStream ps = new PrintStream(바이트출력스트림);
PrintWriter pw = new PrintWriter(문자출력스트림);
* printf : 출력하고자 하는 format을 정하고 씀
객체 입출력 보조 스트림
* 객체를 파일 또는 네트워크로 입출력할 수 있는 기능 제공
* 객체 직렬화 : 객체의 데이터(필드값)을 바이트 기반 스트림으로 데이터 변경하는 것 (객체는 문자가 아니기 때문) : 파일에 쓰는 거
* 객체 역직렬화 : 읽어들인 바이트를 객체로 복원하는 것 : 읽어오는 거
* 직렬화가 가능한 클래스 : Serializable 인터페이스를 구현한 클래스만 직렬화 가능
- transient가 붙은 필드는 제외
- private 필드를 포함한 모든 필드를 바이트로 변환 가능
serialVersionUID 필드
* 직렬화된 객체를 역직렬화 할 땐 직렬화했을 때와 같은 클래스 사용
* 클래스 이름이 같아도, 클래스 내용이 변경된 경우 역직렬화 실패
* serialVersionUID
- 같은 클래스임을 알려주는 식별자 역할
- Serializable 인터페이스 구현 : 컴파일 시 자동적으로 serialVersionUID 정적 필드 추가
- 재컴파일하면 serialVersionUID의 값 변경됨!
* 수정이 있다면, 명시적으로 serialVersionUID 선언해야 함
static final long serialVersionUID = 정수값;
wirteObject()와 readObject() 메소드
* writeObject(ObjectOutputStream out)
- 직렬화 직전 자동 호출
- 추가 직렬화할 내용 작성 가능
*readObject( )
- 역직렬화 직전 자동 호출
- 추가 역직렬화 내용 작성 가능
* 추가 직렬화 및 역직렬화 필요한 경우 : 부모가 serializable 구현X, 자식만 serializable 구현한 경우
- 부모 필드는 직렬화에서 제외됨
- writeObject()에서 부모 필드 직렬화 필요
- readObject()에서 부모 필드 역직렬화 필요
- 부모 클래스가 Serializable 구현하도록 하는게 가장 쉬운 방법