该文总结了JavaIO流常用对象与方法的使用样例,可根据需要学习。
文件
意义
Java把电脑中的文件和文件夹封装成了File类,我们可以使用File类对文件和文件夹进行一些操作
操作
- 删除文件夹/文件
- 创建文件/文件夹
- 判断文件/文件夹是否存在
- 获取文件/文件夹的大小
注意点
File类是一个与系统无关的类,任何操作系统都可以使用这个类中的方法
分隔符
路径分隔符:
- windows系统的路径分隔符 - ;
- Linux系统下的路径分隔符 - :
文件名称分隔符:
- window系统下 - \
- Linux系统下 - /
相关函数
/*static String pathSeparator
与系统有关的路径分隔符,为了方便,它被表示为一个字符串。
static char pathSeparatorChar
与系统有关的路径分隔符。
static String separator
与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
static char separatorChar
与系统有关的默认名称分隔符。
*/
String pathseparator = File.pathSeparator;
System.out.println("pathseparator = " + pathseparator);// 输出;
System.out.println("pathseparator = " + File.pathSeparatorChar);// 输出;
String separator = File.separator;
System.out.println("separator = " + separator);// 输出\
System.out.println("separator = " + File.separatorChar);// 输出\
路径
相对路径和绝对路径:
绝对路径:一个完整路径(从盘符开始的路径),如:D:\xxxx\xxx\xxx.java
它表示的是当前文件或者目录在当前操作系统中的真实路径,
缺点:
- 比较不灵活并且比较少见
相对路径:一个简化的路径。
注意:相对指的是什么?相对指的是对于当前项目的根路径,如果使用当前项目的根目录,路径可以简化书写
绝对路径:D:\xxxx\xxx\xxx.java
相对路径:xxx.java
-
表示相对于某个基准目录的路径(类似于我们所说的参照物)
-
不用指定文件在哪个盘下
-
java项目下,是以项目目录作为参考路径
注意:使用File对象的时候只能做到对文件(目录)的属性进行访问
比如:名字,大小,是否为文件等,还可以做到对文件(目录)进行删除和创建的操作
注意:
-
相对路径可以省略项目的根目录
-
相对路径不区分大小写
-
路径中的中文文件名称分割符:window下使用 \ , \是转译字符 , \ 代表这个是一个普通的\
//相对路径
String pathname = "src/cn/egret/Demo01.java";
File file = new File(pathname);
System.out.println(file); // src\cn\egret\Demo01.java
/**
* 获取绝对路径:getAbsolutePath()
*/
System.out.println(file.getAbsolutePath()); // C:\Users\86176\eclipse-workspace\day20210318\src\cn\egret\Demo01.java
File构造器
构造器 | 说明 | 优点 | 注意点 |
---|---|---|---|
File(File parent, String child) | 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。 | 1. 父路径和子路径他们可以单独书写,使用起来比较灵活,并且父路径和子路径都可以变化 2. 路径可以随时变化,父类是File类型,可以使用File的方法对路径进行操作,在使用路径去创建对象 | |
File(String pathname) | 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 | 1、路径可以是相对路径也可以是绝对路径<br/ >2、传入的路径它可以是存在的也可以是不存在 3、路径可以是以文件结尾,也可以是以文件夹结尾 4、创建File对象的时候只是把字符串路径封装成字符串对象,可以不用考虑路径的真假问题 | |
File(String parent, String child) | 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。 | ||
File(URI url) | 通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。 |
String pathName = "D:\\egret\\javase2101\\day20210318\\cn\\egret\\Demo04.java";
File file = new File(pathName);
System.out.println(file); // D:\egret\javase2101\day20210318\cn\egret\Demo04.java
/**
* 注意:File对象被创建成功,但是不一定表示File对象所代表的文件或者文件夹就已经存在
*/
String parent = "/Users/egret";
String child = "javase2101.txt";
File file2 = new File(parent, child);
System.out.println(file2);// /Users/javase2101.txt
常用方法
方法 | 说明 |
---|---|
getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串。 |
getPath() | 将此抽象路径名转换为一个路径名字符串。 |
getName() | 返回由此抽象路径名表示的文件或目录的名称。 |
length() | 返回由此抽象路径名表示的文件的长度。 |
isDirectory() | 测试此抽象路径名表示的文件是否是一个目录。 |
isFile() | 测试此抽象路径名表示的文件是否是一个标准文件。 |
exists() | 测试此抽象路径名表示的文件或目录是否存在。 |
//1、getAbsolutePath():返回此抽象路径名的绝对路径名字符串
//注意:它是获取构造方法中传递的路径,无论是相对路径或者是绝对路径返回的都是绝对路径
String pathname = "day20210318\\src\\cn\\egret";
String pathname1 = "src/cn/egret/javase2101.txt";
File file1 = new File(pathname);
File file2 = new File(pathname1);
System.out.println(file1.getAbsolutePath()); // 输出C:\Users\86176\eclipse-workspace\day20210318\src\cn\egret\javase2101.txt
System.out.println(file2.getAbsolutePath()); // 输出C:\Users\86176\eclipse-workspace\day20210318\src\cn\egret\javase2101.txt
//2、getPath():将此抽象路径名转换为一个路径名字符串
//注意:这里获取的路径是表示你这个路径是什么它就是什么
//toString()调用的就是getPath方法
String s1 = file1.getPath();
String s2 = file2.getPath();
System.out.println("s1 = " + s1);// s1 = \Users\86176\eclipse-workspace\day20210318\src\cn\egret\javase2101.txt
System.out.println("s2 = " + s2); // s2 = src\cn\egret\javase2101.txt
//getName():返回由此抽象路径名表示的文件或目录的名称。
String name1 = file1.getName();
String name2 = file2.getName();
System.out.println("name1 = " + name1); // name1 = javase2101.txt
System.out.println("name2 = " + name2); // name2 = javase2101.txt
//length():返回由此抽象路径名表示的文件的长度。
//注意:获取的是构造方法中指定的文件大小,以字节为单位
//注意:文件夹是没有大小的概念的,不可以获取文件夹的大小
//注意:如果构造方法中给出路径是假的,则此时他返回0
String p1 = "/Users/egret/javase2101";//文件夹
String p2 = "C:\\Users\\86176\\eclipse-workspace\\day20210318\\src\\cn\\egret\\Demo01.java";//真实存在的文件
String p3 = "D:\\egret\\javase2101\\day20210318\\src\\cn\\egret\\Demo01.java";//假路径
File f1 = new File(p1);
File f2 = new File(p2);
File f3 = new File(p3);
System.out.println("f1 =" + f1.length());//f1 = 0由于文件夹没有大小的概念,故我们不可以用于获取文件夹大小
System.out.println("f2 =" + f2.length());// f2 =1743
System.out.println("f3 =" + f3.length()); // f3 =0
/**
* boolean exists():测试此抽象路径名表示的文件或目录是否存在。
* 存在:返回true 不存在:返回false
*/
String p1 = "/Users/egret/javase2101/day20210318/src/cn/egret";
String p2 = "D:/egret/javase2101/day20210318/src/cn/egret";
File f1 = new File(p1);
File f2 = new File(p2);
boolean exists2 = f2.exists();
boolean exists = f1.exists();
System.out.println("文件夹/文件是否存在 :" + exists);
System.out.println("文件夹/文件是否存在 :" + exists2);
/**
* boolean isDirectory(): 测试此抽象路径名表示的文件是否是一个目录。
* 是:true 否:false
*
* boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。
* 是:true 否:false
*
* 注意:电脑的硬盘只有文件/文件夹,他们是互斥的
*
* 这两个方法使用的前提:路径必须存在,否则都返回false
*/
if(f1.exists()) {
System.out.println("f1.isDirectory :" + f1.isDirectory());
System.out.println("f1.isFile :" + f1.isFile());
}else {
System.out.println("文件/文件夹不存在");
}
System.out.println("f2.isDirectory :" + f2.isDirectory());
System.out.println("f2.isFile :" + f2.isFile());
文件方法
创建文件
方法 | 说明 |
---|---|
createNewFile() | 创建文件 |
mkdir() | 创建单级文件夹 |
mkdirs() | 既能创建单级文件夹,也能够创建多级文件夹 |
String p1 = "src/cn/egret/javase2101.txt";
File f1 = new File(p1);
if(!(f1.exists())) {
boolean createNewFile = f1.createNewFile();
System.out.println("创建成功");
}else {
System.out.println("您要创建的文件已经存在,请不要重复创建");
}
String p2 = "src/cn/egret/aaa";
String p3 = "src/bn/egret/aaa/bbb/ccc/ddd";
File f2 = new File(p2);
File f3 = new File(p3);
if(!(f3.exists())) {
boolean mkdir = f3.mkdirs();
System.out.println("创建成功");
}else {
System.out.println("文件夹已经存在");
}
String path1 = "src/cn/egret/aaa";
String path2 = "src/cn/egret/aaa/bbb/ccc/ddd";
File f1 = new File(path1);
File f2 = new File(path2);
boolean mkdir = f1.mkdir();
boolean mkdirs = f2.mkdirs();
System.out.println("一级目录创建 :" + mkdir);
System.out.println("多级目录创建 :" + mkdirs);
删除文件delete()
删除有次级file的方法,他的返回值是boolean
true:文件、文件夹被删除
false:文件夹中有内容,不会删除,返回false。 构造方法中的路径是假路径也会删除失败
注意:delete直接在硬盘中删除文件或者文件夹,不放入回收站,删除需谨慎
String path = "src/cn/egret/aaa";
File file = new File(path);
boolean delete = file.delete();
System.out.println("文件是否删除:" + delete);
目录遍历 list()和 listFiles()
String [] list(); // 函数原型
//返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。
File [] listFiles(); // 函数原型
//返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
注意:注意:list( )和listFiles()这两个方法遍历的是构造器中给的目录
- 如果构造函数中给出的目录路径不存在,会抛出空指针异常。
- 如果构造方法中给出的路径不是一个目录的话,它也会抛出空指针异常
String path1 = "src/cn/egret";
File f1 = new File(path1);
if(!f1.exists()){
f1.mkdirs();
System.out.println("创建成功");
}else{
System.out.printle("创建失败");
}
//list()遍历构造方法中给的目录,会获取目录中所有文件/文件夹的名称, 并且把获取到的名称存到一个String类型的数组当中
File f2 = new File(path1);
if(f2.isDFirectory()){
String [] list = f2.list();
for(String name : list){
System.out.println("name" + name);
}
}else{
System.out.println("非文件夹“);
}
练习:删除多级文件夹
public static void delete(String pathName){
File f1 = new File(pathName);
if(f1.isDirectory()){
String [] list = f1.list();
for(int i = 0; i < list.length(); i++){
delete(pathname + "/" + list[i]);
}
}
f1.delete(); // 删除最外层文件夹
}
public static void main(String[] args) {
String pathname = "src/cn/egret/aaa";
File f1 = new File(pathname);
if(f1.isDirectory()) {
delete(pathname);
System.out.println("删除成功");
}else {
System.out.println("你要删除的目录不存在");
}
}
文件过滤器 FileFilter
获取当前目录下所有满足条件的部分子类
FilenameFilter 接口: boolean accept(File dir, String name) ;其中dir表示的是当前目录,name是文件名
FileFilter 接口:boolean accept(File pathname) pathname 表示的是文件
String pathname = "src/cn/egret";
File f1 = new File(pathname);
/**
* 通过FileFilter实现指定文件过滤
*/
FileFilter fileFilter = new FileFilter() {
/**
* 实现accept方法,当其值为true的时候表示这个文件是我们需要的
*
* 需求:只需要后缀名是java的文件
*/
@Override
public boolean accept(File pathname) {
if(pathname.isDirectory()) {
return true;
}
String name = pathname.getName();
/**
* endsWith:代表以什么为结尾
*/
return name.endsWith(".txt");
}
};
/**
* 可能数据从别的地方传入。所以这是我们还是需要先做一个判断,判断他是文件还是文件目录
*/
if(f1.isDirectory()) {
File[] listFiles = f1.listFiles(fileFilter);
for (File filename : listFiles) {
System.out.println("filename = " + filename);
}
}
}
文件随机读写类 RandomAccessFile
文件随机读写类 RandomAccessFile
基于指针对文件数据的读写,是用于实现对文件内容的读写操作
String p1 = "src\\cn\\egret\\d.txt";
File f1 = new File(p1);
System.out.println(f1.getAbsolutePath());
/**
* 使用文件随机读写类RandomAccessFile
* 注意:它内部会自动判断这个file是否存在,如果不存在则会创建
*
*
* 构造函数: RandomAccessFile(File file, String mode)
* 实现对指定File对象所表示文件或者指定路径的文件进行读写操作
*
*
* file:表示读写的文件
* mode:表示读写的模式
* r:只读模式 rw: 读写模式
* 注意:它的读模式和写模式是分开的
*/
RandomAccessFile randomAccessFile = new RandomAccessFile(p1, "rw");
/**
* write(int d)
* 向指定文件写入一个字节数据
* 实现上写的是d的低8位
*/
//randomAccessFile.write(16); // 并不能写入你想要的16
randomAccessFile.writeInt(16);
randomAccessFile.close();
/**
* 思考如何写入一个数字3
*
* 3 ---> 00000000 00000000 00000000 00000011
*
* -------------------------------------------------
*
* 00000000 00000000 00000000 00000011
* 00000000 00000000 00000000 00000011
* 00000000 00000000 00000000 00000011
* 00000000 00000000 00000000 00000011
*
* 使用右移运算符:写入4次就能够将3写入
*/
randomAccessFile.write(3>>>24);
randomAccessFile.write(3>>>16);
randomAccessFile.write(3>>>8);
randomAccessFile.write(3);
/**
* RandomAccessFile也为我们提供了若干个
* writeXXX() 用于实现对于基本数据类型的写出操作
* writeInt(int value) 会一次写出,表示int的4字节的数据
*/
long length1 = f1.length();
System.out.println("写入数据之前的文件大小 = " + length1);
randomAccessFile.writeInt(16);
System.out.println("写入成功");
long length2 = f1.length();
System.out.println("写入数据之后的文件大小 = " + length2);
//释放资源
randomAccessFile.close();
String p1 = "src/cn/egret/d.txt";
File f1 = new File(p1);
RandomAccessFile randomAccessFile = new RandomAccessFile(f1, "r");
/**
* readInt() 表示从指定文件中读取一个int类型的数据
*
* read() 表示从指定文件中读取一个byte类型的数据
*
* 一次会读取4个字节的数据
*/
int read= randomAccessFile.readInt();
System.out.println("读取到的数据为 :" + read);
------------------------------------------------------------------------------------
File f1 = new File("src/cn/egret/e.txt");
RandomAccessFile ra = new RandomAccessFile(f1, "rw");
/**
* getFilePointer() :获取当前指针位置
*/
long filePointer1 = ra.getFilePointer();
System.out.println("filePointer1 = " + filePointer1);
ra.write('a');
System.out.println("写入成功");
long filePointer2 = ra.getFilePointer();
System.out.println("filePointer2 = " + filePointer2);
ra.write('b'); //ab
long filePointer3 = ra.getFilePointer();
System.out.println("filePointer3 = " + filePointer3);
/**
* seek(long length) : 表示设定当前指针的位置
*/
ra.seek(1);
ra.write('c'); //ac
System.out.println("c写入成功当前指针位置为: " + ra.getFilePointer());
-----------------------------------------------------------------------------------
/**
* 一次性写入一串数据
*
* 注意:对于大文件操作都是通过字节数组进行的
*/
String str = "abcdefghijklmnopqrstuvwxyz";
byte[] bytes = str.getBytes();
ra.write(bytes);
System.out.println("写入成功");
byte[] bytes = new byte[(int)randomAccessFile.length()];
randomAccessFile.read(bytes);
System.out.println("读取到的数据为 :" + new String(bytes,"UTF-8"));
IO
硬盘特点:永久储存
输入流:把硬盘中的数据读取到内存中使用
输出流:把内存中的东西输出到硬盘
I : input 输入
o : output 输出
流: 数据(字符,字节) 1个字符 = 2个字节, 1个字节 = 8个比特位
注意:根据读数据不同有
- 字符输入流
- 字节输入流
- 字符输出流
- 字节输出流
根据数据的流向分为:输入流和输出流
- 输入流:把数据从其他设备上读取到内存中使用的流
- 输出流:把数据从内存中写入到其他设备上的流
根据数据类型可将流分为: 1、字符流 2、字节流
- 字节流:以字节为单位去读写数据的流
- 字符流:以字符为单位去读写数据的流
输入流 | 输出流 | |
---|---|---|
字节流 | InputStream | OutputStream |
字符流 | Reader | Writer |
字节流
字节流: 一切皆为字节,一切文本数据在存储的时候,都是以二进制的形式进行保存,既都是一个个字节。 所以字节流是可以传输任意文件的数据。
字节输出流outputStream
java.io.OutputStream:此抽象类是表示输出字节流的所有类的超类。将指定的字节信息写出到目的地。 它定义了字节输出流基本的共性方法。
方法 | 说明 |
---|---|
void close() | 关闭此输出流并释放与此流有关的所有系统资源。 |
void flush() | 刷新此输出流并强制写出所有缓冲的输出字节。 |
void write(byte[] b) | 将 b.length 个字节从指定的 byte 数组写入此输出流。 |
void write(byte[] b, int off, int len) | 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 |
abstract void write(int b) | 将指定的字节写入此输出流。 |
注意:当完成流操作时,必须调用close方法,释放资源
FileOutputStream
FileOutputStream : 文件字节输出流,用于将数据写到文件当中去
构造方法:
方法 | 说明 |
---|---|
FileOutputStream(File file) | 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 |
FileOutputStream(String name) | 创建一个向具有指定名称的文件中写入数据的输出文件流。 |
FileOutputStream(File file, boolean append) | 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 |
FileOutputStream(String name, boolean append) | 创建一个向具有指定 name 的文件中写入数据的输出文件流。 |
参数:写入数据的目的地
- String name:目的是一个文件路径
- File file:目的地是一个文件
构造方法的作用:
- 创建FileOutputStream对象
- 根据构造方法传递的文件/文件路径,创建一个空文件
- 会把FileOutputStream对象指向创建好的文件
代码实例:
//使用File对象创建流对象
File f1 = new File("src/cn/egret/pm/a.txt");
FileOutputStream fos = new FileOutputStream(f1);
使用文件名称创建流对象
FileOutputStream fos1 = new FileOutputStream("b.txt");
写入方法使用
/**
* 写出字节数据
*
* void write(int b) 将指定字节写入此文件输出流。
*
* 写入数据(内存 -> 硬盘)
*
* java程序 ——> Java虚拟机 ——> os(操作系统) ——> os调用写数据的方法 ——>将数据写入到文件当中
*
*
* 字节输出流使用步骤
* 1、创建FileOutputStream对象,构造方法中写入数据传递的目的地
* 2、调用FileOutputStream对象中的write,将数据写入到文件当中
* 3、释放资源(使用流是会占内存,使用要将内存清空,提高程序的效率)
*/
1、创建FileOutputStream对象,构造方法中写入数据传递的目的地
FileOutputStream fos = new FileOutputStream("src/cn/egret/pm/a.txt");
2、调用FileOutputStream对象中的write,将数据写入到文件当中
fos.write('a');
3、释放资源
fos.close();
/**
* 一次写多个字节的方法
*
* void write(byte[] b) 将 b.length 个字节从指定 byte 数组写入此文件输出流中。
*
*
* void write(byte[] b,int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
*/
FileOutputStream fos2 = new FileOutputStream("b.txt");
/**
* 思考:在文件中写入100,是写几个字节
*/
fos2.write(49);
fos2.write(48);
fos2.write(48);
fos2.close();
/**
* void write(byte[] b) 一次写入多个字节
*
* 注意:
* 1、如果写的第一个字节是正数,会查询ASCII码表
* 2、如果写的第一个字节是负数,那么第一个字节和第二个字节会组成一个中文显示
* 3、查询系统默认的编码表(GBK)
*/
String str = "ABCDEFG";
byte[] bytes = str.getBytes();
fos2.write(bytes);
/**
* void write(byte[] b,int off, int len)
*
* 把字节数组中的一部分写入到文件中
*
* off : 数组开始索引
* len : 写几个字节
*/
String str2 = "fileOutputStream";
byte[] bytes2 = str2.getBytes();
fos2.write(bytes2, 10, 6);
System.out.println("写入成功");
追加写入
/**
* 思考:如何在保留目标文件数据的同时,还能够添加新的数据
*
* FileOutputStream(File file, boolean append)
* 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
*
* FileOutputStream(String name, boolean append)
* 创建一个向具有指定 name 的文件中写入数据的输出文件流。
*
* 参数:
* append :追加写的开关
* true:创建对象不会覆盖原有文件,继续在文件末尾添加数据
* false:创建一个新文件覆盖原有文件,在新文件中写数据
* name : 写入数据的目的地
*/
FileOutputStream fos3 = new FileOutputStream("b.txt",true);
String str3 = "hello";
byte[] bytes = str3.getBytes();
fos3.write(bytes);
System.out.println("fos3写入数据成功");
String str4 = ",FileOutputStream";
byte[] bytes2 = str4.getBytes();
fos3.write(bytes2); //hello,FileOutputStream
/**
* 写换行,书写换行符号
* windows:\r\n
* linux: \n
* mac: \r
*
* 换行追加写
*/
FileOutputStream fos4 = new FileOutputStream("b.txt",true);
String str3 = "hello";
byte[] bytes = str3.getBytes();
for (int i = 0; i < 5; i++) {
fos4.write(bytes); //hello
fos4.write("\r".getBytes()); //windows系统下换行符 : \r\n
}
fos4.close();
字节输入流InputStream
java.io.InputStream:所有字节输入流的父类,可以读取字节信息到内存中去,它定义了字节输入流基本的共性方法
方法 | 说明 |
---|---|
void close() | 关闭此输入流并释放与该流关联的所有系统资源。 |
abstract int read() | 从输入流中读取数据的下一个字节。 |
int read(byte[] b) | 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。 |
int read(byte[] b, int off, int len) | 将输入流中最多len 个数据字节读入 byte 数组。 |
FileInputStream
FileInputStream:文件字节输入流
作用:把硬盘文件中的数据,读取到内存中去使用
构造方法:
方法 | 说明 |
---|---|
FileInputStream(File file) | 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 |
FileInputStream(String name) | 通过打开一个到实际文件的连接来创建一个 * FileInputStream,该文件通过文件系统中的路径名 name 指定。 |
参数:读取文件的数据域
- String name:文件路径
- File file:文件
构造方法中的作用:
-
创建FileInputStream对象
-
会把FileInputStream对象指定构造方法中要读取的文件
读取文件的原理(硬盘到内存)
- java程序 - JVM — os操作系统 - os读取数据的方法
使用字节输入流的步骤
- 创建FileInputStream对象,构造方法中绑定要读取的数据
- 使用FileInputStream对象中提供read方法读取文件
- 释放资源
读入示例
FileInputStream fis = new FileInputStream("src/cn/egret/pm/a.txt");
/*
* int read() :读取文件中一个字节并返回,读取到文件末尾返回-1
*/
int read = fis.read();
System.out.println("read :" + read);
int read1 = fis.read();
System.out.println("read1 :" + read1);
int read2 = fis.read();
System.out.println("read2 :" + read2);
int read3 = fis.read();
System.out.println("read3 :" + read3);
int read4 = fis.read();
System.out.println("read4 :" + read4);
/**
* 使用while循环优化程序
*
* 思考:while循环的结束条件
*
* 读取到-1的时候结束
*
* while循环表达式
* 1、fis.read() 读取一个字节
* 2、len = fis.read() 将读取到的字节给len
* 3、判断len != -1
*/
//定义一个变量,用于记录读取到的字节
int len = 0;
while ((len = fis.read()) != -1) {
System.out.println((char)len);
}
/**
* 一次读取多字节的方法
*
* read(byte[] b)
* 从输入流中读取一定数量的字节,并将其存储到缓冲区数组b中
*
* 明确两个事情
* 1、方法参数byte[]的作用
* 2、方法返回值int是什么
*/
FileInputStream fis = new FileInputStream("src/cn/egret/pm/a.txt");
byte[] bytes = new byte[2];
int len = fis.read(bytes);
System.out.println("len :" + len);
System.out.println("arrays.toString: " + Arrays.toString(bytes));
len = fis.read(bytes);
System.out.println("len :" + len);
System.out.println("arrays.toString: " + Arrays.toString(bytes));
len = fis.read(bytes);
System.out.println("len :" + len);
System.out.println("arrays.toString: " + Arrays.toString(bytes));
byte[] bytes = new byte[1024];
int len = 0; //记录每次读取到的有效字个数
while((len = fis.read(bytes)) != -1) {
/**
* String类的构造方法
* String(byte[] b) 把字节数组转化成字符串
*
* String(byte bytes[], int offset, int length) 把字节数组中的一部分转化成字符串
* offset:数组开始的索引
* length:转化的字节个数
*/
String str = new String(bytes,0,len);
System.out.println(str);
}
//释放资源
fis.close();
小练习:
/**
* 字节输入输出流练习:
*
* 需求:实现图片的复制,要将cn/egret/pm/image1.jpg 复制到 cn/egret/am并且将其命名为image2.jpg
*
* 明确:
* 1、数据源是什么
* 2、数据的目的地是哪里
*
*
* @author egret
*
*/
字符流
使用字节流读取中文文件时,1个中文:
- gbk:占两个字节
- UTF-8:占用3个字节
字符输入流Reader
来自包 java.io.Reader,所有字符输入流的父类
常用方法:
方法 | 说明 |
---|---|
close() | 关闭此流并且释放资源 |
read() | 从输入流中读取一个字符 |
read(char[] ch)) | 从输入流当中读取一些字符,并且将其存入字符数组中去 |
构造方法:
方法 | 说明 |
---|---|
FileReader(File file) | 在给定从中读取数据的 File 的情况下创建一个新 FileReader。 |
FileReader(String fileName) | 在给定从中读取数据的文件名的情况下创建一个新 FileReader。 |
字符输入流使用步骤
-
创建FileReader对象,构造方法绑定要读取到数据源
-
调用read()读取文件
-
释放资源
读取字符:read() ,每次读取一个字符的数据,将其提升为int类型,当读取到文件末尾到时候返回-1。
代码示例:
String fileName = "src/cn/egret/a.txt";
// 1、创建FileReader对象,构造方法绑定要读取到数据源
FileReader fileReader = new FileReader(fileName);
// 2、调用read()读取文件
char[] ch = new char[1024];
int len = 0;//记录每次读取到有效字符个数
while((len = fileReader.read(ch)) != -1) {
/**
* String构造方法
*
* String(char value[])
* String(char value[], int offset, int count)
*
* 参数:
* offset:数组开始索引
* count : 转化的个数
*/
String str = new String(ch,0,len);
System.out.println(str);
}
// 3、释放资源
fileReader.close();
字符输出流Writer
字符输出流:Writer 所有字符输出流的父类
直接已知子类:
- BufferedWriter
- CharArrayWriter
- FilterWriter
- OutputStreamWriter
- PipedWriter
- PrintWriter
- StringWriter
常用共性方法:
方法 | 说明 |
---|---|
void write(char[] cbuf) | 写入字符数组。 |
abstract void write(char[] cbuf, int off, int len) | 写入字符数组的某一部分。 |
void write(int c) | 写入单个字符。 |
void write(String str) | 写入字符串。 |
void write(String str, int off, int len) | 写入字符串的某一部分。 |
flush() | 刷新该流的缓冲。 |
close() | 关闭此流,但要先刷新它。 |
FileWriter:用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
构造方法:
方法 | 说明 |
---|---|
FileWriter(File file) | 根据给定的 File 对象构造一个 FileWriter 对象。 |
FileWriter(String fileName) | 根据给定的文件名构造一个 FileWriter 对象。 |
作用:把内存中的数据写入到文件中去
使用步骤:
-
创建FileWriter对象,构造方法绑定要写出到资源路径
-
调用write()写出数据
-
释放资源
代码示例:
// 1、创建FileWriter对象,构造方法绑定要写出到资源路径
FileWriter fw = new FileWriter("src/cn/egret/a.txt",true);
char[] ch = {'2','0','2','1','j','a','v','a','s','e'};
// 2、调用write()写出数据
fw.write(ch);
System.out.println("写入成功");
/**
* 注意:由于字符输入流内置缓冲区的原因,如果我们并没有关闭流或者刷新资源,则无法将数据写入到文件中
*/
/**
* flush和close的区别
*
* flush:刷新缓冲区,流对象可以继续使用
*
* close:先刷新缓冲区,然后通知系统释放资源,并且流不可以在被使用
*/
fw.flush();
fw.write('刷');
fw.flush();
fw.write('新');
fw.flush();
/**
* 一次性写入多个字符
*/
String str = "javase进阶";
fw.write(str);
fw.flush();
/**
* write(String str, int off, int len)
* off:索引位置
* len:写入个数
*/
fw.write(str, 0, 6);
fw.flush();
for (int i = 0; i < 5; i++) {
fw.write("javase进阶" + (i + 1) + "\r");
}
// 3、释放资源
fw.close();
fw.write('刷'); //java.io.IOException: Stream closed 流已经关闭IO异常
fw.write('新');
转换流
编码:字符(能看的懂得) -> 字节(看不懂的)
解码:字节(看不懂的) -> 字符(能看的懂得)
注意:如果指定了编码。就相当于指定了字符集。所以编码才是我们要关心的内容
gbk 格式进行编码 要想让其显示正确 要用gbk对应字符集进行解码
gbk 格式进行编码 使用utf-8格式进行解码 乱码
字符集(charset):编码表,是系统支持的所有字符的集合,其中包括国家文字,标点符号,图形符号,数字
常见的字符集:
- ASCII字符集
- GBxxx字符集
- GB(国标)
- GB2312:简体中文码表
- GBK :常见的中文码表
- GB18030: 最新的中文码表
- Unicode字符集等
- ISO-8859-1:字符集(拉丁码表)
FileReader:读取默认编码格式文件,读取系统默认的编码(gbk)会产生乱码的情况
注意:gbk存储在电脑上的实际是4个字节,使用utf-8存储实际是6个字节,编码格式不同,占用的大小不同
注意:FileReader只能查询系统的默认码表
思考:查询默认码表的目的? 将字节转化成字符(解码的过程)
使用InputStreamReader读取文件,它能够查询utf-8和指定的编码表
InputStreamReader()是字节通向字符的桥梁
FileWriter 同样只能使用系统默认码表将字符转成字节
OutputStreamWriter() 从字符流到字节流的桥梁
注意:转换流的强大之处在于我们可以指定编码表
InputStreamReader
构造方法:
方法 | 说明 |
---|---|
InputStreamReader(InputStream in) | 创建一个使用默认字符集的 InputStreamReader。 |
InputStreamReader(InputStream in, String charsetName) | 创建使用指定字符集的 InputStreamReader。 |
参数:
- InputStream in :字节输入流,用与读取文件中保存的字节
- String charsetName : 设置表码表
使用步骤:
- 创建InputStreamReader对象,构造方法中传入指定的字节输入流和表码格式
- 使用InputStreamReader对象中read()读取文件
- 释放资源
注意:构造方法中指定的编码表明要和文件编码相同,否则会乱码
代码示例:
FileInputStream fis = new FileInputStream("src/cn/egret/a.txt");
InputStreamReader in = new InputStreamReader(fis,"gbk");
int len = 0;
while ((len = in.read()) != -1) {
System.out.print((char)len);
}
/**
* 注意:后开启的资源先释放
*/
in.close();
fis.close();
OutputStreamWriter
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset,将要写入流中的字符编码成字节。 它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
构造方法:
方法 | 说明 |
---|---|
OutputStreamWriter(OutputStream out) | 创建使用默认字符编码的 OutputStreamWriter。 |
OutputStreamWriter(OutputStream out, String charsetName) | 创建使用指定字符集的OutputStreamWriter。 |
使用步骤:
- 创建OutputStreamWriter对象,构造方法中传递字节输出流和指定编码格式的名称
- 使用write()将数据写入到缓冲区
- 使用flush方法刷新缓冲区将数据写入文件中
- 释放资源
代码示例:
FileOutputStream fos = new FileOutputStream("src/cn/egret/a.txt");
OutputStreamWriter os = new OutputStreamWriter(fos,"gbk");
String str = "你好Javase2101";
os.write(str);
os.flush();
os.close();
fos.close();
字符缓冲流
字符缓冲流:可以传递一个FileWriter/FileReader,缓冲会给FileWriter/FileReader增加一个缓冲区,便于提高读写效率
字符缓存输入流
BufferedReader构造方法:
方法 | 说明 |
---|---|
BufferedReader(Reader in) | 创建一个使用默认大小输入缓冲区的缓冲字符输入流。 |
BufferedReader(Reader in, int sz) | 创建一个使用指定大小输入缓冲区的缓冲字符输入流。 |
参数:
- Reader in:字符输入流
- int sz : 指定缓冲区大小,如果不写的话使用默认大小
字符缓存输出流
BufferedWriter构造方法:
方法 | 说明 |
---|---|
BufferedWriter(Writer out) | 创建一个使用默认大小输出缓冲区的缓冲字符输出流。 |
BufferedWriter(Writer out, int sz) | 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 |
参数:
- Writer out:字符输出流
- int sz : 指定缓冲区大小,如果不写的话使用默认大小
特有方法:
-
newLine():写入一个行分隔符,会根据不同的操作系统,获取到不同到行分隔符; :
使用步骤
- 创建字符缓冲输入/输出流,构造方法中传递对应的输入/输出流
- 使用write/read方法,读取指定文件的数据/将数据写入到指定到文件中
- 如果是写操作的话,调用flush方法刷新缓冲区,如过是读操作则忽略该步骤
- 释放资源
代码示例:
---------------------------------写------------------------------------------
// 1、创建字符缓冲输入/输出流,构造方法中传递对应的输入/输出流
FileOutputStream fos = new FileOutputStream("src/cn/egret/a.txt");
OutputStreamWriter ow = new OutputStreamWriter(fos,"gbk");
BufferedWriter bw = new BufferedWriter(ow);
for (int i = 0; i < 5; i++) {
String str = "这个是使用gbk格式写入的文件数据";
bw.write(str + (i+1));
// bw.write("\r");
bw.newLine();
}
bw.flush();
bw.close();
ow.close();
fos.close();
--------------------------------读------------------------------------------
FileInputStream fis = new FileInputStream("src/cn/egret/a.txt");
InputStreamReader ir = new InputStreamReader(fis,"gbk");
BufferedReader br = new BufferedReader(ir);
char[] ch = new char[1024];
/**
* readLine(): 读取一个文本行。
*/
String readLine = null;
while((readLine = br.readLine()) != null) {
System.out.println(readLine + "\r");
}
br.close();
ir.close();
fis.close();
小练习:
/**
* 需求:
* 对文本的内容进行排序,按照(1,2,3...)顺序进行排序
*
* 分析:
* 1、创建map集合用来存放文件
* key : 可以存放文本序号(1,2,3)
* value: 存放每行文本的内容
* 2、创建字符缓冲输入流对象,构造方法中绑定字符输入流
* 3、创建字符缓存输出流对象,构造方法中绑定字符输出流
* 4、使用字符缓冲输入流的方法readLine()逐行读取文本数据
* 5、对读取到的文件进行切割操作,获取文本序号和文本内容
* 6、将切割好的序号和内容存入集合中(key是有序的,所以会自动排序)
* 7、遍历每个键值对
* 8、将每个键值对拼接成一个文本行
* 9、将拼接好的数据写入到指定文件当中
* 10、释放资源
*/
// 1、创建map集合用来存放文件key :可以存放文本序号(1, 2, 3) value: 存放每行文本的内容
Map<String,String> map = new HashMap<String,String>();
// 2、创建字符缓冲输入流对象,构造方法中绑定字符输入流
FileInputStream fis = new FileInputStream("src/cn/egret/a.txt");
InputStreamReader ir = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(ir);
// 3、创建字符缓存输出流对象,构造方法中绑定字符输出流
File0utputStream fos = new File0utputStream("src/cn/egret/b.txt");
OutputStreamWriter ow = new 0utputStreamWriter(fos);
BufferedWriter bw = new BufferedWriter(ow);
//4、使用字符缓冲输入流的方法readLine()逐行读取文本数据
String readLine = null;
while((readLine = br.readLine()) != null) {
//5、对读取到的文件进行切割操作,获取文本序号和文本内容
//1.asdsasdsa
String[] split = readLine.split("\\.");
// 6、将切割好的序号和内容存入集合中(key是有序的, 所以会自动排序)
map.put(split[0],split[1]); //split[0]: 序号 split[1]: 文本内容
}
//7、遍历每个键值对
for (String key : map.ketSet()) {
String value = map.get(key);
//8、将每个键值对拼接成一个文本行
readLine = key + "." + value; .
//9、将拼接好的数据写入到指定文件当中
bw.write(readLine);
bw.newLine();//换行
}
//10、释放资源
bw.close();
0W.close();
fos.close( );
br.close();
ir.close();
fis.close();
对象流
对象序列化流
Person p = new Person("Egret",3);
把对象用流的形式存放文件当中进行保存,可以叫做写对象,也可以称之为对象的序列化
注意:对象中可能不仅仅包含字符,还有字节。需要使用字节流进行对象的序列化
对象的序列化流:ObjectOutputStream
注意:硬盘中保存的对象都是字节
java.io.ObjectOutputStream:将Java对象的原始数据类型写入到文件当中去,实现对象持久存储
类别 | 方法 | 说明 |
---|---|---|
构造方法 | ObjectOutputStream(OutputStream out) | 创建写入指定 OutputStream 的 ObjectOutputStream。 |
特有的成员方法 | writeObject(Object obj) | 将指定的对象写入 ObjectOutputStream。 |
使用步骤:
- 创建一个ObjectOutputStream对象,构造方法中传递字节输出流
- 使用ObjectOutputStream对象中特有的成员方法writeObject,将对象写入到文件当中
- 释放资源
注意:java.io.NotSerializableException: cn.egret.am.Person
-
序列化和反序列化的时候如果对象没有实现Serializable接口,则会抛出该异常:没有序列化异常
对象序列化需要的条件
- 实现Serializable
- 该类的所有属性都必须是可序列化的,如果该类中有一个属性不需要被序列化,则需要表明这个属性是瞬态的,必须用transient关键字修饰
对象的反序列化流:ObjectInputStream
作用:将文件中保存的对象,以流的形式读取出来
思考:写的时候是可以写任意对象的,那么读取对象的时候我们要用什么类型来接受读取到的对象?
答:Object类型
类别 | 方法 | 说明 |
---|---|---|
构造方法 | ObjectInputStream(InputStream in) | 创建从指定 InputStream 读取的 ObjectInputStream。 |
特有的成员方法 | readObject() | 从 ObjectInputStream 读取对象。 |
使用步骤:
- 创建ObjectInputStream对象,构造方法中传递字节输入流
- 使用ObjectInputStream对象中的方法readObject读取保存的对象文件
- 释放资源
- 使用读取出来的文件(打印)
注意:
- 反序列的类必须实现Serializable
- 必须存在类对应的class文件
例子Person类:
public class Person implements Serializable{
private static final long serialVersionUID = -12312312312312L;
/**
* 在实现了Serializable序列化接口中,会要求我们去定义一个静态常量serialVersionUID作为序列化的版本号
*
* serialVersionUID作用:
* 其起到决定当前实例在反序列的时候能否成功,
*
* 当我们使用ObjectOutputStream输出流将对象写入硬盘中的时候,该序列号也会被一起写入
* 我们如果要通过ObjectInputStream输入流进行反序列化的时候
* 会检测当前对象的序列化的版本号和接收对象的序列化版本号是否一致
* 如果一致则反序列化成功,不一致则失败
*
* 在反序列化的过程中,如果版本一致,则采用兼容模式进行还原
*/
public String name;//姓名
public String address;//地址
public Integer age;//年龄
public Person(String name, String address, Integer age) {
super();
this.name = name;
this.address = address;
this.age = age;
}
使用例子:
// 对象序列化方法
public static void write() throws IOException {
// 1、创建一个ObjectOutputStream对象,构造方法中传递字节输出流
FileOutputStream fos = new FileOutputStream("src/cn/egret/am/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
//创建对象
Person person = new Person("张三", "厦门", 18);
// 2、使用ObjectOutputStream对象中特有的成员方法writeObject,将对象写入到文件当中
oos.writeObject(person);
System.out.println("对象序列化成功");
// 3、释放资源
oos.close();
fos.close();
}
//对象反序列化的方法
/**
* @throws IOException
* @throws ClassNotFoundException class文件找不到异常
*/
public static void read() throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("src/cn/egret/am/a.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Object object = ois.readObject();
ois.close();
System.out.println(object);
Person person = (Person) object;
System.out.println(person.getName() + " " + person.getAddress() + " " + person.getAge());
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
write();
read();
}
小练习:
/**
* 序列化集合
* 需求:
* 我们需要在一个文件中存放多个对象的时候,为了节省空间,我们需要将多个对象存储到一个集合当中去
*
* 目的:
* 对集合进行序列化和反序列化操作
*
* 分析:
* 1、定义一个存储对象的集合(ArrayList)
* 2、将对象存储到ArrayList集合中去
* 3、创建一个序列化流ObjectOutputStream对象
* 4、使用ObjectOutputStream中的writeObject方法对集合进行序列化
* 5、创建ObjectInputStream对象
* 6、使用ObjectInputStream中的readObject方法进行反序列化操作
* 7、把Object类型的集合转为ArrayList
* 8、遍历集合
* 9、释放资源
*
*/
public static void method01() throws I0Exception {
// 1、定义一个存储对象的集合(ArrayList)
List list = new ArrayList();
// 2、将对象存储到ArrayList集合中去
Person person1 = new Person("aa","福州",18);
Person person2 = new Person ("bbb","福州",18);
Person person3 = new Person("ccc","福州",18);
list.add(person1);
list.add(person2);
list.add(person3);
// 3、创建一个序列化流0bject0utputStream对象
File0utputStream fos = new File0utputStream("src/cn/egret/person. txt");
0bjectOutputStream out = new 0bject0utputStream(fos);
// 4、使用0bject0utputStream中的write0bject方法对集合进行序列化
out.writeObject(list);
System.out.println("对象序列化成功");
out.close();
fos.close();
}
public static void method02() throws IOException, Clas sNotFoundException {
// 5、创建0bjectInputStream对象
FileInputStream fis = new FileInputStream("src/cn/egret/person.txt");
ObjectInputStream ois = new 0bjectInputStream(fis);
// 6、使用0bjectInputStream中的read0bject方法进行反序列化操作
0bject person = ois.read0bject();
// 7、把0bject类型的集合转为ArrayList
List<Person> list = (List<Person>)person;
// 8、遍历集合
for (int i = 0; i < list.size(); i++) {
0bject object = list.get(i);
System.out.println(object);
}
for (Person person2 : list) {
System.out.println(person2);
}
ois.close();
fis.close();
}