io系统

1.File类

2.I/O概念

3.字节流

4.字符流

5.包装方式的灵活性

6.转换流

7.其它流

8.Properties

9.IO中保证流对象关闭的标准格式

 

一、File类

1.概念

        java.io包下代表平台无关的文件和目录。也就是说如果希望在程序中操作文件或目录都可以通过File类来完成。

        File可以新建、删除、和重命名文件和目录。但是File不能访问文件本身,如果需要访问文件的内容,则需要I/O。

        File类的实例表示一个文件或者目录(文件夹)。

        构造一个File实例并不是创建这个目录或文件夹,而是该路径的一个抽象,它可能真实存在也可能不存在(就是指向这个文件或目录)。

2.相对路径和绝对路径

        路径分类:绝对路径、相对路径

        绝对路径:从根目录开始的路径,称为绝对路径。

        Windows系统中:盘符目录就是根目录,C:、D:。

        Linux系统中:/就是根目录,从/开始的路径就是绝对路径

        相对路径:相对于某个路径而言的路径

        相对于不同的路径,同样的相对路径,表达的是不同的路径

        Dos命令行中:相对于当前路径

        Eclipse中:相对于当前工程的根目录

 3.File类的构造方法

File(File parent, String child) 

从父抽象路径名和子路径名字符串创建新的 File实例。

File(String pathname) 

通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。

File(String parent, String child) 

从父路径名字符串和子路径名字符串创建新的 File实例。

        代码示例

import java.io.File;
public class Test {

	public static void main(String[] args) {	
		File parent = new File("test/a/b");
		File file1 = new File(parent,"c.txt");//test\a\b\c.txt
		System.out.println(file1);

		File file2 = new File("E:/test/a/b/c.txt");
		System.out.println(file2);//E:\test\a\b\c.txt  绝对路径指向文件
		File file3 = new File("c.txt");
		System.out.println(file3);//c.txt 相对路径,相对于当前项目的根目录

		File file4 = new File("test/a/b","c.txt");
		System.out.println(file4);//test\a\b\c.txt
    }
}

 

4.File类的常用方法

      (1)创建的方法

boolean

createNewFile() 

当且仅当具有该名称的文件尚不存在时,原地创建一个由该抽象路径名命名的新的空文件。

boolean

mkdir() 

创建由此抽象路径名命名的目录。

boolean

mkdirs() 

创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。

       示例代码

import java.io.File;
public class Test {

	public static void main(String[] args) throws Exception{	
		
	/*
		//创建一个文件,父路径(test/a/b)不存在
		File f1 = new File("E:/test/a/b/c.txt");
		f1.createNewFile();
		//报错,系统找不到指定的路径
	*/

		//创建一个文件,父路径存在
		File f2 = new File("E:/test/a/b/c.txt");
		f2.createNewFile();
		//文件创建成功

		//创建一个文件,相对于当前项目的根目录
		File f3 = new File("c.txt");
		f3.createNewFile();
		//项目test里面创建了一个c.txt文件

		//创建目录,只能创建单级目录
		File parent1 = new File("E:/z");
		parent1.mkdir();
		//创建成功,E盘下创建了z文件夹

		//创建目录,创建多级目录
		File parent2 = new File("E:/x/xx");
		parent2.mkdirs();
		//创建成功,E盘下创建了x文件夹,x文件夹里有xx文件夹

    }
}

       (2)删除的方法

boolean

delete() 

删除由此抽象路径名表示的文件或目录。

       

        在删除文件夹的时候,只能删除空的文件夹。

        delete方法不走回收站

        代码示例

import java.io.File;
public class Test {

	public static void main(String[] args) throws Exception{	
		
		//E:\test\a\b\c.txt

		//删除文件夹a
		File f1 = new File("E:/test/a");
		boolean b1 = f1.delete();
		System.out.println(b1);//false
		//删除不成功,a文件夹非空
		
		//删除文件c.txt
		File f2 = new File("E:/test/a/b/c.txt");
		boolean b2 = f2.delete();
		System.out.println(b2);//true


		//删除文件夹b
		File f3 = new File("E:/test/a/b");
		boolean b3 = f3.delete();
		System.out.println(b3);//true

    }
}

     (3)重命名的方法

boolean

renameTo(File dest) 

重命名由此抽象路径名表示的文件。

       注

       如果在同一个文件夹下,修改路径,就是重命名。

       如果在不同文件夹下,修改路径,就是剪切。

       代码示例

import java.io.File;
public class Test {

	public static void main(String[] args) throws Exception{	
		
	/*
		//E盘的test文件夹下a.txt和b.txt都存在
		File f1 = new File("E:/test/a.txt");
		File f2 = new File("E:/test/b.txt");
		f1.renameTo(f2);
		//a.txt没有重命名
	*/

	/*
		//E盘的test文件夹下只有a.txt,b.txt文件夹不存在
		File f3 = new File("E:/test/a.txt");
		File f4 = new File("E:/test/b.txt");
		f3.renameTo(f4);
		//a.txt被重命名为b.txt
	*/

	
	/*
		//E盘的test文件夹下有a.txt,E盘下没有b.txt这个文件
		File f5 = new File("E:/test/a.txt");
		File f6 = new File("E:/b.txt");
		f5.renameTo(f6);
		//a.txt被重命名为b.txt,并且位置移到了E盘下
	*/
	

	/*
		//E盘的test文件夹下有a.txt,E盘下有b.txt这个文件
		File f7 = new File("E:/test/a.txt");
		File f8 = new File("E:/b.txt");
		f7.renameTo(f8);
		//a.txt没有被重命名,也没有被移动位置
	*/

    }
}

       (4)判断功能的方法

boolean

exists() 

测试此抽象路径名表示的文件或目录是否存在。

boolean

isDirectory() 

测试此抽象路径名表示的文件是否为目录。

boolean

isFile() 

测试此抽象路径名表示的文件是否为普通文件。

       代码示例

import java.io.File;
public class Test {

	public static void main(String[] args) throws Exception{	
		
		File f1 = new File("E:/test");
		File f2 = new File("E:/test/test.java");

		System.out.println(f1.exists());//true
		System.out.println(f2.exists());//true

		System.out.println(f1.isDirectory());//true
		System.out.println(f2.isDirectory());//false

		System.out.println(f1.isFile());//false
		System.out.println(f2.isFile());//true

    }
}

        (5)获取功能的方法

String

getAbsolutePath() 

返回此抽象路径名的绝对路径名字符串。

String

getPath() 

将此抽象路径名转换为路径名字符串。

String

getName() 

返回由此抽象路径名表示的文件或目录的名称。

long

length() 

返回由此抽象路径名表示的文件的长度。

String[]

list() 

获取当前文件夹下的所有文件和文件夹的名称,到一个字符串数组中

File[]

listFiles() 

获取当前文件夹下的所有文件和文件夹的File对象,到一个File对象数组中

      代码示例

import java.io.File;
public class Test {

	public static void main(String[] args) throws Exception{	
		
		//E:/test/a/b/....
		File f = new File("a/b");
		System.out.println(f.getAbsolutePath());//E:\test\a\b
		System.out.println(f.getPath());//a\b
		System.out.println(f.getName());//b
		System.out.println(f.length());//0

		String[] list = f.list();
		for(String ff:list){
			System.out.println(ff);
		}
		/*c.txt
		  D.txt
		  f.mp4
		  g
		 */

		 File[] fileList = f.listFiles();
		 for(File fff:fileList){
			 System.out.println(fff.length()+"..."+fff.getName());
		 }
		 /*6...c.txt
		   66...D.txt
		   20123312...f.mp4
		   0...g
		 */

    }
}

      练习:键盘录入一个字符串,表示一个文件夹路径,如果不是文件夹提示重新录入,打印当前文件夹下,所有的大于10M的后缀名名为.txt文件的绝对路径。

import java.io.File;
import java.util.Scanner;
public class Test {
	public static void main(String[] args) {		         
		
		Scanner sc = new Scanner(System.in);
		boolean flag = true;
		
		while(flag) {
			String str = sc.nextLine();
			File dir = new File(str);
			
			if(dir.isDirectory()) {
				
				File[] listFiles = dir.listFiles();
				for(File f:listFiles) {
					if(f.length()>10*1024*1024 && f.getName().endsWith(".txt")) {
						System.out.println(f.getAbsolutePath());
					}
				}							
				flag = false;
			}else {
				System.out.println("请重新输入");
				continue;
			}
			
		}			
	}
}

二、I/O概念

1.概念

      File对象可以表示存在的文件或文件夹,也可以表示不存在的。我们想要得到文件怎么办?File只是操作文件,文件的内容就需要使用I/O流技术。

      例如:在C盘下有一个名称为a.txt的文本文件。想要通过java程序读出来文件中的内容,需要使用IO流技术。同样想要将程序中的数据,保存到硬盘的文件中,也需要IO流技术。

       IO:Input和Output两个单词的缩写,input是输入,output是输出

         输入、输出,都是相对应用程序来说的。数据流向程序,叫做输入流。数据从程序流出叫做输出流。

         站在内存的角度看待方向,从其它设备进入到内存的,都是输入,从内存到其它设备,都是输出。

         Java中用于操作设备之间的数据传输的对象,都是IO流对象,都在io包下。

2.分类

         分类的方式有两种:按照功能可以分类,按照流向也可以分类。

         按照功能分类:

         字节流:可以直接操作字节的流对象

         字符流:可以直接操作字符的流对象

         按照流向分类:

         输入流:其他设备流到内存的流对象

         输出流:内存流到其他设备的流对象

         IO流的体系结构,根据分类,有四种流对象的类型。

         字节流

         字节输入流:InputStream

         字节输出流:OutputStream

         字符流

         字符输入流:Reader

         字符输出流:Writer

三、字节流

1.概述

         可以直接操作字节信息的流对象

         根据流向,可以分成字节输入流和字节输出流

         顶层抽象父类分别是:InputStream和OutputStream

         根据交互设备的不同,有不同的具体子类

2.InputStream

       字节输入流的顶层父类。InputStream是一个抽象类,不能直接创建对象,只能由子类创建对象,最常用子类  FileInputStream,用于和磁盘上的文件进行交互。

       (1)构造方法

FileInputStream(File file) 

通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。

FileInputStream(String name) 

通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。

        (2)常用方法

abstract int

read() 

从输入流读取数据的下一个字节。 结束标志为-1

int

read(byte[] b) 

从输入流读取一些字节数,并将它们存储到缓冲区 b 。 结束标志为-1

int

read(byte[] b, int off, int len) 

从输入流读取最多 len字节的数据到一个字节数组。

void

close() 

关闭此文件输入流并释放与流相关联的任何系统资源。

       read:一次读到一个字节。输出这个数据到控制台上发现,显示的是数字而不是字母。为什么?因为显示的是字母对应的码值,如果需要转成字符,可以强转成char类型。当read()的值为 -1 时,表示读到文件的末尾。

     read(byte[] buf):返回值为覆盖字节数组的长度。

    使用read方法的时候,流需要读一次就处理一次,可以将读到的数据装入到字节数组中,一次性的操作数组,可以提高效率。

     代码示例

import java.io.File;
import java.io.FileInputStream;
import java.util.Arrays;
public class Test {

	public static void main(String[] args) throws Exception{	
		//a.txt里面的内容为abc
		File f = new File("E:/test/a.txt");
		FileInputStream fis = new FileInputStream(f);

	/*
		System.out.println(fis.read());//97
		System.out.println(fis.read());//98
		System.out.println(fis.read());//99
		System.out.println(fis.read());//-1
		fis.close();
	*/

	/*
		int i = 0;
		while((i=fis.read())!=-1){
			System.out.print(i+" ");//97 98 99
		}
		fis.close();
	*/
	
	/*
		byte[] buf = new byte[2];
		int length = fis.read(buf);//length为返回的覆盖的长度
		System.out.println(length+"..."+Arrays.toString(buf));//2...[97, 98]
		length = fis.read(buf);
		System.out.println(length+"..."+Arrays.toString(buf));//1...[99, 98]   // fis.read读取的a.txt里面的内容,放到了数组buf里面,但是读取的只有c,所以长度为1。数组buf里面b没有被覆盖掉
		length = fis.read(buf);
		System.out.println(length+"..."+Arrays.toString(buf));//-1...[99, 98]  //c,b没有被覆盖掉
		fis.close();
	*/

	/*
		byte[] buf = new byte[2];
		int length = 0;
		while((length=fis.read(buf))!=-1){
			for(byte b:buf){
				System.out.print((char)b);//abcb
			}
		}
		fis.close();
	*/

	/*	byte[] buf = new byte[2];
		int length = 0;
		while((length=fis.read(buf))!=-1){
			String str = new String(buf,0,length);
			System.out.println(str);
		}
		//ab
		//c	
		fis.close();
	*/


    }
}

        (3)InputStream类型的read()方法和read(byte[] buf)方法的比较

          读取字节个数的区别

          read()方法一次只读取一个字节

          read(buf)方法一次可以读取多个字节,取决于数组的大小

          返回值不同

          read()读取到的是文件字节信息是作为返回值进行返回的。

          read(buf):读取到的字节存储到数组中,返回的是覆盖数组的长度。

3.OutputStream

         字节输出流的顶层抽象父类,最常用子类FileOutputStream。

       (1)构造方法   

FileOutputStream(File file) 

创建文件输出流以写入由指定的 File对象表示的文件。

FileOutputStream(File file, boolean append) 

创建文件输出流以写入由指定的 File对象表示的文件。

FileOutputStream(String name) 

创建文件输出流以指定的名称写入文件。

FileOutputStream(String name, boolean append) 

创建文件输出流以指定的名称写入文件。

           (2)常用方法

void

write(byte[] b) 

b.length字节从指定的字节数组写入此输出流。

void

write(byte[] b, int off, int len) 

从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。

abstract void

write(int b) 

将指定的字节写入此输出流。

void

close() 

关闭此文件输出流并释放与此流相关联的任何系统资源。

       代码示例

import java.io.File;
import java.io.FileOutputStream;
public class Test {

	public static void main(String[] args) throws Exception{	
		//输出流关联的文件若不存在,会自动创建
		//输出流关联的文件存在,则还会创建一个新的文件,默认覆盖原来的文件
	//	File f = new File("E:/a.txt");
	//	FileOutputStream fos = new FileOutputStream(f);

	/*
		fos.write(97);
		fos.write(98);
		fos.close(); //ab  a.txt不存在,自动创建,写上数据,a.txt的内容为ab
	*/

	/*
		fos.write(99);
		fos.close();//c   a.txt存在且原来的内容为ab,写入数据c之后,a.txt被覆盖内容变为c
	*/



		//不覆盖原来的文件
	//	File f2 = new File("E:/a.txt");
	//	FileOutputStream fos2 = new FileOutputStream(f2,true);

	/*
		fos2.write(100);
		fos2.write(101);//cde  a.txt存在原来的内容为c,写入数据后,没有被覆盖,内容变为cde
		fos2.close();
	*/

	/*
		byte[] buf = {102,103,104};
		fos2.write(buf);
		fos2.close();//cdefgh  a.txt存在原来的内容为cde,写入数据后,内容变为cdefgh
	*/

	/*	
		byte[] buf = {105,106,107};
		fos2.write(buf,0,2);
		fos2.close();//cdefghij   a.txt存在原来的内容为cdefgh,写入数据后,内容变为cdefghij 
	*/

    }
}

       练习:使用字节流完成文件拷贝

import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Test {

	public static void main(String[] args) throws Exception{	
		FileInputStream fis = new FileInputStream("E:/test/a.jpg");
		FileOutputStream fos = new FileOutputStream("E:/a.jpg");
	
		int i = 0;
		while((i=fis.read())!=-1){
			fos.write(i);
		}
		fis.close();
		fos.close();
    }
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Test {

	public static void main(String[] args) throws Exception{	
		FileInputStream fis = new FileInputStream("E:/test/a.jpg");
		FileOutputStream fos = new FileOutputStream("E:/a.jpg");

		byte[] b = new byte[1024];
		int length=0;
		while((length=fis.read(b))!=-1){
			fos.write(b,0,length);
		}

		fis.close();
		fos.close();
    }
}

          练习扩展-拷贝的时候显示进度百分比

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Test {

	public static void main(String[] args) throws Exception{	
		File srcFile = new File("E:/test/a.jpg");
		FileInputStream fis = new FileInputStream(srcFile);
		
		double srcLength = srcFile.length();
		
		File desFile = new File("E:/a.jpg");
		FileOutputStream fos = new FileOutputStream(desFile);
		
		byte[] buf = new byte[1024];
		int length = 0;

		while((length=fis.read(buf))!=-1){
			fos.write(buf, 0, length);
			String str = String.valueOf(desFile.length()/srcLength*100);
			str = (String) str.subSequence(0, 5);
			System.out.println(str+"%");
			
		}
		fis.close();
		fos.close();
	}
}

4.字节型缓冲流BufferedInputStream和BufferedOutputStream

      (1)概念

       BuffereInputStream和BufferedOutputStream类可以通过减少读写次数来提高输入和输出的速度。它们内部有一个缓冲区,用来提高处理效率。查看API文档,发现可以指定缓冲区的大小。其实内部也是封装了字节数组。没有指定缓冲区大小,默认的字节是8192。

       显然缓冲区输入流和缓冲区输出流要配合使用。首先缓冲区输入流会将读取到的数据读入缓冲区,当缓冲区满时,或者调用flush方法,缓冲输出流会将数据写出。

       注意:当然使用缓冲流来进行提高效率时,对于小文件可能看不到性能的提升。但是文件稍微大一些的话,就可以看到实质的性能提升了。

     (2)原理

       BufferedInputStream高效的原理:在该类型中准备了一个数组,存储字节信息,当外界调用read()方法想获取一个字节的时候,该对象从文件中一次性读取了8192个字节到数组中,只返回了第一个字节给调用者。将来调用者再次调用read方法时,当前对象就不需要再次访问磁盘,只需要从数组中取出一个字节返回给调用者即可,由于读取的是数组,所以速度非常快。当8192个字节全都读取完成之后,再需要读取一个字节,就得让该对象到文件中读取下一个8192个字节了。

        BufferedOutputStream高效的原理:在该类型中准备了一个数组,存储字节信息,当外界调用write方法想写出一个字节的时候,该对象直接将这个字节存储到了自己的数组中,而不刷新到文件中。一直到该数组所有8192个位置全都占满,该对象才把这个数组中的所有数据一次性写出到目标文件中。如果最后一次循环过程中,没有将数组写满,最终在关闭流对象的时候,也会将该数组中的数据刷新到文件中。

      (3)构造方法

BufferedInputStream(InputStream in) 

创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。

BufferedOutputStream(OutputStream out) 

创建一个新的缓冲输出流,以将数据写入指定的底层输出流。

       练习:拷贝mp4文件

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
public class Test {

	public static void main(String[] args) throws Exception{	
		FileInputStream fis = new FileInputStream("E:/test/a.mp4");//打开输入流
		FileOutputStream fos = new FileOutputStream("E:/b.mp4");//打开输出流
		BufferedInputStream bis = new BufferedInputStream(fis);//使用缓冲流
		BufferedOutputStream bos = new BufferedOutputStream(fos);//使用缓冲流

	/*
		int len = 0;
		while((len=bis.read())!=-1){
			bos.write(len);
		}

		bis.close();//关闭流
		bos.close();//关闭流
	*/

	/*
		byte[] buf = new byte[1024];
		int len = 0;
		while((len=bis.read(buf))!=-1){
			bos.write(buf,0,len);//数据写入缓存区,不会写入文件,字节流会自动将缓冲区的数据刷新到文件中,字符流不会自动将缓冲区的数据刷新到文件中。另外close方法在调用前,会默认调用flush方法,将缓冲区的数据刷新到文件中。
		}

		bis.close();//关闭流
		bos.close();//关闭流
	*/
	}
}

四、字符流

        字节流虽然也可以操作字符,但是在操作中英文混合的时候,判断起来较麻烦,需要判断中英文的标识,如果是英文的标识,直接读取一个字节,如果是中文的标识,需要再次读取一个字节拼接成两个字节,一块读取,这种操作判断起来非常 麻烦,恰巧字符流帮咱们封装这种判断标识的操作,所以直接可以使用字符流来操作中英文混合的字符。

1.字符输入流Reader

       Reader是抽象类,常用实现类FileReader

int

read() 

读一个字符  结束标志-1

int

read(char[] cbuf) 

将字符读入数组。 结束标志-1

       练习:读取文件中全部内容,输出到控制台。

import java.io.FileReader;
public class Test {

	public static void main(String[] args) throws Exception{	
		//a.txt中的内容为:abcd
		FileReader fr = new FileReader("E:/test/a.txt");

	/*
		int a = fr.read();
		System.out.println(a);//97
		a = fr.read();
		System.out.println(a);//98
		a = fr.read();
		System.out.println(a);//99
		a = fr.read();
		System.out.println(a);//100
		a = fr.read();
		System.out.println(a);//-1   表示读到文件末尾
		fr.close();
	*/

	/*
		int len = 0;
		while((len=fr.read())!=-1){
			System.out.print((char)len);//abcd
		}
		fr.close();
	*/

	/*
		char[] cs = new char[5];
		fr.read(cs);
		for(char c:cs){
			System.out.print(c);//abcd
		}
		fr.close();
	*/

    }
}

2.字符输出流Writer

       Writer是抽象类,常用实现类FileWriter。

void

write(char[] cbuf) 

写入一个字符数组。

abstract void

write(char[] cbuf, int off, int len) 

写入字符数组的一部分。

void

write(int c) 

写一个字符

void

write(String str) 

写一个字符串

void

write(String str, int off, int len) 

写一个字符串的一部分。

abstract void

close() 

关闭流,先刷新。

abstract void

flush() 

刷新流。

      案例:使用字符流完成文件拷贝。

import java.io.FileReader;
import java.io.FileWriter;
public class Test {

	public static void main(String[] args) throws Exception{	
		FileReader fr = new FileReader("E:/test/a.txt");
		FileWriter fw = new FileWriter("E:/a.txt");

	/*
		int i=0;
		while((i=fr.read())!=-1) {
			fw.write(i);//数据写入到缓冲区中
			fw.flush();//将缓冲区的数据刷新到文件中
		}
		fr.close();
		fw.close();
	*/

	/*
		char[] chs = new char[10];
		int len = 0;
		while((len=fr.read(chs))!=-1){
			fw.write(chs,0,len);
			fw.flush();//如果不刷新,文件看不到数据
		}
		fr.close();
		fw.close();//close()方法在调用前默认先调用flush方法
	*/
    }
}

3.输出流中的close和flush的区别

      close方法在调用的时候默认先会调用flush

      close方法把流对象关闭掉了

      flush只是把缓冲区的数据刷新到文件中

4.字符型缓冲流BufferedReader和BufferedWriter

      (1)原理

       BufferedReader:每次调用read方法,只有第一次从磁盘中读取了8192个字符,存储到该类型对象的缓冲区数组中,将其中一个返回给调用者,再次调用read方法时,就不需要再访问磁盘,直接从缓冲区中拿一个出来即可,效率提升了很多。

      BufferedWriter:每次调用write方法,不会直接将字符刷新到文件中,而是存储到字符数组中,等字符数组写满了,才一次性刷新到文件中,减少了和磁盘交互的次数,提升了效率。

     (2)特有方法

String

readLine() 

读一行文字。 结束标记null

void

newLine() 

写一行行分隔符。

       代码示例

import java.io.FileReader;
import java.io.BufferedReader;
public class Test {

	public static void main(String[] args) throws Exception{	
		FileReader fr = new FileReader("E:/test/a.txt");
		BufferedReader br = new BufferedReader(fr);
	
	/*
		String line = br.readLine();
		System.out.println(line);//1111
		line = br.readLine();
		System.out.println(line);//2222
		line = br.readLine();
		System.out.println(line);//3333
		line = br.readLine();
		System.out.println(line);//4444
		line = br.readLine();
		System.out.println(line);//null
		br.close();
	*/
		String line2 = null;
		while((line2=br.readLine())!=null){
			System.out.println(line2);
		}
		br.close();
		//1111
		//2222
		//3333
		//4444
    }
}
import java.io.FileWriter;
import java.io.BufferedWriter;
public class Test {

	public static void main(String[] args) throws Exception{	
		//a.txt文件不存在,自动创建
		FileWriter fw = new FileWriter("E:/test/a.txt");
		BufferedWriter bw = new BufferedWriter(fw);

		bw.write("abcd");
		bw.newLine();//换行
		bw.write("1234");
		bw.close();
		//abcd
		//1234
		
    }
}

      默认的FileWriter方法新值会覆盖旧值,想要实现追加功能需要使用如下构造函数创建输出流 ,append值为true即可。

FileWriter(File file, boolean append) 

给一个File对象构造一个FileWriter对象。

FileWriter(String fileName, boolean append) 

构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。

     代码示例

import java.io.FileWriter;
import java.io.BufferedWriter;
public class Test {

	public static void main(String[] args) throws Exception{	
		//a.txt文件存在,如果写数据,不被覆盖
		//a.txt原来的内容为1234
		FileWriter fw = new FileWriter("E:/test/a.txt",true);
		BufferedWriter bw = new BufferedWriter(fw);

		bw.write("zxc");
		bw.close();
		//a.txt中的内容为1234zxc
    }
}

      练习:使用字符型缓冲流完成文件拷贝

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

public class Test {

	public static void main(String[] args) throws Exception{	
		FileReader fr = new FileReader("E:/test/a.txt");
		FileWriter fw = new FileWriter("E:/a.txt");
		BufferedReader br = new BufferedReader(fr);
		BufferedWriter bw = new BufferedWriter(fw);

		char[] chs = new char[10];
		int len = 0;
		while((len=br.read(chs))!=-1){
			bw.write(chs,0,len);
		}
		br.close();
		bw.close();
    }
}

       练习:自己定义一个类实现readLine()方法

import java.io.Reader;
import java.io.FileReader;
public class Test {

	public static void main(String[] args) throws Exception{	
		FileReader fr = new FileReader("E:/test/a.txt");
		MyBufferedReader mr = new MyBufferedReader(fr);

	/*
		String line = mr.myReadLine();
		System.out.println(line);
		line = mr.myReadLine();
		System.out.println(line);
	*/
		String line = null;
		while((line=mr.myReadLine())!=null){
			System.out.println(line);
		}
    }
}
class MyBufferedReader{
	Reader reader;
	public MyBufferedReader(Reader reader){
		this.reader = reader;
	}
	public String myReadLine() throws Exception{
		StringBuilder sb = new StringBuilder();
		int i = 0;
		while((i=reader.read())!=-1){
			char c = (char)i;
			if(c=='\r'){
				continue;
			}
			if(c=='\n'){
				break;
			}
			sb.append(c);
		}
		if(sb.length()==0){
			return null;
		}
		return sb.toString();
	}
}

五、包装方式的灵活性

       System.in:标准输入流,默认关联键盘

       通过转换流InputStreamReader 可以将 System.in包装成字符流。

       通过BufferedReader将InputStreamReader包装成带有缓冲区的字符流,可以调用readLine().

       代码示例

import java.io.InputStreamReader;
import java.io.InputStream;
import java.io.BufferedReader;
public class Test {

	public static void main(String[] args) throws Exception{	
		//源:键盘  目的:文件
		//Scanner sc = new Scanner(Sytsem.in);

		//实现键盘录入
		InputStream is = System.in;
		//字节流转换为字符流
		InputStreamReader isr = new InputStreamReader(is);
		//高效
		BufferedReader br = new BufferedReader(isr);
		String str = br.readLine();
		System.out.println(str);

    }
}

六、转换流

      有时,我们需要将字节流转换为字符流,使用InputStreamReader和OutputStreamWriter。

1.编码表

      GBK:国标码 gb2312,定义的英文字母和中文字符,在GBK编码表中,英文字符占一个字节,中文字符占两个字节。

      UTF-8:万国码,定义了全球所有的语言的所有的符号,定义了这些符号和数字的对应关系,英文字母使用一个字节进行存储,中文字符使用三个字节进行存储。

      乱码:编码格式和解码格式不一致。

2.输入转换流InputStreamReader

     构造方法

InputStreamReader(InputStream in) 

创建一个使用默认字符集的InputStreamReader。

InputStreamReader(InputStream in, Charset cs) 

创建一个使用给定字符集的InputStreamReader。

    示例代码


import java.io.InputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
public class Test {

	public static void main(String[] args) throws Exception{	
		
		InputStream is = new FileInputStream("E:/test/a.txt");

		//字节流转换诶字符流
		InputStreamReader isr = new InputStreamReader(is);
		//InputStreamReader isr = new InputStreamReader(is,"utf-8");
		//InputStreamReader isr = new InputStreamReader(is,"GBK");
		
		//高效缓冲流
		BufferedReader br = new BufferedReader(isr);
		//特有方法
		System.out.println(br.readLine());
		System.out.println(br.readLine());
		br.close();

    }
}

3.输出转换流OutputStreamWriter

     构造方法

OutputStreamWriter(OutputStream out) 

创建一个使用默认字符编码的OutputStreamWriter。

OutputStreamWriter(OutputStream out, Charset cs) 

创建一个使用给定字符集的OutputStreamWriter。

   示例代码

import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
public class Test {

	public static void main(String[] args) throws Exception{	
		
		OutputStream os = new FileOutputStream("E:/a.txt");
		//转换流
		OutputStreamWriter osr = new OutputStreamWriter(os);
		//高效缓冲流
		BufferedWriter bw = new BufferedWriter(osr);
		bw.write("aa");
		bw.write("11");
		//特有方法
		bw.newLine();
		bw.write("中国");
		bw.close();      
    }
}

七、其它流

1.LineNumberReader

       带有行号的流对象,同时也是BufferedReader子类对象,带有高效缓冲区。

       常用方法

int

getLineNumber() 

获取当前行号。

void

setLineNumber(int lineNumber) 

设置当前行号。

       示例代码

import java.io.Reader;
import java.io.FileReader;
import java.io.LineNumberReader;

public class Test {
	public static void main(String[] args) throws Exception{
		Reader r = new FileReader("E:/a.txt");
		//高效缓冲流
		LineNumberReader inr = new LineNumberReader(r);
		System.out.println(inr.getLineNumber()+":"+inr.readLine());
		System.out.println(inr.getLineNumber()+":"+inr.readLine());
		System.out.println(inr.getLineNumber()+":"+inr.readLine());
		
		//设置行号
		inr.setLineNumber(5);
		System.out.println(inr.getLineNumber()+":"+inr.readLine());
		System.out.println(inr.getLineNumber()+":"+inr.readLine());

		inr.close();

	}

}

2.ByteArrayOutputStream

        名字是输出流,体系也在IO体系中,但是其实没有在内存和其它设备之间进行交互,仅仅是在内存中做了拷贝,所以没有实现真的IO操作

       作用:当从某个文件汇总,读取了一部分不完整的字节信息的时候,需要找一个容器存储这些不完整的信息,ByteArrayOutputStream就可以充当容器,因为这个容器可以自动增长。

      本质:一个可自动增长的字节数组,并且提供一些常用的方法。

     常用方法

byte[]

toByteArray() 

获取对象中的字节信息,返回一个字节数组

String

toString() 

使用平台的默认字符集将缓冲区内容转换为字符串解码字节。

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;

public class Test {
	/*
	 * 有一个文件,a.txt,有一句话:你好你好你好呀
	 * 使用一个大小为3的字节数组,将文件中的内容,打印在控制台
	 * */
	public static void main(String[] args) throws Exception {
		
		//method01(); //你?媚?好?愫?呀
		//method02(); //你好你好你好呀
					  //[-60, -29, -70, -61, -60, -29, -70, -61, -60, -29, -70, -61, -47, -67]
	}

	private static void method01() throws Exception {
		FileInputStream fis = new FileInputStream("E:/a.txt");
		byte[] buf = new byte[3];
		int len = 0;
		while((len=fis.read(buf))!=-1) {
			System.out.print(new String(buf,0,len));
		}
		fis.close();
	}

	private static void method02() throws Exception {
		FileInputStream fis = new FileInputStream("E:/a.txt");
		//准备一个用缓存不完整字节信息的容器,容器可以自动增长,不需要指定大小
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte[] buf = new byte[3];
		int len = 0;
		while((len=fis.read(buf))!=-1) {
			//System.out.println(new String(buf,0,len));
			//本次读取到多个字节,就写到字节输出流中
			baos.write(buf, 0, len);
		}
		//将对象中的字节信息,转成字符串
		System.out.println(baos.toString());
		//将对象中的完整的字节信息返回
		byte[] arr = baos.toByteArray();
		System.out.println(Arrays.toString(arr));
		baos.close();
	}

}

3.随机访问流RandomAccessFile

        支持对文件进行非顺序的、随机的访问。

        不是流对象,不在IO体系中,但是比较流对象更加的强大,既可以读,也可以写,又可以随机访问。

        (1)构造方法

RandomAccessFile(File file, String mode) 

创建一个随机访问文件流从File参数指定的文件中读取,并可选地写入文件。

RandomAccessFile(String name, String mode) 

创建随机访问文件流,以从中指定名称的文件读取,并可选择写入文件。

       mode:操作文件的模式:r只读模式,rw读写模式

      (2)常用方法

       read():读取一个字节

       readXxx():可以将基本类型直接从文件中读取

       read(buf):读取的内存存储到数组中

       write(int b):将一个字节的内容写到文件中

       write(buf):将字节数组的数据写文件中

       writeXxx():可以将基本数据类型写到文件中

       readLine():读取一行

       seek(long pos):用于设定光标的位置,从指定位置开始读写

       它实现了DataInput 和DataOutput 接口,并提供了三个额外方法

       int skipBytes(int)   向前移动文件指针指定的字节数

       void seek(long)     文件指针定位,参数为偏移量

       long getFilePointer()返回当前文件指针的位置(相对文件首的偏移量)

       代码示例

import java.io.RandomAccessFile;

public class Test {
	public static void main(String[] args) throws Exception{
		
		//abcdefg
		RandomAccessFile raf = new RandomAccessFile("a.txt","rw");
		
		raf.skipBytes(2);//向后移动2个字节,再开始读数据
		System.out.println(raf.read());//99
		
		raf.seek(5);//直接把指针定位的某个位置
		System.out.println(raf.read());//102

		long p = raf.getFilePointer();
		System.out.println(p);//6
		raf.close();

	}
}

4.PrintStream和PrintWriter

       (1)PrintStream

        打印字节流:System.out就是这个类型的流对象。

        在该类型中,提供了很多的print和println的方法,可以将各种数据类型转换成字符串进行输出。

        注意事项:

       1、如果直接使用System.out的方式来获取PrintStream的对象,这个对象默认关联的输出目的地就是控制台;如果手动创建PrintStream的对象,那么关联的就是指定的设备。

       2、PrintStream中重载了很多print和println的方法,有两个比较特殊,print(char[] arr)、print(Object obj)。一般的数组使用print方法,会直接打印数组的地址,唯独字符数组打印之后出现的是数组的内容。普通的数组调用的都是println(Object obj)方法,先获取了数组的toString的内容,也就是数组的地址,而字符数组调用的是println(char[] arr)方法,没有运行String.valueOf方法,没有获取数组的toString内容,而是直接调用了write方法用于输出字符内容。

       构造方法

PrintStream(File file) 

使用指定的文件创建一个新的打印流,而不需要自动换行。

PrintStream(OutputStream out) 

创建一个新的打印流。

PrintStream(OutputStream out, boolean autoFlush) 

创建一个新的打印流。

      代码示例

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

public class Test {
	public static void main(String[] args) throws Exception{
		//FileOutputStream fis = new FileOutputStream("E:/test/a.txt");//a.txt存在,内容被覆盖
		FileOutputStream fis = new FileOutputStream("E:/test/a.txt",true);//a.txt存在,追加不覆盖
		PrintStream ps = new PrintStream(fis);
		ps.println("Hello");
		ps.println(true);
		ps.println(123);
		ps.close();

	}
}

      (2)PrintWriter

        构造方法

PrintWriter(File file) 
使用指定文件创建不具有自动行刷新的新 PrintWriter。

PrintWriter(File file, String csn) 
创建具有指定文件和字符集且不带自动刷行新的新 PrintWriter。

PrintWriter(OutputStream out) 
根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。

PrintWriter(OutputStream out, boolean autoFlush) 
通过现有的 OutputStream 创建新的 PrintWriter。

PrintWriter(String fileName) 
创建具有指定文件名称且不带自动行刷新的新 PrintWriter。

PrintWriter(String fileName, String csn) 
创建具有指定文件名称和字符集且不带自动行刷新的新 PrintWriter。

PrintWriter(Writer out) 
创建不带自动行刷新的新 PrintWriter。

PrintWriter(Writer out, boolean autoFlush) 
创建新 PrintWriter。

       注

       1.该类中除了有write方法外,还提供了print方法和println方法,用来写数据。

       2.autoFlush:代表是否自动刷新,默认为flase。如果不刷新,写入的数据存在缓冲区中,不会在文件中。但是close方法在调用前默认先调用flush方法。

       FileWriter fw = new FileWriter("1.txt",true);

       第二个参数代表是否追加,如果设置true,代表追加,如果设置为false,直接产生覆盖,默认为false。

       练习:使用BufferedReader和PrintWriter完成文件拷贝。

import java.io.FileReader;
import java.io.BufferedReader;
import java.io.PrintWriter;

public class Test {
	public static void main(String[] args) throws Exception{
		FileReader fr = new FileReader("E:/test/a.txt");
		BufferedReader br = new BufferedReader(fr);
		PrintWriter pw = new PrintWriter("E:/a.txt");
		String str = null;
		while((str=br.readLine())!=null){
			pw.println(str);
		}
		br.close();
		pw.close();

	}
}

5.标准输入流和输出流

       标准输入流:System.in

       InputStream 字节输入流

       设备:键盘

       包装:BufferedInputStream

       标准输出流:System.out

       PrintWriter

       设备:默认关联到控制台

       print println()

6.序列流SequenceInputStream(也称合并流)

      构造方法

SequenceInputStream(Enumeration<? extends InputStream> e) 

初始化新创建 SequenceInputStream通过记住参数,它必须是一个 Enumeration产生对象,它们的运行时类型是 InputStream 。

SequenceInputStream(InputStream s1, InputStream s2) 

通过记住两个 SequenceInputStream来初始化新创建的SequenceInputStream,这些参数将按顺序读取,首先是 s1 ,然后是 s2 ,以提供要从此 SequenceInputStream读取的字节。

     示例代码:合并两个流

import java.io.SequenceInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
class Test {
	public static void main(String[] args) throws Exception{
		
		FileInputStream fis1 = new FileInputStream("E:/a.txt");
		FileInputStream fis2 = new FileInputStream("E:/b.txt");
		SequenceInputStream s1 = new SequenceInputStream(fis1,fis2);
		int len = 0;
		byte[] byt = new byte[1024];
		FileOutputStream fos = new FileOutputStream("E:/c.txt");
		while((len=s1.read(byt))!=-1){
			fos.write(byt,0,len);
		}
		s1.close();	
	}
}

     示例代码:合并多个流

import java.io.InputStream;
import java.io.FileInputStream;
import java.util.LinkedHashSet;
import java.util.Iterator;
import java.util.Enumeration;
import java.io.SequenceInputStream;
import java.io.FileOutputStream;
class Test {
	public static void main(String[] args) throws Exception{
		InputStream in1 = new FileInputStream("E:/a.txt");
		InputStream in2 = new FileInputStream("E:/b.txt");
		InputStream in3 = new FileInputStream("E:/c.txt");

		LinkedHashSet<InputStream> set = new LinkedHashSet<InputStream>();
		set.add(in1);
		set.add(in2);
		set.add(in3);

		final Iterator<InputStream> iter = set.iterator();

		SequenceInputStream sin = new SequenceInputStream(
			new Enumeration<InputStream>(){
			@Override
				public boolean hasMoreElements(){
				return iter.hasNext();
			}
			@Override
				public InputStream nextElement(){
				return iter.next();
			}
		});

		FileOutputStream out = new FileOutputStream("E:/d.txt");

		for(int b=-1;(b=sin.read())!=-1;){
			out.write(b);
		}
		sin.close();
		out.close();
		
	}
}

7.数据流

         数据流支持二进制的IO--基本数据类型的值(boolean,char,byte,double等)以及String值。所有的数据流,实现了DataInput/DataOutput接口。

        (1)DataOutputStream

          常见方法

void

write(int b) 

将指定的字节(参数 b的低8位)写入底层输出流。

void

writeInt(int v) 

将底层输出流写入 int作为四字节,高位字节。

void

writeBoolean(boolean v) 

boolean写入底层输出流作为1字节值。

void

writeShort(int v) 

short写入底层输出流作为两个字节,高字节优先。

void

writeLong(long v) 

long写入底层输出流,为8字节,高字节为首。

void

writeUTF(String str) 

使用 modified UTF-8编码以机器无关的方式将字符串写入基础输出流。

       代码示例

import java.io.DataOutputStream;
import java.io.FileOutputStream;
class Test {
	public static void main(String[] args) throws Exception{
		
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("a.txt"));
		//dos.write(12);//只会写int的后八位,前面的都舍弃了
		dos.writeInt(23);
		dos.writeBoolean(true);
		dos.writeUTF("hello");
		
	}
}

        (2)DataInputStream

         常见方法

int

readInt() 

一次读取四个字节,并将其转成int值。

short

readShort() 

一次读取两个字节,并将其转成int值。

long

readLong() 

 一次读取八个字节,并将其转成int值。

String

readUTF() 

按照utf-8修改版读取字符。注意,它只能读writeUTF()写入的字符数据。

       代码示例

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

class Test {
	public static void main(String[] args) throws Exception{
		
		DataInputStream dis = new DataInputStream(new FileInputStream("a.txt"));

		int x = dis.readInt();
		boolean f = dis.readBoolean();
		String str = dis.readUTF();
	}
}

8.对象序列化(对象流)

      当创建对象时,程序运行时它就会存在,但是程序停止时,对象也就消失了.但是如果希望对象在程序不运行的情况下仍能存在并保存其信息,将会非常有用,对象将被重建并且拥有与程序上次运行时拥有的信息相同。可以使用对象的序列化。

      对象的序列化:将内存中的对象直接写入到文件设备中或者在网络上传输。

      对象的反序列化:将文件设备中持久化的数据转换为内存对象。

      基本的序列化由两个方法产生:一个方法用于序列化对象并将它们写入一个流,另一个方法用于读取流并反序列化对象。

     (1)ObjectOutputStream

      OutputStream的子类

      构造方法

 

ObjectOutputStream(OutputStream out) 

将一个普通的字节输出流包装成对象输出流 

     最常用方法

void

writeObject(Object obj) 

将指定的对象写入ObjectOutputStream。

      代码示例:序列化

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.File;
class Test {
	public static void main(String[] args) throws Exception{
		Cat cat = new Cat("tom",3);
		FileOutputStream fos = new FileOutputStream(new File("E:/cat.txt"));
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		
		oos.writeObject(cat);
		System.out.println(cat);
		oos.close();

	}
}
class Cat  implements Serializable{
	String name;
	int age;
	public Cat(){}
	public Cat(String name,int age){
		this.name=name;
		this.age=age;
	}
	public String toString(){
		return "name:"+name+"...age:"+age;
	}
}

       注意:Cat类的对象,想要被保存到文件中,Cat类必须实现Serializable接口

     (2)ObjectInputStream

      常用方法

Object

readObject() 

从ObjectInputStream读取一个对象。

      代码示例:反序列化

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.io.File;
class Test {
	public static void main(String[] args) throws Exception{
		
		FileInputStream fis = new FileInputStream(new File("E:/cat.txt"));
		ObjectInputStream ois = new ObjectInputStream(fis);
		Object readObject = ois.readObject();
		Cat cat2 = (Cat) readObject;
		System.out.println(cat2);
		fis.close();

	}
}
class Cat  implements Serializable{
	String name;
	int age;
	public Cat(){}
	public Cat(String name,int age){
		this.name=name;
		this.age=age;
	}
	public String toString(){
		return "name:"+name+"..."+"age:"+age;
	}
}

     (3)注意事项

        连续序列化对象:当连续两次序列化同一对象引用时,并不会有两个对象被序列化。第2次只是输出一个序列号编号。即使当第一序列化完成后,修改了对象的值,再次进行序列化,被序列化对象的值也不会发生变化。

       示例代码

import java.io.ObjectOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.FileInputStream;
import java.io.Serializable;

class Test {
	public static void main(String[] args) throws Exception{
		
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
		Student stu = new Student(1,"zhangsan",20);
		oos.writeObject(stu);		
		stu.name="李四";
		//和第一次写的是同一个对象,第二次序列化时并不会有两个对象被序列化。第二次只是输出一个序列号编号
		oos.writeObject(stu);
		oos.close();

		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
		Student stu1 = (Student)ois.readObject();
		System.out.println(stu1);

		Student stu2 = (Student)ois.readObject();
		System.out.println(stu2);

		//Student stu3 = (Student)ois.readObject();
		//System.out.println(stu3);//只存了两次,第3次报错

		ois.close();

		/*id:1...name:zhangsan...age:20
		  id:1...name:zhangsan...age:20
		  */
		
	}
}
class Student implements Serializable{
	int id;
	String name;
	int age;
	public Student(){}
	public Student(int id,String name,int age){
		this.id=id;
		this.name=name;
		this.age=age;
	}
	public String toString (){
		return "id:"+id+"...name:"+name+"...age:"+age;
	}
}

9.压缩流

       压缩流 ZipInputStream 和 解压缩流 ZipOutputStream 位于java.util包

      (1)压缩流ZipOutputStream

       代码示例

package com.xj.Test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Zos {
	
	ZipOutputStream zos = null;
	
	public Zos (){
		
		try {
			
			zos= new ZipOutputStream(new FileOutputStream("D:/xyz.zip"));
			
		} catch (FileNotFoundException e) {
			
			e.printStackTrace();
		}
	}
	
	
	public static void main(String[] args) {
		
		File f = new File("E:/aaaa");
		
		Zos  zzz = new Zos ();
		
		zzz.zip(f);
		
		zzz.close();
        
	}
	
	
	// 传入要压缩的文件夹
	public  void zip(File file){
		
		File[] fs = file.listFiles();
		
		for(File f:fs){
			
			if(f.isFile()){
				
				zipFile(f);
				
			}else{
				zip(f);  // 是文件夹,递归调用
			}
		}
	}
	
	//压缩文件
	public  void zipFile(File f){
		
		try {
			// 如果传入ZipEntry的文件名带有目录,在压缩时,压缩包中就会生成目录
			zos.putNextEntry(new ZipEntry(f.getPath().substring(3))); 
			// 一个ZipEntry是一个要被压缩的项
			
			InputStream is = new FileInputStream(f);
			
			byte[] b = new byte[1024];
			
			int len = 0;
			
			while((len=is.read(b))!=-1){
				zos.write(b,0, len);
			}
			
			zos.closeEntry();
			is.close();
			
		} catch (IOException e) {
			
			e.printStackTrace();
		}
	}
	
	public void close(){
		
		try {
			zos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

      (2)解压缩流ZipInputStream(了解)

       代码示例

package com.xj.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

public class UnZip {
	
	public static void main(String[] args) throws Exception{
		
		   File file = new File("D:/xyz.zip");// 指向要被解压的zip文件
		   ZipInputStream zis = new ZipInputStream(new FileInputStream(file)); // 读取zip格式压缩的文件
		   
		   ZipFile zf = new ZipFile(file); // 用来获取流 读取被压缩的文件
		   
		    ZipEntry entry = null;
			InputStream is = null;
			OutputStream os = null;
			
			while((entry=zis.getNextEntry())!=null){
				
				if(entry.isDirectory()){  // entry是一个文件夹
					
					File ff = new File(entry.getName());
					ff.mkdirs(); // 创建文件夹

				}else{
					
					System.out.println("解压缩" + entry.getName() + "......");
					File outFile = new File("D:/" + entry.getName()); // 定义输出文件
					
					if (!outFile.getParentFile().exists()) {
						outFile.getParentFile().mkdirs();
					}
					if (!outFile.exists()) {
						outFile.createNewFile();
					}

					is = zf.getInputStream(entry);
					os = new FileOutputStream(outFile);
					byte[] b = new byte[1024];
					int len = 0;
					while ((len = is.read(b)) != -1) {
						os.write(b, 0, len);
					}
				}
			}
			
			is.close();
			os.close();
			zis.close();

	   }
}

八、Properties

1.概述

       Properties对象表示一个持久的属性集

       属性集:属性名称和属性值的对应关系,其实还是一个双列集合

       持久的:可以保存到流中,也可以从流中读取。可以很方便和文件进行交互

       Properties没有泛型,因为表示一个配置文件中的信息,而配置文件中都是字符串,所以Properties类型不需要使用广泛的类型,存储的键和值都是字符串类型

       Properties是Hashtable的子类,所以可以当做普通的Map来使用。

2.特有的方法

String

getProperty(String key) 

使用此属性列表中指定的键搜索属性。

Object

setProperty(String key, String value) 

致电 Hashtable方法 put 。

Enumeration<?>

propertyNames() 

       代码示例

import java.util.Properties;
import java.util.Enumeration;

public class Test {
	public static void main(String[] args) throws Exception{
		
		Properties props = new Properties();
		props.setProperty("01","zz");
		props.setProperty("02","xx");
		props.setProperty("03","cc");

		System.out.println(props.get("01"));//zz

		Enumeration enumeration = props.propertyNames();
		while(enumeration.hasMoreElements()){
			String key = (String)enumeration.nextElement();
			System.out.println(key+".."+props.getProperty(key));
		}
	/*
		03..zz
		02..xx
		01..cc
	*/
	}
}

3.Properties和配置文件交互的方式

void

load(InputStream inStream) 

从输入字节流读取属性列表(键和元素对)。

void

store(Writer writer, String comments) 

将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式输出到输出字符流。

      load:输入流到集合中

      store:集合到输出流

      注意事项

      1、使用prop修改了属性值之后,文件中的属性值不会发生变化,因为prop只修改了内存中的对象数据,没有同步到文件中。必须调用store方法之后,才能将变化同步到文件。

      2、调用store方法时,一般需要指定更新属性的原因,即第二个参数comments,如果没有注释,可以传入null;如果有注释,必须是纯英文注释。

      代码示例

import java.util.Properties;
import java.io.FileInputStream;
import java.io.FileWriter;


public class Test {
	public static void main(String[] args) throws Exception{
		
		Properties props = new Properties();
		FileInputStream fis = new FileInputStream("E:/test/config.properties");
		//将流中的内容,加载到属性集合容器中
		props.load(fis);
		System.out.println(props);

		props.setProperty("01","GG");
		FileWriter fw = new FileWriter("E:/test/config.properties");
		//将集合中的数据加载到输出流-->文件
		props.store(fw, "zz-->GG");

	}
}

       案例:一个软件,免费使用3次,超过3次要续费。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.util.Properties;

public class Test {

	public static void main(String[] args) throws Exception {
		Properties props = new Properties();
		FileInputStream fis = new FileInputStream("count.properties");
		props.load(fis);
		String value = props.getProperty("count");
		int count = 0;
		
		if(value==null) {
			//第一次使用
			props.setProperty("count", "1");
			count = 1;
		}else {		
			value = props.getProperty("count");
			if("3".equals(value)) {
				System.out.println("亲,您好,软件免费次数已使用完毕,请续费!");
				return;
			}
			count = Integer.parseInt(value)+1;
			props.setProperty("count", count+"");
			
		}
		
		System.out.println("欢迎您第"+count+"次使用本软件!");
		FileWriter fw = new FileWriter("count.properties");
		props.store(fw,null);
		
	}

}

九、IO中保证流对象关闭的标准格式

1.捕获异常和声明异常的原则

        如果知道如何处理异常,那么就使用try...catch来进行处理;如果不知道如何处异常,那么就使用throws来对异常进行声明,或者当将当前的异常对象封装成其它对象,优化处理。

       如果你希望程序出现异常之后,继续运行下去,那么就使用try...catch处理,如果出现异常之后,希望当前的方法代码进行停止,那么就使用throws。

2. IO中保证流对象关闭的格式(jdk1.7之前)

       将流对象创建使用与流对象的关闭,分离开,分别放在try...finally,就算流对象在创建或者使用的过程发生异常,流对象照样关闭。

       为了使对象的引用泛型更大一些,定义在try...catch..finally之外

       为了避免在关闭流对象的时候,出现空指针异常,做一个判断为空的操作

       为让保证同时让两个流对象在关闭时互不影响,分别写在try...finally块中

       代码示例

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

class Test {
	public static void main(String[] args){
		FileReader fr = null;
		FileWriter fw = null;
		try {
			fr = new FileReader("1.txt");
			fw = new FileWriter("2.txt");
			int i = 0;
			while((i=fr.read())!=-1) {
				fw.write(i);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			
			try {
				if(fr!=null) {
					fr.close();
				}						
			} catch (IOException e) {
				e.printStackTrace();
			}finally {
				if(fw!=null) {
					try {
						fw.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
			
		}

	}

}

3.IO中保证流对象关闭的格式(jdk1.7之后)

       格式:try(

              流对象的创建;

       ){

              流对象的使用;

       }

       流对象使用之后,不需要手动关闭,因为这个格式已经帮助咱们自动闭了流对象。

      代码示例

import java.io.FileReader;
import java.io.FileWriter;

public class Test {

	
	public static void main(String[] args) throws Exception {
		
		try(
			FileReader fr = new FileReader("1.txt");
			FileWriter fw = new FileWriter("2.txt");
		){
			
			int i = 0;
			while((i=fr.read())!=-1) {
				fw.write(i);
			}
		}
		

	}

}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值