1、IO流,什么是IO流?
I :Input
O :Output
通过IO可以完成硬盘文件的读和写
2、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个字节)
3、java IO流这块有四大家族:
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
四大家族的首领都是抽象类
所有的流都实现了:
java.io.Closeable接口,都是可关闭的,都有close()方法
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费很多 资源
所有输出流都实现了:
java.io.Flushable接口,都是可刷新的,都有flush()方法。输出流在最终输出以后
一定要记得flush()刷新一下。这个刷新表示将通道/管道当中剩余为输出的数据强行输出完
刷新的作用就是清空管道
注意:如果没有flush()可能会导致丢失数据
注意:在java中只要“Stream”为结尾的都是字节流,以“Reader/Writer”为结尾的都是字符流
4、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)java.io.FileInputStream
1、文件字节输入流,万能的,任何类型的文件都可以采用这个流来读
2、字节的方式,完成输入的操作,完成读的操作(硬盘--->内存)
3、read方法进行读取,返回它的编码值(这种方法效率低,频繁的内存和硬盘之间的交互)
-
-
int
read()
-
4、将数据读取到数组当中,返回一个读取多少数据
-
-
int
read(byte[] b)
-
5、int available() :返回流当中剩余的没有读到的字节数量
6、long skip(long n):跳过几个字节不读
7、String 中有一个构造方法 传一个byte数组 String s = new String(bytes); 可以将获得的编 码值转换为字符串
public class FileInputStreamTest04 {
public static void main(String[] args) {
//这个就相当于和硬盘中的文件有了一个桥梁 通道
FileInputStream fis = null;
try {
fis= new FileInputStream("lianxi");
//创建一个byte数组
byte[] bytes = new byte[4];
//进行读取
/* while(true){
int readCount = fis.read(bytes);
if(readCount == -1){
break;
}
//将byte数组转换为String 用到String的构造方法
//read几个数量 就输出几个
System.out.print(new String(bytes,0,readCount));
}*/
//改良一下
int readCount = 0;
while ((readCount = fis.read(bytes)) != -1){
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();
}
}
}
}
}
(2)java.io.FileOutputStream
1、FileOutputStream:叫做输出流, 写
2、如果不想要每次运行的时候将文件里面的内容清空,在创建对象的时候,后面添加一个 true new FileOutputStream("lianxi2",true)
3、写的时候不要忘记刷新 刷新的作用是防止通道里面有遗漏的数据
public class FileOutputStreamTest01 {
public static void main(String[] args) {
//创建输出流 写
FileOutputStream fos = null;
try {
//lianxi2这个文件不存在,会自动创建的
//这种方式谨慎使用,这种方式会将原文件清空,然后再写入
//fos = new FileOutputStream("lianxi2");
//这种方式以追加的方式在文件末尾写入。不会清空原文件内容,在后面添加一个true
fos = new FileOutputStream("lianxi2",true);
//创建一个数据
byte[] bytes = {97,98,99,100,101,102,103};
//进行读取
//fos.write(bytes);
//fos.write(bytes,0,3);
//fos.write(bytes,0,3);
//字符串
String s = "我是一个中国人,我骄傲!!";
//将字符串转换为byte数组
byte[] bytes1 = s.getBytes();
//将bytes1写入到文件当中
fos.write(bytes1);
//写的时候不要忘记刷新 刷新的作用是防止通道里面有遗漏的数据
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)java.io.FileReader
文件字符输入流,只能读取普通文本
读取文本内容时,比较方便、便捷
创建数组的时候 创建char类型的
public class FileReaderTest01 {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("lianxi");
//创建数组
char[] chars = new char[4];
//进行循环读取数据
int readCount = 0;
while((readCount = fr.read(chars)) != -1){
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();
}
}
}
}
}
(4) java.io.FileWriter
文件字符输出流。只能输出普通文本
一定要注意:要是出现不报错,但是没有出现写的内容,有可能是没有刷新
public class FileWriterTest01 {
public static void main(String[] args) {
FileWriter fw = null;
try {
//后面加上true表示在文件后面添加东西,要是不加true的话,就会把文件清空在加
fw = new FileWriter("lianxi2",true);
//创建一个char数组 表示将要写的元素
char[] chars = {'我','是','崔','铮','!'};
//将这个char数组写进文件里面
fw.write(chars);
//记得要刷新
fw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fw == null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
(5)java.io.BufferedReader
1、带有缓冲区的字符输入流
2、使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组,自带缓冲
3、当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流
4、外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流
public class BufferedReaderTest01 {
public static void main(String[] args) throws Exception{
//当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流
//外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流
//像当前这个程序来说:FileReader就是一个节点流,BufferedReader就是一个包装流/处理流
FileReader reader = new FileReader("lianxi");
BufferedReader br = new BufferedReader(reader);
/*String s1 = br.readLine();
System.out.println(s1);
String s2 = br.readLine();
System.out.println(s2);
String s3 = br.readLine();
System.out.println(s3);*/
//使用while循环
String s = null;
while(( s = br.readLine()) != null){
System.out.println(s);
}
//关闭流 只需要关闭最外面的流 里面的流不需要关闭 因为外面的流close会调用里面流的close
br.close();
}
}
(6)java.io.BufferedWriter
public class BufferedWriterTest01 {
public static void main(String[] args) {
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter("BufferedWriterTest01",true));
//进行写
bw.write("你好啊");
//刷新
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (bw == null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
(7) java.io.DataOutputStream
这个流可以将数据连同数据的类型一并写入文件
注意:这个文件不是普通文件
public class DataOutputStreamTest01 {
public static void main(String[] args) {
DataOutputStream dataOutputStream = null;
try {
dataOutputStream = new DataOutputStream(new FileOutputStream("data",true));
int i = 1000;
boolean boo = true;
double dou = 3.14;
dataOutputStream.writeInt(i);
dataOutputStream.writeBoolean(boo);
dataOutputStream.writeDouble(dou);
//刷新
dataOutputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (dataOutputStream == null) {
try {
dataOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
(8) java.io.DataInputStream
1、DataOutputStream写的文件只能由DataInputStream去读,并且读的时候你需要提前知道写入的顺序
2、读的顺序需要和写的顺序一致。才可以正常取出数据
public class DataInputStreamTest01 {
public static void main(String[] args) {
DataInputStream dataInputStream = null;
try {
dataInputStream = new DataInputStream(new FileInputStream("data"));
System.out.println(dataInputStream.readInt());
System.out.println(dataInputStream.readBoolean());
System.out.println(dataInputStream.readDouble());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (dataInputStream == null) {
try {
dataInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
(9)PrintStream
1、日志工具
public class Logger {
/*
日志工具
*/
public void log(String ms){
//创建对象
PrintStream ps = null;
FileOutputStream fos = null;
try {
fos = new FileOutputStream("工作日志.txt",true);
ps = new PrintStream(fos);
//修改输出格式
System.setOut(ps);
//创建一个时间
Date data = new Date();
//修改日期格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss SSS");
String nowtime = sdf.format(data);
System.out.println(nowtime + "发生了" +ms);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
2、System.setOut(ps); 更改规则,不在控制台输出,输出到log文件当中
public class PrintStreamTest01 {
public static void main(String[] args) {
PrintStream ps = null;
try {
ps = new PrintStream(new FileOutputStream("log"));
//更改规则 不在控制台输出,输出到log文件当中
System.setOut(ps);
System.out.println("啊啊啊");
System.out.println("你好啊");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
(10)java.io.ObjectOutputStream与java.io.ObjectInputStream
1、NotSerializableException
对象不支持序列化
2、参与序列化和反序列化的对象,必须实现Serializable接口
public interface Serializable{
}
这个接口当中什么代码都没有,那么它起到一个什么作用呢?
起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个 类进行特殊待遇
Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类 自动生成一个序列化版本号
3、序列化版本有什么用呢?
Student; local class incompatible: stream classdesc serialVersionUID = 8444881787634386424, local class serialVersionUID = -3527981529236755747
java中采用什么机制来区分类的?
第一:首先通过类名进行对比,如果类名不一样,肯定不是同一个类
第二:如果类名一样,在怎么进行类的区别? 靠序列化版本号进行区分
对于java虚拟机来说,java虚拟机可以区分这两个类的,因为两个类都实现了Serializable接口
都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了(这是自动生成序列化版本的好处)
请思考?
自动生成序列化版本号有什么缺陷?
这种自动生成的序列化版本号一旦代码确定之后,不能进行后续的修改,因为只要修改,必定会重新
编译,此时生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类。(这样就不好了)
最终结论:
凡是一个类实现了Seriali接口,建议给该类提供一个固定不变的序列化
4、代码展现
Student对象
public class Student implements Serializable {
//java虚拟机看到Serializable接口之后,会主动生成一个序列化版本号
//这里没有手动写出来,java虚拟机会默认提供这个序列化版本号
//private transient int no; transient表示游离的 不参加序列化
//建议将序列化版本号手动的写出来。不建议自动生成
private static final long serialVersionUID = 1L;
private int no;
private transient String name;
private int age;
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 + '\'' +
'}';
}
}
进行序列化
public class ObjectOutputStreamTest02 {
public static void main(String[] args) {
//创建集合
List<Student> list = new ArrayList<>();
list.add(new Student(1,"lihua"));
list.add(new Student(2,"zhangsan"));
list.add(new Student(3,"lsii"));
//序列化
ObjectOutputStream objectOutputStream = null;
try {
objectOutputStream = new ObjectOutputStream(new FileOutputStream("ObjectOutputStreamTest02"));
objectOutputStream.writeObject(list);
//进行刷新
objectOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
进行反序列化
public class ObjectInputStreamTest02 {
public static void main(String[] args) {
//反序列化
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(new FileInputStream("ObjectOutputStreamTest02"));
List<Student> list = (List<Student>) objectInputStream.readObject();
for(Student l : list){
System.out.println(l);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (objectInputStream != null) {
try {
objectInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
5、File类
1、File类和四大家族没有什么关系,所以File类不能完成完成文 件的读和写
2、File对象代表什么?
文件和目录路径的抽象表示形式
一个File对象又可能对应的是目录,也可能是文件
File只是一个路径名的抽象表达形式
3、需要掌握File类中常用的方法:
boolean createNewFile() 若是没有这个文件就创建这个文件
boolean delete() 删除这个文件
boolean exists() 检测文件是否存在
String getAbsolutePath() 返回这个文件的绝对路径
String getParent() 返回父路径的文件名字
boolean isDirectory() 判断这个是不是目录
boolean isFile() 判断这个是不是文件
long lastModified() 返回此文件上次修改的时间
File[] listFiles() 表示当前目录有哪些文件 存储到数组当中
boolean mkdir() 创建目录
boolean mkdirs() 创建所有目录
4、创建目录
public class CopyDirectory {
public static void main(String[] args) {
//原路径
File file1 = new File("F:\\租号");
//目标路径
File file2 = new File("D:\\");
//调用方法
copy(file1,file2);
}
static void copy(File file1,File file2){
//先判断是否为文件
if(file1.isFile()){
copy2(file1,file2);
return;
}
//将当前文件夹中的文件和文件夹放到数组当中
File[] files = file1.listFiles();
//遍历数组
for(File f : files){
if(f.isDirectory()){
//原文件 文件夹路径
String s = f.getAbsolutePath();
//System.out.println(s);
//目标文件 文件夹路径
String m = (file2.getAbsolutePath().substring(0,3).endsWith("\\") ? file2.getAbsolutePath().substring(0,3) : file2.getAbsolutePath().substring(0,3) + "\\" ) + s.substring(3);
//将目标文件转为File文件
File mubiao = new File(m);
if(!mubiao.exists()){
mubiao.mkdirs();
}
}
//下面的file2不正确 传过去的参数不对 路径应该和f的一样只是变了前面
String s = (file2.getAbsolutePath().substring(0,3).endsWith("\\") ? file2.getAbsolutePath().substring(0,3) : file2.getAbsolutePath().substring(0,3) + "\\" ) + f.getAbsolutePath().substring(3);
file2 = new File(s);
//进行递归
copy(f, file2);
}
}
static void copy2(File yuanlujing,File mubiaolujing){
FileInputStream fis = null;
FileOutputStream fos = null;
//创建数组
byte[] bytes = new byte[1024 * 1024]; //1MB
//读取的数量
int count = 0 ;
try {
fis = new FileInputStream(yuanlujing);
fos = new FileOutputStream(mubiaolujing);
while((count = fis.read(bytes)) != -1){
fos.write(bytes);
}
//进行刷新
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();
}
}
}
}
}