第五章:File类与IO流
第一节:File类
1.1 概述
java.io.File
类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作
Java把电脑中的文件和文件夹(目录)封装为了一个File类,我们可以使用File类对文件和文件夹进行操作
我们可以使用File类的方法
创建一个文件/文件夹
删除文件/文件夹
获取文件/文件夹
判断文件/文件夹是否存在
对文件夹进行遍历
获取文件的大小
File类是一个与系统无关的类,任何操作系统都可以使用这个类中的方法
**重点:**记住这三个单词——file:文件 directory:文件夹/目录 path:路径
1.2 File类的静态成员变量
static String pathSeparator
:与系统有关的路径分隔符,为了方便,它被表示为一个字符串
System.out.println(File.pathSeparator);//路径分隔符 Windows:分号;linux:冒号
-
static char pathSeparatorChar
:与系统有关的路径分隔符 -
static String separator
:与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串
System.out.println(File.separator);
//文件名称分隔符 Windows:反斜杠\ linux:正斜杠/
操作路径:路径不能写死了
C:\develop\a\a.txt Windows
C:/develop/a/a.txt Linux
"C:"+File.separator+"develop"+File.separator+"a"+File.separator+"a.txt"
static char separatorChar
:与系统有关的默认名称分隔符
1.3 绝对路径和相对路径
**绝对路径:**是一个完整的路径,以盘符(C: D:)开始的路径
C:\\Users\itcast\\IdeaProjects\\a\\a.txt
**相对路径:**是一个简化的路径,是相对于当前项目的根目录
如果使用当前项目的根目录,路径可以简化书写
a.txt
(可以省略项目的根目录)
注意:
1、路径是不区分大小写
2、路径中的文件名称分隔符windows使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠
1.3 File类的构造方法
public File(String pathname)
:通过将给定的路径名字符串转换为抽象路径名来创建新的File实例
**String pathname:**字符串的路径名称
路径可以是以文件结尾,也可以是以文件夹结尾;可以是相对路径,也可以是绝对路径;可以存在,也可以不存在。创建File对象,只是把字符串路径封装为File对象,不考虑路径的真假情况
//绝对路径
File f1=new File("D:\\Java\\CG\\38.穆娅.jpg");
System.out.println(f1);//D:\Java\CG\38.穆娅.jpg 重写了Object类的toString方法
//相对路径
File f2=new File("B.txt");
System.out.println(f2);//B.txt
public File(String parent,String child)
:从父路径名字符串和子路径名字符串创建新的File实例
**参数:**把路径分成了两部分
String parent:父路径
String child:子路径
**好处:**父路径和子路径可以单独书写,使用起来非常灵活;父路径和子路径都可以变化
File f3=new File("D:\\Java\\CG\\","38.穆娅.jpg");
System.out.println(f3);//D:\Java\CG\38.穆娅.jpg
public File(File parent,String child)
:从父抽象路径名和子路径名字符串创建新的File实例
参数:把路径分成了两部分
**File parent:**父路径
**String Child:**子路径
好处:
1、父路径和子路径可以单独书写,使用起来非常灵活;父路径和子路径都可以变化
2、父路径是File类型,可以使用File类的方法对路径进行一些操作,再使用路径创建对象
File f4=new File("D:\\Java\\CG\\");
File f5=new File(f4,"38.穆娅.jpg");
System.out.println(f5);//D:\Java\CG\38.穆娅.jpg
1.4 File类的常用方法
获取功能的方法:
public String getAbsolutePath()
:返回此File类的绝对路径名字符串
获取的构造方法中传递的路径,无论路径是绝对的还是相对的,getAbsolutePath方法返回的都 是绝对路径
File f1=new File("D:\\Java\\CG\\38.穆娅.jpg");
System.out.println(f1.getAbsolutePath());//D:\Java\CG\38.穆娅.jpg
File f1=new File("38.穆娅.jpg");
System.out.println(f1.getAbsolutePath());//D:\Java\CG\38.穆娅.jpg
public String getPath()
:将此File转换为路径名字符串
获取的构造方法中传递的路径,但是getPath方法是什么形式的就是什么形式的
File f1=new File("D:\\Java\\CG\\38.穆娅.jpg");
System.out.println(f1.getAbsolutePath());//D:\Java\CG\38.穆娅.jpg
File f1=new File("38.穆娅.jpg");
System.out.println(f1.getPath());//38.穆娅.jpg
public String getName()
:返回由此File表示的文件或目录的名称
获取的就是构造方法传递路径的结尾部分,要么是文件,要么是文件夹
File f1=new File("D:\\Java\\CG\\38.穆娅.jpg");
System.out.println(f1.getName());//38.穆娅.jpg
File f2=new File("D:\\Java\\CG\\");
System.out.println(f2.getName());//CG
public long length()
:返回由File表示的文件的长度
获取的是构造方法指定的文件的大小,以字节为单位
注意:
1、文件夹是没有大小概念的,不能获取文件夹大小
2、如果构造方法中给出的路径不存在或者获取的对象是文件夹,那么length方法的返回值是0
File f3=new File("D:\\Java\\CG\\38.穆娅.jpg");
System.out.println(f3.length());//192798(字节)
判断功能的方法
-
public boolean exists()
:此File表示的文件或目录是否实际存在存在:true 不存在:false
File f1=new File("D:\\Java\\CG\\38.穆娅.jpg");
System.out.println(f1.exists());//true
public boolean isDirectory()
:此File表示的是否为目录
用于判断构造方法中给定的路径是否以文件夹结尾
是:true 否:flase
File f1=new File("D:\\Java\\CG\\38.穆娅.jpg");
File f2=new File("D:\\Java\\CG");
System.out.println(f1.isDirectory());//flase
System.out.println(f2.isDirectory());//true
public boolean isFile()
:此File表示的是否为文件
用于判断构造方法中给定的路径是否以文件结尾
是:true 否:flase
File f1=new File("D:\\Java\\CG\\38.穆娅.jpg");
File f2=new File("D:\\Java\\CG");
System.out.println(f1.isFile());//true
System.out.println(f2.isFile());//false
注意:
电脑的硬盘中只有文件/文件夹,isDirectory与isFile两个方法是互斥的
**isDirectory与isFile的使用前提:**路径必须是存在的,否则都返回flase
File类创建删除功能的方法
public boolean creatNewFile()
:当且仅当该名称的文件尚不存在时,创建一个新的空文件
创建文件的路径和名称在构造方法中给出(构造方法的参数)
返回值:布尔值 true:文件不存在,创建文件,返回true flase:文件存在,不会创建,返回false
注意:
1、此方法只能创建文件,不能创建文件夹
2、创建文件的路径必须存在,否则会抛出异常
File f1=new File("D:\\Java\\CG\\a.txt");
System.out.println(f1.createNewFile());//true creatNewFile声明抛出了IOException,我们调用这个方法,就必须处理这个异常,要么throws,要么trycatch
File f1=new File("D:\\Java\\CG");
System.out.println(f1.createNewFile());//true 没有报错,但是生成了一个不能打开的文件,不要被名称迷惑,要看类型
File f2=new File("D:\\Java1\\CG\\a.txt");
System.out.println(f2.createNewFile());//路径不存在,抛出IOException异常
public boolean delete()
:删除由此File表示的文件或目录
此方法,可以删除构造方法路径中给出的文件/文件夹
返回值:布尔值 true:文件/文件夹删除成功,返回true false:文件夹中有内容,不会删除,返回false;构造方法中路径不存在,返回false
注意:delete方法是直接在硬盘删除文件/文件夹,不走回收站,删除要谨慎
public boolean mkdir
:创建由File表示的目录
System.out.println(f3.delete());//true
创建单级空文件夹
创建文件夹的路径和名称在构造方法中给出(构造方法的参数)
返回值:布尔值 true:文件夹不存在,创建文件夹,返回true flase:文件夹存在,不会创建,返回false,构造方法中给出的路径不存在也返回false
File f2=new File("D:\\Java\\CG\\a");
System.out.println(f2.mkdir());//true
public boolean mkdirs()
:创建由此File表示的目录,包括任何必需但不存在的父目录
既可以创建单级文件夹,也可以创建多级文件夹
路径不存在抛出异常,路径不存在不会创建
File f3=new File("D:\\Java\\CG\\a\\aa\\aaa\\aaaa");
System.out.println(f3.mkdirs());//true
1.5 目录的遍历
public String[] list()
:返回一个String数组,表示该File目录中所有的子文件或目录
遍历构造方法中所给出的目录,会获取目录中所有文件/文件夹的名称,把获取到的多个名称存储到一个String类型的数组中
可以获取到隐藏文件夹
File f1=new File("D:\\Java\\CG");
String[] st=f1.list();
for (String s:st) {
System.out.println(s);
}
public File[] listFiles()
:返回一个File数组,表示该File目录中所有的子文件或目录
遍历构造方法中所给出的目录,会获取目录中所有文件/文件夹,把获取到文件/文件夹封装成为File对象,多个File对象存储到File数组中
File f2=new File("D:\\Java\\CG");
File[] files=f2.listFiles();
for (File file:files) {
System.out.println(file);
}//文件的路径也打印出来了
注意:
list方法和listFiles方法遍历的是构造方法中给出的目录
1、如果构造方法中给出的目录路径不存在,会抛出空指针异常
2、如果构造方法中给出的路径不是一个目录,也会抛出空指针异常
第二节:递归
2.1 概述
**递归:**指在当前方法内调用自己的这种现象
递归的分类:
直接递归:方法调用自己
**间接递归:**可以A方法调用B方法,B方法调用C方法,C方法调用A方法
注意:
1、递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出
2、在递归中虽然有限定条件,但是递归次数不能太多,否则也会发生栈内存溢出
3、构造方法禁止递归:构造方法是创建对象使用的,一直递归会导致内存中有无数个对象,直接编译报错
**使用前提:**当调用方法的时候,方法的主体不变,每次调用方法的参数不同,可以使用递归
栈内存溢出示意图:
第三节:文件过滤器FileFilter
java.io.FileFilter
接口:用于抽象路径名(File对象)的过滤器
**作用:**用来过滤文件
抽象方法:
boolean accept(File pathname)
:测试pathname是否应该包含在当前File目录中,符合则返回true
**参数:**File pathname:使用ListFiles方法遍历目录,得到的每一个文件对象
java.io.FilenameFilter
接口:用于过滤文件名称
作用:用于过滤文件名称
**抽象方法:**用来过滤文件的方法
-
boolean accept(File dir,String name)
:测试指定文件是否应该包含在某一文件列表中参数:File dir :构造方法中传递的被遍历的目录 String name:使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称
注意:两个过滤器结构是没有实现类的,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤的规则
listFiles方法一共做了3件事:
1、listFiles方法会对构造方法中传递的目录进行遍历,获取目录中的每一个文件/文件夹---->封装为File对象
2、listFiles方法会调用参数传递的过滤器中的方法accept
3、listFiles方法会把遍历得到的每一个File对象,传递给accept方法的参数:pathname
accept方法返回值是一个布尔值:
true:就会把传递过去的File对象保存到File数组中
false:就不会把传递过去的File对象保存到File数组中
过滤的规则:
在accept方法中,判断File对象是否以对应字符串结尾,是就返回true,不是就返回false
第三节:IO字节流
3.1 IO概述
数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为输入input
和输出output
,即流向内存是输入流,流出内存是输出流
Java中I/O操作主要是使用java.io
包下的内容,进行输入、输出操作,输入也叫做读取数据,输出也叫做写出数据
3.2 IO的分类
根据数据的流向分为:输入流和输出流
- **输入流:**把数据从
其他设备
上读取到内存
中的流 - **输出流:**把数据从
内存
中写出到其他设备
上的流
格局数据的类型分为:字节流和字符流
顶级父类们:
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流 InputStream | 字节输入流 OutputStream |
字符流 | 字符输入流 Reader | 字符输出流 Writer |
3.3 字节流
3.3.1 一切皆为字节
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都是一个个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据
3.3.2 字节输出流【OutputStream】
java.io.OutputStream
extendsOutputstream
,它是抽象类,是表示字节输入流的所有类的超类,将指定的字节信息写出到目的地。
构造方法:
-
FileOutputStream(File file)
:创建一个向指定File对象表示的文件中写入数据的文件输入流**参数:**写入数据的目的地 File file:目的地是一个文件
-
FileOutputStream(FileDescriptor fdobj)
:创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接 -
FileOutputStream(String name)
:创建一个向具有指定名称的文件中写入数据的输出文件流**参数:**写入数据的目的地 String name:目的地是一个文件的路径
构造方法的作用:
1、创建了一个FileOutStream对象
2、会根据构造方法中传递的文件/文件路径,创建一个空的文件
3、会把FileOutputStream对象指向创建好的文件
它定义了字节输出流的基本共性功能方法,定义了一些子类共性的成员方法
public void close()
:关闭此输出流并释放与此流相关联的任何系统资源public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出public abstract void write(int b)
:将指定的字节输出流
public static void main(String[] args) throws IOException {
FileOutputStream fos=new FileOutputStream("D:\\Java\\CG\\a.txt");
fos.write(97);
fos.write(115);
fos.close();
}//as
写入数据的原理:(内存---->硬盘)
java程序---->JVM(java虚拟机)---->OS(操作系统)----->OS调用写数据的方法----->把数据写入到文件中
字节输出流的使用步骤(重点):
1、创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
2、调用FileOutputStream对象中的方法write,把数据写入到文件中
3、释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序效率)
3.3.3 文件存储的原理和记事本打开文件的原理:
3.3.4 字节输出流写多个字节的方法
public void write(byte[] b)
:将b.length字节从指定的字节数组写入此输入流
**一次写多个字节:**如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表,如果写的第一个字节是负数,那第一个字节会和第二个字节组成一个中文显示,查询系统默认码表(GBK)
FileOutputStream fos=new FileOutputStream(new File("D:\\Java\\CG\\aa.txt"));
byte[] bytes={-65,-67,-68,69};
fos.write(bytes);
fos.close();
public void write(byte[] b,int off,int len)
:从指定的字节数组写入len字节,从偏移量off开始输出到此输入流
把字节数组的一部分写入到文件中,从指定的字节数组写入len字节,从偏移量off开始输出到此输出流
int off:数组的开始索引 **int len:**写几个字节
FileOutputStream fos=new FileOutputStream(new File("D:\\Java\\CG\\aa.txt"));
byte[] bytes={-65,-67,-68,69};
fos.write(bytes,1,2);
fos.close();
**写入字符串的方法:**可以使用String类中的方法.getBytes()
把字符串转换为字节数组
3.3.5 字节输出流的续写与换行
**追加写/续写:**使用两个参数的构造方法
-
FileOutputStream(String name,boolean append)
:创建一个具有指定name的文件中写入数据的输出文件流 -
FileOutputStream(File file,boolean append)
:创建一个向指定File对象文件中写入数据的文件输出流参数: String name,File file:写入数据的目的地
boolean append:追加写开关 true:创建对象不会覆盖原文件,继续在文件的末 尾追加写数据 false:创建一个新文件,覆盖源文件
**写换行:**写换行符号
windows:\r\n Linux:/n mac:/r
public static void main(String[] args) throws IOException {
FileOutputStream fos=new FileOutputStream("D:\\Java\\CG\\a.txt",true);//续写
for (int i = 0; i <10 ; i++) {
fos.write("你好\r\n".getBytes(StandardCharsets.UTF_8));//换行写
}
fos.close();
}
3.3.6 字节输入流【InputStream】
java.io.InputStream
extends InputStream
抽象类是表示字节输入流所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法
**作用:**把硬件文件中的数据读取到内存中使用
构造方法:
-
FileInputStream(String name)
-
FileInputStream(File file)
**参数:**读取文件的数据源 String name:文件的路径 File file:文件
构造方法的作用:
1、会创建一个FileInputString对象
2、会把FileInputStream对象指向构造方法中要读取的文件
3.3.7 字节输入流读取字节数据
读取数据的原理(硬盘---->内存):
Java程序-----JVM----->OS----->OS读取数据的方法----->读取文件
字节输入流的使用步骤:
1、创建一个FileInputStream对象,构造方法中绑定要读取的数据源
2、使用FileInputStream对象中的方法read,读取文件
3、释放资源
public void close()
:关闭输入流并释放与此流相关联的任何系统资源public abstract int read()
:从输入流中读取数据的下一个字节
遍历一个文本文件的所有内容:
FileInputStream fis=new FileInputStream("D:\\Java\\CG\\a.txt");
int s=0;
while((s=fis.read())!=-1) {
System.out.println((char)s);
}
fis.close();
布尔表达式:(s-fis.read()!=-1)
1、fisread():读取一个字节
2、s=fis.read():把读取的字节赋给变量s
3、(s-fis.read())!=1:判断变量s是否不等于-1
3.3.8 字节输入流一次读取一个字节的原理
3.3.9 字节输入流一次读取多个字节
public int read(byte[] b)
:从输入流中读取一些字节流,并把它存储到字节数组b中
String类的构造方法:
**String(byte[] bytes):**把字节数组转换为字符串
String(byte[] bytes,int offset,int length):把字节数组的一部分转换为字符串
offset:数组的开始索引 length:转换的字节个数
byte[]的作用:起到缓冲的作用,存储每次读取到的多个字节,数组的长度一般定义为1024(1kb)或者1024的整数倍
方法的返回值int是:每次读取的有效字节个数
FileInputStream fis=new FileInputStream("D:\\Java\\CG\\aa.txt");
byte[] bytes=new byte[2];
fis.read(bytes);
System.out.println(Arrays.toString(bytes));//[97,115]
System.out.println(new String(bytes));//as
fis.close();
一次读取多个字节的原理:
第四节:IO字符流
当使用字节流读取文本文件时,可能会有一个小问题,就是遇到中文时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件
4.1 字符输入流【Reader】
java.io.Reader
抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
java.io.FileReader
extends InputStreamReader
extends Reader
FireReader:文件字符输入流
作用:把硬盘文件中的数据以字符的方式读入到内存中
构造方法:
FileReader(String fileName)
FileReader(File file)
**参数:**读取文件的数据源
String fileName:文件的路径 **File file:**一个文件
FileReader构造方法的作用:
1、会创建一个FileReader对象
2、会把FileReader对象指向要读取的文件
4.2 字符输入流读取字符数据
字符输入流的使用步骤:
1、创建FileReader对象,构造方法中绑定要读取的数据源
2、使用FileReader对象中的方法read读取文件
3、释放资源
public void close()
:关闭此流并释放与此流相关联的任何系统资源public int read()
:从输入流读取一个字符
FileReader fr=new FileReader("D:\\Java\\CG\\aa.txt");
int s=0;
while((s=fr.read())!=-1) {
System.out.print((char)s);
}
fr.close();
public int read(char[] cbuf)
:从输入流中读取一些字符,并将他们存储到字符数组cbuf中
FileReader fr=new FileReader("D:\\Java\\CG\\aa.txt");
int s=0;
char[] chars=new char[1024];
while((s=fr.read(chars))!=-1)
System.out.println(new String(chars,0,s));
fr.close();
4.3 字符输出流【Writer】
java.io.Writer
抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法
java.io.FileWriter
extends OutputStreamWriter
extends Writer
**FileWriter:**文件字符输出流
**作用:**把内存中字符数据写入到文件中
构造方法:
-
FileWriter(File file)
:根据给定的File对象构造一个FileWriter对象 -
FileWriter(String fileName)
:根据给定的文件名构造一个FileWriter对象**参数:**写入数据的目的地
**String fileName:**文件的路径 **File file:**是一个文件
构造方法的作用:
1、会创建一个FileWriter对象
2、会根据构造方法中传递的文件/文件的路径,创建文件
3、会把FileWriter对象指向创建好的文件
成员方法:
void write(int c)
:写入单个字符void flush()
:刷新该流的缓冲void close()
:关闭此流,但要先刷新它
FileWriter fw=new FileWriter("D:\\Java\\CG\\a.txt");
fw.write(97);
fw.flush();
fw.close();
void write(char[] cbuf)
:写入字符数组
FileWriter fw=new FileWriter("D:\\Java\\CG\\b.txt");
char[] chars={'a','b','c','d','e'};
fw.write(chars);
fw.close();
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是写的字符个数
FileWriter fw=new FileWriter("D:\\Java\\CG\\b.txt");
char[] chars={'a','b','c','d','e'};
fw.write(chars);
fw.write(chars,1,3);
fw.write("你好你好!");
fw.write("你好你好",0,3);
fw.close();
字符输出流的使用步骤:
1、创建一个FileWriter对象,构造方法中绑定要写入数据的目的地
2、使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
3、使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
4、释放资源(会先把内存缓冲区中的数据刷新到文件中)
关闭和刷新:
因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中,但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要使用flush方法
flush
:刷新缓冲区,流对象可以继续使用close
:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了
字符输出流的续写和换行:
**续写和换行:**类似于
**续写:**追加写:使用两个参数的构造方法
-
FileWriter(String fileName,boolean append)
-
FileWriter(File file,boolean append)
参数:
**String fileName,File file:**写入数据的目的地
boolean append:续写的开关,true:不会创建新文件覆盖原文件;false:创建新的文件覆盖原文件
**换行:**换行符号
windows:\r\n linux:\n mac:\r
第五节:IO 异常处理
5.1 使用try catch finally处理流中的异常
在JDK 1.7之前使用try catch finally 处理流中的异常
格式:
try{
可能产生异常的代码
}catch(异常类的变量 变量名){
异常的处理逻辑
}finally{
一定会执行的代码
资源释放
}
例子:
public class IO_try_catch {
public static void main(String[] args) {
//提高fw的作用域,让finally可以使用
//变量在定义的时候可以没有值,但是使用的时候必须有值
//fw=new FileWriter("D:\\Java\\CG\\bb.txt");执行失败,fw没有值,fw.close会报错
FileWriter fw = null;
try {
//可能会产生异常的代码
fw = new FileWriter("D:\\Java\\CG\\bb.txt");
for (int i = 0; i < 10; i++) {
fw.write("Hello World" + i + "\r\n");
}
} catch (IOException e) {
//异常的处理逻辑
System.out.println(e);
} finally {
//一定会执行的代码
//创建对象失败了,fw的默认值是NULL,null是不能调用方法的,会抛出空指针异常,需要加一个判断,不是null再把资源释放
if (fw != null) {
try {
//fw.close方法声明抛出了IOException异常对象,所以我们就得处理这个异常对象,要么throws
// ,要么try catch
fw.close();
} catch (IOException e) {
System.out.println(e);
} finally {
}
}
}
}
}
5.2 JDK7和JDK9流中异常的处理
**JDK7 的新特性:**在try的后边可以增加一个(),在括号中可以定义流对象,那么这个流对象的作用域就在try中有效,try中的代码执行完毕,会自动把流对象释放,不用写finally
格式:
try(定义流对象;定义流对象...){
可能会产生异常的代码
}catch(异常类变量 变量名){
异常的处理逻辑
}
例子:
public class JDK_7 {
public static void main(String[] args) {
try(
FileWriter fw=new FileWriter("D:\\Java\\CG\\bbb.txt");
FileReader fr=new FileReader("D:\\Java\\CG\\bbb.txt");
){
fw.write("Are you OK!\n\r");
fw.close();
int s=0;
char[] chars=new char[1024];
while((s=fr.read(chars))!=-1)
System.out.println(new String(chars,0,s));
fr.close();
}
catch (IOException e){
System.out.println(e);
}
}
}
JDK 9的新特性:
try的前边可以定义流对象,在try后边的()中可以直接引入流名称(变量名),在try代码执行完毕后,流对象也可以释放掉,不用写finally
格式:
A a=new A();
B b=new B();
try(a;b){
可能会产生异常的代码
}catch(异常类变量 变量名){
异常的处理逻辑
}
例子:
public class JDK_9 {
public static void main(String[] args) throws IOException {
FileReader fr=new FileReader("D:\\Java\\CG\\c.txt");
FileWriter fw=new FileWriter("D:\\Java\\CG\\c.txt");
try(fr;fw){
fw.write("Hello \n\rWord!");
int s=0;
char[] chars=new char[1024];
while((s=fr.read(chars))!=-1)
System.out.println(new String(chars,0,s));
}
catch (IOException e){
System.out.println(e);
}
}
}
9不如7方便
第六节:Properties 属性集
6.1 概述
java.util.Properties
extends Hashtable<k,v>
implents Map<k,v>
,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多java类使用,比如获取系统属性时,System.getProperties
方法就是返回一个Properties
对象
它是唯一一个和IO流相结合的集合,是一个双列集合,key和value默认都是字符串,有一些操作字符串的特有方法
功能:
1、可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
2、可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
6.2 Properties类
构造方法
public Properties()
:创建一个空的属性列表
Properties prop=new Properties();
基本的存储方法
public Object setProperty(String key,String value)
:保存一对属性
prop.setProperty("张三","168");
public String getProperty(String key)
:通过key,找到value值,此方法相当于Map集合中的get(key)方法public Set<String> stringPropertyNames()
:返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keySet方法
Set<String> set=prop.stringPropertyNames();
for (String s:set) {
System.out.println(s+" "+prop.getProperty(s));
}
李四 178
张三 168
王五 188
6.3 Properties集合中的方法store
public void store(OutputStream out,String comments)
public void store(Writer writer,String comments)
参数:
**OutputStream out:**字节输出流,不能写入中文
Writer writer:字符输出流,可以写中文
String comments:注释,用来解释说明保存的文件是做什么的。不能使用中文,会产生乱码,默认是Unicode编码,一般使用""空字符串
使用步骤:
1、创建一个Properties集合对象,添加数据
2、创建字节输出流/字符输出流,构造方法中绑定要输出的目的地
3、使用Properties集合中的方法store,把集合中的临时数据持久化写入到硬盘中存储
4、释放资源
prop.store(new FileWriter("D:\\Java\\CG\\bbb.txt"),"");
FileWriter fw=new FileWriter("D:\\Java\\CG\\ccc.txt");
prop.store(fw,"");
fw.close();
6.4 Properties集合中的方法load
public void load(InputStream inStream)
public void load(Reader reader)
参数:
**InputStream inStream:**字节输入流,不能读取含有中文的键值对
**Reader reader:**字符输入流,能读取包含中文的键值对
使用步骤:
1、创建Properties集合对象
2、使用Properties集合对象中的方法load读取保存键值对的文件
3、遍历Properties集合
注意:
1、存储键值对的文件中,键与值默认的连接符号可以使用=,空格(其他符号)
2、存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
3、存储键值对的文件中,键与值默认都是字符串,不用再加引号
第七节:缓冲流
7.1 概述
缓冲流,也叫高效流,是对4个基本的FileXxx
流的增强,所以也是4个流,按照数据类型分类:
- 字节缓冲流:
BufferedInputStream
,BufferedOutputStream
- 字符缓冲流:
BufferedReader
,BufferedWriter
缓冲流的基本原理,是在创建对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BZSwqWpz-1611640905198)(图片/34.png)]
7.2 字节缓冲流
字节缓冲输出流
java.io.BufferedOutputStream
extends OutputStream
构造方法:
public BufferedOutputStream(OutputStream out)
:创建一个新的缓冲输出流,以将数据写入指定的底层输出流public BufferedOutputStream(OutputStream out,int size)
:创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
参数:
OutputStream out:字节输出流。我们可以传递FileOutputStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率
int size:指定缓冲流内部缓冲区的大小,不指定则默认
使用步骤:
1、创建一个FileOutputStream对象,构造方法中绑定要输出的目的地
2、创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FileOutputStream对象效率
3、使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
4、使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据刷新到文件中
5、释放资源(会先调用flush方法刷新数据,第4步可省略)
public class BufferedOutputStream_Creat {
public static void main(String[] args) throws IOException {
FileOutputStream fos=new FileOutputStream("D:\\Java\\CG\\a.txt");
BufferedOutputStream bos=new BufferedOutputStream(fos);
bos.write("你好妈麻马骂麻麻".getBytes(StandardCharsets.UTF_8));
bos.flush();
bos.close();
}
}
字节缓冲输入流
java.io.BufferedInputStream
extendsInputStream
构造方法:
public BufferedInputStream(InputStream in)
:创建一个BufferedInputStream并保存其参数,即输入流in,以便将来使用public BufferedInputStream(InputStream in,int size)
创建具有指定缓冲区大小的BufferedInputStream并保存其参数,即输入流
参数:
**InputStream in:**字节输入流。我们可以传递FileInputStream,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率
**int size:**指定缓冲流内部缓冲区的大小,不指定则默认
使用步骤:
1、创建一个FileInputStream对象,构造方法中绑定要输出的目的地
2、创建BufferedInputStream对象,构造方法中传递FileOutputStream对象,提高FileInputStream对象效率
3、使用BufferedInputStream对象中的方法read,读取文件
4、释放资源
public class BufferedInputStream_Creat {
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("D:\\Java\\CG\\a.txt");
BufferedInputStream bis=new BufferedInputStream(fis);
byte[] bytes=new byte[1024];
int len=0;
while ((len=bis.read(bytes))!=-1)
System.out.println(new String(bytes,0,len));
bis.close();
}
}
7.3 字符缓冲流
字符缓冲输出流
java.io.BufferedWriter
extends Writer
构造方法:
public BufferedWriter(Writer out)
:创建一个使用默认大小输出缓冲区的缓冲字符流public BufferedWriter(Writer out,int size)
:创建一个使用给定大小输出缓冲区的新缓冲字符输出流
参数:
**Writer out:**字符输出流。我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高FileWriter的写入效率
**int size:**指定缓冲区的大小,不写默认大小
特有的成员方法:
public void newLine()
:写出一个行分隔符,由系统属性定义符号。根据不同的操作系统写出不同的行分隔符
使用步骤:
1、创建字符缓冲输出流对象,构造方法中传递字符输出流
2、调用字符缓冲输出流的方法write,把数据写入到内存缓冲区中
3、调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
4、释放资源
public class BufferedWriter_Creat {
public static void main(String[] args) throws IOException {
BufferedWriter bw=new BufferedWriter(new FileWriter("D:\\Java\\CG\\a" +
".txt"));
for (int i = 0; i <10 ; i++) {
bw.write("你好你好");
bw.newLine();
}
bw.flush();
bw.close();
}
}
字符缓冲输入流
java.io.BufferedReader
extends Reader
构造方法:
public BufferedReader(Reader in)
:创建一个使用默认大小输入缓冲区的缓冲字符输入流public BufferedReader(Reader in,int size)
:创建一个使用指定大小输入缓冲区的缓冲字符输入流
参数:
**Reader in:**字符输入流。我们可以传递FileReader,缓冲流会给FileReader增加一个缓冲区,提高FileReader的写入效率
**int size:**指定缓冲区的大小,不写默认大小
特有的成员方法:
public String readLine()
:读一行数据
行的终止符号:
换行:\n 回车:\r 或者 回车后直接跟着换行(\r\n)
返回值:包含该行内容的字符串,不包含任何终止符,如果已经到达流末尾,则返回null
使用步骤:
1、创建字符缓冲输入流对象,构造方法中传递字符输入流
2、调用字符缓冲输入流的方法read/readLine,读取文本
3、释放资源
public class BufferedReader_Creat {
public static void main(String[] args) throws IOException {
BufferedReader brd=new BufferedReader(new FileReader("D:\\Java\\CG\\a" +
".txt"));
String s;
while((s=brd.readLine())!=null)
System.out.println(s);
brd.close();
}
第八节:转换流
8.1 字符编码和字符集
字符编码:
计算机中存储的信息都是用二进制表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符都是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象
**编码:**字符(能看懂的)—>字节(看不懂的)
**解码:**字节(看不懂的)---->字符(能看懂的)
-
字符编码
Character Encoding
:就是一套自然语言的字符与二进制数之间的对应规则。编码表:生活中文字和计算机中二进制的对应规则
字符集:
- 字符集
Charset
:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
计算机要准确存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见的字符集有ACCII字符集、GBK字符集、Unicode字符集等
8.2 编码引出的问题
在IDEA中,使用FileReader
读取项目中的文本文件。由于IDEA的设置,默认都是UTF-8
编码,所以没有任何问题。但是,当读取Windows系统中创建的文本文件时,由于Windows系统的默认是GBK编码,就会出现乱码
8.3 转换流的原理
InputStreamReader
原理图:
OutputStreamWriter
原理图:
8.4 OutputStreamWriter类
java.io.OutputStreamWriter
extends Writer
OutputStreamWriter
是字符流通向字节流的桥梁,可使用指定的charset
将要写入流中的字符编码成字节。(编码)它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
构造方法:
OutputStreamWriter(OutputStream out)
:创建使用默认编码集的OutputStreamWriterOutputStreamWriter(OutputStream out,String charsetName)
:创建使用指定编码集的OutputStreamWriter
参数:
OutputStream out:字节输出流,可以用来写转换之后的字节到文件中
String charsetName:指定的编码表名称,可以是utf-8/UTF-8,gbk/GBK,…不指定默认使用UTF-8
使用步骤:
1、创建一个OutputStreamWriter
对象,构造方法中传递字节输出流和指定的编码表名称
2、使用OutputStreamWriter
对象中的方法write,把字符转换为字节存储存储到缓冲区中(编码)
3、使用OutputStreamWriter
对象中的方法flush,把内存缓冲区的字节刷新到文件中(使用字节流写字节的过程)
4、释放资源
8.5 InputStreamReader类
转换流java.io.InputStreamReader
,是Reader的子类,是从字节流通向字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符(解码)。它的字符集可以由名称指定,也可以接受平台的默认字符集
构造方法:
InputStreamReader(InputStream in)
:创建一个使用默认字符集的字节流InputStreamReader(InputStream in,String charsetName)
:创建一个指定字符集的字符流
参数:
InputStream in:字节输入流,用来读取文件中保存的字节
String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,…不指定默认使用UTF-8
使用步骤:
1、创建一个InputStreamWriter
对象,构造方法中传递字节输入流和指定的编码表名称
2、使用InputStreamWriter
对象中的方法read,读取文件
3、释放资源
**注意:**构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码
public class OutputStreamWriter_Creat {
public static void main(String[] args) throws IOException {
FileOutputStream fps=new FileOutputStream("D:\\Java\\CG\\d.txt");
OutputStreamWriter osw=new OutputStreamWriter(fps,"GBK");
osw.write("你好!");
osw.flush();
FileInputStream fis=new FileInputStream("D:\\Java\\CG\\d.txt");
InputStreamReader isr=new InputStreamReader(fis,"GBK");
char[] chars=new char[10];
int s=0;
while((s=isr.read(chars))!=-1)
System.out.println(new String(chars,0,s));
isr.close();
osw.close();
}
}
第九节: 序列化流
9.1 概述
**对象的序列化:**把对象以流的方式,写入到文件中保存,叫写对象,也叫对象的序列化
对象中包含的不仅仅是字符,所以使用字节流
ObjectOutputStream
:对象的序列化流
**对象的反序列化:**把文件中保存的对象,以流的方式读取出来,叫做读对象,也叫做对象的反序列化
读取的文件保存的都是字节,使用字节流
ObjectInputStream
:对象的反序列化流
注意:
序列化和反序列化的时候,会抛出NotSerializableException
没有序列化异常,类通过实现java.io.Serializable
接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
Serializable
接口也叫做标记型接口,要进行序列化和反序列化的类必须实现Serializable
接口,就会给类添加一个标记,当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记,如果有:可以序列化和反序列化 如果没有:就会抛出NotSerializableException
没有序列化异常
9.2 ObjectOutputStream类
java.io.ObjectOutputStream
extends OutputStream
类,将Java对象的原始数据类型写出到文件,实现对象的持久存储
构造方法:
public ObjectOutputStream(OutputStream out)
:创建写入指定OutputStream
的ObjectOutputStream
参数:
ObjectOutputStream out
:字节输出流
特有的成员方法:
public void writeObject(Object obj)
:将指定的对象写入ObjectOutputStream
使用步骤:
1、创建ObjectOutputStream
对象,构造方法中传递字节输出流
2、使用ObjectOutputStream
对象中的方法writeObject
,把对象写入到文件中
3、释放资源
public class ObjectOutputStream_Creat {
public static void main(String[] args) throws IOException {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:\\Java\\CG\\a.txt"));
oos.writeObject(new Person("张三",18));
oos.close();
}
}
9.3 ObjectInputStream类
ObjectInputStream
反序列化流,将之前使用ObjectInputStream
序列化的原始数据恢复为对象
java.io.ObjectInputStream
extends InputStream
**作用:**把文件中保存的对象,以流的方式读取出来使用
构造方法:
public ObjectInputStream(InputStream in)
:创建一个指定InputStream
的ObjectInputStream
参数:
InputStream in
:字节输入流
特有的成员方法:
public Object readObject()
:从ObjectInputStream
读取对象
使用步骤:
1、创建ObjectInputStream
对象,构造方法中传递字节输入流
2、使用ObjectInputStream
对象中的方法readObject
读取保存对象的文件
3、释放资源
4、使用读取出来的对象
反序列化的前提:
1、类必须实现Serializable
接口
2、必须存在对应的class文件
public class ObjectInputStream_Creat {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:\\Java\\CG\\a.txt"));
Object one=ois.readObject();
ois.close();
System.out.println(one);
}
}
9.4 瞬态关键字 transient
static关键字:静态关键字
静态优先于非静态加载到内容中(静态化优先于对象进入到内存中)
被static修饰的成员变量不能被序列化的,序列化的都是对象
transient关键字:瞬态关键字
被transient修饰的成员变量,不能被序列化
private transient int age;//age输出为0
9.5 InvalidClassException异常
当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException
异常。发生这个异常的原因如下:
- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
- 该类包含未知数据类型
- 该类没有可访问的无参数构造方法
Serializable
接口给需要序列化的类提供了一个序列版本号。serialVersionUID
该版本号的目的用于验证序列化的对象和对应类是否版本匹配
异常原理图:
解决方案:
序列化类可以通过声明为serialVersionUID
的字段(该字段必须是静态(static)、最终(final)的long型字段),显式声明自己的serialVersionUID
private static final long serialVersionUID=1L;
第十节:打印流
10.1 概述
平时我们在控制台打印输出,是调用print
方法和println
方法完成的,这两个方法都来自java.io.PrintStream
类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式
10.2 PrintStream类
PrintStream
extends OutputStream
特点:
1、只负责数据的输出,不负责数据的读取
2、与其他输出流不同,PrintStream
永远不会抛出IOException
3、有特有的方法,print,println
public void print(任意类型的值)
public void println(任意类型的值并换行)
构造方法:
-
public PrintStream(File file)
:输出的目的地是一个文件 -
public PrintStream(OutputStream out)
:输出的目的地是一个字节输出流 -
public PrintStream(String fileName)
:输出的目的地是一个文件路径
**注意:**如果使用继承来自父类的write方法写数据,那么查看数据的时候会查询编码表 97–>a
如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97—>97
public class PrintStream_Creat {
public static void main(String[] args) throws FileNotFoundException {
PrintStream ps=new PrintStream("D:\\Java\\CG\\e.txt");
ps.write(97);
ps.print(97);
ps.print(97);
ps.println(97);
ps.println(97);
ps.close();
}
}
a979797
97
改变输出语句的目的地(打印流的流向)
输出语句默认在控制台上输出
使用System.setOut(PrintStream out)
方法改变输出语句的目的地改为参数中传递的打印流的目的地
public class PrintStream_setOut {
public static void main(String[] args) throws FileNotFoundException {
System.out.println("我在控制台输出");
PrintStream out=System.out;//保存最原始的输出流
PrintStream ps=new PrintStream("D:\\Java\\CG\\目的地是打印流.txt");
System.setOut(ps);
System.out.println("我在打印流的目的地输出");
System.setOut(out);
System.out.println("我又恢复在控制台上打印");
}
}