Java-35 IO流
IO流的分类:

一般情况下,开发中默认按照数据类型区分类使用
一、字节流
1、字节输出流:


import java.io.File; import java.io.FileOutputStream; /* 需求:往文件中写入一句话:“大数据,yyds” FileOutputStream 构造方法: FileOutputStream(File file) FileOutputStream(String name) void write(byte[] b) 将 b.length个字节从指定的字节数组写入此文件输出流。 void write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流。 void write(int b) 将指定的字节写入此文件输出流。 */ public class FileOutputStreamDemo1 { public static void main(String[] args)throws Exception { //创建字节输出流对象 //FileOutputStream(File file) FileOutputStream fos = new FileOutputStream(new File("a.txt")); //写数据 //void write(int b) 将指定的字节写入此文件输出流。 //97,底层存储的是二进制,97对应的ASCLL码值是--a fos.write(97); fos.write(57); fos.write(59); //void write(byte[] b)将 b.length个字节从指定的字节数组写入此文件输出流。 byte[] bys = {97,98,99,100,110}; fos.write(bys); //void write(byte[] b, int off, int len) // 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流。 fos.write(bys,1,3); fos.write("大数据,yyds".getBytes()); //释放资源 //关闭此文件输出流并释放与此流相关联的任何系统资源 fos.close(); } }
代码分析:

注意事项:


那么如何实现续写和追加写呢

import java.io.FileOutputStream; /* 每个系统的换行符都不一样 Windows:\r\n Wac:\r Linux:\n 如何实现数据的追加呢? 追加:在原有的文件内容上继续添加一些其他内容 覆盖:将原有的文件内容先删掉,再添加一些其他内容 FileOutputStream(File file, boolean append) 创建文件输出流以写入由指定的 File对象表示的文件。 */ public class FileOutputStreamDemo2{ public static void main(String[] args) throws Exception{ //FileOutputStream fos = new FileOutputStream("a1.txt"); //追加写入 true代表的追加写入,默认是false覆盖 FileOutputStream fos = new FileOutputStream("a1.txt",true); for (int i=0;i<10;i++){ fos.write("大数据,yyds".getBytes()); fos.write("\r\n".getBytes()); } fos.close(); } }

字节输入流:


import java.io.FileInputStream; /* FileInputStream:字节输入流: 1、创建FileInputStream对象 2、调用read方法读取数据,输出到控制台 3、释放资源 int read() 从该输入流读取一个字节的数据。 int read(byte[] b) 从该输入流读取最多 b.length个字节的数据为字节数组。 */ public class FileInputStreamDemo1 { public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("a1.txt"); //调用read方法读取数据,输出到控制台 //int read()从该输入流读取一个字节的数据。 // System.out.println(fis.read()); //强转,转成字符类型 // System.out.println((char)fis.read()); //我们发现,当文件的内容过多的时候,一个字节一个字节的去读,发现重复且麻烦 //API告诉我们什么时候读到了末尾,如果达到文件的末尾, -1 //这是字节输入流写法的第一种方式 // int len = 0; // while((len=fis.read())!=-1){ // System.out.println((char)len); // } //字节流读取数据第二种方式最终代码 //字节输入流读取数据的第二种方式:一次读取一个字节数组 //int read(byte[] b) 从该输入流读取最多b.length个字节的数据为字节数组。 byte[] bys = new byte[1024]; //开发中这里数值最好给的是1024的倍数 int length = 0; while ((length=fis.read(bys))!=-1){ String s = new String(bys, 0, bys.length); System.out.print(s); } fis.close(); } }
读取数据的两种方式:
1、一次读一个字节

2.一次都多个字节

两种方式对比:

import java.io.FileInputStream; import java.io.FileOutputStream; /* 文件复制: 数据源:从哪来 a.txt -- 读取数据 -- FileInputStream 目的地:写到哪里去 a1.txt -- 写出数据 -- FileOutputStream 因为我们这次,读取的时候,是一个字节一个字节的去读,写的时候也是一个字节一个字节的 去写,没有做任何转换,它自己内部会做转换。 */ public class FileCopyDemo1 { public static void main(String[] args) throws Exception{ //创建字节输入流 FileInputStream fis = new FileInputStream("a1.txt"); //创建字节输出流 FileOutputStream fos = new FileOutputStream("a2.txt"); //按一个字节读写数据 // int b = 0; // while ((b = fis.read())!=-1){ // System.out.println(b); // fos.write(b); // } //一个数组读取 byte[] bytes = new byte[1024]; int len =0; while ((len=fis.read(bytes))!=-1){ fos.write(bytes,0,len); } //释放资源 fos.close(); fis.close(); } }
文件复制小案例:

需求分析:

代码实现:
import java.io.FileInputStream; import java.io.FileOutputStream; public class FileInputStreamDemo2 { public static void main(String[] args) throws Exception { //新建一个字节输入流对象,构造方法中绑定要读取的数据 FileInputStream fis = new FileInputStream("c:\\1.png"); //创建一个字节输出流对象,构造方法中写入要绑定的目的地 FileOutputStream fos = new FileOutputStream("d:\\1.png"); //一次读取一个字节写入一个字节 // int len = 0 ; // while((len=fis.read())!=-1){ // fos.write(len); // } //使用数组缓冲区读取多个字节,写入多个字节 byte[] bytes = new byte[1024]; int len1 = 0 ; while((len1=fis.read(bytes))!=-1){ fos.write(bytes,0,len1); } //资源释放(先关写的,后关闭读的,如果写完毕了。肯定读完毕了) fos.close(); fis.close(); } }
二、字符流
字符输入流


import java.io.FileReader; public class FileReaderDemo { public static void main(String[] args) throws Exception { //创建FileReader对象,构造方法绑定要读取的数据源 FileReader fr = new FileReader("a1.txt"); //使用FileReader对象read读取文件 //int read()读取单个字符返回 // int len = 0 ; // while((len=fr.read())!=-1){ // System.out.println((char)len); // } // //一次读取多个字符,将字符读入数组 //int read(char[] cbuf)一次读取多个字符,将字符读入数组 char[] chars = new char[1024]; int len1 = 0 ; while((len1=fr.read(chars))!=-1){ /* String类的构造方法 String(char[] value)把字符数组转换为字符串 String(char[] value,int offset,int count)把字符数组一部分转换为字符串,offset是开始索引,count是转换的个数 */ System.out.println(new String(chars, 0, len1)); } fr.close(); } }
字符输出流:


import java.io.FileWriter; import java.io.IOException; public class FileWriterDemo { public static void main(String[] args) throws IOException { FileWriter fw = new FileWriter("c.txt"); //写入单个字符 void write(int c) fw.write(97); //写入字符数组 char[] cs ={'a','b','c'}; fw.write(cs,1,2); //写入字符串 fw.write("大数据yyds"); fw.write("大数据牛",0,3);//大数据 //资源刷新 fw.flush(); //刷新之后流可以继续使用 fw.write(98); //资源释放 fw.close(); //释放资源之后流不可以再用 // fw.write(98); } }

三、缓冲流
字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流

1、字节缓冲输出流 BufferedOutputStream

public class BufferedOutputStreamDemo1 { public static void main(String[] args) throws Exception{ // FileOutputStream fos = new FileOutputStream("a3.txt"); // // //BufferedOutputStream // BufferedOutputStream bos = new BufferedOutputStream(fos); //强烈推荐使用该方式创建你 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a3.txt")); bos.write("大数据,yyds;小红18".getBytes()); bos.flush(); bos.close(); } }
2、字节缓冲输入流 BufferedInputStream

public class BufferedInputStreamDemo1 { public static void main(String[] args) throws Exception{ BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt")); //一个字节的读取 // int b = 0; // while ((b=bis.read())!=-1){ // System.out.print((char) b); // } System.out.println("------------------------"); //一次读取一个字节数组 byte[] bys = new byte[1024]; int length = 0; while ((length= bis.read(bys))!=-1){ System.out.println(new String(bys,0,length)); } bis.close(); } }
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; public class BufferText { public static void main(String[] args) throws Exception { long s = System.currentTimeMillis(); BufferedInputStream bis = new BufferedInputStream(new FileInputStream("c:\\1.png")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\1.png")); //使用字节缓冲输入流读取文件 //一次读一个字节 int len = 0 ; while((len=bis.read())!=-1){ bos.write(len); } bos.close(); bis.close(); long e = System.currentTimeMillis(); System.out.println("复制文件共耗时:"+(e-s)+"毫秒");//45 } }
3、字符缓冲输出流


public class BufferedWriterDemo1 { public static void main(String[] args) throws Exception { //创建字符缓冲输出流 //BufferedWriter(Writer out) //创建使用默认大小的输出缓冲区的缓冲字符输出流。 // BufferedWriter bw = new BufferedWriter( // new OutputStreamWriter( // new FileOutputStream("b4.txt"))); BufferedWriter bw = new BufferedWriter(new FileWriter("b4.txt")); //void write(String str) //写一个字符串 bw.write("hello"); bw.write("world"); bw.write("java"); bw.flush(); //释放资源 bw.close(); } }
4、字符缓冲输入流

import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileReader; import java.io.InputStreamReader; /* BufferedReader:字符缓冲输入流 BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。 */ public class BufferedReaderDemo1 { public static void main(String[] args) throws Exception { // BufferedReader br = new BufferedReader( // new InputStreamReader(new FileInputStream("a.txt"))); BufferedReader br = new BufferedReader(new FileReader("a.txt")); //第一种:一次只读一个字符 // int ch =0; // while ((ch=br.read())!=-1){ // System.out.print((char) ch); // } //第二种:一次读取一个字符数组 char[] chars = new char[1024]; int length = 0; while ((length=br.read(chars))!=-1){ System.out.println(new String(chars,0,length)); } //释放资源 br.close(); } }
字符缓冲流中的特殊方法(一次读一行写一行 readline)
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; /* 字符缓冲流的特殊方法: BufferedWriter: public void newLine() throws IOException写一行行分隔符。 行分隔符字符串由系统属性line.separator定义, 并不一定是单个换行符('\ n')字符。 BufferedReader: public String readLine() throws IOException读一行文字。到换行符终止 */ public class bufferedWriterDemo2 { public static void main(String[] args) throws Exception { // write(); // read(); } private static void read() throws Exception { //创建字符缓冲输入流 BufferedReader br = new BufferedReader(new FileReader("a.txt")); // String s = br.readLine(); // System.out.println(s); // // String s1 = br.readLine(); // System.out.println(s1); // // String s2 = br.readLine(); // System.out.println(s2); //用循环改进,因为不知道一个文件有多少行,所以我们用while循环 //如果已达到流的末尾,则为null String line = null; while ((line = br.readLine())!=null){ System.out.println(line); } br.close(); } private static void write() throws Exception { //创建字符缓冲输出流 BufferedWriter bw = new BufferedWriter(new FileWriter("b6.txt")); //往文件中写入数据 for (int i = 0; i < 10; i++) { bw.write("大数据 " + i); // bw.write("\r\n"); bw.newLine();//根据系统自动识别换行符是什么 bw.flush(); } //释放资源 bw.close(); } }
四、转换流
由于字节流操作中文不是特别方便,所以,java就提供了转换流。 字符流=字节流+编码表。
编码表 由字符及其对应的数值组成的一张表
常见编码表
ASCII/Unicode
字符集
ISO-8859-1
GB2312/GBK/GB18030
BIG5
UTF-8

编码 把看得懂的变成看不懂的
解码 把看不懂的变成看得懂的

可以看出字符输入流底层源码就是继承父类方法字节输出流

编码和解码原理

import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class fis { public static void main(String[] args) throws Exception{ InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"), "gbk"); OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"), "utf-8"); int len=0; while((len=isr.read())!=-1){ osw.write(len); } osw.close(); isr.close(); } }
五、序列化流:
对象序列化是将对象状态转换为可保持或传输的过程。一般的格式是与平台无关的二进制流,可以将这种二进制流持久保存在磁盘上,也可以通过网络将这种二进制流传输到另一个网络结点。
对象反序列化,是指把这种二进制流数据还原成对象。

序列化:
对象实现标记接口


import java.io.Serializable; public class Person implements Serializable { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }

对象的序列化流,写方法其实就是将对象持久化
import java.io.FileOutputStream; import java.io.ObjectOutputStream; public class ObjectInputStreamDemo { public static void main(String[] args) throws Exception{ //创建ObjectOutputStream对象,构造方法传递字节输出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt")); //使用ObjectOutputStream中的方法 oos.writeObject(new Person("小花",18)); //关闭资源 oos.close(); } }
对象的反序列化流
import java.io.FileInputStream; import java.io.ObjectInputStream; public class ObjectOutStreamDemo { public static void main(String[] args) throws Exception{ //创建ObjectOutputStream对象,构造方法传递字节输出流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt")); //使用ObjectInputStream中的readObject()方法读取保存对象的文件 Object object = ois.readObject(); //关闭资源 ois.close(); //使用读取出来的对象(打印) System.out.println(object); Person p = (Person) object; System.out.println(p.getName() + p.getAge()); } }
Java在序列化中提供了一个ID值,可以让我们去设定。serialVersionUID
需求:
现在我不想在序列化的时候把年龄也序列化存到文件中。
java中提供了一个关键字,可以让我们在序列化的时候选择哪些成员不被序列化
叫做:transient
打开uid
import java.io.Serializable; /* 未序列化异常: java.io.NotSerializableException 类的序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态序列化或反序列化。 可序列化类的所有子类型都是可序列化的。 序列化接口没有方法或字段,仅用于标识可串行化的语义。 java.io.InvalidClassException: com.shujia.java.day23.Person; local class incompatible: stream classdesc serialVersionUID = 6760410433349567446, local class serialVersionUID = -6862371829361004873 为什么会有这样的问题呢? Person类实现了Serializable标记接口,它本身就应该有一个标记值。 假设一开始这个标记值是id=100 在没有修改Person类的之前: Person.class -- id=100 写数据的时候
0 进行修改后:(把private删了) Person.class -- id=200 写数据的时候 :obj.txt -- id=100 读数据的时候 :obj.txt -- id=100 在实际开发中,我们因业务问题,不允许重复往文件或者某数据库重复存储(写入),那怎么办呢? 这个问题是由于id值的不匹配(class文件的id值与文件中存储对象的id不匹配)导致的。 如果说有一个办法可以让这个ID值固定不变就好了。 Java在序列化中提供了一个ID值,可以让我们去设定。serialVersionUID 需求: 现在我不想在序列化的时候把年龄也序列化存到文件中。 java中提供了一个关键字,可以让我们在序列化的时候选择哪些成员不被序列化 叫做:transient */ public class Person implements Serializable { private static final long serialVersionUID = -8678085560037674101L; private String name; private transient int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
总结:
IO:是用来解之间数据传输的问题
按照流向进行划分: 1、输入流 2、输出流
按照数据类型划分:
1、字节流
1)基本字节流
基本字节输入流 InputStream(抽象类) FileInputStream
读取数据的两种方式:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(...));
int b = 0; while((b=bis.read())!=-1){ System.out.print((char)b); } bis.close();
byte[] bytes = new byte[1024]; int length = 0; while((length = bis.read(bytes))!=-1){ System.out.print(new String(bytes,0,length)); } bis.close();
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(...));
2、字符流
字符流 = 字节流 + 编码表
1)基本字符转换流
基本字符输入流 Reader
A: InputStreamReader
创建对象:
InputStreamReader isr = new InputStreamReader(new FileInputStream(...));
读取数据的两种方式:
a) 一次只读取一个字符
int ch = 0;
while((ch = isr.read())!=-1){
System.out.println((char)ch);
}
isr.close();
b) 一次读取一个字符数组
char[] chars = new char[1024];
int length = 0;
while((length = isr.read(chars))!=-1){
System.out.print(new String(chars,0,length));
}
isr.close();
B:简化写法:FileReader
创建对象:
FileReader fr = new FileReader(...);
读取数据的两种方式:
a) 一次只读取一个字符
int ch = 0;
while((ch = fr.read())!=-1){
System.out.println((char)ch);
}
fr.close();
b) 一次读取一个字符数组
char[] chars = new char[1024];
int length = 0;
while((length = fr.read(chars))!=-1){
System.out.print(new String(chars,0,length));
}
fr.close();
基本字符输出流 Writer:
A: OutputStreamWriter
创建对象:
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(...));
写数据方法
一次只写一个字符
一次写一个字符数组
一次写一个字符数组的一部分
注意:每写完后,调用flush()刷新缓冲区
B:简化写法:FileWriter
创建对象:
FileWriter fw = new FileWriter(...);
一次只写一个字符
一次写一个字符数组
一次写一个字符数组的一部分
注意:每写完后,调用flush()刷新缓冲区
2)字符缓冲流
字符缓冲输入流:
BufferedReader:
创建对象:
方式1:BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(...)));
方式2:BufferedReader br = new BufferedReader(new FileReader(...));
读取数据的三种方式:
a)一次读取一个字符
b)一次读取一个字符数组
c)特殊方法:readLine(),一次读取一行
String line = null;
while((line=br.readLine())!=null){
System.out.println(line);
}
br.close();
字符缓冲输出流:
BufferedWriter
创建对象:
方式1:BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(...)));
方式2:BufferedWriter bw = new BufferedWriter(new FileWriter(...));
写数据的四种方式:
a)一次只写一个字符
b)一次写一个字符数组
c)一次写一个字符数组的一部分
d)特殊方法:newLine();
注意:每写完后,调用flush()刷新缓冲区


浙公网安备 33010602011771号