java基础(2)--异常类&IO流ing

1、异常类
底层是Throwable,直接子类包括Exception和Error。
一般RuntimeException类都是程序员错误。

2、异常处理
try:用来检测异常
catch:用来捕获异常
finally:释放资源
一个try,可以对应多个catch,一个finally。
一个catch中可以包含多种异常,类用’|'分开
可以直接使用throw在方法体内直接抛出,此时需要在方法后使用throws声明异常。
PS:运行时异常在方法体内抛出,那么就不需要throws声明;编译时异常必须有。
PS:Throwable的默认输出(default):printStackTrace();

3、throws和throw的区别
3.1 throws用在方法声明后面,跟的是异常类名;用在方法体内,跟的是异常对象名。
3.2 throws可以跟多个异常类名,用逗号隔开;throw只能抛出一个异常对象名。
3.3 throws表示抛出异常,由该方法的调用者来处理;throw表示抛出异常,由方法体内的语句处理。

4、finally关键字控制的语句体一定会执行(即使提前return也会执行),除非在此之前jvm提前退出(e.g. System.exit(0))
PS:对于catch中的return,finally是在return之后执行的。

5、final、finally和finalize的区别
5.1 final可以修饰类,不能被继承;修饰方法,不能被重写;修饰变量,只能赋值一次。
5.2 finally是try语句中的一个语句体,不能单独使用,用来释放资源。
5.3 finalize是一个方法,当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

6、异常类继承:子类只能抛出父类的异常子集(相同或少于),如果重写的类没有抛出异常,那么继承该类的子类也不能抛出异常

7、File类
7.1 createNewFile():创建文件,如果存在,就不创建并返回false
7.2 mkdir():创建文件夹,如果存在,就不创建并返回false
7.3 mkdirs():创建文件夹,如果父文件夹不存在,也会一同创建
7.4 renameTo(File dest):重命名文件;如果路径相同,那么就是重命名;如果路径不同,那么就是重命名+剪切
7.5 delete():删除文件/文件夹;删除不走回收站;如果删除文件夹,那么文件夹中不能包含内容
7.6 isDirectory():是否是目录;isFile():是否是文件
7.7 exists():是否存在;isHidden():是否隐藏
7.8 canRead():是否可读;canWrite():是否可写
PS:设置setReadable(false),window下依然可读,因为window认为一切文件都可读;linux下设置不可读就不可读。
设置setWriteable(false),那么window和linux都不可写
7.9 getAbsolutePath():获取绝对路径;getPath():获取路径;getName():获取名称;lastModified():获取最后一次的修改时间(毫秒);list():获取指定目录下的所有文件或文件夹的名称(String[],文件名称);listFiles():获取指定目录下的所有文件或文件夹的File数组(File[],完全路径)

8、FilenameFilter接口,过滤文件名
示例:

new FilenameFilter() {
	@Override
	public boolean accept(File dir, String name){
		File file = new File(dir, name);
		return file.isFile() && file.getName().endsWith(".jpg");
	}
}

9、IO流,就好像创建了一根内存到硬盘文件的管子,来对文件进行读写、操作。
IO流的常用父类:
1.1 字节流的抽象父类:InputStream、OutputStream (字节流可以操作任何数据,因为计算机中数据是以字节形式存储的)
1.1.1 InputStream read():虽然读的是byte,但是一般返回的是int:因为字节输入流可以操作任意类型的文件,文件底层都是以二进制形式存储的。如果返回byte,可能会读到中间的时候遇到全1字节,那么就和那会-1。因为文件读完的时候,也是返回-1,这样就会出现错误。因此使用int类型接受,那么当遇到全1字节时会在其前面补上24个0凑足4个字节,此时byte的-1就变为了int的255。保证了整个文件数据的读取完毕,同时结束标记的-1在byte和int类型下都是相同的。
1.1.2 InputStream available():文件剩余字节数
1.1.3 InputStream read(byte[] b) or read(byte[] b, int off, int len):一次性读取b.length(or len)个字节数据到b数组中;off表示数组中的偏移量(索引)
1.1.4 OutputStream 如果文件不存在,会创建文件;默认清空重写文件,第二参数为是否追加模式;write():虽然写的是int类型,但是会转换成byte类型写入,自动去除前三个8位。补充:write(byte[] b)
PS:FileInputStream(path)、FileOutputStream(path);BufferedInputStream(InputStream)、BufferedOutputStream(OutputStream)
BufferedInputStream:
BufferedInputStream内置一个缓冲区(数组)
从BufferedInputStream中读取一个字节时,BufferedInputStream会一次性从文件中读取8192个字节,存在缓冲区中,并返回程序一个字节
程序再次读取时,直接从缓冲区获取,直到缓冲区所有字节被获取完毕以前,无需再查找文件
BufferOutputStream:
BufferOutputStream也内置一个缓冲区(数组)
程序向流中写字节时,不会直接写到文件中,而是先写到缓冲区中
直到缓冲区写满/数据写完,BufferOutputStream才会把缓冲区的数据一次性写到文件中。
因此,BufferInputStream和BufferOutputStream的read()和write()方法同样是读写字节(单纯读写字节),但是比FileInputStream和FileOutputStream快很多。
但是如果在FileInputStream和FileOutputStream使用read(byte[] b)和write(byte[] b)方法的话(8192个字节大小),会比BufferInputStream和BufferOutputStream的read()和write()方法快一点点。因为FileXXXputStream操作的是同一个数组,而BufferedXXXputStream操作的是两个不同的数组。
1.1.5 close():具备刷新功能,在关闭流之前,就会想刷新一次缓冲区,将缓冲区的直接全部刷新到文件上,再关闭。
1.1.6 flush():具备单纯的刷新功能。主要用于难以将缓存区填满就需要传递的场景,e.g. 聊天
PS:因为调用流方法打开就必须有关闭。同时为了防止文件不存在,所以需要将FileXXXputStream、BufferedXXXputStream等类的包含文件的声明需要加到try语句块中。
不过可以在JDK1.7以后,可以将该类声明加到try(…)中,那么文件流调用完毕以后会自动调用close()方法。原因是因为JDK1.7中这些原生文件流继承了InputStream(or OutputStream)类 -->实现了Closeable接口 -->继承了AutoCloseable接口,该类中只有一个方法就是close()方法(PS:那么如果自定义文件流类实现了AutoCloseable接口,也可以自动调用close()方法)。
e.g.

try(
	FileInputStream fis = new FileInputStream("xxx.txt");
	FileOutputStream fis = new FileOutputStream("yyy.txt");
) {
	int b;
	while((b = fis.read()) != -1){
		fos.write(b);
	}
}catch(...){...}

PS:加密图片最简单的方法就是将每个字节异或上一个数字,当字符再次异或该数字时就解密了。
1.2 字符流的抽象父类:Reader、Writer (字符流只能操作字符数据;Writer自带1024字节的缓冲区)
1.2.1 常用子类:FileReader、FileWriter;BufferedReader、BufferedWriter;LineNumberReader、LineNumberWriter;InputStreamReader、OutputStreamWriter
BufferedReader的readLine():读取一行字符(不包含换行符)
BufferedWriter的newLine():输出一个跨平台的换行符号
PS:不推荐在拷贝文本的时候,使用字符流,因为读取时会把字节转为字符,写出时需要将字符转为字节。
但是当只是单向流操作(只读,只写)的时候,可以使用字符流,因为读取的时候是按照字符的大小读取的,不会出现半个中文;写出的时候可以直接将字符串写出,不用转换为字节数据。
PS:字符流不可以拷贝非纯文本文件,因为字节会被转换为字符,那么就可能存在一些字节在转成字符的过程中没有在码表中找到对应的字符,会被程序使用?字符替代,但是在转换写出的时候,并不能转换为正确的字节数据。
PS:newLine()是跨平台的方法,"\r\n"只支持window系统(linux下只需要"\n",IOS下只需要"\r")
LineNumberXXX整行读整行写。LineNumberReader可以通过setLineNumber()方法设置从哪一行之后开始读,getLineNumber()获取读取的行号。
PS:UTF-8中一个汉字占据3个字节,而GBK中一个汉字占据2两个字节。
XXputStreamXXX是字节流与字符流之间的桥梁(InputStreamReader字节流通向字符流;OutputStreamWriter字符流到字节流)
e.g.

BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("utf-8.txt","utf-8"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("gbk.txt","gbk"));

PS:多层类嵌套包装,可以提升读写的效率
图解:

1.3 特殊流
序列流 SequenceInputStream可以将多个字节输入流整合成一个,从序列流中读取数据时,将从被整合的第一个流开始读,读完一个以后继续读取第二个,直到读取完毕。
e.g.

FileInputStream fis1 = new FileInputStream("1.txt");
FileInputStream fis2 = new FileInputStream("2.txt");
FileInputStream fis3 = new FileInputStream("3.txt");
// 整合两个字节流
SequenceInputStream sis1 = new SequenceInputStream(fis1,fis2);
// 整合多个字节流
Vector<InputStream> v = new Vector<>();
v.add(fis1);
v.add(fis2);
v.add(fis3);
Enumeration<InputStream> en = v.elements();
SequenceInputStream sis2 =new SequenceInputStream(en);

内存输出流 ByteArrayOutputStream可以向内存中写数据,将内存当做一个缓冲区(数组),当需要写出时,可以一次性获取所有数据。方法:write()写数据,toByteArray()获取数据

猜你喜欢

转载自blog.csdn.net/IndusInAutumn/article/details/84518736