概述
计算机内存与硬盘之间时常有交互行为,由于参考对象是内存,所以我们把数据从硬盘到内存传输的行为称为输入,也叫写;相反地,数据从内存到硬盘传输的行为称为输出,也叫读。由于数据传输的动作是像流水一样,因此传输的数据也被称为流,于是就有了输入流和输出流的概念。
IO流有2种分类,按照流的方向可分为输入流和输出流;按读取数据的方式可分为字节流和字符流,其中字节流是万能的,能传输各种格式的数据,而字符流只能传输纯文本文件,即能用记事本打开的文件。
Java的IO流有4个抽象类,分别是InputStream-OutputStream、Reader-Writer,以Stream结尾的文件称为字节流文件,以Reader或Writer结尾的文件被称为字符流文件。值得注意的是,只有数据传输时内存和硬盘的通道才会被打开,所以数据传输完毕后所有流都有关闭流的动作,因此所有流都实现了Closeable接口。同时,当内存向硬盘传输数据完成以后,是需要刷新的,因为这个刷新的动作表示将管道中剩余未输出的数据强行清空,所以所有的输出流都会实现Flushable接口,如果没有实现此接口,则有可能数据在传输过程中会丢失。
注:①路径中,\和/是等效的 ②IDEA目录中,默认的路径是当前工程下的根目录
先以FileInputstream类为例,介绍以下几个方法:
字节流
package IOStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest03 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
//创建文件字节输出流对象
//此文件建在project的直接目录下
fis = new FileInputStream("mytest1.txt");
int readCount;
byte[] bytes = new byte[1024];
while ((readCount = fis.read(bytes))!= -1){
System.out.println(readCount);//19
//以byte[1024]大小的数组去读,然后数据留在了数组中,再将数组中的数据转成String类型
//String类的构造方法,String(char[]),字符数组转成字符串
System.out.println(new String(bytes,0,readCount));//heLLo,?? world...
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileInputStream类的其他2个方法:
package IOStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* FileInputStream类的其他常用方法
* ①int available():其作用是避免了while()循环,不需要判断是不是读到头了。这个方法不适合大文件
* ②long skip(long n):跳过几个字节不读
*/
public class FileInputStream04 {
public static void main(String[] args) {
testAvailMethod();
testSkipMethod();
}
private static void testAvailMethod() {
FileInputStream fis = null;
try {
fis = new FileInputStream("mytest1.txt");
System.out.println("刚开始读的字节数:" + fis.available()); //刚开始读的字节数:14
int readCount = fis.read();
System.out.println("读完后的字节数:" + fis.available()); //读完后的字节数:13
System.out.println(new String(new byte[fis.available()])); //
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void testSkipMethod(){
FileInputStream fis = null;
File file = new File("mytest1.txt");
try {
fis = new FileInputStream(file);
fis.skip(3);
System.out.println(new String(new byte[fis.available() - 3]));//
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileOutputStream类用法类似,比如我们将刚才从文件读取的内容写到另外一个文件中:
package IOStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamTest01 {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
//以追加方式写入,英文字符和汉字。
//注意:不追加的话每写一次就会清空之前的内容
fos = new FileOutputStream("myoutfile.txt",true);
byte[] bytes = {101,28,23,78,69};
fos.write(bytes,0,bytes.length);
String s = "今天天气不太好!!!";
byte[] bytes1 = s.getBytes();
fos.write(bytes1); //文件内容:eNE今天天气不太好!!!eNE今天天气不太好!!!
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节流复制文件
文件复制:由于C、D盘并不能直接交互,需要以内存为中介进行操作,所有需要边读边写,具体代码如下:
package IOStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyFileTest {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("C:\\Users\\Simon\\Desktop\\List\\something convinient\\面试day726.md");
fos = new FileOutputStream("D:\\面试day726.md");
byte[] bytes = new byte[1024*1024];
int readCount = 0;
while ((readCount = fis.read(bytes,0,readCount))!= -1){
fos.write(bytes,0,bytes.length);
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
字符流
package IOStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("D:\\DataValue\\project\\MyData\\Data1\\src\\IOStream\\iotest1.txt");
char[] chars =new char[20];
int readCount = 0;
while ((readCount = fr.read(chars))!=-1){
//hello,wo rld
//sdaj
//kfsdghsdkbk我就偶返回数据拷贝
//到方便快速
//撒娇不看电视
System.out.println(new String(chars, 0, readCount));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package IOStream;
import java.io.FileWriter;
import java.io.IOException;
public class WriterTest {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("D:\\Data\\temp.txt");
char[] c = {'我','是','人'};
fw.write(c);
fw.write(c,0,2);
fw.write("mytest.only you");//文件内容:我是人我是mytest.only you
fw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package IOStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyFileTest2 {
public static void main(String[] args) {
FileReader in =null;
FileWriter out = null;
try {
in = new FileReader("D:\\DataValue\\project\\MyData\\Data1\\src\\IOStream\\iotest1.txt");
out = new FileWriter("D:\\DataValue\\project\\MyData\\Data1\\iotest1.txt");
//文件输出:
// hello,wo rld
//sdajkfsdghsdkbk我就偶返回数据拷贝到方便快速
//撒娇不看电视
char[] chars = new char[1024*1024];
int readCount = 0;
while ((readCount=in.read(chars))!=-1){
out.write(chars,0,readCount);
}
out.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
缓冲流
package IOStream;
import java.io.BufferedReader;
import java.io.FileReader;
/**
*带缓冲区的字符流:
* ①由于带缓冲区,所以不需要再指定字节数组或者字符数组
* ②包装流、节点流
*本流优点是,一读就读一行
*/
public class BufferReaderTest{
public static void main(String[] args) throws Exception{
FileReader reader = new FileReader("D:\\DataValue\\project\\MyData\\Data1\\iotest1.txt");
//这里,FileReader称为节点流,外部的BufferedReader称为包装流
//这里的参数要传一个Reader类的对象,但Reader类是抽象的,所以可以以FileReader传入
BufferedReader br = new BufferedReader(reader);
String s = null;
while ((s=br.readLine())!=null){
//这里并不自带换行符
System.out.println(s);
//读取文件内容如下:
// hello,wo rld
//sdajkfsdghsdkbk我就偶返回数据拷贝到方便快速
//撒娇不看电视
}
//对于包装流来说,只需要关闭最外层流就行,里面的节点流会自动关闭。
br.close();
}
}
//注意:文件字符流可以直接作为参数传入给缓冲流,但是字节流不行,它还必须经过转换流转成字符流
//作为参数传入,所以会经历2次转换。
package IOStream;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.OutputStreamWriter;
public class BufferWriterTest {
public static void main(String[] args) throws Exception{
BufferedWriter bw = new BufferedWriter(new FileWriter("tetsBufferWriterTest.txt",true));
BufferedWriter bw2 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream
("testBufferWriterTest2.txt",true)));
bw.write("hello"+"\n");
bw.write(100);
bw.write("test0000001",0,3);
//文件内容如下:
// hello
//dteshello
//dtes
bw2.write("my test for the .java file");
//文件内容输出如下:
// my test for the .java file
bw.flush();
bw.close();
bw2.flush();
bw2.close();
}
}
读入和写出:以字节流为节点流
package sumup.iostream;
import java.io.*;
//以字节流作为缓冲流的节点流
//运行成功
public class IOStreamTest06 {
public static void main(String[] args) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream("myfile5")));
String s = null;
while ((s = br.readLine())!=null){
System.out.println(s);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("myfile7",true)));
bw.write("m买买买");
char[] chars = {'z','o','o'};
bw.write(chars);
bw.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
读入和写出:以字符流为节点流
package sumup.iostream;
import java.io.*;
//运行成功
//用缓冲流读入和写出一个java文件,先以字符流作为节点流
public class IOStreamTest05 {
public static void main(String[] args) {
FileReader fr =null;
BufferedWriter bw = null;
try {
fr = new FileReader("printLog");
BufferedReader br = new BufferedReader(fr);
String s = null;
while ((s=br.readLine())!=null){
System.out.println(s);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
FileWriter fw = new FileWriter("myfile5");
bw = new BufferedWriter(fw);
bw.write("这是一个秘密,请保密");
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fr != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
数据流
package IOStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
public class DataOutputStreamTest {
public static void main(String[] args) throws Exception{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("dataOutputStream.txt"));
//写数据
byte b = 100;
short s = 30;
float f = 3.0f;
double d = 3.0;
int i = 3;
char c ='c';
//写:把数据及其类型写入文件,但由于不是普通文件,所以打开乱码
dos.writeByte(b);
dos.writeChar(c);
dos.writeInt(i);
dos.writeFloat(f);
dos.flush();
dos.close();
}
}
package IOStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
/**
* 按什么规则写,就按什么规则读
*/
public class DataInputStreamTest {
public static void main(String[] args) throws Exception{
DataInputStream dis = new DataInputStream(new FileInputStream("dataOutputStream.txt"));
//开始读
byte b = dis.readByte();
char c = dis.readChar();
float f = dis.readFloat();
int i = dis.readInt();
//b:100 c:c f:4.2E-45 i:1077936128
System.out.println("b:" + b + "\tc:" + c + "\tf:" + f + "\ti:" + i);
dis.close();
}
}
标准打印流
package IOStream;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamTest {
public static void main(String[] args) throws Exception{
PrintStream ps = System.out;
ps.println("zs");
ps.println("ls");
ps.println("ww");
//不再输出到控制台,而是输出到printLog文件中
PrintStream pl = new PrintStream(new FileOutputStream("printLog"));
System.setOut(pl);
System.out.println("hello,world");
System.out.println("hello,kitty");
}
}
//一个案例
package IOStream;
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)throws Exception{
//指向一个日志文件
PrintStream out = new PrintStream(new FileOutputStream("loggerTest.txt", true));
//改变输出方向
System.setOut(out);
//日期当前时间
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");
String strTime = sdf.format(date);
System.out.println(strTime + ":" + msg);
}
}
package IOStream;
public class LoggerTest {
public static void main(String[] args) {
try {
Logger.log("zs做了一道奥数题");
Logger.log("ls打了一桶水");
Logger.log("ww完了一把游戏");
} catch (Exception e) {
e.printStackTrace();
}
}
}
新增文档内容:
2021/10/12 10:36:37 864:zs做了一道奥数题
2021/10/12 10:36:37 890:ls打了一桶水
2021/10/12 10:36:37 890:ww完了一把游戏
关于File类
package IOStream;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* File的直接父类是Object,不属于四大家族,所以File类是不能完成文件读和写的
* File对象是文件和目录的路径名的抽象表示形式
* Fiel类的常用方法
*/
public class FileTest {
public static void main(String[] args) throws Exception{
File file = new File("D:/file");
System.out.println(file.exists()); //false
//以文件形式新建
/* if (!(file.exists())){
file.createNewFile();
}*/
//以目录形式新建,可创建多重目录--->mkdirs
if (!(file.exists())){
file.mkdir();
}
//获取父路径
System.out.println(file.getParent()); //D:\
//获取父路径的绝对路径
System.out.println(file.getParentFile().getAbsolutePath()); //D:\
//获取文件名
System.out.println(file.getName()); //file
System.out.println(file.isDirectory()); //ture
System.out.println(file.isFile()); //false
//获取对象的最后一次修改时间
long lm = file.lastModified();
Date date = new Date(lm);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy - MM - dd HH:mm:ss SSS");
System.out.println(sdf.format(date)); //2021 - 10 - 12 10:54:31 487
//获取文件大小
System.out.println(file.length()); //0
//File中的listFiles方法
File[] files = file.listFiles();
for (File file1 : files) {
System.out.println(file1.getName());
//输出内容如下(是正确的):
// 1
//2
//3.txt
//4.txt
}
}
}
对象流-序列化和反序列化
package IOStream.bean;
import java.io.Serializable;
//Serializable标志性接口
public class Student implements Serializable {
private int no;
private String name;
//若 private transient String name;则最终输出:
//Student{no=1111, name='null'}
@Override
public String toString() {
return "Student{" +
"no=" + no +
", 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;
}
public Student(int no, String name) {
this.no = no;
this.name = name;
}
public Student() {
}
}
package IOStream;
import IOStream.bean.Student;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 对象的序列化和反序列化
* 参与对象序列化和反序列的对象,必须实现Serializable接口
* 仅有接口没有方法的称为标志性接口,作用是什么?
* JVM看到这个接口后,会为这个类自动生成一个序列化版本号
* 序列化版本号有什么用?每序列化一次就会生成一个序列化版本号,JVM怎么判断一个类有没有被改动?
*①全类名是否变化 ②若①相同,比较2个序列化版本号是否一样,一样则表示为同一个类
*
*/
public class ObjectOutputStreamTest {
public static void main(String[] args) throws Exception{
//创建java对象
Student s = new Student(1111, "zhangsan");
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("studentTest"));
//序列化对象
oos.writeObject(s);
oos.flush();
oos.close();
}
}
/**
*注意:如果改动了代码,然后没有序列化,直接点了反序列化操作,会报以下异常:
*Exception in thread "main" java.io.InvalidClassException: IOStream.bean.Student; local class incompatible: stream classdesc serialVersionUID = 9082654830569486289, local class serialVersionUID = 3103404913107206989
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at IOStream.ObjectInputStreamTest.main(ObjectInputStreamTest.java:9)
Process finished with exit code 1
*/
//自动生成序列化版本号有什么坏处?只要修改就必须重新编译。
//所以凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号
//比如ArrayList的:private static final long serialVersionUID = 8683452581122892189L;
package IOStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ObjectInputStreamTest {
public static void main(String[] args) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("studentTest"));
Object o = ois.readObject();
System.out.println(o); //Student{no=1111, name='zhangsan'}
ois.close();
}
}
IO和Properties的联合使用
在idea写一个Properties文件,以key-value形式存储在硬盘中,我们创建一个userinfo.txt文件,内容写入:
username=admin
password=123
########################################这个#号表示注释##########################
package IOStream;
import java.io.FileReader;
import java.util.Properties;
/**
* Properties是一个Map集合,key和value都是String类型
* 想将userinfo.txt文件中的数据加载到Properties对象中
*
* 这是一个非常好的设计理念:
* 以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取,将来只需要修改文件的内容,
* java代码不需要改动,不需要重新编译,服务器也不需要重启
*
* 类似以上机制的这种文件称为配置文件,当配置文件的内容格式是:
* key1=value;
* key2=value;
* 时,我们把这种配置文件称为属性配置文件
*
* java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的。
* .properties是专门存放在属性配置文件内容的一个类
*/
public class IOPropertiesTest {
public static void main(String[] args) throws Exception{
//创建文件字符输入流对象
FileReader fr = new FileReader("D:\\IDEA_Data\\Data5\\test\\src\\userinfo.txt");
//创建Map对象
Properties properties = new Properties();
//Map的load方法,将字符流对象加载进来
properties.load(fr);
//读取
String username = properties.getProperty("username");
String password = properties.getProperty("password");
//username:admin,password:123
System.out.println("username:" + username + ",password:" + password);
}
}