String getMessage()
: 返回异常发生时的简要描述String toString()
: 返回异常发生时的详细信息String getLocalizedMessage()
: 返回异常对象的本地化信息。使用 Throwable
的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与 getMessage()
返回的结果相同void printStackTrace()
: 在控制台上打印 Throwable
对象封装的异常信息try
块:用于捕获异常。其后可接零个或多个 catch
块,如果没有 catch
块,则必须跟一个 finally
块。catch
块:用于处理 try 捕获到的异常。finally
块:无论是否捕获或处理异常,finally
块里的语句都会被执行。当在 try
块或 catch
块中遇到 return
语句时,finally
语句块将在方法返回之前被执行。try {
System.out.println("Try to do something");
throw new RuntimeException("RuntimeException");
} catch (Exception e) {
System.out.println("Catch Exception -> " + e.getMessage());
} finally {
System.out.println("Finally");
}
try {
System.out.println("Try to do something");
throw new RuntimeException("RuntimeException");
} catch (Exception e) {
System.out.println("Catch Exception -> " + e.getMessage());
// 终止当前正在运行的Java虚拟机
System.exit(1);
} finally {
System.out.println("Finally");
}
try-with-resources
代替try-catch-finally
?适用范围(资源的定义): 任何实现 java.lang.AutoCloseable
或者 java.io.Closeable
的对象
关闭资源和 finally 块的执行顺序: 在 try-with-resources
语句中,任何 catch 或 finally 块在声明的资源关闭后运行
Java 中类似于InputStream
、OutputStream
、Scanner
、PrintWriter
等的资源都需要我们调用close()
方法来手动关闭
//读取文本文件的内容 try-catch-finally
Scanner scanner = null;
try {
scanner = new Scanner(new File("D://read.txt"));
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (scanner != null) {
scanner.close();
}
}
//try-with-resources
try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
int b;
while ((b = bin.read()) != -1) {
bout.write(b);
}
}catch (IOException e) {
e.printStackTrace();
}
Java 泛型(Generics) 是 JDK 5 中引入的一个新特性。使用泛型参数,可以增强代码的可读性以及稳定性。
ArrayList persons = new ArrayList()
这行代码就指明了该 ArrayList
对象只能传入 Person
对象,如果传入其他类型的对象就会报错。List
返回类型是 Object
,需要手动转换类型才能使用,使用泛型后编译器自动转换泛型一般有三种使用方式:泛型类、泛型接口、泛型方法。
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//泛型类
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
Generic<Integer> genericInteger = new Generic<Integer>(123456);
public interface Generator<T> {
public T method();
}
//泛型接口
public interface Generator<T> {
public T method();
}
//实现泛型接口,不指定类型:
class GeneratorImpl<T> implements Generator<T>{
@Override
public T method() {
return null;
}
}
//实现泛型接口,指定类型:
class GeneratorImpl<T> implements Generator<String>{
@Override
public String method() {
return "hello";
}
}
//泛型方法:
public static < E > void printArray( E[] inputArray )
{
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
// 创建不同类型数组:Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray( intArray );
printArray( stringArray );
CommonResult
通过参数 T
可根据具体的返回类型动态指定结果的数据类型Excel
处理类 ExcelUtil
用于动态指定 Excel
导出的数据类型Collections
中的 sort
, binarySearch
方法)通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。
Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制
下面是通过 JDK 实现动态代理的示例代码,其中就使用了反射类 Method
来调用指定的方法。
public class DebugInvocationHandler implements InvocationHandler {
/**
* 代理类中的真实对象
*/
private final Object target;
public DebugInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println("before method " + method.getName());
Object result = method.invoke(target, args);
System.out.println("after method " + method.getName());
return result;
}
}
Annotation
(注解) 是 Java5 开始引入的新特性,可以看作是一种特殊的注释,主要用于修饰类、方法或者变量,提供某些信息供程序在编译或者运行时使用。
注解本质是一个继承了Annotation
的特殊接口:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
public interface Override extends Annotation{
}
注解只有被解析之后才会生效,常见的解析方法有两种:
@Override
注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。@Value
、@Component
)都是通过反射来进行处理的SPI 即 Service Provider Interface ,字面意思就是:“服务提供者的接口”,我的理解是:专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口。
SPI 将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。
很多框架都使用了 Java 的 SPI 机制,比如:Spring 框架、数据库加载驱动、日志接口、以及 Dubbo 的扩展实现等等。
通过 SPI 机制能够大大地提高接口设计的灵活性,但是 SPI 机制也存在一些缺点,比如:
ServiceLoader
同时 load
时,会有并发问题。需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。
序列化和反序列化常见应用场景:
综上:序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。
序列化协议对应于 TCP/IP 4 层模型的哪一层?
表示层做的事情主要就是对应用层的用户数据进行处理转换为二进制流。反过来的话,就是将二进制流转换成应用层的用户数据
对于不想进行序列化的变量,使用 transient
关键字修饰。
transient
关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient
修饰的变量值不会被持久化和恢复。
关于 transient
还有几点注意:
transient
只能修饰变量,不能修饰类和方法。transient
修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 int
类型,那么反序列后结果就是 0
。static
变量因为不属于任何对象(Object),所以无论有没有 transient
关键字修饰,均不会被序列化。//Java 中的 for-each 就是一个常用的语法糖,其原理其实就是基于普通的 for 循环和迭代器
String[] strs = {"JavaGuide", "公众号:JavaGuide", "博客:https://javaguide.cn/"};
for (String s : strs) {
System.out.println(s);
}
JVM 其实并不能识别语法糖,需要先通过编译器进行解糖,也就是在程序编译阶段将其转换成 JVM 认识的基本语法。
Java 中真正支持语法糖的是 Java 编译器而不是 JVM。com.sun.tools.javac.main.JavaCompiler的源码,compile()中有一个步骤就是调用
desugar()`,这个方法就是负责解语法糖的实现的。
Java 中最常用的语法糖主要有泛型、自动拆装箱、变长参数、枚举、内部类、增强 for 循环、try-with-resources 语法、lambda 表达式等。