简单来讲:
你编写了一个java程序,
你用这个java程序从你的电脑上读取文件,就是输入流;
你把你所写的java程序的数据传递给某个文件,就是输出流;
一、四大家族的首领:
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
四大家族的首领都是抽象类。(abstract class)
java.io包下需要掌握的流有16个:
文件专属:
java.io.FileInputStream(掌握)
java.io.FileOutputStream(掌握)
java.io.FileReader
java.io.FileWriter
转换流:(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流专属:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
数据流专属:
java.io.DataInputStream
java.io.DataOutputStream
标准输出流:
java.io.PrintWriter
java.io.PrintStream(掌握)
对象专属流:
java.io.ObjectInputStream(掌握)
java.io.ObjectOutputStream(掌握)
二、举例
1.FileInputStream
方法摘要 | 方法 | 作用 |
---|---|---|
abstract int | read() | 从输入流中读取数据的下一个字节 |
int | read(byte[] b) | 将输入流中读取一定数量 并将其存储在缓冲区数组 b 中。 |
int | read(byte[] b, int off, int len) | 将输入流中最多 len 个数据字节读入 byte 数组。返回总字节数. |
演示read[byte[] b]:
下面while()的初始版
fis=new FileInputStream("E:\\IO\\ceshi\\finally.text");
byte[] bytes=new byte[4];
/* while(true){
int readcount=fis.read(bytes);
if(readcount=-1){
break;
}
//把byte数组转化为字符串,读到几个转换几个
System.out.print(new String(bytes,0,readCount));
}
*/
最终版:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Finally {
public static void main(String[] args) {
FileInputStream fis =null;
try {
fis=new FileInputStream("E:\\IO\\ceshi\\finally.text");
//准备一个byte数组
byte[] bytes=new byte[4];
int readCount=0;
//read(bytes)方法是读到的字节个数为几个,当读到没有时返回-1
while ((readCount=fis.read(bytes))!=-1){
//把byte数组转化为字符串,读到几个转换几个
System.out.print(new String(bytes,0,readCount));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
abcdef
2.FileOutputStream
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class OutTest {
public static void main(String[] args) {
FileOutputStream fos=null;
try {
//这种方式谨慎使用,这种方式会先将原文件清空,然后重新写入
// fos=new FileOutputStream("E:\\IO\\ceshi\\finally.text");
//以追加的方式在文件末尾写入。不会清除原文件内容。
fos=new FileOutputStream("E:\\IO\\ceshi\\finally.text",true);
byte[] bytes ={97,98,99,100};
//将byte数组全部写出!
fos.write(bytes);//abcd
//将byte数组一部分写出!
fos.write(bytes,0,2);//再写出ab
//写完之后一定要刷新
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.所有文件的复制
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class OutTest {
public static void main(String[] args) {
FileInputStream fis=null;
FileOutputStream fos=null;
try {
fis=new FileInputStream("E:\\IO\\ceshi\\resource.txt");
fos=new FileOutputStream("E:\\IO\\ceshi2\\position.txt");
//最核心的:一边读,一边写
byte [] bytes = new byte[1024*1024];//1MB(最多可拷贝1MB)
int readCount=0;
while ((readCount=fis.read(bytes))!=-1){
fos.write(bytes,0,readCount);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//分开try,不要一起try
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4.java.io.BufferedReader和转换流
import java.io.*;
/*
带有缓冲区的字符输入流
使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组,自带缓冲。
*/
public class BufferedReaderTest {
public static void main(String[] args) throws IOException {
/*
这个构造方法只能传一个字符流。不能传字节流//通过转换流转换(InputStreamReader将字节流转换为字符流)
FileInputStream是节点流,BufferedReader是包装流。
*/
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("E:\\IO\\ceshi\\finally.text")));
//br.readLine读一行
String Line = null;
while ((Line = br.readLine()) != null) {
System.out.println(Line);
}
//关闭流
//对于包装流来说,只需要关闭最外层流就行,里面的节点流自动关闭(可以看源码)
br.close();
}
}
5.DataOutputStream和DataInputStream
/*java.io.DataOutputStream:数据专属的流。
这个流可以将数据连同数据的类型一并写入文件。
注意:这个文件不是普通文本文档。(这个文件使用记事本打不开。)
*/
/*
DataInputStream:数据字节输入流。
DataOutputStream写的文件,只能使用DataInputStream去读。
并且读的时候你需要提前知道写入的顺序。读的顺序需要和写的顺序一致。才可以正常取出数据。
*/
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Data {
public static void main(String[] args) throws IOException {
//创建数据专属的字节输出流
//Dataoutputstream(Outputstream) Outputstream是抽象类无法实例化,我们可以new 他的子类
DataOutputStream dos=new DataOutputStream(new FileOutputStream("E:\\IO\\ceshi\\finally.text"));
//写数据
int i=300;
boolean sex=false;
char c='a';
//写
dos.writeInt(i);
dos.writeBoolean(sex);
dos.writeChar(c);
//刷新
dos.flush();
//关闭最外层
dos.close();
}
}
6.标准输出流
标准输出流是不需要关闭流的
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Logger {
public static void log(String msg){
try {
//指向一个日志文件
PrintStream out=new PrintStream(new FileOutputStream("log.txt",true));
//改变输出方向
System.setOut(out);
//日期当前时间
Date nowTime = new Date();
SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss: SSS");
String strTime =sdf.format(nowTime);
System.out.println(strTime+":"+msg);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
public class LogTest {
public static void main(String[] args) {
//测试工具类
Logger.log("调用了System类的gc方法,建议启动垃圾回收");
Logger.log("调用了UserService的doSome()方法");
Logger.log("用户尝试登陆,验证失败");
Logger.log("我非常喜欢这个记录工具哦");
}
}
2021-11-10 17:12:04: 764:调用了System类的gc方法,建议启动垃圾回收
2021-11-10 17:12:04: 797:调用了UserService的doSome()方法
2021-11-10 17:12:04: 801:用户尝试登陆,验证失败
2021-11-10 17:12:04: 801:我非常喜欢这个记录工具哦
7.序列化和反序列化
1:参与序列化和反序列化的对象,必须实现Serializable接口。
注意:通过源代码发现,Serializable接口只是一个标志接口:
public interface Serializable {
}
2
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。
这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。
3:
transient关键字表示游离的,不参与序列化。
private transient String name; // name不参与序列化操作
package com.bjpowernode.java.io;
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 1L;// java虚拟机识别一个类的时候先通过类名,如果类名一致,再通过序列化版本号。
private int no;
private String name;
public Student() {
}
public Student(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
package com.bjpowernode.java.io;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamTest01 {
public static void main(String[] args)throws Exception {
//创建java对象
Student s =new Student(111,"zhangsan");
//序列化
ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("students"));
//序列化对象
oos.writeObject(s);
//刷新
oos.flush();
//关闭
oos.close();
}
}
一次性序列多个对象(参考)
/*
一次序列化多个对象呢?
可以,可以将对象放到集合当中,序列化集合。
提示:
参与序列化的ArrayList集合以及集合中的元素User都需要实现 java.io.Serializable接口。
*/
public class ObjectOutputStreamTest02 {
public static void main(String[] args) throws Exception{
List<User> userList = new ArrayList<>();
userList.add(new User(1,"zhangsan"));
userList.add(new User(2, "lisi"));
userList.add(new User(3, "wangwu"));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users"));
// 序列化一个集合,这个集合对象中放了很多其他对象。
oos.writeObject(userList);
oos.flush();
oos.close();
}
}
8.IO流和Properties的使用
userinfo.properties
username=admin
password=123
import java.io.FileReader;
import java.util.Properties;
public class IoPropertiesTest01 {
public static void main(String[] args) throws Exception {
//新建一个输入流对象
FileReader reader =new FileReader("src/userinfo.properties");
//新建一个Map集合
Properties pro=new Properties();
//调用properties对象的load方法将文件中的数据加载到Map集合中
pro.load(reader);
//通过key来获取value
String username=pro.getProperty("username");
System.out.println(username);
String password=pro.getProperty("password");
System.out.println(password);
}
}
9.File类常用方法
Files.exists():检测⽂件路径是否存在。
Files.createFile():创建⽂件。
Files.createDirectory():创建⽂件夹。
Files.delete():删除⼀个⽂件或⽬录。
Files.copy():复制⽂件。
Files.move():移动⽂件。
Files.size():查看⽂件个数。
Files.read():读取⽂件。
Files.write():写⼊⽂件。
三、面试题
1.序列化和反序列化
- 序列化: 将对象转换成二进制字节流的过程
- 反序列化:将在序列化过程中所生成的二进制字节流转换成对象的过程
序列化可以将对象的状态写在流里进行网络传输,或者保存到文件、数据库等系统里,并在需要的时候把该流读取出来重新构造成一个相同的对象。