异常
Exception 和 Error 有什么区别?
- 在Java当中,所有的异常都有一个共同的祖先,Throwable类,该类中有两个重要的子类即Exception和Error
- Exception:程序本身可以处理的异常,可以通过
catch
来进行捕获 - Error:属于程序无法处理的错误 。
Checked Exception 和 Unchecked Exception 有什么区别?
- 编译处理要求:Checked Exception要求在编译时进行处理。Unchecked Exception则不要求在编译是进行处理
- 处理方式:对于Checked Exception,处理通常通过try-catch语句块或者通过throws来声明。而Unchecked Exception,可以选择捕获并处理异常,也可以不处理
Throwable 类常用方法有哪些?
- String getMessage(): 返回异常发生时的简要描述
- String toString(): 返回异常发生时的详细信息
- String getLocalizedMessage(): 返回异常对象的本地化信息
try-catch-finally 如何使用?
- try块:用于捕获异常。
- catch块:用于处理 try 捕获到的异常。
- finally块:无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。
泛型
什么是泛型?有什么作用?
- 泛型是Java5引入的一个新特性,允许在编写代码时使用类型参数,有了泛型可以让代码能够灵活地适应不同的数据类型。
- 作用:
- 类型安全:泛型可以在编译的时候帮你检查你代码,确保代码当中用的数据类型是正确的
- 代码复用:由于泛型不依赖于特定的数据类型,使得代码可以被更多的复用
泛型的使用方式有哪几种?
- 泛型类
- 泛型接口
- 泛型方法
重点强调一下泛型方法:在泛型方法中有特殊的一类方法,即静态泛型方法如(public static < E > void printArray( E[] inputArray ) {})在这个方法中为什么要使用<E>
在这个方法当中,Java泛型只是一个占位符,必须在传递类型后才能使用。而类在实例化时才能真正的传递类型参数,由于静态方法是属于类的,其加载先与类的实例化,也就是说在静态方法加载完成后,类中的泛型还没有传递真正的类型参数。所以静态泛型方法是没有办法使用类上声明的泛型的。只能用自己声明的<E>
项目中哪里用到了泛型?
- 自定义接口通用返回结果 CommonResult<T> 通过参数 T 可根据具体的返回类型动态指定结果的数据类型
- 定义 Excel 处理类 ExcelUtil<T> 用于动态指定 Excel 导出的数据类型
为什么泛型中类型参数不能用基本数据类型而是要用引用类型?
- Java泛型设计的初衷就是提供编译时期时期的类型检查,而基本数据类型是值类型,无法满足泛型的要求
- 底层原理:泛型底层是通过擦除来实现的。编译器在编译时将泛型类型擦除为其边界类型(通常为Object)如果基本数据类型作为泛型参数,就会导致擦除工作不能完成。
反射
什么是反射?
- 反射是一种能力,允许程序在运行的过程中获取自身信息、动态地操作类的成员、方法和属性的能力
反射的优缺点?
优点:
- 灵活性和通用性:通过反射可以在运行时动态地获取和操作类的信息
- 动态性:通过反射,可以在运行时动态地创建对象、调用方法、访问字段等
缺点:
- 性能开销:反射是在运行时动态解析和调用,开销较大
- 类型安全性:由于反射是在运行时进行的,所以编译器无法对反射调用进行类型检查,可能会导致一些安全性问题
- 可读性和维护性:反射代码通常更加复杂,可读性较差
反射的应用场景?
- 框架和库的设计
- 序列化和反序列化
- 动态代理
注解
什么是注解?
- 注解是Java中的一种特殊形式的注释,以
@
符号开头,用来为程序元素(类、方法、字段等)添加元数据(元数据是描述数据的数据,它可以用来提供关于程序元素的补充性信息)
注解的解析方法有哪几种?
- 编译期直接扫描:编译器在编译 Java 代码的时候扫描对应的注解并处理。比如某个方法使用@Override 注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。
- 运行时反射:在程序运行时通过反射获取注解的类型、属性值等信息,并进行相应的处理。例如在依赖注入、AOP等方面的应用。
SPI
什么是SPI?
- Java提供的一种服务提供接口。它是一种动态加载机制,允许开发者为某个接口提供不同的实现,而无需修改原有的代码。
- 通过SPI,可以使得程序更加灵活和可扩展,实现了松耦合的设计。
SPI和API的区别?
SPI:
- SPI是一种Java编程语言提供的一种动态加载机制,允许开发者在不修改代码的情况下为程序提供新的功能或替换现有的实现。
- SPI通常涉及到定义接口、提供多个不同实现、以及通过配置文件来指定实现类的加载方式
API:
- API是一组定义了软件组件之间交互的规则和约定的接口
- 开发者可以通过调用API中定义的方法来实现特定的功能。
序列化和反序列化
什么是序列化?什么是反序列化?
- 序列化:将数据结构或对象转换成二进制字节流的过程
- 反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
序列化和反序列化常见应用场景:
- 对象在进行网络传输(比如远程方法调用 RPC 的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
- 将对象存储到数据库(如 Redis)之前需要用到序列化,将对象从缓存数据库中读取出来需要反序列化;
序列化协议对应于 TCP/IP 4 层模型的哪一层?
四层模型分别为:
- 应用层(应用层、表示层、会话层)、传输层、网络层、网络接口层(数据链路层、物理层)
七层模型分别为:
- 应用层:为计算机用户提供服务
- 表示层:数据处理(编解码、加密解码、压缩解压缩)
- 会话层:管理应用程序之间的会话
- 传输层:为两台主机进程之间的通信提供通用的数据传输服务
- 网络层:路由和寻址(决定数据在网络的游走路径)
- 数据链路层:帧编码和误差纠正控制
- 物理层:透明地传送比特流传输
表示层做的事情主要就是对应用层的用户数据进行处理转换为二进制流。反过来的话,就是将二进制流转换成应用层的用户数据。因此序列化和反序列化发生在这一层
因为,OSI 七层协议模型中的应用层、表示层和会话层对应的都是 TCP/IP 四层模型中的应用层
如果有些字段不想进行序列化怎么办?
- 对于不想进行序列化的变量,使用 transient 关键字修饰。
- transient 关键字的作用是:表示这些字段在序列化过程中不会被包含。告诉序列化引擎在序列化对象时忽略这些字段。
关于transient需要注意的是?
- transient 只能修饰变量,不能修饰类和方法。
- transient 修饰的变量,在反序列化后变量值将会被置成类型的默认值。
- static 变量因为不属于任何对象(Object),所以无论有没有 transient 关键字修饰,均不会被序列化。
常见序列化协议有哪些?
- JSON
- XML
- protobuf
- ProtoStuff
为什么不推荐使用 JDK 自带的序列化?
- 不支持跨语言调用
- 性能差
- 存在安全问题
I/O
Java的I/O流了解吗?
- IO 即 Input/Output,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储的过程即输出。根据数据的处理方式又分为字节流和字符流。
- InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
- OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流
I/O 流为什么要分为字节流和字符流呢?
- 字节流处理的是原始的字节数据,而字符流处理的是经过编码的字符数据
- 使用字符流能够更方便地处理文本数据,而字节流则更适合处理二进制数据
- 选择合适的流取决于你要处理的数据类型。
Java IO 中的设计模式有哪些?
- 装饰器模式:用于在不修改现有对象结构的情况下动态地添加功能
- 工厂模式:用于封装对象的创建过程,使客户端代码与具体对象的创建过程解耦。
- 观察者模式:用于对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会自动更新
BIO、NIO 和 AIO 的区别?
- BIO:同步阻塞 I/O 模型,适用于连接数目少且固定的场景
- NIO:同步非阻塞 I/O 模型,适用于连接数目多且连接时间短的场景
- AIO:异步非阻塞 I/O 模型,适用于连接数目多且连接时间长的场景
语法糖
什么是语法糖?
- 语法糖是指编程语言中的一种语法结构,它并不提供新的功能,只是为了让代码更易读、更易写,从而提高开发效率,使得代码更加简洁清晰。
- 举例来说,Java 中的增强型 for 循环,迭代器。都可以被视为语法糖。它们并不是新增了一种迭代的机制,而只是提供了一种更简洁的语法形式来实现相同的功能。
Java 中有哪些常见的语法糖?
- Java 中最常用的语法糖主要有泛型、自动拆装箱、变长参数、枚举、内部类、增强 for 循环、lambda 表达式等。