zoukankan      html  css  js  c++  java
  • 基础加强____【IO流个人总结】



    IO流体系的知识梳理与深化,用EditPlus以代码格式编写

    IO流(重点)
    	设备之间的数据传输,由于会操作系统底层设备,io操作会抛 IOException,编程时要进行处理
    	要认清"流"的概念,read 是获取数据到流中,write是将流中的数据写出去
    主要体系:	抽象基类,派生出的子类都是以父类作为后缀
    ----------------------------------	
    
    IO继承体系"*"为较常用; "♂"&"♀"为成对使用
    字符流继承体系
    
    	//Reader 实现接口Closeable, Flushable,	提供close()和flush()功能
    	Reader	-->InputStreamReader "*"转换流又称桥梁流-->FileReader	"*"//由此可见字符流在底层调用了字节流
    			-->BufferedReader	"*"缓冲&装饰-->子类: LineNumberReader 行操作
    			-->CharArrayReader 操作字符数组
    			-->StringReader		操作字符串
    	//Writer 实现接口 Closeable, Flushable, Appendable	多一个append()	Appendable接口出现于1.5版本
    	Writer	-->OutputStreamWriter "*" -->FileWriter	"*"	//append方法用于添加字符
    			-->BufferedWriter	"*"	
    			-->PipedWriter	-->PrintWriter 打印流 "*"
    			-->CharArrayWriter 
    			-->StringWriter
    
    字节流继承体系:
    	//InputStream 实现Closeable接口		close()
    	InputStream	-->FileInputStream "*"
    				-->FilterInputStream 过滤流-->BufferedInputStream 字节流缓冲区(装饰)
    										   -->DataInputStream 操作基本数据类型
    				-->SequenceInputStream	合并流
    				-->ObjectInputStream"♀"	序列流	对象反序列化,将对象数据读取进流
    				-->PipedInputStream	"♂"	管道流,发送
    				-->ByteArrayInputStream	操作字节数组
    	//OutputStream 实现Closeable, Flushable接口	close() & flush()
    	OutputStream-->FileOutputStream "*"
    				-->FilterOutputStream-->BufferedOutputStream
    									 -->PrintStream 打印流
    									 -->DataOutputStream
    				-->ObjectOutputStream "♂"对象序列化 持久化,对象需实现 Serializable "标记接口"
    				-->PipedOutputStream"♀"	管道流,接收
    				-->ByteArrayOutputStream
    
    IO体系外其它相关类
    	File"*"		操作文件和目录
    	RandomAccessFile	随机读写
    	Properties 该类是 Hashtable 的子类,位于java.util包中
    
    -----------------------------------
    字符流
    	FileReader 读入步骤: new FileReader(File)/*已有文件*/-->(循环)int read() ||int read(char[])-->操作--(-1 循环结束)-->close()
    	FileWriter 写出步骤: new FileWriter(File)/*创建|覆盖*/-->(循环)write(...)-->flush()-->close()
    				文件续写: new FileWriter(File,true) ...	//将true改为false就是覆盖原文件
    				字符文件的copy就是循环的read()和write()
    	字符流缓冲区:用于提高效率 "装饰类"
    	BufferedReader	读入: new BufferedReader(new FileReader(File))-->(循环)String ReadLine()-->操作--(null 循环结束)-->close()
    		子类: LineNumberReader 使用方法: new LineNumberReader(new FileReader(File)) 
    		//.setLineNumber(int n)设置行号,从第n行开始readLine() || .getLineNumber()获取行号
    	BufferedWriter	写出: new BufferedWriter(new FileWriter(File))-->(循环)write(linne)-->newLine()-->flush()-->close()
    					使用缓冲区能够减少读写的次数,提高效率
    设计模式之--"装饰设计模式"Decorator
    	当想要对已有对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,提供加强功能,自定的该类称为装饰类
    	readLine()是read()方法的增强,BufferedReader是FileReader类的增强类
    	相比继承扩展,装饰模式降低了两个类之间的关联度,避免了继承体系的臃肿,具有更高的灵活性和扩展性
    
    ----------------------------------------------------------------
    字节流
    
    	FileInputStream	 读入:new FileInputStream(File)-->(循环)int read() || read(byte[])-->操作--(-1 循环结束)-->close()
    		读入的第三种方式: new FileInputStream(File)--> new byte[ fis.available()]-->read(byte[])-->close()
    		//available返回该文件对象有效的字节数,不使用循环而是一次性写入,文件较大时要慎用(JVM内存分配默认为64m)
    		在读写文件时,使用数组缓冲与单字节读写相比往往有数十倍的性能提升,可以使用"模板模式"来对比三种方法的效率
    		一般来说缓冲区大小为1M左右时效率是最高的,当然,不同系统环境也可能有差异
    	FileOutputStream 写出:new FileOutputStream(File)-->write(byte[])-->close()
    
    	BufferedInputStream	
    	BufferedOutputStream
    	自定义字节流缓冲区 <==> read & write的特点
    		计算机中数据以二进制保存,以字节以byte为单位存取。读取时为便于操会将二进制转成十进制
    		这样问题就随之而来了,字节流的read方法以返回值 -1 来表示到达文件末尾
    		如果刚好有连续的8个1,直接转成int型就是-1,就意外满足了read 方法-1的控制条件
    		导致程序提前结束,所以字节流缓冲区的read方法必须避免这种情况的发生生
    	解决办法:将 byte 转为 int,然后通过二进制"&"运算补0 
    		byte -1 --> int -1
    		 11111111 11111111 11111111 11111111                          
    		&00000000 00000000 00000000 11111111   //255
    		------------------------------------  
    		 00000000 00000000 00000000 11111111   //int
    	
    	这也是为什么读取单个字节时read 方法的返回值为 int 而不是 byte 的原因
    
    ---------------------------------------	
    
    读取键盘录入
    	标准的输入输出流: System.in(InputStream) & System.out(PrintStream)
    
    转换流:	InputStreamReader	
    		OutputStreamWriter
    
    键盘录入最常见形式:
    	BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));	//字符	<--字节
    	BufferedWriter bufw = new BufferedWriter(new InputStreamReader(System.out));//字符	-->字节
    	设置源和目的:
    		System.setIn(new FileInputStream("ReadIn.java"));   //源,也可直接将字节流传入转换流
    		System.setOut(new PrintStream("printStream.txt"));  //目的, FileOutputStream 亦可                                                 
    
    	1)字节流和字符流最大的区别是,字节流可以操作任何数据,字符流只能操作文本数据。
    	2)字符流在底层调用了字节流的缓冲区,所以需要刷新动作;而字节流在操作时一般不需要刷新
    	
    	数据在硬盘中是以二进制存储的,以 byte (8位)为单位,字节流的read 和write 操作单位对象是 byte
    	字符流在底层调用了字节流,专门用于操作字符,其read 和 write 方法操作的单位对象是 char 
    	使用时要区分"源"&"目的",操作对象是否为纯文本? 字符:字节
    
    --------------------------------------
    IO流中另一个重要的类 "File"
    	File 类是文件和目录路径名的抽象表示形式。 专门用于对文件进行增删改查的操作
    	File类的过滤,两个接口
    		FileFilter 过滤路径
    		FilenameFilter	过滤文件名
    
    	示例: 过滤获取指定文件夹下的 .java文件
    		 File dir = new File("f:\JAVA\test");  
            String[] arr = dir.list(new FilenameFilter(){//匿名内部类
                public boolean accept(File dir,String name)  {  
                    return name.endsWith(".java");  //String类方法
                }  });  
    	
    	Java的跨平台分隔符"separator"
    	
    "递归": 即函数自身调用自身
    	使用递归要注意: 1)要有明确的递归结束条件;避免死循环
    					2)控制调用次数,避免栈内存溢出
    					3)递归书写简洁,但效率较低,应避免在算法题中使用
    
    	应用: 遍历访问指定目录下文件及其子目录中内容,并对其进行操作
    		
    -----------------
    Properties 类
    	Properties 是hashtable的子类,具备map集合的特点,里面存储的键值对都是字符串,该集合可以加载进IO流
    		该类提供了一些对io流进行操作的方法
    	较为熟悉的应用:	
    		系统运行日志: Properties prop = System.getProperties();
    		从配置文件中获取数据,以键值对的形式存入集合并进行操作。如限制软件运行次数 time = 5;时停止服务
    
    "打印流" PrintWriter & PrintStream
    	可以直接操作输入流和文件。
    	PrintWriter out = new PrintWriter(new FileWriter("...") ,true)
    	PrintWriter 的print & println 就是常用的输出语句sop,//该方法可以自动刷新,换行
    	
    "合并流" SequenceInputStream
    	对多个输入流进行合并,到同一个输出流
    	"文件的分割" 即将一个输入流对应到多个输出流, 输出大小以一个缓冲数组为单位
    	"文件的合并" 即将多个输入流指向同一个输出流
    	使用 Vector & Enumeration :List数组集合枚举
    	示例:
    		Vector<FileInputStream> v = new Vector<FileInputStream>
    		v.add(new FileInputStream(File))...	//添加多个输入流
    		new SequenceInputStream(v.elements());	//该参数为枚举类型,合并源
    		定义输出目的...读写操作
    	使用 Vector+枚举 不够高效,使用 ArrayList+迭代 来代替
    		而 SequenceInputStream 的构造参数必须为枚举类型
    		所以就有必要复写 Enumeration 接口中的方法
    	示例:
    		定义 ArrayList 和 Iterator
    合并:	new SequenceInputStream(new Enumeration<FileInputStream>(){
    			public boolean hasMoreElements(){//接口型匿名内部类
    				return it.hashNext();
    			}	//将迭代结果作为枚举结果,提高效率
    			public FileInputStream nextElement(){
    				return it.next();
    			}
    		})
    
    
    "序列流"	ObjectInputStream & ObjectOutputStream
    	这两个类必须成对使用
    	方法:  readObject() & writeObject()
    	为了在下次程序执行时继续使用对象数据,out序列化将对象保存到硬盘,in反序列化从硬盘读取数据
    	对对象进行序列化(持久化) 与反序列化操作, 被操作的对象所属类需要实现 Serializable "标记接口"
    	序列化示例:
    		class Person implements Serializable	//被操作的对象要实现标记接口
    		new ObjectOutputStream(new FileOutputStream("obj.txt")).writeObject(person)//对象写入文件
    		new ObjectInputStream(new FileInputStream("obj.txt")).readObject()	//从文件中读取对象
    	注:序列化操作的是堆中的对象,所以 static 成员不能被序列化
    		private 成员无法被序列化,使用 UID 标识可打破该限制
    		非静态成员加上 transient 修饰就不会被序列化
    
    "管道流"	PipedInputStream & PipedOutputStream
    	这两个类也是一起使用的
    	一般流的输入输出没有太大关联,而管道流输入输出可以直接进行连接,
    	必须相互连接后创建通信管道;要结合多线程使用,单线程可能会死锁。
    	示例:
    	class Read implements Runnable{}	//封装线程任务
    	class Write implements Runnable{}
    	main	//主函数调用
    	PipedInputStream in = new PipedInputStream();
    	PipedOutputStream out = new PipedOutputStream(in);//构造参数in,也可空参,然后in.connect(out)关联
    	new Thread(new Read(in)).start();	//创建线程
    	new Thread(new Write(out)).start();
    	
    	阻塞式方法,使用中要预防线程死锁
    
    
    Object-->RandomAccessFile 随机读写文件
    	强大的随机读写功能
    	特性: 构造对象时指定读写权限, "r"只读,"rw"读写
    			seek(8*x)方法,通过指针从文件的某个位置开始读写
    	该类是IO包中成员,具备文件读写功能,内部封装了字节输入流和输出流
    	随机是因为在内部封装了一个byte[] 数组,通过指针对数组的元素进行操作,
    	同时可以通过getFilePointer获取指针位置,通过seek改变指针位置。
    	创建对象示例: new RandomAccessFile("File","rw")	//能够对该文件读写操作
    	RandomAccessFile 类如果引入多线程技术,就可以实现 P2P 下载,大大提高效率
    
    其它类似的类区分
    	RandomAccess util包中的标记接口,被 List 实现,用来表明其支持快速随机访问,
    		该类与 RandomAccessFile 无直接联系,另见该包中 Random 类
    	另 java.lang 包中 Math 类的 random() 方法也提供了随机功能"骰子"
    
    ----------------------------------------------
    其他基本数据流
    DataInputStream & DataOutputStream 专门操作基本数据类型的流对象
    	构造时要包装字节流使用
    	如使用 writeInt(22)方法写入; 再使用 readInt()读取到的 数据类型为 int 而不是 byte
    
    ByteArrayInputStream & ByteArrayOutputStream 专门用于操作"字节"数组
    	数据源 为字节数组
    CharArrayReader & CharArrayWriter	操作"字符"数组
    
    StringReader & StringWriter		操作字符串
    
    -----------------------------------------------
    字符编码的问题:乱码
    	如果编码时的码表与解码时的码表不一致,就会导致乱码的情况
    	转换流 具有指定码表的构造方法
    	示例: new InputStreamReader(new FileInputStream("gbk.txt") , "utf-8")//指定utf-8来解码
    	编码与解码的过程: String --编码--> byte[] --解码-->String
    "中间码"的问题:
    	客户端与服务器端的码表不一致,例如一个服务器端(iso8859-1)的网页在客户端(中文 GBK)打开时就可能会产生乱码,
    	所以要加上二次编码与解码的过程,将乱码按照 iso8859-1 码表编码成 byte,再将该数据按照 GBK 解码
    	要注意的是: 作为中间码,一定要保持数据的精度, 即一个字符占据的字节要少于"终端码",
    			如: 终端码为GBK时,就不能用utf-8 作为中间码,因为utf-8 部分字符会占据3个字节,gbk为2个
    		在二次编码的过程中会按照utf-8 码表添加一些字节进去,导致数据精度损失,还会造成乱码。
    		
    "为什么不能用字符流copy 图片"
    	字节流可以操作任何文件,因为其操作单位是数据存储的最基本类型 byte
    	字符流只能用于操作文本,操作的基本数据类型为字符 char,一个字符一般占据 2(1~3)个字节
    	字符流在底层使用了字节流,通过查询编码表(java默认为 unicode)获取到字符,如果码表中能查到这两个字节对应的字符
    	就会返回该 char, 如果没有对应的字符就返回 -1;
    	所以,如果字符流操作的是文本,该文件内的字节在码表中都能够找到,就不会发生丢失数据的问题
    	而如果字符流操作的是非文本文件,就不可能保证所有的字节都能在码表中找到对应的字符,而这些找不到字符的字节
    	随着返回的-1 就消失了,从而造成字节丢失,数据损坏
    




  • 相关阅读:
    第四章 处理器体系结构
    第四节、程序的机器语言
    第三节 信息的表示和处理
    app
    你只是看起来很努力
    tap news:week5 0.0 create react app
    28.week4
    ubuntu去除带锁文件的锁 sudo chown 用户名 目标文件夹/ -R
    26.如何使用python操作我们自己创建的docker image呢?
    25.week4 docker build 也就是创建自己的image 上传image到dockerhub 从dockerhub下载images
  • 原文地址:https://www.cnblogs.com/Joure/p/4337195.html
Copyright © 2011-2022 走看看