/***
* _ooOoo_
* o8888888o
* 88" . "88
* (| -_- |)
* O\ = /O
* ____/`---'\____
* . ' \\| |// `.
* / \\||| : |||// \
* / _||||| -:- |||||- \
* | | \\\ - /// | |
* | \_| ''\---/'' | |
* \ .-\__ `-` ___/-. /
* ___`. .' /--.--\ `. . __
* ."" '< `.___\_<|>_/___.' >'"".
* | | : `- \`.;`\ _ /`;.`/ - ` : | |
* \ \ `-. \_ __\ /__ _/ .-` / /
* ======`-.____`-.___\_____/___.-`____.-'======
* `=---='
*
* .............................................
* 佛祖保佑 永无BUG
*/
public class ExceptionTest {
/*Java中的异常处理机制
* Java所有异常类的顶层父类是java.lang.Throwable
* 其直接子类有两种:Exception和Error
* Exception很好理解,当程序违反了Java语言预定义的规则时就会引发异常,导致程序中止
* 但可以通过一些措施将其捕获处理掉
* 而Error是系统级别的异常,面对他我们通常无能为力,当他被抛出时由JVM接管,无法被捕获
* 其中Exception又分为普通异常和运行时异常,普通异常编译器要求必须对其进行处理,否则编译不会通过
* 而运行时异常可以不对其进行处理,因为他通常是无法预测的。所以很少会去捕获运行时异常*/
@Test
void test() {
/*先来看看Error吧,他的直接父类是java.lang.Error*/
/*最常见的内存溢出*/
class ErrorTest {
ErrorTest e = new ErrorTest();
}
/*无限套娃,每new出一个实例,加载属性又会重复这一过程
* 每个实例都有着自己的引用,无法被回收掉,这样就导致了栈堆内存瞬间爆满*/
ErrorTest e = new ErrorTest();
}
@Test
void test1() {
/*再来一个例子*/
/*当动态定义一个数组时,指定的长度是一个int型参数
* 所以一个数组的理论最大长度为2147483647
* 但由于JVM内存的限制,数组实际的最大长度可能达不到这个值*/
byte[] bytes = new byte[Integer.MAX_VALUE];
/*这是异常信息
* Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at com.snake.ExceptionTest.test1(ExceptionTest.java:34)
嗯,没错,他告诉我们请求的数组长度超出了虚拟机的限制,要解决这个异常就只能去修改JVM的配置文件了
* */
}
/*好了,下面到了关键的Exception
* 普通异常,这种异常在通常在使用JDK提供的API时出现,即由方法的内部抛出
* 这是在一种特定的情况下出现的异常*/
@Test
void test2() throws FileNotFoundException {//抛出异常
File file = new File("a.txt");
/*在这里,要构建读取文件的字节流,他会抛出一个异常,即强制让你在编译时考虑:如果
* 读取的文件不存在该怎么办,这种异常Java提供了两种解决方案
* 1.不做处理,继续接力抛出去,由下一个调用者处理,使用throws关键字抛出异常
* 如果每一个调用者均不做处理,他最终由main方法做最后的抛给JVM处理,并终止程序的运行
* 多线程的情况下就由java.lang.Thread.run()方法抛出,同时该线程被销毁
* 单元测试那就由这个方法完成最后一抛*/
FileInputStream fis = new FileInputStream(file);
}
/*但这种方式除了可以在控制台看到其异常信息之外,无法做任何补救措施
* 所以通常处理异常的方式是第二种,即try...catch代码块*/
@Test
void test3() {
File file = new File("a.txt");
/*考虑到变量作用域范围的问题,通常将变量预先定义在try...catch结构之外*/
InputStream is = null;
try {
/*将可能会出现异常的代码放在try{}中,
* 这里的代码一旦出现异常就会被catch所定义的异常类型进行匹配和捕获
* 之后,便可以针对这种异常将处理逻辑写在catch{}中*/
is = new FileInputStream(file);
} catch (FileNotFoundException e) {
/*如下,当文件不存在时就先将其创建*/
try {
/*当然,创建文件的方法也会抛出一个异常,即当文件路径无效时
* 这个先不做任何处理*/
file.createNewFile();
/*然后确保文件存在后再构建字节输入流*/
is = new FileInputStream(file);
/*这里catch的结构是可以存在多个的,即每种异常类型对应一种处理方式
* 当然,如果try中的代码所有抛出的异常类型是同源的,只需要声明一个其共同父类就可以全部捕获到*/
} catch (IOException ex) {
/*printStackTrace()打印异常信息,这是编辑环境在生成try...catch的默认处理方式
他同样会导致程序停止,如果下面的代码不依赖于如上的结构时
可以不做任何措施,保持程序正常运行*/
ex.printStackTrace();
}
}
}
/*运行时异常 顶层父类是java.lang.RuntimeException
* 这种异常不同与普通异常的是,他不出现于特定的API中
* 通常是由于我们自己编写的代码逻辑不严谨导致违反了Java语言规则的时候由JVM抛出的异常,
* 所以他难以被人为侦测到(自己故意制造的除外嗷),例如下面这个方法
* 指定一个String型参数,判断他的字面量是否是"A"*/
boolean isA(String s) {
return s.equals("A");
}
/*就这么过一眼好像逻辑没什么问题,
但当传递的参数是一个null的时候就有问题了*/
@Test
void test4() {
ExceptionTest et = new ExceptionTest();
/*到这一步控制台就该报空指针了,所以良好的编程规范还是很有用的
* 就对于equals方法来说,应该拿已知比未知,方法体该这样写
* return "A".equals(s) 可以有效避免空指针*/
boolean isA = et.isA(null);
}
@Test
void test5() {
/*几种常见的运行时异常*/
/*空指针:java.lang.NullPointerException
* 参上*/
/*类型不匹配异常:java.lang.ClassCastException
* 两个有继承或实现关系的引用数据类型强制转换失败时出现*/
Object o = new Object();
/*强制转换是否能成功的关键在于变量指向的堆内存中的结构是否兼容,
而不是变量的类型,很显然,这个转换会出现类型不匹配异常*/
String s = (String)o;
/*解决方案:在转换前先确认二者类型是否兼容*/
if (o instanceof String) {
String s1 = (String) o;
}
/*数字格式异常: java.lang.NumberFormatException
* 将一个字符串转换为数值型时,若字符串不是纯数字组成时出现*/
int a = Integer.parseInt("132a");
/*解决方案:直接捕获就好*/
int b = 1010;
try {
b = Integer.parseInt("a");
} catch (NumberFormatException e) {
/*出现异常直接捕获,不需要多余的处理,
程序继续执行,变量b也会保持原来的值
虽然很少有捕获运行时异常的,但洒家认为这是最好的处理方式
当然你要是会用正则那当我没说*/
}
/*索引越界异常:java.lang.ArrayIndexOutOfBoundsException
* 当通过索引访问数组元素时,若索引超出数组的最大索引时出现
* */
byte[] bys = new byte[3];
byte b1 = bys[3];
/*解决方案,直接捕获*/
byte b2 = 0;
try {
b2 = bys[3];
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException");
}
/*索引越界最常出现的场景是在循环结构中遍历数组由于控制条件存在缺陷导致的
* 而增强for又无法根据索引在其中做一些操作
* 所以遍历数组时还可以这么写*/
try {
/*每次循环时还少了判断控制条件这一步,效率反而更高了*/
for (int i = 0; ; i++) {
bys[i] = i % 2 == 0 ? bys[i]++ : bys[i];
}
} catch (ArrayIndexOutOfBoundsException e){
}
}
/*自定义异常*/
class MyException extends Exception{
public MyException(){
super();
}
public MyException(String message){
super(message);
}
}
public String toString(Object o) throws MyException {
if(o==null){
/*选择在特定的情况下通过throw抛出一个异常*/
throw new MyException("传递的参数为null");
}
return o.toString();
}
/*自定义运行时异常*/
class MyRuntimeException extends RuntimeException{
public MyRuntimeException(){
super();
}
public MyRuntimeException(String message){
super(message);
}
}
public String toString0(Object o){
if(o == null){
/*运行时异常不被编译器强制要求处理*/
throw new MyRuntimeException("传递的参数为null");
}
return o.toString();
}
}
Java 异常处理机制
最新推荐文章于 2024-10-06 20:16:20 发布