小林Coding—Java「二、Java基础篇」

󠀲󠀲二 Java基础面试篇

数据类型

引用类型

类:Class接口:Interface数组:Array枚举:Enum

自动装箱:int -> Integer
自动拆箱:Integer -> int

// 下面代码会先自动拆箱将sum转为int,然后执行 + 操作。然后发生自动装箱操作将int转为Integer操作。
Integer sum = 0; for(int i=1000; i<5000; i++){ sum+=i; }
// 其内部变化如下:
int result = sum.intValue() + i; Integer sum = new Integer(result);
// 由于我们这里声明的sum为Integer类型,在上面的循环中会创建将近4000个无用的Integer对象,在这样庞大的循环中,会降低程序的性能并且加重了垃圾回收的工作量。

看起来Integer非常不好。那为什么Java还要有Integer?
因为Integer是int的包装类。Java的绝大部分方法或类都是用来处理类型对象的,比如ArrayList只能以作为他的存储对象(所以只能用ArrayList而不能用List)。

泛型中的应用: 此外,泛型中也只能使用引用类型包装类。所以包装类还是很有存在的必要的。

List<Integer> list = new ArrayList<>();
list.add(3);
list.add(1);
list.add(2);
Collections.sort(list);System.out.println(list);

转换中的应用

在Java中,基本类型和引用类型不能直接转换,必须使用包装类实现。例如将 int 转为 String,需要int -> Integer -> String

集合中的应用

集合也是只能存储对象而不能存储基本数据类型。因此想存int,就要存Integer。如果有一个列表,想要求所有元素的和,如果用int,需要一个循环来遍历。如果是Integer包装类,就可以直接用stream()方法计算所有元素的和。

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
int sum = list.stream().mapToInt(Integer::intValue).sum();

Integer的缓存
Java的Integer类内部实现了一个静态缓存池,用于存储特定范围内的整数值对应的Integer对象。这个范围为-128~127。当通过Integer.valueOf(int)方法创建一个在这个范围内的整数对象时,会复用缓存中的现有对象,直接从内存中取出,不用重新new。


面向对象

面向对象的三大特征:

  • 封装
  • 继承
  • 多态
    • 重载 (编译时多态)
    • 重写 (运行时多态)

多态体现在哪些方面?

  • 方法重载:
    • 同一类中可以有多个同名方法,且有不同的参数列表。(eg, add(int a, int b) 以及add(int a, int b, int c)
  • 方法重写:
    • 子类能够提供对父类中同名方法的具体实现。在运行时,JVM会根据对象的数据类型确定调用哪个版本的方法。(eg, 在一个动物类中,定义一个sound方法, 子类Dog可以重写该方法实现bark,而Cat可以实现meow)
  • 接口的实现
    • 多个类可以实现同一个接口,并用接口的引用来调用这些类的方法。(eg, 多个类,如Cat Dog 都实现了 Animal 接口,当用 Animal 类型的引用来调用 makeSound 方法时,会出发对应的实现)
  • 向上转型和向下转型
    • 向上转型:使用父类类型的引用指向子类对象。向上转型可以在运行时期采用不同的子类实现。
    • 向下转型:将父类引用转回子类类型。但在执行前需要确认引用实际指向的对象类型来避免ClassCastException

多态解决了什么问题?

多态指子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现。
多态可以提高代码的扩展性和复用性。是很多设计模式、设计原则、编程技巧的代码实现基础。如策略模式、基于接口而非实现变成、依赖倒置原则、里式替换原则、利用多态去掉冗长的 if-else 语句等。

重载和重写有什么区别?

重载是在同一个类中定义多个同名不同参数方法。
重写是子类重新定义父类中的方法。

抽象类和普通类的区别

  • 实例化:普通类可以直接实例化对象,而抽象类不能被实例化。
  • 方法实现:普通类中的方法可以有具体的实现,抽象类中的方法可以有实现也可以没有实现。
  • 继承:一个类可以继承一个普通类 & 多个接口;一个类只能继承一个抽象类 & 多个接口
  • 实现限制:普通类可以被其他类继承和使用,而抽象类一般用于作为基类,需要被其他类继承和扩展使用。 (因此抽象类不能被final修饰,因为final修饰符禁止类被继承或方法被重写)

抽象类和接口的区别

两者的特点

  • 抽象类用于描述类的共同特性和行为,可以有成员变量、构造方法和具体方法。适用于有明显继承关系的场景。
  • 接口用于定义行为规范,可以多实现,只能有常量、抽象方法、默认方法和静态方法。

两者的区别

  • 实现方式:一个类可以实现多个接口,继承一个抽象类。所以使用接口可以间接的实现多重继承。
  • 方法方式:接口只能有定义不能有方法的实现。java1.8中引入定义default方法体。抽象类可以有定义+实现。
  • 访问修饰符:接口的成员变量默认为public static final,必须赋初值,不能被修改。其所有成员方法都是public abstract的。抽象类的成员变量默认为default。抽象方法由于被abstract修饰不能被private, static, synchronized, native修饰。抽象方法必须以分号结尾,不能带花括号。
  • 变量:抽象类中可以包含实例变量和静态变量,接口只能包含常量(静态常量)

接口里可以定义哪些方法? 不能定义具体方法体吗?

  • 抽象方法:在接口中定义的方法默认都是public abstract 修饰的。这些修饰符可以省略。
  • 默认方法:用default修饰,就可以允许接口提供具体实现。实现类可以选择重写默认方法
public interface Animal {void makeSound();default void sleep() {System.out.println("Sleeping...");}
}
  • 私有方法:私有方法是在Java 9 中引用,用于在接口中为默认方法或其他私有方法提供辅助功能。私有方法不能被实现类访问,只能在接口内部使用。
public interface Animal {void makeSound();default void sleep() {System.out.println("Sleeping...");}private void logSleep() {System.out.println("Logging sleep");}
}

解释Java中的静态变量和静态方法

静态变量和静态方法是与类本身关联的,而不是与类的实例(对象)关联。他们在内存中只有一份,可以被类的所有实例共享。
静态是用static修饰的。

静态变量

  • 共享性:所有该类的实例共享同一个静态变量。
  • 初始化:静态变量在类被加载时初始化,只会对其分配一次
  • 访问方式:可以直接通过类名方式访问。推荐。

静态方法

  • 无实例依赖:可以直接通过类名访问,无需创建类实例。静态方法不能直接访问非静态的成员变量或方法。
  • 访问静态成员:静态方法可以直接访问静态变量和静态方法。但是不能直接访问非静态成员。
  • 多态性:静态方法不支持重写,可以被隐藏。

使用场景
静态变量:常用于在所有对象间共享的数据。如计数器,常量等。
静态方法:常用于助手方法,获取类级别的信息或者是没有依赖于实例的数据处理。

// 静态变量如何访问非静态变量和方法
class Car {// 非静态变量和方法String model = "Toyota";public void displayModel() {System.out.println("Model: " + model);}// 静态方法public static void showCarInfo() {// 不能直接访问非静态变量和方法// System.out.println(model); // 编译错误// displayModel(); // 编译错误// 通过实例访问非静态变量和方法Car car = new Car();  // 创建对象System.out.println(car.model);  // 访问非静态变量car.displayModel();  // 调用非静态方法}
}public class Main {public static void main(String[] args) {// 调用静态方法Car.showCarInfo();}
}

有一个父类和子类,都有静态的成员变量、静态构造方法和静态方法,在我new一个子类对象的时候,加载顺序是怎么样的?

  1. 父类静态成员变量、静态代码块(如果有)
  2. 子类静态成员变量、静态代码块(如果有)
  3. 父类构造方法(实例化对象时)
  4. 子类构造方法(实例化对象时)

对象

Java创建对象除了 new 还有什么方式?

  • 通过反射创建对象。
    可以使用 Class 类的 newInstance() 方法或者通过 Constructor 类来创建对象呢
public class MyClass {public MyClass() {// Constructor}
}
public class Main {public static void main(String[] args) throws Exception {Class<?> clazz = MyClass.class;MyClass obj = (MyClass) clazz.newInstance();}
}
  • 通过反序列化创建对象:
    通过将对象序列化(保存到文件或网络传输),然后再反序列化(从文件或网络传输中读取对象)的方式来创建对象,对象能被序列化和反序列化的前提是实现Serializable接口
import java.io.*;public class MyClass implements Serializable {// Class definition
}public class Main {public static void main(String[] args) throws Exception {// Serialize objectMyClass obj = new MyClass();ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("object.ser"));out.writeObject(obj);out.close();// Deserialize objectObjectInputStream in = new ObjectInputStream(new FileInputStream("object.ser"));MyClass newObj = (MyClass) in.readObject();in.close();}
}

New出的对象什么时候回收?

Java的对象回收机制是由垃圾回收器根据一些算法决定的,会周期性地检测不再被引用的对象,并将其回收,释放内存。具体的算法有:

  1. 引用计数法:引用计数为0就回收
  2. 可达性分析算法:从根对象出发,通过对象之间的引用链遍历,如果存在一条引用链到达某个对象,则说明该对象是可达的。反之就回收这个对象

反射

反射机制是在运行状态中,对于任意一个类,都能知道这个类中的所有属性和方法,对于任意一个对象,都能调用它的方法和属性。这种动态获取的信息和动态调用对象方法的功能称为Java的反射机制。

反射具有如下特性:

  1. 运行时类信息访问:反射机制允许程序在运行时获取类的完整结构信息,包括类名,包名,父类,实现的接口,构造函数,方法和字段等。
  2. 动态对象创建:可以使用反射API动态地创建对象实例,即使在编译时不知道具体的类名。这是通过Class类的newInstance()方法或Constructor对象的newInstance()方法实现的。
  3. 动态方法调用:可以在运行时动态地调用对象的方法,包括私有方法。这通过Method类的invoke()方法实现。允许你传入对象实例和参数值来执行方法。
  4. 访问和修改字段值:私有字段也可以访问和修改。这是通过Field类的get()和set()方法完成的。
    在这里插入图片描述

平时写代码和框架中发射有哪些应用场景?

  • 加载数据库
    项目可能有的用mysql,有的用oracle,需要动态地根据实际情况加载驱动类。这个时候在使用JDBC连接数据库时,使用Class.forName()通过反射加载数据库的驱动程序,如果是mysql则传入mysql的驱动类,而如果是oracle则传入的参数就变成另一个
// DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
Class.forName("com.mysql.cj.jdbc.Driver");
  • 配置文件加载
    Spring框架的IOC控制反转(动态加载管理bean),Spring通过配置文件配置各种各样的bean,需要什么就配什么,spring容器会根据你的需求动态加载。

Spring通过XML配置模式装载Bean的过程:

  1. 将程序中所有的XML或properties配置文件加载入内存
  2. Java类里面解析xml或者properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息
  3. 使用反射机制,根据这个字符串获取某个类的Class实例
  4. 动态配置实例的属性。

eg:
配置文件:

className=com.example.reflectdemo.TestInvoke
methodName=printlnState

实体类:

public class TestInvoke {private void printlnState() {System.out.println("I am fine");}
}

解析配置文件内容:

public static String getName(String key) throws IOException {// 创建 Properties 对象Properties properties = new Properties();// 读取 properties 文件FileInputStream in = new FileInputStream("D:\\language-specification\\src\\main\\resources\\application.properties");properties.load(in);in.close();// 根据 key 获取属性值return properties.getProperty(key);
}

利用反射获取实体类的Class实例,创建实体类的实例对象,调用方法

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, ClassNotFoundException, InstantiationException {// 使用反射机制获取 Class 对象Class<?> c = Class.forName(getName("className"));System.out.println(c.getSimpleName());// 获取类中的方法Method method = c.getDeclaredMethod(getName("methodName"));// 绕过安全检查method.setAccessible(true);// 创建实例对象TestInvoke testInvoke = (TestInvoke) c.newInstance();// 调用方法method.invoke(testInvoke);
}

在这里插入图片描述

注解

注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。

我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。
(当你调用一个注解的方法时,实际上是通过动态代理对象调用AnnotationInvocationHandlerinvoke方法,这个方法不是直接执行单吗返回结果,而是从一个内部的Map,即memberValues中查找与注解方法对应的值。)

异常

Java的异常类层次结构:
在这里插入图片描述
Java的异常主要基于Throwable类及其子类。

  1. Error(错误):表示运行时环境的错误。错误是指程序无法处理的严重问题,如系统崩溃,虚拟机错误,动态连接失败等。通常,程序不应该捕获这类错误,如OutOfMemoryError, StackOverflowError等。
  2. Exception(异常):表示程序本身可以处理的异常条件,分为两大类
    1. 非运行时异常:这类异常中编译时就必须被捕获或声明抛出。他们通常是外部错误,如文件不存在(FileNotFoundException)、类未找到(ClassNotFoundException)等。
    2. 运行时异常:这类包括运行时异常(RuntimeException)和错误(Error)。运行时异常由程序错误导致,如空指针访问(NullPointerException)、数组越界(ArrayIndexOutOfBoundsException)等。运行时异常是不需要在编译时强制捕获或声明的。

Java的异常处理有哪些?

  • try-catch语句块:用于捕获并处理可能抛出的异常。try块中包含可能抛出的异常的代码,catch块用于捕获并处理特定类型的异常。可以有多个catch块来处理不同类型的异常
try {// 可能抛出异常的代码块
} catch (ExceptionType1 e1) {// 处理异常类型1的逻辑
} catch (ExceptionType2 e2) {// 处理异常类型2的逻辑
} catch (ExceptionType3 e3) {// 处理异常类型3的逻辑
} finally {// 可选的finally块,用于无论发生何种异常都需要执行的代码
}
  • throw语句:用于手动抛出异常。可以根据需要在代码中使用throw语句主动抛出特定类型的异常。
throw new ExceptionType("Exception message");
  • throws关键字:用于在方法生命中声明可能抛出的异常类型。如果一个方法可能抛出异常,但不想再方法内部处理,可以使用throws关键字将异常传递给调用者来处理
public void methodName() throws ExceptionType {// 方法体
}
  • finally块:用于定义无论是否发生异常都会执行的代码块。通常用于释放资源,确保资源的正确关闭。
try {// 可能抛出异常的代码
} catch (ExceptionType e) {// 处理异常的逻辑
} finally {// 无论是否发生异常都会执行的代码
}

抛出异常什么时候不用throws?

如果异常是未检查异常或者在方法内部被捕获和处理了,那就不需要用throws。

  • Unchecked Exceptions: 未检查异常是继承RuntimeException类和Error类的异常,编译器不强制要求异常处理,如NullPointerException, ArrayIndexOutOfBoundsException等。
  • 捕获和处理异常:如果在方法内部捕获了可能出现的异常并在方法内部处理他们,就不用在方法签名处throws

try{return "a"} finally {return "b"} 这条语句返回什么?

finally块中的return语句会覆盖try中的return返回,因此将返回 b

object

== 和 equal有什么区别?

  • 对于字符串变量,使用 == 和 equals比较字符串,其比较方法不同。 ==比较两个变量本身的值,即两个对象在内存中的首地址,equals比较字符串中包含的内容是否相同
  • 对于非字符串变量,== 和 equals 的作用一样,都是比较首地址,即两个引用变量是否指向同一个对象。

总结:

  • ==:比较的是字符串内存地址是否相同,属于地址比较
  • equals():比较的是两个字符串的内容,属于内容比较。

StringBuffer和StringBuild的区别是什么?

  • StringBuffer:是线程安全的,适用于多线程
  • StringBuilder:线程不安全的,适用于单线程

速度:
从快到慢依次为:StringBuilder > StringBuffer > String

Java 1.8 新特性

Java中的stream的API简单介绍

案例1: 过滤并收集满足条件的元素
l

List<String> originalList = Arrays.asList("apple", "fig", "banana", "kiwi);
List<String> filteredList = originalList.stream().filter(s -> s.length() > 3).collect(Collectors.toList());
/*这里可以直接在原始列表上调用 .stream() 方法创建一个流,使用 .filter() 中间操作筛选出长度大于3的字符串,最后使用 .collect(Collectors.toList()) 终端操作将结果收集到一个新的列表中。
*/

案例2:计算列表中所有数字的和

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);int sum = numbers.stream().mapToInt(Integer::intValue).sum();

通过stream API,可以先使用 .mapToInt() 将Integer流转为IntStream(这是为了搞笑处理基本类型),然后直接调用 .sum() 方法来计算总和。

Stream流的并行API是什么?

Stream流的并行API是 ParallelStream
并行流(ParallelStream) 就是将元数据分为多个子流对象进行多线程操作,然后将处理的结果再汇总为一个流对象,底层使用通用的fork/join池来实现,即将一个任务拆分为多个小任务并行计算,再把多个小任务的结果合并成总的计算结果。

Stream’串行流和并行流的主要区别:
在这里插入图片描述
对于cpu密集型,适用于并行流。
对于i/o密集型且任务数相对线程数较大,那么直接用ParallelStream并不是很好的选择。

completableFuture怎么用的?

completableFuture是由Java 8 引用的,在Java8之前一般通过Future实现异步。
Future用于表示异步计算的结果,只能通过阻塞或者轮询的方式获取结果,而且不支持回调方法。每一步的处理逻辑都嵌套在前一步的回调函数中,增加了理解和维护的难度。
CompletableFuture对Future进行了扩展,可以通过设置回调的方式处理计算结果,同时也支持组合操作,支持进一步的编排,同时一定程度解决了回调地狱的问题。
CompletableFuture的实现如下:

// 创建一个具有5个线程的固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);// 创建第一个异步任务:打印并返回"step1 result"
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {System.out.println("执行step 1");return "step1 result";
}, executor);// 创建第二个异步任务:打印并返回"step2 result"
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {System.out.println("执行step 2");return "step2 result";
}, executor);// 将两个异步任务的结果组合,当两个任务都完成后执行
cf1.thenCombine(cf2, (result1, result2) -> {// 打印两个任务的结果System.out.println(result1 + " + " + result2);System.out.println("执行step 3");// 返回新的结果,供下一步使用return "step3 result";
}).thenAccept(result3 -> {// 接收最终的结果并打印System.out.println(result3);
});

序列化

怎么把一个对象从一个jvm转移到另一个jvm?

  • 使用序列化和反序列化。将对象序列化为字节流,并将其发送到另一个JVM,然后在另一个JVM中反序列化字节流恢复对象。可以通过Java的 ObjectOutputStream 和ObjectInputStream实现。
  • 使用消息传递机制:利用消息队列(RabbitMQ、Kafka)或者通过网络套接字进行通信,将对象从一个JVM发送到另一个。这需要自定义协议来序列化对象并在另一个JVM中反序列化。
  • 使用远程方法调用(RPC):可以使用远程方法调用框架,如gRPC,来实现对象在不同JVM之间的传输。远程方法调用可以在分布式系统中调用远程JVM上的对象的方法。
  • 使用共享数据库或缓存。将对象存储在共享数据库(Mysql, PostgreSQL)或共享缓存(如Redis)中,让不同的JVM可以访问这些共享数据。这种方法适用于需要共享数据但不需要直接传输数据的场景。

序列化和反序列化让你自己实现你会怎么实现?

Java默认的序列化虽然实现方便,但却存在安全漏洞,不跨语言以及性能差等缺陷。
所以选择用主流序列化框架更好,如FastJson,Protobuf来替代Java序列化。
如果追求性能的话,Protobuf 序列化框架会比较合适,Protobuf 的这种数据存储格式,不仅压缩存储数据的效果好, 在编码和解码的性能方面也很高效。Protobuf 的编码和解码过程结合.proto 文件格式,加上 Protocol Buffer 独特的编码格式,只需要简单的数据运算以及位移等操作就可以完成编码与解码。可以说 Protobuf 的整体性能非常优秀。

将对象转为二进制字节流具体怎么实现?

其实是定义了序列化后,反序列化前的字节流格式,然后对此格式进行操作,生成符合格式的字节流或者将字节流解析为对象。

在Java中通过序列化对象流来完成序列化和反序列化

  • ObjectOutputStream:通过writeObject() 方法做序列化操作。
  • ObjectInputStream:通过readObject() 方法做反序列化操作。

只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常。

实现对象序列化的步骤:

  • 让类实现Serializable接口:
import java.io.Serializable;public class MyClass implements Serializable {// class code
}
  • 创建输出流并写入对象
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;// 创建MyClass类型的对象
MyClass obj = new MyClass();try {// 创建一个FileOutputStream来指向一个文件,这里的文件名为"object.ser"FileOutputStream fileOut = new FileOutputStream("object.ser");	// .ser一般用于表示序列化对象的文件// 创建一个ObjectOutputStream,它封装了FileOutputStream// ObjectOutputStream用于将对象写入到文件输出流ObjectOutputStream out = new ObjectOutputStream(fileOut);// 使用ObjectOutputStream的writeObject方法将obj对象序列化并写入到文件中out.writeObject(obj);// 关闭ObjectOutputStream流out.close();// 关闭FileOutputStream流fileOut.close();
} catch (IOException e) {// 捕获IOException异常,如果在打开、写入或关闭文件过程中出现问题,将执行此块e.printStackTrace();
}
  • 实现对象反序列化:创建输入流并读取对象
import java.io.FileInputStream;
import java.io.ObjectInputStream;// 声明一个 MyClass 类型的变量,初始为 null
MyClass newObj = null;try {// 创建一个 FileInputStream 来从指定的文件(这里是 "object.ser")读取数据FileInputStream fileIn = new FileInputStream("object.ser");// 创建一个 ObjectInputStream,它封装了 FileInputStream// ObjectInputStream 用于从文件中读取并恢复对象ObjectInputStream in = new ObjectInputStream(fileIn);// 使用 ObjectInputStream 的 readObject 方法读取先前序列化的对象// 需要将返回的 Object 类型强制转换为 MyClass 类型newObj = (MyClass) in.readObject();// 关闭 ObjectInputStreamin.close();// 关闭 FileInputStreamfileIn.close();
} catch (IOException | ClassNotFoundException e) {// 捕获 IOException 或 ClassNotFoundException 异常// IOException 可能由文件操作引发,如文件不存在、数据不完整等// ClassNotFoundException 由于找不到序列化对象的类定义而抛出e.printStackTrace();
}

设计模式

volatile和sychronized如何实现单例模式?(volatile和synchronized都是保证同步的。volatile更轻便,synchronized成为重量级锁)

public class SingleTon {// volatile 关键字修饰变量 防止指令重排序private static volatile SingleTon instance = null;private SingleTon(){}public static  SingleTon getInstance(){if(instance == null){//同步代码块 只有在第一次获取对象的时候会执行到 ,//第二次及以后访问时 instance变量均非null故不会往下执行了 直接返回啦synchronized(SingleTon.class){if(instance == null){instance = new SingleTon();}}}return instance;}
}

正确的双重检查锁定模式需要使用volatile。volatile包含两个功能:

  • 保证可见性。使用volatile定义的变量,将会保证对所有的线程的可见性。
  • 禁止指定重排序优化。

由于volatile禁止对象创建时指定之间重排序,所以其他线程不会访问到一个未初始化的对象,从而保证安全性。

代理模式和适配器模式有什么区别?(代理模式和适配器模式都是Java设计模式中的)

适配器模式:将一个接口转换成客户希望的另一个接口,使原本不兼容的接口类可以一起工作
代理模式:给一个对象提供一个代理对象,并由代理对象控制对原对象的引用,使客户不能直接与真正的目标对象通信
结构不同:代理模式一般包含抽象主题,真实主题和代理 三种角色,适配器模式包含目标接口,适配器和被适配者 三种角色
应用场景不同:代理模式常用于添加额外功能或控制对对象的访问,适配器模式常用于让不兼容的接口协同工作。

I/O (⭐️⭐️看不懂)

BIO、NIO、AIO区别是什么?

  • BIO(blocking IO):就是传统的java.io包,是基于流模型实现的,交互方式是同步、阻塞方式,也就是说在读入输入流或输出流时,在读写动作完成前,线程会一直阻塞在哪里,他们之间的调用是可靠的线性顺序
  • NIO(non-blocking IO):是java 1.4引入的java.nio包。可以构建多路复用、同步非阻塞的IO程序,同时提供了更接近操作系统底层的数据操作方式。
  • AIO(Asynchronous IO):是Java1.7后引入的NIO的升级版本,提供了异步非阻塞的IO操作方式,是基于事件和回调机制实现的。应用操作后会直接返回,不会阻塞在那里,当后台处理完成后操作系统会通知响应线程进行后续操作。

Java 是如何实现网络IO高并发编程的?

可以使用Java NIO,是一种同步非阻塞的I/O模型,也是I/O多路复用的基础
NIO是基于I/O多路复用实现的,它可以只用一个线程处理多个客户端I/O,如果需要管理成千上万个连接,但是每个连接只发送少量数据例如一个聊天服务器,用NIO实现会好点

NIO 是如何实现的?

NIO是一种同步非阻塞的IO模型,所以也可以叫NON-BLOCKINGIO。(同步是指线程不断轮询IO事件是否就绪,非阻塞是指线程在等待IO的时候,可以同时做其他任务。)
NIO主要有三个核心部分:Channel(通道)、Buffer(缓冲区),Selector(选择区)。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer进行操作。数据总是从通道读取到缓冲区中,或从缓冲区写入到通道。Selector用于监听多个通道事件(如连接打开,数据到达)。因此单个线程可以监听多个数据通道。

NIO有一个专门的线程处理所有的IO时间,并负责分发。事件驱动机制,事件到来时出发操作,不需要阻塞的监视时间,线程之间通过wait,notify通信,减少线程切换。
在这里插入图片描述

哪个框架用到了NIO?

Netty。

其他问题

有一个学生类,想按照分数降序排序,再按照学号升序排序,如何实现?

可以使用Comparable接口来实现按照分数排序,再按照学号排序。

public class Student implements Comparable<Student> {private int id;private int score;// 构造方法和其他属性、方法省略@Overridepublic int compareTo(Student other) {if(this.score != other.score)return Integer.compare(other.score, this.score);	// 按照分数降序排序elsereturn Integer.compare(this.id, other.id);	// 如果分数相同,则按照学号升序排序}
}

然后在需要对学生列表排序的地方,使用Collections.sort() 方法对学生列表进行排序即可。

List<Student> students = new ArrayList<>();
// 添加学生对象列表中
Collections.sort(students);

如何对一个List进行排序?

  1. 使用 Collections.sort()
    如果列表元素实现了Comparable接口,那么可以直接使用它们的自然顺序进行排序。你也可以提供一个自定义的Comparator来指定排序的具体规则。
// 使用元素自然顺序
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 1, 4, 2));
Collections.sort(list);
System.out.println(list); // 输出排序后的列表: [1, 2, 3, 4, 5]//使用自定义比较器
List<String> list = new ArrayList<>(Arrays.asList("banana", "apple", "cherry"));
Collections.sort(list, new Comparator<String>() {public int compare(String o1, String o2) {return o1.length() - o2.length(); // 根据字符串长度排序}
});
System.out.println(list); // 输出排序后的列表: ["apple", "banana", "cherry"]
  1. 使用 List.sort()
// 使用元素自然顺序
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 1, 4, 2));
list.sort(null); // 等同于 list.sort(Comparator.naturalOrder());
System.out.println(list); // 输出: [1, 2, 3, 4, 5]//使用自定义比较器
List<String> list = new ArrayList<>(Arrays.asList("banana", "apple", "cherry"));
list.sort((s1, s2) -> s1.length() - s2.length()); // 使用Lambda表达式简化Comparator的编写
System.out.println(list); // 输出: ["apple", "banana", "cherry"]

Native方法解释一下

在Java中,native方法是一种特殊类型的方法, 他允许Java代码调用外部的本地代码,如C C++编写的代码。
在Java类中,native方法看起来和其他方法类似,只是其方法体由native关键字代替,没有实际的实现代码。例如:

public class NativeExample {public native void nativeMethod();
}

实现Native方法需要完成的步骤:

  1. 生成JNI头文件
  2. 编写本地代码:用别的语言
  3. 编译本地代码:将C/C++代码编译成动态链接库(DLL for Windows),共享库(SO for linux)
  4. 加载本地库:在Java中使用System.loadLibrary()方法来加载你编译好的本地库,这样JVM就可以找到并调用native方法的实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/19740.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

GBDT 算法

GBDT 梯度决策提升树是将一些弱分类决策树的结果加在一起&#xff0c;每一棵决策树对前一颗觉得树残差进行优化&#xff0c;从而使得总体的损失值达到最小。 GBDT 公式 Fm-1: 上一棵树的结果 α \alpha α: 学习率 hm(x): 当前树&#xff0c;通过训练调整结果&#xff0c;降低…

java~Lambda表达式

目录 Lambda和匿名内部类 语法 函数式接口 无返回值&#xff08;无参、有参&#xff09; 有返回值&#xff08;无参、有参&#xff09; 语法精简 四个基本的函数式接口 方法引用 实例方法引用 静态方法引用 特殊方法引用 构造方法引用 数组引用 集合 List、Set …

PyQt5信号与槽二

窗口数据传递 在开发程序时&#xff0c;如果这个程序只有一个窗口&#xff0c;则应该关心这个窗口里面的各个控件之间是如何传递数据的&#xff1b;如果这个程序有多个窗口&#xff0c;那么还应该关心不同的窗口之间是如何传递数据的。对于多窗口的情况&#xff0c;一般有两种…

【java】多态

一、概念 多态是同一个行为具有多个不同表现形式或形态的能力。 多态就是同一个接口&#xff0c;使用不同的实例而执行不同操作。 同一个事件发生在不同的对象上会产生不同的结果。 比如&#xff1a; public class Test {public static void main(String[] args) {Person xn…

使用Holoviews创建复杂的可视化布局

目录 一、Holoviews简介 二、安装Holoviews 三、Holoviews的基本概念 元素&#xff08;Elements&#xff09;&#xff1a; 容器&#xff08;Containers&#xff09;&#xff1a; 映射&#xff08;Mappings&#xff09;&#xff1a; 四、基本用法 创建元素&#xff1a; …

Java2.1——异常

异常基本概念 一&#xff1a;程序出错 分类 &#xff1a; 编辑错误&#xff0c;逻辑错误&#xff0c;运行时错误 目的&#xff1a; 异常处理让程序出错了还运行&#xff0c;避免中止运行 二&#xff1a; 运行时错误 当出现编译时无法预料的问题&#xff0c;将运行错误报告…

2025年假期python,工作日python脚本求出 输出日期内容

# coding:utf-8 import datetime# 假设已知的节假日和调休安排 holidays [datetime.date(2025, 1, 1), # 元旦datetime.date(2025, 1, 28), # 春节datetime.date(2025, 1, 29), # 春节datetime.date(2025, 1, 30), # 春节datetime.date(2025, 1, 31), # 春节datetime.dat…

1TB! 台湾最新倾斜摄影3DTiles数据分享

之前的文章分享了546GB香港倾斜摄影3DTiles数据&#xff0c;主要是验证倾斜模型3DTiles转换工具的生产效率和数据显示效率&#xff0c;结果对比可以看出无论是数据生产速度以及成果数据显示效率上&#xff0c;都优于其他两种技术路线。最近使用倾斜模型3DTiles工具生产了台湾地…

ssm136公司项目管理系统设计与实现+jsp(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;公司项目管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本T公司项目管理系…

【Sql Server】sql server 2019设置远程访问,外网服务器需要设置好安全组入方向规则

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言1、无法链接…

小车综合玩法--2.超声波避障

一、实验准备 通过超声波模块与小车结合&#xff0c;实现小车超声波避障。小车接线已安装&#xff0c;且安装正确 二、实验原理 通过超声波我们获取小车与障碍物的距离。当检测到小车与障碍物的距离小于我们的设置的距离时&#xff0c;小车左旋避开障碍物。 三、实验源码 #!…

「二」体验HarmonyOS端云一体化开发模板——创建端云一体化工程

关于作者 白晓明 宁夏图尔科技有限公司董事长兼CEO、坚果派联合创始人 华为HDE、润和软件HiHope社区专家、鸿蒙KOL、仓颉KOL 华为开发者学堂/51CTO学堂/CSDN学堂认证讲师 开放原子开源基金会2023开源贡献之星 「目录」 「一」HarmonyOS端云一体化概要 「二」体验HarmonyOS端云一…

操作系统启动实验

简单的操作系统 汇编代码 ; hello-os ; TAB4ORG 0x7c00 ; 指明程序装载地址; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy codeJMP entryDB 0x90DB "HELLOIPL" ; 启动扇区名称&#xff08;8字节&#xff09;DW 512 ; 每个扇区&#xff08;s…

助力模型训练,深度学习的经典数据集介绍

想要训练出效果好的模型&#xff0c;高质量的数据集必不可少。深度学习的经典数据集包括MNIST手写数字数据集、Fashion MNIST数据集、CIFAR-10和CIFAR-100数据集、ILSVRC竞赛的ImageNet数据集、用于检测和分割的PASCAL VOC和COCO数据集等&#xff0c;本文将对这些数据集进行介绍…

Spring基础——针对实习面试

目录 Spring基础什么是Spring框架&#xff1f;列举一些重要的Spring模块Spring Core 核心模块Spring AOP 模块Spring MVC 模块Spring Data 模块Spring Security 模块Spring Boot 模块 Spring&#xff0c;Spring MVC&#xff0c;Spring Boot之间什么关系&#xff08;区别&#x…

PH热榜 | 2024-11-17

DevNow 是一个精简的开源技术博客项目模版&#xff0c;支持 Vercel 一键部署&#xff0c;支持评论、搜索等功能&#xff0c;欢迎大家体验。 在线预览 1. Octokit 标语&#xff1a;人人都能用的无代码游戏和AR滤镜制作工具 介绍&#xff1a;Octokit 是一款无需编程技能就能使用…

分享本周所学——三维重建算法3D Gaussian Splatting(3DGS)

大家好&#xff0c;欢迎来到《分享本周所学》第十二期。本人是一名人工智能初学者&#xff0c;刚刚读完大二。前几天自学了一下3D Gaussian Splatting&#xff08;3DGS&#xff09;&#xff0c;觉得非常有意思。写这篇文章主要是因为网上大部分关于3DGS的文章都比较晦涩&#x…

Python-简单病毒程序合集(一)

前言&#xff1a;简单又有趣的Python恶搞代码&#xff0c;往往能给我们枯燥无味的生活带来一点乐趣&#xff0c;激发我们对编程的最原始的热爱。那么话不多说&#xff0c;我们直接开始今天的编程之路。 编程思路&#xff1a;本次我们将会用到os,paltform,threading,ctypes,sys,…

技术成长战略是什么?

文章目录 技术成长战略是什么&#xff1f;1. 前言2. 跟技术大牛学成长战略2.1 系统性能专家案例2.2 从开源到企业案例2.3 技术媒体大V案例2.4 案例小结 3. 学习金字塔和刻意训练4. 战略思维的诞生5. 建议 技术成长战略是什么&#xff1f; 1. 前言 在波波的微信技术交流群里头…

哪家云电脑便宜又好用?ToDesk云电脑、顺网云、达龙云全方位评测

陈老老老板&#x1f934; &#x1f9d9;‍♂️本文专栏&#xff1a;生活&#xff08;主要讲一下自己生活相关的内容&#xff09;生活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f9d9;‍♂️本文简述&#xff1a;讲一下市面上云电脑的对比。 &#x1f9d9;‍♂️上一篇文…