[자바7]입출력

★ 자바7은 네트워크 입출력 버전 2 API(NIO.2)를 새로 추가해서
   이제 폴더 모니터링, OS 의존적인 메서드 접근, 확장 가능한 비동기 네트워크 소켓 생성 기능이 가능해짐. <- import java.nio.file.*
★ I/O 스트림은 자바 I/O의 거의 모든 것의 기반이 됨.
   하나의 '스트림'은 데이터의 입출력을 나타냄(흐르는 강물에 비유)
   또, 이 스트림들은 서로 연결 될 수도 있는데 이것을 '데커레이터 패턴'이라 부름(이 데터레이터 패턴 덕분에 매우 다양한 효과를 얻을 수 있음 = 모든 종류의 입력을 취하고, 출력을 생산하는 스트림을 생성할 수 있고, 다른 모든 스트림들이 서로 엮일수 있음)

   입력 스트림 - 파일 입력 스트림   ---> 파일 출력 스트림과 상응
       - 네트워크 소켓 입력 스트림 : 네트워크 소켓 출력 스트림과 상응
   ※ 입/출력 스트림들은 다양하게 엮어 사용 가능(예: 네트워크 입력 스트림[유입 데이터=소스] -> 파일 출력 데이터[유출 데이터=싱크])
   
   이런 스트림 이외에 BufferInputStream이 있음. 이 스트림은 조각(chunk)단위로 데이터를 읽을 수 있음.(바이트 단위로 읽는 것보다 효율적)
   DataOutputStream은 자바 원형 객체를 출력 스트림으로 쓸 수 있음.
   ※ 가장 유용한 스트림 중 하나인 ObjectInputStream과 ObjectOutputStream은 객체를 직렬화/역직렬화할 때 사용.

   cf. 직렬화 : (자바)시스템 내부에서 사용되는 객체 혹은 데이터를 외부(시스템)에서도 사용할 수 있도록 '바이트'형태로 데이터를 변환하는 기술 = 객체를 직렬화하면 전송 가능한 형태로 만드는 것을 의미함.
       역직렬화 : 바이트로 변환된 데이터를 다시 객체로 변환하는 기술 = 직렬화된 데이터 등을 역으로 직렬화하여 다시 '객체'의 형태로 만드는 것
=> 직렬화에서 제공하는 기능은 객체를 얻어 그 객체의 바이트 표현을 생성하는 것임.

1. 자바 객체 직렬화하기
- 내장된 직렬화 메커니즘 사용. ObjectOutputStream 클래스를 사용하여 그 메커니즘에 접근

2. 자바 객체를 더 효율적으로 직렬화하기 : 클래스를 직렬화하고 싶은데, 기본적으로 제공하는 내장 직렬화 메서드의 생성 결과보다 크기가 더 작거나 효율적인 산출물을 원함.
- 'Externalizable 인터페이스'를 구현하는 객체를 한 개 생성하고, 자바 가상 머신(JVM)이 그 객체에서 제공하는 readExternal/writeExternal 메서드를 사용해 사용자 맞춤 직렬화/역직렬화 메커니즘을 수행.
- Serializable 인터페이스 대신 Externalizable 구현이 더 효율적인 이유는 자바의 기본 직렬화가 비효율적이기 때문.
  자바 직렬화 프렝미워크는 모든 객체를 직렬화 대상으로 보기 때문에 null 또는 empty 같은 기본값을 갖는 객체까지도 직렬화하려 함.
  그러나 Externalizable 인터페이스를 구현하면 더 매끄럽게 클래스 직렬화를 제어할 수 있음.
  cf. Externalizable 인터페이스를 구현한 클래스를 작성하려면 그 클래스는 전닭받는 인자가 없는 비어 있는 생성자 필요.
3. 자바 객체를 XML로 직렬화하기
- XMLEncoder 객체를 사용해 프로그램 설정 정보를 저장하고, 설정 정보를 settings.xml 파일에 옮겨 쓰는 Settings 객체를 인코드 함.
  XMLDecider는 settings.xml 파일ㅇ르 스트림으로 읽고 Settings 객체로 디코드함.
예시)
/*자바 객체를 xml로 직렬화*/
ProgramSettings settings = new ProgramSettings( new Point(10, 10), new Dimension(300, 200), Color.blue, "The Title of thee application" );
try{
//인코딩
FileSystem fileSystem = FileSystems.getDefault();
FileOutputStream fos = new FileOutputStream("settings.xml");
XMLEncoder encoder = new XMLEncoder(fos);
encoder.setExceptionListener(new ExceptionListener() {
@Override
public void exceptionThrown(Exception arg0) {
// TODO Auto-generated method stub
System.out.println("exception :" + arg0.toString());
}
});
encoder.writeObject(settings);
encoder.close();
fos.close();
//디코딩
FileInputStream fis = new FileInputStream("settings.xml");
XMLDecoder decoder = new XMLDecoder(fis);
ProgramSettings decodedSettings = (ProgramSettings)decoder.readObject();
System.out.println("is same? :" + settings.equals(decodedSettings));
fis.close();
}catch(IOException e){
e.printStackTrace();
}

4. 네트워크를 통해 직렬화 객체를 전송하는 소켓 연결 생성하기
- NIO 2.0의 논블록킹 소켓 기능 사용.
5. 자바 실행 경로 얻기
- System 클래스의 getProperry 메서드를 수행 : 자바 프로그램이 시작될 떄 JRE는 user.dir 시스템 속성 값을 JRE가 수행된 위치의 레코드로 갱신함.
6. 파일 복사하기 : 폴더에 있는 파일을 다른 폴더로 복사
- 디폴터 FileSystem에서 파일이나 폴더에 대한 from과 to 경로를 생성. Files.copy 정적 메서드를 이용해 앞에서 생성한 경로로 파일을 복사
cf. FileSystem.getDefaults()는 파일 처리가 가능하 추상화된 시스템을 반환
7. 파일 이동하기 
- Files.move() 정적 메서드 실행
cf. import java.nio.file.Files;
 Files 객체가 제공하는 다른 메서드들
- Delete(path) : 파일(또는 빈 폴더)을 삭제
- Exists(path) : 파일 또는 폴더가 존재하는지 확인
- isDirectory(path) : 생성된 경로가 디렉터리를 가리키는 확인
- isExecutable(path) : 실행 파일인지 확인
- isHidden(path) : 해당 파일이 그 운영체제에서 숨긴 파일인지 확인
8. 디렉터리 만들기
- Files.createDirectory() 정적 메서드 : 명시한 경로에 디렉터리를 생성. 다른 설정을 하지 않는다면 생성된 디렉토리는 기본허가를 상속함.
- 리눅스 운영체제에서 PosixFilePermission() 메서드를 수행 : 폴더 속성 지정 가능
9. 디레터리 파일을 순회하기
- NIO.2 를 사용해 FileVisitor를 생성하고 폴더를 방문하기
- SimpleFileVisitor 클래스
예시)
        FileVisitor<Path> myFileVisitor = new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                System.out.println("Visited File: "+file.toString());
                return FileVisitResult.CONTINUE;
            }
        };
        
        FileSystem fileSystem = FileSystems.getDefault();
        Path directory = fileSystem.getPath(".");
        try{
         Files.walkFileTree(directory, myFileVisitor);
        }catch(IOException e){
         e.printStackTrace();
        }
    
10. 파일 메타데이터를 조회하고 설정하기
= 예시)
            System.out.println("File Size:"+Files.size(path));
            System.out.println("Is Directory:"+Files.isDirectory(path));
            System.out.println("Is Regular File:"+Files.isRegularFile(path));
            System.out.println("Is Symbolic Link:"+Files.isSymbolicLink(path));
            System.out.println("Is Hidden:"+Files.isHidden(path));
            System.out.println("Last Modified Time:"+Files.getLastModifiedTime(path));
            System.out.println("Owner:"+Files.getOwner(path));

            // Specific attribute views.
            DosFileAttributeView view = Files.getFileAttributeView(path, DosFileAttributeView.class);
            System.out.println("\nDOS File Attributes\n------------------------------------\n");
            System.out.println("Archive  :"+view.readAttributes().isArchive());
            System.out.println("Hidden   :"+view.readAttributes().isHidden());
            System.out.println("Read-only:"+view.readAttributes().isReadOnly());
            System.out.println("System   :"+view.readAttributes().isSystem());
            
            view.setArchive(false);
11. 디렉터리 변경 감시하기
-WatchService를 사용해 폴더에서 발생하는이벤트 알림을 구독할 수 있음, 폴(poll) 메커니즘이 동작.
예시)
System.out.println("Watch Event, press q<Enter> to exit");
FileSystem fileSystem = FileSystems.getDefault();
WatchService service = fileSystem.newWatchService();
Path path = fileSystem.getPath(".");
System.out.println(path.toAbsolutePath().toString());
path.register(service, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
boolean shouldContinue = true;
while (shouldContinue) {
WatchKey key = service.poll(250, TimeUnit.MILLISECONDS);
//프로그램을 중지하는 코드
while (System.in.available() > 0) {
int readChar = System.in.read();
if((readChar == 'q') || (readChar == 'Q')){
shouldContinue = false;
break;
}
}
if(key == null) continue;
for(WatchEvent<?> event : key.pollEvents()){
if(event.kind() == StandardWatchEventKinds.OVERFLOW) continue;
WatchEvent<Path> ev = (WatchEvent<Path>)event;
Path filename = ev.context();
System.out.println("Event detected :" + filename.toString() + " " + ev.kind());
}
boolean valid = key.reset();
if(!valid){
break;
}
}

12. 속성 파일 읽기
-Properties 객체를 사용하여 properties.conf파일에 저장된 속성을 읽어옴.
 Properties 클래스는 프로그램 속성을 관리함. 이 클래스를 사용하면 누군가 myapp.conf 파일을 편집하는 것과 같은 외부 파일 수정이나 Properties.store()메서드를
 호출해 속성을 수정하는 것을 관리할 수 있음.
 Properties 객체는 메모리에 올려진 파일을 가지고 초기화가 가능한데, 실제 파일이 없어도 초기화가 가능함.
 이 객체는 속성 이름과 값을 [name]=[value] 형식으로 표현하는 파일을 읽을 수 있음
 속성 조회할 떄는 getProperties(String, String) 메서드를 호출, 
13. 압축 파일 풀기
- java.util.zip 패키지를 사용하면 .zip파일을 열고, .zip 파일 내부에 있는 항목들을 순회할 수 있음. ZipFile 객체 생성

댓글