Java内容梳理(18)API学习(6)I/O流
目录:
1、I/O流的分类
2、常用的I/O流
3、对象的序列化和反序列化
4、对象克隆
5、图片操作
1、I/O流的分类
(1)介绍
流说明了Java中读写数据的方式:
顺序读写数据:从左到右,从上到下依此读取数据
数据的流动方向:单向
参照物:程序内存
(2)分类
1.按照方向分:
输入流:InputStream父类/Reader父类
输出流:OutputStream父类/Writer父类
2.按照单位分:
字节流:InputStream父类/OutputStream父类-------以字节为单位进行操作
字符流:Reader父类/Writer父类-------以字符为单位进行操作
3.按照功能分:
节点流(功能流):提供核心基础读写数据能力的流
识别节点流:看类名前缀,若是一种明确的数据存放地方,那个流就是节点流。
过滤流(装备流):在节点流提供的基础能力之上进行功能加强
4、流的父类型
InputStream --- 字节输入流
OutputStream --- 字节输出流
Reader --- 字符输入流
Writer --- 字符输出流
2、常用的I/O流
(1)节点流
FileInputStream类:文件字节输入流,能实现按字节读取文件
构造方法:为欲指定的文件建立输入管道
FileInputStream( String filepath )
读入数据:read( byte[] buf ) 返回值是int,表示读入的字节个数。
每次从硬盘上最大读满buf数组个字节,返回值为-1时,表示没有读到数据,可以通过循环读完整个文件。
FileOutputStream类:文件字节输出流,能实现将字节写入文件(追加方式和覆盖方式两种)
构造方法:与指定文件建立输出管道(默认是替换模式)
FileOutputStream( File file )
FileOutputStream( String filePath )
按指定的模式与指定的文件建立输出管道(true为追加)
FileOutputStream( File file ,boolean isAppendModel)
FileOutputStream( String filePath ,boolean isAppendModel)
写入数据:
write( byte[] buf ):每次向指定的文件写出整个buf数组个字节
write( byte[] buf,int offset,int len ):从buf数组的offset位置开始,写出len长度的字节
注意:
1.当输出的目标文件不存在时,会自动将其创建
2.输出流默认采用替换方式,不会追加
3.当文件是文本文件时,可以使用字符输入流(FileReader),字符输出流(FileWriter),效率更高。
(2)缓冲过滤流
核心:与磁盘交互的过程是由JVM自动管理,我们都从缓冲区读写数据,一般在字符输入,输出流的基础上使用
BufferedInputStream类 和 BufferedOutputStream类 不常用
BufferedReader类: 带缓冲的字符输入流(将文件数据读取到内存)
构造方法:要依赖于另外一个流来创建
BufferedReader( Reader in ) 在指定的字符输入流上创建一个缓冲流(默认8k)
BufferedReader( Reader in ,int size) 在指定的字符输入流上创建一个size大小的缓冲流
读方法:
String readLine(): 读取一行数据(换行符或回车符表示一行读完)
过程:
BufferedWriter类:带缓冲的字符输出流(将内存数据写到文件中)
不常用,通常情况下我们会使用PrintWriter流替代这个类;
调用写方法时,并不直接向文件输出,而是写到流的缓冲区中;
当缓冲区满了时JVM才会将缓冲区中的数据一次性提交到文件中;
当我们调用close()方法时,JVM在关流之前会将缓冲区中的数据提交到文件;
不关流的情况下可以使用flush()方法,强制将缓冲区中的数据提交一次。
(3)转换流(也是一种过滤流,提升数据处理单位)
InputStreamReader类 作用:InputStream ---> Reader
OuputStreamReader类 不常用,被PrintWriter代替
如下:is是InputStream字节输入流,由InputStreamReader转换成了BufferedReader缓冲字符输入流
(4)特殊流
PrintWriter类,既可以当作节点流,也可以当作过滤流,还可以当作转化流来使用
举例:用PrintWriter去实现文本文件的复制
package stream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class PrintWriterStream {
public static void main(String[] args) {
File file = new File("src/a.txt");
try {
/**
* 先用字符缓冲输入流BufferedReader将文件和内存建立通道,需要用该流读取文件数据
* BufferedReader对象的创建,要依赖另一个字符流创建
* 构造方法有2个:BufferedReader( Reader in ); BufferedReader( Reader in ,int size)
*/
BufferedReader reader = new BufferedReader(
new InputStreamReader( new FileInputStream(file) )
);
//测试文件是否找到
System.out.println(file.exists());
//建立内存和b.txt文件的输出通道,需要用PrintWriter流写数据到b.txt中,若没有b.txt会自动新建一个
PrintWriter writer = new PrintWriter("src/b.txt");
String str = reader.readLine();
while(str != null) {
//注意writer.println(line);在输出到bbb之后,会多一个换行
writer.println(str); //用PrintWriter流写数据到b.txt中
str = reader.readLine();
}
writer.flush();//强制提交一次,确保提交
reader.close();//关流
writer.close();//关流
} catch ( IOException e) {
e.printStackTrace();
}
}
}
3、对象的序列化和反序列化
前提:
该对象的类型必须实现Serializable接口;否则报NotSerializableException异常
对象的序列化:(内存对象输出(写)到文件)
ObjectOutputStream类 writeObject方法 将内存中的对象输出
对象的反序列化:(读取文件数据,解析读取出来的对象)
ObjectInputStream类 readObject方法
transient关键字:
当前进行对象序列化,将不会对transient修饰的属性进行序列化
private static final long serialVersionUID = 1L;
反序列化是否成功主要依据serialVersionUID的值
举例:将自定义对象写到文件中,再从文件中读取出来,输出到控制台上
第一步:将对象从内存写到文件
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
//...省略get和set方法
}
package stream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Run {
public static void main(String[] args) {
Student stu1 = new Student("Jack",18);
Student stu2 = new Student("Rose",13);
try {
/*建立内存和文件的输出通道*/
ObjectOutputStream oos = new ObjectOutputStream(
//true表示追加,后一个写进文件的数据不会覆盖前一个
new FileOutputStream("src/c.txt",true)
);
/*将两个Student对象写(输出)到文件c.txt中去*/
oos.writeObject(stu1);
oos.writeObject(stu2);
/*关流*/
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
第二步:从文件中读取数据到内存,并输出到控制台上;为了代码完整,在上面的代码上添加
package stream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Run {
public static void main(String[] args) {
Student stu1 = new Student("Jack",18);
Student stu2 = new Student("Rose",13);
try {
/*建立内存和文件的输出通道*/
ObjectOutputStream oos = new ObjectOutputStream(
//true表示追加,后一个写进文件的数据不会覆盖前一个
new FileOutputStream("src/c.txt",true)
);
/*将两个Student对象写(输出)到文件c.txt中去*/
oos.writeObject(stu1);
oos.writeObject(stu2);
/*关流*/
oos.close();
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("src/c.txt")
);
Student s1 = (Student)ois.readObject();
System.out.println(s1);
Student s2 = (Student)ois.readObject();
System.out.println(s2);
ois.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
4、对象克隆
含义:
复制一个副本,并不是同一个对象
浅克隆:
能被浅克隆的前提是:
类型必须实现Clonable接口,没有任何抽象方法,称为标识接口
仅复制一层 Object中的clone方法是浅克隆
深克隆:
所有层次均复制一个副本
利用对象的序列化和反序列化来完成深克隆:
ByteArrayInputStream类
ByteArrayOutputStream类
来完成内存到内存,再到内存的一个深克隆的过程,把对象转化成Byte数组,再转化成对象
浅克隆举例:
package clone;
public class Resume implements Cloneable{
private String name;
private int age;
private WorkExperience work;
public Resume(String name, int age, WorkExperience work) {
super();
this.name = name;
this.age = age;
this.work = work;
}
public WorkExperience getWork() {
return work;
}
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;
}
public void setWork(WorkExperience work) {
this.work = work;
}
//Object中clone()方法是被声明为protected
//protected被修饰的方法只能在本类,同一个包中的类,或它的子类中使用;
public Resume Clone() {
try {
return (Resume) this.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "Resume [name=" + name + ", age=" + age + ", work=" + work + "]";
}
}
package clone;
public class WorkExperience {
private String timeArea;
private String company;
public WorkExperience(String timeArea, String company) {
super();
this.timeArea = timeArea;
this.company = company;
}
@Override
public String toString() {
return "WorkExperience [timeArea=" + timeArea + ", company=" + company + "]";
}
public String getTimeArea() {
return timeArea;
}
public void setTimeArea(String timeArea) {
this.timeArea = timeArea;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
}
package clone;
public class Run {
public static void main(String[] args) {
WorkExperience work1 = new WorkExperience("1999-2008", "中国航空公司");
Resume r1 = new Resume("小明", 12, work1) ;
System.out.println(r1);
Resume r2 =(Resume) r1.Clone();
System.out.println(r2);
System.out.println(r1 == r2);//显示false,表示克隆出来的对象和原来的对象不是一个
System.out.println(r1.getWork() == r2.getWork());//显示true,表示它们内部的WorkExperience对象其实是一个
//上面的测试表明,浅克隆其实只复制了一层对象,对象中的对象并没有克隆,String对象是特殊对象,也被复制了一份
//再测试:如修改work1的内容两个Resume的WorkExperience都会改变;但r2改变name不会影响r1
work1.setCompany("美国白宫");
r2.setName("小红");
System.out.println(r1);
System.out.println(r2);
}
}
举例:深克隆:利用对象序列化实现深克隆;A对象被序列化,而B是A下的属性也会被序列化,C在B类下,也会被序列化;序列化完成再反序列化成对象;赋值给新的A对象,就实现了深克隆。
5、图片操作
1、图片类: java.awt.Image类
用来表示图片数据 是一个抽象类,是所有图片类的父类
BufferedImage类 是Image类的实现类,封装了图片数据
2、图片读写: 主要用的是BufferedImage类
当程序需要使用一张图片时(或者需要使用到BufferedImage类对象时),我们可以利用ImageIO来读写图片。
ImageIO的用法(了解):
BufferedImage read( File file ): 加载指定的图片文件到内存(通常用来加载本地文件)
BufferedImage read( URL url ) 加载指定url位置上的图片文件到内存(通常用来加载网络图片)
BufferedImage read( InputStream input ) 将图片数据在网络上传输
write( File file )
write( OutputStream output )