JavaIO
在javaIO中主要有5个类和1个接口,它们是:File,InputStream,OutputStream,Reader,Writer,Serializable接口。
File类主要功能是完成与平台无关的文件操作。其中File.pathSeparator表示路径分隔符“:\” File.separator则表示分隔符“/”或“\\”
字节流和字符流:
字节流:(inputStream,OutputStream)
字符流:(reader,writer)
OutputStream.write(byte[] b) 一次性写入一个定义的字节数组
OutputStream.write(int i)一次性写入一个整数如write(byte[1])一个字节可以转换成一个整数
InputStream.read(byte[] b) 将文件内容读到一个字节数组中
InputStream.read()一次只读一个字节。
Writer.write(String str)直接写入字符串
字符输入流使用的char[]
字节流在操作的时候是直接与文件本身关联,不使用缓冲区
字节->文件
字符流在操作的时候是通过缓冲区与文件操作
字符->缓冲->文件
下面是一个使用字节流方式完全copy一个文件夹或文件到另一个文件夹或文件的代码
1 public static void copy(File source, File dest) throws Exception { 2 if (!source.exists()) { 3 System.out.println("the source file isn't exist!!!"); 4 return; 5 } 6 7 if (source.isFile()) { 8 9 if (!dest.exists()) { 10 dest.mkdir(); 11 } 12 File destFile = new File(dest.getPath() + "/" + source.getName()); 13 14 InputStream in = new FileInputStream(source); 15 OutputStream out = new FileOutputStream(destFile); 16 /* 17 * 方法一:这种方式是一次读一个字节写一个字节 18 * int i; 19 * while ((i = in.read()) != -1) { 20 * out.write(i); } 21 */ 22 23 // *方法二:这种方式是一次读自定义的字节数组这么多,相对来说快一点,相当于加了缓冲 24 byte[] b = new byte[512]; 25 int j; 26 while ((j = in.read(b)) != -1) { 27 out.write(b, 0, j); 28 } 29 30 in.close(); 31 out.close(); 32 33 } 34 if (source.isDirectory()) { 35 File destDir = new File(dest.getPath() + "/" + source.getName()); 36 if (!destDir.exists()) { 37 destDir.mkdirs(); 38 } 39 40 File[] files = source.listFiles(); 41 for (File file : files) { 42 copy(file, destDir); 43 } 44 45 } 46 }
运用字符流copy文本文件或直接把字符串写入到文件中:
1 //注意字符流一般只适合操作字符类文本文件,如果操作一个二进制文件如图片就可能出错 2 public static void copyFile(File source,File dest)throws Exception{ 3 if(!source.exists()){ 4 System.out.println("The source file isn't exist"); 5 return; 6 } 7 File destFile = new File(dest.getPath() + "/" + source.getName()); 8 if(!destFile.exists()){ 9 destFile.createNewFile(); 10 } 11 Reader rd = new FileReader(source); 12 Writer wt = new FileWriter(destFile); 13 //方法一:一次读一个字符,写一个字符 14 /* 15 int i; 16 while((i=rd.read())!=-1){ 17 wt.write(i); 18 } 19 */ 20 21 //*方法二:一次读取一个字符数组的内容,相当于缓冲 22 int len; 23 char[] ch = new char[512]; 24 while((len=rd.read(ch))!=-1){ 25 wt.write(ch, 0, len); 26 } 27 //用字符流可以直接往文件里写入字符串 28 wt.write("我是被写入的字符串"); 29 //如果没有显示关闭输出流可能不会写入全部数据,要调用wt.flush()方法把缓冲区清空就可以全部定入了 30 rd.close(); 31 wt.close(); 32 33 }
内存操作流:输入和输出的操作对象都是内存,ByteArrayOutputStream和ByteArrayInputStream完成内存的操作流。
内存流的操作,可以这样想象一下,相当于内存中有一块特殊的区域(区别于其它内存区),可以把它叫做内存流区,此区域只能让内存输入流和内存输出流操作,内存输入流只能把数据从内存中输入到此区域,而内存输出流同样也只能从这个区域拿到数据输出到内存中,内存输入流的构造方法需要一个字节数组的内容作为输入,而内存输出流的构造方法则不用任何参数,它会默认从此内存流区取东西出来放到内存中,以便我们使用。
下面以一个读取xml文件并把内容打印出来的例子来说明内存流:
1 public static void showFileContext(File xmlFile)throws Exception{ 2 if(!xmlFile.exists()){ 3 System.out.println("The file isn't exist!!!"); 4 return; 5 } 6 //文件输入流 7 InputStream fin = new FileInputStream(xmlFile); 8 //内存输出流 9 OutputStream out = new ByteArrayOutputStream(); 10 byte[] b = new byte[512]; 11 int len; 12 while((len = fin.read(b))!=-1){ 13 //构建内存输入流,向内存中输入内容,其实是向内存流中输入内容 14 InputStream in =new ByteArrayInputStream(b,0,len); 15 int len2; 16 //从内存输入流中读出一个字节 17 while((len2=in.read())!=-1){ 18 //把读到的字节转换成字符(一个字节8位,一个字符相当于两个字节16位,肯定能转) 19 char ch = (char)len2; 20 //把字符写入到内存输出流中 21 out.write(ch); 22 }; 23 in.close(); 24 } 25 //从内存输出流中取出内容 26 String str = out.toString(); 27 out.close(); 28 System.out.println(str); 29 30 }
管道流:就是进行两个线程间通讯的,使用PipedOutputStream和PipedInputStream两个类完成。类似于其它输出输入流。唯一特别的是要进行管道连接才能操作
管道相当于内存外一个特殊东西,线程与它关联,管道输入流是把管道中的东西输入到内存中,而管道输出流则是把内存中的东西输出到管道中,也就是只有管道输出流往这个管道输进了东西,管道输入流才能从这个管道中读出内容。下面以一个例子表示
1 public class PipedInputStreamAndPipedOutputStream { 2 3 /** 4 * @param args 5 * @throws IOException 6 */ 7 public static void main(String[] args) throws IOException { 8 Send send = new Send(); 9 Receive rec = new Receive(); 10 send.getPipedOutputStream().connect(rec.getPipedInputStream());// 进行管道连接 11 new Thread(send).start(); 12 new Thread(rec).start(); 13 } 14 15 } 16 17 class Send implements Runnable { 18 private PipedOutputStream output = null; 19 20 public Send() { 21 this.output = new PipedOutputStream(); 22 } 23 24 public PipedOutputStream getPipedOutputStream() { 25 return this.output; 26 } 27 28 public void run() { 29 String str = "我是要进入到管道的内容并且管道输入流会打印出来我";// 要发送的数据 30 try { 31 this.output.write(str.getBytes());//写入数据 32 } catch (IOException e) { 33 e.printStackTrace(); 34 }// 发送 35 } 36 } 37 38 class Receive implements Runnable { 39 private PipedInputStream input; 40 41 public Receive() { 42 this.input = new PipedInputStream(); 43 } 44 public PipedInputStream getPipedInputStream(){ 45 return this.input; 46 } 47 public void run() { 48 byte[] b = new byte[1024];// 接收内容 49 int len = 0; 50 try { 51 len = this.input.read(b);//读出数据放到byte[]中 52 } catch (IOException e) { 53 e.printStackTrace(); 54 }// 内容读取 55 System.out.println(new String(b, 0, len)); 56 } 57 }
打印流:PrintStream(子节流),PrintWriter(字符流)PrintStream(OutputStream out),实际上PrintStream应用了装饰模式,也就是说根据实例化PrintStream类对象的不同,输出的位置也不同,根据传入的参数决定使用打印流输出最为方便,建议以后在输出的时候使用打印流完成。而且在JDK1.5后对打印流进行了更新,可以输出格式化输出,提供了printf(String format,Object... args);
打印流是包装流的一种,它常用于输出各种内容操作,PrintStream接收OutputStream的子类作为输出,而PrintWriter则接收OutputStream和Writer,它可以实现把字节流转换成字符流
下面是使用的示例:
1 public class PrintStreamAndPrintWriter { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args) throws Exception{ 7 printStreamTest(); 8 printWriterTest(); 9 } 10 11 public static void printStreamTest()throws Exception{ 12 13 PrintStream out = new PrintStream(new File("d:/hmc.txt"));//类似于new PrintStream(new OutputStream(new File("..."))) 14 out.println(false);//写入布尔值 15 out.println('a');//写入字符值 16 out.println(new char[]{'b','c','d'});//写入字符数组值 17 out.println(5.9);//写入double 18 out.println(1.05f);//写入float 19 out.println(2);//写入int 20 out.println(34l);//写入long 21 out.println(new Object());//写入对象 22 out.println("my name is abc");//写入字符串 23 out.append('z');//追加 24 out.close(); 25 26 27 28 } 29 public static void printWriterTest()throws Exception{ 30 PrintWriter out = new PrintWriter(new FileOutputStream(new File("d:/hmc.txt"),true));//表示在文件末尾追加内容 31 out.println(false);//写入布尔值 32 out.println('a');//写入字符值 33 out.println(new char[]{'b','c','d'});//写入字符数组值 34 out.println(5.9);//写入double 35 out.println(1.05f);//写入float 36 out.println(2);//写入int 37 out.println(34l);//写入long 38 out.println(new Object());//写入对象 39 out.println("my name is abc");//写入字符串 40 out.append('z');//追加 41 out.close(); 42 43 } 44 45 }
System类对IO的支持:
public static final PrintStream out 标准输出,输出位置显示器
public static final PrintStream err 表示错误,错误的输出
public static final InputStream in 表示是键盘的输入,标准输入
虽然这几个类输入输出位置固定,但System提供了为这些重定向的方法:
System.out重定向:public static void setOut(PrintStream out)
System.err重定向:public static void setErr(PrintStream err)
System.in 重定向:public static void setIn(InputStream in)
System.out和System.err本身就是PrintStream类使用类似于PrintStream,而System.in则是inputStream默认接收键盘输入.如下例接收键盘输入输入到一个文件中,当输入“*”时退出
1 public static void getKeyboardIn(File file) throws Exception{ 2 InputStream in = System.in; 3 4 OutputStream out = new FileOutputStream(file,true); 5 int i; 6 while((i=in.read())!=-1){ 7 out.write(i); 8 char ch = (char)i; 9 if(ch=='*'){ 10 break; 11 } 12 } 13 }
另一个类就是Scanner(扫描器输入)它是一个可以使用正则表达式来解析基本类型和字符串的简单文本扫描器。
Scanner
使用分隔符模式将其输入分解为标记,默认情况下该分隔符模式与空白匹配。然后可以使用不同的 next 方法将得到的标记转换为不同类型的值。如下为一个示例
1 public static void getKeyboardIn2()throws Exception{ 2 Scanner scanner = new Scanner(System.in); 3 while(true){ 4 String str = scanner.nextLine(); 5 System.out.println(str); 6 if(str.equals("exit")){ 7 break; 8 } 9 } 10 }
缓存流:
字节缓存流BufferedInputStream,BufferedOutputStream
字符缓存流BufferedReader,BufferedWriter;
缓存流都是包装类流,它们分别可以包装对应字节流与字符流
下面是字节缓冲流的一个方法,用于copy文件:用法与普通的字节流类似,只是在底层加了缓冲而已
1 public static void copy(File source, File dest) throws Exception { 2 if (!source.exists()) { 3 System.out.println("the source file isn't exist!!!"); 4 return; 5 } 6 if (source.isDirectory()) { 7 System.out.println("the source file is a directory!!!"); 8 return; 9 } 10 if (!dest.exists()) { 11 dest.createNewFile(); 12 } 13 BufferedInputStream bis = new BufferedInputStream(new FileInputStream( 14 source)); 15 BufferedOutputStream bos = new BufferedOutputStream( 16 new FileOutputStream(dest)); 17 int len; 18 byte[] b = new byte[128]; 19 while ((len = bis.read(b)) != -1) { 20 bos.write(b, 0, len); 21 } 22 bis.close(); 23 bos.close(); 24 25 }
如果要想使用BufferedReader类接收键盘的输入内容的话,则此时就无法直接实例化了,因为System.in属于InputStream类型的。
但是在Java中提供了两个专门的类,用于字节,字符流的转换类:
InputStreamReader:将字节的输入变成字符流
OutputStreamWriter:将字符的输出流变成字节的输出流
直接使用以上的类就可以完成转换
下面是一个接收键盘输入并把内容写到文件中的例子:
1 public static void putKeyBoaredInFile(File dest) throws Exception { 2 if (!dest.exists()) { 3 dest.createNewFile(); 4 } 5 6 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 7 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter( 8 new FileOutputStream(dest, true))); 9 String str; 10 11 while ((str = br.readLine()) != null) { 12 System.out.println(str); 13 bw.write(str); 14 if (str.equals("exit")) { 15 break; 16 } 17 ; 18 } 19 br.close(); 20 bw.close(); 21 }
对象序列化流:
要序列化的对象必须实现Serialization接口。对象序列化流包括ObjectOutputStream(),ObjectInputStream( ),这两个也是包装类接收InputStream和OutputStream作为参数。用法类似于打印流和Scanner可以写入写出一个基本类型和object类型。下面为一个示例,把一个对象序列化到文件中,再从这个文件中把对象反序列化出来:
1 public class ObjectInputStreamAndObjectOutputStream { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args)throws Exception { 7 Person2 p = new Person2(22,"tim","123456"); 8 File file = new File("D:/object.txt"); 9 setObjectToFile(p, file); 10 getObjectFromFile(file); 11 12 } 13 //把对象序列化到对应的文件中 14 public static void setObjectToFile(Object obj,File destFile)throws Exception{ 15 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(destFile)); 16 oos.writeObject(obj); 17 oos.close(); 18 } 19 //从文件中反序列化出对应的对象 20 public static void getObjectFromFile(File sourceFile)throws Exception{ 21 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(sourceFile)); 22 Person2 obj = (Person2)ois.readObject(); 23 24 System.out.println(obj.getAge()); 25 System.out.println(obj.getName()); 26 System.out.println(obj.getPassword()); 27 } 28 } 29 30 31 class Person2 implements Serializable{ 32 private int age; 33 private String name; 34 private transient String password;//声明为transient将不会被序列化 35 public Person2(){}; 36 public Person2(int age,String name,String password){ 37 this.age = age; 38 this.name = name; 39 this.password = password; 40 } 41 public int getAge() { 42 return age; 43 } 44 public void setAge(int age) { 45 this.age = age; 46 } 47 public String getName() { 48 return name; 49 } 50 public void setName(String name) { 51 this.name = name; 52 } 53 public String getPassword() { 54 return password; 55 } 56 public void setPassword(String password) { 57 this.password = password; 58 } 59 }