I:Input
O:Output
通过IO可以完成硬盘文件的读和写
1.IO流的分类
(1)按流的方向分类(以内存作为参照物)
往内存中去,叫输入(Input)或叫做读(Read);
从内存中出来,叫做输出(Output)或叫做写(Write)
(2)按照数据读取方式的不同进行分类
①有的流是按字节的方式读取数据,一次读取1个字节byte,等同于一次读取了8个二进制位,这种流是万能的,什么类型的文件都可以读取,包括:文本文件、图片、声音文件、视频文件等。
②有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件。只能读取普通文本文件,连world文件都无法读取。
终上所述,IO流的分类有:输入流、输出流、字符流、字节流
2.Java中所有的流都在java.io.*
3.Java中IO流有4大类,分别是:
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
注意!在java中只要“类名”以Stream结尾的都是字节流,以“Reader/Writer”结尾的都是字符流
(1)所有的流都实现了:java.io.Closeable接口,都是可关闭的,都有close()方法
注意!流毕竟是一个管道,这是硬盘和内存之间的通道,用完之后需要关闭,不然会耗费很多资源,关闭流的前提是流不是空
(2)所有的输出流都实现了java.io.Flushable接口,都是可刷新的,都有flush()方法
注意!输出流在最终输出之后,一定要记得flush()刷新一下,这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道),刷新的作用就是清空管道,如果没有flush()可能会导致丢失数据。
4.java.io包下需要掌握的流有16个
文件专属流:
转换流:(将字节流转换成字符流)
缓冲流:
数据流:
对象流:
标准输出流:
1.文件字节输入流,万能的,任何类型的文件都可以采用这个流来读
2.字节的方式完成输入的操作,完成读的操作(硬盘----->内存)
在IDEA中默认的当前路径是Project的根
3.FileInputStream类的其他常用方法:
int available() //返回流当中剩余的没有读到的字节数量
long skip(long n) //跳过几个字节不读
1.使用FileInputStream+FileOutputStream完成文件的拷贝的过程应该是一边读一边写,使用以上的字节流拷贝文件时,文件类型随意,万能的。什么样的文件都能拷贝。
2.输入输出流的异常分开try,不要一起try,一起try的时候,其中一个出现异常可能会影响到另一个流的关闭
代码示例:
package cn.itcast_01;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* 复制文本文件
*
* 数据源:从哪里来
* a.txt --------读取数据-----FileInputStream
*
* 目的地:到哪里去
* b.txt----------写入数据-----FileOutputStream
*
*
*/
public class 字节流复制文本文件案例 {
public static void main(String[] args) throws IOException {
//封装数据源
FileInputStream fis = new FileInputStream("a.txt");
//封目的地
FileOutputStream fos = new FileOutputStream("b.x.txt");
int by = 0;
while((by = fis.read()) != -1) {
fos.write(by);
}
//释放资源(先关谁都可以)
fos.close();
fis.close();
}
}
1.带有缓冲区的字符输入流,使用这个流时不需要自定义char数组,或者说不需要定义byte数组,自带缓冲。
2.当一个流的构造方法中需要一个流时,这个被传进来的流叫做节点流;负责外部包装的流叫做包装流。
例:
FileReader reader = new FileReader(“文件名”);
BufferedReader br = new BufferedReader(reader);
//像上述两个语句中,FileReader就是一个节点流,BufferedReader就是一个包装流/处理流
//对于包装流来说,只需要关闭最外层流即可,里面的节点流会自动关闭
BufferedWriter:带有缓冲的字节输出流
OutputStreamWriter:转换流
数据专属的流
这个流可以将数据连同数据的类型一起写入文件,但是要注意这个文件不是普通文本文档,使用记事本是打不开的
数据字节输入流
DataOutput写的文件只能使用DataInputStream去读,并且读的时候还需要提前知道写入的顺序,读的顺序和写的顺序一致才可以正常取出数据
标准的字节输出流,默认输出到控制台
1.标准输出流不需要手动close()关闭
2.可以改变标准输出流动的输出方向
代码示例:
PrintStream printStream = new PrintStream(new FiledOutputStream("文件名"));//标准输出流不再指向控制台,指向“log”文件
System.out.println(printStream);//再输出
System.out.println("......");//再输出
1.File类和IO的四大类没有关系,所以File类不能完成文件的读和写
2.File类文件代表文件和目录路径名的抽象表现形式,一个File对象有可能对应的是目录,也有可能对应的是文件。File只是一个路径名的抽象表现形式
3.File类中常用方法
(1)创建一个File对象
File f1 = new File(“文件路径”);
(2)判断是否存在
代码示例:
System.out.println(f1.exit());
//如果文件不存在,则以文件的形式创建出来
if(!f1.exit()){
f1.createNewFile();//以文件形式新建
}
//如果文件不存在,则以目录的形式创建出来
if(!f1.exit()){
f1.mkdir();//以目录形式新建
}
//可以创建多重目录
File f2 = new File("D:/a/b/c/d/e/f");
if(!f2.exit()){
f2.mkdirs();//以多重目录的形式新建
}
(3)获取文件的父路径
代码示例:
File f3 = new File("文件路径");
String parentPath = f3.getParent();
System.out.println("parentPath");//输出文件的父路径
File parentFile = f3.getParentFile();
System.out.println("获取绝对路径" + parentFile.getAbsoluPath());//获取文件的绝对路径
(4)获取文件名
File f1 = new File(“文件路径”);
System.out.println(“文件名:” + f1.getName());//获取文件名
(5)判断是否是一个目录
System.out.println(f1.isDirectory());
//判断是否是一个文件
System.out.println(f1.isFile());
(6)获取文件最后一次修改时间
long haoMiao = f1.lastModifies();//这个毫秒数是从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(strFime);
(7)获取文件大小
System.out.println(f1.length());
(8)ListFile方法
//获取当前目录下所有的子文件
File f = new File(“文件路径”);
File[] files = f.listFiles();
目录拷贝
import java.io.*;
public class CopyCouse {
public static void main(String[] args) {
//可以继续降低耦合度
File f = new File("E:\\Java笔记");
CopyOperation.CopyCouse(f,"D:\\");
}
//封装成内部类的形式也是极好的
static class CopyOperation{
public static void CopyCouse(File A, String B){
//这段代码使得 “路径仅为盘符” 的时候,去除路径末尾的斜杠
boolean flag = true;
if(flag && B.endsWith("\\")){
B.substring(0,B.length()-2);
flag = false;
}
flag = false;
//如果是文件
if(A.isFile()){
Copy(A,B + "\\" + A.getName());
return;
}
//如果是目录
else{
File f = new File(B + "\\" + A.getName());
if(!f.exists()){
f.mkdirs();
}
File[] files = A.listFiles();
for(File file : files){
CopyCouse(file,B + "\\" + A.getName());
}
return;
}
}
//封装 文件拷贝代码
public static void Copy(File A, String B){
FileOutputStream fos = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(A);
fos = new FileOutputStream(B);
byte[] bytes = new byte[1024 * 1024];
int readCound = 0;
while((readCound = fis.read(bytes)) != -1){
fos.write(bytes,0,readCound);
}
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();
}
}
}
}
}
}
1.序列化:Serialize
java对象存储到文件中,将java对象的状态保存下来的过程
反序列化:Deserialize
将硬盘上的数据重新恢复到内存当中,恢复成java对象
2.序列化的实现(序列化之前要有java对象)
代码示例:
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("文件名"));
//序列化对象
oos.writeObject(s);
//刷新
oos.flush();
//关闭
oos.close();
注意!
参与序列化和反序列化的对象必须实现Serializable接口
3.通过源码发现,Serializable接口只是一个标志接口,该接口什么代码都没有,起到的作用:
起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇,Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成一个序列化版本号。
4.反序列化
代码示例:
ObjectInputSream ois = new ObjectInputStream(new FileInputStream("文件名"));
//开始反序列化读
Object obj = ois.readObject();
//反序列化回来是一个学生对象,所以想会调用学生对象的toString方法
System.out.println(obj);
ois.close();
5.可以一次序列化多个对象,将对象放到集合当中序列化集合。
代码示例:
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("文件名"));
oos.writeObjcet(userList);
//序列化一个集合,这个集合对象中放了很多其他对象
oos.flush();
oos.close();
6.transient关键字表示游离的,不参与序列化
7.序列化版本号有什么用?
Java虚拟机看到Serializable接口之后会自动生成一个序列化版本号,便于Java虚拟机区分类,但是建议手动写出序列化版本号,给予一个固定值,这样即使代码修改了,但是序列化版本号不变,Java虚拟机会认为是同一个类。
8.IO+Properties联合使用
IO流:文字的读和写
Properties:是一个Map集合,key和value都是String类型,以后经常修改的数据可以单独写到一个数据文件中,使用程序动态读取,将来只需要修改这个文件的内容,java代码不需要改动不用重新编译。服务器也不需要重启就可以拿到动态的消息。类似于以上机制的这种文件被称为配置文件,并且当配置文件中的内容格式是:
key1 = value
key2 = value
时,我们把这种配置文件叫做属性配置文件。java规范中有要求:属性配置文件以“.properties”结尾。这种以.properties的文件在java中被称为:属性配置文件。Properties是专门存放属性配置文件内容的一个类。