IO流 流 流的概念 流(stream)是指一连串流动字节/字符,按照先进先出的方式发送的信息的通道中。 数据源:流入通道中的数据的来源 目的地:流出通道的数据的目的地 输入流和输出流 数据源的数据流入程序的流称为输入流 程序中的数据流出到目的地的流称为输出流 流的分类 InputStream/Ou ......
io流
流
流的概念
流(stream)是指一连串流动字节/字符,按照先进先出的方式发送的信息的通道中。
数据源:流入通道中的数据的来源
目的地:流出通道的数据的目的地
输入流和输出流
数据源的数据流入程序的流称为输入流
程序中的数据流出到目的地的流称为输出流
流的分类
inputstream/outputstream
inputstream 表示字节输入流,是所有字节输入流的抽象父类。提供了read/read(byte[] buf) 用于读取一个或者多个字节;close用于关闭输入流。
outputstream 表示字节输出流,是所有字节输出流的抽象父类。
fileinputstream
fileinputstream 专门用于读取文件的字节输入流。可以用于读取文本性文件(存在编码)、图片音频视频等二进制文件。
一次读取一个字节
package cn.sxt01.fileinputstream;
import java.io.file;
import java.io.fileinputstream;
import java.io.filenotfoundexception;
import java.io.ioexception;
public class test01 {
public static void main(string[] args) {
// 需求:读取c.txt中的内容
file file = new file("d:\\javatest\\c.txt");
// 【1】构建输入流(管道)
fileinputstream fis = null;
try {
fis = new fileinputstream(file);
system.out.println(fis);
} catch (filenotfoundexception e) {
e.printstacktrace();
}
// 【2】读取一个字节
int r = 0;
try {
r = fis.read();
} catch (ioexception e) {
e.printstacktrace();
}
system.out.println((char)r);
// 【3】关闭流
try {
fis.close();
} catch (ioexception e) {
e.printstacktrace();
}
}
}
|
read() 一次读取一个字节,如果已到达文件末尾,则返回 -1。
一次读取多个字节
package cn.sxt01.fileinputstream;
import java.io.file;
import java.io.fileinputstream;
import java.io.filenotfoundexception;
import java.io.ioexception;
import java.util.arrays;
public class test03 {
public static void main(string[] args) {
// 需求:读取c.txt中的内容
file file = new file("d:\\javatest\\c.txt");
// 【1】构建输入流(管道)
fileinputstream fis = null;
try {
fis = new fileinputstream(file);
system.out.println(fis);
} catch (filenotfoundexception e) {
e.printstacktrace();
}
// 【2】读取一个字节
int len = 0; // 读取到缓冲区中的字节个数
byte[] buf = new byte[2]; // 字节缓冲区
stringbuilder sb = new stringbuilder();
try {
while( (len=fis.read(buf)) != -1 ) {
string tmp = new string(buf,0,len);
sb.append(tmp);
}
} catch (ioexception e) {
e.printstacktrace();
}
system.out.println(sb);
// 【3】关闭流
try {
fis.close();
} catch (ioexception e) {
e.printstacktrace();
}
}
}
|
read(byte[] buf)一次读取多个字节到字节缓冲区,并返回读取的字节个数。
fileoutputstream
一次写一个字节
package cn.sxt01.fileouputstream;
import java.io.file;
import java.io.fileinputstream;
import java.io.filenotfoundexception;
import java.io.fileoutputstream;
import java.io.ioexception;
public class test01 {
public static void main(string[] args) {
// 需求:写入helloworld 到d.txt中的内容
file file = new file("d:\\javatest\\d.txt");
// 【1】创建输出流(管道)
fileoutputstream fos = null;
try {
fos = new fileoutputstream(file);
} catch (filenotfoundexception e) {
e.printstacktrace();
}
//【2】写入信息到输出流
try {
fos.write('h');
fos.write('e');
fos.write('l');
fos.write('l');
fos.write('o');
} catch (ioexception e) {
e.printstacktrace();
}
// 【3】刷新缓冲区
try {
fos.flush();
fos.close();
} catch (ioexception e) {
e.printstacktrace();
}
}
}
|
当调用write(int)方法时,把一个字节写入到输出流,输出流会立即把该字节写入文件中。
所以,如果不手动代用flush方法,信息也写入到文件中。
一次写多个字节(指定编码)
package cn.sxt01.fileouputstream;
import java.io.file;
import java.io.fileinputstream;
import java.io.filenotfoundexception;
import java.io.fileoutputstream;
import java.io.ioexception;
public class test02 {
public static void main(string[] args) {
// 需求:写入helloworld 到d.txt中的内容
file file = new file("d:\\javatest\\d.txt");
// 【1】创建输出流(管道)
fileoutputstream fos = null;
try {
fos = new fileoutputstream(file);
} catch (filenotfoundexception e) {
e.printstacktrace();
}
//【2】写入信息到输出流
try {
string str = "hello world中国";
// 默认gbk编码
/*byte[] buf = str.getbytes();
fos.write(buf);*/
byte[] buf = str.getbytes("utf8");
fos.write(buf);
} catch (ioexception e) {
e.printstacktrace();
}
// 【3】刷新缓冲区
try {
fos.flush();
// 【4】关闭文件
fos.close();
} catch (ioexception e) {
e.printstacktrace();
}
}
}
|
write(byte[] buf)一次写入多个字节到输出流。注意,此时的字节流已经经过编码。默认是系统编码(win7:gbk)。
write(byte[] buf,offset,len) 一次写入buf中从offset开始,len个长度的字节到输出流。
综合案例:
[1]把目录中的logo.jpg复制到工程中。
[2]请打印复制进度
reader/writer
reader 是字符输入流的抽象父类。提供了read()一次读取一个字符;read(char[] buf)一次多去多个字符到字符缓冲区。
writer 是字符输出流的抽象父类。提供了
write()写入单个字符
write(char[] cbuf) 写入多个字符
write(string str) 写入字符串
filereader
filereader是文件字符输入流,专门用于读取文本性文件。不能读取图片、音频、视频等二进制文件。
一次读取一个字符
package cn.sxt03.filereader;
import java.io.file;
import java.io.filenotfoundexception;
import java.io.filereader;
import java.io.ioexception;
public class test01 {
public static void main(string[] args) {
file file = new file("d:\\javatest\\d.txt");
// 【1】建立字符输入流
filereader fr = null;
try {
fr = new filereader(file);
} catch (filenotfoundexception e) {
e.printstacktrace();
}
// 【2】一次读取一个字符
int r = 0;
try {
/*r = fr.read();
r = fr.read();
r = fr.read();
r = fr.read();
r = fr.read();
r = fr.read();
system.out.println(r);*/
/*
中国abc\r\n
中国你好你好
*/
stringbuilder sb = new stringbuilder();
while( (r=fr.read()) != -1 ) {
sb.append((char)r);
}
system.out.println(sb);
} catch (ioexception e) {
e.printstacktrace();
}
// 【3】关闭输入流
try {
fr.close();
} catch (ioexception e) {
e.printstacktrace();
}
}
}
|
一次读取多个字符
package cn.sxt03.filereader;
import java.io.file;
import java.io.filenotfoundexception;
import java.io.filereader;
import java.io.ioexception;
import java.util.arrays;
public class test01 {
public static void main(string[] args) {
file file = new file("d:\\javatest\\d.txt");
// 【1】建立字符输入流
filereader fr = null;
try {
fr = new filereader(file);
} catch (filenotfoundexception e) {
e.printstacktrace();
}
// 【2】一次读取多个字符
int len = 0;
char[] cbuf = new char[2];
try {
/*
len = fr.read(cbuf);
len = fr.read(cbuf);
len = fr.read(cbuf);
system.out.println(len);
system.out.println(arrays.tostring(cbuf));
*/
stringbuilder sb = new stringbuilder();
while( (len=fr.read(cbuf)) != -1) {
sb.append(cbuf, 0, len);
}
system.out.println(sb);
} catch (ioexception e) {
e.printstacktrace();
}
// 【3】关闭输入流
try {
fr.close();
} catch (ioexception e) {
e.printstacktrace();
}
}
}
|
filewriter
filewriter 文件字符输出流,专门用于写入文本性文件。
通过filewriter构造方法构造的对象写入文件时编码是系统默认编码(win7:gbk)。
由于写入的是字符,字符在写入时一定要经过编码,所以filewriter内部有一个字符缓冲区用于存放待写入的字符,在调用flush时,filewriter把字符缓冲区的字符编码成字节然后写入目标文件。
package cn.sxt03.filewriter;
import java.io.file;
import java.io.filewriter;
import java.io.ioexception;
public class test01 {
public static void main(string[] args) {
file file = new file("d:\\javatest\\e.txt");
filewriter fw = null;
// 【1】建立输出流管道
try {
/*
* append:表示写入文件的方式
* true:追加 false:覆盖
*/
fw = new filewriter(file,false);
} catch (ioexception e) {
e.printstacktrace();
}
// 【2】写入
try {
// 写入一个字符
/*fw.write('中');
fw.write('国');*/
// 写入一个字符数组
/*char[] cbuf = {'中','国','\r','\n','a','b','c'};
fw.write(cbuf);*/
// 写入一个字符串
fw.write("中国abc");
} catch (ioexception e) {
e.printstacktrace();
}
// 【3】刷新
try {
fw.flush();
fw.close();
} catch (ioexception e) {
e.printstacktrace();
}
}
}
|
思考:如何读取一个utf8编码的文本文件。
转换流
所谓转换流可以把字节流转化成字符流的转换流
inputstreamreader 是字节流通向字符流的桥梁,它使用指定的字符集读取字节并将其解码为字符。
outputstreamwriter 是字符流通向字节流的桥梁,可使用指定的字符集将要写入流中的字符编码成字节
转换流工作原理
以utf8编码写入文件
package cn.sxt04.outputstramwriter;
import java.io.file;
import java.io.filenotfoundexception;
import java.io.fileoutputstream;
import java.io.filewriter;
import java.io.ioexception;
import java.io.outputstream;
import java.io.outputstreamwriter;
public class test01 {
public static void main(string[] args) throws filenotfoundexception,ioexception {
// 需求:写入 “中国abc” 以utf8编码写入
file file = new file("d:\\javatest\\g.txt");
fileoutputstream out = new fileoutputstream(file);
outputstreamwriter osw = new outputstreamwriter(out, "utf8");
osw.write('中');
char[] cbuf = {'国','a'};
osw.write(cbuf);
osw.write("中国abc");
osw.flush();
osw.close();
}
}
|
以utf8编码读取文件
public class test02 {
public static void main(string[] args) throws filenotfoundexception,ioexception {
// 需求:读取g.txt的内容
file file = new file("d:\\javatest\\g.txt");
fileinputstream fis = new fileinputstream(file);
inputstreamreader isr = new inputstreamreader(fis, "utf8");
/*int r = isr.read();
system.out.println((char)r);*/
char[] cbuf = new char[2];
int len = 0;
stringbuilder sb = new stringbuilder();
while( (len=isr.read(cbuf)) != -1) {
sb.append(cbuf,0,len);
}
system.out.println(sb);
}
}
|
思考:filereader和inputstreamreader的关系?
注意:win7手动创建utf8编码的文件(utf8-bom)
java程序写入的utf8文件不带bom
public class test03 {
public static void main(string[] args) throws filenotfoundexception,ioexception {
// 需求:读取win手动创建的utf8编码的h.txt的内容
file file = new file("d:\\javatest\\h.txt");
fileinputstream fis = new fileinputstream(file);
inputstreamreader isr = new inputstreamreader(fis, "utf8");
/*int r = isr.read();
system.out.println((char)r);*/
char[] cbuf = new char[2];
int len = 0;
stringbuilder sb = new stringbuilder();
while( (len=isr.read(cbuf)) != -1) {
sb.append(cbuf,0,len);
}
system.out.println(sb);
}
}
|
bufferedreader/bufferedwriter
filereader 提供的读取字符的方式效率稍低,如果高效读写字符的方式,可以使用bufferedreader/bufferedwriter。
bufferedreader
bufferedreader继承于reader,专门用于高效的处理文本,提供了readline方法用于一次读取一行文本。
把一首诗读取到控制台显示
package cn.sxt01.bufferedreader;
import java.io.bufferedreader;
import java.io.file;
import java.io.filenotfoundexception;
import java.io.filereader;
import java.io.ioexception;
public class test01 {
public static void main(string[] args) throws filenotfoundexception,ioexception {
file file = new file("d:\\javatest\\i.txt");
// ctrl+t:查看类继承关系
filereader reader = new filereader(file);
bufferedreader br = new bufferedreader(reader);
// 一次读取一行
/*
string str = br.readline();
str = br.readline();
str = br.readline();
str = br.readline();
str = br.readline();
system.out.println(str);
*/
string line;
while( (line=br.readline() ) != null) {
system.out.println(line);
}
br.close();
reader.close();
}
}
|
bufferedwriter
bufferedwriter 是writer的子类,专门用于高效的写入文本。提供了writer(string)和newline()方法,写入完成后调用flush()刷新缓冲区。
package cn.sxt01.bufferedwriter;
import java.io.bufferedreader;
import java.io.bufferedwriter;
import java.io.file;
import java.io.filenotfoundexception;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.io.outputstreamwriter;
public class test01 {
public static void main(string[] args) throws filenotfoundexception,ioexception {
// 需求:以utf8存入一首诗
file file = new file("d:\\javatest\\k.txt");
fileoutputstream out = new fileoutputstream(file);
outputstreamwriter osw = new outputstreamwriter(out, "utf8");
bufferedwriter bw = new bufferedwriter(osw);
bw.write("床前明月光,");
bw.newline();
bw.write("疑似地上霜。");
bw.newline();
bw.flush();
bw.close();
osw.close();
out.close();
}
}
|
标准输入输出流
标准输入流
java中用system.in表示标准输入流,属于inputstream字节输入流,标准输入设备(源):鼠标、键盘、手写板、麦克风、触摸屏等。
需求:从键盘输入一个字符并打印出来。
package cn.sxt02.inout;
import java.io.ioexception;
import java.io.inputstream;
public class test01 {
public static void main(string[] args) throws ioexception {
// 从控制台输入一个字符并打印
inputstream in = system.in;
// 【1】一次读取一个字节:(输入/数据源是键盘)
// int r = in.read();
// system.out.println((char)r);
// 【2】一次读取多个字节
byte[] buf = new byte[1024];
int len = 0;
len = in.read(buf);
// 默认控制台是gbk编码
string str = new string(buf, 0, len);
system.out.println(str);
}
}
|
标准输出流
java中用system.out表示标准输出流,属于printstream。标准的输出设备(源):显示器、显示屏
需求:从文件中读取文件内容并显示在标准输出设备上。
package cn.sxt02.inout;
import java.io.file;
import java.io.fileinputstream;
import java.io.ioexception;
import java.io.printstream;
public class test02 {
public static void main(string[] args) throws ioexception {
// 思考:为什么会乱码?
file file = new java.io.file("d:\\javatest\\i.txt");
fileinputstream fis = new fileinputstream(file);
// 标准输出流(gbk)
printstream ps = system.out;
int len = 0;
byte[] buf = new byte[2];
while( (len=fis.read(buf)) != -1 ) {
ps.write(buf, 0, len);
}
fis.close();
}
}
|
通过打印流直接写入文件
package cn.sxt02.inout;
import java.io.file;
import java.io.filenotfoundexception;
import java.io.fileoutputstream;
import java.io.printstream;
import java.io.unsupportedencodingexception;
public class test05 {
public static void main(string[] args) throws filenotfoundexception, unsupportedencodingexception {
// 通过打印流写入数据到一个文件(gbk)
/*file file = new file("d:\\javatest\\m.txt");
printstream ps = new printstream(file);
ps.write('a');
ps.write('b');
ps.close();*/
// 通过打印流写入一个utf8编码的文件
file file = new file("d:\\javatest\\m1.txt");
printstream ps = new printstream(file,"utf8");
ps.println("abc中国");
ps.close();
}
}
|
需要flush的printstream
package cn.sxt02.inout;
import java.io.file;
import java.io.filenotfoundexception;
import java.io.fileoutputstream;
import java.io.printstream;
import java.io.unsupportedencodingexception;
public class test04 {
public static void main(string[] args) {
printstream ps = system.out;
ps.write('a');
ps.write('b');
ps.flush();
}
}
|
printstream称为打印字节流,继承于outputstream,之前已经知道outputstream写入文件时不需要flush。printstream的目的地是显示器,在printstream内部有个缓冲区,专门用于缓冲待输出的字节,结合显示器工作原理,当需要显示器显示待打印的字节时,需要手动调用flush方法,此时显示器才解码后显示。
测试自动刷新(c)
package cn.sxt02.inout;
import java.io.file;
import java.io.filenotfoundexception;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.io.printstream;
import java.io.unsupportedencodingexception;
public class test04 {
public static void main(string[] args) throws ioexception {
printstream ps = system.out;
/*ps.write('a');
ps.write('b');*/
// 自动调用flush方法
/*byte[] buf = {'a','b'};
ps.write(buf);*/
// 自动调用flush方法
/*ps.write('a');
ps.write('b');
ps.write('\n');*/
// 自动调用flush方法
ps.println("ab");
// sps.flush();
}
}
|
字符打印流
printwriter 专门用于向自定目的地(显示器、文件、浏览器)中输出大量字符。继承于writer。除了write(char)/write(char[] cbuf),还提供了特有的print/println方法用于输出各位数据类型的字符。
public class test01 {
public static void main(string[] args) {
// 字符输出流
printwriter pw = new printwriter(system.out);
pw.println("hello");
pw.println("中国");
pw.flush();
pw.close();
}
}
|
序列化
把(程序)内存中的数据保存到硬盘的过程就是序列化,也称为数据持久化。
当我们把硬盘中的数据再次读取到内存中时,这个过程就是反序列化。
serializable接口
自定义类通过实现 java.io.serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化
需求:把一个对象序列化到d:\\javatest\\n1.txt
package cn.sxt04.serializable;
import java.io.file;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.io.objectoutputstream;
public class test01 {
public static void main(string[] args) throws ioexception {
user user = new user("001", "二狗", "123", 20);
/**
* 思路:
* 序列化:把对象个各个属性按照有规律的格式拼接成字符串,把字符串写入文件。
* 反序列化:把文件中的字符串读取为内存中,按序列化格式把字符串拆开得到很多属性值,然后初始化对象。
*/
// string info = user.getid()+"-"+user.getname()+"-"+user.getpwd()+"-"+user.getage();
// system.out.println(info);
file file = new file("d:\\javatest\\n1.sxt");
fileoutputstream out = new fileoutputstream(file);
objectoutputstream oos = new objectoutputstream(out);
oos.writeobject(user);
oos.close();
out.close();
}
}
|
需求:把刚在序列化的对象反序列化
package cn.sxt04.serializable;
import java.io.file;
import java.io.fileinputstream;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.io.objectinputstream;
import java.io.objectoutputstream;
public class test02 {
public static void main(string[] args) throws ioexception, classnotfoundexception {
file file = new file("d:\\javatest\\n1.sxt");
fileinputstream in = new fileinputstream(file);
objectinputstream ois = new objectinputstream(in);
user user = (user) ois.readobject();
system.out.println(user);
}
}
|
序列号
当程序在升级过程中对源代码进行修改,新代码对之前的序列化文件进行反序列化存在一个invalidclassexception异常
exception in thread "main" java.io.invalidclassexception: cn.sxt04.serializable.user; local class incompatible: stream classdesc serialversionuid = 4281284299154400224, local class serialversionuid = 8687762707351138232
at java.io.objectstreamclass.initnonproxy(objectstreamclass.java:687)
at java.io.objectinputstream.readnonproxydesc(objectinputstream.java:1876)
at java.io.objectinputstream.readclassdesc(objectinputstream.java:1745)
at java.io.objectinputstream.readordinaryobject(objectinputstream.java:2033)
at java.io.objectinputstream.readobject0(objectinputstream.java:1567)
at java.io.objectinputstream.readobject(objectinputstream.java:427)
at cn.sxt04.serializable.test02.main(test02.java:19)
|
如何解决?
始终保持本地类和序列化中文件的类的版本号一致。
[1]默认版本号,永远是1l
[2]手动根据类的信息(属性、方法)生成一串数字。
版本号工作原理
如果自定义类没有添加任何序列化版本号,jvm自动添加一个序列化版本号,当修改源代码时,jvm自动升级该序列化版本号,此时导致和序列化到本地文件的类版本号不一致,反序列化必将失败,抛出invalidclassexception异常。
transient 关键字
在序列化过程中,存在一些字段序列化没有意义或一些敏感自动不许序列化,可以使用transient修饰。
public class user implements serializable {
/**
*
*/
private static final long serialversionuid = 1l;
private string id;
private string name;
private transient string pwd;
private int age;
private string phone;
|
其他流
datainputstream/dataoutputstream