100道Java 经典面试题
- JDK、JRE和JVM分别是什么?
- 答案:JDK(Java Development Kit)是Java开发工具包,提供了Java的开发环境和运行环境。JRE(Java Runtime Environment)是Java运行环境,为Java程序的运行提供必要的库和类。JVM(Java Virtual Machine)是Java虚拟机,是一个允许Java程序在任何支持该虚拟机的平台上运行的解释器。
- 为什么Java被称作是“平台无关的编程语言”?
- 答案:Java通过虚拟机实现了跨平台性,即同一份Java代码可以在不同的操作系统上运行,只需将其编译成相应平台的字节码即可。
- Java支持哪些数据类型?
- 答案:Java支持八种基本数据类型:byte, short, int, long, float, double, boolean,以及八种引用类型:Byte, Short, Integer, Long, Float, Double, Boolean。
- 什么是自动拆装箱?
- 答案:自动拆装箱是Java 5引入的一个特性,它允许使用装箱类(如Integer、Double等)和包装类(如Integer类中的value属性)之间进行转换,无需显式地创建临时对象。
- Java中,什么是构造方法?
- 答案:构造方法是类中的一个特殊方法,用于初始化对象实例。当创建对象时,会自动调用该方法来初始化对象的状态。
- Java支持多继承吗?
- 答案:Java不支持多继承,但可以通过接口来模拟多继承的效果。一个类可以实现多个接口,但只能继承一个类。
- 接口和抽象类有哪些区别?
- 答案:接口不能包含任何方法定义,而抽象类可以包含抽象方法。接口默认为public,而抽象类的抽象方法默认为protected。一个类可以实现多个接口,但只能继承一个抽象类。
- 面向对象编程有哪些特征?
- 答案:面向对象编程的特征包括封装、继承和多态。封装隐藏了对象的内部细节;继承允许新创建的类继承现有类的属性和方法;多态允许子类重写父类的方法。
- ++i与i++的区别是什么?
- 答案:++i和i++都表示对变量i的自增操作,但它们的执行顺序不同。++i先将i的值增加1,然后返回新的值;而i++先返回i的当前值,然后将i的值增加1。
- 类中的main方法可以声明为private吗?
- 答案:Java中main方法必须是public,如果是private,那么它虽然在编译时不会出错,但是在运行时会出错,因为main方法是程序的入口点,必须是public。
“==”和equals方法究竟有什么区别?
- 答案:
==
用于比较两个对象的内存地址,而equals()
方法用于比较两个对象的内容是否相同。即使两个对象的内容相同,如果它们的内存地址不同,使用==
会返回false
,而使用equals()
会返回true
。
- 答案:
静态变量和实例变量的区别?
- 答案:静态变量属于类本身,所有实例共享;实例变量属于类的实例,每个实例有自己的副本。静态变量在类加载时就已经存在,而实例变量则随着对象的创建而存在。
是否可以从一个static方法内部发出对非static方法的调用?
- 答案:可以。虽然static方法不能直接访问非static成员,但可以通过实例来间接调用非static方法。
思考Tomcat 类加载器为什么要违背双亲委派模型?
- 答案:Tomcat类加载器需要加载多个Web应用,这些应用可能依赖于不同版本的库。如果严格遵循双亲委派模型,新加载的类将无法替换旧加载的类,从而导致运行时错误。因此,Tomcat类加载器采用了破坏性破坏(破坏双亲委派模型)策略,以确保正确的类替换。
Java8 lambda 表达式forEach 如何提前终止?
- 答案:在使用lambda表达式的
forEach
方法时,可以通过抛出异常并捕获这个异常来实现提前终止。例如:这样,当满足某个条件时,就会抛出异常,从而提前终止循环。1
2
3
4
5
6
7List<String> list = Arrays.asList ("a", "b");
list.forEach (e -> {
if (条件) {
throw new RuntimeException();
}
// 其他逻辑
});
- 答案:在使用lambda表达式的
线程安全的set有哪些?
- 答案:Java中没有内置的线程安全集合,但可以使用
CopyOnWriteArrayList
或使用synchronized关键字来实现线程安全的集合操作。
- 答案:Java中没有内置的线程安全集合,但可以使用
HashMap1.7与1.8底层如何实现?
- 答案:在Java 1.8中,HashMap采用了红黑树来优化大量元素存储的情况。红黑树是一种自平衡的二叉查找树,它可以保证最坏情况下的时间复杂度为O(log n),从而提高了性能。
什么是并发与并行?
- 答案:并发是指多个任务在同一时间段内执行,但不一定在同一时刻执行;并行是指多个任务同时在不同的处理器上执行。并发关注的是任务的执行顺序,而并行关注的是任务的执行位置。
线程和进程的区别?
- 答案:线程是进程的一部分,是轻量级的用户线程;进程是操作系统调度和分配资源的基本单位。线程共享父进程的地址空间,而进程则拥有独立的地址空间。线程比进程更节省系统资源。
守护线程是什么?
- 答案:守护线程(Daemon Thread)是一种特殊类型的线程,当Java虚拟机(JVM)启动后,守护线程会在JVM关闭之前一直运行。守护线程通常用于执行后台任务,如清理缓存、日志记录等。
JVM内存模型是什么?
- JVM内存模型包括堆(Heap)、栈(Stack)、方法区.Method Area、程序计数器.Program Counter。堆用于存储对象实例,栈用于存储局部变量和操作码,方法区用于存储已被虚拟机加载的类信息、常量、静态变量等数据,以及即时编译器编译后的代码缓存等信息。
解释一下垃圾回收(GC)的工作原理。
- 垃圾回收是通过标记-清除算法来实现的。首先,GC会遍历堆中的对象,将所有可达对象标记为“活着”。然后,将未被任何引用指向的对象标记为“不可达”。最后,清除所有不可达的对象,并将其内存空间重新分配给其他对象使用。
什么是单例模式?请给出实现代码示例。
- 单例模式是一种确保一个类只有一个实例,并提供一个全局访问点的设计模式。实现代码如下:
1
2
3
4
5
6
7
8
9
10public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 单例模式是一种确保一个类只有一个实例,并提供一个全局访问点的设计模式。实现代码如下:
解释一下Synchronized关键字的工作原理。
synchronized
关键字可以修饰方法或代码块。当一个线程访问某个对象的synchronized
方法时,它会自动获取该对象的监视器。如果另一个线程尝试访问同一对象的synchronized
方法,它将被阻塞,直到前一个线程释放监视器。
什么是多态?请给出一个例子。
- 多态是面向对象编程中的一个核心概念,指的是不同类的对象对同一消息做出响应,但具体行为取决于它们的实际类型。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25interface Animal {
void speak();
}
class Dog implements Animal {
public void speak() {
System.out.println ("Bark!");
}
}
class Cat implements Animal {
public void speak() {
System.out.println ("Meow!");
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.speak (); // 输出: Bark!
myCat.speak (); // 输出: Meow!
}
}
- 多态是面向对象编程中的一个核心概念,指的是不同类的对象对同一消息做出响应,但具体行为取决于它们的实际类型。例如:
解释一下接口和抽象类的区别。
- 接口是一种引用类型,可以包含方法声明,但不包含方法实现。抽象类可以包含抽象方法(没有实现的方法)和具体方法(有实现的方法)。接口不能包含状态信息,而抽象类可以包含状态信息。
什么是反射?请给出一个使用反射的例子。
- 反射是Java的一个高级特性,允许程序在运行时查询、访问和修改其自身属性和方法。例如:
1
2
3
4
5
6
7
8
9
10import java.lang.reflect.Method ;
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class<?> cl = Class.forName ("java.lang.String ");
Method method = cl.getMethod ("length", new Class<?>[0]);
Object result = method.invoke ("hello");
System.out.println ("Length: " + result);
}
}
- 反射是Java的一个高级特性,允许程序在运行时查询、访问和修改其自身属性和方法。例如:
解释一下异常处理机制。
- 异常处理机制包括捕获异常、声明异常、抛出异常和自定义异常。异常处理通常通过
try-catch-finally
语句来实现,其中try
块中可能发生异常,catch
块用于捕获异常,finally
块无论是否发生异常都会执行。
- 异常处理机制包括捕获异常、声明异常、抛出异常和自定义异常。异常处理通常通过
什么是序列化?请给出一个序列化和反序列化的例子。
- 序列化是将对象的状态转换为一种形式的流,以便可以在网络上传输或持久化存储。反序列化则是将这种流转换回原始对象的状态。例如:
1
2
3
4
5
6
7
8import java.io.Serializable ;
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Person
- 序列化是将对象的状态转换为一种形式的流,以便可以在网络上传输或持久化存储。反序列化则是将这种流转换回原始对象的状态。例如:
日期和时间:
- 打印昨天的当前时刻。
比较一下Java 和JavaScript:
- Java是一种静态类型的、面向对象的编程语言,用于开发大型应用程序。它在运行时需要一个JVM(Java虚拟机)环境。而JavaScript是一种动态类型的、基于事件驱动的脚本语言,通常用于客户端脚本和服务器端脚本。
什么时候用断言(assert)?
- 断言用于验证程序中的某些条件是否满足。在开发阶段,断言可以帮助检测错误;在生产环境中,断言可以帮助确认程序的状态。
Error 和Exception 有什么区别?
- Error是指程序执行过程中出现的问题,但这些问题不会被捕获。Exception是指程序执行过程中出现的可预见的问题,可以通过try-catch块捕获。
notify()和notifyAll()有什么区别?
- notify()方法仅唤醒一个等待在此对象监视器上的单个线程。如果有多个线程等待,则只有一个会被唤醒。notifyAll()方法则唤醒所有等待在此对象监视器上的线程。
线程的run()和start()有什么区别?
- run()方法是直接调用线程的run方法,而start()方法是启动一个新的线程,并调用该线程的run方法。使用start()方法可以避免线程间的竞争。
创建线程池有哪几种方式?
- 可以通过实现ExecutorService接口来创建线程池,也可以使用java.util.concurrent.ThreadPoolExecutor 类来创建线程池。
Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
- Array可以包含基本类型和对象类型,因为Array的元素类型是泛型。而ArrayList只能包含对象类型,因为ArrayList的元素类型也是泛型。
Array大小是固定的,ArrayList的大小是动态变化的。
- Array的大小在初始化时就确定下来,是固定的。而ArrayList的大小是动态变化的,可以根据需要进行扩容或缩容。
ArrayList提供了哪些方法?
- ArrayList提供了add()、remove()、get()、set()、clear()等方法。其中add()方法用于添加元素,remove()方法用于删除元素,get()方法用于获取元素,set()方法用于设置元素,clear()方法用于清空列表。
CyclicBarrier 和 CountDownLatch 的区别?
- CyclicBarrier用于等待所有的线程都达到某个同步点,然后继续执行。CountDownLatch用于等待一组操作完成后再继续执行。
CAS 的问题:
- CAS容易造成ABA问题,不能保证代码块的原子性,且可能导致CPU利用率增加。
ReadWriteLock 是什么?
- ReadWriteLock是一种读写锁,它允许多个读操作同时进行,但写操作必须排队等待读操作完成后才能进行。
类成员函数的重载、覆盖和隐藏区别?
- 重载是指在同一个类中,具有相同名称但参数列表不同的方法。覆盖是指子类继承自父类的方法。隐藏是指子类中的方法与父类中的同名方法签名相同,但返回类型不同。
接口中所有的方法隐含的都是抽象的。
- 是的,Java中的接口默认所有方法都是抽象的,除非显式地声明为具体实现。
类可以实现很多个接口,但是只能继承一个抽象类。
- 是的,一个类可以实现多个接口,但只能继承一个抽象类。
类如果要实现一个接口,它必须要实现接口声明的所有方法。
- 是的,一个类如果要实现一个接口,就必须实现接口中声明的所有方法。
在Java中导入包时,其子包不会被导入,如果有需要,开发者必须单独导入。
- 是的,当你导入一个包时,只有该包中的类会被导入,子包中的类需要单独导入。
类中的main方法可以声明为private?
- 不可以