[Java] File IO

2024. 1. 29. 00:09공부 중/Java

1. 노드스트림

 

  • 노드
    : 입력과 출력의 양 끝단
  • 스트림
    : 두 노드를 연결하고 데이터를 전송할 수 있는 개념
    : 단방향 통신

 

  • 입력 스트림 : InputStream, Reader
  • 출력 스트림 : OutputStream, Writer

 

  • byte 단위
    • InputStream
    • OutputStream
  • char 단위
    • Reader
    • Writer

 


가. InputStream

 

System.inInputStream 타입이다.

 

 

메서드 설명
read() byte 하나를 읽어서 int로 반환. 더 이상 읽을 값이 없으면 -1 반환.
read(byte[] b) 데이터를 읽어서 b에 넣고 읽은 바이트의 개수를 반환. 더 이상 읽을 값이 없으면 0 반환.
read(byte[] b, int offset, int len) 최대 len 만큼 데이터를 읽어서 b의 offset부터 저장하고 읽은 바이트의 수를 반환. 따라서 len+offset은 b의 크기 이하여야 한다.
close() 스트림 종료 후 자원 반납.

 

read()byte를 읽어서 int를 반환한다.

 

자바의 입출력이 비효율적인 이유 중 하나다.

 

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;

public class SimpleInputTest {

    private String data1 = "hi java world";
    private String data2 = "자바는 객체지향 언어입니다.";

    private void read1() {
        try (InputStream input = new ByteArrayInputStream(data1.getBytes())) {
            int read = -1;
            while ((read = input.read()) != -1) {
                System.out.printf("read1 : 읽은 값: %d, 문자로: %c%n", read, read);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void read2() {
        byte[] buffer = new byte[10];
        try (InputStream input = new ByteArrayInputStream(data1.getBytes())) {
            int read = -1;
            while ((read = input.read(buffer)) > 0) {

                System.out.printf("read2 : 읽은 개수: %d, 문자열: %s%n", read, new String(buffer, 0, read));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void read3() {
        byte[] buffer = new byte[10];
        try (InputStream input = new ByteArrayInputStream(data2.getBytes())) {
            int read = -1;
            while ((read = input.read(buffer)) > 0) {

                System.out.printf("read3 : 읽은 개수: %d, 문자열: %s%n", read, new String(buffer, 0, read));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        SimpleInputTest ns = new SimpleInputTest();
        ns.read1();
         ns.read2();
         ns.read3();
    }
}
/*
read1 : 읽은 값: 104, 문자로: h
read1 : 읽은 값: 105, 문자로: i
read1 : 읽은 값: 32, 문자로:  
read1 : 읽은 값: 106, 문자로: j
read1 : 읽은 값: 97, 문자로: a
read1 : 읽은 값: 118, 문자로: v
read1 : 읽은 값: 97, 문자로: a
read1 : 읽은 값: 32, 문자로:  
read1 : 읽은 값: 119, 문자로: w
read1 : 읽은 값: 111, 문자로: o
read1 : 읽은 값: 114, 문자로: r
read1 : 읽은 값: 108, 문자로: l
read1 : 읽은 값: 100, 문자로: d
read2 : 읽은 개수: 10, 문자열: hi java wo
read2 : 읽은 개수: 3, 문자열: rld
read3 : 읽은 개수: 10, 문자열: 자바는 
read3 : 읽은 개수: 10, 문자열: 객체지�
read3 : 읽은 개수: 10, 문자열: �� 언어�
read3 : 읽은 개수: 9, 문자열: ��니다.*/
  • read1() : 바이트 한 개씩.
  • read2() : 버퍼를 생성하고 버퍼만큼 읽어드림. UTF-8 한글은 한 글자가 3byte라서 글자가 깨질 수 있다.

 

텍스트가 이동할 때 byte는 적합하지 않음.

 

Java는 내부에선 UTF-16 BE를 외부로 주고받을 때 UTF-8을 사용한다.

 

한글은 UTF-8로 한 글자당 3byte다.

 

버퍼 크기가 10이라면 3 + 3+ 3 + 1?이라서 마지막 글자가 깨질 수 있다.

 

유니코드를 안정적으로 읽어오기 위해서는 Reader를 이용하자.

 


나. Reader

 

메서드 설명
read() char 하나를 읽어서 int로 반환한다. 더 이상 읽을 값이 없으면 -1 반환.
read(char[] cbuf) 데이터를 읽어서 cbuf를 채우고 읽은 문자의 개수를 반환한다. 0이면 더 이상 읽을 값이 없는 것이다.
read(char[] cbuf, int off, int len) 최대 len 만큼 읽어서 cbuf의 offset부터 cbuf에 저장하고 읽은 char 개수를 리턴한다. 따라서 len+off는 cbuf 크기 이하여야 한다.
read(java.nio.CharBuffer target) 데이터를 읽어서 target에 저장한다.
close() 스트림 종료 후 자원 반납.

 

import java.io.CharArrayReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;

public class SimpleInputTest {

    private String data2 = "자바는 객체지향 언어입니다.";

    private void read4() {
        char[] buffer = new char[10];
        try (Reader reader = new CharArrayReader(data2.toCharArray())) {
            int read = -1;
            while((read = reader.read(buffer)) > 0) {
                System.out.println("read4 : 읽은 개수 : " +  read + " : " + new String(buffer, 0, read));
            }
        }catch(IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        SimpleInputTest ns = new SimpleInputTest();
         ns.read4();
    }
}
/*
read4 : 읽은 개수 : 10 : 자바는 객체지향 언
read4 : 읽은 개수 : 5 : 어입니다.
*/
  • 유니코드를 안정적으로 받을 수 있다.
  • read4() : 유니코드는 char 타입으로 즉 Reader 계열로 읽자.
  • try (Reader reader = new CharArrayReader(data2.toCharArray())) {
    : try-with-resource. 자원을 자동으로 반환.
  • new String(buffer, 0, read)
    : buffer0에서부터 read만큼 읽는다.

 


다. OutputStream

메서드 설명
write(int b) b의 내용을 byte로 출력
write(byte b[]) b를 문자열로 변환해서 출력
write(byte b[], int off, int len) b의 off부터 off+len-1만큼을 문자열로 변환하여 출력
close() 스트림 종료 후 자원 반납. 내부적으로 close() 호출.
flush() 버퍼가 있는 스트림에서 버퍼의 내용을 출력하고 버퍼를 비운다.

 


라. Writer

 

메서드 설명
write(int c) c를 char로 출력한다.
write(char[] cbuf) cbuf를 문자열로 변환해서 출력
write(char[] cbuf, int off, int len) cbuf의 off부터 off+len-1만큼을 문자열로 출력
write(String str) str 출력
write(String str, int off, int len) cbuf의 off부터 off+len-1만큼을 문자열로 출력
append(CharSequence csq) csq를 출력하고 Writer를 반환
append(CharSequence csq, int start, int end) csq의 start부터 end까지 출력후 Writer 반환.
close() 스트림을 종료 후 자원 반납. 내부적으로 flush() 호출
flush() 버퍼가 있는 스트림에서 버퍼의 내용을 출력하고 버퍼를 비운다.

 

 

Writer (Java Platform SE 8 )

Flushes the stream. If the stream has saved any characters from the various write() methods in a buffer, write them immediately to their intended destination. Then, if that destination is another character or byte stream, flush it. Thus one flush() invocat

docs.oracle.com

  • 텍스트는 char 타입으로 즉 Writer 계열로 쓰자.

 


마. File

 

파일과 디렉터리를 다루는 클래스.

 

 

메서드 설명
File(String pathname) pathname에 해당하는 파일을 생성한다.
File(File parent, String child) parent 경로 아래 child 생성한다.
createNewFile() 새로운 파일 생성. boolean 반환.
mkdir() 새로운 디렉터리 생성. boolean 반환.
mkdirs() 경로상 모든 디렉터리 생성. boolean 반환.
delete() 파일 또는 디렉터리를 삭제
getName() 파일의 이름을 String으로 반환
getPath() 파일의 경로를 String으로 반환
getAbsolutePath() 파일의 절대 경로를 String으로 반환
getCanimicalPath() 파일의 정리된? 절대 경로를 String으로 반환.
isDirectory() 파일이 디렉터리인지 확인한다. boolean 반환.
isFile() 파일이 파일인지 확인. boolean 반환.
exists() 파일 또는 폴더가 존재하는지 확인. boolean 반환.
length() 파일의 길이를 리턴한다.
listFiles() 파일이 디렉터리인 경우 자식 파일들을 File[]로 반환

 

1) 절대경로, 상대경로

import java.io.File;
import java.io.IOException;

public class UseFileTest {

    public static void main(String[] args) {
        try {
            // 절대 경로
            File temp = new File("c:\\Temp");
            System.out.printf("존재? %b, 디렉토리? %b%n", temp.exists(), temp.isDirectory());

            // 상대경로
            File current = new File(".");
            System.out.printf("여기는 어디? %s%n", current.getCanonicalPath());

            File readme = new File("C:\\company\\work\\java_basic\\src\\com\\company\\node\\readme.txt");
            File readme2 = new File(".\\src\\com\\company\\node\\readme.txt");
            File readme3 = new File(UseFileTest.class.getResource("./readme.txt").getFile());

            System.out.println(readme.getCanonicalPath());
            System.out.println(readme.canRead());
            System.out.println(readme2.getCanonicalPath());
            System.out.println(readme2.canRead());
            System.out.println(readme3.getCanonicalPath());
            System.out.println(readme3.canRead());

            // END
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
  • File readme = new File("C:\\company\\work\\java_basic\\src\\com\\company\\node\\readme.txt");
    : 절대 경로
  • File readme2 = new File(".\\src\\com\\company\\node\\readme.txt");
    : 상대경로
    : 상대경로는 java program을 실행하는 위치다. 소스의 위치와 무관하다.
    : 상대경로로 표시하지 말자. 실행 위치에 따라서 결과가 달라진다.
  • File readme3 = new File(UseFileTest.class.getResource("./readme.txt").getFile());
    : 상대경로보다는 차라리 특정 클래스를 기준으로 파일을 찾자.

 


바. FileInputStream, FileOutputStream

 

byte 단위로 파일을 입출력할 경우.

 

FileInputStream() 설명
FileInputStream(String name) name 경로의 파일을 읽는 FileInputStream 생성
FileInputStream(File file) file을 읽는 FileInputStream 생성

 

FileOutputStream() 설명
FileOutputStream(String name) name 경로의 파일에 출력하는 FileOutputStream 생성
FileOutputStream(String name, boolean append) name 경로의 파일에 출력하는 FileOutputStream 생성.
단, append가 true라면 기존 파일에 이어쓴다.
FileOutputStream(File file) file에 출력하는 FileOutputStream 생성.
FileOutputStream(File file, boolean append) file에 출력하는 FileOutputStream 생성.
단, append가 true라면 기존 파일에 이어쓴다.

 

1) 파일 이동

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class UseFileStream {
    public static void main(String[] args) throws IOException {
        UseFileStream st = new UseFileStream();

        System.out.printf("buffer size: %d, time: %d%n", 100, st.fileMove(100));
        System.out.printf("buffer size: %d, time: %d%n", 1000, st.fileMove(1000));
        System.out.printf("buffer size: %d, time: %d%n", 10000, st.fileMove(10000));
        System.out.printf("buffer size: %d, time: %d%n", 100000, st.fileMove(100000));
        System.out.printf("buffer size: %d, time: %d%n", 1000000, st.fileMove(1000000));
    }

    public long fileMove(int bufferSize) {
        long start = System.currentTimeMillis();
        File src = new File("C:\\파일 경로");
        File target = new File("C:\\복사 경로");
        byte[] buffer = new byte[bufferSize];
        try(FileInputStream input = new FileInputStream(src); FileOutputStream output = new FileOutputStream(target);) {
            int read = 0;
            while((read = input.read(buffer))>0) {
                output.write(buffer, 0, read);
            }
        }catch(IOException e) {
            e.printStackTrace();
        }

        return System.currentTimeMillis() - start;
    }
}
/*
buffer size: 100, time: 26214
buffer size: 1000, time: 3354
buffer size: 10000, time: 768
buffer size: 100000, time: 336
buffer size: 1000000, time: 277
*/
  • try(FileInputStream input = new FileInputStream(src); FileOutputStream output = new FileOutputStream(target);) {
    : try-with-resource로 FileInputStreamFileOutputStream 생성.
  • byte[] buffer = new byte[bufferSize]; : 데이터를 이동시킬 버퍼.
  • while((read = input.read(buffer))>0) { : 읽기
  • output.write(buffer, 0, read); : 쓰기
  • buffer가 크면 빠르지만 자원을 많이 사용함으로 적절한 크기로 사용하자.

 


사. FileReader, FileWriter

 

파일의 문자를 처리할 때.

 

FileReader 설명
FileReader(String name) name 경로의 파일을 읽는 FileReader 생성
FileReader(File file) file을 읽는 FileReader 생성

 

FileWriter() 설명
FileWriter(String name) name 경로의 파일에 출력하는 FileWriter 생성
FileWriter(String name, boolean append) name 경로의 파일에 출력하는 FileWriter 생성.
단, append가 true라면 기존 파일에 이어쓴다.
FileWriter(File file) file에 출력하는 FileWriter 생성.
FileWriter(File file, boolean append) file에 출력하는 FileWriter 생성.
단, append가 true라면 기존 파일에 이어쓴다.
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
import java.util.Scanner;

public class NodeDiaryTest {
    public static void main(String[] args) throws IOException {
        NodeDiaryTest st = new NodeDiaryTest();
        st.writeDiary();
    }

    private void writeDiary() {
        File target = new File("c:" + File.separator + "Temp" + File.separator + "diary.txt");
        try (Scanner scanner = new Scanner(System.in); FileWriter writer = new FileWriter(target, true); FileReader reader = new FileReader(target);) {

            System.out.println("일기를 작성합니다. 그만두려면 x를 입력하세요.");
            writer.write("\n오늘 날짜: - " + new Date() + "\n");

            String str = null;
            while(true) {
                str = scanner.nextLine();
                if(str.equals("x")) {
                    break;
                }else {
                    writer.write(str+"\n");
                }
            }

            System.out.println("일기 저장 완료!!");

            char[] buffer = new char[10];
            int read = -1;
            while((read = reader.read(buffer))>0) {
                System.out.println(String.valueOf(buffer, 0, read));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • File target = new File("c:" + File.separator + "Temp" + File.separator + "diary.txt");
    : 운영체제마다 file sparator가 다르다. 운영체제 독립적으로 경로를 설정하기 위해서 사용.
  • 텍스트를 파일에 쓰거나 읽는 경우.

 


2. 보조스트림

 

Filter Stream라고 하기도 한다.

 

단순한 byte, char을 전달하는 노드스트림을 잘 사용하지 않음.

 

보통 노드스트림과 보조스트림을 결합해서 사용함.

 

  • 문자 set 변환
  • buffering
  • 기본 데이터 형의 전송
  • 객체 입출력

 

등등 결합된 스트림에 부가적인 기능을 제공한다.

 

  • 스트림 체이닝 : 필요에 따라서 여러 보조 스트림을 연결해서 사용할 수 있다.

 

 


가. 보조스트림의 종류

BufferedReader br = new BufferedReader(new InpuStreamReader(System.in));

보조 스트림(br)의 close()가 호출되면 노드 스트림의 close()까지 자동으로 호출됨.

(System.in은 예외.)

 


나. InputStreamReader & OutputStreamWriter

 

byte 기반의 Stream을 char 기반으로 변환하는 보조 스트림.

 


다. Buffered 계열

 

스트림의 입/출력 성능 향상을 위해서 버퍼 사용.

 

1) BufferedInputStream & BufferedOutputStream

 

 

생성자 설명
BufferedInputStream(InputStream in) in을 이용해 크기가 8192인 버퍼를 같는 BufferedInputStream 생성.
BufferedInputStream(InputStream in, int size) in을 이용해 크기가 size인 버퍼를 같는 BufferedInputStream 생성.
BufferedOutputStream(OutputStream out) out을 이용해 크기가 8192인 버퍼를 같는 BufferedOutputStream 생성.
BufferedOutputStream(OutputStream out, int size) out을 이용해 크기가 size인 버퍼를 같는 BufferedOutputStream 생성.
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopyExample {
    public static void main(String[] args) {
        String sourceFile = "source.txt";
        String destinationFile = "destination.txt";

        try (
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destinationFile))
        ) {
            byte[] buffer = new byte[1024];
            int bytesRead;

            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }

            System.out.println("파일 복사가 완료되었습니다.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

2) BufferedReader & BufferedWriter

생성자 설명
BufferedReader(Reader in) in을 이용해 크기가 8192인 버퍼를 같는 BufferedReader 생성.
BufferedReader(Reader in, int size) in을 이용해 크기가 size인 버퍼를 같는 BufferedReader 생성.
BufferedWriter(Writer out) out을 이용해 크기가 8192인 버퍼를 같는 BufferedWriter 생성.
BufferedWriter(Writer out, int size) out을 이용해 크기가 size인 버퍼를 같는 BufferedWriter 생성.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;

public class KTest {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int a = Integer.parseInt(st.nextToken());
        String input = st.nextToken();

        StringBuilder sb = new StringBuilder();
        for(int i=0; i<a; i++) {
            sb.append(input);
        }
        String str = sb.toString();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        bw.write(a);
        bw.write(str);

        bw.flush();
        bw.close();
        br.close();
    }
}

참고.

 

 

[알고리즘] Java 입출력

1. 입력 입력 속도 비교 여러가지 언어와 입력 방법을 이용해서 시간이 얼마나 걸리는지 비교해 보았습니다. 방법: 첫째 줄에 정수의 개수 N (= 10,000,000), 둘째 줄부터 N개의 줄에 한 개의 자연수(10

ramen4598.tistory.com

 


3) DataInputStream & DataOutputStream

 

DataInputStreamDataOutputStream은 원시 데이터 타입(primitive data types)을 바이트 스트림으로 읽고 쓸 수 있다.

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class DataInputStreamExample {
    public static void main(String[] args) {
        String fileName = "data.bin";

        try (DataInputStream dis = new DataInputStream(new FileInputStream(fileName))) {
            int intValue = dis.readInt();
            double doubleValue = dis.readDouble();

            System.out.println("Read Int: " + intValue);
            System.out.println("Read Double: " + doubleValue);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

DataInputStream을 사용하여 간단한 정수와 실수를 읽는 예제.

 

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataOutputStreamExample {
    public static void main(String[] args) {
        String fileName = "data.bin";

        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(fileName))) {
            int intValue = 42;
            double doubleValue = 3.14;

            dos.writeInt(intValue);
            dos.writeDouble(doubleValue);

            System.out.println("Write Int: " + intValue);
            System.out.println("Write Double: " + doubleValue);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

DataOutputStream을 사용하여 정수와 실수를 파일에 쓰는 예제입니다.

 


4) ObjectInputStream & ObjectOutputStream

 

객체를 직렬화하고 역직렬화함으로써 파일에 저장하거나 네트워크를 통해 전송하는 등 다양한 용도로 활용.

 

 

  생성자 메서드
ObjectOutputStream ObjectOutputStream(OutputStream out) void writeObject(Object obj)
ObjectInputStream ObjectInputStream(InputStream in) Object readObject()

 


라. 객체 직렬화

 

객체를 파일 등에 저장하거나 네트워크로 전송하기 위해서 연속적인 데이터로 변환하는 것.

 

자바에서 직렬화되기 위한 조건

  • Serializable 인터페이스를 구현할 것. (내부에 구현해야 할 abstract method가 존재하지 않는 markup interface다.)
  • 클래스의 모든 멤버가 Serializable 인터페이스를 구현해야 함.
  • 직렬화에서 제외하려는 멤버는 transient 선언
  • static 변수도 직렬화 대상이 아니다. (객체로 접근할 일 없음)

 


1) 실습

import java.io.Serializable;

public class Person {
    public static int age = 10;
    private String id;
    private transient String pass;
    private Address addr;

    public Person(String id, String pass, String zipCode, String city) {
        this.id = id;
        this.pass = pass;
        this.addr = new Address(zipCode, city);
    }

    @Override
    public String toString() {
        return "[id=" + id + ", pass=" + pass + ", addr=" + addr + "]" + age;
    }

     class Address {
        private String zipCode;
        private String city;

        public Address(String zipCode, String city) {
            this.zipCode = zipCode;
            this.city = city;
        }

        public String toString() {
            return "Address [zipCode=" + zipCode + ", city=" + city + "]";
        }
    }
}
  • private transient String pass; : 민감한 데이터는 직렬화에서 제외한다.
  • public static int age = 10; : 직렬화 대상에서 제외됨.

 

import java.io.Serializable;

public class Person implements Serializable{
    public static int age = 10;
    private String id;
    private transient String pass;
    private Address addr;

    public Person(String id, String pass, String zipCode, String city) {
        this.id = id;
        this.pass = pass;
        this.addr = new Address(zipCode, city);
    }

    @Override
    public String toString() {
        return "[id=" + id + ", pass=" + pass + ", addr=" + addr + "]" + age;
    }

     class Address implements Serializable {
        private String zipCode;
        private String city;

        public Address(String zipCode, String city) {
            this.zipCode = zipCode;
            this.city = city;
        }

        public String toString() {
            return "Address [zipCode=" + zipCode + ", city=" + city + "]";
        }
    }
}
  • public class Person implements Serializable{, class Address implements Serializable {
    : 직렬화를 위한 Serializable 인터페이스 구현. (내부에 구현해야 할 abstract method가 존재하지 않는 markup interface다.)
    : 클래스의 모든 멤버가 Serializable 인터페이스를 구현해야 함.

 

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectStreamTest {
    public static void main(String[] args) {
        write();
        read();
    }

    private static File target = new File("c:/Temp/objPerson.dat");

    private static void write() {
        Person person = new Person("홍길동2", "pass1234", "123-456", "seoul");
        try(ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(target))){
            output.writeObject(person);
            System.out.println("출력완료");
        }catch(IOException e) {
            e.printStackTrace();
        }
    }

    private static void read() {
        try(ObjectInputStream input = new ObjectInputStream(new FileInputStream(target))){
            Object obj = input.readObject();
            if(obj != null && obj instanceof Person p) {
                System.out.println("복원된 객체");
            }
        }catch(IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

 

 

2) serialVersionUID

 

클래스의 변경 여부를 파악하기 위한 유일한 키.

 

직렬화할 때의 UID와 역 직렬화 할 때의 UID가 다를 경우 예외 발생.

 

직렬화되는 객체에 UID가 설정되지 않았을 경우 컴파일러가 자동 생성됨.

 

멤버 변경으로 인한 컴파일 시마다 변경 → InvalidClassException 초래.

 

import java.io.Serializable;

public class Person implements Serializable{
        public static int age = 10;
        public String name = "sam"; // 추가. 구조 변경
    private String id;
    private transient String pass;
    private Address addr; 

    public Person(String id, String pass, String zipCode, String city) {
        this.id = id;
        this.pass = pass;
        this.addr = new Address(zipCode, city);
    }

    @Override
    public String toString() {
        return "[id=" + id + ", pass=" + pass + ", addr=" + addr + "]" + age;
    }

     class Address implements Serializable {
        private String zipCode;
        private String city;

        public Address(String zipCode, String city) {
            this.zipCode = zipCode;
            this.city = city;
        }

        public String toString() {
            return "Address [zipCode=" + zipCode + ", city=" + city + "]";
        }
    }
}
  • public String name = "sam"; 추가하고 ObjectStreamTest.main()에서 read()만 실행하면…
    : java.io.InvalidClassException 발생.

 

Person의 구조가 수정되었기 때문에 새로 컴파일되면서 UID가 변경되었기 때문에.

 

직렬화되는 객체에 대하여 serialVersionUID 설정 권장.

 

import java.io.Serializable;

public class Person implements Serializable{
    private static final long serialVersionUID = -2801503818029578242L; // 추가
        public static int age = 10;
        public String name = "sam";
    private String id;
    private transient String pass; 
    private Address addr;

    public Person(String id, String pass, String zipCode, String city) {
        this.id = id;
        this.pass = pass;
        this.addr = new Address(zipCode, city);
    }

    @Override
    public String toString() {
        return "[id=" + id + ", pass=" + pass + ", addr=" + addr + "]" + age;
    }

     class Address implements Serializable {
        private String zipCode;
        private String city;

        public Address(String zipCode, String city) {
            this.zipCode = zipCode;
            this.city = city;
        }

        public String toString() {
            return "Address [zipCode=" + zipCode + ", city=" + city + "]";
        }
    }
}
  • private static final long serialVersionUID = -2801503818029578242L;
    : 직렬화되는 객체에 대하여 serialVersionUID 설정.

 


3. Steam API

 

 

Stream (Java Platform SE 8 )

A sequence of elements supporting sequential and parallel aggregate operations. The following example illustrates an aggregate operation using Stream and IntStream: int sum = widgets.stream() .filter(w -> w.getColor() == RED) .mapToInt(w -> w.getWeight())

docs.oracle.com

  • Stream
    : JDK 8부터 추가됨.
    : Collection 데이터를 다루는데 유용한 함수형 프로그래밍 스타일의 API.
    : 컬렉션, 배열 등 데이터 소스에 대한 공통적인 접근 방식 제공.
    : 코드를 간결하고 가독성 있게 만듦.
    : 손쉬운 병렬 처리.

 

 


가. 스트림 생성하기

 

java.util.stream에 위치.

 

 

소스 메서드
Collection default Stream<E> stream()
default Stream parallelStream()
Array public static<T> Stream<T> stream(T[] array)
public static Stream of(T… values)
Array(int) public static IntStream stream(int[] array)
public static IntStream of(int… values)
Array(long) public static LongStream stream(Long[] array)
public static LongStream of(Long… values)
Array(double) public static DoubleStream stream(double[] array)
public static DoubleStream of(double… values)

 

List<String> myList = Arrays.asList("Java", "Stream", "API");
Stream<String> myStream = myList.stream();
  • 컬렉션으로부터 스트림을 생성

 

int[] numbers = {1, 2, 3, 4, 5};
IntStream numberStream = Arrays.stream(numbers);
  • 배열에서도 스트림을 생성

 

이외에도 다양한 데이터 소스로부터 스트림을 생성할 수 있음.

 


나. 단계별 주요 처리 메서드

 

스트림은 중간 연산과 종단 연산으로 구분된다.

  • 중간 연산
    : 스트림을 다른 스트림으로 변환하거나, 요소를 필터링하거나, 변환하는 작업을 수행.
  • 종단 연산
    : 최종 결과를 생성하거나, 컬렉션에 요소를 수집하거나, 스트림을 반복 등으로 마무리 짓는 작업

 

  • 중간 연산
메서드 설명 예제
filter(Predicate<T>) 주어진 조건을 만족하는 요소만을 걸러냄 stream.filter(x -> x > 5)
map(Function<T, R>) 각 요소를 주어진 함수에 따라 변환 stream.map(x -> x * 2)
flatMap(Function<T, Stream<R>>) 각 요소를 스트림으로 변환하고 평면화 stream.flatMap(str -> Arrays.stream(str.split(" ")))
distinct() 중복 요소를 제거 stream.distinct()
sorted() 요소를 정렬 stream.sorted()
peek(Consumer<T>) 각 요소를 소비하면서 추가적인 작업 수행 stream.peek(x -> System.out.println("Processing: " + x))

 

  • 종단 연산
메서드 설명 예제
forEach(Consumer<T>) 각 요소에 대해 주어진 동작을 수행 stream.forEach(x -> System.out.println(x))
count() 요소의 개수를 반환 stream.count()
collect(Collector<T, A, R>) 요소를 수집하여 컬렉션 또는 다른 형식으로 반환 stream.collect(Collectors.toList())
reduce(BinaryOperator<T>) 모든 요소를 하나의 값으로 축소 (최종 결과를 생성) stream.reduce((x, y) -> x + y)
min(Comparator<T>) 최소 요소를 반환 stream.min((x, y) -> Integer.compare(x, y))
max(Comparator<T>) 최대 요소를 반환 stream.max((x, y) -> Integer.compare(x, y))
anyMatch(Predicate<T>) 적어도 한 요소가 주어진 조건을 만족하는지 여부 stream.anyMatch(x -> x > 5)
allMatch(Predicate<T>) 모든 요소가 주어진 조건을 만족하는지 여부 stream.allMatch(x -> x > 5)
noneMatch(Predicate<T>) 모든 요소가 주어진 조건을 만족하지 않는지 여부 stream.noneMatch(x -> x < 0)
findFirst() 첫 번째 요소를 반환 stream.findFirst()
findAny() 임의의 요소를 반환 stream.findAny()
toArray() 스트림을 배열로 변환 stream.toArray()
forEachOrdered(Consumer<T>) 순서가 있는 경우에 각 요소에 대해 주어진 동작을 수행 (병렬 스트림에서 순서를 보장) stream.forEachOrdered(x -> System.out.println(x))

 


다. 스트림 연산 체인

 

double averageLength = myList.stream()
                             .filter(s -> s.startsWith("S"))
                             .mapToInt(String::length)
                             .average()
                             .orElse(0.0);

여러 중간 및 종단 연산을 연결하여 복잡한 데이터 처리 파이프라인을 생성할 수 있다.

 

myList.stream()
      .filter(s -> s.length() > 3)
      .map(String::toUpperCase)
      .sorted()
      .forEach(System.out::println);

스트림에 중간 연산을 적용하여 필터링, 매핑, 정렬 등의 작업을 수행할 수 있다.

 

long count = myList.stream().filter(s -> s.startsWith("S")).count();
System.out.println("Words starting with 'S': " + count);

스트림에 종단 연산을 적용하여 최종 결과를 얻을 수 있다.

 


라. 병렬 처리

myList.parallelStream().forEach(System.out::println);

 


마. 람다 표현식 활용

myList.stream().forEach(s -> System.out.println(s));

람다 표현식을 사용하여 간결한 코드를 작성할 수 있다.

 


'공부 중 > Java' 카테고리의 다른 글

[Java] 람다식  (0) 2024.01.28
[Java] Collection  (0) 2024.01.28
[Java] 예외 처리  (0) 2024.01.28
[Java] String  (0) 2024.01.22
[Java] 인터페이스  (0) 2024.01.22