java中 File类 和 IO流相关操作

1. file类的使用

1) 概述

在现实中
		1. 目录(directory): 目录指的是文件夹,用来存放文件。
		2. 文件(File): 文件,用来保存一些数据。
		3. 路径(Path):指的是计算机的一个位置,可以是文件的位置,也可以是文件夹的位置。

在java中
	1. 有一个类,可以表示计算机中的文件和文件夹,这个类叫File类。
	2. 虽然File的本义是文件,但是在java中,File类既可以表示文件,也可以表示文件夹。
	3. 也就是说,我们可以调用File类的一些方法,来完成对文件或者是对文件夹的操作。
	4. java.io.File类是文件和文件夹路径名的抽象表示,所指向的路径对应的文件或者文件夹不一定真实存在。

2) 构造方法

1. file(String pathName) 
		参数是一个路径名称,这个路径名称可以是文件,也可以是文件夹。 
					
2. file(String parent,String child)
		参数是2个路径(父路径+子路径),根据父路径和子路径组合构造成一个File对象。父路径和自路径都是字符串。	
		比如: 
		现在有一个文件路径为:  D:\it\aa\bb\cc\dd.txt 
		那么:	 对应的父路径 parent : D:\it\aa\bb\cc 
  					 对应的子路径 child :   dd.txt 

3.	File(File parent,String child)
		根据父路径和子路径组合构造一个File对象 。
  		 父路径是 File 类型,子路径是 String 类型。
   		因为父路径是File类型,所以等于是先用构造方法构造一个父路径,再去拼接一个子路径,因为还有子路径,所以这样构造的父路径一般是文件夹,而不是文件。

注意点: 
				 使用构造方法可以创建出一个File对象,这个File对象表示的是计算机中的一个文件或者文件夹,
				 并且表示的这个文件或者文件夹,可以在计算机中是实际存在的,也可以是不存在的。

3) 相对路径和绝对路径

	1. 相对路径
		一个不是特别详细的路径,没有从盘符开始。
		没有从盘符开始的路径就是: 相对路径,在idea中,相对路径相对的是当前项目。	
		
	2. 绝对路径
		是一个非常详细的路径,绝对路径都是从盘符开始的。
		从盘符开始书写的路径就是绝对路径。

4)File类的获取方法

1. 	String  getAbsolutePath() 
		获取此file对象表示的绝对路径(从盘符开始)。
		如果创建的时候使用的是相对路径,那么创建时的相对路径表示的是该项目下的相对路径。
		获取到的还是从盘符开始的绝对路径。
		
2. String getPath()
	获取此file对象表示的路径。
	在创建file对象的时候给的是什么,获取到的就是什么。
	如果创建file对象的时候,使用的是相对路径,那么获取到的就是相对路径。
	如果创建file对象的时候,使用的是绝对路径,那么获取到的就是绝对路径。

3. 	String getName()
	获取此file对象表示的文件或者是文件夹的名字。
	这个方法不在乎这个文件或者文件夹是否真实存在,只要给一个路径,就能获取到对应的名称。
	其本质是截取的最后一个分隔符(\) 后的内容返回。

4.		Long lenth()
	获取此File对象表示的文件的字节数大小。
	只能准确的获取文件的字节数大小,如果是文件夹,获取的是一个不固定的值(没法准确获取).
	如果要获取的File对象表示的文件不存在,那么结果为 0  。

5)File类的判断方法

1.		Boolean exists()
		判断此file对象表示的文件或者文件夹是否真实存在。

2.		Boolean isDirectory()
		判断此file对象表示的 是否是一个真实存在的文件夹。
		如果这个File对象表示的 确实是一个文件夹,并且真实存在,那么返回true,
		如果不是文件夹或者是文件夹,但是不存在,返回的都是false。

3.		Boolean isFile()
		判断此File对象表示的是否是一个真实存在的文件。
		如果这个File对象表示的是一个真实存在的文件,那么返回true,
		如果不是文件或者是文件但不是真实存在的,那么返回的都是false。

6)File类的创建方法

	1.		Boolean createNewFile() 
			创建一个文件,如果文件已经存在,那么创建失败。
			如果创建文件时,对应的上级目录不存在,那么会报异常。
			eg:
				File  file = new File(“D:\\test\\aa.txt“);
  			    file.creatNewFile() ;      //true 

	2.		Boolean mkdir() 
			创建一个文件夹,如果文件夹已经存在,那么创建失败。
			如果创建文件夹时上级目录不存在,那么会创建失败 ,但是不会报错。
		
	3.		Boolean mkdirs() 
			创建一个文件夹,如果文件夹已经存在,那么创建失败。
			如果创建文件夹时上级目录不存在,那么会先自动创建上级目录,再创建指定文件目录。
			eg:   File  file = new File(“D:\\test\\aa\bb\cc”);
 						file.mkdirs() ;  //true
		 注: mkdir()只能创建单级目录,mkdirs()可以创建多级目录,使用的时候一般用mkdirs()比较多,用mkdir()较少。

7)File类的删除方法

1.		Boolean delete () 
		删除一个文件或者是文件夹。

注意点:
	a.	如果delete删除的是一个文件夹,那么这个文件夹必须是空文件夹,否则删除失败,不能删除。	
	b.	调用delete()方法删除时,不会走回收站,而是直接彻底删除,所以需要特别注意,小心误删。

8)File类的遍历方法

1.		String[ ] list()
		获取指定目录下的所有文件和文件夹,返回字符串数组。
		数组里面放的是所有文件和文件夹的名字(跟getName()方法获取的一样)。

2.		File[ ]	listFiles()
		获取指定目录下的文件和文件夹,返回的是File数组。
		可以直接对File数组进行其他操作。

3.		File[ ] listFiles(FileFilter  fileFilter) 
		传递一个文件过滤器接口的子类实现,可以按照指定的规则对文件进行过滤,返回符合要求的文件或者文件夹。
	
		注意点:
			1.	如果File对象表示的是一个文件,而不是文件夹,那么调用listFiles()方法,得到的是 null,如果遍历,会报空指针异常 。 
			2.	如果File对象表示的是一个空的文件夹,那么调用listFiles()方法,得到的是一个空的数组{ },遍历时没有内容,但不会报错。

4.		static	File[ ] listRoots()
		列举出操作系统的根目录。

9)File类的递归方法

	1. 概念
		递归指的是: 方法直接或者间接的调用方法本身。

	2.	包括的内容
		直接递归: A-->A
		间接递归: A-->B    B-->C		C-->A

	3.	 可能产生的问题
		内存溢出:内存不够用的情况。
		内存泄漏:有些对象虽然没有用了,但是却不能够被回收,这种现象就是内存泄漏。 内存泄漏会导致内存溢出。 

	4.	递归注意点
		a.	递归要有结束的条件
		b.	因为递归比较占用栈内存,所以递归的循环次数不能太多,否则会造成栈内存溢出。

10)文件过滤器

1.	定义
		文件过滤器接口,涉及的类为:   java.io.FileFilter 。
		
2.	可以作为listFiles()方法的	传入参数。

3.	接口中有且只有一个抽象方法 :  boolean  accept (File   file )  。

4.	重写抽象方法,确定文件保留的原则,比如: 要么是.java文件,要么是文件夹再次遍历。

5. 本质:就是一个文件过滤器 ,可以过滤不符合要求的文件,而只留下符合要求的文件。

eg:  
		File dir = new File("D:\\qqq");

		public static void printDir2(File dir){
				File[ ]  fileList = 	dir.listFiles(new FileFilter(){
					@Override
					public 	Boolean  accept(File file){
						return file.getName().endWith(".java")|| file.isDirectory();
						}
				})
		也可以用lambada表达式
				File[] fileList = dir.listFiles(file ->
					 file.getName().endsWith(".java") || file.isDirectory()
					);

			//循环打印
			for(File file fileList){
			if(file.isFile(){
				sout("这是个文件,文件名称:" +file.getAbsolutePath())		
					})else{
					//递归文件夹,重新便利
				printDir2(file);
				}
			}

2. IO流

1) 概述

1. 计算机中一般用来存储的设备有2个: 内存和硬盘。
	内存: 用来运行程序, 所有的程序都是在内存中运行的,也可以临时存储数据,但是当程序关闭后,保存的数据会丢失。
	硬盘:用于持久化的存储数据。 只要不删除,一直存在。

2.	IO流
	I:input ,输入,读取, 将数据从其他设备(硬盘)读取到内存中。 
	O:output, 输出,写出,将内存中的数据写出到硬盘或其他设备中。
	流: 数据传输的通道,可以传输数据。
  
注: IO流的输入和输出都是依照内存为参照物进行的。
	输入是读取到内存中,输出是从内存中写出到外面。

作用: IO流主要用来传输数据,像:文件拷贝,上传,下载 等等。

图示:
在这里插入图片描述

2)字节与字符

字节: 
		计算机存储的基本单位,8位二进制(0~255)
		主要处理二进制数据(文件,网络传输)
		直接操作原始数据

字符:
		人类可读的文本单位(如:字母,数字,符号)
		主要处理文本数据(字符串,文档内容)
		需要编码/转码 转换成字节。
	
字节与字符可以相互转化,但是需要一定的转化规则。	
字节---> 字符:	需要指定字符编码(如 UTF-8, GBK)进行解码。
							解码:将存储在计算机中的二进制数按照某种规则解析显示出来。
字符---> 字节:	通过编码规则转换为字节序列。	
							编码:按照某种规则,将字符以二进制的形式存储到计算机中。	

常见的编码规则: 
	ASCII:1 字节表示 1 字符(英文字符)。
	UTF-8:变长编码(1~4 字节),兼容 ASCII。
	GBK:中文编码,1~2 字节。

注意:
		在计算机中,一切文件数据(文本,图片,视频等)在存储时,都是以二进制数字的形式保存的,都是一个一个的字节,数据传输的时候也是这样。
		也就是说最基本的结构都是通过字节构成的,只不过是因为通过不同的方式(不同的解码规则)打开,才展示出我们想让它展示的效果。

3) IO流顶层分类

1. 从流向上分: 
	输入流:
			字节输入流		InputStream
			字符输入流		Reader
	
	输出流			
			字节输出流		OutputStream
			字符输出流		Writer	

2.	从数据类型上分
	字节流
			字节输入流		InputStream
			字节输出流		OutputStream

	字符流
			字符输入流		Reader
			字符输出流	 	Writer		

注意: 
		这4个顶层父类都是抽象类,实际上一般使用的是他们的子实现类。

4) 字节输入流

1. 概述
	a. InputStream是字节输入流,主要是将硬盘中的文件写入到内存中。可以将文件中的内容写入到java程序中 。
	b.	InputStream是所有字节输入流的顶层父类,并且是一个抽象类,我们需要用它的子类才可以。一般用的比较多的是它的一个子实现类:FileInputPutStream 。

2. FileInputPutStream 
	1) 构造方法
		FileInputPutStream (File file )	:  参数需要传递一个File类型的文件,表示从这个文件中读取数据到内存。
		FileInputStream (String name) : 参数需要传递一个String类型的文件路径,表示从这个文件路径中读取数据到内存。

	2)	读取方法
	 int  read( ) : 从该文件中读取一个字节(只能是1个字节)到内存,并且返回读取到的字节。如果读取结束,返回-1 。
	 int  read(byte [ ] bArr) : 从该文件中读取数据到指定的字节数组中。
                   				返回的是读取到的有效的字节个数,如果读取结束,返回-1。
	int read ( byte [ ] bArr, int off, int length) : 从文件中读取数据到指定的字节
	数组,返回的是读取到的有效的字节个数,如果读取结束,返回-1。
 	注意: 
 	       byte [ ] bArr : 字节数组。
			off :           从哪个位置开始读取。
			 length:     每次读取的字节个数。

	3)	关闭资源的方法
		 void close( ) ;  关闭流,释放资源。

	4) 使用实例
		a.	创建一个字节输入流 FileInputStream,绑定一个获取文件数据的数据源。
		 InputStream  in = new FileInputStream(“aa.txt”);// 文件内容:abcde
		//相对路径,表示的是本项目下的地址,如果绑定的数据源不存在,那么会抛出 FIleNotFoundException 。
		注意: 这一步骤做了2件事情
			1.	创建一个字节输入流对象。
			2. 将要绑定的数据源(aa.txt)绑定到这个字节输入流(in)上,以后通过这个字节输入流操作的就是这个绑定的数据源文件的内容,如果数据源不存在,会抛出异常。

	b. 读取数据【read()方法一次读取一个字节,read(byte[ ] byteArr) 方法一次读取一个字节数组】
		int  i = in.read();   //一次读取一个字节,当没有读取结束时,返回的是读取到的内容,当读取结束时,返回的是 -1 。
	
		  byte[ ]  byteArr = new byte[1024] ; 
        int length  = in.read(byteArr) ;   //
        
	c. 判断是否读完
			While((len= in.read(byteArr) ) != -1){
				// 说明还没有读完,可以对数组中的数据进行操作
			}
	 注意: 该条件判断做了如下事情: 
	 		1.	调用了输入流的read()方法,将数据读取到byteArr数组中。
	 		2.	返回了读取到的有效个数,并且将这个有效个数的值赋值给len。
	 		3.	判断len是否不等于-1,如果不等于-1,表示读取到了内容,那么就在循环中对数据进行处理,读取到的数据在byteArr数组中存储着。
	 		4.	如果len等于-1,表示数据已经读取结束。 
	 		5.	这里不能直接读取或者打印byteArray[ ]数组的内容,因为可能会有上次读取的残留。需要使用:Syste.out.println(new String(byteArr,0,len)) ;
	 	
	 d. 关闭资源

	5)注意点
		a. 采用字节数组的方式比采用单字节的方式要快很多,一般用的字节数组的大小是 1024的整数倍,最快的是1024的8倍。 
		b.	通过字节输入流读取数据,如果采取一次读取一个字节数组的方式进行读取,那么会将数据读取到参数byteArr数组中,
		参数byteArr数组的长度是几,那么就一次读取几个字节。如果读取的是UTF-8的文件,而一次读取字节数组的长度又不是3的倍数,
		那么还是会出现把中文拆开的情况,导致错误。哪怕长度是3的倍数,但是读取的文字中如果既有中文,又有字符,还是会出现错误。
		所以不能用字节流来读取/写出 文本数据。

5) 字节输出流

1. 概述
	a.	OutPutStream是字节输出流,主要是将内存中的文件写出到硬盘中。可以将java程序中的数据写到文件中。
	b.	OurPutStream是所有字节输出流的顶层父类,并且是一个抽象类,我们需要用它的子类才可以。一般用的比较多的是它的一个子实现类: FileOutPutStream 。

2.	FileOutPutStream
	1) 构造方法
		FileOutPutStream(File file ) 	参数需要传递一个File类型的文件,表示向这个文件中写出数据。
		FileOutPutStream(String name) 	参数需要传递一个String类型的文件路径,表示向这个文件路径中写出数据。
	
		FileOutPutStream(File file,Boolean append ) 	
				  File file :          需要进行写出的文件
		 	  Boolean append :是否进行追加写 
		FileOutPutStream(String name,Boolean append ) 
				String name : 一个String类型的文件路径,表示向这个文件路径中写出数据。
				Boolean append : 是否进行追加写。
		
		注意:
				如果FileOutPutStream没有加第二个参数true,那么后面执行的会覆盖前面执行的,每次都会新创建一个文件,再把内容写入,
				如果加了true,那么不会新创建文件,而是在原文件的基础上追加写入。

	2) 写出的方法
			void  write( int  b) : 将指定的字节写出到文件中。每次只能写出一个字节!
			void  write(byte [ ] bArr) 	: 将指定的字节数组写出到文件中。
			void  write( byte [ ] bArr, int off, int length) : 将指定字节数组的一部分字节写出到文件中。
						byte[ ] bArr: 指定的字节数组
           				off : 从哪个位置开始写出。
           				length: 写出几个字节。

	3) 关闭资源的方法
			 void close( ) ;   关闭流,释放资源。
      		void  flush() ; 刷新该输出流,并强制任何缓冲的输出字节被写出,不是强制使用。

	4) 使用实例
		a.	 创建一个输出流FileOutPutStream,参数传递一个写出的文件路径。
				 OutPutStream  os = new FileOutPutStream(“D:\\aa\\bb.txt”);
				 注意:这一步骤做了3件事情: 
				 1.	调用系统资源,在D盘的aa文件夹下创建文件bb.txt,如果这个文件已经存在,那么会新建一个新文件,覆盖之前的文件。		
				 2.	创建一个字节输出流对象。
				 3.	将创建的文件(bb.txt)绑定到这个字节输出流(os)上,以后通过这个字节输出流操作的就是这个文件的内容。
				 
		b.	向路径中写出数据。
			write(int  b)  
			注意:
				1. 使用write(int b) 方法,每次只能写出一个字节,如果超过一个字节,写出时会进行截取,导致写出时有问题。
				2. 数字可以安全写出: 如果传入的是一个数字,那么会先将这个数字转换成ASCII码上对应的字符,再将字符写出。
				3. 字符也可也安全写出:在java中,一个字符是占两个字节,但是如果这个字符是ASCII码表上的字符,那么这个字符在操作系统中是占用一个字节的,所以这个字符用一次写一个字节的方式也是可以安全写出的。
				4. 中文不可以安全写出,会有问题:中文在操作操作系统中是大于一个字节的(如果采用的是GBK,那么占用两个字节,如果采用的是UTF-8,那么占用3个字节),所以如果用一次写一个字节的方式来写出中文,那么会造成字节溢出,产生问题。

			write(byte [ ] bArr) 
			write( byte [ ] bArr, int off, int length) 
			注意: 
				1. 也可以使用字节数组的方式进行写出。
				2. 字节数组和字符串可以进行相互转化,
					字节数组---> 字符串(直接构造方法)
						String (byte[ ] bytes) : 将整个字节数组转换成String . 	
						String (byte[ ] bytes, int offset, int length ):将指定字节数组的一部分转换成字符串。
      						参数: bytes : 要转换的byte数组
            						offset : 从哪个位置开始转换(索引位置)
           							 length:   转换几个字节
      			   例: String  ss2 = new String(byteArr,1,2) ;  byteArr:abcde
           					   System.out.println(“ss2”) ;   // bc
	
				字符串----> 字节数组
						byte[ ]  getBytes( );  使用平台默认的编码格式(开发环境),将字符串转换成字节数组。 
						byte[ ]  getBytes(String  s ) ;  使用指定的编码格式,将字符串转换成字节数组。
					例:
							 String s2 = “中国” ; 
				               byte [ ]   byteArr2 = s2.getBytes(); 
							System.out.println(Ayyays.toString(byteArr2)) ; // [-28,-72,-83,-27,-101,-67]

				3. 因为一个中文占用3个字节,那么很可能因为开始的位置不是中文的第一个字节位置,或者length不是3的倍数,而导致写出文本失败。
				
	c.	续写/换行写
			续写: 使用带有 append 参数的多参数构造。
						FileOutPutStream(File file,Boolean append ) 
						FileOutPutStream(String name,Boolean append )
		
			换行写: 
					 windows :   \r\n 
              		   linux    :    \n 
              		  macOs  :   \r    
			eg: 
					OutPutStream  os1 = new FileOutPutStream(“aa.txt”);
					os1.write(“床前明月光\r\n ”.getBytes());  	
				
	d.	关闭流	
			 os.close() ; 					 

6) 字符输入流

	1. 概述: 
		a .字符输入流的顶层父类是 Reader,可以将文件中的数据读取到程序中,Reader也是一个抽象类,一般用的是它的子类: FileReader 。主要用来读取文本文件。
	
	2.  FileReader 
		1) 构造方法
			FileReader(File  file) : 传递一个File类型的文件,作为用来获取数据的数据源。
			FileReader(String name ) : 传递一个String类型的文件路径,作为获取数据的数据源。

	2) 读取数据的方法
		int  read() ;  一次读取一个字符,并返回读取到的字符,如果读取结束,返回-1。不管是中文还是字母还是数字,都是一个字符。
		int  read(char[ ]  charArr);  一次读取一个字符数组,返回的是读取到的字符数组的长度,如果读取结束,返回-1。

	3)	关闭资源的方法
		 void close( ) ;  关闭流,释放资源。

7) 字符输出流

	1. 概述: 
		a . 字符输出流的顶层父类是 Writer, 可以将内存(程序)中的数据写出到文件中。Writer是一个抽象类,一般用的是它的子类 FileRwiter 。
	
	2.  2)	FileWriter
		1) 构造方法
			FileWriter(File  file) : 传递一个File类型的文件,表示向这个File文件中写出数据。
			FileReader(String name ) : 传递一个String类型的文件路径,表示向这个文件路径中写出数据。
			FileWriter(File file ,Boolean append) : 向file文件中追加写出数据。
              		File file :表示向这个File文件中写出数据 。  
             		 Boolean append :  是否追加写出数据,true表示追加写。
			FileWriter(String name,Boolean append) :向给出的文件路径中追加写数据。
				 String name :String类型的文件路径,表示向这个文件路径中写出数据。
				Boolean append :  是否追加写出数据,true表示追加写。

	
		2) 写出数据的方法
	void write(int c) : 向指定文件中写出一个字符。
	void write(char[ ] charArr) :  向指定文件中写出一个字符数组。  
	void write(char[ ] charArr, int off , int len) :  向指定文件中写出一个字符数组的一部分内容。
       		char[ ] charArr : 指定的字符数组。
      		 int   off:    开始写出的位置(索引位置)
      		 int   len:    写出的长度(多少个字符)
	void write(String str) : 向指定文件中写出一个字符串内容。
	void write(String str, int off , int len) : 向指定文件中写出一个字符串内容的一部分。
		String  str:      指定的字符串。
      	 int   off:    字符串开始写出的位置(索引位置)
		int   len:    写出的长度(多少个字符)


	3)	资源处理相关的方法
			void  flush() : 刷新,仅仅是刷新操作,而且刷新后还可以向文件中再写出数据。 
			void close( ) ;  先刷新,再关闭,关闭之后不能再向文件中再写出数据,否则会直接报错。

	4)注意点
			a.	调用write()方法只是把数据写出到了内存缓冲区中,并没有放入到文件中,要想把写出的内容放到文件中,必须要进行fluch()刷新操作才可以。
			b.	仅仅字符输出流写完数据后要刷新,其他流不需要该操作。

8) 字节流和字符流的区别

	字节流:
			1. 以单个字节为处理单位,每次读取或者写入一个字节(或者字节数组)。
			2. 不能操作中文,更适合处理二进制文件,如:图片、视频、音乐等。
			
			3. 不进行任何编码转换,读入的是原始字节,写出的是原样字节。如果用字节流读文本文件,容易出现乱码。
			
			4. 本身没有内置缓冲区,但是可以通过BufferedInputStream 装饰。一般调用read会直接请求系统。


	
	字符流: 
			1.	以2个字节的Unicode字符为处理单位,每次读取或者写入一个字符(或者字符数组)。
			2.	适合处理文本文件(用记事本打开能看懂的文件就是文本文件),比如:.txt、.java、.xml、.json等。
			3.	内部使用字符集编码器/ 解码器,读到几个字节,先去查找编码表,再将查到的字符返回。

	总结: 
			如果操作文本文件,就用字符流,如果操作的是非文本文件,就用字节流。

9) IO流的异常处理操作

	1. JDK1.7之前,需要手动try...catch...finally ,然后在finally中手动关闭流。
	
	2.	JDK1.7及以后处理方式:直接将要创建的流 放到 try 代码块中。 
		 代码:  
		 	 try( 创建流对象1  ;创建流对象2 ){
                      //操作流的代码
			} catch (IOException e){
						e.printStackTrace();
				}  ------这种格式称为:  try…with…resource 
				
		注: 小括号内也可以创建多个对象,多个对象之间用分号隔开。
				 这种方式不需要我们自己去调用close()方法关闭流,系统会自动调用方法帮我们关闭流,而且不管程序有没有异常,都可以关闭流。
     		原因: try后面小括号中创建的对象,必须实现AutoCloseable接口。这个接口中有重写的close()方法,不管有没有异常,这个方法都会执行,作用就是关闭流或者关闭其他资源,相当于try…catch的语法糖。

	3. 如果要释放处理try外面的流资源,
		jdk1.7以及之前需要将外面的流赋值给新的流
				FileWriter  fw = new FileRwiter("file1.txt"); 
				try(FileWriter fw2 = fw){ 
					 fw2.write("你好")
				}catch(IOException e){
				}

		jdk1.9以及之后,直接将流放到try后面
			try(fw2){
					 fw2.write("你好")
				}catch(IOException e){
				}

10) IO流总结

在这里插入图片描述

11) IO流分类

		1. BIO: 同步阻塞IO,线程在执行IO操作的时候会阻塞等待,不能做其他事情。
		
		2. NIO(用的较多):  同步非阻塞IO,线程在执行IO操作的时候,会去干别的事情。并且会实时的去看IO操作有没有结束,如果结束了就会回来继续io后的操作。
		
		3.	AIO: 异步非阻塞IO ,找别人帮我完成一些事情。 

3. 缓冲流

1)分类

		字节缓冲流
					字节输入缓冲流: BufferedInputStream 
           			字节输出缓冲流: BufferedOutputStream

		字符缓冲流
					字符输入缓冲流: BufferedReader 
           			字符输出缓冲流: BufferedRwiter

2)特点

	 	a.	缓冲流的特点就是:快。可以提高读写的速率;
        b.	缓冲流本身并不具备读或者写的功能,它的作用只是给其他流提供加速。
       		缓冲流效率高的原因是因为: 它的内部有一个缓冲区,缓冲区的大小是1024*8;

3)使用步骤

	1. 创建缓冲流对象;
    2. 调用方法进行读或者写操作;
    3. 关闭流。

4)字节输入缓冲流

	构造方法:
		BufferedInputStream(InputStream  in ) : 将一个普通的字节输入流作为入参,转换为字节输入缓冲流,默认字节数组大小为1024*8 ;

	BuffredInputStream(InputStream in, int size ) : 将一个普通的字节输入流和一个字节数组(缓冲区)大小作为入参,转换为字节输入缓冲流。 

5)字节输出缓冲流

构造方法:
		BufferedOutputStream(OutputStream  out) : 将一个普通的字节输出流作为入参,转换为字节输出缓冲流,默认字节数组大小为1024*8 ; 

	BufferedOutputStream (OutputStream  out, int size ) : 将一个普通的字节输出流和一个字节数组(缓冲区)大小作为入参,转换为字节输出缓冲流。		

示例:
在这里插入图片描述

6)字符输入缓冲流

	BufferedReader(Reader  in) : 将一个普通的字符输入流作为入参,转换为字符输入缓冲流,默认字符数组大小为1024*8 ; 
	
	BufferedReader (Reader in, int size ) : 将一个普通的字符输入流和一个字符数组(缓冲区)大小作为入参,转换为字符输入缓冲流。 

7)字符输出缓冲流

	BufferedWriter(Writer out) : 将一个普通的字符输出流作为入参,转换为字符输出缓冲流,默认字符数组大小为1024*8 ; 

	BufferedWriter (Writer out, int size ) : 将一个普通的字符输出流和一个字符数组(缓冲区)大小作为入参,转换为字符输出缓冲流。

示例:
在这里插入图片描述

8) 字符缓冲流的特殊方法

字符输入缓冲流:
		BufferReader中有一个方法,可以一次读取一行数据。
		
		 String  readLine() ;  读取一行数据并返回,读取结束时,返回 null 。
          注意:readLine()方法不会读取换行符,需要自己手动换行(println)。

字符输出缓冲流:
		BufferWriter有一个方法,可以实现跨平台换行。

		void  newline() : 实现一个跨平台的换行。

示例:
在这里插入图片描述

4. 转换流

1)编码表

1. 计算机中存储的数据都是二进制的,但是我们在屏幕上看到的文字却是各式各样,这些都是二进制转换过后的结果。
	编码:按照某种规则,将字符以二进制的形式存储到计算机中,称为编码;
	解码:将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。

2.	字符编码:指的是自然语言的字符和二进制数之间的对应规则。

3.	字符集: 又叫做编码表。  
					是一个系统支持的所有字符的集合,包括各个国家的文字,标点符号,图形符号,汉字等。
					编码表指的是计算机中的字节和我们看到的字符的对应关系表。

4. 在windows操作系统中,默认的是gbk编码,在 gbk 编码中, 一个中文占用两个字节。
	在idea中,默认的是utf-8编码,在utf-8 编码中,一个中文占用3个字节。

在这里插入图片描述

2)转换流的作用

	在windows系统中,文本文件默认使用的是 gbk编码,一个中文占用两个字节。
	但是在java程序(idea)中,文本文件默认使用的是utf-8编码,一个中文占用3个字节。
	那么当用idea 读取windows系统下的文本文件时,就会因为中文占用的字节数不同而导致乱码。
	这时候就需要用到转换流,来指定统一的编码格式。

3) InputStreamReader

1. 介绍
		InputStreamReader 是将(硬盘中的)文件读取到java程序中。
		在文件中,所有的数据都是以字节的形式保存,读取到java后,以字符的形式展示。
		 所以 InputStreamReader是字节到字符的桥梁。
    	 InputStreamReader的本质:先查询码表,再进行转换。

2. 构造方法
		InputStreamReader (InputStream in ): 传递一个字节输入流。
		InputStreamReader (InputStream in ,String charName)
    		  InputStream in : 传递一个字节输入流
              String charName:一个字符串类型的编码格式,表示要通过这种编码格式进行读取。

		
3.  使用
		a.	创建流。
		b. 读取。
		c. 释放资源。
	
4.  示例
		BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("file.txt"), "StandardCharsets.UTF_8)"));
		String line;
		while ((line = br.readLine()) != null) {
		System.out.println(line);
		}

4) OutputStreamWriter

	1. 介绍
			OutputStreamWriter其实就是将内存中的数据写出到硬盘中。			
			OutputStreamWriter可以指定编码格式,来向硬盘中写出数据。

	2.  构造方法
		OutputStreamWriter(OutputStream out ): 传递一个字节输出流,会按照idea默认的编码格式向文件中写出数据。
		OutputStreamWriter(OutputStream out ,String  charsetName): 
             OutputStream out : 传递一个字节输出流
           String  charsetName: 设置一个编码格式,使数据按照指定的编码格式向文件中写出数据。
	
	3.  使用步骤
		a. 创建流。
		b. 写出。
		c. 释放资源。
	
	4. 具体示例
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("file.txt"), "StandardCharsets.UTF_8)"));
		bw.write("1111");
		bw.close();
		}	

5) 关系

	 InputStreamReader: 从文件中读取的是字节,在idea中看到的是字符;
	OutputStreamWriter: 在idea中写出的是字符,保存到文件中的是字节。

在这里插入图片描述

5. 序列化流

1) 序列化流:ObjectOutputStream

	1. 定义: 
			ObjectOutputStream:  是一个处理流(包装流),主要负责把java对象写出(序列化成字节序列)。写出的具体位置,是由它包装的底层输出流决定的,
					FileOutputStream --》写出到硬盘
					ByteArrayOutputStream --》写出到内存
					Socket.getOutputStream()--》写出到网络
				
	2.	构造方法
			ObjectOutputStream (OutputStream out ) :传递一个字节输出流。
	
	3.	写对象的方法
			void  writeObject(Object  obj) : 向文件中写出一个对象。
	
	4. 使用步骤
			a.	创建一个序列化流。
			b.	调用writeObject()方法,向文件中写对象。
			c.	释放资源。
	
	5. 示例:
			public class test{
				public static void main (String[ ] args){
				ObjectOutputSream  os = new ObjectOutputSream (new FileOutputStream("aa.txt"));
				Person p = new Person("张三",18);
				os.writeObject(p);
				os.close(); 
				}
		}						

	6. 注意点
		a.	只有实现Serializable接口的类的对象才能够被序列化。
		b.	Serializable接口中没有任何方法,它只是做一个标记,表示实现该接口的类的对象可以被序列化。

2) 反序列化流:ObjectInputStream

	1. 定义: 
			ObjectInputStream: 将之前序列化写出的对象,重新反序列化,读入到内存(java程序)中。
	
	2.	构造方法
			ObjectInputStream(InputStream  out ) :传递一个字节输入流。
	
	3.	读取对象到内存(java程序)的方法
			Object  readObject() : 将之前序列化的对象进行反序列化,读入内存(java程序)中。
	
	4. 使用步骤
			a.	创建一个反序列化流。
			b.	调用readObject()方法进行读取操作。
			c.	释放资源。
	
	5. 示例:
			public class test{
				public static void main (String[ ] args){
				ObjectInputSream  os = new ObjectInputSream  (new FileInputStream("aa.txt"));
				Object obj = os.readObject();
				Person p =(Person) obj;
				Sout(p.getName+p.getAge);
				os.close(); 
				}
		}						

	6. 注意点
		a.	要读取的文件必须是之前序列化生成的文件。
		b.	如果读取的文件时候,这个类不存在,那么会抛出classNotFoundException异常。
		c.	如果读取文件的时候,类存在,但是由于类发生了改变,版本号也发生改变,导致版本号不一致而无法反序列化时,会抛出InvalidClassException异常。

3) 序列化中的static

	1. 被static修饰的成员变量不能被序列化。
	
	2.  被static修饰的成员变量会变为静态变量,其值或者引用将不能被改变。
		如果希望一个变量不被序列化,又不想用static修饰,那么可以用transient修饰,被transient修饰的成员变量不会改变其原来属性,而且不会被序列化。

4) 序列化版本问题

	1.	当一个类的对象先进行序列化写出到文件中,然后对类进行了修改(不包括加空格,加注释等),再对之前序列化的文件进行反序列化读取时,就会因为类的修改,而导致这个类的版本号发生改变,序列化文件中类的版本号和现在类的版本号对应不上,而导致反序列化失败。

	2.	解决方法: 给类分配一个固定的版本号,不管类是否进行修改,版本号都不会发生改变。

3.	给类分配固定版本号方法:
		提供一个静态成员变量,这个静态成员变量的内容为: 
              private  static  final  Long  serialVersionUID = 1L; 	// 1 也可以是其他数,只要固定就可以。

5)什么是序列化

1.定义
		序列化是一种用来处理对象流的机制,对象流就是将对象的内容进行流化,并可以对流化后的对象进行读写、传输于网络之间等操作。序列化是为了解决对对象流进行操作时引发的问题。

2. 实现
		将需要被序列化的类实现Serializable接口。该接口没有需要实现的方法,只是起到一个标记作用。

6. 打印流

1) 特点

		输出数据非冲方便,但是打印流只能输出数据,不能输入数据。

2) 分类

					字节打印流:PrintStream  ;
					字符打印流:PrintWriter  

3) 构造方法

		PrintStream (File file ) :传递一个file类型的文件 
        PrintStream (String fileName) :传递一个字符串类型的文件路径
        PrintStream (OutputStream out) : 传递一个字节输出流

4) 其他方法

			Print() ;   输出不换行
			Println();  输出并换行
			Write() ; 写出

5) 调用write()方法写出和调用print()方法写出的区别

		调用write()写出数字时,会对照ASCII码表,转换成字符;
		而调用print()方法不会对照码表,会直接输出数字。

6) 标准输出流

	System.out.println()  里面的System.out 是一个打印流,也叫做标准输出流。
 
 	 我们可以调用System的setOut(PrintlnStream out) 方法,来改变打印流的走向,使打印流不是打印到控制台,而是打印到指定的文件目录。

	eg: 
			PrintStream() ps  = new PrintStream("aa.txt");
			System.setOut(ps);		//打印到指定目录

7. Properties集合

1) 介绍

	Properties是一个双列集合,里面是以键值对的形式存放数据的。
	特点: 
			1.	Properties实现了Map接口,拥有Map中的所有功能,并且是一个双列集合。
			2.	Properties键值对没有泛型,键和值都是String类型的。
			3.	Properties支持对流的操作,可以从流中加载键值对。(使用的主要原因)

2) 常用方法

	1. Properties();  无参构造
	
	2. object setProperty(String key , String value) ; 向Properties集合中添加键值对。	

	3.	String getProperty( String key) ;  根据key获取value值。
	
	4.	Set<String> stringPropertyNames();  获取所有存放key的集合,相当于keySet。	

	5.	void load(InputStream inputStream) ; 从字节流(文件)中加载键值对。
	
	6.	void load (Reader reader) ;  从字符流(文件)中加载键值对。	

3)使用步骤

	1.	创建一个Properties集合。
	2.	创建一个输入流对象,绑定一个数据源文件,表示要从这个文件中读取数据。
	3.	调用Properties集合的load()方法,传递刚才创建的输入流对象,就可以将输入流绑定的文件中的键值对加载到Properties对象中。
			 要读取的文件的要求:
					1.	一般文件以.properties 结尾,这种后缀的文件是配置文件。
					2.	文件的内容是键值对,而且必须按照以下方式存储
						 key = value
						 key = value
						key = value
						
						 注: 1)不能有分号,如果有分号,会当做value值的一部分获取
								 2)value 如果是中文,用字节流读会产生乱码,需要指定解码规则。
								 如果使用的是idea,那么需要修改配置中的编码规则,修改步骤为: 
 								File—settings--Editor--Code Style---File Encodinds------
     							项目编码:UTF-8 ; properties默认编码:ISO-8859-1 ;  勾选自动转换为UTF-8。

4)具体示例

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sing~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值