首页 > 非科班如何在校招中突出重围-面试准备篇《Java基础篇》
头像
大黄奔跑
编辑于 2021-02-23 21:43
+ 关注

非科班如何在校招中突出重围-面试准备篇《Java基础篇》

写在之前

Hello,大家好,好久没有更新文章了,当了很久的咸鱼~

上一篇和大家分享了当初校招时如何艰难的寻找实习,总体写得比较简单,今天给大家分享一下当时准备面试时的一些面试题目,之前秋招过程中得到了多位优秀大佬的指导,也看了很多经典的书籍(比如《Java编程思想》、《深入理解Java虚拟机》、《Java并发编程艺术》等),总结了一些知识点,分享出来与大家共享,同时自己和温习一些知识点。

这篇文章主题主要是《Java基础相关的面试题目》,讲解的结构主要是题目 + 答案展开,大家可以先看题目,再看答案,不建议直接背诵答案。

核心问题十二问

1. String,StringBuilder 以及 StringBuffer三者区别

关于这三个字符串类的异同之处主要关注可变不可变、安全不安全两个方面:

  1. StringBuffer(同步的)和String(不可变的)都是线程安全的,StringBuilder是线程不安全的;
  2. String是不可变的,StringBuilderStringBuffer是可变的;
  3. String的连接操作的底层是由StringBuilder实现的;
  4. 三者都是final的,不允许被继承;
  5. StringBuilder 以及 StringBuffer 都是抽象类AbstractStringBuilder的子类,它们的接口是相同的。

为什么String是不可变的?

  1. String主要的三个成员变量char value[], int offset, int count均是private,final的,并且没有对应的 getter/setter;
  2. String 对象一旦初始化完成,上述三个成员变量就不可修改;并且其所提供的接口任何对这些域的修改都将返回一个新对象;

2. 重载,重写

  1. 重载:类内多态,静态绑定机制(编译时已经知道具体执行哪个方法),方法同名,参数不同
  2. 重写:类间多态,动态绑定机制(运行时确定),实例方法,遵循两小两同一大原则。
    方法签名相同,子类的方法所抛出的异常、返回值的范围不大于父类的对应方法,子类的方法可见性不小于父类的对应方法

3. 抽象,封装,继承,多态

Java 的四大特性总结如下:

  1. 封装:把对象的属性和行为(数据)封装为一个独立的整体,并尽可能隐藏对象的内部实现细节;
  2. 继承:一种代码重用机制;
  3. 多态:分离了做什么和怎么做,从另一个角度将接口和实现分离开来,消除类型之间的耦合关系;表现形式:重载与重写;关于多态,大黄之前拙笔一篇《通俗聊聊什么是多态》
  4. 抽象:对继承的另一种表述;表现形式:接口(契约)与抽象类(模板)

4. ArrayList(动态数组)、LinkedList(带头结点的双向链表)两者区别

1、ArrayList主要特性如下

  1. 默认初始容量为 10
  2. 扩容机制:添加元素前,先检查是否需要扩容,一般扩为源数组的 1.5 倍 + 1
  3. 边界检查(即检查 ArrayListSize):涉及到 index 的操作;
  4. 调整数组容量(减少容量):将底层数组的容量调整为当前列表保存的实际元素的大小;
  5. 在查找给定元素索引值等的方法中,源码都将该元素的值分为null和不为null两种情况处理;

关于ArrayList可以参考之前大黄拙笔一篇《Java面试必考题-ArrayList常见知识点》

2、LinkedList核心点
LinkedList 不但实现了List接口,还实现了Dequeue接口。因此,LinkedList不但可以当做List来用,还可以当做Stack(push、pop、peek),Queue(offer、poll、peek)来使用。

3、ArrayListLinkedList两者比较

  1. ArrayList是基于数组的实现,LinkedList是基于带头结点的双向循环链表的实现;
  2. ArrayList支持随机访问,LinkedList不支持;
  3. LinkedList可作队列和栈使用,实现了Dequeue接口,而ArrayList没有;
  4. ArrayList寻址效率较高,插入/删除效率较低;LinkedList插入/删除效率较高,寻址效率较低

5. 容器的Fail-Fast机制

Fail-Fast 是 Java 集合的一种错误检测机制,为了防止在某个线程在对Collection进行迭代时,其他线程对该Collection进行结构上的修改。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险,抛出 ConcurrentModificationException 异常。

private class Itr implements Iterator<E> {  
    int cursor;  
    int lastRet = -1;  
    int expectedModCount = ArrayList.this.modCount;  

    public boolean hasNext() {  
        return (this.cursor != ArrayList.this.size);  
    }  

    public E next() {  
        checkForComodification();  
        /** 省略此处代码 */  
    }  

    public void remove() {  
        if (this.lastRet < 0)  
            throw new IllegalStateException();  
        checkForComodification();  
        /** 省略此处代码 */  
    }  

    final void checkForComodification() {  
        if (ArrayList.this.modCount == this.expectedModCount)  
            return;  
        throw new ConcurrentModificationException();  
    }  
}  

6. Set (HashSet,LinkedHashSet,TreeSet)各个实现类区别

Set不包含重复的元素,这是Set最大的特点,也是使用Set最主要的原因。
常用到的Set实现有 HashSetLinkedHashSetTreeSet。一般地,如果需要一个访问快速的Set,你应该使用HashSet;当你需要一个排序的Set,你应该使用TreeSet;当你需要记录下插入时的顺序时,你应该使用LinedHashSet

Set结构

1、HashSet:底层通过HashMap进行实现,实现了Set接口。

HashSet是采用hash表来实现的,其中的元素没有按顺序排列,add()remove()以及contains()等方法都是复杂度为O(1)的方法。
通过观察底层代码发现,HashSet底层基本上都是通过HashMap实现

五个构造方法 = 4(基于HashMap的实现,用于实现HashSet) + 1(基于LinkedHashMap的实现,用于实现LinkedHashSet)

2、LinkedHashSet:是HashSet的子类,底层主要由HashMap的子类LinkedHashMap进行实现,实现了Set接口

LinkedHashSet继承于HashSet,利用下面的HashSet构造函数即可,注意到,其为包访问权限,专门供LinkedHashSet的构造函数调用。LinkedHashSet性能介于HashSetTreeSet之间,是HashSet的子类,也是一个hash表,但是同时维护了一个双链表来记录插入的顺序,基本方法的复杂度为O(1)

3、TreeSet:底层主要由TreeMap进行实现。

TreeSet是采用树结构实现(红黑树算法),元素是按顺序进行排列,但是add()remove()以及contains()等方法都是复杂度为O(log (n))的方法,它还提供了一些方法来处理排序的set,如first()、 last()、 headSet()tailSet()等。此外,TreeSet不同于HashSetLinkedHashSet,其所存储的元素必须是可排序的(元素实现Comparable接口或者传入Comparator),并且不能存放null值。

7. Map及Map的三种常用实现

链表数组HashMapLinkedHashMap(HashMap的子类,带头结点的双向链表 + HashMap),基于红黑树的TreeMap(对key排序)]

关于HashMap极度推荐,美团技术团队的——Java 8系列之重新认识HashMap

8. 如何判断容器中两个对象是否相等

  1. 判断两个对象的hashCode是否相等:如果不相等,认为两个对象也不相等;如果相等,转入第二步;

  2. 判断两个对象用equals运算是否相等:如果不相等,认为两个对象也不相等;如果相等,认为两个对象相等

9. ConcurrentHashMap,HashMap 与 HashTable区别

  1. 本质:三者都实现了Map接口,ConcurrentHashMapHashMapAbstractMap的子类,HashTableDictionary的子类;

  2. 线程安全性:HashMap 是线程不安全的,但 ConcurrentHashMapHashTable是线程安全的,但二者保证线程安全的策略不同;前者采用的是分段锁机制,默认理想情况下,可支持16个线程的并发写和任意线程的并发读,效率较高;HashTable 采用的是同步操作,效率较低

  3. 键值约束:HashMap 允许键、值为null,但 ConcurrentHashMapHashTable既不允许键为null,也不允许值为null;

  4. 哈希策略:三者哈希策略不同,HashTablekey.hashCode取余;ConcurrentHashMapHashMap都是先对hashCode进行再哈希,然后再与(桶数 - 1)进行取余运算,但是二者的再哈希算法不同;

  5. 扩容机制:扩容检查机制不同,ConcurrentHashMapHashTable 在插入元素前检查,HashMap 在元素插入后检查;

  6. 初始容量:HashTable 初始容量 11,扩容 2倍 + 1;HashMap初始容量16,扩容2倍

10. equals, hashCode, ==区别

大黄在之前的文章中已经详细的阐述了三者之间的区别,可以参见《》

1、总结来看有以下几点:

  1. == 用于判断两个对象是否为同一个对象或者两基本类型的值是否相等;
  2. equals 用于判断两个对象内容是否相同;
  3. hashCode 是一个对象的 消息摘要函数,一种 压缩映射,其一般与equals()方法同时重写;若不重写hashCode方法,默认使用Object类的hashCode方法,该方法是一个本地方法,由 Object 类定义的 hashCode 方***针对不同的对象返回不同的整数。

2、集合中使用可变对象作为Key带来的问题

HashMapKey哈希值来存储和查找键值对,如果HashMap Key的哈希值在存储键值对后发生改变,那么Map可能再也查找不到这个Entry了。也就是说,在HashMap中可变对象作为Key会造成 数据丢失
因此一般有如下建议:

  1. HashMap中尽量使用不可变对象作为Key,比如,使用String等不可变类型用作Key是非常明智的或者使用自己定义的不可变类。
  2. 如果可变对象在HashMap中被用作键,那就要小心在改变对象状态的时候,不要改变它的哈希值了,例如,可以只根据对象的标识属性生成HashCode

3、重写equals但不重写HashCode会出现的问题

在使用Set时,若向其加入两个相同(equals返回为true)的对象,由于hashCode函数没有进行重写,那么这两个对象的hashCode值必然不同,它们很有可能被分散到不同的桶中,容易造成重复对象的存在。

11. 什么是不可变对象

一个不可变对象应该满足以下几个条件:

  1. 基本类型变量的值不可变;
  2. 引用类型变量不能指向其他对象;
  3. 引用类型所指向的对象的状态不可变;
  4. 除了构造函数之外,不应该有其它任何函数(至少是任何public函数)修改任何成员变量;
  5. 任何使成员变量获得新值的函数都应该将新的值保存在新的对象中,而保持原来的对象不被修改。

12. Java的序列化/反序列化机制

1、使用Serializable序列化/反序列化
将实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象,序列化可以弥补不同操作系统之间的差异。

序列化具体的含义:

  1. 需要序列化的对象必须实现Serializable接口;
  2. 只有非静态字段和非transient字段进行序列化,与字段的可见性无关;

总结

《Offer快到碗里来》面试准备篇——Java基础篇01到这里结束了,这里提到的十二个知识点只是Java知识点中的一小撮,也是从之前准备秋招中挑选出的,尽量避免想与世面的面试题合集重合,后续有什么问题或者有什么想要了解的也可以私信我,我也会陆陆续续的完善Offer快到碗里来系列。

全部评论

(0) 回帖
加载中...
话题 回帖

推荐话题

相关热帖

近期精华帖

热门推荐