1 异常
概述:程序出现不正常的情况。
分类:
-
Throwable:类, 异常的最顶层类, 所有的异常都是它子类.
-
Error:表示错误, 例如: 服务器宕机, 数据库崩溃, 这些异常一般和我们的代码没关系, 也不需要我们(程序员)处理。
-
Exception: 表示异常, 这个才是我们常说的异常.
编译期异常: 发生在编译期间的异常, 不处理, 程序编译通不过。非RuntimeException及其子类, 都叫: 编译期异常。
运行时异常: 发生在运行期间的异常, 不处理, 程序也能通过编译。指的是RuntimeException及其子类。
JVM的默认处理异常的方式:抛出异常(即: throws),把异常的类型, 原因, 位置直接打印到控制台上, 后边的代码是不能执行的。
处理异常的方式:
-
try.catch.finally
格式:
try{
//可能出问题的代码
} catch(Exception e) {
//走这里, 说明程序出现异常了, 所以这里写的基本上都是解决方案.
e.printStackTrace(); //该方法会将异常的类型, 描述信息及位置打印到控制台上.
} finally {
//这里的代码正常情况下永远都会执行, 一般是用来释放资源的.
}特点:处理完后, 程序会继续向下执行。
-
声明抛出异常
格式:throws 异常的类型; //该内容是写到方法的形参列表之前的。
特点:处理完异常代码后, 程序会终止执行。
重点:
- finally里边的代码正常情况下永远都会执行
- 前边如果没有return, 省不省略finally没有区别,但如果前边有return, 省略finally后, 可能会导致后续代码不执行。
2 File类
概述:
- 它是文件和目录路径名的抽象表示
- 文件和目录是可以通过File封装成对象的
- 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名(它可以是存在的,也可以是不存在的),将来是要通过具体的操作把这个路径的内容转换为具体存在的。
构造方法:
方法名 | 说明 |
---|---|
File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 |
File(String parent, String child) | 从父路径名字符串和子路径名字符串创建新的 File实例 |
File(File parent, String child) | 从父抽象路径名和子路径名字符串创建新的 File实例 |
注意:
- 上述三种形式创建的文件(夹), 结果都是一样的.
- 之所以提供这么多的创建File对象的形式, 就是为了满足用户灵活多变的需求.
- 盘符不区分大小写
- 因为\在Java中表示转移符, 所以如果写 右斜线需要写两个, 即: \, 左斜线写一个就行了.
File类创建功能:
方法名 | 说明 |
---|---|
public boolean createNewFile() | 若具有该名称的文件不存在,创建一个由该抽象路径名的新空文件 |
public boolean mkdir() | 创建单级文件夹 |
public boolean mkdirs() | 创建多级文件夹 |
方法调用方式:File(自定义文件类).createNewFile();
File类判断:
方法名 | 说明 |
---|---|
public boolean isDirectory() | 测试此抽象路径名表示的File是否为目录 |
public boolean isFile() | 测试此抽象路径名表示的File是否为文件 |
public boolean exists() | 测试此抽象路径名表示的File是否存在 |
File类获取功能:
方法名 | 说明 |
---|---|
public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 |
public String getPath() | 将此抽象路径名转换为路径名字符串 |
public String getName() | 返回由此抽象路径名表示的文件或目录的名称 |
public String[] list() | 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组 |
public File[] listFiles() | 返回此抽象路径名表示的目录中的文件和目录的File对象数组 |
File类删除功能:
方法名 | 说明 |
---|---|
public boolean delete() | 删除由此抽象路径名表示的文件或目录 |
注意:
- 如果是删除文件夹, 则该文件夹必须为空文件夹.
- Java中的删除不走回收站
3 IO流
概述:处理设备间的数据传输问题的。
应用场景:文件复制、文件上传、文件下载。
分类:
-
流向分
输入流: 读取数据
输出流: 写入数据.
-
操作分
字节流: 以字节为单位来操作数据,能操作所有的文件类型。
字符流: 以字符为单位来操作数据,只能操作文本文件。
3.1 字节输出流
概述:字节输出流指的是OutputStream, 表示以字节为单位往文件中写入数据, 但是它是一个抽象类, 所以我们一般都是用它的子类。
子类:
- FileOutputStream: 普通的字节输出流.
- BufferedOutputStream:高效的字节输出流
3.1.1 FileOutputStream
概述:表示普通的字节输出流, 用来以字节的形式将数据写入到指定的文件中。
构造方法:
-
public FileOutputStream(String name)
创建普通的字节输出流对象, 关联目的地文件(字符串形式).
如果文件中有数据, 当程序启动之后并往文件中写入数据时, 文件中已有的数据会被覆盖掉.
-
public FileOutputStream(File file)
创建普通的字节输出流对象, 关联目的地文件(File对象形式).
如果文件中有数据, 当程序启动之后并往文件中写入数据时, 文件中已有的数据会被覆盖掉.
-
public FileOutputStream(String name, boolean append)
创建普通的字节输出流对象, 关联目的地文件(字符串形式).
当append的值为true时, 表示往文件中追加数据, 即: 文件中的数据不会被覆盖掉.
-
public FileOutputStream(Filefile, boolean append)
创建普通的字节输出流对象, 关联目的地文件(File对象形式).
当append的值为true时, 表示往文件中追加数据, 即: 文件中的数据不会被覆盖掉.
成员方法:
- public void write(int b) 解释: 一次写入一个字节, 到指定的目的地文件中.
- public void write(byte[] bys)解释: 一次写入一个字节数组, 到指定的目的地文件中.
- public void write(byte[] bys,int start, int len)解释: 一次写入一个字节数组的一部分, 到指定的目的地文件中.
- public void close()解释: 关闭此文件输出流并释放与此流相关联的任何系统资源.
小知识点:换行符 win: \r\n, linux: \n mac: \r
IO异常:
- 方式1:构造输出流时,路径可能存在,因此需要抛出异常,才能正常编译。
public static void main(String[] args) throws IOException{ }
- 方式2:try.catch.finally方式
public static void main(String[] args) {
//1.创建FileOutputStream对象, 关联指定的目的地文件.
FileOutputStream fos = null;
try{ //可能会出现问题的代码
fos = new FileOutputStream(“day12/data/2.txt”); //2.往文件中写入10次hello, 每个hello占一行.
for (int i = 0; i < 10; i++) {
fos.write(“hello”.getBytes());
//换行符
fos.write(“\r\n”.getBytes());
}
}catch(Exception e) { //出现问题的异常处理代码.
e.printStackTrace();
}finally{ //3. 关闭流, 释放资源
try {
if (fos != null) { fos.close(); //try.catch关键字 快捷键: alt + enter
fos = null; //GC会优先回收null对象.
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 两个异常:一个是路径不存在异常,另一个是能否关闭资源异常。
- if (fos != null) 判断的原因是,如果fos因为其他异常未能创建,那么关闭资源时就会出现空指针异常。
- fos = null;释放完资源以后,将fos改为null的原因是,资源关闭后,GC的二次标记算法导致它不会直接将其回收,而是打个戳,但是GC会优先回收Null资源,所以将其改为NULL。
3.2 字节输入流
概述:字节输入流指的是InputStream, 表示以字节为单位从文件中读取数据, 但是它是一个抽象类, 所以我们一般都是用它的子类。
子类:
- FileInputStream: 普通的字节输入流.
- BufferedInputStream: 高效的字节输入流.
3.2.1FileInputStream
概述:表示普通的字节输入流, 用来以字节的形式从文件中读取数据
构造方法:
- public FileInputStream(Stringname)解释: 创建普通的字节输入流对象, 关联数据源文件(字符串形式).
- public FileInputStream(Filefile)解释: 创建普通的字节输入流对象, 关联数据源文件(File对象形式).
成员方法:
-
public int read() 解释: 从指定的数据源文件中, 一次读取一个字节, 并返回该字节对应的整数. 读不到则返回-1.
-
public int read(byte[] bys)
从指定的数据源文件中, 一次读取一个字节数组, 并将读取到的内容存入到字节数组中 .
返回读取到的有效字节数. 如果读不到则返回-1.
-
public void close()解释: 关闭此文件输入流并释放与此流相关联的任何系统资源.
3.3 字节缓冲流
概述:字节流一次读写一个字节数组的速度比一次读写一个字节的速度快很多(稍后测试), 这是加入了数组这样的缓冲区效果, 但是如果每次都需要我们自己来定义数组的话, 是非常繁琐的. 所以Java本身在设计的时候, 也考虑到了这样的设计思想, 并提供了字节缓冲流
构造方法:
- BufferedOutputStream类的构造方法
- publicBufferedOutputStream(OutputStream os) 创建字节缓冲输出流对象,
- BufferedInputStream类的构造方法
- publicBufferedInputStream(InputStream is) 创建字节缓冲输入流对象.
**问:**为什么构造方法传递的是一个: OutputStream(或者InputStream), 而不是具体的文件或者路径呢?
答:字节缓冲流仅仅提供缓冲区, 而真正的底层的读写数据还是需要基本的流对象进行操作.
普通字节流和高效字节流对比:
- 普通的字节流和高效的字节流一次读写一个字节:普通字节流速度很慢,如果要求一个一个字节读取和写出,建议使用高效字节流方式读取和写出。
- 普通的字节流和高效的字节流一次读写一个字节数组:当数组长度为8192(8kb)时,两个读取速度不相上下,但高效字节流还需要封装,因此在一次读取一个字节数组时建议使用普通字节流。
3.4 字符流
由来:假如某个数据源文件中包含中文, 此时通过字节流来读取数据, 就不是特别方便了, 因为中文在不同的码表中, 占用的字节数是不一样的, 针对于这种情况, Java就提供了字符流。
- 中文在GBK码表中占2个字节, 在UTF-8码表中占3个字节.
- 不管在什么码表中, 中文的第一个字节肯定是负数.
- 字符流 = 字节流 + 编码表.
问:用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
答:汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
编解码
- GBK是针对于我们中国国内的码表, ISO-8859-1: 是欧洲通用码表. UTF-8是国际通用码表(统一码, 万国码)
- 编码: 字符 -> 字节, 把我们能看懂的, 转成计算机能看懂的(但是我们看不懂)
- 解码: 字节 -> 字符, 把计算机能看懂的(但是我们看不懂), 转成我们能看懂的
- 编解码时采用何种规则编码, 就采用对应规则解码, 否则就可能会出现乱码情况
概述:我们知道字符流 = 字节流 + 编码表. 所以我们可以通过转换流的方式, 结合字节流和编码表一起使用, 从而实现字符流的功能
通过转换流实现:
-
InputStreamReader:是从字节流到字符流的桥梁
它读取字节,并使用指定的编码将其解码为字符
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
InputStreamReader isr = new InputStreamReader(new FileInputStream(“day12/data/1.txt”), “utf-8”);
-
OutputStreamWriter:是从字符流到字节流的桥梁
是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“day12/data/2.txt”), “utf-8”);
普通字符流:普通字符流指的是FileReader和FileWriter, 用法和字节流类似, 只不过字节流操作的是字节, 而字符流操作的是字符
3.5 字符缓冲流
概述:字符流一次读写一个字符数组的速度比一次读写一个字符的速度快很多, 这是加入了数组这样的缓冲区效果, 但是如果每次都需要我们自己来定义数组的话, 是非常繁琐的. 所以Java本身在设计的时候, 也考虑到了这样的设计思想, 并提供了字符缓冲流.
- BufferedReader: 字符缓冲输入流
- BufferedWriter: 字符缓冲输出流
构造方法:
- public BufferedWriter(Writer w)创建字符缓冲输出流对象,
- public BufferedReader(Reader r)创建字符缓冲输入流对象.
问:为什么构造方法传递的是一个: Writer (或者Reader ), 而不是具体的文件或者路径呢?
答:字符缓冲流仅仅提供缓冲区, 而真正的底层的读写数据还是需要基本的流对象进行操作。
常用方法:
字符缓冲流在实际开发中用的非常多, 它也提供了两个非常常用的方法, 具体如下:
-
BufferedWriter类中的方法
public void newLine(); 根据当前操作系统, 给出对应的换行符.
-
BufferedReader类中的方法
public String readLine(); 读取一行数据, 只包含数据, 不包含任何终止字符, 如果读到流末尾, 则返回null。
3.6 序列化流
3.6.1 对象序列化流
对象序列化概述:就是将对象保存到磁盘中,或者在网络中传输对象。
这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息。字节序列写到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
对象序列化流概述:将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。
构造方法:
方法名 | 说明 |
---|---|
ObjectOutputStream(OutputStream out) | 创建一个写入指定的OutputStream的ObjectOutputStream |
序列化对象方法:
方法名 | 说明 |
---|---|
void writeObject(Object obj) | 将指定的对象写入ObjectOutputStream |
注意:
- 一个对象要想被序列化,该对象所属的类必须必须实现Serializable接口
- Serializable是一个标记接口,实现该接口,不需要重写任何方法
3.6.2 对象反序列化流
ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
构造方法:
方法名 | 说明 |
---|---|
ObjectInputStream(InputStream in) | 创建从指定的InputStream读取的ObjectInputStream |
反序列化对象方法:
方法名 | 说明 |
---|---|
Object readObject() | 从ObjectInputStream读取一个对象 |
3.6.3 serialVersionUID&transient
问:用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
答:会出问题,会抛出InvalidClassException异常,因为序列版本号不一致。
- 方法1:重新序列化。
- 方法2:给对象所属的类加一个序列化版本号(serialVersionUID )。
private static final longserialVersionUID = 42L;
问:如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程