IO框架
一、流的概念
概念:内存与存储设备之间传输数据的通道。
二、流的分类
按方向分类:
- 输入流:将<存储设备>中的内容读入到<内存>中
- 输出流:将<内存>中的内容读入到<存储设备>中
内存:内存是一种用于暂时存放[CPU]中的运算数据和外部储存器交换数据的随机储存器。
存储:一般可分为机械[硬盘]和[固态硬盘],是一种储存硬件,用于存放数据。
按单位进行划分:
- 字节流:以字节为单位,可以读写所有数据。
- 字符流:以字符为单位,只能读写文本数据。
按功能进行划分:
- 节点流:具有实际传输数据的读写功能。
- 过滤流:在节点流的基础上增强功能,比如缓冲流。
三、字节流
文件字节流
字节流的两个抽象父类:
- 字节输入流:
InputStream
这个抽象类是表示输入字节流的所有类的超类。
常用方法:
- 字节输出流:
OutputStream
这个抽象类是表示字节输出流的所有类的超类。 输出流接收输出字节并将其发送到某个接收器。
常用方法:
字节流的两个子类——文件字节流:
- 文件字节输入流:
FileInputStream
——读取文件
public class FileInputStream extends InputStream
从文件系统中的文件获取输入字节。 什么文件可用取决于主机环境。
FileInputStream
用于读取诸如图像数据的原始字节流。 要阅读字符串,请考虑使用
FileReader
。
构造方法:
常用方法:
public int read(byte[] b)从流中读取
多个
字节,将读到的内容存入
b
数组,返回实际读到的
字节数
;如果达到文件的尾部,则返回
-1
第一种读取方式:单个字节读取——效率不高!
【参考代码】
import java.io.FileInputStream;import java.io.FileNotFoundException;public class InputStreamTest {public static void main(String[] args) throws Exception {// 1.创建FileInputStream,并指定文件路径FileInputStream fis = new FileInputStream("d:\\\\aaa.txt");// 2.读取文件fis.read():一个一个的读int data = 0;while ((data = fis.read()) != -1){System.out.println();}// 3.读完之后,关闭流fis.close();}}
第二种读取方式:一次读取多个字节,多一个字节数组!
import java.io.FileInputStream;import java.io.FileNotFoundException;public class InputStreamTest {public static void main(String[] args) throws Exception {// 1.创建FileInputStream,并指定文件路径FileInputStream fis = new FileInputStream("d:\\\\aaa.txt");// 2.读取文件fis.read():一个一个的读// int data = 0;// while ((data = fis.read()) != -1){// System.out.println();// }// 2.1一次读取多个字节byte[] buf = new byte[1024];int count = 0;while ((count = fis.read(buf)) != -1){System.out.println(new String(buf, 0, count)); // abcdefg}// 3.读完之后,关闭流fis.close();}}
- 文件字节输出流:
FileOutputStream
——写入文件
public class FilterOutputStream extends OutputStream
这个类是过滤输出流的所有类的超类。 这些流位于已经存在的输出流( 底层输出流) 之上 ,它使用它作为数据的基本接收器,但是可能沿着数据方向转换或提供附加功能。
构造方法:
常用方法:
public int write(byte[] b)一次写多个字节,将b数组中的所有字节,写入输出流中!
【参考代码】
import java.io.FileNotFoundException;import java.io.FileOutputStream;public class FileOutPutStreamTest {public static void main(String[] args) throws Exception {// 创建文件输出流FileOutputStream fos = new FileOutputStream("d:\\\\bbb.txt",true); // 加true后新的字节不会覆盖原字节// 读入多个字节String str = "abcdef";fos.write(str.getBytes());// 关闭流fos.close();System.out.println("加载完毕!");}}
注:如果是文本文件最好用字符流
案例:使用文件字节流进行文件复制
复制文件:利用文件字节流,一边读取,一边写入!
注:使用字节流可以复制任意文件,而字符流却不行!
【参考代码】
import java.io.FileInputStream;import java.io.FileOutputStream;public class FileCopyDemon {public static void main(String[] args) throws Exception{// 创建文件输入流FileInputStream fis = new FileInputStream("d:\\\\bbb.txt");// 创建文件输出流FileOutputStream fos = new FileOutputStream("d:\\\\ccc.txt");//复制文件:一边读取,一边写入byte[] buf = new byte[1024];int count = 0;// count实际读取的个数while ((count = fis.read(buf)) != -1){fos.write(buf, 0, count);}fis.close();fos.close();System.out.println("复制完毕!");}}
文件字节缓冲流
缓冲流:
BufferedInputStream/BufferedOutputStream
。提高IO效率,减少访问磁盘的次数;数据存储在缓冲区,
flush
是将缓冲区的内容写入文件中,也可以直接
close
。
- 字节输入缓冲流:
BufferedInputStream
——快速读取文件
BufferedInputStream
为另一个输入流添加了功能,即缓冲输入和支持
mark
和
reset
方法的功能。 当创建
BufferedInputStream
时,将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次有多个字节。
mark
操作会记住输入流中的一点,并且
reset
操作会导致从最近的
mark
操作之后读取的所有字节在从包含的输入流中取出新的字节之前重新读取。
构造方法:
常用方法:
【参考代码】
import java.io.BufferedInputStream;import java.io.FileInputStream;public class BufferedInputStreamDemon {public static void main(String[] args) throws Exception{FileInputStream fis = new FileInputStream("d:\\\\aaa.txt");//1. 创建字节缓冲输入流BufferedInputStream bis = new BufferedInputStream(fis); // 维护字节流,从缓冲区读取字节,加快效率//2. 读取int data = 0;while((data = bis.read()) != -1){System.out.println((char) data);}//3. 关闭缓冲流bis.close();}}
- 字节输出缓冲流:
BufferedOutputStream
——快速写入文件
该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,不必为写入的每个字节导致底层系统的调用。
构造方法:
常用方法:
【参考代码】
import java.io.BufferedOutputStream;import java.io.FileOutputStream;public class BufferedOutputStreamDemon {public static void main(String[] args) throws Exception{FileOutputStream fos = new FileOutputStream("d:\\\\buff.txt");//1. 创建输出缓冲流BufferedOutputStream bos = new BufferedOutputStream(fos);//2. 写入文件String str = "hello";for(int i = 0; i <= 5; i ++){bos.write(str.getBytes()); // 写入8k缓冲区bos.flush(); // 刷新到硬盘}//3. 关闭流(内部调用)fos.close(flush方法);}}
四、编码方式
注:当编码方式和解码方式不一致时,就会出现乱码!
五、字符流
**引入:**3个字节相当于一个字符,当我们要读取汉字(字符)时,如果用字节流来读取的话,他是一个一个字节读取的,最终的结果是字节,而不是我们想要的字符了!
字符流的两个父类(抽象类):
-
Reader
:字符输入流
常用方法:
public int read() {}public int read(char[] c) {}public int read(char[] b, int off, int len) {}
-
Write
:字符输出流
常用方法:
public void write(int n) {}public void write(String str) {}public void write(char[] c) {}
文件字符流
-
FileReader
——文件字符输入流
阅读字符文件的便利课。 该类的构造函数假定默认字符编码和默认字节缓冲区大小是适当的。 要自己指定这些值,请在FileInputStream上构造一个InputStreamReader。
FileReader
是用于读取字符流。 要读取原始字节流,请考虑使用
FileInputStream
。
public class FileReader extends InputStreamReader
构造方法:
常用方法:继承父类!
public int read(char[] c);从流中读取多个字符,讲读到内容存入c数组,返回实际读到的字符数;如果文件达到尾部,则返回-1.
【参考代码】
import java.io.FileReader;public class FileReaderDemon {public static void main(String[] args) throws Exception{//1. 创建FileReader 文件字符输入流FileReader fr = new FileReader("d:\\\\hello.txt");//2. 读取//2.1 单个读取// int data = 0;// while ((data = fr.read()) != -1){ // 读取一个字符!// System.out.println((char)data);// }char[] buf = new char[1024];int count = 0;while((count = fr.read(buf)) != -1){System.out.println(new String(buf, 0, count));}//3. 关闭fr.close();}}
-
FileWriter
——文件字符输出流
public void write(String str);一次写入多个字符,将b数组中所有字符,写入输出流;
【参考代码】
import java.io.FileWriter;public class FileWriterDemon {public static void main(String[] args) throws Exception{//1. 创建FileWriter对象FileWriter fw = new FileWriter("d:\\\\write.txt");//2. 写入字符String str = "防不胜防";for(int i = 0; i < str.length(); i ++){fw.write(str);fw.flush();}//3. 关闭fw.close();System.out.println("执行完毕");}}
案例:文件字符流实现文本文件复制
注:
FileReader、FileWriter
只能复制文本文件,不能复制图片或者二进制文件!—— 文本文件有字符编码!
【参考代码】
import java.io.FileReader;import java.io.FileWriter;public class CopyDemon {public static void main(String[] args) throws Exception{//1. 创建FileReader FileWriter 对象FileReader fr = new FileReader("d:\\\\write.txt");FileWriter fw = new FileWriter("d:\\\\write2.txt");//2. 读写int data = 0;while((data = fr.read()) != -1){fw.write(data);fw.flush();}//3. 关闭fr.close();fw.close();System.out.println("复制完毕!");}}
字符缓冲流
字符缓冲流:
BufferedReader/BufferedWriter
(1)高效读写
(2)支持输入换行
(3)可一次写一行,读一行。
-
BufferedReader
——字符缓冲输入流
从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。
可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途。
通常,由读取器做出的每个读取请求将引起对底层字符或字节流的相应读取请求。 因此,建议将BufferedReader包装在其read()操 作可能昂贵的读取器上,例如FileReader
BufferedReader in = new BufferedReader(new FileReader("foo.in"));
将缓冲指定文件的输入。 没有缓冲,每次调用read()或readLine()可能会导致从文件中读取字节,转换成字符,然后返回,这可能非常低效。
构造方法:
常用方法:
【参考代码】
```import java.io.BufferedReader;import java.io.FileReader;/*** 字符缓冲流读取文件*/public class BufferedReaderDemon {public static void main(String[] args) throws Exception{//1. 创建缓冲流FileReader fr = new FileReader("d:\\\\write.txt");BufferedReader br = new BufferedReader(fr);//2. 读取//2.1 第一种读取方式// char[] buf = new char[1024];// int count = 0;// while ((count = br.read(buf)) != -1){// System.out.println(new String(buf, 0, count));// }//2.2 第二种读取方式。 一行一行的读取String line = null;while ((line = br.readLine()) != null){System.out.println(line);}//3. 关闭br.close();}}
-
BufferedWriter
——字符缓冲输出流(写入字符)
将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。
可以指定缓冲区大小,或者可以接受默认大小。 默认值足够大,可用于大多数用途。
提供了一个newLine()方法,它使用平台自己的系统属性
line.separator
定义的行分隔符概念。 并非所有平台都使用换行符(\’\\ n\’)来终止行。 因此,调用此方法来终止每个输出行,因此优选直接写入换行符。
构造方法:
常用方法:
【参考代码】
import java.io.BufferedWriter;import java.io.FileWriter;public class BufferedWriterDemon {public static void main(String[] args) throws Exception{//1. 创建BufferedWriter对象FileWriter fw = new FileWriter("d:\\\\buffer.txt");BufferedWriter bw = new BufferedWriter(fw);//2. 写入for (int i = 0; i < 5; i ++){bw.write("好好学习吧!");bw.newLine();// 换行!bw.flush();}//3. 关闭bw.close();}}
打印流
-
PrintWriter
——打印流
将对象的格式表示打印到文本输出流。 这个类实现了全部在发现
print种
方法
PrintStream
。 它不包含用于编写原始字节的方法,程序应使用未编码的字节流。
不像类,如果启用自动刷新,它只会在调用的
println,printf,
或
format
方法来完成,而不是当一个换行符恰好是输出。 这些方法使用平台自己的行分隔符而不是换行符。
转换流
转换流:
InputStreamReader/OutputStreamWriter
(1)可以将字节流转为字符流
(2)可设置字符的编码方式
转换流的使用:
InputStreamReader读取文件
【参考代码】
import java.io.FileInputStream;import java.io.InputStreamReader;public class ZhuanHuanLiuTest {public static void main(String[] args) throws Exception{//1. 创建InputStreamReader对象FileInputStream fis = new FileInputStream("d:\\\\write.txt");InputStreamReader isr = new InputStreamReader(fis, "utf-8"); // 转换流设置编码方式//2.读取文件int data = 0;while ((data = isr.read()) != -1){System.out.println((char) data);}//3. 关闭isr.close();}}
OutputStreamWriter——写入文件
【参考代码】
import java.io.FileOutputStream;import java.io.OutputStreamWriter;public class ZhuanHuanLiuTest {public static void main(String[] args) throws Exception{//1. 创建OutputStreamWriter对象FileOutputStream fos = new FileOutputStream("d:\\\\info.txt");OutputStreamWriter osw = new OutputStreamWriter(fos, "gbk"); // 转换流设置编码方式//2. 写入for(int i = 0; i < 5; i ++){osw.write("我爱学习\\r\\n");osw.flush();}//3. 关闭osw.close();}}
六、对象流
对象流:
ObjectInputStream/ObjectOutputStream
。
(1)增强了缓冲区功能
(2)增强了读取8种基本数据类型和字符串功能
(3)增强了读写对象的功能:
readObject() 从流中读取一个对象(反序列化)
writeObject(Object obj) 向流中写入一个对象(序列化)
使用流传输对象的过程称为序列化,和反序列化。
- 对象输出流:
ObjectOutputStream
——序列化(写入对象)
ObjectOutputStream将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以 通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。
构造方法:
常用方法:
【参考代码】
import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class ObjectOutputStreamDemon {/*** 使用ObjectOutputStream实现对象的序列化————读入对象* 要求:序列化类必须实现接口*/public static void main(String[] args) throws Exception{//1. 创建对象流FileOutputStream fos = new FileOutputStream("d:\\\\stu.bin");ObjectOutputStream oos = new ObjectOutputStream(fos);//2. 序列化(写入操作)Student student = new Student("张三",19);oos.writeObject(student);//3. 关闭(自带flush()方法了)oos.close();System.out.println("序列化完毕");}}class Student implements Serializable {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"name=\'" + name + \'\\\'\' +", age=" + age +\'}\';}}
使用ObjectOutputStream实现对象的序列化————写入对象
要求:序列化类必须实现接口
- 对象输入流:
ObjectInputStream
——反序列化(读取重构成对象)
ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象。
构造方法:
常用方法:
【参考代码】
import java.io.*;public class ObjectInputStreamDemon {public static void main(String[] args) throws Exception{//1. 创建对象流FileInputStream fis = new FileInputStream("d:\\\\stu.bin");ObjectInputStream ois = new ObjectInputStream(fis);//2. 读取文件(反序列化)Student st = (Student) ois.readObject();//3. 关闭ois.close();System.out.println(st.toString());// Student{name=\'张三\', age=19}}}
序列化和反序列化注意事项:
(1)序列化类必须实现
Serializable
接口
(2)序列化类中对象属性要求实现Serializable接口
(3)序列化版本号ID,保证序列化的类和反序列化的类是同一个类
(4)使用transient(瞬间的)修饰属性,这个属性不能序列化
(5)静态属性不能序列化
(6)序列化多个对象,可以借助集合
七、File类
概念:代表物理磁盘中的一个文件或者文件夹(目录)。
常用方法:
文件操作
(1)分隔符:
- 路径分隔符
;
- 名称分隔符
\\
(2)文件操作:
-
创建文件
createNewFile()
boolean createNewFile()
当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。
if(!file.exists()){ // 如果文件不存在则创建boolean b = file.createNewFile();System.out.println("创建结果" + b);}
-
删除文件
直接删除:
delete()
boolean delete()
删除由此抽象路径名表示的文件或目录
JVM退出时删除:
file.deleteOnExit();Thread.sleep(5000); // 休眠五秒
-
获取文件信息
getAbsolutePath()
:获取到文件的绝对路径
-
getPath()
:获取到文件的路径
-
getName()
:获取文件的名称
-
getParent()
:获取文件的父级目录
-
length()
:获取文件的长度
-
lastModified()
:获取文件的创建时间
System.out.println("文件创建时间:" + new Date(file.lastModified()));
判断
canWrite()
:判断文件是否可写
isFile()
:判断是否是文件
isHidden()
:判断文件是否隐藏
【参考代码】
import java.io.File;import java.util.Date;public class FileTest {public static void main(String[] args) throws Exception{fileOpe();}/*** 文件操作*/public static void fileOpe() throws Exception{//1. 创建文件File file = new File("d:\\\\file.txt"); // 只是创建了一个文件对象,此时在d盘下并没有该文件if(!file.exists()){ // 如果文件不存在则创建boolean b = file.createNewFile();System.out.println("创建结果" + b);}//2. 删除文件//2.1 直接删除// System.out.println("删除结果" + file.delete());// //2.2 JVM退出时删除// file.deleteOnExit();// Thread.sleep(5000); // 休眠五秒//3. 获取文件信息System.out.println("获取文件绝对路径" + file.getAbsolutePath()); // 获取文件绝对路径d:\\file.txtSystem.out.println("获取路径" + file.getPath());System.out.println("获取文件名称" + file.getName());System.out.println("获取文件父目录" + file.getParent());System.out.println("获取文件长度" + file.length());System.out.println("文件创建时间:" + new Date(file.lastModified()));//4. 判断System.out.println("是否可写" + file.canWrite());System.out.println("是否是文件" + file.isFile());System.out.println("是否隐藏" + file.isHidden());}}
文件夹操作
-
创建文件夹
boolean
mkdir()
创建由此抽象路径名命名的目录。
boolean
mkdirs()
创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。
File dir = new File("d:\\\\aaa\\\\bbb\\\\ccc");if(! dir.exists()){dir.mkdir();// 只能单级目录System.out.println("创建结果:" + dir.mkdirs()); //mkdirs();//可以创建多级目录}
-
删除文件夹
直接删除:
delete()
——只能删除空目录
JVM删除:
file.deleteOnExit();Thread.sleep(5000);
// 休眠五秒
-
获取文件夹信息
getAbsolutePath()
:获取到文件夹的绝对路径
-
getPath()
:获取到文件夹的路径
-
getName()
:获取文件夹的名称(最里层)
-
getParent()
:获取文件夹的父级目录
-
lastModified()
:获取文件的创建时间
判断
isDirectory()
:判断是否是文件夹
isHidden()
:判断文件是否隐藏
遍历文件
(1)
list()
|
String[]
|
list()
返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。 || ———- | ———————————————————— |
//5. 遍历文件夹File dir2 = new File("e:\\\\picture");String[] files = dir2.list();for(String str: files){System.out.println(str);}
(2)
listFiles()
|
File[]
|
listFiles()
返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。 || ——– | ———————————————————— |
//5. 遍历文件夹File dir2 = new File("e:\\\\picture");File[] files = dir2.listFiles(); // 文件数组for(File file: files){System.out.println(file.getName());}
FileFilter接口
当调用File类中的
listFiles()
方法时,支持传入
FileFilter
接口接口实现类,对获取文件进行过滤,只有满足条件的文件才可以出现在
listFiles()
方法的返回值中。
|
File[]
|
listFiles(FileFilter filter)
返回一个抽象路径名数组,表示由此抽象路径名表示的满足指定过滤器的目录中的文件和目录。 || ——– | ———————————————————— |
【参考代码】
File dir2 = new File("e:\\\\picture");// 过滤:过滤出满足条件的文件File[] file2 = dir2.listFiles(new FileFilter() {@Overridepublic boolean accept(File pathname) {if(pathname.getName().endsWith(".png")){ // 只要.png结尾的图片return true;}return false;}});// 遍历输出for(File file : file2){System.out.println(file.getName());}
递归遍历与删除
递归遍历文件夹:
遍历拿到
dir.listFiles()
路径的路径数组,数组不为空且有文件的情况下,如果是文件夹递归则进去,直到不是文件夹,然后输出文件,否则输出当前目录下的文件!
【参考代码】
import java.io.File;public class ListDemon {public static void main(String[] args) {listDir(new File("e:\\\\aaa"));}//递归遍历文件夹public static void listDir(File dir){File[] files = dir.listFiles();// 得到所有得子文件与子文件夹System.out.println("路径:"+ dir.getAbsolutePath());if(files != null && files.length > 0){for(File file : files){if(file.isDirectory()){// 判断是否为文件夹listDir(file); // 如果是文件夹递归进去,直到不是文件夹}else{// 不是文件夹 则输出System.out.println(file.getAbsolutePath());}}}}}
我电脑E盘下的aaa文件如下图:
递归删除文件夹:
我们直到
delete()
方法只能删除空目录。如果目录里边有内容,
delete()
方法是无法删除的,即我们想用
delete()
方法删除上述aaa文件是行不通的!
为此,我们得先将
aaa
文件中所有内容给删除之后,才能将
aaa
文件删除掉!
aaa
里边有
bbb
文件夹,为此得先把它里边得内容先删掉,
bbb
里边有
ccc
文件夹为此得先把它里边得内容先删掉……..然后再逐步回退删除文件夹!
【参考代码】
import java.io.File;public class ListDemon {public static void main(String[] args) {deleteDir(new File("e:\\\\aaa"));}// 递归删除文件夹public static void deleteDir(File dir){File[] files = dir.listFiles();if(files != null && files.length > 0){for(File file : files){if(file.isDirectory()){deleteDir(file); // 递归}else {// 删除文件System.out.println(file.getAbsolutePath() + "删除:" + file.delete());}}}// 删除文件夹System.out.println(dir.getAbsolutePath() + "删除:" + dir.delete());}}
图示:
Properties
Properties:属性集合
public class Properties extends Hashtable<Object,Object>
Properties
类表示一组持久的属性。
Properties
可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。
属性列表可以包含另一个属性列表作为其“默认值”; 如果在原始属性列表中找不到属性键,则会搜索此第二个属性列表。
特点:
- 存储属性名和属性值
- 属性名和属性值都是字符串类型
- 没有泛型
- 和流有关
String |
getProperty(String key) 使用此属性列表中指定的键搜索属性,得到对应得value。 |
---|---|
Object |
setProperty(String key, String value) 同 Hashtable 方法 put 。 |
void |
list(PrintStream out) 将此属性列表打印到指定的输出流。 |
void |
load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)。 |
void |
store(OutputStream out, String comments) 将此属性列表(键和元素对)写入此 Properties 表中,以适合于使用方法加载到 Properties 表中的格式输出流。 |
后面学习的JDBC连接数据会用得到
.properties
文件!
八、总结
注:如果文章有任何错误或不足,请各位大佬尽情指出,评论留言留下您宝贵的建议!如果这篇文章对你有些许帮助,希望可爱亲切的您点个赞推荐一手,非常感谢啦