Java最新面试题100道,包含答案示例(21-30题)
21. Java 中什么是类加载器?有哪些类加载器?
Java 中的类加载器用于将字节码文件加载到内存中,并生成对应的 Class 对象。Java 中的类加载器包括以下几种:
- Bootstrap ClassLoader:负责加载 JDK 中的核心类库,例如 rt.jar、resources.jar 等。
- Extension ClassLoader:负责加载 JAVA_HOME/lib/ext 目录下的扩展类库。
- System ClassLoader:负责加载 CLASSPATH 环境变量或者 java.class.path 系统属性指定的路径下的类库。
- Custom ClassLoader:用户自定义的类加载器,可以根据需要实现不同的加载策略。
22. Java 中的字符串常量池是什么?如何向字符串常量池中添加字符串?
Java 中的字符串常量池是指一块存储字符串的区域,其中相同的字符串只会存在一个实例。在使用双引号创建字符串时,字符串常量池会先检查字符串是否已经存在,如果存在则直接返回该字符串的引用;否则就在常量池中创建一个新的字符串,并返回其引用。
在 Java 中可以使用 intern 方法向字符串常量池中添加字符串,例如:
String s1 = "Hello";
String s2 = new String("Hello");System.out.println(s1 == s2.intern()); // 输出 true
23. Java 中的反射是什么?有哪些应用场景?
Java 中的反射机制是指在程序运行时,动态地获取类的信息、调用对象的方法和访问对象的属性的能力。Java 中的反射机制对于一些插件化开发、动态代理等场景非常有用。
Java中反射的应用场景包括:
- 动态代理:通过反射可以实现动态代理,从而更加灵活地控制对象的访问。
- 插件化开发:通过反射可以动态加载和使用不同的插件,从而实现插件化开发。
- 动态生成代码:通过反射可以在运行时根据需要动态生成代码,从而提高程序的灵活性。
- Spring等IoC框架:Spring就大量使用了反射,将对象的创建、属性赋值等延迟到运行时处理,以实现IoC的功能。
24. Java 中的单例模式是什么?如何实现单例模式?
Java 中的单例模式是一种只允许创建一个对象的设计模式。在实现单例模式时,需要将对象的构造函数私有化,然后提供一个静态方法来返回对象的唯一实例。
常用的单例模式实现方式有以下两种:
- 饿汉式单例模式:在类加载的时候就已经创建了对象,因此线程安全但可能会浪费内存。
- 懒汉式单例模式:在需要使用时才创建对象,因此节省了内存空间,但需要考虑线程安全问题。
以下是两种方式的示例代码:
饿汉式单例模式
public class Singleton {private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}
懒汉式单例模式
public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
25. Java 中什么是泛型?如何使用泛型?
Java 中的泛型是指通过参数化类型来实现代码复用的机制。通过使用泛型,可以让代码更加通用,避免出现类型转换异常等问题。
Java 中使用泛型的语法格式为在类或方法名称后面添加尖括号,其中尖括号内可以指定泛型类型或者添加通配符。例如:
// 定义一个泛型类
class Pair<T, U> {private T first;private U second;public Pair(T first, U second) {this.first = first;this.second = second;}public T getFirst() {return first;}public U getSecond() {return second;}
}// 使用泛型
Pair<String, Integer> pair = new Pair<>("Hello", 123);
String first = pair.getFirst();
Integer second = pair.getSecond();
可以看到,在定义泛型类时,需要在类名后面添加尖括号,并在其中指定泛型类型。在使用泛型类时,需要在类名后面添加尖括号,并在其中指定具体的类型。
可以让代码更加通用,避免出现类型转换异常等问题。
Java 中使用泛型的语法格式为在类或方法名称后面添加尖括号,其中尖括号内可以指定泛型类型或者添加通配符。例如:
// 定义一个泛型类
class Pair<T, U> {private T first;private U second;public Pair(T first, U second) {this.first = first;this.second = second;}public T getFirst() {return first;}public U getSecond() {return second;}
}// 使用泛型
Pair<String, Integer> pair = new Pair<>("Hello", 123);
String first = pair.getFirst();
Integer second = pair.getSecond();
可以看到,在定义泛型类时,需要在类名后面添加尖括号,并在其中指定泛型类型。在使用泛型类时,需要在类名后面添加尖括号,并在其中指定具体的类型。
26. Java 中的 Stream 是什么?有哪些函数式接口?
Java 中的 Stream 是一种基于函数式编程思想,提供了一系列操作集合元素的 API。通过使用 Stream API 可以更加方便地对集合中的数据进行筛选、排序、映射等操作。
Java 中常见的函数式接口包括以下几种:
- Predicate:判断一个对象是否满足某个条件。
- Consumer:接受一个参数并对其执行操作,没有返回值。
- Supplier:提供一个对象的实例。
- Function:接受一个参数并对其执行操作,返回一个结果。
通过使用以上函数式接口,可以更加方便地在 Stream 中进行操作。
27. Java 中的集合类有哪些?它们之间有什么区别?
Java 中常见的集合类包括以下几种:
- List:有序集合,允许存储重复元素。
- Set:无序集合,不允许存储重复元素。
- Map:键值对集合,不允许重复键,但允许重复值。
- Queue:队列集合,按照一定规则进行排队,提供了进队、出队等功能。
- Deque:双端队列,既可以从队首添加元素,也可以从队尾添加元素,并且可以进行队首和队尾的操作。
List 和 Set 的主要区别是 List 允许存储重复元素,而 Set 不允许存储重复元素;Map 则是一种键值对集合,用于存储映射关系;Queue 和 Deque 则是一种先进先出的数据结构,在需要按照一定顺序处理数据时非常有用。
28. Java 中的 JNI 是什么?如何使用 JNI?
Java Native Interface(JNI)是 Java 提供的一种用于将 Java 代码与本地代码进行交互的机制。通过 JNI 可以在 Java 中调用 C/C++ 代码、库等,也可以将本地方法暴露给 Java 使用。
使用 JNI 需要经过以下步骤:
- 编写本地方法的实现代码,将其编译成动态链接库(.so 文件或 .dll 文件)。
- 在 Java 中定义本地方法,并声明 native 关键字。
- 使用 javac 命令编译 Java 代码,并生成对应的 class 文件。
- 使用 javah 命令生成对应的头文件。
- 实现 JNI 中的函数,并将其编译成动态链接库。
- 使用 System.loadLibrary 加载动态链接库。
- 调用 Java 中定义的本地方法即可。
29. Java 中如何进行异步编程?
Java 中常见的异步编程方式包括以下几种:
- Callback 回调机制:将需要异步执行的代码封装成回调函数,在异步操作执行完成后调用该回调函数。
- Future/Promise 机制:Future 可以获取异步操作的结果,并且可以在结果返回之前继续执行其他操作;Promise 是一种类似于 Future 的概念,但是它可以手动设置异步操作的结果。
- CompletionStage/CompletableFuture:CompletionStage 继承了 Future 接口的特性,并且提供了更加丰富的 API,让开发者可以更加方便地对异步操作进行组合和链式调用。
- Reactive Programming 响应式编程:通过使用 RxJava、Project Reactor 等响应式编程框架,可以将异步操作封装成流式处理的模式,从而实现高效的事件驱动编程。
30. Java 中如何避免线程安全问题?有哪些常见的线程安全类?
Java 中常见的避免线程安全问题的方法包括以下几种:
- Synchronized 同步锁:使用 synchronized 关键字对关键代码块进行同步,保证同一时刻只有一个线程访问该代码块。
- ReentrantLock 可重入锁:与 synchronized 类似,但是具有更加灵活的操作方式。
- Atomic 原子类:提供了一些原子级别的操作,可以保证多线程同时执行时不会产生冲突。
- ThreadLocal 线程局部变量:为每个线程提供独立的变量副本,避免多个线程同时访问同一个变量的问题。
Java 中常见的线程安全类包括以下几种:
- ConcurrentHashMap:线程安全的 HashMap 实现。
- CopyOnWriteArrayList:线程安全的 List 实现,写操作时会复制一份新的数组。
- AtomicInteger:线程安全的整数类型,可在多线程环境中原子性地进行加减操作。
- BlockingQueue:一种阻塞式队列,具有 put 和 take 方法,用于实现生产者消费者模式。
- Semaphore:一种信号量机制,用于控制同时并发执行的线程数量。
- CountDownLatch:一种倒数计数器,用于等待其他线程执行完成后再执行。
CyclicBarrier:一种循环屏障,用于让多个线程在同一时刻达到某个状态,然后同时开始执行。
以上这些线程安全类能够帮助开发者使用更加高效和安全的方式进行多线程编程。