文章目录
可以探究一下:JVM默认的最大内存占用是多少
1. 异常部分
- try内可能有多个大括号,对于throw和这些大括号的交互:
exception的匹配
- 在一系列的catch中,处于上层的异常不能是下面异常的祖先类,否则就会报错
- 故而正确的方法是,上层的异常是继承树中的近叶节点,上下层也可以是无关的或同层次的节点
throws的声明
-
可以吹牛但不能瞒报,也即:
-
可以在函数前声明抛出很多异常,但在函数内部可以一个不抛出它们
-
但是,所有调用声明抛出一些异常的函数,必须能够处理这些异常,如果无法处理,就只能更向更上一层抛出异常
-
如果不做处理(瞒报),就会报错
-
C++的异常声明在编译时,不起任何作用,仅仅是在运行时起作用。而kotalin没有异常声明检查
-
Exception实现了Throwable接口,重要的接口方法如下:
异常的再次抛出
- 异常可以被反复抛出:
catch(Exception e){
System.err.println("An Exception");
throw e;
}
// 更可靠的方法:将原始异常设置为新异常的原因
try{
...
}
catch(SQLException e){
Throwable se = new ServletException("database err");
se.initCause(e);
throw se;
}
// 这样可以使用getCause()得到原始异常的信息
可以用fillInStackTrace来将stack trace更改到再次抛出处为起点
finally的作用
三个可能性:
- 无事发生(没有异常)
- 有异常被抛出,且被捕捉
- 有异常被抛出,但没有捕捉
在上述三种情况下,finally都会被执行
finally可以用于处理内存以外的资源(如文件等等)
可以使用try-finally的方式,跳过catch。
附带资源的try语句
语法:
try(Resoutrce res = ...){
...
}
catch(...){
...
}
其中,在try小括号中创建对象的类必须实现AutoCloseable
接口(实现一个close()
方法)。在跳出try语句块的时候,res对象会自动执行close()
方法,这类似于python中的with语句。
当实现的资源对象的close方法抛出异常的时候,会产生在异常的传递过程中有另一个异常产生的现象,此时后产生的异常就会被抑制(suppressed),如果调用捕捉到的异常的printStackTrace()
方法,会打印出一个被捕获的异常和若干个被抑制的异常(这说明它们也被捕获了)。此时使用getSuppressed()
方法可以获取被抑制的异常数组。
异常中的堆栈跟踪(stack trace)元素
堆栈跟踪是一个方法调用的列表,包含了程序执行过程中方法调用的特定位置,printStackTrace()
是Throwable
接口声明的方法原型。另外,还可以使用getStackTrace()
方法得到StackTraceElement
数组,对每一帧进行分析。
StackTraceElement
对象能过获得异常产生处的一系列信息:
而Thread.getAllStackTrace()可以获取全部线程的堆栈跟踪:
继承中的异常
-
子类的构造函数会调用父类的构造函数,所以,子类ctor抛出的异常必须至少包含父类ctor抛出函数的所有异常(并且可以加新异常)
-
而相反,子类的成员函数必须是所有继承和实现来源的重写函数的交集的广义子集(代表上述交集和所有它们的派生类的子集,因为子类方法要表示这些父类或接口的特性,如果子类抛出新的异常,针对父类或接口的处理就有可能无效而导致程序中断。
使用异常的注意事项
异常顶层结构:
其中RuntimeException包括了大部分程序错误导致的异常。包括但不限于:
- 类型转换错误
- 数组访问越界
- 访问空指针
如果出现RuntimeException,那么一定是程序员的问题
而不是派生自RuntimeException的异常包括但不限于:
- 试图在文件尾部后面读取文件
- 试图打开不存在文件
- 试图根据给定字符串查找Class对象,而这个字符串表示的类并不存在
Java将派生与Error类或RuntimeException类的所有异常称为未检查异常,而将其他异常称为已检查异常。编译器会核查确保为所有的已检查异常提供了异常处理器。而理论上,我们不应该在自己的代码中抛出未检查异常,因为要么我们对Error没有派生能力,要么可以在程序中避免RuntimeException。
- 在创建自己的异常类的时候,习惯上穿件两个构造器,一个无参,一个带有详细信息
异常与性能
- 只在异常情况下使用异常机制
使用异常机制的异常处理比使用简单判断的处理耗时大1-2个数量级。 - 异常不应当被过分细化
应当尽量把一块,而非一行代码放在try-catch块中 - 利用异常的结构层次
尝试抛出更精准的派生类异常 - 早抛出,弯捕获
2. IO与流(Stream)
IO流的结构:
不论何种IO流都以字节为最小单位。
InputStream
是Java标准库提供的最基本的输入流,是一个抽象类,最重要的抽象方法是int read()
只能处理字节(任何格式的数据都会被变成字节)
c语言main函数使用int作为main函数的返回值,是因为区分合理的0xff和遇到EOF字符。
Java使用16位的unicode,而utf-8是8bit编码的unicode,实际上是一种霍夫曼编码,比较常用的如ascii码在utf-8中就是一个字节。而非ascii码的字符是用2或3个字符表达,所有汉字都是使用3字节表达的。
OutputStream
同样也只能处理字节
Java是大端(big-endian)
3.文件及其相关类
文件总体使用File类表示,它可以通过以下方法创建新文件:
try{
File file = new File("C:/myfile.txt");
if(file.createNewFile())
System.out.println("文件创建成功!");
else
System.out.println("出错了,该文件已经存在。");
}
catch(IOException ioe) {
ioe.printStackTrace();
}
}