김영한의 실전 자바 - 고급 2편(스트림)

2025. 1. 23. 22:53·김영한의 실전 자바 - 고급 2편

자바가 외부 파일/네트워크/콘솔 등과 데이터를 주고 받는 단위는 무엇인가

  • byte 단위를 기본으로 사용함

 

여러 외부 자원과 동일한 방식으로 인터페이스 할 수 있도록 자바에서 제공하는 추상 클래스는?

 

  • 외부 시스템과 데이터를 주고 받는 방법은 서로 다를 수 있기 때문에, 이에 따라 코드도 다 각각 짜야 한다면 코드가 복잡하고 변경도 많이 해야하는 문제가 생길 수 있음.
  • 따라서, 자바에서는 InputStream, OutputStream이라는 기본 추상 클래스를 제공해 모든 외부 자원과 동일한 인터페이스로 통신을 할 수 있게 제공함.
  • 기본 인터페이스 함수를 제공하며, byte 단위로 통신

 

InputStream에서 read()가 int를 반환하는 이유는?

  • byte는 부호가 있어 -128 ~ 127로 표현되어 음수 방식을 사용해야 하는 불편함이 있음.
  • EOF(End of File)을 표시해줘야 하는데, 이를 위한 한 자리를 byte에 담을 수 없음.
  • 따라서, int를 사용한다면 -1을 추가로 표현 가능.

 

FileInputStream과 같은 클래스를 사용해서 1바이트씩 파일을 읽는 방식보다 성능을 개선할 수 있는 방법은 무엇이며, 주의해야  할 점은?

  • write()나 read()를 호출 할 때마다 OS의 시스템 콜을 통해 파일을 읽거나 쓰는 명령어를 전달하는데, 이러한 시스템 콜은 상대적으로 무거운 작업임.
  • 따라서, 시스템 콜 호출 횟수를 줄이기 위해 읽고 쓰는 데이터를 버퍼에 일정량 모아서 한 번에 읽기/쓰기 작업을 하면 시스템 콜 횟수가 줄어 성능이 개선 됨.
  • 주의 해야할 점은, 데이터를 버퍼에 넣고, 버퍼가 다 차면 쓰기 작업을 하기 때문에 마지막에 버퍼가 꽉 차지 않은 경우 우에 대해서도 마저 쓰기 작업을 하도록 추가 코드를 넣어 줘야 함.

 

버퍼의 사이즈는 몇으로 설정하는게 좋은가?

  • 시스템 콜 횟수를 줄이기 위해서라면 버퍼의 사이즈는 크면 클 수록 좋다고 생각할 수 있음.
  • 하지만 너무 많은 데이터를 한 번에 처리하려고 하다보면 메모리가 부족해 OutOfMemory 문제가 발생할 수 있음.
  • 또한, 디스크나 파일 시스템에서 데이터를 읽고 쓰는 기본 단위가 보통 4KB 또는 8KB이기 때문에, 이 이상으로 버퍼 사이즈를 키워봤자 성능상 이점이 거의 발생하지 않음.
  • 따라서, 버퍼의 사이즈는 이런 것을 고려해 4KB나 8KB 정도가 적당함.

 

버퍼를 사용한 스트림 읽기쓰기 클래스는?

FileOutputStream fos = new FileOutputStream(FILE_NAME);
BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER_SIZE);
long startTime = System.currentTimeMillis();
for (int i = 0; i < FILE_SIZE; i++) {
    bos.write(1);
}
bos.close();

  • BufferedInputStream, BufferedOutputStream을 사용
  • write 함수가 불리면 BufferedOutputStream의 버퍼 안에 데이터를 채움
  • 만약 버퍼가 가득 찬 경우, FileOutputStream으로 버퍼를 내보냄
  • flush() 함수를 사용하면 버퍼가 다 차지 않아도 내보낼 수 있으며, 스트림 close()시 내부 남아있는 데이터들은 자동으로 flush()가 호출 됨.

 

BufferedXXStream 클래스가 직접 버퍼를 넣어 구현한 스트림보다 성능이 느린 이유는

  • Bueffered 클래스는 멀티 스레드를 고려하여 만든 클래스라 모두 동기화 처리가 되어 있기 때문에 느려짐.
  • 따라서, 성능이 더 중요하다면 직접 구현해서 버퍼를 다뤄주는게 더 좋음.

 

Byte가 아닌 String을 File에 입력하려면 어떻게 구현해야하는가?

String writeString = "ABC";
// 문자 -> byte UTF-8 인코딩
byte[] writeBytes = writeString.getBytes(UTF_8);
// 파일에 쓰기
FileOutputStream fos = new FileOutputStream(FILE_NAME);
fos.write(writeBytes);
fos.close();
  • Stream은 무조건 Byte로만 입출력을 관리함
  • 따라서, String을 getBytes를 통해 Bytes로 변환해줘야 하며, 이때 어떤 문자 집합으로 인코딩할지 결정할 수 있음.
  • 앞으로 어떤 구현체가 나오던지 결국엔 무조건 Byte로 입출력이 관리됨!

 

String을 자동으로 내부에서 자동으로 처리해서 Stream으로 보내는 방식에 대해 설명하시오

//OutputStreamWriter를 사용하는 방식
String writeString = "ABC";
// 파일에 쓰기
FileOutputStream fos = new FileOutputStream(FILE_NAME);
OutputStreamWriter osw = new OutputStreamWriter(fos, UTF_8);
osw.write(writeString);
osw.close();


//FileWriter를 사용하는 방식
String writeString = "ABC";
// 파일에 쓰기
FileWriter fw = new FileWriter(FILE_NAME, UTF_8);
fw.write(writeString);
fw.close();



//InputStreamReader를 사용하는 방식
FileInputStream fis = new FileInputStream(FILE_NAME);
InputStreamReader isr = new InputStreamReader(fis, UTF_8);
StringBuilder content = new StringBuilder();
int ch;
while ((ch = isr.read()) != -1) {
content.append((char) ch);
}
isr.close();


//FileReader를 사용하는 방식
StringBuilder content = new StringBuilder();
FileReader fr = new FileReader(FILE_NAME, UTF_8);
int ch;
while ((ch = fr.read()) != -1) {
content.append((char) ch);
}
fr.close();
  • writer, reader 클래스들은 기본적으로 끝에 Stream이 붙는 클래스와 다른게 parameter를 byte로 받지 않고 char나 String으로 받음.
  • 그 이후, String이나 char를 bytes로 변환해서 사용하는 식으로 구현됨.
  • OutputStreamWriter를 사용하면 String을 write하면 내부에서 자동으로 이를 byte로 변환해서 stream으로 보냄. 
  • FileWriter를 사용하면 OutputStreamWriter를 사용하는 것보다 더 간결하게 사용이 가능함.
  • FileWriter는 내부에 super(new FileOutputStream(fileName), charset) 와 같은 함수가 있어서 사실상 내부에 FileOutputStream을 만들어서 사용 해 결국 동일한 구조라 볼 수 있음.

 

Reader/Writer 클래스에서 Buffer를 사용하는 방식에 대해 설명하시오

String writeString = "ABC\n가나다";
// 파일에 쓰기
FileWriter fw = new FileWriter(FILE_NAME, UTF_8);
BufferedWriter bw = new BufferedWriter(fw, BUFFER_SIZE);
bw.write(writeString);
bw.close();
// 파일에서 읽기
StringBuilder content = new StringBuilder();
FileReader fr = new FileReader(FILE_NAME, UTF_8);
BufferedReader br = new BufferedReader(fr, BUFFER_SIZE);
String line;
while ((line = br.readLine()) != null) {
content.append(line).append("\n");
}
br.close();
  • Stream에서 사용하는 방식과 똑같이 Reader/Writer도 BufferedWriter와 같은 객체 생성해서 버퍼 사이즈를 지정하여 버퍼단위로 읽고 쓸 수 있음.
  • BufferedReader는 한 줄 단위로 읽는 기능인 readLine() 함수도 제공
  • 단, 파일의 끝에 도달하면 String이 아니기 때문에 -1을 표현할 수 없어 null로 표현함.

 

PrintStream에 대해 설명하시오

FileOutputStream fos = new FileOutputStream("temp/print.txt");
PrintStream printStream = new PrintStream(fos);
printStream.println("hello java!");
printStream.println(10);
printStream.println(true);
printStream.printf("hello %s", "world");
printStream.close();
  • 앞에서 언급한 클래스들과 다르게 PrintStream은 String, Byte 외에 Integer나 boolean과 같은 다른 타입도 쓸 수 있음.
  • 이 println이 우리가 사용하던 System.out과 같으며, printStream의 인자로 System.out이 들어가면 콘솔에 출력함.

 

DataOutputStream에 대해 설명하시오

 FileOutputStream fos = new FileOutputStream("temp/data.dat");
 DataOutputStream dos = new DataOutputStream(fos);
 dos.writeUTF("회원A");
 dos.writeInt(20);
 dos.writeDouble(10.5);
 dos.writeBoolean(true);
 dos.close();
 FileInputStream fis = new FileInputStream("temp/data.dat");
 DataInputStream dis = new DataInputStream(fis);
 System.out.println(dis.readUTF());
 System.out.println(dis.readInt());
 System.out.println(dis.readDouble());
 System.out.println(dis.readBoolean());
 dis.close();
  • DataOutputStream을 사용하면 자바의 String, int, double, boolean 과 같은 데이터 형을 편리하게 다룰 수 있음.
  • 타입별로 write/read를 할 수 있음.
  • 다만, 꼭 저장한 순서대로 읽어야 함.
  • 파일을 열어보면 우리가 알아보기 힘든 형태일 수 있는데, 그 이유는 UTF-8의 저장방식이 아니라 Int는 4바이트, boolean은 1바이트 이런식이기 때문임.

'김영한의 실전 자바 - 고급 2편' 카테고리의 다른 글

김영한의 실전 자바 - 고급 2편(네트워크 예외)  (2) 2025.01.28
김영한의 실전 자바 - 고급 2편(네트워크 자원정리)  (1) 2025.01.28
김영한의 실전 자바 - 고급 2편(네트워크, TCP, IP, UDP, DNS)  (1) 2025.01.28
김영한의 실전 자바 - 고급 2편(스트림 활용)  (2) 2025.01.25
김영한의 실전 자바 - 고급 2편(ASCII, UTF)  (0) 2025.01.23
'김영한의 실전 자바 - 고급 2편' 카테고리의 다른 글
  • 김영한의 실전 자바 - 고급 2편(네트워크 자원정리)
  • 김영한의 실전 자바 - 고급 2편(네트워크, TCP, IP, UDP, DNS)
  • 김영한의 실전 자바 - 고급 2편(스트림 활용)
  • 김영한의 실전 자바 - 고급 2편(ASCII, UTF)
5jyan5
5jyan5
  • 5jyan5
    jyan
    5jyan5
  • 전체
    오늘
    어제
    • 분류 전체보기 (242)
      • 김영한의 스프링 핵심 원리(기본편) (8)
      • 김영한의 스프링 핵심 원리 - 고급편 (11)
      • 김영한의 스프링 MVC 1편 (1)
      • 김영한의 스프링 DB 1편 (3)
      • 김영한의 스프링 MVC 2편 (3)
      • 김영한의 ORM 표준 JPA 프로그래밍(기본편) (9)
      • 김영한의 스프링 부트와 JPA 활용2 (2)
      • 김영한의 실전 자바 - 중급 1편 (1)
      • 김영한의 실전 자바 - 고급 1편 (9)
      • 김영한의 실전 자바 - 고급 2편 (9)
      • Readable Code: 읽기 좋은 코드를 작성.. (2)
      • 김영한의 실전 자바 - 고급 3편 (9)
      • CKA (118)
      • 개발 (37)
      • 경제 (4)
      • 리뷰 (1)
      • 정보 (2)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      gesingleresult
      @within
      WAS
      jpq
      김영한
      고급
      reentarantlock
      양방향 맵핑
      hibernate5module
      Target
      @discriminatorvalue
      버퍼
      typequery
      프록시
      자바
      @args
      @discriminatorcolumn
      페치 조인
      락
      조회 성능 최적화
      프록시 팩토리
      빈 후처리기
      jdk 동적 프록시
      requset scope
      Thread
      cglib
      단방향 맵핑
      스레드
      log trace
      JPQL
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.2
    5jyan5
    김영한의 실전 자바 - 고급 2편(스트림)
    상단으로

    티스토리툴바