IO流(Input和Output):即数据在两设备间的传输称为流,流的本质是数据传输.
IO概述
1、IO流分类
一种是按照流的方向分类:
以内存作为参照物,
数据往内存中去,叫做输入(Input)。或者叫做读(Read)。
数据从内存中出来,叫做输出(Output)。或者叫做写(Write)。
另一种是按照读写数据的方式不同进行分类:
有的流是按照字节的方式读取,一次读取1个字节byte,等一于一次读取8个二进制位。
这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件等等等...
有的流是按照字符的方式进行读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,
这种流不能读取:图片、声音、视频等文件。只能读取纯文本文件,连word文件都无法读取。
综上所述流的分类:
输入流、输出流
字节流、字符流
2、java IO流这块有四大家族:
java.io.InputStream; 字节输入流
java.io.OutputStrean; 字节输出流
java.io.Reader; 字符输入流
java.io.Writer; 字符输出流
在Java中只要“类名”以Stream结尾的都是字节流。以“Reader/Writer”结尾的都是字符流。
所有的流都实现了:java.io.Closeable接口,都是可关闭的,都有close()方法。
流毕竟是一个管道,这个是内存和硬盘之间的管道,用完之后一定要关闭,不然会耗费很多资源。养成好习惯。
所有的输出流都实现了:java.io.Flushable接口,都是可刷新的,都有flush()方法。输出流在使用完毕之后一定要记得flush(),刷新一下。这个刷新表示将通道/管道当中的剩余为输出的数据强行输出完(清空管道!)刷新的作用就是清空管道。如果没有flush可能会导致丢失数据。
3、java.io包下需要掌握的流有16个:
文件专属流:
java.io.FileInputStream;
java.io.FileOutputStream;
java.io.FileReader;
java.io.FileWriter;
转换流:(将字节流转换成字符流)
java.io.InputStreamReader;
java.io.OutputStreanWriter;
缓冲流专属:(包装流)
java.io.BufferedReader;
java.io.BufferedWriter;
java.io.BufferedInputStream;
java.io.BufferedOutputStream;
数据流专属:
java.io.DataInputStream;
java.io.DataOutputStream;
对象流专属:
java.io.ObjectInputStream;
java.io.ObjectOutputStream;
标准输出流:
java.io.PrintWriter;
java.io.PrintStream;
FileInputStream
1、文件字节输入流。
2、可以读取任何类型的文件。(万能的)
3、常用构造方法:
1.FileInputStream(String fileName); //fileName可以是绝对路径也可以是相对路径,在Windows中fileName路径中的分隔符可以是“\\”,也可以是“/”。
如果要是使用相对路径当前路径是哪里?工程(项目)Project的根路径就是IDEA中使用相对路径时的默认当前路径。
2.FileInputStream(File file) ;//通过File对象创建文件字节输入流
4、常用方法:
1.int read();从该输入流一次最多读取一个字节的数据【返回读取到的字节本身】。
2.int read(byte[] b);从该输入流一次最多读取b.length个字节的数据【返回读取到的字节数量】。减少硬盘和内存的交互,提高程序的执行效率。
比如在一个文件中又“abcdef”。byte数组长度是4。
第一次读取能读取到“abcd”,read方法返回4
第二次读取能读取到“ef”,read方法返回2,“ef”把“ab”覆盖
第三次读取一个字符都读取不到了,那么就会返回-1。
3.int available();返回流当中剩余的没有读到的字节数量,返回剩余的字节的数量
可以用于声明一个available()长度的byte数组,使内存一次性读取文件中所有的数据。
4.long skip(long n);跳过n个字节不读,指针往后移动n个字节,指针直接指向n的位置,下一次读取直接读取到“n+1”位置的字节
FileOutputStream
1、文件字节输出流。
2、常用构造方法:
1.FileOutputStream(String fileName); //fileName可以是绝对路径也可以是相对路径,在Windows中fileName路径中的分隔符可以是“\\”,也可以是“/”。
如果要是使用相对路径当前路径是哪里?工程(项目)Project的根路径就是IDEA中使用相对路径时的默认当前路径。
2.FileOutputStream(String fileName, boolean append);append默认是false,false表示将文件原有内容清空,再将新的内容写进去。true表示再原有内容的基础上往后追加数据。
3.FileOutputStream(File file) ;//通过File对象创建文件字节输出流
3、常用方法:
1.void write(byte[] b);将 b.length个字节从指定的字节数组写入此文件输出流。
2.void write(byte[] b,int off,int len);将 len字节从位于偏移量 off的指定字节数组写入此文件输出流
3.void write(int b);将指定的字节写入此文件输出流。
上述三个方法中,如果写入的文件不存在那么自动创建该文件。
FileReader
1、文件字符输入流,只能读取普通文本。读取纯文本内容时,比较方便快捷。
2、常用构造方法:
1.FileReader(String fileName);
3、常用方法:
1.int read();读取一个字符,返回该字符的ascii码值。
2.int read(char[] c);从该输入流一次最多读取c.length个字节的数据【返回读取到的字节数量】。减少硬盘和内存的交互,提高程序的执行效率。
FileWriter
1、文件字符输出流,只能输出普通文本。
2、常用构造方法:
1.FileWriter(String fileName);
2.FileWriter(String fileName,boolean append);
3、常用父类方法:
1.void write(char[] cbuf);将cbuf数组写入某个文件中
2.void write(int ch);将ch写入文件
3.void write(String str);将str写入文件
4.void write(char[] cbuf,int off,int len);将数组的一部分写入文件
5.void write(String str,int off,int len);将字符串的一部分写入某个文件
BufferedReader
1、带有缓冲区的字符输入流。使用这个流的时候不需要自定义char数组(FileReader和FileWriter需要自定义char数组),自带缓冲。
2、常用构造方法:
1.BufferedReader(Reader in);创建“默认大小的输入缓冲区”的缓冲字符输入流
2.BufferedReader(Reader in, int sz);创建“指定大小的输入缓冲区”的缓冲字符输入流。
当一个流的构造方法中需要流的时候,这个被传进来的流叫做:节点流。
外部负责包装的这个流:叫做“包装流”或者“处理流”。
对于包装流来说只需要关闭最外层的“包装流”即可,“节点流”不需要我们关闭。
3、常用方法:
1.String readLine();指针初始位置再第一行的上面,调用本方法将指针往下移动一行,并且返回指针指向的当前行的内容,读取一行,。
读取一行中所有的内容,但是不包括最后的那个换行符。
到文件末尾,读不到数据了就返回null。
java.io.DataOutputStream和java.io.DataInputStream
1、这个流可以将数据连同数据的数据类型一并写入文件。
注意:这个文件不是普通的文本文档。(这个文件用记事本打不开)
2、构造方法:
DataOutputStream(OutputStream out);
DataInputStream(InputStream in);
3、使用 DataOutputStream 写进去的,只能使用DataInputStream读出来。
并且是怎样写进去的就必须怎样读出来。知道写进去的时候的顺序,知道他的规则,才能读出来。(可以理解为加密)
比如先写进去写个int类型的数据,在写进去一个char类型的数据。就必须先读出一个int类型的数据,再读出一个char类型的数据。
4、写入数据使用:
writeXxx();Xxx代表要写入的数据的数据类型
读出数据使用:
readXxx();Xxx代表要读出来的数据的数据类型
PrintStream
1、PrintStream是标准字节输出流。默认输出到控制台。
2、System.out.println("");中的System.out;返回的说就是一个PrintStream对象。
PrintStream流默认指向控制台,我们可以通过:System.setOut(PrintStream ps);改变流的默认指向
3、与之相对的是 PrintWriter [这个是以字符的方式输出]。
4、System.in,返回的是InputStream类对象,是一个标准输入流。这个标准输入流默认输入设备是键盘。
可以通过System.setIn(InputStream in);改变输入流的默认指向
File
1、File类和四大家族没有关系,所以File不能完成文件的都和写。
2、File是“文件”或“目录”的路径名的抽象表示形式。
3、常用构造方法:
1.File(File parent,String child);从父抽象路径名和子路径名字符串创建新的 File实例。
2.File(String pathname) ;通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
3.File(String parent, String child) ;从父路径名字符串和子路径名字符串创建新的 File实例。
4.File(URI uri) ;通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。
3、File类的常用方法:
1.boolean exists() 测试文件或目录是否存在【存在返回true,不存在返回false】
2.boolean createNewFile() 当且仅当具有该名称的文件或目录都不存在的时候,才会创建新的文件。【新建了文件返回true,没有新建文件返回false】
3.boolean mkdir() 当且仅当具有该名称的文件或目录都不存在的时候,才会创建新目录。【新建了目录返回true,没有新建目录返回false】
4.boolean mkdirs() 支持以多重目录的形式新建目录
5.boolean canExecute() 是否可执行
6.boolean canRead() 是否可读
7.boolean canWrite() 是否可写
8.int compareTo(File pathname) 按字典的顺序比较两个File
9.boolean equals(Object obj) 判断两个File是否相等
10.File getAbsoluteFile() 将绝对路径包装成一个File对象返回。
11.String getAbsolutePath() 返回File对象的绝对路径的String表示形似
12.String getName() 返回文件或目录的名称
13.String getParent() 返回文件或目录的父路径
14.String getPath() 将此路径转换为字符串
15.boolean isAbsolute() 测试File对象是否为绝对路径
16.boolean isDirectory() 测试File对象是否为目录
17.boolean isFile() 测试File对象是否为文件
18.boolean isHidden() 测试File对象是否为隐藏文件
19.long lastModified() 获取文件上次修改时间
20.long length() 返回文件的长度(字节数)
21.String[] list() 如果File对象是目录,则返回该目录中的文件名和目录名包装到一个String数组中;如果File对象是文件则返回null
22.File[] listFiles() 返回File数组对象,其中每一个元素都是File对象
23.File[] listRoots() 列出可用的文件系统根
24.boolean renameTo(File dest) 重命名
25.boolean setExecutable(boolean executable) 设置是否可执行(还有一些可读、可写等操作)
26.Path toPath() 返回从此抽象路径构造的java.nio.file.Path对象。
27.URI toURI()
28.URL toURL()
对象流
1、序列化和反序列化:
序列化:Serialize,内存中的java对象存储到硬盘的文件中的过程。【使用ObjectOutputStream】
反序列化:DeSerialize,将硬盘上的数据重新恢复到内存当中,恢复成java对象。【使用ObjectInputStream】
2、ObjectOutputStream:对象输出字节流
构造方法:ObjectOutputStream();
ObjectOutputStream(OutputStream in);
将对象写进文件:序列化,使用writeObject(Object o)方法
3、ObjectInputStream:对象输入字节流
构造方法:ObjectInputStream();
ObjectInputStream(InputStream in);
将对象加载进内存:反序列化,使用Object readObject()
4、参与序列化和反序列化的对象,必须实现Serializable接口。
5、通过源代码发现Serializable只是一个标志性的接口:
public interface Serializable {
}
这个接口中什么代码也没有。
那么它起到一个什么作用呐?
起到一个标志的作用,JVM看到某个类实现了这个接口之后,可能会对这个类进行特殊待遇。
6、序列化多个对象可以用集合。对一个集合对象序列化,集合对象和集合中存储的对象都需要实现序列化接口。
7、transient关键字表示“游离的”,transient修饰的属性,不参与序列化的。
8、Java语言中是采用什么机制来区分类的?
第一:首先通过类名进行对比,如果类名不一样,肯定不是同一个类。
第二:如果类名一样,然后再靠序列化版本号进行区分。
9、实现Serializable接口,编译器会为Java类自动生成一个序列化版本号。
自动生成的序列化版本号缺点:后续不能该代码。【加入10年前写的一个类,序列化进去的数据,因为修改了本类的代码而导致,不能反序列化出来了】
10、最终结论:凡是实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。
11、序列化版本号字段生命为:
private static final long serialVersionUID = 21124124124123L;
具有全球唯一性最好。
因为小红写了一个类叫做Student,小刚也写了一个类叫做Student【也可能是10年前的一个类,10年后要升级了,重新编译就对导致有新的序列化版本号】
那么当他们的序列化版本号一样的时候,JVM就会认为他们两个人写的两个类是同一个类。
会导致小红序列化进去的数据,小刚可以反序列化出来,但是这个类本来就不是小刚的。
对于一个文件来说,文件只知道类名相同序列化版本号相同就是一个类。
序列化的时候会将类的名字,和序列化版本号同时存储进硬盘文件。反序列化的时候会将类名和序列化版本号统一对比。