写点什么

面试问题总结(一)

作者:xfgg
  • 2023-12-07
    福建
  • 本文字数:4388 字

    阅读完需:约 14 分钟

面试问题总结(一)

写这份文章,是为了将自己经历的以及从各个网站看到面试题目记录下来

反复阅读,提升自己,查缺补漏!

方法重载和方法重写

JVM 调用方法是通过方法签名来判断到底要调用哪个方法,而方法签名 = 方法名称+参数类型+参数个数组成的一个唯一值,这个唯一值就是方法签名。

方法重载:在同一个类中定义多个方法,它们具有相同的名称但参数列表不同。

方法重写:发生在两个类之间,这两个类具有父子关系,也就是子类继承父类。当子类需要改变从父类继承来的方法的行为时,子类可以定义一个与父类方法签名完全相同(方法名、参数列表、返回类型)的方法,这样,子类就重写了父类的方法。


多态的实现原理

  • 多态概念:允许通过父类类型的引用变量来引用子类对象,并在运行时根据实际对象的类型来确定调用哪个方法。换句话说,一个对象可以根据不同的情况表现出多种形态。通过多态,我们可以利用父类类型的引用变量来指向子类对象,并根据实际对象的类型调用对应的方法。这样可以在不修改现有代码的情况下,动态地切换和扩展对象的行为。

  • 特点和优势:可替换性:子类对象可以随时替代父类对象,向上转型。可扩展性:通过添加新的子类,可以扩展系统的功能。接口统一性:可以通过父类类型的引用访问子类对象的方法,统一对象的接口。代码的灵活性和可维护性:通过多态,可以将代码编写成通用的、松耦合的形式,提高代码的可维护性。

  • 实现原理:基于 Java 虚拟机(JVM)的动态方法分派机制,静态类型的类型信息用于在编译期进行类型检查,并确定了该方法的版本(抽象的位置),然后在运行时根据实际类型确定实际的方法版本。

  • 动态绑定:指的是在编译时,Java 编译器只能知道变量的声明类型,而无法确定其实际的对象类型。而在运行时,Java 虚拟机(JVM)会通过动态绑定来解析实际对象的类型。这意味着,编译器会推迟方法的绑定(即方法的具体调用)到运行时。

  • 虚拟方法调用:在 Java 中,所有的非私有、非静态和非 final 方法都是被隐式地指定为虚拟方法。虚拟方法调用是在运行时根据实际对象的类型来确定要调用的方法的机制。当通过父类类型的引用变量调用被子类重写的方法时,虚拟机会根据实际对象的类型来确定要调用的方法版本,而不是根据引用变量的声明类型。

  • 实现流程:

  1. 创建父类类型的引用变量,并将其赋值为子类对象。

  2. 在运行时,通过动态绑定确定引用变量所指向的实际对象的类型。

  3. 根据实际对象的类型,调用相应的方法版本。


多态性表现为方法的重载和重写,以及接口的实现。

public class Animal {    public void talk() {        System.out.println("Animals Talk");    }}
public class Dog extends Animal { @Override public void talk() { System.out.println("Dog barks"); }}
public class Cat extends Animal { @Override public void talk() { System.out.println("Cat meows"); }}
public class Main { public static void main(String[] args) { Animal myDog = new Dog(); Animal myCat = new Cat(); myDog.talk(); // Dog barks myCat.talk(); // Cat meows }}
复制代码

抽象类、普通类、接口的区别

  • 实例化:普通类可实例化,但抽象类和接口不可以。

  • 继承:普通类和抽象类是“is-a”的关系,接口是"like-a"的关系。

  • 设计层级:接口是最抽象的,没有具体实现。抽象类则介于接口和普通类之间。

  • 方法实现:普通类中的方法都需要实现,接口中的方法不需要实现,抽象类中则可包含已实现的和未实现的方法

ArrayList、LinkedList、Vector 区别

三者的主要区别在于数据结构和线程安全性。ArrayList 和 Vector 有良好的随机访问性能,但插入删除性能较差,而 LinkedList 则相反。在进行并发操作时,应优先选择线程安全的 Vector,当然也可以使用 Collections.synchronizedList 来包装 ArrayList 或 LinkedList 使其具有线程安全性。

  1. ArrayList

  • ArrayList 底层数据结构是动态数组,可以动态调整容量大小。

  • ArrayList 插入元素和删除元素需要移动大量元素,所以这两个操作的时间复杂度较高,适合随机查找和遍历,不适合插入和删除。

  • ArrayList 是非线程安全的。

  1. LinkedList

  • LinkedList 底层数据结构是双向链表,插入和删除元素只需要改变元素的引用值,所以插入和删除操作的时间复杂度低。

  • LinkedList 不能进行有效的随机读取,因为每次读取都要从头开始遍历,所以它不适合进行查找和修改操作。

  • LinkedList 是非线程安全的。

  1. Vector

  • Vector 和 ArrayList 非常类似,底层都是使用数组实现,所以随机访问效率高,插入和删除效率低。

  • Vector 与 ArrayList 的一大不同是,Vector 是线程安全的,它的大部分方法都进行了 synchronize 同步。

HashMap、HashTable、HashSet 区别

这三者的主要区别是 HashMap 和 Hashtable 是储存键值对的映射,而 HashSet 则只存储不重复的元素。另外,对于安全性,Hashtable 是线程安全的,而 HashMap 和 HashSet 是非线程安全的。从性能上来说,由于线程安全需要额外的同步,所以 Hashtable 的性能通常会比 HashMap 和 HashSet 低一些。

1. HashMap

  • HashMap 是一个在 Java Collections Framework 中常用的数据结构,它是以键值对的形式存储数据,键不允许重复,但值可以重复。

  • HashMap 允许一个空键和多个空值。

  • HashMap 是不同步的,也就是说它是非线程安全的。

  • HashMap 的迭代器(Iterator)是 fail-fast 的,如果在使用迭代器进行迭代过程中有其他线程修改了 map,则会抛出 ConcurrentModificationException。

2. Hashtable

  • Hashtable 和 HashMap 类似,也是基于哈希表的数据结构,以键值对存储数据,键不允许重复,值允许重复。

  • Hashtable 不允许空键和空值。

  • Hashtable 是同步的,即是线程安全的,性能可能比较低,因为它支持多线程,任何线程都可以操作。

  • Hashtable 的迭代器同样也是 fail-fast 的。

3. HashSet

  • HashSet 是一种没有重复元素的集合,它没有键和值的概念,更接近于一个不允许有重复元素的 List。

  • HashSet 允许空值但不允许复制。

  • HashSet 内部其实是一个 HashMap,仅仅是将 HashMap 中 value 的位置设为唯一常量。

  • HashSet 不是线程安全的,它的迭代器同样也是 fail-fast 的。

负载因子

HashMap 负载因子 load factor,也叫做扩容因子和装载因子,它是 HashMap 在进行扩容时的一个阈值,当 HashMap 中的元素个数超过了容量乘以负载因子时,就会进行扩容。默认的负载因子是 0.75,也就是说当 HashMap 中的元素个数超过了容量的 75% 时,就会进行扩容。当然,我们也可以通过构造函数来指定负载因子。

扩容的计算公式:initialCapacity * loadFactor = HashMap 扩容,initialCapacity 是初始容量,loadFactor 是负载因子

扩容是为了解决哈希冲突,提高 HashMap 性能

HashMap 底层原理

1.8 之前,HashMap 底层是通过数组+链表实现的

1.8 之后,HashMap 底层是通过数组+链表或红黑树实现的

HashMap 内部维护了一个数组,每个数组元素又是一个链表或者红黑树,每个链表或者红黑树节点存储了一个键值对。当需要存储新的键值对时,HashMap 会根据键的哈希值确定其在数组中的位置,如果该位置已经有了其他键值对,则通过链表或红黑树解决冲突,将新的键值对添加到链表或红黑树的末尾。当链表或红黑树长度达到一定程度后,HashMap 会自动将链表转换为红黑树,以提高查找效率。

链表和红黑树互转流程

  • 链表升级为红黑树:

  • 红黑树退化为链表:

哈希冲突的解决方案

  • 链地址法:将哈希表中的每个桶都设置为一个链表,当发生哈希冲突时,将新的元素插入到链表的末尾。这种方法的优点是简单易懂,适用于元素数量较多的情况。缺点是当链表过长时,查询效率会降低。

  • 开放地址法:当发生哈希冲突时,通过一定的探测方法(如线性探测、二次探测、双重哈希等)在哈希表中寻找下一个可用的位置。这种方法的优点是不需要额外的存储空间,适用于元素数量较少的情况。缺点是容易产生聚集现象,即某些桶中的元素过多,而其他桶中的元素很少。

  • 再哈希法:当发生哈希冲突时,使用另一个哈希函数计算出一个新的哈希值,然后将元素插入到对应的桶中。这种方法的优点是简单易懂,适用于元素数量较少的情况。缺点是需要额外的哈希函数,且当哈希函数不够随机时,容易产生聚集现象。

反射原理

反射是指在运行时检查和操作类、接口、字段、方法等程序结构的能力。通过反射,可以在运行时获取类的信息,创建类的实例,调用类的方法,访问和修改类的字段

public class User{  public String name = "张三";  private int age = 18;  public String getName(){    return name;  }  public static int getAge(){    return age;  }  private int getAges(){    return age;  }}
复制代码

反射执行公共方法

// 反射得到对象Class<?> clazz = Class.forName("User");// 得到方法Method method = clazz.getDeclaredMethod("getName");// 执行普通方法method.invoke(clazz.newInstance());
复制代码

反射执行私有方法

// 反射得到对象Class<?> clazz = Class.forName("User");// 得到方法Method method = clazz.getDeclaredMethod("getAges");// 设置私有方法可访问method.setAccessible(true);// 执行普通方法method.invoke(clazz.newInstance());
复制代码

反射执行静态方法

// 反射得到对象Class<?> clazz = Class.forName("User");// 得到方法Method method = clazz.getDeclaredMethod("getAge");// 执行普通方法method.invoke(clazz.newInstance());
复制代码

反射得到公共属性值

// 反射得到对象Class<?> clazz = Class.forName("User");// 得到公共属性Field field = clazz.getDeclaredField("name");// 得到属性值String name = (String) field.get(clazz.newInstance());
复制代码

反射得到私有属性值

// 反射得到对象Class<?> clazz = Class.forName("User");// 得到私有属性Field privateField = clazz.getDeclaredField("age");// 设置私有属性可访问privateField.setAccessible(true);// 得到属性值int age = (int) privateField.get(clazz.newInstance());
复制代码

Java 内存模型

Java 内存模型是用来定义 Java 线程和内存之间的操作规范的,目的是解决多线程正确执行的问题

  • 主内存:所有线程共享的内存区域,包含了对象的字段、方法和运行时常量池等数据。

  • 工作内存:每个线程拥有自己的工作内存,用于存储主内存中的数据的副本。线程只能直接操作工作内存中的数据。

  • 内存间交互操作:线程通过读取和写入操作与主内存进行交互。读操作将数据从主内存复制到工作内存,写操作将修改后的数据刷新到主内存。

  • 原子性:JMM 保证基本数据类型(如 int、long)的读写操作具有原子性,即不会被其他线程干扰,保证操作的完整性。

  • 可见性:JMM 确保一个线程对共享变量的修改对其他线程可见。这意味着一个线程在工作内存中修改了数据后,必须将最新的数据刷新到主内存,以便其他线程可以读取到更新后的数据。

  • 有序性:JMM 保证程序的执行顺序按照一定的规则进行,不会出现随机的重排序现象。这包括了编译器重排序、处理器重排序和内存重排序等


发布于: 刚刚阅读数: 3
用户头像

xfgg

关注

THINK TWICE! CODE ONCE! 2022-11-03 加入

目前:全栈工程师(前端+后端+大数据) 目标:架构师

评论

发布
暂无评论
面试问题总结(一)_Java_xfgg_InfoQ写作社区