本章主要详细讲解IO流
文件/目录的基本操作
- 文章 Java 常用类及其常用方法的使用中的File类有详细讲解
IO流基本知识
输入输出流原理
- 从程序的角度来讲,从 计算机读取到的原始数据肯定都是010101这种形式的,一个字节一个字节地往外读,如果你觉得这样的方法不合适,你再在这根管道的外面再包一层比较强大的管道,这个管道可以帮你把 010101转换成字符串。这样你使用程序读取数据时读到的就不再是010101这种形式的数据了,而 是一些可以看得懂的字符串了。

输入输出流分类
- 按数据流的方向不同可以分为输入流和输出流
- 按照处理数据单位不同可以分为字节流和字符流
- 字节流:最原始的一个流,读出来的数据就是010101这种最底层的数据表示形式,只不过它是按照字节来读的
- 字符流:字符流是一个字符一个字符地往外读取数据
- 按照功能不同可以分为节点流和处理流
- 节点流:可以从一个特定的数据源(节点)读取数据
- 处理流:“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更强大的读写能力

节点流类型

处理流类型

FileInputStream
FileInputStream的基本方法
//读取一个字节并以整数的形式返回(0~255) //如果返回-1就说明已经到了输入流的末尾
int read() throws IOException
//读取一系列字节并存储到一个数组buffer //返回实际读取的字节数,如果读取前已到输入流的末尾,则返回-1
int read(byte[] buffer) throws IOException
//读取length个字节
//并存储到一个字节数组buffer,从length位置开始 //返回实际读取的字节数,如果读取前以到输入流的末尾返回-1.
int read(byte[] buffer,int offset,int length) throws IOException
//关闭流释放内存资源
void close() throws IOException
//跳过n个字节不读,返回实际跳过的字节数
long skip(long n) throws IOException
FileInputStream的案例
"/Users/test/iotest.txt"
Hello World
public class FileInputTest {
public static void main(String[] args) {
int count =0;
byte[] buf=new byte[8];
FileInputStream ips=null;
try{
ips=new FileInputStream("/Users/test/iotest.txt");
//使用FileInputStream流来读取有中文的内容时,读出来的是乱码,因为使用
//InputStream流里面的read()方法读取内容时是一个字节一个字节地读取的,而一个汉字是占用两个字节的,所以读取出来的汉字无法正确显示。
//int read() throws IOException
//ReadNoPra(count,ips);
//输出:
//Hello World
//int read(byte[] buffer) throws IOException
//ReadBuf(buf,count,ips);
//输出:
//Hello Wo
//rld
//int read(byte[] buffer,int offset,int length) throws IOException
//long skip(long n) throws IOException
ReadBufLen(buf,count,ips);
//输出:
//1
//ell
} catch (Exception e) {
System.out.println("读取失败");
} finally {
try {
if (ips!=null){
ips.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
static void ReadBufLen(byte[] buf,int count,FileInputStream ips) throws IOException {
//跳过一个字节不读
System.out.println(ips.skip(1));
if ((count=ips.read(buf,0,3))!=-1) {
System.out.println(new String(buf, 0, count));
}
}
static void ReadBuf(byte[] buf,int count,FileInputStream ips) throws IOException {
while ((count=ips.read(buf))!=-1){
System.out.println(new String(buf,0,count));
}
}
static void ReadNoPra(int count,FileInputStream ips) throws IOException {
while ((count=ips.read())!=-1){
System.out.printf("%c",(char) count);
}
}
}
FileOutputStream
FileOutputStream的基本方法
//向输出流中写入一个字节数据,该字节数据为参数b的低8位
void write(int b) throws IOException
//将一个字节类型的数组中的数据写入输出流
void write(byte[] b) throws IOException
//将一个字节类型的数组中的从指定位置(off)开始的len个字节写入到输出流
void write(byte[] b,int off,int len) throws IOException
//关闭流释放内存资源
void close() throws IOException
//将输出流中缓冲的数据全部写出到目的地 void flush() throws IOException
FileOutputStream的基本方法的案例
"/Users/test/iotest.txt"
Hello
public class FileOutputTest {
public static void main(String[] args) {
int count =0;
byte[] buf=new byte[8];
FileOutputStream ops=null;
try{
//若没有这个文件,会自动创建文件并写入数据
//如果添加true,那新写入的数据不会覆盖原先数据而是追加在后面
ops=new FileOutputStream("/Users/test/iotest.txt",true);
//void write(int b) throws IOException
ops.write(' ');
//void write(byte[] b) throws IOException
String str="World";
ops.write(str.getBytes());
//void write(byte[] b,int off,int len) throws IOException
ops.write(' ');
String str2="Love China";
ops.write(str2.getBytes(),0,4);
} catch (Exception e) {
System.out.println("写入失败");
} finally {
try {
if (ops!=null){
ops.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//输出结果:
"/Users/test/iotest.txt"
Hello World Love
文件拷贝
"/Users/test"
boji.jpeg
public class FileCopy {
public static void main(String[] args) {
int count=0;
FileInputStream ips=null;
FileOutputStream ops=null;
byte[] buf=new byte[1024];
try{
ips=new FileInputStream("/Users/test/boji.jpeg");
ops=new FileOutputStream("/Users/test/boji2.jpeg");
while ((count=ips.read(buf))!=-1){
ops.write(buf,0,count);
}
System.out.println("success");
} catch (Exception e) {
System.out.println("fail");
} finally {
try{
if (ips!=null){
ips.close();
}
if (ops!=null){
ops.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//输出结果:
"/Users/test"
boji.jpeg
boji2.jpeg
FileReader的案例
"/Users/test/iotest.txt"
Hello World
你好,世界
public class FileReaderTest {
public static void main(String[] args) {
ReadOne();
System.out.println();
System.out.println("-------");
ReadMore();
}
//一个字符为单位读
static void ReadOne(){
int count=0;
FileReader fr=null;
try{
fr=new FileReader("/Users/test/iotest.txt");
while ((count=fr.read())!=-1){
System.out.print((char)count);
}
} catch (Exception e) {
System.out.println("读取失败");
}finally {
try {
if (fr!=null){
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//字符数组为单位读
static void ReadMore(){
int count=0;
FileReader fr=null;
char[] chars=new char[8];
try{
fr=new FileReader("/Users/test/iotest.txt");
while ((count=fr.read(chars))!=-1){
System.out.print(new String(chars,0,count));
}
} catch (Exception e) {
System.out.println("读取失败");
}finally {
try {
if (fr!=null){
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//输出结果:
Hello World
你好,世界
-------
Hello World
你好,世界
FileWriter的案例
- 注意,FileWriter使用后,必须关闭(close)或刷新(flush),否则写入不到指定文件。
"/Users/test/iotest.txt"
Hello
public class FileWriterTest {
public static void main(String[] args) {
int count=0;
FileWriter fw=null;
char[] chars={'a','b','c'};
try{
//后面没有true,表示覆盖
fw=new FileWriter("/Users/test/iotest.txt");
//写入单个字符
fw.write('H');
//写入字符数组
fw.write(chars);
//写入指定字符数组的指定长度
fw.write("你好呀".toCharArray(),0,2);
//写入字符串
fw.write("很高兴认识你");
//写入字符串指定部分
fw.write("杭州北京",0,2);
} catch (Exception e) {
System.out.println("写入失败");
}finally {
try {
if (fw!=null){
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//输出结果:
"/Users/test/iotest.txt"
Habc你好很高兴认识你杭州
处理流讲解
缓冲流
BufferedInputStream/BufferedOutputStream的案例
- 可以通过上述流操作二进制文件[图片、声音、视频、doc、pdf]
"/Users/test/iotest.txt"
public class BufInputTest {
public static void main(String[] args) {
FileInputStream ips=null;
FileOutputStream ops=null;
BufferedInputStream bis=null;
BufferedOutputStream bos=null;
try{
ops= new FileOutputStream("/Users/test/iotest.txt");
bos=new BufferedOutputStream(ops);
bos.write("Hello World".getBytes());
bos.flush();
ips= new FileInputStream("/Users/test/iotest.txt");
bis=new BufferedInputStream(ips);
int count=0;
System.out.println((char) bis.read());
//public void mark(int readlimit)
//readlimit 参数告知此输入流在标记位置无效之前允许读取的字节数
bis.mark(3);
for (int i=0;i<=10&&(count=bis.read())!=-1;i++){
System.out.print((char) count);
}
System.out.println();
bis.reset();
for (int i=0;i<=10&&(count=bis.read())!=-1;i++){
System.out.print((char) count);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (bos!=null){
bos.close();
}
if (bis!=null){
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//输出结果:
H
ello World
ello World
BufferedReader/BufferedWriter的案例
- 如果通过上述流操作二进制文件[图片、声音、视频、doc、pdf],可能会造成文件损坏
"/Users/test/iotest.txt"
public class BufReaderTest {
public static void main(String[] args) {
BufferedWriter bw=null;
BufferedReader br=null;
try{
bw=new BufferedWriter(new FileWriter("/Users/test/iotest.txt"));
String s=null;
Random random = new Random();
for (int i=0;i<3;i++){
//随机生成数字
s=String.valueOf(random.nextInt(10));
bw.write(s);
bw.newLine();
}
bw.flush();
br=new BufferedReader(new FileReader("/Users/test/iotest.txt"));
while ((s=br.readLine())!=null){
System.out.println(s);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (bw!=null){
bw.close();
}
if (br!=null){
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//输出结果:
7
4
9
对象流
序列化和反序列化
- 序列化就是在保存数据时,保存数据的值和数据类型
- 反序列化就是在恢复数据时,恢复数据的值和数据类型
- 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
- Serializable 这是一个标记接口,里面没有方法,推荐使用
- Externalizable 该接口有方法需要实现
ObjectOutputStream的案例
public class ObjOutTest {
public static void main(String[] args) {
ObjectOutputStream oos=null;
try{
oos=new ObjectOutputStream(new FileOutputStream("/Users/test/iotest.txt"));
//序列化数据到iotest.txt
oos.writeInt(100);//自动装箱 int -> Integer (实现了Serializable)
oos.writeBoolean(true);
oos.writeChar('a');
oos.writeDouble(3.14);
oos.writeUTF("Love Java");
//保存一个dog对象
Dog dog=new Dog(6,"小花");
oos.writeObject(dog);
} catch (Exception e) {
e.printStackTrace();
} finally {
try{
if (oos!=null){
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class Dog implements Serializable{
private int age;
private String name;
public Dog(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Dog{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
ObjectInputStream的案例
public class ObjInputTest {
public static void main(String[] args) {
ObjectInputStream ois=null;
try{
ois=new ObjectInputStream(new FileInputStream("/Users/test/iotest.txt"));
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
Dog dog= (Dog)ois.readObject();
System.out.println(dog);
System.out.println(dog.getClass());
} catch (Exception e) {
e.printStackTrace();
} finally {
try{
if (ois!=null){
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//输出结果:
100
true
a
3.14
Love Java
Dog{age=6, name='小花'}
class javabase.javaio.Dog
对象流处理事项
- 读取顺序要一致
- 要求序列化或反序列化对象,需要实现Serializable
- 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
- 序列化对象时,默认将里面所有的属性都进行序列化,但出了static或transient修饰的成员
- 序列化对象时,要求里面属性的类型也需要实现序列化接口
- 序列化具备可继承性,也就是说如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化
数据流
DataInputStream/DataOutputStream的案例
- DataOutputStream数据输出流允许应用程序将基本Java数据类型写到基础输出流中
- DataInputStream数据输入流允许应用程序以机器无关的方式从底层输入流中读取基本的Java类型
//例子1
public class DataStreamTest {
public static void main(String[] args) throws IOException {
ByteArrayOutputStream boutput = new ByteArrayOutputStream();
DataOutputStream dop=new DataOutputStream(boutput);
Random random=new Random();
dop.writeByte('a');
dop.writeByte('b');
dop.writeByte('c');
dop.writeByte('d');
byte[] b= boutput.toByteArray();
System.out.println("Print the content");
for(int x= 0;x<b.length;x++) {
//打印字符
System.out.print((char)b[x] + " ");
}
System.out.println();
int count;
ByteArrayInputStream binput = new ByteArrayInputStream(b);
DataInputStream dip=new DataInputStream(binput);
System.out.println("Converting characters to Upper case " );
while((count= dip.read())!= -1) {
System.out.print(Character.toUpperCase((char)count)+" ");
}
}
}
//输出结果:
Print the content
a b c d
Converting characters to Upper case
A B C D
//例子2
public class DataStreamTest {
public static void main(String[] args) throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream("/Users/test/iotest.txt"));
dos.writeUTF("Love Java");
dos.writeInt(3);
dos.writeBoolean(true);
dos.writeShort((short)123);
dos.writeLong((long)456);
dos.writeDouble(3.14);
DataInputStream dis = new DataInputStream(new FileInputStream("/Users/test/iotest.txt"));
System.out.println(dis.readUTF());
System.out.println(dis.readInt());
System.out.println(dis.readBoolean());
System.out.println(dis.readShort());
System.out.println(dis.readLong());
System.out.println(dis.readDouble());
dis.close();
dos.close();
}
}
//输出结果:
Love Java
3
true
123
456
3.14
转换流
- 转换流非常的有用,它可以把一个字节流转换成一个字符流
InputStreamReader的案例
"/Users/test/iotest.txt"
Hello World
public class ISReaderTest {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("/Users/test/iotest.txt"),"utf-8"));
String s=br.readLine();
System.out.println(s);
br.close();
}
}
//输出结果:
Hello World
OutputStreamWriter的案例
"/Users/test/iotest.txt"
public class OSWriterTest {
public static void main(String[] args) throws IOException {
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("/Users/test/iotest.txt"),"utf-8"));
bw.write("你好,世界!");
bw.close();
}
}
//输出结果:
"/Users/test/iotest.txt"
你好,世界!
标准输入输出流
System.in/System.out的案例
public class InAndOutTest {
public static void main(String[] args) {
//System.in 编译类型 InputStream
//System.in 运行类型 BufferedInputStream
System.out.println(System.in.getClass());
//System.out 编译类型 PrintStream
//System.out 运行类型 PrintStream
System.out.println(System.out.getClass());
}
}
//输出结果:
class java.io.BufferedInputStream
class java.io.PrintStream
打印流
- 打印流只有输出流没有输入流
PrintStream的案例
"/Users/test/iotest.txt"
public class PrintStreamTest {
public static void main(String[] args) throws IOException {
PrintStream pr=new PrintStream(System.out);
pr.println("Love Java");
pr.write("Love Java".getBytes());
pr.close();
System.setOut(new PrintStream("/Users/test/iotest.txt"));
System.out.println("这里没有东西");
}
}
//输出结果:
Love Java
Love Java
"/Users/test/iotest.txt"
这里没有东西
PrintWriter的案例
"/Users/test/iotest.txt"
public class PrintWriterTest {
public static void main(String[] args) throws FileNotFoundException {
PrintWriter pw=new PrintWriter("/Users/test/iotest.txt");
pw.println("你好,世界!");
pw.write("你好,世界!");
pw.close();
}
}
//输出结果:
"/Users/test/iotest.txt"
你好,世界!
你好,世界!
Hi, welcome to JasperのBlog!