[필기정리]Day25 - I/O모델 문제

SW/Java

2020. 7. 14. 20:07

Q1. [System.out의 이해와 I/O의 응용]

     이번에는 System.out에 대해서 지금까지 몰랐던 내용을 소개하면서,

     더불어 I/O의 이해도를 점검하기 위한 문제를 제시하고자 한다.

     다음 예제에서는 우리가 지금까지 활용해온 System.out을 대상으로 하는

     각종 메소드의 호출방법을 보이고 있다.

class MyInfo
{
	String info;
	public MyInfo(String info) { this.info = info; }
	public String toString() { return info; }
}

class PrintlnPrintf
{
	public static void main(String[] args)
	{
		MyInfo mInfo = new MyInfo("저는 자바 프로그래머입니다.");
		System.out.println("제 소개를 하겠습니다.");
		System.out.println(mInfo);
		System.out.printf("나이 %d, 몸무게 %dkg입니다.", 24, 72);
	}
}

위의 실행결과로 다음의 출력결과를 확인할 수 있다.

제 소개를 하겠습니다.
저는 자바 프로그래머입니다.
나이 24, 몸무게 72kg입니다.

 

그런데 여기서 사용된 System.outSystem클래스에 다음과 같이 선언되어 있다.

public static final PrintStrem out;

즉, System.outPrintStream의 인스턴스를 참조하고 있다.

그런데 PrintStream 클래스가 직간접적으로 상속하는 클래스 둘은 다음과 같다.

java.io.OutputStream;
java.io.FilterOutputStream // 필터 스트림임을 의미함!

 

즉, PrintStream도 출력 스트림에 연결할 수 있는 필터 스트림이다.

따라서 System.out은 모니터를 의미하는 출력 스트림에 PrintStream의 필터 스트림이 연결된 형태로 볼 수 있다.

그렇다면 이 필터 스트림은 어떠한 특징을 지니고 있는가?

 

이미 println, printf 메소드를 사용해 왔기 때문에 대략적인 특징은 알고 있겠지만,

이는 다음과 같이 정리할 수 있다.

"다양한 형태의 데이터를 문자열의 형태로 출력하거나(println),

문자열의 형태로 조합하여 출력한다(printf)."

이는 정수가 있는 그대로 출력되는 것이 아니라, 문자열의 형태로 변환이 되어서 출력되는 것이다.

원래 콘솔은 문자열만 출력이 가능하다.

따라서 정수나 실수를 출력하려면 문자열로 변환해야 하는데,

이러한 변환을 PrintStream이 대신해 줬던 것이다.

 

그럼 문제를 제시하겠다.

위 예제에서 보이는 출력결과가 파일 println.txt에 문자열의 형태로 저장되도록 예제를 변경해 보자.

그리고 문자열의 형태로 저장이 되었다면,

메모장(notepad)을 통해서 확인이 가능하니 반드시 확인하기 바란다.

 

A.

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

class MyInfo
{
	String info;
	public MyInfo(String info) { this.info = info; }
	public String toString() { return info; }
}

public class PrintlnPrintf
{
	public static void main(String[] args)
	{
		MyInfo mInfo = new MyInfo("저는 자바 프로그래머입니다.");
		FileOutputStream fo = null;
		try {
			fo = new FileOutputStream("println.txt");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		PrintStream out = new PrintStream(fo);
		out.println("제 소개를 하겠습니다.");
		out.println(mInfo);
		out.printf("나이 %d, 몸무게 %dkg입니다.", 24, 72);
		out.close();
	}
}

 

Q2. [PrintWriter와 버퍼링]

     예제 PrintWriterStream.java에서 생성하는 스트림에 BufferedWriter에 의한 버퍼링 기능을 추가해보자.

     그리고 더불어 파일에 문자열 저장 이후에,

     파일에 저장된 문자열을 전부 다시 출력하는형태로 예제를 확장해보자.

import java.io.*;

class PrintWriterStream
{
	public static void main(String[] args) throws IOException
	{
		PrintWriter out=
			new PrintWriter(new FileWriter("printf.txt"));
		
		out.printf("제 나이는 %d살 입니다.", 24);
		out.println("");
		
		out.println("저는 자바가 좋습니다.");
		out.print("특히 I/O 부분에서 많은 매력을 느낍니다.");
		out.close();
	}
}

 

A.

 

- 자바는 유니코드를 기반으로 문자를 표현한다.

  이렇듯 정해진 규칙을 기준으로 문자를 수(number)의 형태로 표현하는 것을 가리켜

  '인코딩(encoding)'이라 한다.

  예를 들어서 다음의 문장도 일종의 인코딩이다.

  문자 A와 문자 B2바이트 유니코드 값으로 변환해서(인코딩 해서),

  각각ch1ch2에 저장하기 때문이다.

char ch1='A';
char ch2='B';

Windows는 문자의 종류에 따라서 다음과 같이 인코딩 한다.

-영문과 특수문자 1바이트 데이터로 인코딩

-한글 2바이트 데이터로 인코딩

 

Windows의 문자표현 방식을 기준으로 문자 A와 문자 B를 저장해 보자.

참고로 Windows에서는 문자 A1바이트 정수 65,

문자 B1바이트 정수 66으로 표현한다(인코딩 한다).

 

public static void main(String[] args) throws IOException
{
	OutputStream out = new FileOutputStream("hyper.txt");
	out.write(65);
	out.write(66);
	out.close();
}

위의 코드에서는 파일의 출력 스트림에 정수 6566을 저장하고 있다.

OutputStreamwrite 메소드는 1바이트 단위로 데이터를 저장하므로,

1바이트 형태로 6566이 저장된다.

(65661바이트로 표현이 가능한 숫자이기 때문에

저장과정에서 상위 3바이트가 잘려나가도 문제가 없다).

 

 

Q3. [문자 스트림이 별도로 존재하는 이유]

     바이트 스트림이 존재함에도 불구하고, 문자 스트림이 별도로 필요한 이유는 무엇인가?

A. 운영체제의 기본 인코딩 방식으로의 인코딩을 자동화하기 위해서

 

Q4. 문자 입력 스트림과 문자 출력 스트림의 최상위 클래스는?

A. Reader, Writer

 

Q5. 문자 단위 파일 입력 스트림과 출력 스트림은?

A. FileReader, FileWriter

 

Q6. Reader의 대표적인 메소드는?

A.

public int read() throws IOException
public abstract int read(char[] cbuf, int off, int len) throws IOException

첫 번째 메소드는 파일로부터 읽어 들인 문자 하나를 반환한다. 

 

반면 두 번째 메소드는 최대 len의 개수만큼 문자를 읽어 들여서, 

cbuf로 전달된 배열의 인덱스 위치 off에서부터 문자를 저장한다. 

그리고 실제로 읽어 들인 문자의 수를 반환한다. 

 

물론 더 이상 읽어 들일 문자가 존재하지 않는다면 두 메소드 모두 -1을 반환한다. 

 

Q7. Writer의 대표적인 메소드는?

public void write(int c) throws IOException
public abstract void write(char[] cbuf, int off, int len) throws IOException

첫 번째 메소드는 파일에 하나의 문자를 저장한다. 

자바 프로그램에서는 문자가 2바이트로 표현되므로, 

인자로 전달된 4바이트 데이터 중에서(매개변수 형이 int이므로) 상위 2바이트는 무시가 된다. 

그렇다고 파일에 2바이트가 저장된다고 판단하면 곤란하다.

 

두 번째 메소드는 cbuf로 전달된 배열의 인덱스 위치 off에서부터

len개의 문자를(최대 len개가 아닌, 그냥 len개이다) 파일에 저장한다.

 

Q8. hyper.txt라는 파일에 문자 'A'와 문자 'B'를 저장하자.

A.

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class JavaIO24 {
	public static void main(String[] args) throws IOException
	{
		OutputStream out = new FileOutputStream("hyper2.txt");
		out.write('A');
		out.write('B');
		out.close();
	}
}


위에 소스코드를 문자스트림을 이용하여 다시 짜면

import java.io.*;

class FileWriterStream
{
	public static void main(String[] args) throws IOException
	{
		char ch1='A';
		char ch2='B';
		
		Writer out=new FileWriter("hyper.txt");
		out.write(ch1);
		out.write(ch2);
		out.close();
	}
}

 

Q9. Q8번에서 출력한 데이터를 읽어들여서 모니터에 출력해보자.

A.

import java.io.*;

class FileReaderStream
{
	public static void main(String[] args) throws IOException
	{
		char[] cbuf=new char[10];
		int readCnt;
		
		Reader in=new FileReader("hyper.txt");
		readCnt=in.read(cbuf, 0, cbuf.length);
		for(int i=0; i<readCnt; i++)
			System.out.println(cbuf[i]);
		
		in.close();
	}
}

 

Q10. 문자 스트림의 입출력 버퍼 필터 스트림은?

A. BufferedReader, BufferedWriter

 

Q11. 버퍼 필터 스트림의 문자열 입출력 기능을 담당하는 클래스와 메소드는?

A.

- 문자열의 입력
BufferedReader 클래스의 메소드

public String readLine() throws IOException


- 문자열의 출력
Writer 클래스의 메소드

public void write(String str) throws IOException

 

Q12. String.txt에다가 다음 내용을 출력하시오.

박지성 - 메시 멈추게 하는데 집중하겠다.
올 시즌은 나에게 있어 최고의 시즌이다.
팀이 승리하는 것을 돕기 위해 최선을다하겠다.
환상적인 결승전이 될 것이다.

기사 제보 및 보도자료
press@goodnews.co.kr

A.

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class Test8 {
	public static void main(String[] args) {
		try {
			BufferedWriter out = new BufferedWriter(new FileWriter("String.txt"));
			out.write("박지성 - 메시 멈추게 하는데 집중하겠다.");
			out.newLine(); // 엔터 같이 한 줄 띄우는 역할
			out.write("올 시즌은 나에게 있어 최고의 시즌이다.");
			out.newLine();
			out.write("팀이 승리하는 것을 돕기 위해 최선을다하겠다.");
			out.newLine();
			out.write("환상적인 결승전이 될 것이다.");
			out.newLine();
			out.newLine();
			out.write("기사 제보 및 보도자료");
			out.newLine();
			out.write("press@goodnews.co.kr");
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 

Q13. Q12번에서 만든 파일을 모니터에 출력해보자.

A.

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class JavaIO35 {
	public static void main(String[] args) {
		String fileName = "String.txt";
		String str = null;
		try
		{
			BufferedReader br = new BufferedReader(new FileReader(fileName));
			while( (str = br.readLine()) != null) // 문자열 읽어오기, readLine은 값이 없으면 null 반환
			{
				System.out.println(str);
			}
			br.close();
			
		}
		catch(FileNotFoundException e)
		{
			System.out.println("파일이 존재하지 않습니다.");
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
	}
}

 

Q14. 문자 필터 스트림 (PrintWriter)이 제공하는 기능

A. 다양한 형태의 데이터를 문자열의 형태로 출력하거나(println),

   문자열의 형태로 조합하여 출력한다(printf).

   PrintStream 은 바이트 스트림이다.

   그럼에도 불구하고 문자 단위로(문자열 단위로) 데이터를 출력한다.

   이러한 이유로 이후의 자바 버전에서는

   PrintStream를 개선시커서 PrintWriter라는 클래스를 정의하였고,

   이는 Writer 클래스를 상속하는 문자 필터 스트림으로 정의하였다.

 

- System.outPrintStream임을 기억하고, 이 이상으로 PrintStream을 활용하지 않는다.

- printf, println 등 문자열 단위의 출력이 필요하다면 반드시 PrintWriter를 사용한다.

 

Q15. PrintWriter를 이용하여 다음 내용을 "printf.txt"에 출력하자.

제 나이는 %d살 입니다. 24
저는 자바가 좋습니다.
특히 I/O 부분에서 많은 매력을 느낍니다.

A.

import java.io.*;

class PrintWriterStream
{
	public static void main(String[] args) throws IOException
	{
		PrintWriter out=
			new PrintWriter(new FileWriter("printf.txt")); // 추가기능을 사용하고 싶을 때
		
		out.printf("제 나이는 %d살 입니다.", 24);
		out.println("");
		
		out.println("저는 자바가 좋습니다.");
		out.print("특히 I/O 부분에서 많은 매력을 느낍니다.");
		out.close();
	}
}

 

Q16. Q15에서 생성하는 스트림에 BufferedWriter에 의한 버퍼링 기능을 추가해보자.

       그리고 더불어서 파일에 문자열 저장 이후에,

       파일에 저장된 문자열을 전부를 다시 출력하는 형태로 예제를 확장해보자.

A.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class JavaIO38 {

	public static void main(String[] args) {
		String fileName = "printf.txt";
		try
		{
			FileWriter fw = new FileWriter(fileName);
			BufferedWriter bWriter = new BufferedWriter(fw);
			PrintWriter writer = new PrintWriter(bWriter);
			
			writer.printf("제 나이는 %d살입니다.", 24);
			writer.println();
			writer.println("저는 자바가 좋습니다.");
			writer.println("특히 I/O 부분에서 많은 매력을 느낍니다.");
			writer.close();
			
			FileReader fr = new FileReader(fileName);
			BufferedReader bReader = new BufferedReader(fr);
			String str = null;
			while( (str = bReader.readLine()) != null )
			{
				System.out.println(str);
			}
			bReader.close();			
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}		
	}
}

 

Q17. 파일의 마지막에는 8바이트 double형 데이터가 저장되어 있다. 

       파일을 열어서 이 부분의 데이터만 읽어서 출력하는 프로그램을 작성해 보자.

       참고로 RandomAccessFile 클래스에 정의되어 있는 다음의 메소드를 활용하면

       보다 쉽게 문제를 해결할 수 있다.

public long length() throws IOException
import java.io.*;

class RandomFileReadWrite
{
	public static void main(String[] args) throws IOException
	{
		RandomAccessFile raf=new RandomAccessFile("data.bin", "rw"); // (파일명, 모드);
		
		System.out.println("Write..............");
		System.out.printf("현재 입출력 위치: %d 바이트 \n", raf.getFilePointer()); // 파일포인터의 위치를 반환해줌 ex) 커서
		
		raf.writeInt(200);
		raf.writeInt(500);
		System.out.printf("현재 입출력 위치: %d 바이트 \n", raf.getFilePointer());
		
		raf.writeDouble(48.65);
		raf.writeDouble(52.24);
		System.out.printf("현재 입출력 위치: %d 바이트 \n\n", raf.getFilePointer());
		
		System.out.println("Read..............");
		raf.seek(0);	// 맨 앞으로 이동
		System.out.printf("현재 입출력 위치: %d 바이트 \n", raf.getFilePointer());
		
		System.out.println(raf.readInt());
		System.out.println(raf.readInt());
		System.out.printf("현재 입출력 위치: %d 바이트 \n", raf.getFilePointer());
		
		System.out.println(raf.readDouble());
		System.out.println(raf.readDouble());
		System.out.printf("현재 입출력 위치: %d 바이트 \n", raf.getFilePointer());
		raf.close();
	}
}

 

A.

import java.io.*;

class RTest
{
	public static void main(String[] args) throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("data.bin", "r");

		raf.seek(raf.length()-8);
		System.out.println(raf.readDouble());
		raf.close();
	}
}

//Tip : RandomAccessFile 클래스

         ㄴ 파일 내 순서대로가 아니라 임의의 위치부터 불러오고 싶을 때 사용한다.

             r : 읽기 전용

             rw : 읽고 쓰는 것까지 가능하다. ex) 한글파일 

 

- [현재 디렉터리와 상위 디렉터리의 정보 추출]

  프로그램이 실행중인 현재 디렉터리의 이름과 현재 디렉터리의 상위 디렉터리 이름을 출력하고,

  각각의 디렉터리에 존재하는 모든 파일 또는 디렉터리의 이름을 출력하는 예제를 작성해 보려고 한다

  (파일인지 디렉터리인지에 대한 정보까지 출력).

 

-현재 디렉터리의 절대경로를 얻는 방법

-> String workingDir = System.getProperty("user.dir");

 

다소 어렵게 들릴 수도 있겠지만, 현재 디렉터리의 이름은 시스템 정보에 해당이 된다.

그리고 시스템 정보를 얻기 위해서는 System.getProperty 메소드를 활용해야 한다.

이 메소드를 통해서 얻을 수 있는 정보들을 조금 정리해 보면 다음과 같다.

 

os.name 운영체제의 이름

java.home 자바가 설치된 경로정보

user.dir 현재 디렉터리의 절대경로

java.version JRE(가상머신을 포함하는 자바의 실행환경)의 버전정보

 

왼편에 존재하는 것이 정보를 얻기 위한 Key이다.

따라서 Key를 문자열의 형태로 getProperty 메소드의 인자로 전달하면,

Key에 해당하는 정보를 문자열의 형태로 얻을 수 있다. 

 

Q18. 인스턴스의 입출력에 사용되는 ( ) 클래스와 ( ) 클래스는 사실상 바이트 스트림에 속한다. 

       그러나 일반적으로 이 둘은 '오브젝트 스트림'으로 구분 짓는 것이 보통이다.

 

A. 인스턴스의 입출력에 사용되는 ( ObjectInputStream ) 클래스와 ( ObjectOutputStream ) 클래스는

    사실상 바이트 스트림에 속한다.

    그러나 일반적으로 이 둘은 '오브젝트 스트림'으로 구분 짓는 것이 보통이다.

    이 둘은 사용방법이 필터 스트림과 매우 흡사하지만,

    그럼에도 불구하고 기술적으로 필터 스트림으로 분류하지 않는다.

    필터 스트림이 상속하는 클래스를 상속하지 않기 때문이다.

 

Q19. 인스턴스의 저장을 위해서는 ObjectOutputStream 클래스에 정의되어 있는 

       다음 메소드를 호출하면 된다.

A.

public final void writeObject(Object obj) throws IOException

 

Q20. 인스턴스의 복원을 위해서는 ObjectInputStream 클래스에 정의되어 있는 

      다음 메소드를 호출하면 된다.

A.

public final Object readObject() throws IOException, ClassNotFoundException

 

Q20. 입출력의 대상이 되는 인스턴스의 클래스는 다음 인터페이스를 구현하거나, 

       다음 인터페이스를 구현하는 클래스를 상속해야 한다.

       즉, 직간접적으로 다음 인터페이스를 구현해야 한다.

 

     인스턴스가 파일에 저장되는 과정을 가리켜 (  )라 하고,

     파일로부터 인스턴스가 복원되는 과정을 가리켜 (  )라 하는데,

     Serializable 인터페이스는 "이 클래스의 인스턴스는 직렬화를 해도 괜찮습니다." 의 표시를 목적으로 사용된다.

     때문에 추가로 정의해야 할 메소드도 존재하지 않는다.  

 

A.

java.io.Serializable

인스턴스가 파일에 저장되는 과정을 가리켜 ( 직렬화(serializable) )라 하고,

파일로부터 인스턴스가 복원되는 과정을 가리켜 ( 역직렬화(deserializable) )라 하는데,

Serializable 인터페이스는 "이 클래스의 인스턴스는 직렬화를 해도 괜찮습니다." 의 표시를 목적으로 사용된다.

때문에 추가로 정의해야 할 메소드도 존재하지 않는다. 

//Tip. 파일에 제한되지는 않는다.

        직렬화, 역직렬화라는 단어는 파일에 제한적인 표현이 아니다.

        파일이 아니더라도, 다양한 입출력 대상을 통해서 인스턴스의 이동이 이뤄지는 상황에서는

        직렬화, 그리고 역직렬화가 발생한다고 표현한다.

 

Q21. 문제43 폴더에 있는 Circle 클래스를 이용하여 
Circle 인스턴스 1, 1, 2.4와
Circle 인스턴스 2, 2, 4.8을 출력하자.
그리고 "String implements Serializable"을 출력하자.

그리고 이 인스턴스들을 입력받아 그 정보값들을 출력하도록 하자.

class Circle 
{
	int xPos;
	int yPos;
	double rad;
	
	public Circle(int x, int y, double r)
	{
		xPos=x;
		yPos=y;
		rad=r;
	}
	public void showCircleInfo()
	{
		System.out.printf("[%d, %d] \n", xPos, yPos);
		System.out.println("rad: "+rad);
	}
}

인스턴스의 입출력이 리소스 소모가 많은 작업이다. 

때문에 과도한 직렬화는 성능에 영향을 줄 수 있다. 

하지만 빈번히, 연속적으로 입출력이 발생하는 상황이 아니라면, 

그리고 시스템에 크게 영향을 주지 않는 상황이라면, 

직렬화의 적절한 활용은 다양한 상황에서 프로그래머의 수고를 덜어주기도 한다.

 

A.

import java.io.*;

class Circle implements Serializable
{
	int xPos;
	int yPos;
	double rad;
	
	public Circle(int x, int y, double r)
	{
		xPos=x;
		yPos=y;
		rad=r;
	}
	public void showCirlceInfo()
	{
		System.out.printf("[%d, %d] \n", xPos, yPos);
		System.out.println("rad: "+rad);
	}
}
class ObjectSerializable
{
	public static void main(String[] args) 
					throws IOException, ClassNotFoundException
	{
		/* 인스턴스 저장 */
		ObjectOutputStream out= 
			new ObjectOutputStream(new FileOutputStream("Object.ser"));
		
		out.writeObject(new Circle(1, 1, 2.4));
		out.writeObject(new Circle(2, 2, 4.8));	
		out.writeObject(new String("String implements Serializable"));
		out.close();
		
		/* 인스턴스 복원 */
		ObjectInputStream in=
			new ObjectInputStream(new FileInputStream("Object.ser"));
		Circle cl1=(Circle)in.readObject();
		Circle cl2=(Circle)in.readObject();
		String message=(String)in.readObject();
		in.close();
		
		/* 복원된 정보 출력 */
		cl1.showCirlceInfo();
		cl2.showCirlceInfo();
		System.out.println(message);
	}
}

 

Q22. Q20번의 Circle 클래스에 있는 인스턴스 변수 xPos와 yPos를 Point 클래스로 만들자.

"직렬화되는 인스턴스의 멤버 변수가 참조하는 인스턴스도 Serializable 인터페이스를 구현한다면, 

  이 역시도 함께 묶여서 직렬화된다."

 

A. 

import java.io.*;

class Point implements Serializable
{
	int x, y;
	
	public Point(int x, int y)
	{
		this.x=x;
		this.y=y;
	}
}

class Circle implements Serializable
{
	Point p;
	double rad;
	
	public Circle(int x, int y, double r)
	{
		p=new Point(x, y);
		rad=r;
	}
	public void showCirlceInfo()
	{
		System.out.printf("[%d, %d] \n", p.x, p.y);
		System.out.println("rad: "+rad);
	}
}
class SerializableInstMember
{
	public static void main(String[] args) 
		throws IOException, ClassNotFoundException
	{
		/* 인스턴스 저장 */
		ObjectOutputStream out= 
			new ObjectOutputStream(new FileOutputStream("Object.ser"));
		
		out.writeObject(new Circle(1, 1, 2.4));
		out.writeObject(new Circle(2, 2, 4.8));	
		out.writeObject(new String("String implements Serializable"));
		out.close();
		
		/* 인스턴스 복원 */
		ObjectInputStream in=
			new ObjectInputStream(new FileInputStream("Object.ser"));
		Circle cl1=(Circle)in.readObject();
		Circle cl2=(Circle)in.readObject();
		String message=(String)in.readObject();
		in.close();
		
		/* 복원된 정보 출력 */
		cl1.showCirlceInfo();
		cl2.showCirlceInfo();
		System.out.println(message);
	}
}

 

Q23. 직렬화의 대상에서 제외시키겠다면, ( )

       Personalnfo 클래스의 String secretInfo와 int secretNum을 직렬화 대상에서 제외시키고

       이것을 파일로 출력한 후 다시 입력받아 출력해보자.

import java.io.*;

class PersonalInfo implements Serializable
{
	String name;
	transient String secretInfo;
	
	int age;
	transient int secretNum;

	public PersonalInfo(String name, String sInfo, int age, int sNum)
	{
		this.name=name;
		secretInfo=sInfo;
		this.age=age;
		secretNum=sNum;
	}
	public void showCirlceInfo()
	{
		System.out.println("name: "+name);
		System.out.println("secret info: "+secretInfo);
		System.out.println("age: "+age);
		System.out.println("secret num: "+secretNum);
		System.out.println("");
	}
}

A. 직렬화의 대상에서 제외시키겠다면, ( transient! )

class TransientMembers
{
	public static void main(String[] args) 
		throws IOException, ClassNotFoundException
	{
		/* 인스턴스 저장 */
		ObjectOutputStream out= 
			new ObjectOutputStream(new FileOutputStream("Personal.ser"));
		
		PersonalInfo info=new PersonalInfo("John", "baby", 3, 42);
		info.showCirlceInfo();
		out.writeObject(info);
		out.close();
		
		/* 인스턴스 복원 */
		ObjectInputStream in=
			new ObjectInputStream(new FileInputStream("Personal.ser"));
		
		PersonalInfo recovInfo=(PersonalInfo)in.readObject();
		in.close();
		
		/* 복원된 정보 출력 */
		recovInfo.showCirlceInfo();
	}
}

 

Q24. transient로 선언된 변수는 인스턴스 복원 시 어떠한 값으로 초기화되는가?

A. transient로 선언된 참조변수에는 null이, 그리고 int형 변수에는 0이 저장됨을 알수 있다. 

   그런데 이는 인스턴스 변수를 별도의 값으로 초기화하지 않을 경우에 자동으로 삽입되는 디폴트 값들이다.

   이로써 transient로 선언된 변수들은 복원의 과정에서 별도의 초기화가 이뤄지지 않음을 확인하였다.

 

Q25. 다음과 같은 특징을 갖는 스트림 클래스는? 

- 입력과 출력이 동시에 이뤄질 수 있다.
- 입출력 위치를 임의로 변경할 수 있다.
- 파일을 대상으로만 존재하는 스트림이다.

A. RandomAccessFile

사실 스트림이라는 표현에는 "한쪽 방향으로만 형성되는 데이터의 흐름"이라는 의미가 담겨있다. 

즉, 스트림이라 부르기 위해서는 데이터의 입력 및 출력이 순차적이어야 하고,

입력만 가능하거나 출력만 가능해야 한다.

그래서 엄밀히 말하면 RandomAccessFile 클래스는 스트림 클래스가 아니다.

 

Q26. Q25에서 말하는 클래스의 대표적인 입력 메소드는?

A.

public int read() throws IOException
public int read(byte[] b, int off, int len) throws IOException
public final int readInt() throws IOException
public final double readDouble() throws IOException

 

Q27. Q25에서 말하는 클래스의 대표적인 출력 메소드는?

A.

public void write(int b) throws IOException
public void write(byte[] b, int off, int len) throws IOException
public final void writeInt(int v) throws IOException
public final void writeDouble(double v) throws IOException

 

Q28. Q25에서 말하는 클래스의 현재의 입출력 위치를 확인하거나 변경할 수 있는 메소드는?

A.

public long getFilePointer() throws IOException
public void seek(long pos) throws IOException

위치 정보는 getFilePointer 메소드의 호출을 통해서 확인이 가능하다. 

반면 seek 메소드의 호출을 통해서 입출력의 위치도 변경할 수도 있다.

 

Q29. Q25에서 말하는 클래스의 생성자에 대해서 설명하시오.

A.

public RandowmAccessFile(String name, String mode) throws FileNotFoundException

이 생성자의 첫 번째 인자를 통해서는 파일의 이름을 전달한다.

그리고 두 번째 인자를 통해서는 파일의 용도 정보를 전달하는데, 전달할 수 있는 정보의 종류는 다음과 같다.

"r" 읽기 위한 용도 
"rw" 읽고 쓰기 위한 용도


즉, 두 번째 인자로 "r"을 전달하면 '읽기'만 가능하다.

때문에 생성자의 첫 번째 인자로 전달한 이름의 파일이 존재하지 않으면 예외가 발생한다.

반면 "rw"를 전달하면 '읽기'와 '쓰기'가 동시에 가능하다.

뿐만 아니라 파일이 존재하지 않으면 새로운 이름의 파일을 생성하기도 한다.

 

Q30. data.bin 파일을 읽고 쓰기 모드로 열고

파일의 위치를 출력

정수 200 파일에 출력

정수 500 파일에 출력

파일의 위치를 출력

실수 48.65를 파일에 출력

실수 52.24를 파일에 출력

파일의 위치를 출력

위치를 맨 처음으로 이동

파일의 위치를 출력

파일로부터 정수 200500을 입력받아 모니터에 출력

파일의 위치를 출력

파일로부터 실수 48.6552.24를 입력받아 입력받아 출력

파일의 위치를 출력

 

A. 

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class JavaIO52 {

	public static void main(String[] args) {
		String fileName = "data.bin";
		RandomAccessFile rf = null;
		int rInt1 = 0, rInt2=0;
		double rDbl1 = 0, rDbl2 = 0;
		try
		{
			rf = new RandomAccessFile(fileName, "rw");
			System.out.println("Write..............");
			System.out.println("현재 포인터의 위치 " + rf.getFilePointer());
			rf.writeInt(200);
			rf.writeInt(500);
			System.out.println("현재 포인터의 위치 " + rf.getFilePointer());
			rf.writeDouble(48.65);
			rf.writeDouble(52.24);
			System.out.println("현재 포인터의 위치 " + rf.getFilePointer());
			System.out.println("Read..............");
			rf.seek(0);
			System.out.println("현재 포인터의 위치 " + rf.getFilePointer());
			rInt1 = rf.readInt();
			rInt2 = rf.readInt();
			System.out.println(rInt1 + " " + rInt2);
			System.out.println("현재 포인터의 위치 " + rf.getFilePointer());
			rDbl1 = rf.readDouble();
			rDbl2 = rf.readDouble();
			System.out.println(rDbl1 + " " + rDbl2);
			System.out.println("현재 포인터의 위치 " + rf.getFilePointer());
			rf.close();
		}
		catch(FileNotFoundException e)
		{
			System.out.println("파일이 존재하지 않습니다.");
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
	}
}

 

Q31. [마지막에 저장된 데이터 Read!]
      Q30에서 생성한 파일의 마지막에는 8바이트 double형 데이터가 저장되어 있다. 
      파일을 열어서 이 부분의 데이터만 읽어서 출력하는 프로그램을 작성해 보자.
      참고로 RandomAccessFile 클래스에 정의되어 있는 다음의 메소드를 활용하면 보다 쉽게 문제를 해결할 수 있다.

public long length() throws IOException

A.

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class JavaIO53 {

	public static void main(String[] args) {
		String fileName = "data.bin";
		try
		{
			RandomAccessFile rf = new RandomAccessFile(fileName, "r");
			rf.seek(rf.length()-8);
			double num1 = rf.readDouble();
			System.out.println(num1);
		}
		catch(FileNotFoundException e)
		{
			System.out.println("파일이 없습니다.");
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
	}
}

 

Q32. 파일 및 디렉터리 관련 연산을 위해서 자바는 (  )이라는 이름의 클래스를 별도로 제공하고 있다.

- 디렉터리의 생성, 소멸
- 파일의 소멸
- 디렉터리 내에 존재하는 파일이름 출력

A. 파일 및 디렉터리 관련 연산을 위해서 자바는 ( File )이라는 이름의 클래스를 별도로 제공하고 있다.

 

Q33. 우선 문제를 풀기전에 직접 cMyJava라는 폴더와 그 안에 my.bin이라는 파일을 만들어 놓자.

      밑에는 소스로 구현하자.

      MyJava 폴더안에 my.bin 이라는 파일이 없으면 "원본 파일이 준비되어 있지 않습니다." 라고 출력한다.

      cYourJava라는 폴더를 만든다.

      YourJava 안에 my.bin 파일을 복사해 넣는다.

      그리고 파일이 복사에 성공하면

      "파일 이동에 성공하였습니다."

      실패하면 "파일 이동에 실패하였습니다." 라고 출력한다.

A.

import java.io.File;

public class JavaIO55 {

	public static void main(String[] args) {
		File myFile=new File("C:\\MyJava\\my.bin");
		if(myFile.exists()==false)
		{
			System.out.println("원본 파일이 준비되어 있지 않습니다.");
			return;
		}
		
		File reDir=new File("C:\\YourJava");
		reDir.mkdir();
		File reFile=new File(reDir, "my.bin");
		myFile.renameTo(reFile);
		if(reFile.exists()==true)
			System.out.println("파일 이동에 성공하였습니다.");
		else
			System.out.println("파일 이동에 실패하였습니다.");
	}
}

 

Q34. 위의 소스코드를 운영체제에 상관없이 실행될수 있도록 수정하자.

A. separator를 사용 시 각 운영체제에 맞게 알아서 구분자가 적용된다.

import java.io.File;

public class JavaIO56 {
	private void mai() {
		File myFile=
				new File("C:"+File.separator+"MyJava"+File.separator+"my.bin");
		if(myFile.exists()==false)
		{
			System.out.println("원본 파일이 준비되어 있지 않습니다.");
			return;
		}

		File reDir=new File("C:"+File.separator+"YourJava");
		reDir.mkdir();
		File reFile=new File(reDir, "my.bin");
		myFile.renameTo(reFile);
		if(reFile.exists()==true)
			System.out.println("파일 이동에 성공하였습니다.");
		else
			System.out.println("파일 이동에 실패하였습니다.");
	}
}

 

Q35. AAA 라는 폴더를 경로로 잡고 절대경로를 출력하자
       AAA라는 폴더 밑에 BBB라는 폴더를 잡고 그것의 절대경로를 출력하자. 

A.

import java.io.File;

public class JavaIO57 {

	public static void main(String[] args) {
		File curDir=new File("AAA"); // 파일에 있는 정보를 curDir에 저장하는 것
		System.out.println(curDir.getAbsolutePath()); // 절대경로 가지고 올 경우 사용
		
		File upperDir=new File("AAA"+File.separator+"BBB");
		System.out.println(upperDir.getAbsolutePath());
	}
}

 

Q36. MyDir이라는 폴더를 경로로 잡고 그 경로에 있는 리스트를 출력하자.
       파일이면 파일이라고 출력하고 디렉토리면 디렉토리라고 출력하도록 하자.
       그리고 나서 다음 정보를 출력하자
       현재위치 절대경로
       OS 이름
       JAVA HOME
       JAVA VERSION

A.

import java.io.File;

public class JavaIO58 {

	public static void main(String[] args) {
		File myDir=new File("MyDir"); // 디렉토리 정보를 가지고 옴
		File[] list=myDir.listFiles(); // 폴더 내 자료들의 리스트를 배열화함
		if(list == null)
		{
			System.out.println("디렉토리가 존재하지 않음.");
			myDir.mkdir(); // 해당 폴더 생성
		}
		else
		{
			for(int i=0; i<list.length; i++)
			{
				System.out.print(list[i].getName());
				if(list[i].isDirectory()) // 디렉토리인지 단순 파일인지 물어보는 메소드
					System.out.println("\t\tDIR");
				else
					System.out.println("\t\tFILE");
			}			
		}
		
		System.out.println("시스템 정보");
		String workingDir = System.getProperty("user.dir"); // 자바 파일이 존재하는 현재 디렉토리 출력
		System.out.println("현재위치 " + workingDir);
		String osName = System.getProperty("os.name"); // 운영체제 이름 출력
		System.out.println("os " + osName);
		String javaHome = System.getProperty("java.home"); // 자바가 설치된 경로
		System.out.println("JavaHome " + javaHome );
		String javaVersion = System.getProperty("java.version"); // 자바 버전
		System.out.println("javaVersion " + javaVersion);
	}
}

 

Q37. [현재 디렉터리와 상위 디렉터리의 정보 추출]

프로그램이 실행중인 현재 디렉터리의 이름과 현재 디렉터리의 상위 디렉터리 이름을 출력하고,

각각의 디렉터리에 존재하는 모든 파일 또는 디렉터리의 이름을 출력하는 예제를 작성해 보려고 한다

(파일인지 디렉터리인지에 대한 정보까지 출력).

- 현재 디렉터리의 절대경로를 얻는 방법

-> String workingDir = System.getProperty("user.dir");

 

다소 어렵게 들릴 수도 있겠지만, 현재 디렉터리의 이름은 시스템 정보에 해당이 된다.

그리고 시스템 정보를 얻기 위해서는 System.getProperty 메소드를 활용해야 한다.

이 메소드를 통해서 얻을 수 있는 정보들을 조금 정리해 보면 다음과 같다.

 

os.name 운영체제의 이름

java.home 자바가 설치된 경로정보

user.dir 현재 디렉터리의 절대경로

java.version JRE(가상머신을 포함하는 자바의 실행환경)의 버전정보

 

왼편에 존재하는 것이 정보를 얻기 위한 Key이다.

따라서 Key를 문자열의 형태로 getProperty 메소드의 인자로 전달하면,

Key에 해당하는 정보를 문자열의 형태로 얻을 수 있다.

 

A.

import java.io.File;

public class JavaIO59 {

	public static void showDirList(File[] list)
	{
		for(int i=0;i<list.length;i++)
		{
			System.out.print(list[i].getName());
			if(list[i].isDirectory())
				System.out.println("\t \t DIR");
			else
				System.out.println("\t \t FILE");
		}
	}
	
	public static void main(String[] args)
	{
		String workingDir = System.getProperty("user.dir");
		System.out.println(workingDir);

		File currentDir = new File(workingDir);
		System.out.println("현재 디렉터리 : " + currentDir.getName());
		showDirList(currentDir.listFiles());


		File upperDir = currentDir.getParentFile();
		System.out.println("상위 디렉터리 : " + upperDir.getName());
		showDirList(upperDir.listFiles());		
	}
}

 

Q38. File 인스턴스를 인자로 전달받는, 파일 관련 스트림의 생성자들은?

A.

public FileInputStream(File file) throws FileNotFoundException // FileInputStream의 생성자 
public FileOutputStream(File file) throws FileNotFoundException // FileOutputStream의 생성자  
public FileReader(File file) throws FileNotFoundException // FileReader의 생성자 
public FileWriter(File file) throws FileNotFoundException // FrileWriter의 생성자

 

Q39. 만약에 파일의 유효성 검사 등 파일과 관련된 컨트롤이 더불어 필요하다면,

       File 인스턴스를 먼저 생성해서 파일의 유효성 검사를 마친 다음에

       위 유형의 생성자를 통해서 스트림을 형성하는 것이 낫다.

A.

즉, 다음의 형태로 스트림을 형성하는 것이 낫다.

File inFile = new File("data.bin");
if(inFile.exists() == false) // 해당 파일의 존재 유무 묻는 것
{
	// 데이터를 읽어 들일 대상 파일이 존재하지 않음에 대한 적절한 처리
}
InputStream in = new FileInputStream(inFile);

 

728x90