1. 异常
小王每天开车去上班,一般情况下半个小时内就能到,一旦发生堵车或者其他不可预知的情况,小王都会与公司取得联系然后告知情况,以免影响工作。而对于程序而言,堵车就是一种异常,与公司联系告知情况,就是一种异常处理。
概念: 异常也称例外,在程序运行时发生并打断程序运行,分类两类:
- 非受检异常/运行异常:不接受javac检查,可以不处理,一般指逻辑错误,应积极避免:
ClassCastException:类型转换异常。ArrayIndexOutOfBoundsException:数组越界异常。NullPointerException:空指针异常。
- 受检异常/可控异常:必须接受javac检查,必须处理,一般是程序运行时由于外界因素造成的:
ClassNotFoundException:类丢失异常。IOException:IO异常。SQLException:SQL异常。
部分异常关系继承结构图.md
Throwable [抛出能力类]
|__Error [错误]
|__VirtulMachineError [虚拟机错误]
|__StackOverFlowError [栈溢出]
|__OutOfMemoryError [内存溢出]
...
|__Exception [异常]
|__IOException [文件读写异常]
|__EOFException [end of file文件已经读完了,你还在读]
|__FileNotFoundException [文件没找到异常]
|_ ReflectiveOperationException [响应操作异常]
|__ClassNotFountException [类没找到异常]
|__RuntimeException [运行时异常]
|__ArithmeticException [数学异常]
|__MissingResourceException [资源文件丢失异常]
|__ClassCastException [类转换异常]
|__NullPointerException [空指针异常:使用null调用,计算,取值等]
|__IllegalArgumentException [非法参数异常]
|_ UnknownEntityException [未知实体异常]
|__UnknownTypeException [未知类型异常]
|_ IndexOutOfBoundsException [角标越界]
|__ArrayIndexOutOfBoundsException [数组角标越界异常]
|__StringIndexOutOfBoundsException [字符串角标越界异常]
...
2. 异常处理
概念: 异常爆发后的处理方案有记录日志,System.exit(-1) 退出虚拟机,e.printStackTrace() 打印堆栈信息,发短信,发邮件等。
2.1 异常处理结构
概念: 在一套完整的try/catch/finally结构中:
try{}:包裹可能会爆发异常的代码,必须且只能存在一个:- 选中代码,然后使用快捷键
ctrl + alt + t可以自动进行包裹。
- 选中代码,然后使用快捷键
catch(){}:捕获一个或者多个异常,以及之后的处理工作:- 允许同时捕获多个,但必须严格按照先小后大的顺序进行捕获。
- 允许不存在,即不对异常进行捕获和处理。
- jdk8中提供了multipleCatch写法,可同行捕获多个异常。
finally{}:无论程序是否爆发异常,其中的代码都一定会执行:- 允许不存在,但如果存在则只能存在一个。
源码: /javase-advanced/
- src:
c.y.exception.ExceptionTest.tryCatchStructure()
/**
* @author yap
*/
public class ExceptionTest {
@Test
public void tryCatchStructure() {
try {
System.out.println(1 / 0);
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
System.out.println("程序结束...");
}
System.out.println("如果异常爆发,我不会被输出...");
}
}
- src:
c.y.exception.ExceptionTest.multipleCatch()
@Test
public void multipleCatch() {
int[] arr = {1, 2};
int num = 0;
try {
arr[0] = 1 / num;
} catch (NullPointerException | ArithmeticException e) {
e.printStackTrace();
}
}
2.2 异常结构中的return
概念: 异常结构中无论是否存在 return,finally{} 都一定会执行:
- 若
return在try{}/catch{}中:- 执行
try{}/catch{}获得return结果并缓存起来(此时不直接返回)。 - 执行
finally{},若finally{}中对刚才的结果进行了修改:- 基本类型结果,修改无效,以
try{}/catch{}中缓存的结果为准。 - 引用类型结果,修改有效,以
finally{}中修改后的结果为准。
- 基本类型结果,修改无效,以
- 执行
try{}/catch{}中的return,将结果返回给调用方。
- 执行
- 若
return在finally{}中,则最终只执行finally{}中的return。
源码: /javase-advanced/
- src:
c.y.exception.ExceptionTest.returnTime()
private int returnInTryOrCatchWithBase(int num) {
try {
System.out.println(100 / num);
return 100;
} catch (Exception e) {
System.out.println("catch执行...");
return 200;
} finally {
System.out.println("finally执行...");
num = 300;
}
}
private int[] returnInTryOrCatchWithReference(int num) {
int[] arr = {0, 1};
try {
System.out.println(100 / num);
arr[0] = 100;
return arr;
} catch (Exception e) {
System.out.println("catch执行...");
arr[0] = 200;
return arr;
} finally {
System.out.println("finally执行...");
arr[0] = 300;
}
}
private int returnInTryOrCatchAndFinally(int num) {
try {
System.out.println(100 / num);
return 100;
} catch (Exception e) {
System.out.println("catch执行...");
return 200;
} finally {
System.out.println("finally执行...");
return 300;
}
}
@Test
public void returnTime() {
System.out.println(returnInTryOrCatchWithBase(100));
System.out.println(returnInTryOrCatchWithBase(0));
System.out.println(returnInTryOrCatchWithReference(100)[0]);
System.out.println(returnInTryOrCatchWithReference(0)[0]);
System.out.println(returnInTryOrCatchAndFinally(100));
System.out.println(returnInTryOrCatchAndFinally(0));
}
2.3 throws抛出多个
概念: 可以在方法名后通过 throws 将异常抛给调用者处理,抛出多种异常使用用逗号隔开,且在抛出的多种异常之间是在乎顺序和大小的。
源码: /javase-advanced/
- src:
c.y.exception.ExceptionTest.throwsException()
2.4 throw抛出一个
概念: throw 异常实例 用于主动抛出某个异常:
- 如果抛出的是受检异常,则方法也必须配合
throws进行二次抛出。 - 如果抛出的是非受检异常,则不需要额外处理。
源码: /javase-advanced/
- src:
c.y.exception.ExceptionTest.throwException()
private void method(int num) throws RuntimeException {
System.out.println(100 / num);
}
@Test
public void throwsException() {
try {
method(0);
} catch (RuntimeException e) {
e.printStackTrace();
}
}
2.5 tryWithResources捕获异常
概念: jdk8中提供了tryWithResources写法,如果某个实例实现了Closeable接口,则可以将try-catch写法变形如下:
- 为
try{}添加一个小括号,变为try(){} - 将实例的声明放在小括号中,如
try(A a = new A()){} - 效果是tryWithResources自动添加
finally{}且在其中调用实例的close()方法。
源码: /javase-advanced/
- src:
c.y.exception.TryWithTest
/**
* @author yap
*/
public class TryWithTest {
private static class Person implements Closeable {
@Override
public void close() throws WebServiceException {
System.out.println("Person的close()被调用...");
}
}
@Test
public void tryWithResources() {
try (Person person = new Person()) {}
}
}
3. 自定义异常类
概念: 继承 Exception 即可自定义异常类。
源码: /javase-advanced/
- src:
c.y.exception.CustomExceptionTest
/**
* @author yap
*/
public class CustomExceptionTest {
private static class CustomException extends Exception {
public CustomException(){ }
public CustomException(String message) {
super(message);
System.out.println("my exception service...");
}
}
@Test
public void myException() throws CustomException {
throw new CustomException("你触发了我自己写的异常...");
}
}
4. 异常方法的重写原则
概念: 对于抛出异常的方法我们也可以对其进行Override,但是要遵循以下几条原则:
- 非受检异常在什么情况都不需要被处理。
- 原方法有受检异常,重写方法可以不抛出该异常:
- 若没有使用多态调用重写方法,则这个受检异常无需被处理。
- 若使用了多态来调用重写方法,则这个异常依然需要被处理(非受检除外)。
- 原方法有异常,重写方法的异常只能比原方法小(非受检除外)。
- 原方法没有异常,重写方法不允许抛出异常(非受检除外)。
5. Date工具类
概念: java.util.Date 表示指定的日期信息,可以精确到毫秒,Date中许多方法已经过时但仍能使用。
public Date([long time]):创建当前系统日期,可传入时间戳以创建指定日期。void setTime(long time):设置自定义日期,参数为时间戳。long getTime():获取时间戳。
源码: /javase-advanced/
- src:
c.y.exception.DateTest
/**
* @author yap
*/
public class DateTest {
@Test
public void build(){
Date date = new Date();
System.out.println(date);
date = new Date(1000L);
System.out.println(date);
date.setTime(5000L);
System.out.println(date.getTime());
}
}
6. DateFormat工具类
概念: java.text.DateFormat 用于日期的格式化和解析:
new SimpleDateFomat()/DateFormat.getInstance():创建DateFormat实例。String format(Date date):将日期按照指定格式格式化成字符串。Date parse(String str):将字符串按照指定格式解析成日期。
源码: /javase-advanced/
- src:
c.y.exception.DateFormatTest
/**
* @author yap
*/
public class DateFormatTest {
@Test
public void format() {
Date now = new Date();
String pattern = "yyyy年MM月dd日 hh:mm:ss";
DateFormat dateFormat = new SimpleDateFormat(pattern);
String result = dateFormat.format(now);
System.out.println(result);
}
@Test
public void parse() {
String olympicDate = "2008-08-08";
String pattern = "yyyy-MM-dd";
DateFormat dateFormat = new SimpleDateFormat(pattern);
Date result = null;
try {
result = dateFormat.parse(olympicDate);
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(result);
}
}
7. Calendar工具类
概念: java.util.Calendar 是一个抽象基类,主要用于完成日期字段之间的相互操作功能,可以设置和获取日期数据的特定部分。
Calendar.getInstance():创建Calendar实例。setTime(Date date):使用给定的Date设置此CalendarA的时间。getTime():转化成Date对象add(int field, int amount): 将日历的field字段添加或较少指定值。set(int field, int value): 将日历的field字段改为value值。Calendar.MONTH:老外将12月作为每年的第一个月。Calendar.DAY_OF_WEEK:老外将周日作为每周的第一天。
get(int field):获得当前时间中field字段的值。
源码: /javase-advanced/
- src:
c.y.exception.CalendarTest
/**
* @author yap
*/
public class CalendarTest {
@Test
public void calendarApi() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
// 将年份减去1
calendar.add(Calendar.YEAR, -1);
System.out.println(dateFormat.format(calendar.getTime()));
// 将月份设置为1月
calendar.set(Calendar.MONTH, 0);
System.out.println(dateFormat.format(calendar.getTime()));
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int date = calendar.get(Calendar.DATE);
int week = calendar.get(Calendar.DAY_OF_WEEK);
int hour = calendar.get(Calendar.HOUR);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
System.out.printf("%d-%d-%d 星期%d %d:%d:%s",
year, month + 1, date, week + 1, hour, minute, second);
}
}
8. Optional工具类
概念: java.util.Optional 可以更优雅的避免空指针异常:
Optional.of(obj):生成obj对应的Optional对象。Optional.ofNullable(obj):obj不为空则生成对应的Optional对象,否则返回空Optional对象。optional.map(Function fun):调用函数,若参数为空,直接返回空Optional对象。optional.orElse(null):终止Optional调用链,返回null。
源码: /javase-advanced/
- src:
c.y.exception.OptionalTest
/**
* @author yap
*/
public class OptionalTest {
@Test
public void option() {
A a = new A();
// no good
if (a != null && a.getB() != null && a.getB().getC() != null) {
System.out.println(a.getB().getC().getName());
}
// good
// map(): return empty optional if param is null to avoid NullPointException
String result = Optional.ofNullable(a)
.map(e -> e.getB())
.map(e -> e.getC())
.map(e -> e.getName())
.orElse(null);
System.out.println(result);
}
@Data
class A {
private B b = new B();
}
@Data
class B {
private C c = new C();
}
@Data
class C {
private String name = "z4";
}
}