根据流的类型,可分为字节流、字符流、对象流。
关于字节流和字符流:
有stream字眼的就是字节流。有Reader/Writer字眼的就是字符流。两种字眼都有的就是转换流。
缓冲流都要以原来的文件非缓冲流做参数,如字符缓冲流(文件字符流),字节缓冲流(文件字节流)。
而文件流都是以文件或文件路径做参数。如文件字符流(String FilePath),文件字节流(String FilePath)
一、文件字节流
1.写入到文件(出现乱码即需要修改编码)
FileOutputStream fos = null;
try {
//写出去,写到文件上(如果文件已存在,则清空再写)
fos = new FileOutputStream("F:\\test.txt");
fos.write(97);//a
fos.write('s');//as
fos.write("卑鄙是卑鄙者的通行证".getBytes());//as卑鄙是卑鄙者的通行证
fos.write('的');//写入单个中文字符将会改变文件编码使文件乱码,正确写入中文应为上方法
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
追加修改
//jdk1.7之后提供了自动关流的方式
/**
* try(流定义语句){
*
* }catch{}
*/
try (FileOutputStream fos = new FileOutputStream("F:\\test.txt",true)){
//后面的参数代表是否追加,默认false(清空再写)
fos.write(98);//在不改动原文件基础上追加b
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
2.文件输入流
try(FileInputStream fis = new FileInputStream("F:\\test.txt")){//文件里:ab
int a;
while((a=fis.read())!=-1) {//读取到-1时表示没有文件可读了
System.out.println(a);//我本来以为会打印 97 98,实际上打印239 187 191 97 98
//查阅得知(239 187 191)是utf-8的BOM头
}
}catch(Exception e){
}
try(FileInputStream fis = new FileInputStream("F:\\test.txt")){//文件里:哈哈
for(int i=0;i<3;i++) fis.read();//跳过BOM文件头
int a1=fis.read();//229
int a2=fis.read();//147
int a3=fis.read();//136
byte[] bs = {(byte) a1,(byte) a2,(byte) a3};//一个中文是三个字节
System.out.println(new String(bs));//哈
}catch(Exception e){
}
每次读取固定长度
try(FileInputStream fis = new FileInputStream("F:\\test.txt")){//文件里:哈哈
byte[] bs = new byte[1024];
int len;
while((len=fis.read(bs))!=-1) {
System.out.println(new String(bs,0,len));//哈哈
}
}catch(Exception e){
e.printStackTrace();
}
3.拷贝文件
try(
FileInputStream fis = new FileInputStream("F:\\view.jpg");//从磁盘读
FileOutputStream fos = new FileOutputStream("F:\\view2.jpg");//写入磁盘
){
byte[] bs = new byte[512];
int len;
while((len=fis.read(bs))!=-1) {
fos.write(bs, 0, len);
}
}catch (Exception e) {
// TODO: handle exception
}
二、缓存字节流
try( BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:\\test.txt"));
){
bos.write("我欲乘风破浪".getBytes());
bos.flush();//将缓冲区的内容写入磁盘,close()之前会默认调用。但一般使用缓冲输出流的时候,都会显式地写出来
}catch (Exception e) {
// TODO: handle exception
}
缓冲文件流也能够实现文件的读写、复制,而且速度更快。
三、转换流(字符流)
由于字节流操作中文不是特别方便,java提供了转换流。将字节流转换成字符流。
InputStreamReader/outputStreamWriter 是字节流通向字符流的桥梁:它使用指定的编码读写字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,默认接受平台设置的字符集。
需要注意的是,字符流只能操作纯文本文件。所以不要用该流去拷贝、传输图片、视频等非文本文件。
字符流=字节流+编码表。
//使用指定编码写到磁盘
try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("F:\\test.txt"),"GBK")){
osw.write("借问梅花何处落");
} catch (Exception e) {
// TODO: handle exception
}
//用指定编码读
try(InputStreamReader isr = new InputStreamReader(new FileInputStream("F:\\test.txt"), "GBK")){
char[] chs = new char[1024];
int len;
while((len=isr.read(chs))!=-1) {
System.out.println(new String(chs,0,len));
}
}catch(Exception e) {
}
四、文件字符流(字符流)
来读取、写入字符文件的快捷类
try (FileWriter fw = new FileWriter("F:\\test.txt");
){
fw.write("心心相印");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
try (FileReader fr = new FileReader("F:\\test.txt")){
char[] chs = new char[10];
int len;
while((len=fr.read(chs))!=-1) {
System.out.println(new String(chs,0,len));
}
} catch (Exception e) {
// TODO: handle exception
}
五、缓冲字符流
高效读写字符文件。
try (BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\test.txt"))){
bw.write("我欲乘风破浪");
bw.newLine();//换行
bw.write("踏遍黄沙海洋");
} catch (Exception e) {
// TODO: handle exception
}
try(BufferedReader br = new BufferedReader(new FileReader("F:\\test.txt"))) {
String str;
while((str=br.readLine())!=null) {//按行读取,拷贝文件最好还是按字符数组读取
System.out.println(str);
}
} catch (Exception e) {
// TODO: handle exception
}
try (
BufferedReader br = new BufferedReader(new FileReader("F:\\test.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\test.txt"));
){
bw.write("你就是天选之人");
bw.close();//当我们使用输入输出流操作同一个文件的时候,写完了以后要先关流。否则流继续占用文件,导致读不出来
String str = br.readLine();
System.out.println(str);
} catch (Exception e) {
// TODO: handle exception
}
六、对象流和序列化
1.对象实现序列化,实现序列化接口即可(Serializable)
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:\\person1.ddl"))
){//其实这里写什么文件名后缀都可以
Person person1 = new Person("sponge", 10);
oos.writeObject(person1);
oos.flush();
} catch (Exception e) {//如果person类没被序列化 java.io.NotSerializableException
// TODO: handle exception
e.printStackTrace();
}
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:\\person1.ddl"))){
Object o1 = ois.readObject();
if(o1 instanceof Person) {
Person o = (Person) o1;
System.out.println(o.getName()+" "+o.getAge());
}
}catch(Exception e) {
e.printStackTrace();
}
2.浅谈序列化ID(serialVersionUID)
关于序列化和反序列化,上面都有简单的案例实现了。可是试想一下,如果我在用对象流写出了person类的实例p1到磁盘之后,修改了模板类Person,新增了一个字段,或者改了一个方法。那么p1还能反序列化回对象吗?
不行,它会抛java.io.InvalidClassException异常。
如果不想这样的结果,可以有如下的解决方案:
①.重写一次,再读。
②.在模板类上生成序列化ID。
网上大佬是这么说的,这个序列化ID起着关键的作用,它决定着是否能够成功反序列化!简单来说,java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。
也就是说,你如果不实现序列化ID,那么序列化的时候会自动分配给模板类及类对象一个序列化ID,你要是改了模板类,那么这个类的序列化ID就变了,反序列化的时候,自然就不匹配,抛异常了。如果你实现了序列化ID,由于其final,那么不管你怎么改模板类(只要不改序列化ID的值),都是可以成功反序列化的(新增的字段将会赋默认值)。
七、properties文件
1.如何创建
2.
try {
//1.创建一个properties对象
Properties p = new Properties();;
//2.使用p对象加载properties文件
p.load(PropertiesTest.class.getClassLoader().getResourceAsStream("config.properties"));
//3.获取单个值
String value = p.getProperty("charset");
System.out.println(value);
//4.遍历
Set