Java Io
1 按操作数据的类型分类
1.1字节流
-
一般用于操作二进制文件,数据的单位是byte(视频,音频,图片,exe,dll等文件)
-
以InputStream或OutputStream结尾
1.2字符流
-
一般用于操作文本数据,数据的单位是char(txt,xml,html等文件)
-
以Reade或Writer结尾
1.2.0按数据的流向分类
1.2.1 输入流
-
从磁盘文件或网络流到java程序中,用于读取数据
-
所有字节输入流的父类是InputStream;
-
所有字符输入流的父类是Reade;
1.2.2 输出流
-
从java程序流向磁盘文件或网络写入文件
-
所有字节输出流的父类是OutputStream;
-
所有字符输出流的父类是Writer;
1.3 File类
由于经常需要使用IO流来操作硬盘上的文件,JDK将硬盘上的文件映射为java.io.File
类,所以我们先要学习如何使用这个类。
File类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等。
File对象代表磁盘中实际存在的文件和目录。通过以下构造方法创建一个File对象。
-
常用构造方法
new File(File parent,String child ); new File(String parent,String child); new File(String pathName);
-
常用方法
方法名 | 描述 |
---|---|
String getName() | 获取文件(夹)名 |
String getPath() | 获取文件(夹)路径 |
boolean exists() | 文件(夹)是否存在 |
boolean isFile() | 是否是一个文件 |
boolean isDirectory() | 是否是一个目录(文件夹) |
boolean createNewFile() | 创建一个文件 |
boolean mkdir() | 创建一个具体的文件夹 |
boolean mkdirs() | 创建多级文件夹 |
boolean delete() | 删除某个文件(夹) |
String [] list() | 返回某个文件夹下的所有文件(夹)名 |
File [] listFiles() | 获取某个文件夹下所有的文件(夹) |
代码演示:
package yrh; import java.io.File; import java.io.IOException; import java.util.Arrays; public class FileTest { public static void main(String[] args) throws IOException { File file=new File("D:/fff.txt"); //输出文件是否存在 System.out.println(file.exists()); if (!file.exists()) { //创建这个文件 file.createNewFile(); } //判断是否是一个文件 System.out.println(file.isFile()); //是否是一个目录 System.out.println(file.isDirectory()); //获取文件的名字 System.out.println(file.getName()); //获取文件路径 System.out.println(file.getPath()); //删除此文件 file.delete(); //返回dir目录的所有文件,以string[]数组保存 File file2=new File("D:/dir"); file2.mkdir(); String[] list = file2.list(); System.out.println(Arrays.toString(list)); //返回dir目录的所有文件,以File[]数组保存 File[] listFiles = file2.listFiles(); for (File file3 : listFiles) { System.out.println(file3); } //删除目录 file2.delete(); } }
代码演示:
要求:将盘中的qq.exe(D:\a\blb.jpg) 通过程序复制到另一个路径下(D:\b\qq.exe)。
package yrh; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class TetsIO { public static void main(String[] args) { // TODO Auto-generated method stub //要求:将盘中的qq.exe(D:\a\blb.jpg) //通过程序复制到另一个路径下(D:\b\qq.exe)。 //创建输入输出流接口 InputStream fis=null; OutputStream fos=null; File f1=new File("d:/PCQQ2020.exe"); File f2=new File("d:/test/PCQQ2020.exe"); //创建目标文件 if (!f2.exists()) { try { f2.createNewFile(); //从f1读取文件流 fis=new FileInputStream(f1); //写到f2输出流 fos=new FileOutputStream(f2); //定义一个bety数组 byte buffer[]=new byte[2048]; int len=0; //循环读写文件 while ((len=fis.read(buffer))!=-1) { fos.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } //关闭流 finally{ if (fos!=null) { try { fos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (fis!=null) { try { fis.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } }
2文件读写
2.1文件读取
文件输入流FileInputStream
作用是:读取磁盘文件或网络的数据
FileInputStream(File file) FileInputStream(String filepath)
主要方法:
-
int read(byte[] buffer)读取文件数据,保存到字节数组中,返回值是读取长度-1代表读取完毕
-
void close() 关闭文件流
try-with-resource IO流使用完后必须关闭,否则文件流无法得到释放,close方法调用要写在finally中。 为简化关闭流的代码,Java1.7引入了try-with-resource语法,让资源在try-catch完毕后自动关闭。 语法:
try(类 对象 = new 类()){ 可能出现异常的代码 }catch(异常类型 对象){ 处理异常的代码 }
文件读取过程
-
创建文件输入流
-
创建byte数组
-
循环调用read方法,存入byte数组中
-
操作byte数组
-
读取完毕关闭文件流
import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class FileInputStreamTest { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub // 1.创建文件输入流 String filepath="D:/ddd.txt"; InputStream input=new FileInputStream(filepath); // 2.创建byte数组 byte b[]=new byte[1024]; // 3.循环调用read方法读取数据,存入byte数组 int len=0; while ((len=input.read(b))!=-1) { String str=new String(b,0,len,"UTF-8"); System.out.println(str); } // 4.操作byte数组 // 5.读取完毕关闭文件流 input.close(); } }
2.2 文件写入
文件输出流 FileOutputStream
作用是 :将数据写入磁盘文件或网络
构造方法:
FileOutPutStream(File flie) FileOutPutStream(String fliepath)
主要方法:
-
write(byte[] buffer,int offset,int length) 写入文件,offset开始位置,length写入长度
-
write(byte[] buffer)
-
close() 关闭流
文件写入步骤
-
创建文件输出流
-
将字符串转换问byte数组
-
调用writer方法
-
关闭文件流
package yrh; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Scanner; public class FileOutputStreamTest { public static void main(String[] args) { String filepath="d:/ccc.txt"; // 创建文件输出流 try { OutputStream fos=new FileOutputStream(filepath); Scanner in = new Scanner(System.in); System.out.println("输入写入内容"); String txt=in.next(); // 将字符串转换为byte数组 //调用write写入文件 fos.write(txt.getBytes()); // 关闭文件流 fos.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
2.3文件的复制
文件的复制是在读取文件的同事,将数据写到另一个文件中
思路
-
创建文件输入输出流
-
通过文件输入流读取数据到byte数组中
-
同时将byte数组中的数据写入到输出流中
-
循环 1,2,3
package yrh; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class Filecopy { //文件复制 public static void main(String[] args) throws IOException { // TODO Auto-generated method stub //读取文件位置 String filepath="d:/ccc.txt"; //写入文件位置 String fileOut="d:/eee.txt"; // 1、创建输入流和输出流 InputStream fis=new FileInputStream(filepath); OutputStream fos=new FileOutputStream(fileOut); // 2、创建byte数组 byte buffer[]=new byte[2048]; int len=0; //循环从输入流中读取数据 while ((len=fis.read(buffer))!=-1) { //向输出流中写入数据 fos.write(buffer, 0, len); } //关闭文件流 //先使用的后关闭 fos.close(); fis.close(); System.out.println("复制完成"); } }
3 缓冲
什么事缓冲
前提是内存的读写速度要远高于磁盘的读写速度,缓冲就是内存中的一块空间,通过缓冲可以提高数据的读写速度
JAVA的IO体系中有些流不能独立使用,必须套在其它流的上面才能使用,称为包装流。JAVA的IO体系就是通过装饰模式来实现。需要什么流的功能就在外面套一个对应的流即可,就像一个武士,需要一把武器就装饰一把武器,需要一身铠甲就套一层铠甲,这个武士就能使用武器跟铠甲的功能了。
而缓冲流就是常用的一个包装流,也叫高效流,是对4个基本的File
流的增强,所以也是4个流,按照数据类型分类:
-
字节缓冲流:
BufferedInputStream
,BufferedOutputStream
-
字符缓冲流:
BufferedReader
,BufferedWriter
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
为什么需要缓冲
如果没有缓冲,文件读写是直接从磁盘上进行的,速度比较慢;缓冲就是在内存建立一个空间,读写之前将一部分磁盘上的数据导入到
缓冲内存中,后面读写就直接从缓冲中进行,减少了直接冲磁盘读写的次数,从而提高读写效率。
缓冲流:在普通的IO流基础上,加入内存缓冲区提高IO效率
字节缓冲流
BufferdInputStream缓冲输入流
创建方法 new BufferedInputStream(InputStream in); new BufferedInputStream(InputStream in,int bufferSize);创建一个`BufferedInputStream`并保存其参数,输入流`in`供以后使用。 内部缓冲区数组创建并存储在`buf` 。
创建一个BufferedInputStream
并保存其参数,输入流in
供以后使用。 内部缓冲区数组创建并存储在buf
。
创建具有BufferedInputStream
缓冲区大小的BufferedInputStream,并保存其参数,输入流in
供以后使用。 长度为size
的内部缓冲区阵列被创建并存储在buf
。
参数
in
- 底层输入流。
size
- 缓冲区大小。
BufferdOutputStream 缓冲输入流
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。
使用非缓冲流和缓冲流进行文件复制的效率对比:
package yrh; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class BufferdInputStreamTest { //字节缓冲流 public static void main(String[] args) throws IOException { // TODO Auto-generated method stub //读取文件位置 String filepath="d:/ccc.txt"; //写入文件位置 String fileOut="d:/eee.txt"; //程序开始时间 long start = System.currentTimeMillis(); System.out.println("使用文件缓冲流"); //创建缓冲文件输入输出流对象 InputStream fis=new BufferedInputStream(new FileInputStream(filepath)); OutputStream fos=new BufferedOutputStream(new FileOutputStream(fileOut)); //创建byte数组存放文件 byte buffer[]=new byte[2048]; int len=0; //循环读取文件 while((len=fis.read(buffer))!=-1){ //向输出流写入文件 fos.write(buffer, 0, len); } //关闭文件流 fos.close(); fis.close(); System.out.println("复制完成"); long end = System.currentTimeMillis(); System.out.println("程序运行时间:"+(end-start)+"ms"); // System.out.println("不使用文件缓冲数据流"); // //程序开始时间 // long start = System.currentTimeMillis(); // //创建文件输入输出流对象 // InputStream fis=new FileInputStream(filepath); // OutputStream fos=new FileOutputStream(fileOut); // //创建byte数组存放文件 // byte buffer[]=new byte[2048]; // int len=0; // //循环读取文件 // while ((len=fis.read(buffer))!=-1) { // //向输出流写入文件 // fos.write(buffer, 0, len); // } // //关闭文件流 // fos.close(); // fis.close(); // System.out.println("复制完成"); // long end = System.currentTimeMillis(); // System.out.println("程序运行时间:"+(end-start)+"ms"); } }
4 字符流
4.1 FileReader 字符输入流
以字符char为单位进行的流,适合操作文本数据
-
Reader 字符输入流的父类
-
Writer 字符输出流的父类
FileReader 文件读取器
创建方法:
FileReader(File file) 创建一个新的 FileReader ,给出 File读取。 FileReader(FileDescriptor fd) 创建一个新的 FileReader ,给定 FileDescriptor读取。 FileReader(String fileName) 创建一个新的 FileReader ,给定要读取的文件的名称。
主要方法
-
public void close()
:关闭此流并释放与此流相关联的任何系统资源。 -
public int read()
: 从输入流读取一个字符。 -
public int read(char[] cbuf)
: 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。
代码演示:
要求:将文本文件中的内容读取到控制台中显示。
package test; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class RederTest { public static void main(String[] args) throws IOException { //要求:将文本文件中的内容读取到控制台中显示。 //1,创建字符输入流 FileReader fReader=null; try { File f1=new File("d:/ccc.txt"); fReader=new FileReader(f1); int len=0; while ((len=fReader.read())!=-1) { System.out.print((char)len); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { // 关闭流 if (fReader != null) { fReader.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
4.2 FileWriter 字符输出流
java.io.FileWriter
抽象类是表示用于写出字符流的超类。构造时使用系统默认的字符编码和默认字节缓冲区。
-
void write(int c)
写入单个字符。 -
void write(char[] cbuf)
写入字符数组。 -
abstract void write(char[] cbuf, int off, int len)
写入字符数组的某一部分,off数组的开始索引,len写的字符个数。 -
void write(String str)
写入字符串。 -
void write(String str, int off, int len)
写入字符串的某一部分,off字符串的开始索引,len写的字符个数。 -
void flush()
刷新该流的缓冲。 -
void close()
关闭此流,但要先刷新它。
代码演示:
要求:将下面的一段话输入到D盘的文本文件中。
昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否,知否?应是绿肥红瘦。
package test; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class WriterTest { public static void main(String[] args) throws IOException { //要求:将下面的一段话输入到D盘的文本文件中。 //昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否,知否?应是绿肥红瘦。` //创建文件输出流 FileWriter writer=null; File f1=new File("D:/eee.txt"); //创建文件 if (!f1.exists()) { f1.createNewFile(); } //构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。 writer=new FileWriter(f1, true); writer.write("昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否,知否?应是绿肥红瘦。"); writer.close(); } }
5 对象流
5.1.1概述
Java 提供了一种对象序列化的机制。用来将某个对象保存到磁盘上或者-在网络上传输。可以理解为将内存中的对象进行持久化保存下来。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。
JAVA中是通过对象流来实现对象的序列化跟反序列化的,对象流也是一种包装流。
5.1.2 序列化
java.io.ObjectOutputStream
类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
被序列化的对象需要实现java.io.Serializable
接口。Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。
如果有不需要序列化的字段使用transient关键字修饰即可。
代码演示:
package pojo; import java.io.Serializable; //实体类 public class Person implements Serializable{ // 序列化版本号,必须保证序列化跟反序列化的版本号一致 private static final long seriaVersionUID=-1213754818242979380L; public String name; //设置age字段为transient,表示此字段不需要序列化 public transient int age=1; public String sex; @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", sex=" + sex + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]"; } }
序列化代码演示
package test; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import pojo.Person; public class Demo12Serializable { public static void main(String[] args) throws IOException, ClassNotFoundException { // TODO Auto-generated method stub //序列化 //saveObject(); //反序列化 readObject(); } //序列化 public static void saveObject() throws IOException{ //准备序列化的对象 Person person=new Person(); person.name = "张三"; person.age = 18 ; person.sex = "男"; //准备数据流 FileOutputStream fos=new FileOutputStream("d:/eee.txt"); ObjectOutputStream out=new ObjectOutputStream(fos); //序列化对象 out.writeObject(person); // 关闭流 out.close(); fos.close(); } //反序列化 public static void readObject() throws IOException, ClassNotFoundException{ //准备对象流 FileInputStream fis=new FileInputStream("d:/eee.txt"); ObjectInputStream in=new ObjectInputStream(fis); Person person = (Person) in.readObject(); System.out.println(person); //关闭流 in.close(); fis.close(); } }
重点
序列化的重点在于序列化跟反序列化的的版本号要一致,通常可以设置成固定值或者有IDE自动生成,这个版本号定义在实体类的属性private static final long serialVersionUID
。如果版本号信息不一致会抛出InvalidClassException
异常。1
5.3 properties属性集
5.3.1 概述
前面学习过集合,集合家族中有个成员java.util.Properties
,它继承于Hashtable
。Properties
是使用键值结构存储数据的,但它最大的特点是具有持久化功能。
5.3.2 常用API
-
public Properties()
:创建一个空的属性列表。 -
public Object setProperty(String key, String value)
: 保存一对属性。 -
public String getProperty(String key)
:使用此属性列表中指定的键搜索属性值。 -
public Set<String> stringPropertyNames()
:所有键的名称的集合。 -
public void load(InputStream inStream)
: 从字节输入流中读取键值对数据。 -
public void store(OutputStream out,String comments)
:从字节输入流中存储键值对数据。
代码演示
要求:往Properties
中存储数据,并通过流写出到本地文件中。
package test; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStreamWriter; import java.util.Properties; public class TestProperties { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub //创建对应属性文件 File file=new File("D:/yhr.properties"); if (!file.exists()) { file.createNewFile(); } //创建properties对象,存储文件 Properties p=new Properties(); p.setProperty("name", "易荣辉"); p.setProperty("age", "12"); p.setProperty("sex", "男"); //通过OutputStreamWriter包装一层流,可以解决乱码问题 FileOutputStream fos=new FileOutputStream(file); OutputStreamWriter osw=new OutputStreamWriter(fos,"utf-8"); p.store(osw, ""); osw.close(); fos.close(); } }
代码演示
要求:将Properties
中的数据读取到程序中,遍历所有的数据。
package test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Properties; import java.util.Set; public class Demo14Properties { public static void main(String[] args) throws IOException { //创建对应的属性文件 File file=new File("D:/yhr.properties"); //建立到文件直接的流 FileInputStream fis=new FileInputStream(file); InputStreamReader read=new InputStreamReader(fis); //通过流把配置文件中的数据加载到Propertis对象中 Properties properties=new Properties(); properties.load(read); //关闭流 read.close(); fis.close(); //遍历 Set<String> keys = properties.stringPropertyNames(); for (String string : keys) { System.out.println(string+" = "+properties.getProperty(string)); } } }
tips:
-
properties
文件中键值对之间的分割可以使用=
:
跟空格。 -
由于
properties
继承Hashtable
,所有还可以通过枚举器来遍历。