当前位置: 首页 > news >正文

2024面试offer收割宝典字节篇

1.IO 模型有哪些,讲讲你理解的 nio ,他和 bio,aio 的区别是啥, 谈谈 reactor 模型。

IO 模型主要包括以下几种:
1. 阻塞 I/O (BIO):

  • 当一个线程调用 read()write() 系统调用时,如果数据没有准备好或者缓冲区已满,则该线程会被操作系统阻塞,直到有数据可读或写入完成。
  • 在Java中传统的java.io包中的类如SocketServerSocket就是基于BIO模型实现的。

2. 非阻塞 I/O (NIO):

  • Java NIO(New Input/Output)在Java 1.4版本引入,提供了非阻塞的I/O能力。
  • 使用NIO,当调用channel.read()channel.write()方法时,若当前无数据可读或缓冲区满不能立即写入,不会阻塞线程,而是会返回一个状态值(通常是0,表示没有数据可读或没有足够的空间写入)。
  • NIO支持单线程处理多个Channel(通道),通过Selector可以监听多个Channel的事件,一旦某个Channel准备就绪进行读写操作,Selector就会通知相应的线程去处理。
  • NIO的核心组件包括Buffer(缓冲区)、Channel(通道)和Selector(选择器)。

3. 异步 I/O (AIO, NIO.2 或 AIO in Java):

  • 异步非阻塞I/O在Java 7中引入,也被称为NIO.2。
  • 在AIO模式下,当发起一个读写请求后,应用程序可以继续执行其他任务,而无需等待操作结果。
  • 当数据准备完毕或操作完成时,操作系统会回调预先注册的CompletionHandler(完成处理器)来处理数据或处理完成后的逻辑。
  • 这种模型极大地提高了系统并发处理能力和资源利用率,特别适合于高并发、低延迟的应用场景。

Reactor 模型是一种设计模式,常用于处理大量并发I/O请求。它通常配合NIO或AIO使用,在服务器端能够同时监听多个连接请求,并且根据不同的事件类型分发给相应的处理器进行处理。基本结构包括:

  • Dispatcher:负责监听并分发事件到对应的Handler。
  • Handlers:处理实际的I/O事件,如读取数据、写入响应等。
  • Event Demultiplexer:在Java NIO中对应的是Selector,它可以监控多个Channel上的事件,并在事件发生时通知Dispatcher

总结来说,BIO、NIO与AIO的主要区别在于线程模型和阻塞行为上

  • BIO是一个连接一个线程,容易造成线程资源浪费,性能瓶颈明显。
  • NIO在一个线程内可以管理多个连接,通过非阻塞方式减少无效等待时间,但程序需要自行轮询检查数据是否准备好。
  • AIO则更进一步,由操作系统直接负责数据的读写操作,并在完成后通知应用,从而避免了不必要的上下文切换,理论上能提供最高的并发性能。

 


2.反射的原理,反射创建类实例的三种方式是什么。

 反射是Java中的一项重要特性,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的各种属性和功能

在运行时,Java反射机制可以加载类、获取类的信息(如类名、方法、字段等)、实例化对象以及调用方法等。
反射创建类实例的三种主要方式如下:

1. 通过无参构造函数创建:

   // 假设我们有一个名为Book的类Class<?> bookClass = Class.forName("com.example.Book");Constructor<?> constructor = bookClass.getConstructor();  // 获取无参构造器Book book = (Book) constructor.newInstance();  // 调用无参构造器创建实例

2. 通过带参数的构造函数创建:

   Class<?> bookClass = Class.forName("com.example.Book");Constructor<Book> constructor = bookClass.getDeclaredConstructor(String.class, String.class);  // 获取带有String类型参数的私有构造器constructor.setAccessible(true);  // 如果构造器是私有的,需要设置为可访问Book book = constructor.newInstance("The Title", "The Author");  // 使用指定参数创建实例

3. 通过Class的newInstance()方法创建:

   // 注意:这种方式仅适用于具有默认(无参或public)构造函数的类Class<Book> bookClass = Book.class;try {Book book = bookClass.newInstance();} catch (InstantiationException | IllegalAccessException e) {// 处理异常,例如如果类是抽象类或者没有默认构造函数e.printStackTrace();}

 上述代码展示了如何通过反射API动态地根据类信息创建类的实例。需要注意的是,对于非public的构造函数或其他成员,可能需要调用setAccessible(true)来绕过访问控制检查


3.反射中,Class.forName 和 ClassLoader 区别 。

在Java反射机制中,Class.forName() 方法和 ClassLoader 类加载器都用于动态加载类,但它们之间存在一些区别:
1. Class.forName():

  • 这是一个静态方法,位于 java.lang.Class 类中。
  • 它接受一个字符串参数,该参数是完全限定类名(包括包名)。
  • 当调用 Class.forName(className) 时,它会按照当前线程的上下文类加载器(context class loader)自动寻找并加载指定名称的类,并初始化该类。如果类还未被加载过,则执行类加载过程,包括验证、准备、解析和初始化阶段。
  • 如果类中有静态初始化块或静态字段会被初始化。
  • 通常抛出 ClassNotFoundException,如果找不到指定的类。

2. ClassLoader.loadClass() 或 ClassLoader.findSystemClass() 等相关方法:

  • ClassLoader 是 Java 中用来加载类的抽象类,每个类在运行时都有一个与之关联的类加载器。
  • 使用自定义的 ClassLoader 实例或者系统的默认类加载器,可以更灵活地控制类的加载过程,例如实现自己的类加载逻辑、支持热加载等高级功能。
  • 调用如 ClassLoader.loadClass(className) 不会立即初始化类,仅完成类的加载过程,即验证、准备、解析阶段,而不会执行类的初始化(构造静态变量和执行静态初始化块)。
  • 可以通过调用 Class.forName(className, initialize, classLoader) 来控制是否进行初始化操作,其中 initialize 参数为布尔值。
  • 同样可能抛出 ClassNotFoundException

总结来说,两者的主要区别在于:

  • Class.forName() 默认情况下包含了完整的类加载和初始化过程,更加直观易用,常用于简单场景下的类加载。
  • ClassLoader 提供了更底层和灵活的类加载能力,可以根据需要定制加载策略,且默认情况下不进行类的初始化。

4.描述动态代理的几种实现方式,分别说出相应的优缺点。

在Java中,动态代理有两种主要的实现方式:JDK动态代理和CGLIB代理
1. JDK动态代理:

  • 原理:基于Java反射机制实现。它通过InvocationHandler接口创建一个代理类,该代理类在运行时继承自Proxy类,并实现了与目标对象相同的接口。
  • 优点:
  • 由于是基于接口的,所以可以很方便地为一组具有相同接口的目标对象生成代理,适用范围广。
  • 实现代理逻辑比较简单,只需要实现InvocationHandler接口即可。
  • 不需要预先知道所有要代理的方法,代理逻辑更加灵活。
  • 缺点:
  • 需要目标类实现接口,对于没有接口的类无法进行代理。
  • 由于使用了反射,在性能上相比于直接调用有一定损耗。
  • 功能相对简单,不能针对类的成员变量进行操作。

2. CGLIB代理:

  • 原理:基于ASM字节码生成框架,能够在运行期对字节码进行修改或动态生成新的类。CGLIB代理生成的代理类是被代理类的一个子类。
  • 优点:
  • 不依赖于接口,能够实现代理没有实现任何接口的目标类,适用性更广泛。
  • 相比于JDK动态代理,CGLIB代理功能更加强大,可以拦截方法、修改方法等。
  • 缺点:
  • CGLIB代理会对类的所有方法进行增强,即使不希望增强的方法也会被代理,这可能造成额外的性能开销。
  • 如果目标类做了final修饰或者有final方法,则无法进行代理。
  • CGLIB库不是Java标准库的一部分,需要单独引入依赖。
  • 字节码操作技术相比反射更为复杂,如果出现问题调试起来较为困难。

总结来说,JDK动态代理更适合接口较多且不需要对非接口方法进行增强的情况,而CGLIB代理则在不需要接口或需要对类的所有方法进行增强时更加适用。在实际应用中,如Spring框架会根据目标对象是否有接口智能选择使用哪种动态代理策略。


5.动态代理与 cglib 实现的区别。

JDK动态代理实现:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class JdkProxyExample {interface MyInterface {void doSomething();}static class TargetClass implements MyInterface {@Overridepublic void doSomething() {System.out.println("Target: Doing something...");}}static class LoggingInvocationHandler implements InvocationHandler {private final Object target;public LoggingInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method call: " + method.getName());Object result = method.invoke(target, args);System.out.println("After method call: " + method.getName());return result;}public static MyInterface createLoggingProxy(TargetClass target) {return (MyInterface) Proxy.newProxyInstance(TargetClass.class.getClassLoader(),new Class<?>[]{MyInterface.class},new LoggingInvocationHandler(target));}}public static void main(String[] args) {MyInterface target = new TargetClass();MyInterface proxy = LoggingInvocationHandler.createLoggingProxy(new TargetClass());proxy.doSomething();  // 输出日志并调用目标方法}
}

 在上述例子中,LoggingInvocationHandler实现了InvocationHandler接口,当代理对象的方法被调用时,会触发invoke方法,在该方法中添加了前后打印日志的逻辑,并实际调用了目标类的方法。

 

 CGLIB代理实现:

首先需要引入CGLIB库(例如使用Spring框架中的CGLIB代理支持):

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibProxyExample {static class TargetClass {public void doSomething() {System.out.println("Target: Doing something...");}}static class LoggingMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method call: " + method.getName());Object result = proxy.invokeSuper(obj, args);  // 调用父类(即目标类)的方法System.out.println("After method call: " + method.getName());return result;}public static TargetClass createLoggingProxy(TargetClass target) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(TargetClass.class);enhancer.setCallback(new LoggingMethodInterceptor());return (TargetClass) enhancer.create();}}public static void main(String[] args) {TargetClass target = new TargetClass();TargetClass proxy = LoggingMethodInterceptor.createLoggingProxy(new TargetClass());proxy.doSomething();  // 输出日志并调用目标方法}
}

在这个CGLIB代理的例子中,我们创建了一个LoggingMethodInterceptor类,它实现了MethodInterceptor接口。当代理对象的方法被调用时,会触发intercept方法,在这个方法里同样添加了日志输出,并通过MethodProxy调用了目标类的实际方法。CGLIB库通过字节码增强技术动态生成了继承自TargetClass的新类作为代理对象。

http://www.lryc.cn/news/302715.html

相关文章:

  • 冒泡排序及其优化
  • 【医学大模型 补全主诉】BioGPT + LSTM 自动补全医院紧急部门主诉
  • HCIE-Datacom证书有效期多久?HCIE考试有哪些内容?
  • OpenCV中的边缘检测技术及实现
  • 机器学习基础(一)理解机器学习的本质
  • Eclipse - Makefile generation
  • Sora:新一代实时音视频通信框架
  • 龟兔赛跑算法
  • Yii2项目使用composer异常记录
  • 【蓝桥杯 2021】图像模糊
  • 【leetcode】贪心算法介绍
  • com.alibaba.fastjson.JSONException: toJSON error的原因
  • 华为配置旁挂二层组网直接转发示例
  • OLMo 以促进语言模型科学之名 —— OLMo Accelerating the Science of Language Models —— 全文翻译
  • 单例模式双端检测详解
  • 秦PLUS荣耀版7.98万元起震撼上市,拉开“电比油低”大幕
  • 学习总结19
  • rancher v2.8.1 如何成功注册已有 k8s 集群
  • Vue中$root的使用方法
  • redis 异步队列
  • SpringBoot + Nacos 实现动态化线程池
  • 《Docker极简教程》--Dockerfile--Dockerfile的基本语法
  • css中, grid-auto-rows: 怎样简写在grid:中
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • 算法-矩阵置零
  • xilinx除法器的使用
  • 算法沉淀——递归(leetcode真题剖析)
  • BERT模型中的input_ids和attention_mask参数
  • java+vue_springboot企业设备安全信息系统14jbc
  • vulhub中Apache Log4j Server 反序列化命令执行漏洞复现(CVE-2017-5645)