2023年Java核心技术面试第四篇(篇篇万字精讲)

简介: 2023年Java核心技术面试第四篇(篇篇万字精讲)

八. 对比Vector,ArrayList, LinkedList有何区别?



8.1  典型回答


三者都是实现集合框架中的List,即有序集合,都按照位置进行定位,添加或者删除的操作,都提供迭代器进行遍历内容,由于具体的设计,在行为,性能,线程安全方面又有些不同。


8.1.1 Vector:


是Java早期提供的线程安全的动 态数组,如果不需要线程安全,不建议使用,同步需要进行额外开销,Vector内部使用对象数组来保存数据,可以根据需要自动的增加容量,当数组已满时,会创建新的数组,并且拷贝原有的数组数据


8.1.2 ArrayList :


是应用更加广泛的动态数组实现,本身不是线程安全的,性能好很多。

ArrayList可以根据需要调整容量,vector在扩容的时候会提高一倍,ArrayList则是增加50%


8.1.3 LinkedList


Java 提供的双向链表,不需要进行上面两种进行调整容量,也不是线程安全的。


8.2 考察点分析:


8.2.1 不同容器类型适合的场景


8.2.1.1 Vector和ArrayList


动态数组,内部元素以数组形式顺序存储,适合随机访问的场合,除了尾部插入和删除元素,往往会性能会相对较差,比如我们中间位置插入一个元素,需要移动后续所有元素。


8.2.1.2 LinkedList


进行节点插入,删除高效很多,但是随机访问性能则比动态数组慢。


8.3 小结:


Vector、ArrayList、LinkedList均为线型的数据结构,但是从实现方式与应用场景中又存在差

别。


8.3.1 底层实现方式


ArrayList内部用数组来实现;LinkedList内部采用双向链表实现;Vector内部用数组实现。

8.3.2 读写机制


ArrayList在执行插入元素是超过当前数组预定义的最大值时,数组需要扩容,扩容过程需要

调用底层System.arraycopy()方法进行大量的数组复制操作;在删除元素时并不会减少数组的

容量(如果需要缩小数组容量,可以调用trimToSize()方法);在查找元素时要遍历数组,对

于非null的元素采取equals的方式寻找。


LinkedList在插入元素时,须创建一个新的Entry对象,并更新相应元素的前后元素的引用;

在查找元素时,需遍历链表;在删除元素时,要遍历链表,找到要删除的元素,然后从链表

上将此元素删除即可。


Vector与ArrayList仅在插入元素时容量扩充机制不一致。对于Vector,默认创建一个大小为10

的Object数组,并将capacityIncrement设置为0;当插入元素数组大小不够时,如果capacityI

ncrement大于0,则将Object数组的大小扩大为现有size+capacityIncrement;如果capacityIn


crement<=0,则将Object数组的大小扩大为现有大小的2倍。


8.3.3 读写效率


ArrayList对元素的增加和删除都会引起数组的内存分配空间动态发生变化。因此,对其进行

插入和删除速度较慢,但检索速度很快。


LinkedList由于基于链表方式存放数据,增加和删除元素的速度较快,但是检索速度较慢。


8.3.4 线程安全性


ArrayList、LinkedList为非线程安全;Vector是基于synchronized实现的线程安全的ArrayList。

需要注意的是:单线程应尽量使用ArrayList,Vector因为同步会有性能损耗;即使在多线程环境下,我们可以利用Collections这个类中为我们提供的synchronizedList(List list)方法返回一

个线程安全的同步列表对象。


九.  对比Hashtable,HashMap,TreeMap



Map是广义Java集合框架中的另外一部分,HashMap作为框架中使用频率最高的类型之一。


9.1 典型回答


9.1.1 Hashtable,HashMap,TreeMap


常见的Map实现,以键值对的形式存储和操作数据的容器类型。


9.1.1.1 Hashtable


早期Java类库提供的一个哈希表实现,本身是同步的,不支持null键和值,由于同步导致的性能开销,很少被推荐使用。


9.1.1.1.1详细解析:


它是同步的,也就是说它的方法都是线程安全的,可以在多线程环境下使用。


然而,由于同步操作需要获取锁、释放锁,这会引入额外的性能开销。在高并发的场景下,这种同步机制可能成为瓶颈,影响性能。因此,在现代的Java开发中,很少推荐使用Hashtable。


除了性能开销外,Hashtable还有其他一些限制。首先,Hashtable不支持null键和null值,如果尝试将null作为键或值进行插入,会抛出NullPointerException异常。其次,Hashtable的扩容机制比较低效,当元素数量增加时,需要重新分配更大的内部数组,导致性能下降。


相比之下,Java提供了更好的替代方案,例如HashMap和ConcurrentHashMap。HashMap是非线程安全的,在单线程环境下性能更好,而ConcurrentHashMap则是线程安全的,并且在高并发环境下有更好的性能表现。


总结起来,由于同步导致的性能开销和其他一些限制,Hashtable在现代的Java开发中很少被推荐使用。应该优先考虑使用HashMap或ConcurrentHashMap来满足哈希表的需求。


9.1.2 HashMap:


不是同步的,支持null键和值,通常情况下,HashMap进行put或者get操作,可以达到常数时间的性能,所以是绝大部分利用键值对存取场景的首选。

如实现一个用户ID和用户信息对应的运行时存储结构


9.1.2.1详细解释:


因为HashMap内部通过哈希函数将键映射到数组中的位置,从而快速定位到对应的键值对。只有在哈希冲突发生时,才会涉及到链表或红黑树的查找操作,但其平均时间复杂度仍然是常数级别。


因为HashMap的高效性能和灵活性,它成为了绝大部分利用键值对存取场景的首选数据结构。无论是普通的单线程环境还是多线程环境下,在合适的使用方式下,HashMap都能提供高效的存取操作。


需要注意的是,HashMap不是线程安全的,如果在多线程环境下同时对HashMap进行修改操作,可能会导致数据不一致的问题。如果需要在多线程环境下使用哈希表,并保证线程安全,可以考虑使用ConcurrentHashMap或其他线程安全的数据结构。


9.1.2.2 哈希冲突:


9.1.2.2.1 红黑树和链表查询例子解析:


当哈希冲突发生时,HashMap内部会使用链表或红黑树来解决。在链表过长(达到一定阈值)时,会将链表转换为红黑树来提高查询效率。


假设我们有以下键值对需要插入到HashMap中:


{
    "apple": 1,
    "banana": 2,
    "cat": 3,
    "dog": 4,
    "elephant": 5,
    "fruit": 6
}



通过哈希函数,这些键值对被映射到了数组的同一个位置,即发生了哈希冲突。在初始情况下,该位置形成了一个链表:


-> ("apple", 1) -> ("banana", 2) -> ("cat", 3) -> ("dog", 4) -> ("elephant", 5) -> ("fruit", 6)


假设我们要获取键为"dog"的值。HashMap会先计算出"dog"的哈希值,然后根据哈希值找到相应的数组位置。因为发生了哈希冲突,HashMap会遍历链表,逐个比较键的值。


在这个例子中,HashMap会依次比较"apple"、"banana"、"cat"和"dog"。最终发现了键为"dog"的键值对,并返回其对应的值4。


然而,随着键值对的增加,链表可能会变得很长,导致查找效率下降。为了解决这个问题,当链表长度达到一定阈值时(默认为8),HashMap会将链表转换为红黑树。


例如,当链表长度达到8时,原来的链表会被转化为红黑树结构:


                 "fruit"
                   /
               "elephant"
              /         \
           "dog"       "cat"
          /              \
       "banana"       "apple"


通过使用红黑树,HashMap可以以更高效的方式进行查找操作。红黑树的平均查找时间复杂度是O(log n),相比于链表的线性查找时间复杂度O(n),能够更快地定位到目标键值对。


9.1.3 TreeMap:


是基于红黑树的一种提供顺序访问的Map,和HashMap不一样,它的get,put,remove等操作,具体属性由Comparator来决定,或者根据键的自然顺序来判断。


9.1.3.1 详细解释:


创建一个TreeMap来存储学生信息,其中键为学生的年龄,值为学生的姓名。首先,我们定义一个实现了Comparator接口的自定义比较器AgeComparator,用于确定键的顺序。


import java.util.Comparator;
import java.util.TreeMap;
class AgeComparator implements Comparator<Integer> {
    public int compare(Integer age1, Integer age2) {
        return age1.compareTo(age2);
    }
}
public class Example {
    public static void main(String[] args) {
        TreeMap<Integer, String> treeMap = new TreeMap<>(new AgeComparator());
        // 添加学生信息
        treeMap.put(20, "Alice");
        treeMap.put(18, "Bob");
        treeMap.put(22, "Charlie");
        // 获取学生信息
        System.out.println(treeMap.get(18));  // 输出: Bob
        // 删除学生信息
        treeMap.remove(20);
        // 遍历学生信息
        for (Integer age : treeMap.keySet()) {
            String name = treeMap.get(age);
            System.out.println("Age: " + age + ", Name: " + name);
        }
    }
}


上面的代码中,我们通过传入自定义的比较器AgeComparator来创建TreeMap对象。这个比较器决定了键的排序顺序,即按照学生的年龄进行排序。


我们通过put方法向TreeMap中添加学生信息,其中键为学生的年龄,值为学生的姓名。在这个例子中,我们添加了三个学生信息。


接下来,我们可以使用get方法根据键获取对应的值,例如获取年龄为18的学生的姓名,将返回"Bob"。


然后,我们使用remove方法删除年龄为20的学生的信息。


最后,我们使用keySet方法获取TreeMap中所有键的集合,并遍历输出每个键和对应的值,按照年龄的顺序(由比较器决定)输出学生信息。


通过使用TreeMap,我们可以实现对键值对的有序访问,而不是像HashMap那样无序访问。这在需要按照键的顺序进行操作的场景中非常有用,例如按照年龄范围查找学生,或者按照年龄排序输出学生信息等。


9.1.4 小结:


大部分使用 Map 的场景,通常就是放入、访问或者删除,而对顺序没有特别要求,

HashMap 在这种情况下基本是最好的选择。HashMap 的性能表现非常依赖于哈希码的有效性,掌握 hashCode 和 equals 的一些基本约定。


9.1.4.1 hashCode 和 equals 的一些基本约定:


1 . 如果两个对象通过equals方法比较相等(即具有相同的内容),那么它们的hashCode值必须相等。

  • 这条约定保证了在哈希表等依赖hashCode的数据结构中,相等的对象能够被正确地定位和访问。


2.  如果重写了equals方法,那么必须同时重写hashCode方法。

  • 这是由于Object类中的hashCode方法是基于对象的内存地址计算出来的,与内容无关。而equals方法是用于比较对象的内容是否相等。如果只重写了equals方法而不重写hashCode方法,就会违反前面提到的第一条约定,导致在使用散列集合(如HashMap、HashSet)时无法正确查找和删除对象。


3.  hashCode方法需要保持一致性。

  • 根据这条约定,如果一个对象在运行过程中的状态发生变化,但是其equals方法依然认为两个对象是相等的,那么它们的hashCode方法应该返回相同的值。这是为了保证对象在散列集合中的一致性,使得对象能够在整个生命周期内被正确地操作。


9.1.4.1.1 详细解释:
9.1.4.1.1.1 hashCode和equals的一致性约定:


class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        Person person = (Person) obj;
        return age == person.age && Objects.equals(name, person.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class Example {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Alice", 25);
        System.out.println(person1.equals(person2)); // 输出: true
        System.out.println(person1.hashCode() == person2.hashCode()); // 输出: true
        person1.setAge(30);
        System.out.println(person1.equals(person2)); // 输出: true
        System.out.println(person1.hashCode() == person2.hashCode()); // 输出: true
    }
}


9.1.4.1.1 详细解释:


9.1.4.1.1.1 hashCode和equals的一致性约定:
class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        Person person = (Person) obj;
        return age == person.age && Objects.equals(name, person.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class Example {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Alice", 25);
        System.out.println(person1.equals(person2)); // 输出: true
        System.out.println(person1.hashCode() == person2.hashCode()); // 输出: true
        person1.setAge(30);
        System.out.println(person1.equals(person2)); // 输出: true
        System.out.println(person1.hashCode() == person2.hashCode()); // 输出: true
    }
}


上面的例子中,我们自定义了Person类,并重写了equals和hashCode方法。equals方法比较了两个Person对象的name和age属性是否相等,hashCode方法根据name和age计算出哈希码。


在创建person1和person2时,它们的name和age属性都是相同的,因此equals方法会返回true,hashCode方法计算出的哈希码也会相等。


接着,我们修改了person1的age属性为30,但是equals方法仍然判断person1和person2是相等的。根据前面提到的一致性约定,即使对象状态发生变化,但equals方法认为两个对象是相等的,它们的hashCode方法返回的哈希码仍然要保持一致。


因此,在输出中我们可以看到无论是equals方法还是hashCode方法的比较结果,都符合我们对hashCode和equals的基本约定。


9.1.4.1.1.2 hashCode的重要性例子:

确保相等的对象在使用依赖于hashCode的数据结构(如哈希表、散列集合等)时,能够被正确地定位和访问。


在Java中,当我们将对象插入到使用散列的数据结构中(例如HashMap或HashSet),首先会根据对象的hashCode值来确定对象在内部数据结构中的位置。然后,如果存在多个对象具有相同的hashCode值,那么再通过equals方法进行进一步的比较以区分它们。


详细解释:


class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        Person person = (Person) obj;
        return age == person.age && Objects.equals(name, person.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class Example {
    public static void main(String[] args) {
        Person person1 = new Person("Alice", 25);
        Person person2 = new Person("Alice", 25);
        System.out.println(person1.equals(person2)); // 输出: true
        System.out.println(person1.hashCode() == person2.hashCode()); // 输出: true
        Map<Person, String> map = new HashMap<>();
        map.put(person1, "Value 1");
        System.out.println(map.get(person1)); // 输出: Value 1
        System.out.println(map.get(person2)); // 输出: Value 1
    }
}


创建了两个Person对象person1和person2,它们的name和age属性都是相同的,因此equals方法返回true。


接着,我们将person1作为键插入到HashMap中,并将对应的值设置为"Value 1"。然后,我们通过person1和person2分别作为键去获取对应的值。


根据hashCode的约定,在将person1插入HashMap时,首先会计算person1的hashCode值,并根据该值确定person1在底层数据结构中的位置。当我们使用person2作为键去获取值时,HashMap会再次计算person2的hashCode值,并找到与person2具有相同hashCode值的位置。


由于person1和person2的hashCode值相等,并且equals方法返回true,HashMap能够正确地定位到已经插入的person1,并返回"Value 1"。


说明了如果两个对象通过equals方法比较相等,那么它们的hashCode值必须相等的重要性。只有同时满足这两个条件,才能保证在依赖hashCode的数据结构中正确地定位和访问相等的对象。


9.1.4.1.1.3 map.get(person1)源码:


map.get(person1)的内部代码是HashMap类中的get方法。下面是简化的HashMap的get方法的伪代码,用于说明其大致逻辑:


public V get(Object key) {
    // 1. 计算键的哈希码
    int hashCode = key.hashCode();
    // 2. 根据哈希码找到对应的桶(数组索引)
    int index = hashCode & (table.length - 1);
    // 3. 在桶中查找匹配的键值对
    Entry<K, V> entry = table[index];
    while (entry != null) {
        if (entry.hashCode == hashCode && Objects.equals(entry.key, key)) {
            return entry.value;
        }
        entry = entry.next;
    }
    // 4. 没有找到匹配的键值对,返回null
    return null;
}


9.1.4.1.1.4 解释equals代码:


@Override
public boolean equals(Object obj) {
    // 1. 检查是否是同一个对象
    if (this == obj) {
        return true;
    }
    // 2. 检查传入的对象是否为空或者类型不匹配
    if (obj == null || getClass() != obj.getClass()) {
        return false;
    }
    // 3. 将传入的对象强制转换为Person类型,并进行属性值的比较
    Person person = (Person) obj;
    return age == person.age && Objects.equals(name, person.name);
}


  1. 首先,检查当前对象和传入的对象是否是同一个实例(即内存地址是否相同)。如果是同一个对象,则直接返回true,表示它们相等。
  2. 如果传入的对象为空(null)或者传入的对象的类型与当前对象的类型不匹配,即getClass()返回的类不相同,那么它们不可能相等,直接返回false。
  1. 将传入的对象强制转换为Person类型,并进行属性值的比较。在这里,通过==比较age属性的值是否相等,使用Objects.equals方法比较name属性的值是否相等。
  • age的比较使用==运算符比较原始数据类型int,判断它们的值是否相等。
  • name的比较使用Objects.equals方法,该方法会先检查两个参数是否为null,然后再调用name对象的equals方法进行比较。这是为了避免NullPointerException错误。


如果age和name都相等,则返回true,表示两个Person对象相等;否则返回false,表示它们不相等。


这段代码通过对两个Person对象的属性值进行比较,判断它们是否相等。这里使用了引用类型的Objects.equals方法来比较name属性,保证了在name为null时不会抛出NullPointerException异常。同时,还对传入的对象进行了类型检查,确保传入的对象与当前对象具有相同的类型。


9.1.4.1.1.5 getClass() != obj.getClass()

是用来检查传入的对象 obj 是否和当前对象具有相同的类型。

具体解释如下:

  • getClass() 方法是 Java 中的一个非常基础的方法,它返回当前对象所属的类的运行时类型。
  • obj.getClass() 则是获取对象 obj 所属的类的运行时类型。
  • getClass() != obj.getClass() 用于比较当前对象和传入的对象 obj 的类型是否相同。如果类型不相同,则返回 false 表示两个对象的类型不匹配,也即它们不可能相等。


这里的目的是避免出现类型不匹配的情况。当两个对象具有不同的类型时,它们在属性以及行为上可能会存在差异,因此无法简单地进行比较。所以,在执行进一步的属性值比较之前,首先要确保传入的对象 obj 和当前对象具有相同的类型,才能保证后续的属性值比较是有意义且准确的。


总结来说,getClass() != obj.getClass() 的作用是检查当前对象和传入的对象是否具有相同的运行时类型,以确保后续的属性值比较是有效的。


    Person person = (Person) obj;
    return age == person.age && Objects.equals(name, person.name);


9.1.4.1.1.6 通过==比较age属性的值是否相等,使用Objects.equals方法比较name属性的值是否相等。 这里为什么比较方法不一样?


在代码中通过 == 比较 age 属性的值是否相等,使用 Objects.equals 方法比较 name 属性的值是否相等,是因为 age 和 name 是不同的数据类型。


  • age 是一个基本数据类型(例如 int、double 等),使用 == 运算符可以直接比较它们的值是否相等。因为基本数据类型的值是直接存储在变量中的,所以它们可以通过比较数值来判断是否相等。


  • name 是一个引用类型(例如字符串 String),它是一个对象,在内存中存储的是对象的引用地址。因此,我们不能简单地使用 == 运算符来比较两个字符串对象的内容是否相等,因为这只会比较对象的引用地址是否相同。而我们通常更关心的是字符串的内容是否相等。


为了比较引用类型的属性值是否相等,我们需要使用 equals 方法进行内容比较。Objects.equals 是 Java 提供的工具类 java.util.Objects 中的方法,用于比较两个对象的内容是否相等。它会先检查两个参数是否为 null,然后再调用对象的 equals 方法进行比较。在 String 类中,equals 方法已经被重写,用于比较字符串的内容是否相等。


9.1.4.2 针对有序 Map :


虽然 LinkedHashMap 和 TreeMap 都可以保证某种顺序,但二者还是非常不同的。


9.1.4.2.1  LinkedHashMap 示例:

假设我们有一个 LinkedHashMap 存储了学生的姓名和对应的成绩,按照插入顺序进行迭代时,可以保证元素的顺序与插入的顺序一致。


import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapExample {
    public static void main(String[] args) {
        // 创建一个按插入顺序排序的 LinkedHashMap
        Map<String, Integer> studentGrades = new LinkedHashMap<>();
        // 添加学生姓名和对应成绩
        studentGrades.put("Alice", 90);
        studentGrades.put("Bob", 85);
        studentGrades.put("Charlie", 80);
        // 迭代输出学生姓名和对应成绩
        for (Map.Entry<String, Integer> entry : studentGrades.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}


输出:

1. Alice: 90
2. Bob: 85
3. Charlie: 80


9.1.4.2.2 TreeMap 示例:


TreeMap 是基于红黑树的实现,它可以保持键的自然顺序或者指定的比较器顺序。下面的示例展示了如何使用自然顺序来排序存储的整数:


import java.util.Map;
import java.util.TreeMap;
public class TreeMapExample {
    public static void main(String[] args) {
        // 创建一个按键的自然顺序排序的 TreeMap
        Map<Integer, String> numbers = new TreeMap<>();
        // 添加键值对
        numbers.put(3, "Three");
        numbers.put(1, "One");
        numbers.put(2, "Two");
        // 迭代输出键值对
        for (Map.Entry<Integer, String> entry : numbers.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}


输出:

1. 1: One
2. 2: Two
3. 3: Three


TreeMap 会根据键的自然顺序(数字的大小)进行排序。而对于 LinkedHashMap 来说,它保持了元素的插入顺序。


9.1.4.2.3 小结:


LinkedHashMap 和 TreeMap 的不同点主要体现在以下几个方面:


  1. 内部实现:
  • LinkedHashMap 使用哈希表和双向链表的结合来维护元素的插入顺序。它使用哈希表来支持快速的查找操作,并使用双向链表来保持元素的插入顺序。
  • TreeMap 基于红黑树实现,它通过对键进行排序来维护元素的顺序。
  1. 排序方式:
  • LinkedHashMap 保持元素的插入顺序,当按照插入顺序迭代时,元素会按照被插入的顺序输出。
  • TreeMap 可以根据键的自然顺序或者自定义比较器来进行排序。当按照键的顺序迭代时,元素会按照键的排序顺序输出。

性能:

  • LinkedHashMap 在常见操作(插入、删除、查找)的时间复杂度为 O(1),因为它使用了哈希表进行快速查找。但是它相对于 HashMap 稍微增加了一些额外的开销来维护双向链表。
  • TreeMap 在常见操作的时间复杂度为 O(logN),因为它基于红黑树实现,红黑树的平衡性保证了较高的性能。
  1. 适用场景:
  • LinkedHashMap 适用于需要保持元素插入顺序的场景,尤其是在需要实现 LRU(最近最少使用)缓存淘汰算法时。
  • TreeMap 适用于需要按照键的顺序进行遍历或查找的场景,尤其是在需要范围查询的情况下。


总结起来,LinkedHashMap 适用于维护插入顺序的需求,而 TreeMap 则适用于基于排序的需求。它们的内部实现和性能特点不同,因此在选择使用时需要根据具体的场景和需求进行选择。

相关文章
|
6天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
31 7
|
24天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
64 2
|
12天前
|
Java 程序员
Java社招面试题:& 和 && 的区别,HR的套路险些让我翻车!
小米,29岁程序员,分享了一次面试经历,详细解析了Java中&和&&的区别及应用场景,展示了扎实的基础知识和良好的应变能力,最终成功获得Offer。
36 14
|
23天前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
29天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
6天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
17天前
|
Java 编译器 程序员
Java面试高频题:用最优解法算出2乘以8!
本文探讨了面试中一个看似简单的数学问题——如何高效计算2×8。从直接使用乘法、位运算优化、编译器优化、加法实现到大整数场景下的处理,全面解析了不同方法的原理和适用场景,帮助读者深入理解计算效率优化的重要性。
25 6
|
23天前
|
监控 前端开发 Java
【技术开发】接口管理平台要用什么技术栈?推荐:Java+Vue3+Docker+MySQL
该文档介绍了基于Java后端和Vue3前端构建的管理系统的技术栈及功能模块,涵盖管理后台的访问、登录、首页概览、API接口管理、接口权限设置、接口监控、计费管理、账号管理、应用管理、数据库配置、站点配置及管理员个人设置等内容,并提供了访问地址及操作指南。
|
24天前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
70 2
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。