자바

C++에서도 배웠지만 프로그램이 파일을 읽거나 쓰는 과정은 생각보다 복잡하다.
파일을 읽는 객체나 쓰는 객체를 별도로 생성해 메소드를 사용해 파일을 읽고 쓰는 법을 알아보자.

자바 프로그램에서는 입력되고 출력되는 모든 데이터를 스트림 형태로 주고 받는다.
스트림은 방향에 따라 프로그램 입장에서 들어오는 스트림을 입력 스트림, 나가는 스트림을 출력 스트림이라 한다.
또한 사람이 이해할 수 있는 문자로 구성된 스트림을 문자 스트림이라고 하고, 프로그램이 사용할 수 있는 데이터(바이트)로 구성된 스트림을 바이트 스트림이라 한다.

이런 스트림을 다루는 클래스가 JDK에 선언되어 있는데,
문자 스트림은 입력은 FileReader, 출력은 FileWriter 클래스,
바이트 스트림은 입력 - FileInputStream,출력은 FileOutputStream이다.

이 4개의 클래스는 모두 java.io패키지에 있다.
이 클래스들은 파일 입출력에 필요한 최소한의 기능만 가지고 있기 때문에, 패키지에 있는 다른 보조 클래스들과 함께 사용해야 좀 더 효율적으로 파일입출력을 할 수 있다.

파일을 열고 닫는 과정은 자바에서 전부 하는 것이 아니고, 필요한 시스템 자원을 확보하고 파일이 실제로 있는지 확인하는 과정을 OS가 해주게 된다.
단 이러한 열고 닫는 메소드는 위 4가지 클래스이 메소드에 탑재되어 있다.

다음 예제로 좀 더 알아보자.

 


import java.io.*; // 파일 입출력 클래스 객체를 만들기 위해
class ReaderExample 
{
 public static void main(String[] args) 
 {
  FileReader reader = null; // 파일 읽기 객체 변수 reader생성
  try
  {
   reader = new FileReader("lyric.txt"); // 객체 생성 - 인자로 파일 이름을 받음.
   while(true)
   {
    int data = reader.read(); // 파라미터를 받지 않는 read 메소드 - 한 글자를 읽어 정수형으로 리턴함.
    if (data == -1) // eof(-1)이 되면
     break; // 루프문 탈출
     char ch = (char) data; // read로 읽어 저장한 int 형 data를 캐릭터형으로 캐스팅해 저장함.
     System.out.print(ch); 
   }
   
  }
  catch (FileNotFoundException fnfe) // FileReader의 생성자가 발생하는 익셉션
  {
   System.out.println("파일이 존재하지 않습니다");
  }
  catch (IOException ioe) // FileReader의 read,close 메소드의 익셉션.
  {
   System.out.println("파일을 읽을 수 없습니다");
  }

  finally {
  try
  {
   reader.close();

  }
  catch (Exception e)
  {
  }

  }
 }
}

 

일단 파일 입출력 클래스를 사용하기 위해 java.io.* 패키지를 import했다. java.lang 패키지는 자동으로 되지만 이것들은 꼭 명시를 해줘야 한다.
먼저 파일을 읽기 위해 FileReader (문자 스트림을 다루는 클래스) 객체를 만들기 위해 변수를 선언하고 null값으로 초기화 했다.
이것이 try 구문 밖에 있는 이유는,밖에 꺼내놓지 않으면 밑의 finally 블록에서 해당 변수를 사용할 수 없기 때문이다.
실제적으로 객체를 생성하고 파일을 읽는 기능은 전부 try 기능에 넣어두었다. 이렇게 익셉션 처리를 하지 않으면 에러가 발생하기때문에
꼭 두 익셉션을 예외처리 해주어야 한다.
FileReader 객체의 메소드 read()는 한개의 문자를 파일에서 읽어와 정수형 값으로 리턴하는 메소드다. 해당 값을 -1(파일의 끝)에 도달할 때까지 읽어 캐릭터 변수에 저장하고 출력하는 것을 반복하고 있다.

read 메소드는 다른 형식으로 오버로딩되어 있는데, 만일 read메소드에 인자로 캐릭터 배열을 넣게 되면 read메소드는 한번에 한 글자씩 받아 리턴하는 것이 아니라, 한꺼번에 파일에서 읽어 인자로 받은 배열에 문자열을 저장하고, 문자열을 읽은 경우에는 읽은 문자열의 크기를,읽지 않은 경우에는 -1을 리턴하는 기능을 하게 된다.

   try
  {
   reader = new FileReader("lyric.txt"); // 객체 생성 - 인자로 파일 이름을 받음.
   char arr[] = new char[100];
   int data = reader.read(arr);
   System.out.println(arr);
   System.out.println(data);
   
  }

이렇게 실행하면 루프문을 돌리지 않고서도 문자를 읽어올 수 있다.
data를 출력해보면 읽은 문자수만큼 read 메소드가 숫자를 리턴하는 것을 알 수 있다.

 

이제 문자를 파일로 쓰는 예제를 보자.

import java.io.*; // 파일 입출력 클래스 객체를 만들기 위해
class ReaderExample 
{
 public static void main(String[] args) 
 {
FileWriter writer = null;
try
{
 writer = new FileWriter("output.txt");
 
 char arr[] = {'안','녕','하','세','요'};
 for (int cnt = 0;cnt < arr.length ; cnt++ )
 {
  writer.write(arr[cnt]); // write 메소드
 }
}
catch (IOException ioe) // 파일을 여는 것이 아니기 때문에 쓰기와 close 메소드의 예외처리만 하면 됨.
{
 System.out.println("파일로 출력할 수 없습니다.");
}

finally
  {
 try
 {
  writer.close();
 }
 catch (Exception e)
 {
 }
  }

 }
}

 

파일을 읽을 때 FileReader 클래스를 사용한 것과 다르게 파일을 쓰는 것이므로  FileWriter 클래스를 사용한다.
익셉션 처리 방식은 읽을 때와 비슷한데 다른 것이 있다면 파일을 여는 것이 아니므로 파일 생성 익셉션인 FileNotFoundException을 해줄 필요가 없다는 점이다.

write 메소드는 인자에 문자나 문자열을 받아 파일에 쓰는 기능을 한다.
writer.write('a') 나 writer.wirte("abcde") 같은 방법으로 쓰일 수 있다.
위 예제에서는 캐릭터 배열을 넣었기 때문에 한글자씩 루프문을 통해 넣고 있다.
하지만 그냥 메소드의 인자를 캐릭터 배열 이름으로 하게 되면 알아서 한꺼번에 파일에 쓰게 된다. ( writer.write(arr) )

( + 파일을 열 때 원래 있던 파일의 내용을 유지하고 덧붙여 쓰게 하고 싶다면, 객체를 생성할 때 두번째 파라미터로 true 값을 넣어주면 된다.
writer = new FileWriter("output.txt" , true);  )

 

이제 문자 스트림이 아니라 바이트 스트림을 알아보자.


바이트 스트림은 FileWriter가 아니라 FileOutputStream 클래스를 사용해야 한다. 바이트 스트림도 위의 문자 스트림과 매우 유사한 구조를 갖고 있다.
다른 점이라면 write 메소드는 매개변수를 문자가 아니라 바이트 데이터로 취급한다.

import java.io.*; // 파일 입출력 클래스 객체를 만들기 위해
class ReaderExample 
{
 public static void main(String[] args) 
 {
FileOutputStream writer = null;
try
{
 writer = new FileOutputStream("output.dat");
 byte arr[] = {0,1,2,3,4,5,6,7,8,9};
 for(int cnt = 0;cnt < arr.length;cnt++)
  writer.write(arr[cnt]);
 
}
catch (IOException ioe) // 파일을 여는 것이 아니기 때문에 쓰기와 close 메소드의 예외처리만 하면 됨.
{
 System.out.println("파일로 출력할 수 없습니다.");
}

finally
  {
 try
 {
  writer.close();
 }
 catch (Exception e)
 {
 }
  }

 }
}

 

문자 스트림과 비슷한 구조다. 파일 이름을 인자로 받아 쓰기 객체를 만들고 write메소드로 쓴다.

import java.io.*; // 파일 입출력 클래스 객체를 만들기 위해
class ReaderExample 
{
 public static void main(String[] args) 
 {
FileInputStream reader = null;
try
{
 reader = new FileInputStream("output.dat");
 while(true)
 {
  int data = reader.read();
  if(data==-1)
  break;
      System.out.println(data);
 }
}

 

catch (IOException ioe)
{
 System.out.println("파일로 출력할 수 없습니다.");
}

 

finally
  {
 try
 {
  reader.close();
 }
 catch (Exception e)
 {
 }
  }

 }

위 예제에서 만든 output.dat 파일을 읽기 위해 이번에는 FileInputStream 클래스를 이용해 파일을 읽어보았다.
FileInputStream의 read 메소드는 파일을 바이트 단위로 읽고 int형으로 리턴한다.

 

이번 예제는 프로그램 인수를 통해 파일 이름을 받아 해당 파일에 있는 숫자를 16진수로 출력하는 예제다.

 

import java.io.*;
class  FileDump
{
 public static void main(String[] args) 
 {
  FileInputStream in = null;
  try
  {
   in = new FileInputStream(args[0]); // 프로그램 인수로 객체 생성
   byte arr[] = new byte[16]; //16바이트짜리 배열 생성
   while(true)
   {
    int num = in.read(arr); // read메소드가 파일에서 16바이트만큼 읽어와 arr에 저장하고 읽은 만큼의 바이트 수를 num에 리턴.
    if(num<0)
     break;
    System.out.println(" num = "+num);
    for(int cnt = 0; cnt< num; cnt++)
     System.out.printf("%02X ",arr[cnt]);
    
    System.out.println();
   }
  }
  catch (FileNotFoundException fnfe)
  {
   System.out.println(args[0] + "파일이 존재하지 않습니다");
  }

  catch (IOException ioe)
  {
   System.out.println(args[0] + "파일을 읽을 수 없습니다");
  }


  finally
  {
   try
   {
    in.close();
   }
   catch (Exception e)
   {
   }
  }
 }
}

 

FileInputStream의 객체를 프로그램 인수 args로 파일 이름을  받아 생성하고 있다.
그 후 byte형 배열을 16바이트 크기로 만들고, read 메소드를 통해 파일을 16바이트만큼 읽어 arr에 저장하고 각 원소를 루프문을 통해 출력하는 예제다.
num을 출력해보면 16바이트 읽었다는 것을 확인해 볼 수 있다.
read 메소드를 사용한 직후에는 10이 출력되지만, 파일을 다 읽고 나면 파일의 끝에 도달해 -1이 출력되는 것을 알 수 있다.

 

 

import java.io.*; 
class ReaderExample 
{
 public static void main(String[] args) 
 {
DataOutputStream out = null;
try
{
 out = new DataOutputStream (new FileOutputStream ("output.dat"));
 int arr[] = {0,1,2,3,4,5,6,7,8,9};
 for(int cnt = 0;cnt<arr.length;cnt++)
  out.writeInt(arr[cnt]);
}

 

catch (IOException ioe)
{
 System.out.println("파일로 출력할 수 없습니다.");
}

 

finally
  {
 try
 {
  out.close();
 }
 catch (Exception e)
 {
 }
  }

 }
}

 

이제 byte 타입과 char 타입 이외의 타입들을 다루는 입출력 클래스들을 알아보자.
이 클래스들은 파일에 데이터를 읽고 쓰는 기능은 갖고 있지 않기 때문에 위의 기본 4 클래스와 함께 사용해야 한다.
먼저 char타입과 byte타입이 아닌 다른 프리미티브 타입 데이터를 읽고 쓸 때는 DataInputStream 클래스와 DataOutputStream 클래스를 사용해야 하는데,

DataOutputStream 클래스는 프리미티브 타입 데이터를 바이트 스트림으로 만들어서 출력하는 기능을 한다.
이런 입출력을 보조해주는 클래스들은 먼저 기본 클래스들을 생성해야 사용할 수 있다.
위의 예제를 보면 먼저 DataOutputStream 클래스의 변수를 만들어놓고, try 구문 안에서 객체를 생성할 때 FileOutputStream객체를 인자로 넣어 생성하는 것을 볼 수 있다.
DataOutputStream 클래스의 메소드들은 인자로 받은 데이터를 바이트로 분할한 다음에, 그 바이트 수만큼 FileOutputStream의 write메소드를 호출해서 파일에 데이터를 쓴다.
위의 예제에서는 writeInt 메소드를 사용했기에 arr[]원소를 받아 4 바이트에 원소를 담고 이것을 쓰기 위해  write 함수를 4번 호출하게 된다.
고로 arr의 원소가 10개이므로, 한 원소당 1바이트씩 4개로 쪼개 4번씩 호출해 파일에 쓰므로 10 * 4 = 40바이트 크기의 파일이 만들어지게 된다.

위의 FileOutputStream의 예제에서는 똑같은 배열로 했는데도 10바이트로 써지는 이유는 한 원소당 write 메소드를 한번만 불렀기 때문이다.

 

import java.io.*; 
class ReaderExample 
{
 public static void main(String[] args) 
 {
DataInputStream in = null;
try
{
 in = new DataInputStream (new FileInputStream ("output.dat"));
 while(true)
 {
 int data = in.readInt();
 if (data==-1) // 이렇게 해놔도 익셉션 발생함
 break;
 System.out.println(data);
 }
}


catch (FileNotFoundException fnfe)
  {
  System.out.println("파일이 존재하지 않습니다.");
  }

catch(EOFException eofe)
  {
  System.out.println("끝");
  }

catch (IOException ioe)
{
 System.out.println("파일을 읽을 수 없습니다.");
}

 

finally
  {
 try
 {
  in.close();
 }
 catch (Exception e)
 {
 }
  }

 }

 

이번에는 파일을 int크기 만큼 읽어서 반환하는 readInt에 대한 예제다.
만일 저 output.dat 파일이 그냥 write 메소드로 쓰였을 경우 의도한 것과 다르게 출력되므로(writeInt메소드는 4바이트씩 읽어서 write메소드를 호출한다)
꼭 writeInt로 쓴 파일을 읽어야 한다.
DataInputStream 클래스의 readInt 메소드에 신경써야 할 점이 하나 있다면 FileInputStream의 read 메소드와 다르게 더 읽을 데이터가 없으면 -1을 반환하는 것이 아니라 EOFException을 발생한다는 점이다. 고로 예외처리를 해 주어야 한다. 

 

문자열을 읽고 쓰는 메소드도 위의 방식과 같다.

import java.io.*; 
class ReaderExample 
{
 public static void main(String[] args) 
 {
DataOutputStream out = null;
DataInputStream in = null;
try
{
 out = new DataOutputStream (new FileOutputStream ("output.txt"));
 out.writeUTF("안녕하세요?");
 in = new DataInputStream (new FileInputStream ("output.txt"));
 String data = in.readUTF();
 System.out.println(data);

}


catch (FileNotFoundException fnfe)
  {
  System.out.println("파일이 존재하지 않습니다.");
  }

catch(EOFException eofe)
  {
  System.out.println("끝");

  }

catch (IOException ioe)
{
 System.out.println("파일을 읽을 수 없습니다.");
}

 

finally
  {
 try
 {
  out.close();
  in.close();
 }
 catch (Exception e)
 {
 }
  }

 }
}

 

만일 객체를 파일에 읽고 쓰고 싶다면 어떻게 해야 할까?
객체를 파일에 쓰려면 ObjectInputStream 클래스와 ObjectOutputStream 클래스를 사용해야 한다.
역시 이 클래스들도 마찬가지로 FileOutputStrema과 FileinputStream에 의존해 사용해야 한다.
객체를 읽고 쓰려면 해당 객체가 직렬화가 가능해야 하는데, 여기에 대해서는 별도의 포스트에서 다뤄보도록 하겠다.

 

 

import java.io.*;
class test implements java.io.Serializable
{
 int a;
 int b;
 public static void main(String[] args) 
 {
  test t1 = new test();
  test t2 = new test();
  test t3 = new test();
   
  t1.a = 1;
  t1.b = 2;

  t2.a = 3;
  t2.b = 4;

  t3.a = 5;
  t3.b = 6;

  ObjectOutputStream out = null;
  try
  {
   out = new ObjectOutputStream(new FileOutputStream("output.dat"));
   out.writeObject(t1);
   out.writeObject(t2);
   out.writeObject(t3);
  }

  
  catch(IOException e)
  {
   System.out.println("파일에 쓸 수 없습니다");
  }

 

  finally{

  try
  {
   out.close();
  }
  catch (Exception e)
  {
  }

  }

 


 }
}

 

///////////////////////////////////////////////////////////////////////////////////

 

import java.io.*;
class test_
{

 public static void main(String[] args) 
 {

  ObjectInputStream in = null;
  try
  {
   in = new ObjectInputStream(new FileInputStream("output.dat"));
   while(true)
   {
   test t1 = (test)in.readObject();
   int a = t1.a;
   int b = t1.b;
   System.out.println(a+"  "+b);
   }
  }

  
  catch(IOException ioe)
  {
   System.out.println("파일을 읽을 수 없습니다");
  }

  catch(ClassNotFoundException cnfe)
  {
   System.out.println("파일을 찾을 수 없습니다");
  }

 


  finally{

  try
  {
   in.close();
  }
  catch (Exception e)
  {
  }

  }

 


 }
}

 

 

 객체를 파일에 쓰고 읽는 예제다.

test 객체를 ObjectOutputStream 클래스를 사용해 output.dat파일에 쓰고, ObjectInputStream 클래스를 사용해 
읽어온 후 정수 변수에 넣어 다시 출력하는 예제다.
주의할 점은, 객체를 파일에 읽고 쓰기 위해서는 꼭 직렬화 속성(implements java.io.Serializable) 이 있어야 한다는 점.
그리고 파일에서 객체를 읽어올 때 readObject 메소드는 읽고나서 Object타입(모든 클래스의 슈퍼 클래스로 존재하는 특별한 클래스)으로 리턴하므로,
객체 변수에 담기 전에 꼭 해당 클래스로 캐스팅을 해주어야 한다. writeObject 메소드도 Object 타입으로 파일에 쓰는 것은 마찬가지다.

 

 

 

이상 위의 파일입출력 클래스들은 모두 읽거나 쓸 때 메소드를 바이트 단위나 문자 스트림단위마다 접근하기 대문에 잦은 기억장치 접근으로 프로그램 성능을 떨어뜨리기 쉽다. 이런 문제를 해결 하기 위해 버퍼를 사용하는 입출력 클래스들이 있다.

BufferedInputStream과 BufferedOutputStream은 바이트 입출력을, BufferedWriter와 BufferedReader는 문자 스트림 입출력을 담당한다.

버퍼 입출력은 사용하는 방법은 상위의 다른 클래스들과 크게 다르지 않다.

 

 import java.io.*;
class test implements java.io.Serializable
{

 public static void main(String[] args) 
 {
  

  BufferedOutputStream out = null;
  try
  {
   out = new BufferedOutputStream(new FileOutputStream("output.dat"));
   int arr[] = {1,2,3,4,5,6,7,8,9,10};
   for(int i=0;i<arr.length;i++)
   {
   out.write(arr[i]);
   }
  }

  
  catch(IOException e)
  {
   System.out.println("파일에 쓸 수 없습니다");
  }

 

  finally{

  try
  {
   out.close();
  }
  catch (Exception e)
  {
  }

  }

 


 }
}

 

////////////////////////////////////////////////

 

import java.io.*;
class test_
{

 public static void main(String[] args) 
 {
  BufferedInputStream in = null;
  int a;
  try
  {
   in = new BufferedInputStream(new FileInputStream("output.dat"));
   byte arr[] = new byte[100];
   a = in.read(arr);
   
   for (int i=0;i<a;i++)
   {
    System.out.println(arr[i]);
   }

   
  }
  catch (IOException e)
  {

  }


 }
}


 

일단 test 클래스에서 버퍼출력 객체를 만들어 정수형 배열 arr을 파일에 쓰고 있다.
그 후 test_ 클래스에서는 버퍼입력 객체로 파일을 읽어와 배열 arr에 읽은 데이터를 쓰고 있다.
이때 read(배열) 메소드는 FileInputStream 클래스의 read와 마찬가지로 읽은 데이터의 바이트 수를 int로 반환한다는 점.

이렇게 버퍼로 입출력을 하게 되면 한꺼번에 읽어서 객체 내부에 버퍼에 저장하게 되고, 읽기 쓰기 메소드가 호출 될 때 파일의 데이터가 아니라 버퍼에 접근하게 된다.이때 파일에서 읽어와서 버퍼에 담는 크기는 버퍼 객체를 생성할 때 지정해 줄 수 있다.
(    in = new BufferedInputStream(new FileInputStream("output.dat"),1024);   )

버퍼 입출력의 순서는 -> 파일로부터 버퍼의 크기만큼 읽어온다 -> 읽기나 쓰기 메소드의 특성에 따라 버퍼에서 그만큼 읽어온다 -> 버퍼의 데이터가 없거나 충분하지 않으면 파일로부터 데이터를 읽어 다시 버퍼를 채운다.

이런 버퍼 출력은 버퍼가 다 차기 전까지는 파일에 실제로 데이터를 쓰지 않기 때문에, 만일 다른 프로그램이 그 파일의 내용을 사용하려고 하면 문제가 될 수 있다. 그럴때는 출력 클래스의 flush(); 메소드를 사용하면 즉시버퍼에 있는 데이터를 모두 파일에 쓰고 나서 버퍼를 비우게 된다.

 

 

LineNumberReader 클래스는 텍스트를 읽어들일 때 행 번호를 붙이면서 읽는다.

 

import java.io.*;
class  test
{
 public static void main(String[] args) 
 {
  LineNumberReader reader = null; 
  try
  {
   reader = new LineNumberReader(new FileReader("lyric.txt"));
   
   while(true)
   {
    String str = reader.readLine();
    if(str==null)
     break; 
    int a = reader.getLineNumber();

    System.out.println(a+": "+str);
   }
   
  }
  catch (IOException e)
  {
  }

  finally
  {
   try
   {
    reader.close();
   }
   catch (Exception e)
   {
   }


  }
 
 
 }
}

 

readLine() 메소드는 텍스트 한 줄을 읽어서 리턴하는 메소드다.이 메소드는 파일의 끝에 도달하면 null을 리턴한다.
행의 번호는 getLineNumber 메소드를 이용해 가져올 수 있다.

 

 

간단하게 데이터를 문자열로 바꿔 출력해주는 클래스도 있다.
PrintWriter와 PrintStream인데, PrintStream은 JDK 옛 버전에서부터 사용되는 것으로 둘은 거의 같은 기능을 한다.
우리가 자주 쓰는 System.out.println의 out이 바로 PrintStream 클래스의 필드다.

아무튼 두 클래스는 사용하는데 별 차이가 없지만 가급적 PrintWriter 클래스를 사용하는 것이 좋다.

import java.io.*;
class  test
{
 public static void main(String[] args) 
 {
  PrintWriter writer = null;
  try
  {
   writer = new PrintWriter("output.txt");
   writer.print(12); // 인자로 들으언 타입이 문자 데이터가 아니면 문자 데이터로 만들어 출력.
   writer.println(100); // 개행을 하는 출력 함수 println.
   writer.printf("%d년 %d월 %d일",2013,06,21); // "%d년 %d월 %d일" -> 포맷 문자열, %d -> 포맷 명세자
   writer.printf("%2$d월 %3$d일 %1$d년",2013,06,21); // $ -> argument index
   writer.printf("%1$d년 %1$d월 %1$d일",11); // $을 이용하면 1개의 인자를 계속 쓸 수도 있다.
  }
  catch (IOException ioe)
  {
   System.out.println("에러");
  }

  finally
  {
   try
   {
    writer.close();
   }
   catch (Exception e)
   {
   }


  }

 }
}

 

이 클래스는 FileInputStream(문자열로 쓰인다고 FileWriter 클래스가 아님) 객체를 만들어 넘겨주지 않아도 실행이 된다.
여기서 익숙한 출력 메소드가 보이는데, print나 println등은 PrintStream에 있는 그것과는 똑같은 기능을 하지만 여기선 다만 출력의 대상이 파일이다.
(물론 PrintStream의 것도 파일을 대상으로 출력할 수 있음)
이 메소드들은 인자로 데이터가 들어오면 문자열로 바꿔 파일에 쓴다.
printf메소드의 경우 출력할 포맷을 지정하는 포맷 문자열을 사용할 수 있는데,이 메소드는 첫 번째 파라미터가 문자열이기만 하면 그 뒤에 오는 파라미터의 수에는 제한이 없다.단 첫번째 파라미터는 포맷 문자열이어야 한다.
포맷 문자열 안에서 % 기호가 들어간 것은 포맷 명세자라고 한다. 아마 C에서도 똑같은 쓰임을 보았을 것이다.
%와 알파벳 사이에 "숫자$" 을 통해 포맷 문자열 뒤에 있는 파라미터의 순서를 정해줄 수 있는데, 이를 argument index라 한다.
이 기호를 사용하면 1개의 파라미터를 연속적으로 포맷 문자열 안에서 사용할 수 있게 된다.

 

 

about author

PHRASE

Level 60  머나먼나라

소문은 가장 좋은 소개장이다. -탈무드-

댓글 ( 4)

댓글 남기기

작성

자바 목록    more