1.IO流,什么是IO?
I : Input
O : Output
通过IO可以完成硬盘文件的读和写。
一.IO流的分类?
有多种分类方式:
一种方式是按照流的方向进行分类:
以内存作为参照物,
往内存中去,叫做输入(Input)。或者叫做读(Read)。
从内存中出来,叫做输出(Output)。或者叫做写(Write)。
另一种方式是按照读取数据方式不同进行分类:
有的流是按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。
这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件等…
假设文件file1.txt,采用字节流的话是这样读的:
a中国bc张三fe
第一次读:一个字节,正好读到’a’
第二次读:一个字节,正好读到’中’字符的一半。
第三次读:一个字节,正好读到’中’字符的另外一半。
有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取
普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件。只能读取纯
文本文件,连word文件都无法读取。
假设文件file1.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读:'a’字符('a’字符在windows系统中占用1个字节。)
第二次读:'中’字符('中’字符在windows系统中占用2个字节。)
综上所述:流的分类
输入流、输出流
字节流、字符流
最后一行这个输出流画错了 FileOutStream
通过这个图方便理解
重点理解
String filePath = "e:\\news1.txt"; File file = new File(filePath); 我们这里可以这么理解就是 file对象就是我们内存中的对象 也就是抽象文件和我们这个真实硬盘上的txt文件进行相互绑定
FileInputStream file=null; file=new FileInputStream("D:\\Study\\Java\\Test\\wtf.txt"); 我们可以不用创建抽象文件 直接使用字符串路径名进行查找硬盘上的文件 这里就是通过流对象去操作txt文件对象 也就是将磁盘的文件放入流放对象中也就是放入管道中
同理fileoutputStream则是将管道中的文件放入要放入的地方这里指的是demo.txtbyte 我们所谓的byte数据或byte数组就是中间图示的缓冲区
流对象的read方法会一直往下读取 就是好像默认有个next
序列化 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(".vscode\\oos.class")); 就是操作流对象就是对管道进行操作 将管道输出流中的文件放入以序列化格式二进制字节码硬盘中
反序列化就是将硬盘的管道对象操作 ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(".vscode\\oos.class")); //通过返回序列化 ,从objectInputStream 中将对象序列化数据读取到一个对象 Object obj=objectInputStream.readObject();
二.java IO流这块有四大家族:
四大家族的首领:
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
四大家族的首领都是抽象类。(abstract class)
所有的流都实现了:
java.io.Closeable接口,都是可关闭的,都有close()方法。
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,
不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭。
所有的输出流都实现了:
java.io.Flushable接口,都是可刷新的,都有flush()方法。
养成一个好习惯,输出流在最终输出之后,一定要记得flush()
刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据
强行输出完(清空管道!)刷新的作用就是清空管道。
注意:如果没有flush()可能会导致丢失数据。
注意:在java中只要“类名”以Stream结尾的都是字节流。以“Reader/Writer”结尾的都是字符流。
三.FileInputStream
主要方法介绍:
void close()
关闭此输入流并释放与该流关联的所有系统资源。
abstract int read()
从输入流读取下一个数据字节。
int read(byte[] b)
public int read(byte[] b)
throws IOException
从输入流读取一些字节数,并将它们存储到缓冲区b 。 实际读取的字节数作为整数返回。 该方法阻塞直到输入数据可用,检测到文件结束或抛出异常。
区数组 b 中。
public void write(byte[] b)
throws IOException
将b.length字节从指定的字节数组写入此输出流。 write(b)的一般合约是应该具有与电话write(b, 0, b.length)完全相同的效果。
public int read(byte[] b,
int off,
int len)
throws IOException
从输入流读取len字节的数据到一个字节数组。 尝试读取多达len个字节,但可以读取较小的数字。 实际读取的字节数作为整数返回。
该方法阻塞直到输入数据可用,检测到文件结束或抛出异常。
1.初步
java. io FileInputstream:
1、文件字节输入流,万能的,任何类型的文件部可以采用这个流来读。
2、宇节的方式,完成输入的操作,完成读的操作(硬盘-)内存
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest01 {
public static void main(String[] args) {
FileInputStream file=null;
try {
//创建文件字节输入流对象
file=new FileInputStream("D:\\Study\\Java\\Test\\wtf.txt");
/* while (true){
int data=file.read();
if(data == -1)
break;
System.out.println(data);
}*/
//改进while
int data=0;
while ((data=file.read())!=-1){
System.out.println(data);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//在finally语句块确保流一定关闭
if (file != null) {//避免空指针异常
//关闭流的前提是:流不是空.流是null的时候没必要关闭
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
97
98
99
32(空格)
100
101
102
-1
2.往byte数组中读
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest01 {
public static void main(String[] args) {
FileInputStream file=null;
try {
file=new FileInputStream("D:\\Study\\Java\\Test\\wtf.txt");
//开始读,采用byte数组,一次取多个字节。最多读取"数组.length"个字节。
byte[] bytes=new byte[4];//准备一个个长度的byte数组,一次最多读取4个字。
//这个方法的返回值是:读取到的字节数量。(不是字节本身)
int readcount= file.read(bytes);
System.out.println(readcount);//第一次读到了4个字节。
//将字节数组全部转换成字符串
//System.out.println(new String(bytes));// abcd
//不应该全部部转换,应该是读取了多少个字节,转换多少个。
System.out.println(new String(bytes,0,readcount));
readcount= file.read(bytes);//第二次只能读取到2个字节
System.out.println(readcount);//2
//将字节数组全部转换成字符串
//System.out.println(new String(bytes));// efcd
//不应该全部部转换,应该是读取了多少个字节,转换多少个。
System.out.println(new String(bytes,0,readcount));
readcount= file.read(bytes);//1个字节都没到返回-1
System.out.println(readcount);//-1
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (file != null) {
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4
abcd
2
ef
-1
3.最终版
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest01 {
public static void main(String[] args) {
FileInputStream file=null;
try {
file=new FileInputStream("D:\\Study\\Java\\Test\\wtf.txt");
byte[] bytes=new byte[4];//准备一个个长度的byte数组,一次最多读取4个字。
/* while (true){
int readcount=file.read(bytes);
if (readcount==-1){
break;
}
System.out.print(new String(bytes,0,readcount));
}*/
//极致优化
int readcount=0;
while (( readcount=file.read(bytes)) !=-1){
System.out.print(new String(bytes,0,readcount));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (file != null) {
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
abcdef
4.FileInputStream的其他方法
FileInputstream类的其它常用方法:
int available():返回流当中剩余的没有到的字节数量
Long skip( Long n):跳过几个字节不读。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest05 {
public static void main(String[] args) {
FileInputStream fis=null;
try {
fis=new FileInputStream("D:\\Study\\Java\\Test\\wtf.txt");
System.out.println("总字节数量"+fis.available());
/* 读1个字节
int readByte = fis.read();
还剩下可以峡的字节数量是:5
System.out.println("剩下多少个字节没有读:"+fis. available());
/这个方法有什么用?*/
int data=fis.read();
System.out.println("剩下多少个字节没读"+fis.available());
//这种方式不太适合太大的文件,因为byte[]数组不能太大
byte[] bytes=new byte[fis.available()];
//不需要循环了
//直接测一次就行了
int readCount=fis.read(bytes);
System.out.println(new String(bytes));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis == null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
总字节数量6
剩下多少个字节没读5
bcdef
5.FileOutStream
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamTest01 {
public static void main(String[] args) {
FileOutputStream files=null;
try {
/*
myfile.文件不存在的时候会自动新建!
这种方式谨算使用,这种方式会先将原文件清空,然后重新写入。
files new FileoutputStream ("myfile")*/
//这种方式在末尾添加
//以追加的方式在文件末尾写入,不会清空原文件内容
files=new FileOutputStream("myfile",true);
//开始写
byte[] bytes={97,98,99,100};
//将byte数组全部写出
files.write(bytes);//abcd
//将byte数组一部分写出
files.write(bytes,0,2);//再写出ab
//字符串转换为byte数组
String s="我是一整个";
byte[] bs=s.getBytes();
files.write(bs);
//写出之后,最后一定要刷新
files.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (files != null) {
try {
files.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
abcdab我是一整个
6.文件拷贝
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Copy01 {
public static void main(String[] args) {
FileInputStream fies=null;
FileOutputStream foS =null;
try {
fies=new FileInputStream("D:\\Study\\Java\\Test\\wtf.txt");
foS=new FileOutputStream("D:\\Study\\Java\\wtf.txt");
byte[] bytes=new byte[1024*1024];
int readCount=0;
while ((readCount = fies.read(bytes))!=-1){
foS.write(bytes,0,readCount);
}
foS.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//分开try,不要在一起try
//一起try的时候,其中一个出现异常,可能影响到另一个流的关系
if (foS == null) {
try {
foS.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fies == null) {
try {
fies.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
三.FileReader和FileWriter
区别,以字符char[] 代替byte[],以一字符代替一字节
i
mport java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*FileReader
文件字符输入流,只能读取普通文本。
读取文本内容时,比较方便,快捷。*/
public class FileReaderTest {
public static void main(String[] args) {
FileReader reader=null;
try {
reader =new FileReader("myfile");
/* char[] chars=new char[4];
int readCount=0;
while((readCount=reader.read(chars))!=-1){
System.out.print(new String(chars,0,readCount));
}*/
char[] chars=new char[4];
reader.read(chars);
for (char c:chars){
System.out.print(c);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader == null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
abcd
import java.io.FileWriter;
import java.io.IOException;
public class FileWrite {
public static void main(String[] args) {
FileWriter out=null;
try {
out=new FileWriter("fils");
char[] chars={'我','是','中','国','人'};
out.write(chars);
out.write(chars,1,2);
out.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (out == null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
我是中国人是中
四.节点流和包装流
import java.io.*;
public class BufferedReaderTest02 {
public static void main(String[] args) throws IOException {
/*
//字节流
FileInputStream file=new FileInputStream("myfile");
通过转换流转换( InputstreamReader将字节流转换成字符流。)
in是节点流。 reader是包装流。
InputStreamReader in=new InputStreamReader(file);
这个构造方法只能传一个字符流。不能传字节流。
reader是点流。b是包装流。
BufferedReader br=new BufferedReader(in);*/
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("myfile")));
String s=null;
while ((s=br.readLine())!=null){
System.out.print(s);
}
br.close();
}
}
abcdab我是一整个abcdab我是一整个abcdab我是一整个assdaasg
五.带有缓冲区的字符流
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderTest01 {
public static void main(String[] args) throws IOException {
FileReader reader=new FileReader("myfile");
//当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流。
//外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流。
//像当前这个程序来说: Filereader就是一个节点流。 BufferedReader就是包装流/处理流。
BufferedReader br=new BufferedReader(reader);
/* String firstLine=br.readLine();
System.out.println(firstLine);
String secondLine=br.readLine();
System.out.println(secondLine);*/
String s=null;
while((s=br.readLine())!=null){
System.out.print(s);
}
}
}
六.数据流
import java.io.DataOutputStream;
import java.io.FileOutputStream;
/*java.io. DataOutputstream:数据专属的流。
这个流可以数据连同数据的类型一并写入文件。
注意:这个文件不是普通文本文档。(这个女件使用记事本打不开。)*/
public class DateOutputStreamTest {
public static void main(String[] args) throws Exception {
DataOutputStream dos=new DataOutputStream(new FileOutputStream("data"));
byte b=100;
short s=200;
int i=1;
long l=400L;
float f=3.0f;
double d=3.14;
boolean sex=false;
char c='f';
dos.writeByte(b);
dos.writeShort(s);
dos.writeInt(i);
dos.writeLong(l);
dos.writeFloat(f);
dos.writeDouble(d);
dos.writeBoolean(sex);
dos.writeChar(c);
//刷新
dos.flush();
dos.close();
}
}
import java.io.DataInputStream;
import java.io.FileInputStream;
/*
DataInputstream:数据字节输入流。
DataOutputstream写的文件,只能使用 DataInputstream去读。
并且的时候你需要提前知道写入的顺序。
读的顺序需要和写的顺序一致。才可以正常取出数据。*/
public class DateInputStreamTest {
public static void main(String[] args)throws Exception {
DataInputStream dis=new DataInputStream(new FileInputStream("data"));
byte b=dis.readByte();
short s=dis.readShort();
int i=dis.readInt();
long l=dis.readLong();
float f=dis.readFloat();
double d=dis.readDouble();
boolean sex=dis.readBoolean();
char c=dis.readChar();
System.out.println(b);
System.out.println(s);
System.out.println(i);
System.out.println(l);
System.out.println(f);
System.out.println(d);
System.out.println(sex);
System.out.println(c);
dis.close();
}
}
200
1
400
3.0
3.14
false
f
七.标准输出流
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamTest {
public static void main(String[] args) throws FileNotFoundException {
//联合起来写
System.out.println("hello world");
//分开写
PrintStream ps=System.out;
ps.println("hello zhangsan");
ps.println("hello lisi");
ps.println("hello wangwu");
/*标准输出流不需要手动 close()关闭
可以改交标准输出流的输出方向吗?可以
/这些是之前System类使用过的方法和属性。
System gc();
System.currentTimeMillis();
Printstream ps2= System.out;
System.exit(0);
System.arraycopy(....)*/
//标准输出流不再指向控制台,指向"log"文件
PrintStream printStream=new PrintStream(new FileOutputStream("log"));
//修改输出方向,将输出方向修改到"log"文件
System.setOut(printStream);
printStream.println("fuck");
printStream.println("shift");
printStream.println("I");
printStream.println("can");
printStream.println("win");
}
}
fuck
shift
I
can
win
2.File类的理解
File
1、File类和四大家族没有关系,所以File类不能完成文件的读和写
2、File对象代表什么?
文件和日录路径名的抽泉表示形式。
C:\ Drivers这是一个File对象
C:\Drivers\Lan\Realtek\ Readme.txt也是File对象
个让File对有可能对应的是目录,也可能是文件。
File只是一个路径名的抽象表示形式
3、需要掌握File类中常用的方法
File常用方法Test
import java.io.File;
import java.io.IOException;
public class FileTest01 {
public static void main(String[] args) throws IOException {
//创建file对象
File f1=new File("D:\\Study\\Java\\file");
//判断是否存在!
System.out.println(f1.exists());
//如果D:\Study\Java\file不存在,则以文件的形式创建出来
/* if (!f1.exists()){
//以文件形式新建
f1.createNewFile();
}*/
//以目录形式创建
if(!f1.exists()){
f1.mkdir();
}
File f3=new File("D:\\Study\\Java\\wtf.txt");
//获取文件父路径
String parentParent=f3.getParent();
System.out.println(parentParent);
//获取绝对路径
File parentFile=f3.getParentFile();
System.out.println("获取绝对路径"+parentFile.getAbsolutePath());
}
}
true
D:\Study\Java
获取绝对路径D:\Study\Java
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileTest02 {
public static void main(String[] args) {
File f1=new File("D:\\Study\\Java\\wtf.txt");
//获取文件名
System.out.println(f1.getName());
//判断是否是一个目录
System.out.println(f1.isFile());
//判断是否是一个文件
System.out.println(f1.isDirectory());
//获取文件最后一次修改时间
long haoMiao=f1.lastModified(); //这个毫秒是从1970年到现在的总毫秒数
//将毫秒数转换成日期
Date time=new Date(haoMiao);
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime=sdf.format(time);
System.out.println(strTime);
//获取文件大小
System.out.println(f1.length());
}
}
wtf.txt
true
false
2021-02-24 15:49:08 313
6
import java.io.File;
public class FileTest03 {
public static void main(String[] args) {
//File[] listFiles()
//获取当前录下所有的子文件
File f=new File("D:\\Study\\Java");
File[] files=f.listFiles();
for (File file:files){
System.out.println(file.getAbsolutePath());
}
}
}
D:\Study\Java\02-JavaSE零基础每日复习与笔记
D:\Study\Java\07-JavaSE进阶每日复习与笔记
D:\Study\Java\08-JavaSE进阶讲义
D:\Study\Java\09-JavaSE进阶每章课堂画图
对象流
ObjectInputStream
ObjectOutputStream
重点:
参与序列化的类型必须实现java.io.Serializable接口。
并且建议将序列化版本号手动的写出来。
private static final long serialVersionUID = 1L;
Student类
package com.bjpowernode.java.bean;
import java.io.Serializable;
public class Student implements Serializable {
// IDEA工具自动生成序列化版本号。
//private static final long serialVersionUID = -7998917368642754840L;
// Java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号。
// 这里没有手动写出来,java虚拟机会默认提供这个序列化版本号。
// 建议将序列化版本号手动的写出来。不建议自动生成
private static final long serialVersionUID = 1L; // java虚拟机识别一个类的时候先通过类名,如果类名一致,再通过序列化版本号。
private int no;
//private String name;
// 过了很久,Student这个类源代码改动了。
// 源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件。
// 并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变。
private int age;
private String email;
private String address;
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 + '\'' +
'}';
}*/
@Override
public String toString() {
return "Student{" +
"no=" + no +
", age=" + age +
", email='" + email + '\'' +
", address='" + address + '\'' +
'}';
}
}
序列化
package com.bjpowernode.java.io;
import com.bjpowernode.java.bean.Student;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
/*
1、java.io.NotSerializableException:
Student对象不支持序列化!!!!
2、参与序列化和反序列化的对象,必须实现Serializable接口。
3、注意:通过源代码发现,Serializable接口只是一个标志接口:
public interface Serializable {
}
这个接口当中什么代码都没有。
那么它起到一个什么作用呢?
起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。
Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成
一个序列化版本号。
4、序列化版本号有什么用呢?
java.io.InvalidClassException:
com.bjpowernode.java.bean.Student;
local class incompatible:
stream classdesc serialVersionUID = -684255398724514298(十年后),
local class serialVersionUID = -3463447116624555755(十年前)
java语言中是采用什么机制来区分类的?
第一:首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
第二:如果类名一样,再怎么进行类的区别?靠序列化版本号进行区分。
小鹏编写了一个类:com.bjpowernode.java.bean.Student implements Serializable
胡浪编写了一个类:com.bjpowernode.java.bean.Student implements Serializable
不同的人编写了同一个类,但“这两个类确实不是同一个类”。这个时候序列化版本就起上作用了。
对于java虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口,
都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了。(这是自动生成序列化版本号的好处)
请思考?
这种自动生成序列化版本号有什么缺陷?
这种自动生成的序列化版本号缺点是:一旦代码确定之后,不能进行后续的修改,
因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java
虚拟机会认为这是一个全新的类。(这样就不好了!)
最终结论:
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。
这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。
*/
public class ObjectOutputStreamTest01 {
public static void main(String[] args) throws Exception{
// 创建java对象
Student s = new Student(1111, "zhangsan");
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));
// 序列化对象
oos.writeObject(s);
// 刷新
oos.flush();
// 关闭
oos.close();
}
}
反序列化
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
/*
反序列化
*/
public class ObjectInputStreamTest01 {
public static void main(String[] args) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));
// 开始反序列化,读
Object obj = ois.readObject();
// 反序列化回来是一个学生对象,所以会调用学生对象的toString方法。
System.out.println(obj);
ois.close();
}
}
list集合在java276 277教程中有提到