java io流
起因:学习网编,发现里面的课程大量用到了io的知识,十分影响学习,所以转战io
何为“流”
流:数据在数据源(文件)和程序(内存)之间经历的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径
代码最后要关闭流,释放资源。关闭时写在条件if流!= null(说明流用过了)
创建文件
File file=new File(String pathname);//根据路径构建一个File对象 例子:“e:\\news1.txt”
File file=new File(File parent,String child);//根据父目录文件+子路径创建 例子:"e:\\","news2.txt"
File file=new File(String parent,String child)//根据父目录+子路径创建 例子:"e:\\","news3.txt"
//上面三种方法只是在内存中创建file对象,还没有输出到硬盘里面
file.creatNewFile();//真正创建对象
file对象信息
file.getName()//获得文件名
file.getAbsolutePath()//获得绝对路径
file.getParent()//获得父级目录
file.length()//获得文件大小(字节)
file.exists()//判断文件是否存在
file.isFile()//判断是不是一个文件
file.isDirectory//判断是不是一个目录
file.delete//删除文件,返回值是布尔值,成功为1
目录操作
mkdir创建一级目录,mkdirs创建多级目录,delete删除目录或文件。这三个方法都会返回一个布尔值
在java中目录也被当成文件,所以使用时file方法。比如一个目录既可以使用filePath也可以使用directoryPat
流的分类
- 按操作数据单位不同分为:字节流(8bit)【操作二进制文件保证无损】,字符流(按字符)【文本文件效率更高】
- 按数据流的流向不同分为:输入流,输出流
- 按流的角色的不同分为:节点流,处理流/包装流
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
上面的四个都是抽象类
FileInputStream
InputStream是字节输入流
InputStream抽象类是所有类字节输入流的超类
InputStream常用的子类
- FileInputStream文件输入流
- BufferedInputStream缓冲字节输入流
- ObjectInputStream对象字节输入流
read
-
read( )返回值如果为-1,表示读取完毕,所以一般仍在while的括号中。read返回值是int,在输出时需要强转为char才能输出字符
-
read(byte[ ] b)可以提高效率,不一个一个读取字节。
- 如果返回-1,表示读取完毕
- 如果读取正常,返回实际读取的字节数
- 最多读b.length个字节
java中创建数组的方法
byte[] buf = new byte[8];
while((readLen = fileInputStream.read(buf))!=-1){
sout(new String(buf,0,readLen));
}
//如果文件里内容是Hello,world(大于8个字节)
/*行为逻辑:先读取8个字节,readLen值为8,运用new String(buf,0,readLen)将buf数组中0到readLen(左闭右开)的元素转化为一个字符串
第二次,读取剩下三个,readLen为3,同上
第三次,无法读取,readLen为-1,循环结束
字节流无法读取汉字,因为一个汉字不止一个字节,会出现乱码,所以要用字符流
FileOutputStream
write与read用法类似
write(byte[ ] b)中需要用到一个string类型自带的方法 .getBytes 使字符串转换位一个字节数组
注意:如果使用new FileOutputStream(filepath)的方式,当写入内容时会覆盖原来的内容。使用new FileOutputStream(filepath,true)则写入的内容使从末尾追加的
一定要使用write(byte[ ] b, 0 ,readLen)第一个是byte数组,第二个是写出的起始位置,第三个readLen是自定义的,值为每次fileInputStream.read( byte[] b )的返回值。 防止最后一次数组没填充完,将不属于本次写入的数据也写出
文件字符流
FileReader和FileWriter是字符流,即按照字符来操作IO
FileReader相关用法
- new FileReader(File/String) 创建对象
- read方法:每次读取单个字符,返回该字符,如果到文件末尾返回-1
- read(char [ ] ):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1
- new String(char[ ])将char[ ]转换成String
- new String(char[ ],off,len):将char[ ]的指定部分转换成String
FileWriter相关用法
- new FileWriter(File/String) 创建对象,覆盖模式,相当于流的指针在首端
- new FileWriter(File/String , true) 创建对象,覆盖模式,相当于流的指针在尾端
- write(int):写入单个字符
- write方法:每次写入单个字符,返回该字符,如果到文件末尾返回-1
- write(char [ ] ):批量写入多个字符到数组,返回写入到的字符数,如果到文件末尾返回-1。可以用(char[ ],off,len)指定部分
- write(string):写入整个字符串 可以用(string,off,len)指定部分
- toCharArray将String转换成char[ ]
FileWriter使用后,必须要关闭close或者刷新flush,否则写入不到指定文件
节点流处理流
节点流:对特定的数据源读写数据,如FileReader,FileWrite
处理流(包装流)是连接在已存在的流(节点流或处理流),为程序提供更为强大的读写功能,如BufferedReader,BufferedWriter
BufferedReader类中,有属性Reader,即可以封装一个节点流,该节点流可以是任意的,只要是Reader的子类
writer同理
处理流设计模式
处理流(包装流)包装节点流,既可以消除不同节点流的实现差异(有些对文件操作,有些对管道操作,有些对数组操作),也可以提供更方便的方法来完成输入输出
关闭处理流时,会自动关闭他调用的节点流
Buffered使用方法
实例化Buffered,并且使用他的含参构造器,将需要使用的节点流输入进去。就可以通过对象buffered来实现某个节点流的方法
关闭时关闭buffered对象即可
对象处理流
对象流+处理流:OjectInputStream(完成反序列化) ObjectOutputStream(完成序列化)
需求
- 将int num=100这个int数据保存在文件中,保证这个100是int类型而不是double,string等。能够从文件中直接恢复int 100;
- 将Dog dog = new Dog("小黄",3 )这个dog对象保存到文件中,并且能够从文件中恢复
上面的要求,就是能够将基本数据类型或者对象进行序列化和反序列化操作
为了让某个类是可序列化的,该类必须实现如下两个接口之一
- Serializable//这是一个标记接口 推荐选这个
- Externalizable 该接口有方法需要实现,比较麻烦
序列化后,保存的文件格式不是存文本的,而是按照他的格式来保存的
数据和类型
把他们写到ObjectOutputStream的对象的write方法里
int会包装为Integer,他实现了Serializable
boolean会包装为Boolean,他实现了Serializable
char会包装为Charater,他实现了Serializable
double会包装为Double,他实现了Serializable
String本身就实现了Serializable
对象
如果要序列化某个类的对象,就让他implements Serializable
序列化细节
保存类型通过 .writexxx实现,比如保存Int类型就是 .writeInt( ),不写明类型的话就只是保存数据
特别的 String用的是 .writeUTF 对象用的是 .writeObject( )
反序列化细节
反序列化的顺序需要和保存数码(序列化)的顺序一样
反序列话对象要把ois(OjectInputStream对象名).readObject( )赋值给Object 对象名(比如dog)。然后通过 dog . getClass( )查看他的运行类型,dog查看他的属性信息
查看对象属性信息细节
正常会输出奇怪数字
需要在序列化时在Dog类中重写Serializable的toString方法,此时可以反序列化正常输出dog的属性
调用dog的方法:如果序列化的Dog类不在反序列化类所在的包中,或者是私有的不可调用,则需要把Dog定义(整个代码)拷贝到反序列化类所在的保重或者直接拷贝到反序列化类的下方(可以调用private ) (总之要拷贝到可以引用的地方)(引用的方法是import)
原因:Object dog = ois.readObject( ) 中dog的编译类型是Object,无法调用Dog类的方法
对象处理流的使用细节整理
- 读写顺序要一致
- 要求实现序列化或反序列化对象,需要实现Serializable
- 实例化的类中建议添加SerialVersionUID,为了提高版本的兼容性
private static final long serialVersionUID = 1L;
//新增属性时,系统不会认识是创建了一个新类,而是更新了版本
- 序列化对象时,默认将里面所有属性都进行实例化,但除了static或transient修饰的成员
- 序列化对象时,要求里面属性的类型也需要实现序列化接口
- 序列化具备可继承性,父类已经实现了序列化,他的所有子类就都实现了序列化
标准输入输出流
类型 | 默认设备 | |
---|---|---|
System.in 标准输入 | InputStream | 键盘 |
System.out 标准输出 | PrintStream | 显示器 |
System.in 编译类型是InputStream 运行类型是BufferedInputStream
System.iout 编译类型是PrintStream 运行类型也是PrintStream
转换流
InputStreamReader:Reader的子类,可以将InputStream(字节流)包装(转换)成Reader(字符流)
OutputStreamWriter:Writer的子类,可以将OutputStream(字节流)包装成Writer(字符流
默认情况下读取文件用的是utf-8编码,但是如果读的是其他编码就会出现乱码(因为没有指定读取文件的编码方式)
字节流可以指定读取的编码方式
转换流可以把字节流转换为字符流。所以先通过字节流指定编码方式读取,然后再转换为字符流
InputStreamReader
InputStreamReader(InputStream,Charset)
//先指定一个InputStream子类,然后再指定编码类型
解决乱码问题
InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath) , "gbk");
//先选用FileInputStream且采用gbk编码的字节流读取的方法,然后通过转换流包装为isr字符流
BufferedReader br = new BufferedReader(isr);
//将isr节点流转换为br处理流
String s = br.readLine( );
OutputStreamReader
与In相同,可以通过这种方法改变输出文件的编码方式
打印流
打印流只有输出流,没有输入流
PrintStream字节流
PrintWriter字符流
PrintStream
可以直接PrintStream out = System.out;创建一个out对象
在默认情况下,输出数据的位置是标准输出,即显示器
PrintStream编译类型的对象out有一个print方法,其底层是运用的write方法,所以也可以直接调用out的write方法
可以通过
System.setOut(new PrintStream("e:\\f1.txt"));
来修改打印流输出的位置/设备
Properties类
配置文件的格式
键=值
键=值
注意:键值对不需要有空格,值不需要用引号。默认为String类型
常用方法
load:加载配置文件的键值对到Properties对象
list:将数据显示到指定设备/流对象
getProperty(key):根据键获取值
setProperty(key,value):设置键值对到Properties对象,如果key存在就是修改,不存在就是新建
store:将Properties中的键值对存储到配置文件,在idea中,保存信息到配置文件,如果含有中文,会储存为unicode值 store( )括号中第一个是输出流和新文件(修改文件)地址,第二个如果输入一个字符串就可以作为这个新文件的一个注释