跳至主要內容

Java基础面试题


Java基础面试题

基础

JDK、JRE、JVM是什么?

JDK 即为 Java 开发工具包,包含编写 Java 程序所必须的编译、运行等开发工具以及 JRE。开发工具如:

  • 用于编译 Java 程序的 javac 命令。
  • 用于启动 JVM 运行 Java 程序的 Java 命令。
  • 用于生成文档的 Javadoc 命令。
  • 用于打包的 jar 命令等等。

JRE 即为 Java 运行环境,提供了运行 Java 应用程序所必须的软件环境,包含有 Java 虚拟机(JVM)和丰富的系统类库。系统类库即为 Java 提前封装好的功能类,只需拿来直接使用即可,可以大大的提高开发效率。

JVM 即为 Java 虚拟机,提供了字节码文件(.class)的运行环境支持。

机器码与字节码的区别

字节码是一种包含执行程序、由一系列操作码和数据对组成的二进制文件。

字节码是一种中间码,它比机器码更抽象,需要直译器转译后才能称为机器码。

数据类型与程序结构

String、StringBuffer、StringBuilder的区别?

  • String ,是只读字符串,也就意味着 String 引用的字符串内容是不能被改变的。
  • StringBuffer/StringBuilder 类,表示的字符串对象可以直接进行修改。StringBuilder 是 Java 5 中引入的,它和 StringBuffer 的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被 synchronized 修饰,因此它的效率也比 StringBuffer 要高。

什么是自动拆箱装箱?

自动装箱和拆箱,就是基本类型和引用类型之间的转换。

自动装箱拆箱可以让我们的代码更简练:在Java5之前,集合中不能存入原始类型的值,因为集合只接收对象。

final、finally、finalize 的区别?

  • final:final ,是修饰符关键字。
    • 如果一个类被声明为 final ,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract 的,又被声明为 final 的。
    • 将变量或方法声明为 final ,可以保证它们在使用中不被改变。被声明为 final 的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为 final 的方法也同样只能使用,不能重写。
  • finally:在异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。但是在以下特殊情况中,finally块不会被执行:
    • 在前面的代码中用了 System.exit() 退出程序。
    • 程序所在的线程死亡。
  • finalize:finalize是方法名。Java 允许使用 #finalize() 方法,在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。

java程序的结构?

  • 顺序结构
  • 选择结构:if else
  • 循环结构: while、do while、for

值传递与引用传递的区别?

在方法调用时,通常需要传递一些参数来完成特定的功能。Java语言提供了两种参数传递的方式:值传递与引用传递。

  • 值传递:在方法调用中,实参会把它的值传递给形参,形参只是实参的值初始化一个临时的存储单元,因此形参与实参虽然有着相同的值,但是却有着不同的存储单元,因此对形参的改变不会影响实参的值。
  • 引用传递:在方法调用中,传递的是对象,这时形参与实参的对象指向同一块存储单元,因此对形参的修改就会影响实参的值。

如何实现main()方法执行前执行代码块

在Java语言中,由于静态块在类被加载时就会被调用,因此可以在main()方法执行前,利用静态块实现输出"hello world"的功能。

由于静态块不管顺序如何,都会在main()方法之前执行,因此,即使static代码块写在main()方法之后,它都依然在main()方法之前执行。

面向对象

什么是面向对象?

面向对象是一种思想,世间万物都可以看做一个对象。面向对象软件开发具有以下优点:

  • 代码开发模块化,更易维护和修改。
  • 代码复用性强。
  • 增强代码的可靠性和灵活性。
  • 增加代码的可读性。

面向对象的特征?

封装、继承、多态、抽象。

  1. 封装(Encapsulation):封装是将数据和操作封装在一个单元(即对象)中,通过定义公共接口来控制对数据的访问。对象的内部状态和实现细节被隐藏,只暴露有限的接口供其他对象进行交互。封装提供了数据的安全性和灵活性,同时也实现了代码的模块化和重用。
  2. 继承(Inheritance):继承是一种机制,允许一个类(称为子类或派生类)从另一个类(称为父类或基类)继承属性和方法。子类可以继承父类的特征,并且可以在此基础上进行扩展、修改或重写。继承实现了代码的重用和层次结构的组织,提供了面向对象编程中的一种重要关系。
  3. 多态(Polymorphism):多态是指同一个方法名可以在不同的对象上执行不同的操作,提供了一种统一的接口来处理不同类型的对象。多态可以通过继承和接口实现,使得代码更加灵活和可扩展。在多态中,对象的具体类型可以在运行时确定,实现了代码的动态性。
  4. 抽象(Abstraction):抽象是指将复杂的现实世界问题抽象为类、接口或抽象类等概念,忽略不必要的细节,关注问题的本质和关键特征。抽象提供了一种模型化的方式来描述问题领域,帮助开发人员理解和处理复杂性。抽象也是面向对象编程的核心思想之一。

抽象类和接口有什么区别?

从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。

  • 相同点:接口与抽象类都不能被实例化,但可以定义引用变量指向实例对象。本类型之间扩展关键字都是extends。
  • 不同点:
语法维度抽象类接口
定义关键字abstractinterface
子类继承或实现关键字extendsimplements
方法实现可以有JDK8以前不能有,JDK8之后允许有default实现
方法访问控制符无限制有限制,默认是public abstract类型
属性访问控制符无限制有限制,默认是public static final类型
静态方法可以有不能有
static{}静态代码块可以有不能有
本类型之间扩展单继承多继承

java类的定义/组成?

  • 类的定义由访问级别、类型、类名、是否抽象、是否静态、泛型标识、继承或实现关键字、父类或接口名称等组成。
  • 类的访问级别有public和无访问控制符。
  • 类的类型为class、interface、enum。

相关demo:System.out.println();语句不写在方法内会报错。

public class Test{
    public static void main(String[] args){
        System.out.println("Hello World");
    }
        System.out.println("Hello World");//此处编译器报错
}

== equals()

  • ==比较
    • 基本类型:比较的就是值是否相同。
    • 引用类型:比较的就是地址值是否相同。
  • equals:
    • 引用类型:默认情况下,比较的是地址值。
    • 两个对象的hashCode相同,equals方法不一定为true。

集合

Collection和Collections有什么区别?

Collection是Java集合框架中的一个接口,它表示一组对象,这些对象通常被称为集合。Collections是Java集合框架中的一个工具类,它提供了一些静态方法来操作集合。

ArrayList和LinkedList有什么区别?

ArrayList和LinkedList都是List接口的实现类。ArrayList基于动态数组实现,而LinkedList基于双向链表实现。因此,当需要随机访问集合元素时,ArrayList的性能更好,因为它可以通过索引直接访问元素;而当需要频繁地进行插入和删除操作时,LinkedList的性能更好,因为它只需要修改指针即可完成插入和删除操作。

HashSet和TreeSet有什么区别?

HashSet和TreeSet都是Set接口的实现类。HashSet基于哈希表实现,而TreeSet基于红黑树实现。因此,HashSet中的元素是无序的,而TreeSet中的元素是有序的。此外,HashSet的插入、删除和查找操作的时间复杂度是常数级别的,而TreeSet的时间复杂度是对数级别的。

HashMap和TreeMap有什么区别?

HashMap和TreeMap都是Map接口的实现类。HashMap基于哈希表实现,而TreeMap基于红黑树实现。因此,HashMap中的元素是无序的,而TreeMap中的元素是有序的。此外,HashMap的插入、删除和查找操作的时间复杂度是常数级别的,而TreeMap的时间复杂度是对数级别的。

ConcurrentModificationException是什么?

ConcurrentModificationException是Java集合框架中的一个异常,它表示在迭代集合的过程中,集合的结构被修改了。例如,在使用Iterator迭代集合的过程中,如果在迭代过程中修改了集合的结构(如添加或删除元素),就会抛出ConcurrentModificationException异常。

如何遍历一个Map?

可以使用以下方法遍历一个Map:

  • 使用Iterator遍历Map的entrySet
  • 使用for-each循环遍历Map的keySet或values
  • 使用Java 8中的Stream API遍历Map的entrySet

例如,以下代码展示了如何使用Iterator遍历一个Map的entrySet:

Map<String, Integer> map = new HashMap<>();
// 添加元素到map中
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<String, Integer> entry = iterator.next();
    String key = entry.getKey();
    Integer value = entry.getValue();
    // 处理key和value
}

HashMap工作原理

HashMap是Java中最常用的数据结构之一,它是一种键值对存储结构,可以快速地根据给定的键找到对应的值。它的工作原理是基于哈希表(Hash Table)实现的。

哈希表是一种基于数组的数据结构,它通过将键映射为数组中的索引来访问值。具体来说,当我们向HashMap中添加一个键值对时,HashMap会首先对键进行哈希运算,得到一个哈希值。这个哈希值就是该键在数组中对应的索引。如果该索引位置还没有被占用,那么直接将该键值对存储在该位置即可。如果该索引位置已经被占用,那么就会发生哈希冲突,这时候需要解决冲突。

解决哈希冲突的方法有两种:链表法和开放地址法。链表法是将具有相同哈希值的键值对存储在同一个链表中,而开放地址法则是在数组中寻找下一个空闲位置来存储该键值对。Java中的HashMap采用了链表法解决哈希冲突,当链表长度超过一定阈值(默认为8)时,链表会转化为红黑树,以提高查找效率。

当我们需要查找一个键对应的值时,HashMap会对该键进行哈希运算,得到该键在数组中对应的索引。然后在该索引位置上的链表(或红黑树)中查找该键对应的值。由于哈希表的查找复杂度为O(1),所以HashMap的查找效率非常高。

需要注意的是,由于哈希表的大小是固定的,当哈希表中存储的键值对数量过多时,会导致哈希冲突的概率增大,从而影响HashMap的性能。因此,Java中的HashMap提供了对容量和负载因子的控制,以保证HashMap的性能。

Comparable和Comparator是什么?他们的区别?

Comparable和Comparator都是Java中用于排序的接口,它们的作用是给对象排序。

Comparable是一个内部比较器接口,它定义了一个compareTo方法,用于比较当前对象和另一个对象的大小。实现了Comparable接口的类可以通过调用自身的compareTo方法来与其他对象进行比较。例如,可以实现Comparable接口来对自定义类的对象进行排序,例如在使用Collections.sort()方法时。

Comparator是一个外部比较器接口,它定义了一个compare方法,用于比较两个对象的大小。Comparator接口的实现类可以用来对任意的对象进行比较,而不需要改变对象本身的类定义。因此,Comparator接口适用于需要对一个类进行多种排序方式的场景,例如在使用Collections.sort()方法时,可以传入一个Comparator对象来指定排序方式。

因此,Comparable和Comparator的区别在于:

  1. Comparable接口是一个内部比较器,它定义在对象自身的类中,实现Comparable接口的类可以通过调用自身的compareTo方法来进行比较。而Comparator是一个外部比较器,它定义在一个单独的类中,它可以用来比较任意的两个对象,不需要访问对象自身的方法。
  2. Comparable接口只能定义一种排序方式,而Comparator接口可以定义多种排序方式,通过传入不同的Comparator对象来实现。
  3. 实现了Comparable接口的类可以直接调用Collections.sort()方法进行排序,而对于不实现Comparable接口的类,只能使用Collections.sort(List, Comparator)方法进行排序。

HashMap和ConcurrentHashMap的区别?

HashMap和ConcurrentHashMap都是Java中常用的哈希表实现,它们的主要区别在于线程安全性和性能表现。

HashMap是非线程安全的,多个线程同时对一个HashMap进行操作可能会导致数据不一致或者抛出ConcurrentModificationException异常。因此,如果需要在多线程环境下使用HashMap,需要使用诸如synchronized之类的线程安全措施来保证线程安全性。但是,这样会降低HashMap的性能。

ConcurrentHashMap是线程安全的,它在内部使用了分段锁机制来保证线程安全性。具体来说,ConcurrentHashMap将整个哈希表分成多个小的段,每个段都有一个独立的锁来控制对该段的并发访问。这样就可以在不同线程访问不同段的情况下,实现高并发的读写操作,从而提高了性能。

另外,HashMap在迭代时不是线程安全的,如果在迭代时对HashMap进行修改,可能会导致ConcurrentModificationException异常。而ConcurrentHashMap支持在迭代时对其中的元素进行修改,不会抛出该异常。

上次编辑于:
贡献者: Neil