2025最新Java基础八股文速记精简版(万字笔记)
Java的特点:
平台无关性:“编写一次,运行无处不在”,编译器将代码编译为字节码,只要安装jvm就可以运行
面向对象:将一切事物抽象为对象来描述
内存管理:有自己的垃圾回收机制,减少内存问题
Java优势和劣势:
优势:跨平台,面向对象,强大的生态系统,内存管理(垃圾回收机制),内置的线程机制(多线程支持),安全性好(Java有安全模型比如沙箱机制,适合网络环境),稳定性
劣势:性能相比来说较低,语法繁琐,内存消耗(JVM占用),面向对象严格,开发效率低
Java为什么跨平台:
JVM相当于软件,不同平台有不同版本。Java源码编译后生成.clas文件(字节码文件),jvm为中间件,可以把字节码文件翻译为机器码,即使是.exe文件任然需要JVM支持
JVM、JDK、JRE三者关系:
JVM是java运行环境,负责将字节码(由编译器编译)翻译为机器码,JVM提供内存管理、垃圾回收、安全性功能
JRE是Java程序运行时所需最小环境,包含JVM和一组Java类库
JDK是开发工具包,包含JVM、编译器(javac)、调试器(jdb)等开发工具,以及一系列的类库(如java标准库和开发工具库)。提供开发、编译、调试和运行程序全部工具和环境
为什么Java解释和编译都有:
Java源代码(.java
文件)通过编译器(如javac
)生成字节码(.class
文件,JVM默认使用解释器逐行解释字节码并执行。JVM通过方法调用计数器和回边计数器统计代码执行频率,当某段代码(如循环、高频方法)的调用次数超过阈值(例如10,000次),JIT编译器将热点代码编译为与当前平台相关的本地机器指令,并存储在方法区的CodeCache中,后续直接执行机器码,大幅提升性能(接近C/C++速度)。
JVM是什么:
是Java虚拟机,主要解释自己的指令集(字节码)并映射到本地的CPU指令集和OS的系统调用,
屏蔽不同操作系统差异性,不修改代码即可运行
编译型语言和解释型语言的区别:
编译型语言:在程序执行之前,整个源代码都会被编译成机器码或者字节码并生成可执行文件。执行时直接运行编译后的代码,速度快但跨平台性差(C,C++)
解释型语言:在程序执行时,逐行解释源代码,不生成独立执行文件。由解释器动态解释并执行代码,跨平台性好但执行速度相对较慢(Python、JavaScript)
Python和Java的区别:
Java是一种已编译的语言,Java编译器将源代码编译为字节码,而字节码由JVM执行
Python是一种解释语言,翻译时会在执行程序的同时进行翻译
数据类型
基本数据类型:
byte、short、int、long、float、double
char
boolean
数据类型转换方式:
自动类型转换(隐式转换):小转大如int→long,float→double
强制类型转换(显示转换):大转小,可能导致数据丢失或溢出
字符串转换:Java提供字符串转其它类型的方法
数值之间的传唤:整型转字符,字符转整形
类型转换出现问题:
数据丢失:大转小时,可能数据丢失,如long转int,结果为截断后的地位部分,高位数据将丢死
数据溢出:一个变量存储的数值超过了它所能表示的最大(或最小)范围,导致结果错误或异常。 精度丢失:浮点转换时,单精度转双精度可能产生
类型不匹配:源和目标类型不兼容
bigDecimal :
精确计算小数
装箱和拆箱:
装箱和拆箱时将基本数据类型和对应包装类型互相转换的过程
主要发生在两种情况,一种时赋值时编译器自动装箱拆箱,一种是方法调用时也会自动装箱拆箱参数和返回值
自动装箱的弊端:
Java为什么有Interger:
基本数据类型封装为对象将数据和处理这些数据的方法结合,Java中绝大部分方法或类都是用来处理类型对象的
泛型中:泛型只能使用引用,不能使用基本类型
**转换中:**基本和引用不能直接转换,必须使用包装类(int先转Interger才能转String)
集合中:集合只能存储对象
Interger对比int
基本和引用:int是基本数据类型,是预定义的,无需实例化,不需要额外分配内存,Interger需要实例化,需要分配内存。int操作更快
自动装拆箱:
空指针异常:int可以赋0,如果对一个未初始化的Interger对象操作出现空指针异常
为什么保留int类型
基本数据类型变量对应的内存块直接存储数据类型,读写效率高,占用空间小(Interger占16字节)
Interger缓存:
Interger类内部实现一个静态缓存池,用于存储特定范围内的整数值对应的Interger对象
默认情况下范围(-128到127)。当Integer.valueof(int)方法创建一个此范围内的整数对象,并不会每次生成新的实例,而是复用缓存中的现有对象,直接从内存取出。
面向对象
如何理解面向对象,简单说说三大特性:
面向对象将现实世界事务抽象为对象,对象有属性和行为,以对象为中心,通过对象交互完成程序功能,封装继承提供扩展
封装:对外颖仓内部细节,仅通过对象提供的接口与外界交互。目的是增强安全性简化编程,使对象更独立
继承:使子类共享弗雷数据结构和方法的机制,代码复用重要手段,建立类与类的层级关系
多态:允许不同类的对象对同一消息进行响应。即同一个接口,使用不同实例执行不同操作。
分为编译时多态(重载)和运行时多态(重写)。灵活性拓展性高
多态体现:
重载:多个同名方法有不同方法签名(编译时多态)
重写:子类覆盖父类同名方法具体实现,运行时JVM根据对象实际类型确定调用哪个版本的方法(运行时多态)
接口与实现:多个类实现一个接口,根据不同实例调用不同方法(运行时多态)
向上和向下转型:用父类引用指向子类对象,向上转型。运行时实例为子类对象(运行时多态)
多态解决了哪些问题:
多态指子类可以替换父类,在代码运行中调用子类实现,提高扩展性复用性,是很多设计模式、设计原则、编程技巧的代码实现基础,比如策列模式、基于接口而非实现编程、依赖倒置原则、里式替换原则、利用多态去掉if-else等
面向对象的设计原则:
单一职责:一个类只负责一项职责
开放封闭:软件实体对扩展开放,对修改封闭,比如:定义图形类,让不同图形子类继承,不需要修改图形类本身
里氏替换:子类应该能够替换所有父类对象
接口隔离:客户端不应该以来那些它不需要的接口,接口应该小而专
依赖倒置:高层模块不应该依赖低层模块,细节应该依赖于抽象
最少知识:一个对象只与其最直接的对象交互
重写重载:
重载:同类同名函数,不同方法签名,不同返回类型
重写:子类覆盖父类同名同签名方法同返回类型
抽象与普通类:
实例化:抽象类不能被实例化,只能继承
方法实现:抽象类可以没有实现也可以有
实现限制:抽象类一般用于基类
抽象类和接口
抽象类描述类共同特性和行为,可以有变量,构造方法,具体方法,抽象方法。适用于有明显继承关系时
接口定义行为规范,只能有常量和抽象方法。适用于定义类的功能
区别:
实现方式:implements和extends,可实现多个接口,只能继承一个类
方法实现:接口无方法实现,抽象类可以有
访问修饰符:接口成员变量默认为public static final,必须赋初始值,不可修改,所有成员方法都是public abstract的。抽象类成员变量默认default,可在子类呗重新定义,可以重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native修饰,必须分号结尾,不带花括号
变量:抽象类可以包含实例变量和静态变量,接口只能包含常量(即静态常量)
总结:Java 设计接口时强制接口变量为 public static final
,是为了保持接口的纯粹性与抽象性,使其只描述能力而不持有状态。而抽象类作为类的特化形式,可以提供成员变量和部分实现,是实现代码复用和继承的基础结构。
接口可定义的方法:
抽象方法:默认是这个,修饰符可省略
默认方法:允许接口默认实现
静态方法:属于接口本身
私有方法:为默认方法或其它私有方法提供辅助,只在接口内部使用
抽象类可以实例化嘛:
不可以实例化,它主要是为了被继承,可以有构造器,在子类中被调用
静态方法和静态变量
静态变量属于类:常用于需要在所有对象间共享的数据,如计数器、常量等
共享性:所有类实例共享一个
初始化:旨在类被加载时初始化,只对其进行一次分配内存
访问方式:类名(推荐)或实例
静态方法属于类:常用于助手方法、获取类级别的信息或者是没有依赖于实例的数据处理
无实例依赖:
访问静态成员:
多态性:静态方法不支持重写,可以隐藏,当子类定义了一个与父类静态方法同名同参的静态方法时,子类的那个方法不会覆盖父类方法,而是“隐藏”它:谁的类名调用,执行谁的方法
非静态内部类和静态内部类区别
非静态内部类依赖于外部类的实例,而静态内部类不依赖于外部类的实例。
非静态内部类可以访问外部类的实例变量和方法,而静态内部类只能访问外部类的静态成员。
非静态内部类不能定义静态成员,而静态内部类可以定义静态成员。 非静态内部类在外部类实例化后才能实例化,而静态内部类可以独立实例化。
非静态内部类可以访问外部类的私有成员,而静态内部类不能直接访问外部类的私有成员,需要通过实例化外部类来访问
非静态内部类可以直接访问外部方法,编译器是怎么做到的?
编译器在生成字节码时为非静态内部类维护一个指向外部类实例的引用
编译器会在生成非静态内部类构造方法时,将外部类实例作为参数传入,在内部类实例化过程中建立与外部类实例联系
关键字final
修饰类:不可被继承
修饰方法:不可重写
修饰变量:基本数据类型不可修改,引用类型不能指向其它对象
实现深拷贝三种方式
实现Clonable接口重写clone()方法,递归克隆引用类型字段实现深拷贝
序列化和反序列化
手动递归复制
为什么需要泛型
适用于多种数据类型执行相同代码
泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
对象创建方式
new关键字
Class类的newInstance()方法(通过反射机制):
Constructor类的newInstance()方法(反射获取类的构造函数)
使用clone()方法(实现Cloneable)
使用反序列化:通过将对象序列化到文件或流中,然后再进行反序列化来创建对象。
new出的对象什么时候回收:
垃圾回收器周期性检测不再被引用的对象,将其回收
引用技术:某个对象的引用计数为0
可达性分析算法:从根对象触发,通过对象之间的引用链遍历,如果无法达到某个对象,则回收】
什么是反射
运行时,对于任意类,都可以获得类的完整信息(类名、包名、父类、接口、构造函数、方法、字段),可以调用它任意方法属性;动态获取类信息及动态调用对象
反射特性:
运行时类型信息访问、动态对象创建(Class类的newInstance(),Constructor对象的newInstance())、动态方法调用(Method的invok())、访问修改字段值(Filed的get()set())
反射应用场景:
加载数据库驱动:
在一个项目中,底层数据库可能不固定,有时用 MySQL,有时用 Oracle。因此,数据库驱动类需要动态加载,不能写死。通过 Java 的 Class.forName(String className)
方法来实现反射加载驱动类。
配置文件加载:
SpringIOC创建Bean实例
注解
注解原理
一个继承了Annotation的特殊接口,其具体实现类是运行时生成的动态代理类
通过反射获取注解时,返回的是Java运行时生成的动态代理对象,通过代理对象调用自定义注解的方法,最终会调用AnnotationInvocationHandler的invoke方法。该方法从memberValues这个map中索引处对应的值,而memberValues的来源是Java常量池
注解解析的底层实现
注解本质是接口,被叫做声明式接口
作用范围:
源码级:仅在源码,编译后不保留(@Retention(RetentionPolicy.SOURCE)
) 类文件级:保留着.class文件,但运行时不可见(@Retention(RetentionPolicy.CLASS)
)
运行时:保留在.class,通过反射在运行时访问(@Retention(RetentionPolicy.RUNTIME)
)
当注解被标记为RUNTIME时,编译器会在生成的.class文件保存注解信息,信息存储在字节码的属性表中,包括以下内容:
RuntimeVisibleAnnotations :存储运行时可见的注解信息
RuntimeInvisibleAnnotations :存储运行时不可见的注解信息
RuntimeVisibleParameterAnnotations 和RuntimeInvisibleParameterAnnotations :存储方法参数上的注解信息
通过工具(如:javap -v)可以查看.class文件中的注解信息,注解的解析依赖反射。
解析注解基本流程:
1.获取注册信息:反射获取类,方法,字段等元素上的注解
2.底层原理:反射机制的核心类是java.lang.reflect.AnnotatedElement,他是所有可以被注解修饰的元素(Class、Method、Filed)的父接口,该接口提供以下方法:
getAnnotation(Class<T> annotationClass):获取指定类型的注解
getAnnotations():获取所有注解
isAnnotationPresent(Class<? extends Annotation> annotationClass):判断是否包含指定注解
这些方法底层实现依赖于JVM提供的本地方法。
JVM在加载类时会解析.class文件中的注解信息,并将其存在内存中,供反射机制使用
总结:注解底层实现主要依赖反射机制和字节码文件的存储,通过@Retention元注解可以控制注解的保留策略。从JVM层面,会从字节码文件中读取注解信息,并创建注解的代理对象来获取注解的属性值。
注解的作用域:
1.类级别:描述类,可以指定类的一些属性,如:访问级别、继承关系、注解
2.方法级别:描述方法,指定方法属性如:访问级别、返回值类型、异常类型、注释
2.字段级别:描述字段,指定字段属性如:访问级别、默认值、注释
除了这三种,还有一些其它的,如:构造函数作用域和局部变量作用域。它们可以用来对构造函数和局部变量进行描述和注释
异常
介绍异常
Java异常主要基于两大类:Throwable类及其子类Error和Exception
Error:表示运行时环境错误。无法处理的问题如:系统崩溃、虚拟机错误、动态链接失效。比如:OutOfMemoryError、StackOverflowError
Exception: 程序本身可以处理
运行时异常:包括运行时异常和错误。如空指针、数组越界。不需要在编译时强制捕获声明
非运行时异常:在编译时期必须捕获或声明抛出。通常为外部错误如:文件不存在、类未找到。
Java处理异常方式:
try-catch:捕获并处理可能抛出的异常
throw:手动抛出异常
thorws:在方法声明中声明可能抛出的异常类型,由方法调用者处理
抛出异常为什么不用Throws
如果异常是未检查异常或者在方法内部处理就不需要使用它
try{return “a”} fianlly{return “b”}这条语句返回啥
finally的return会覆盖try块的return
Object
== 与 equals有什么区别
==比较内存地址(堆内存),quals默认比较地址,但很多类重写后比较内容
hashCode和equals关系
一般来说,重写equals方法的类也要重写hashCode并遵循以下约定:
一致性:如果对象使用equals方法比较相同,那么它们hashCode必须相同
非一致性:如果hashCode相同,equals不一定相同
String、StringBuffer、StringBuilder
可变性:String不可变,每次修改生成一个新的对象,StringBuffer和StringBuilder可变
线程安全:String不可变所以线程天然安全。StringBuffer安全,通过synchronized关键字实现同步、StringBulider不安全
性能:String(因为不可变)<StringBuffer(因为引入线程安全)<StringBuilder(因为可变且单线程)
使用场景:固定String,修改且单线程StringBuilder,修改且多线程StringBuffer
Stream的流并行API是什么:
是ParallelStream
并行流将源数据分为多个子流对象多线程操作,最后将结果汇总为一个流对象,底层使用fork/join池实现
对CPU密集型任务来说,并行流使用ForkJoinPool线程池,为每个CPU分配一个任务,这是很有效率的,但如果是I/O密集,且任务数相对线程数比较大,用并行流不好
completableFuture是怎么使用的:
completableFuture由Java8引入,在此之前通过Future实现异步
Future用于表示异计算的结果,只能通过阻塞或者轮询的方式获得结果,并且不支持设置回调方法,Java8之前如果要设置回调一般会使用guava的ListenableFuture,回调的引入又会导致回调地狱
CompetableFuture对Future进行扩展,支持设置回调方式处理计算结果,同时支持组合操作,支持进一步编排,一定程度解决回调地狱问题
// 创建一个固定大小的线程池,包含5个线程
ExecutorService executor = Executors.newFixedThreadPool(5);// 使用CompletableFuture.supplyAsync创建一个异步任务,指定执行线程池
// 该任务将在1秒后返回"Hello"
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {try {// 模拟任务处理时间Thread.sleep(1000);} catch (InterruptedException e) {// 异常处理e.printStackTrace();}return "Hello";
}, executor);// 使用CompletableFuture.supplyAsync创建另一个异步任务,指定执行线程池
// 该任务将在2秒后返回"World"
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {try {// 模拟任务处理时间Thread.sleep(2000);} catch (InterruptedException e) {// 异常处理e.printStackTrace();}return "World";
}, executor);// 使用thenCombine方法组合两个CompletableFuture
// 当cf1和cf2都完成时,执行该方法,并接收两个结果作为参数
cf1.thenCombine(cf2, (s1, s2) -> {// 打印两个字符串组合的结果System.out.println(s1 + " " + s2);// 返回新的结果return "re3";
});
CompetableFuture实现两个接口(Future、CompletionStage)
Future表示异步计算的结果,CompletionStage用于表示异步执行过程中的一个步骤(Stage),这个步骤可能是由另外一个Completion触发的,随着当前步骤完成,也可能触发其它一系列CompletionStage的执行
从而我们可以根据实际业务对这些步骤进行多样化的编译组合,CompletionStage接口定义了这样的能力,我们可以通过thenApply、thenCompose等函数式编程方法组合编排这些步骤
Java21新特性:
Switch语句的模式匹配:允许switch的case标签中使用类型模式匹配,减少样板代码和潜在错误
static void printValue(Object obj) {switch (obj) {case String s -> System.out.println("String length: " + s.length());case Integer i -> System.out.println("Double value: " + i * 2);case null -> System.out.println("Null value");default -> System.out.println("Unknown type");}
}
数组模式:将模式匹配扩展到数组,可高效解构检查数组内容如:if(arr instanceof int[] {1,2,3}),可以直接判断arr是否匹配指定的模式
字符串模板:hello {name}, welcome to the geeksforgeeks!
虚拟线程:轻量级并发,通过共享堆栈降低内存消耗,提高程序吞吐量和响应速度
范围值:提供在线程中贡献不可变数据新方式,避免传统线程既不存储,促进封装和线程安全,用于不通过方法传参时传递上下文,如用户会话或配置设置
序列化
如何将一个对象从一个jvm转移到另一个jvm
序列化和反序列化:将对象序列化为字节流,发送到另一个jvm后反序列化。通过 Java 的 ObjectOutputStream 和 ObjectInputStream 实现
消息传递:消息队列或网络套接字。许哟啊自定义序列化协议
远程调用RPC:通过远程调用框架实现对象在不同jvm传输
共享数据库或缓存:
序列化和反序列化你自己实现会怎么做:
Java默认的工具存存在问题:无法跨语言、容易被攻击、序列化后流太大占用资源
考虑使用主流框架FastJson、Protobuf(性能非常优秀)
将对象转换为二进制流具体实现:
序列化协议定义序列化后的字节流格式
ObjectOutputStream.writeObject()序列化
ObjectIutputStream.readObject()反序列化
目标对象实现Serializable接口
try {FileOutputStream fileOut = new FileOutputStream("object.ser");ObjectOutputStream out = new ObjectOutputStream(fileOut);out.writeObject(new Acm());out.close();fileOut.close();} catch (Exception e) {e.printStackTrace();}try {FileInputStream fileIn = new FileInputStream("object.ser");ObjectInputStream in = new ObjectInputStream(fileIn);Acm acm = (Acm) in.readObject();in.close();fileIn.close();System.out.println(acm);} catch (Exception e) {e.printStackTrace();}
对象obj会被序列化并写入到文件"object.ser"中,然后通过反序列化操作,从文件中读取并回复对象
设计模式
volatile和sychronized如何实现单例模式
private static volatile SingleTon instance = null;private SingleTon() {}public static SingleTon getInstance() {if (instance == null) {synchronized (SingleTon.class) {if (instance == null) {instance = new SingleTon();}}}return instance;}
双重判定保证单例
volatile主要包含两个功能:保证可见性、禁止指令重排
由于volatile禁止对象创建时指令重排序,保证其它线程不会访问一个未初始化的对象,保证安全性
代理模式和适配器模式区别:
目的:代理模式关注控制对对象的访问,是配置用于接口转换,使不兼容的类一起工作
结构:代理模式一般包含抽象主题、真实主题和代理三个角色,适配器模式包含目标接口,适配器和适配者三个角色
应用场景:代理常用于添加额外功能或控制对象的访问,适配器模式用于让不兼容接口协同工作
I/O
Java如何实现网络IO高并发编程:
可以用NIO,一种同步非阻塞的I/O模型,也是I/O多路复用基础
传统BIO的socket.read(),如果TCP RecvBuffer里没有数据,函数会一直阻塞知道接收数据,返回读到的数据,如果BIO要并发处理多个客户端I/O,将使用多线程模式,一个线程专门处理一个,但是随着客户端越来越多,所需要创建更多线程,将急剧消耗性能
NIO是基于I/O多路复用实现,可以只用一个线程处理多个IO
BIO\NIO\AIO区别:
BIO:传统java.io包,基于流模型实现,交互方式为同步阻塞
ServerSocket server = new ServerSocket(8080);
while (true) {Socket socket = server.accept(); // 阻塞new Thread(() -> handle(socket)).start(); // 每连接开启新线程
}
NIO:提供Channel、Selector、Buffer等抽象,构建多路复用、同步非阻塞IO程序,提供更接近操作系统底层高性能的数据操作方式,
- 从 Selector 拿到有事件的 Channel(这个过程阻塞)
- 立刻调用
channel.read()
或channel.write()
,这两个操作都是非阻塞的
Selector.select() 返回的是“可能就绪”,不是“必定有数据”。这就是非阻塞 IO 的设计哲学:你可以尝试读写,但你必须自己判断是否成功。
Selector selector = Selector.open(); // 创建选择器
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 创建服务器通道
serverSocketChannel.configureBlocking(false); // 设置为非阻塞模式
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册选择器
while (true) {selector.select();// 阻塞等待事件发生Set<SelectionKey> selectionKeys = selector.selectedKeys(); // 获取事件集合for (SelectionKey selectionKey : selectionKeys){if (selectionKey.isAcceptable()){} // 处理连接事件if (selectionKey.isReadable()){} // 处理读事件}
}
AIO:NIO的升级版,提供异步非阻塞的IO操作方式,异步IO基于时间和回调机制实现,当后台处理完成,操作系统通知相应线程进行后续操作,无需首夺那个轮询,线程不参与实际数据读取过程
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {@Overridepublic void completed(AsynchronousSocketChannel channel, Object attachment) {// 接收到连接channel.read(ByteBuffer.allocate(1024), null, new CompletionHandler<Integer, Object>() {public void completed(Integer result, Object attachment) {// 数据读取完成,处理数据}@Overridepublic void failed(Throwable exc, Object attachment) {// 处理数据失败}});// 继续接收下一个连接server.accept(null, this);}@Overridepublic void failed(Throwable exc, Object attachment) {// 处理接受连接失败比如:监听端口出错、连接中断、服务器资源耗尽等问题。}});
NIO如何实现:
NIO是同步非阻塞的IO模型,同步指线程不断轮询IO时间是否就绪,非阻塞时指线程在等待IO时可以做其它任务
核心在于Selector(IO多路复用),Selector代替了线程本身轮询IO,避免阻塞同时减少不必要线程消耗,非阻塞核心是通道和缓冲区,当IO事件就绪时,可以通过写到缓冲区保证IO成功
NIO由一个专门的线程处理所有IO时间,并负责分发。事件驱动机制,事件到来的时候触发操作,不需要阻塞的监视事件。线程之间通过wait,notify通信,减少线程切换
NIO有三大核心:Channel(通道)、Buffer(缓冲区)、Selector(选择器)。传统IO基于字节流和字符流操作,而NIO基于Channel和Buffer操作,数据总是从通过到读取到缓冲区中,或者从缓冲区写入到通道中。
Selector用于监听多个通道的事件(比如:链接打开,数据到达),因此,单个线程可以监听多个数据通道
哪个框架使用到了NIO
Netty的IO模型是基于非阻塞IO实现的,底层依赖是NIO框架多路复用器Selector,采用epoll模式后,只需要一个线程负责Selector的轮询,当有数据处于就绪状态后,需要一个事件分发一,负责将读写事件分发给对应的读写事件处理器。事件分发器有两种设计模式Reactor采用同步IO,Proactor采用异步IO
Reactor实现相对简单,适合处理耗时短场景,对于耗时长的IO容易阻塞,Proactor性能更高,实现逻辑十分复杂,适合图片或适配流分析服务器,目前主流的事件驱动模型还是依赖select或epoll实现
其它
有一个学生类,想按照分数排序,再按照学号排序应该如何做:
可以使用Comparable接口实现
public class Student implements Comparable<Student>{private int id;private int score;@Overridepublic int compareTo(Student o) {if (this.score != o.score){return Integer.compare(o.score, this.score);}else {return Integer.compare(this.id, o.id);}}
}
解释Native方法:
一种特殊方法,为C、C++或其它语言编写。native关键字是Java语言中一种声明,标记一个方法的实现将在外部定义
public class NativeExample{public native void nativeMethod();
}
实现native方法:
1.生成JNI头文件:使用javah工具从你的Java类生成C/C++文件,这个头文件包含所有native方法原型
2.编写本地代码:使用C/C++编写本地方的实现,确保方法签名与生成的头文件中的原型匹配
3.编译本地代码:将C/C++代码编译成动态链接库(dll,在windows上),共享库(so,Linux上)
4.加载本地库:在Java程序中,使用System.loadLibrary()方法架子啊你编译好的本地库,这样JVM就能找到并调用native方法的实现了