流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
a:按照数据流向 输入流 读入数据 输出流 写出数据 b:按照数据类型 字节流 可以读写任何类型的文件 比如音频 视频 文本文件 字符流 只能读写文本文件 字符流和字节流 字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别: 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。 结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
1.输入字节流InputStreamIO 中输入字节流的继承图可见上图,可以看出:
InputStream 是所有的输入字节流的父类,它是一个抽象类。 ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。 ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。
2.输出字节流OutputStream IO 中输出字节流的继承图可见上图,可以看出:
OutputStream 是所有的输出字节流的父类,它是一个抽象类。 ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。 ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。
FileOutputStream类 构造方法: FileOutputStream(File file) FileOutputStream(String name) FileOutputStream的三个write()方法 public void write(int b):写一个字节 超过一个字节 砍掉前面的字节 public void write(byte[] b):写一个字节数组 public void write(byte[] b,int off,int len):写一个字节数组的一部分 FileInputStream类 int read():一次读取一个字节 int read(byte[] b):一次读取一个字节数组 注意:read()方法读取的是一个字节,为什么返回是int,而不是byte? 字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111;那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上;24个0凑足4个字节,那么byte类型的-1就变成int类型的255了这样可以保证整个数据读完,而结束标记的-1就是int类型。
public class FileTest { public static void main(String[] args) throws Exception{ FileInputStream fis = new FileInputStream("aaa.txt"); FileOutputStream fos = new FileOutputStream("bbb.txt"); int len; byte[] arr = new byte[1024 * 8];//自定义字节数组 while((len = fis.read(arr)) != -1) { //fos.write(arr); fos.write(arr, 0, len);//写出字节数组写出有效个字节个数 } //IO流(定义小数组) //write(byte[] b) //write(byte[] b, int off, int len)写出有效的字节个数 fis.close(); fos.close(); } }缓冲思想 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多, 这是加入了数组这样的缓冲区效果,java本身在设计的时候, 也考虑到了这样的设计思想,所以提供了字节缓冲区流 BufferedOutputStream的构造方法 实现一个缓冲输出流。通过设置这样的输出流,一个应用程序可以写字节到基本的输出流,而不必导致每个字节写入的底层系统的调用。 BufferedOutputStream(OutputStream out) BufferedInputStream的构造方法 BufferedInputStream(InputStream in) IO流(BufferedInputStream和BufferOutputStream) A:缓冲思想 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多, 这是加入了数组这样的缓冲区效果,java本身在设计的时候, 也考虑到了这样的设计思想,所以提供了字节缓冲区流 B.BufferedInputStream BufferedInputStream内置了一个缓冲区(数组) 从BufferedInputStream中读取一个字节时 BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个 程序再次读取时, 就不用找文件了, 直接从缓冲区中获取 直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个 C.BufferedOutputStream BufferedOutputStream也内置了一个缓冲区(数组) 程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中 直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里
小数组的读写和带Buffered的读取哪个更快?
定义小数组如果是8192个字节大小和Buffered比较的话定义小数组会略胜一筹,因为读和写操作的是同一个数组而Buffered操作的是两个数组 IO流(flush和close方法的区别) flush()方法: 用来刷新缓冲区的,刷新后可以再次写出(字节缓冲流内置缓冲区,如果没有读取出来,可以使用flush()刷新来) close()方法:用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出字符流是什么 * 字符流是可以直接读写字符的IO流 * 字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出.
OutputStreamWriter的构造方法 OutputStreamWriter(OutputStream out):根据默认编码(GBK)把字节流的数据转换为字符流 OutputStreamWriter(OutputStream out,String charsetName):根据指定编码把字节流数据转换为字符流 方法概述 public void write(int c) 写一个字符 public void write(char[] cbuf) 写一个字符数组 public void write(char[] cbuf,int off,int len) 写一个字符数组的 一部分 public void write(String str) 写一个字符串 public void write(String str,int off,int len) 写一个字符串的一部分
InputStreamReader的构造方法 InputStreamReader(InputStream is):用默认的编码(GBK)读取数据 InputStreamReader(InputStream is,String charsetName):用指定的编码读取数据 方法概述 public int read() 一次读取一个字符 public int read(char[] cbuf) 一次读取一个字符数组 如果没有读到 返回-1
转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的, 所以,为了简化我们的书写,转换流提供了对应的子类。 FileWriter FileReader
public class FileTest { public static void main(String[] args) throws Exception{ //FileReader类的read()方法可以按照字符大小读取 FileReader fr=new FileReader("aaa.txt"); int b; while((b=fr.read())!=-1){ System.out.println((char)b);//int类型转为字符型 } fr.close(); //FileWriter类的write()方法可以自动把字符转为字节写出 FileWriter fw = new FileWriter("aaa.txt",true); fw.write("aaa"); fw.close(); //字符流的拷贝 FileReader fr2 = new FileReader("aaa.txt"); FileWriter fw2 = new FileWriter("bbb.txt"); int ch; while((ch = fr2.read()) != -1) { fw2.write(ch); } fr2.close(); fw2.close(); } }高效的字符输出流: BufferedWriter 构造方法: public BufferedWriter(Writer w) 高效的字符输入流: BufferedReader 构造方法: public BufferedReader(Reader e) 字符缓冲流的特殊功能 BufferedWriter: public void newLine():根据系统来决定换行符 具有系统兼容性的换行符 BufferedReader: public String readLine():一次读取一行数据 是以换行符为标记的 读到换行符就换行 没读到数据返回null 包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
public class FileTest { public static void main(String[] args) throws Exception{ BufferedReader br=new BufferedReader(new FileReader("aaa.txt")); BufferedWriter bw=new BufferedWriter(new FileWriter("bbb.txt")); //BufferedReader和BufferedWriter的使用: int b; while((b=br.read())!=-1){ bw.write((char)b); } br.close(); bw.close(); } }IO流(什么情况下使用字符流)
字符流也可以拷贝文本文件, 但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节.程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流读取的时候是按照字符的大小读取的,不会出现半个中文写出的时候可以直接将字符串写出,不用转换为字节数组IO流(字符流是否可以拷贝非纯文本的文件)
不可以拷贝非纯文本的文件因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去如果是?,直接写出,这样写出之后的文件就乱了,看不了了数据输入流: DataInputStream 让应用程序读取原始java数据类型从底层输入流中的一个独立于机器的方式。一个应用程序使用一个数据输出流来写数据,以后可以通过数据输入流读取。 输入流是不一定安全的多线程访问。线程安全是可选的,是在这个类中的方法的用户的责任。 数据输出流: DataOutputStream 数据输出流可以让一个应用java写的原始数据类型在一个便携式的方式输出流。一个应用程序可以使用一个数据输入流来读取数据。 特点: 可以写基本数据类型,可以读取基本数据类型
操作字节数组 ByteArrayOutputStream ByteArrayInputStream 此流关闭无效,所以无需关闭 操作字符数组 CharArrayWrite CharArrayReader 操作字符串 StringWriter StringReader
一个 PrintStream添加功能到另一个输出流,即打印各种数据值表示的功能。还提供了两个其他功能。不像其他的输出流,一个 PrintStream从不抛出一个 IOException;相反,特殊情况下,只设置一个内部标志,可以通过 checkError测试方法。或者,可以创建一 PrintStream从而自动冲洗;这意味着 flush方法自动调用后一个字节数组写入,其中的 println方法被调用,或者一个换行字符或字节( ‘\n’)写。
打印流的特点 a: 打印流只能操作目的地,不能操作数据源(不能进行读取数据) b: 可以操作任意数据类型的数据 调用print() 方法可以写任意数据类型 c: 如果我们启用自动刷新,那么在调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新 /** 通过以下构造创建对象 能够启动自动刷新 然后调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新 * public PrintWriter(OutputStream out, boolean autoFlush) 启动 自动刷新 * public PrintWriter(Writer out, boolean autoFlush) 启动自动刷新 */ d: 这个流可以直接对文件进行操作(可以直接操作文件的流: 就是构造方法的参数可以传递文件或者文件路径)
在System这个类中存在两个静态的成员变量: public static final InputStream in: 标准输入流, 对应的设备是键盘 public static final PrintStream out: 标准输出流 , 对应的设备就是显示器 System.in的类型是InputStream. System.out的类型是PrintStream是OutputStream的孙子类FilterOutputStream 的子类.
该对象并不是流体系中的一员,其封装了字节流,同时还封装了一个缓冲区(字符数组),通过内部的指针来操作字符数组中的数据。 该对象特点:该对象只能操作文件,所以构造函数接收两种类型的参数:a.字符串文件路径;b.File对象。 该对象既可以对文件进行读操作,也能进行写操作,在进行对象实例化时可指定操作模式(r,rw),我们可以通过getFilePointer方法获取文件指针,并且可以通过seek方法设置文件指针 注意:该对象在实例化时,如果要操作的文件不存在,会自动创建;如果文件存在,写数据未指定位置,会从头开始写,即覆盖原有的内容。 可以用于多线程下载或多个线程同时写数据到文件。
所谓的序列化:就是把对象通过流的方式存储到文件中.注意:此对象 要重写Serializable 接口才能被序列化 反序列化:就是把文件中存储的对象以流的方式还原成对象 序列化流: ObjectOutputStream 反序列化流: ObjectInputStream 如何解决序列化时候的黄色警告线问题 我们的一个类可以被序列化的前提是需要这个类实现Serializable接口,就需要给这个类添加一个标记.-在完成序列化以后,序列化文件中还存在一个标记,然后在进行反序列化的时候,会验证这个标记和序列化前的标记是否一致,如果一致就正常进行反序列化,如果不一致就报错了. 而现在我们把这个类做了修改,将相当于更改了标记,而导致这两个标记不一致,就报错了.
Properties 类表示了一个持久的属性集。 Properties 可保存在流中或从流中加载。 属性列表中每个键及其对应值都是一个字符串。 Properties父类是Hashtable 属于双列集合,这个集合中的键和值都是字符串 Properties不能指定泛型 Properties的特殊功能 public Object setProperty(String key,String value) public String getProperty(String key) public Set stringPropertyNames() Properties的load()和store()功能 Properties和IO流进行配合使用: public void load(Reader reader): 读取键值对数据把数据存储到Properties中 public void store(Writer writer, String comments)把Properties集合中的键值对数据写入到文件中, comments注释
表示其他输入流的逻辑串联。 它从输入流的有序集合开始, 并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取, 依次类推,直到到达包含的最后一个输入流的文件末尾为止 a:构造方法 SequenceInputStream(InputStream s1, InputStream s2) 通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2), 以提供从此 SequenceInputStream 读取的字节。 b:构造方法 SequenceInputStream(Enumeration<? extends InputStream> e) 通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。
