IO流
1 什么是I/O流
输入输出都相对于内存,内存作为参照物
有多种分类方式:
一种方式是按照流的方向进行分类:
以内存作为参照物,
往内存中去,叫做输入(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个字节。)
综上所述:流的分类
输入流、输出流
字节流、字符流
2 java IO流这块有四大家族
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
四大家族的首领都是抽象类。(abstract class)
java.io.Closeable接口,都是可关闭的,都有close()方法。
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭。
stream结尾为字节流 Reader Writer 字符流
所有的输出流都实现了:
java.io.Flushable接口,都是可刷新的,都有flush()方法。养成一个好习惯,输出流在最终输出之后,一定要记得flush()刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道!)刷新的作用就是清空管道。注意:如果没有flush()可能会导致丢失数据。
3 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(掌握)
4 FileInputStream 终极版
int read(byte[] b)
一次最多读取 b.length 个字节。减少硬盘和内存的交互,提高程序的执行效率。往byte[]数组当中读。
FileInputStream类的其它常用方法:
int available():返回流当中剩余的没有读到的字节数量
long skip(long n):跳过几个字节不读。
Idea 默认当前路径是 Project的根目录
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Test04 {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("D:\\armadillo-9.900.3\\LICENSE.txt");
byte[] bytes = new byte[1000];
int readcount=0;
while ((readcount=fileInputStream.read(bytes))!=-1){
System.out.println(new String(bytes,0,readcount));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream!=null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Test04 {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("D:\\armadillo-9.900.3\\LICENSE.txt");
int available = fileInputStream.available(); //不适合较大的文件 数组容量有限
byte[] bytes = new byte[available];
int read = fileInputStream.read(bytes);
System.out.println(new String(bytes));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream!=null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
5.FileOutputStream
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test05 {
public static void main(String[] args) {
FileOutputStream fileOutputStream = null;
try {
// fileOutputStream = new FileOutputStream("abc.txt",true); //不存在自动新建
// byte[] bytes = {110,98,87,99};
// fileOutputStream.write(bytes);
// fileOutputStream.write(bytes,0,2);//如何追加写入?
fileOutputStream = new FileOutputStream("ccc.txt",true);
String s="错误信息请联系。。";
byte[] bytes = s.getBytes();
fileOutputStream.write(bytes);
fileOutputStream.write(bytes);
fileOutputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6.文件的复制
package com.fangun;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test06 {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream=null;
try {
fileInputStream = new FileInputStream("D:\\armadillo-9.900.3\\LICENSE.txt");
// int available = fileInputStream.available();
// byte[] bytes = new byte[available];
// int read = fileInputStream.read(bytes);
// fileOutputStream = new FileOutputStream("D:\\code.txt");
// fileOutputStream.write(bytes);
// fileOutputStream.flush();
//一边读一边写
fileOutputStream=new FileOutputStream("D:\\code001.txt");
byte[] bytes = new byte[1024 * 1024];
int read=0;
while ((read=fileInputStream.read(bytes))!=-1){
fileOutputStream.write(bytes,0,read);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try { //注意这里分开catch
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
7 FileReader 与 FileWriter
复制普通文本文件
package com.fangun;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Test09 {
public static void main(String[] args) {
FileReader fileReader=null;
FileWriter fileWriter=null;
try {
fileReader= new FileReader("D:\\code.txt");
fileWriter = new FileWriter("D:\\code001.txt");
char[] chars = new char[4];
int readvalue = 0;
while ((readvalue=fileReader.read(chars))!=-1){
fileWriter.write(chars,0,readvalue);
fileWriter.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fileReader!=null){
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileWriter!=null){
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
8 BufferReader与BufferWriter
带有缓冲区的流,可以不用手动构建byte char 数组。当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流。外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流。FileReader就是一个节点流。BufferedReader就是包装流/处理流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\code.txt")));
9 数据流
DataInputStream DataOutputStream
10 标准的输出流
实现日志记录功能:
PrintStream printStream 指向输出流
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 s){
try {
PrintStream printStream = new PrintStream(new FileOutputStream("log001.txt",true));
System.setOut(printStream);
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(date);
System.out.println(format+s);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
11 File 类
1、File类和四大家族没有关系,所以File类不能完成文件的读和写。
2、File对象代表什么?
文件和目录路径名的抽象表示形式。
C:\Drivers 这是一个File对象
C:\Drivers\Lan\Realtek\Readme.txt 也是File对象。
一个File对象有可能对应的是目录,也可能是文件。
File只是一个路径名的抽象表示形式。
3、需要掌握File类中常用的方法
~~
public class FileTest01 {
public static void main(String[] args) throws Exception {
// 创建一个File对象
File f1 = new File(“D:\file”);
// 判断是否存在!
System.out.println(f1.exists());
// 如果D:\file不存在,则以文件的形式创建出来
/*if(!f1.exists()) {
// 以文件形式新建
f1.createNewFile();
}*/
// 如果D:\file不存在,则以目录的形式创建出来
/*if(!f1.exists()) {
// 以目录的形式新建。
f1.mkdir();
}*/
// 可以创建多重目录吗?
File f2 = new File("D:/a/b/c/d/e/f");
/*if(!f2.exists()) {
// 多重目录的形式新建。
f2.mkdirs();
}*/
File f3 = new File("D:\\course\\01-开课\\学习方法.txt");
// 获取文件的父路径
String parentPath = f3.getParent();
System.out.println(parentPath); //D:\course\01-开课
File parentFile = f3.getParentFile();
System.out.println("获取绝对路径:" + parentFile.getAbsolutePath());
File f4 = new File("copy");
System.out.println("绝对路径:" + f4.getAbsolutePath()); // C:\Users\Administrator\IdeaProjects\javase\copy
}
}
12 java.IO 实现目录全部内容复制
package com.fangun;
import java.io.*;
public class CopyAll {
public static void main(String[] args) {
File src = new File("D:\\Downloads\\armadillo-9.900.3");
File des = new File("E:\\");
copy(src,des);
}
private static void copy(File src, File des) {
if (src.isFile()){
FileInputStream fileInputStream=null;
FileOutputStream fileOutputStream=null;
try {
fileInputStream = new FileInputStream(src);
String s = (des.getAbsolutePath().endsWith("\\") ? des.getAbsolutePath() : des.getAbsolutePath() + "\\") + src.getAbsolutePath().substring(3);
File file = new File(s);
if (!(file.exists())){
boolean newFile = file.createNewFile();
}
fileOutputStream = new FileOutputStream(s);
byte[] bytes = new byte[1024 * 1024];
int readCount=0;
while ((readCount=fileInputStream.read(bytes))!=-1){
fileOutputStream.write(bytes);
fileOutputStream.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fileInputStream!=null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileOutputStream!=null){
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return;
}
File[] files = src.listFiles();
for (File file:files){
if (file.isDirectory()){
String s = (des.getAbsolutePath().endsWith("\\") ? des.getAbsolutePath() : des.getAbsolutePath() + "\\") + file.getAbsolutePath().substring(3);
File file1 = new File(s);
if (!(file1.exists())){
boolean mkdirs = file1.mkdirs();//创建父子目录
}
}
copy(file,des);
}
}
}
13 java的序列化与反序列化
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虚拟机会认为是同一个类。
14 java IO 与 Properties联合使用
IO+Properties的联合应用。
非常好的一个设计理念:
以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取。
将来只需要修改这个文件的内容,java代码不需要改动,不需要重新
编译,服务器也不需要重启。就可以拿到动态的信息。
类似于以上机制的这种文件被称为配置文件。
并且当配置文件中的内容格式是:
key1=value
key2=value
的时候,我们把这种配置文件叫做属性配置文件。
java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的。
这种以.properties结尾的文件在java中被称为:属性配置文件。
其中Properties是专门存放属性配置文件内容的一个类。