Java知识之基础
Java 基础面试准备
救急准备
下面这些问题都是面试互联网大厂后端岗位时的常问问题,我希望你都能熟练的答出来呀!暂时没记牢也不用太担心,我建议你在投简历和约面时,把最想去的公司的面试稍微往后推一推,先面几家自己拿不到 offer 也不会难过的公司。上面这些八股文就是通过我 平时+面试 记熟的,去年 7月 初第一次我面一家中小厂,这些都磕磕绊绊的答不出来,但是到后期 8月底 9月初完全能轻松应对各大厂面试官了。
另外,我通过打星与加粗的方式对下面面试题的重要性进行评级!难度是针对互联网大厂的。
- ⭐ :面试中不常问到,如果面试官问到尽量能答出来,答不出来也没关系。
- ⭐⭐ :面试中不常问到,但是如果面试官问到的话,答不出来对你的印象会减分。
- ⭐⭐⭐:面试中会问到,答不出来面试有点悬。面试官会惊讶为什么你这也不会。
- ⭐⭐⭐⭐:面试高频考点。
- ⭐⭐⭐⭐⭐:面试超高频考点。四星考点和五星考点是参加十场面试,至少能有五场面试问到这些的。大家在准备面试过程中尽量把这些知识点的回答条理梳理清楚,面试官一问就开背。
Java 基础常见面试题汇总 :
Java 语言的特点(如果你简历上有提到 C++ 可能还会问你 Java 和 C++ 的区别)。【⭐⭐】
简单易学;
面向对象(封装,继承,多态);
平台无关性、跨平台( JVM实现平台无关性);
支持多线程
可靠性;
安全性;
支持网络编程并且很方便;
编译与解释并存;比较 JVM 和 JDK 以及 JRE 。【⭐⭐⭐】
Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。实现Java 语言“一次编译,随处可以运行”的关键所在。
JDK 是 Java Development Kit 缩写,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。
JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。为什么说 Java 语言“解释与编译并存”。【⭐⭐】
这是因为 Java 语言既具有编译型语言的特征,也具有解释型语言的特征。因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(.class 文件),然后通过Java 解释器来解释字节码.class文件来执行。
我们需要格外注意的是 .class->机器码 这一步。在这一步 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT(just-in-time compilation) 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言 。Java 基本类型有哪几种,各占多少位?【⭐⭐】
整数:short-2字节16位、int-4字节32位、long-8字节64位、byte-1字节8位
小数:float-4字节32位、double-8字节64位
字符:char-2字节16位
布尔:boolean-1位Java 泛型,类型擦除。【⭐⭐⭐】
JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
Java 的泛型是伪泛型,这是因为 Java 在运行期间,所有的泛型信息都会被擦掉,通过反射机制是可以使用其他非法类型的,这也就是通常所说类型擦除 。
常用的通配符为: T,E,K,V,?==和equals()的区别。【⭐⭐⭐】:这个问题在 2018 年之前几乎是面试必问的问题,但是现在大厂以及比较少问了,现在小厂中厂问的多。
== 对于基本类型和引用类型的作用效果是不同的:
对于基本数据类型来说,== 比较的是值。
对于引用数据类型来说,== 比较的是对象的内存地址。
因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。
equals() 不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,因此所有的类都有equals()方法。1
2
3public boolean equals(Object obj) {
return (this == obj);
}类没有重写 equals()方法 :通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Object类equals()方法。
类重写了 equals()方法 :一般我们都重写 equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。hashCode()和equals()【⭐⭐⭐⭐】:这个问题经常问,面试官经常问为什么重写equals()时要重写hashCode()方法?另外,这个问题经常结合着HashSet问。
hashCode() 的作用是获取哈希码(int 整数),也称为散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。
hashCode()定义在 JDK 的 Object 类中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode?
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashCode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashCode 值作比较,如果没有相符的 hashCode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashCode 值的对象,这时会调用 equals() 方法来检查 hashCode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
其实, hashCode() 和 equals()都是用于比较两个对象是否相等。
那为什么 JDK 还要同时提供这两个方法呢?
这是因为在一些容器(比如 HashMap、HashSet)中,有了 hashCode() 之后,判断元素是否在对应容器中的效率会更高(参考添加元素进HastSet的过程)!
我们在前面也提到了添加元素进HastSet的过程,如果 HashSet 在对比的时候,同样的 hashCode 有多个对象,它会继续使用 equals() 来判断是否真的相同。也就是说 hashCode 帮助我们大大缩小了查找成本。
那为什么不只提供 hashCode() 方法呢?
这是因为两个对象的hashCode 值相等并不代表两个对象就相等。
因为 hashCode() 所使用的哈希算法也许刚好会让多个对象传回相同的哈希值。越糟糕的哈希算法越容易碰撞,但这也与数据值域分布的特性有关(所谓哈希碰撞也就是指的是不同的对象得到相同的 hashCode )。
总结下来就是 :
如果两个对象的hashCode 值相等,那这两个对象不一定相等(哈希碰撞)。
如果两个对象的hashCode 值相等并且equals()方法也返回 true,我们才认为这两个对象相等。
如果两个对象的hashCode 值不相等,我们就可以直接认为这两个对象不相等。
相信大家看了我前面对 hashCode() 和 equals() 的介绍之后,下面这个问题已经难不倒你们了。
因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。
如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。
思考 :重写 equals() 时没有重写 hashCode() 方法的话,使用 HashMap 可能会出现什么问题。
总结 :equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。两个对象有相同的 hashCode 值,他们也不一定是相等的(哈希碰撞)。重载和重写的区别。 【⭐⭐⭐⭐】
重载就是同样的一个方法能够根据输入数据的不同,做出不同的处理。
重写就是当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法。
重载:发生在同一个类中(或者父类和子类之间),方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。
重写:发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写。
方法名、参数列表必须相同,子类方法返回值类型应比父类方法返回值类型更小或相等,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。
如果父类方法访问修饰符为 private/final/static 则子类就不能重写该方法,但是被 static 修饰的方法能够被再次声明。
构造方法无法被重写
综上:重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变。
区别点 重载方法 重写方法
发生范围 同一个类 子类
参数列表 必须修改 一定不能修改
返回类型 可修改 子类方法返回值类型应比父类方法返回值类型更小或相等
异常 可修改 子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
访问修饰符 可修改 一定不能做更严格的限制(可以降低限制)
发生阶段 编译期 运行期深拷贝和浅拷贝。【⭐】
个人理解:
深拷贝:拷贝值;
浅拷贝:拷贝引用;面向对象和面向过程的区别。【⭐⭐⭐】
两者的主要区别在于解决问题的方式不同:
个人理解:线性处理问题和非线性处理问题。
面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。
面向对象会先抽象出对象,然后用对象执行方法的方式解决问题。
另外,面向对象开发的程序一般更易维护、易复用、易扩展。成员变量与局部变量的区别。【⭐⭐⭐】
语法形式 :从语法形式上看,成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
存储方式 :从变量在内存中的存储方式来看,如果成员变量是使用 static 修饰的,那么这个成员变量是属于类的,如果没有使用 static 修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。
生存时间 :从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动生成,随着方法的调用结束而消亡。
默认值 :从变量是否有默认值来看,成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值(一种情况例外:被 final 修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。面向对象三大特性是什么。并解释这三大特性。【⭐⭐⭐⭐】
封装:封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。
继承:
子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法。(以后介绍)。
多态:顾名思义,表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。
对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
多态不能调用“只在子类存在但在父类不存在”的方法;
如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法。String、StringBuffer和StringBuilder的区别。 【⭐⭐⭐⭐】
可变性:不可变、可变、可变。
安全性:常量,安全、安全、不安全
性能:每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
对于三者使用的总结:
操作少量的数据: 适用 String
单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
多线程操作字符串缓冲区下操作大量数据: 适用 StringBufferJava 异常。【⭐⭐⭐】:不会问的特别细。经常的问法是异常可以分为哪几种,然后你答了可检查异常和不可检查异常以后,会让你举例可检查异常有哪些,不可检查有哪些。然后,异常的代码要会写,有一场字节的面试,直接让我写一个把异常捕获了然后抛出去的代码。
Throwable分支:Exception(程序本身可处理)和Error(程序本身不可处理)
受检查异常和不受检查异常。
除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常 。
RuntimeException 及其子类都统称为非受检查异常,例如:NullPointerException、NumberFormatException(字符串转换为数字)、ArrayIndexOutOfBoundsException(数组越界)、ClassCastException(类型转换错误)、ArithmeticException(算术错误)等。
常见的受检查异常有: IO 相关的异常、ClassNotFoundException 、SQLException…。序列化和反序列化【⭐⭐】
如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。
简单来说:
序列化: 将数据结构或对象转换成二进制字节流的过程
反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
对于不想进行序列化的变量,使用 transient 关键字修饰。反射【⭐⭐】面试官可能会问你什么是反射,它的优缺点是什么,有哪些应用场景。
反射之所以被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。
反射机制优缺点
优点 : 可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利
缺点 :让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)
场景:各种框架、注解、MySQL的连接等。List、Set、Map的区别。【⭐⭐】
List(对付顺序的好帮手): 存储的元素是有序的、可重复的。
Set(注重独一无二的性质): 存储的元素是无序的、不可重复的。
Queue(实现排队功能的叫号机): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。
Map(用 key 来搜索的专家): 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),”x” 代表 key,”y” 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。ArrayList和LinkedList的区别。【⭐⭐⭐⭐】:答清楚每个分别采用什么数据结构,对比相应的优点和缺点。
是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
底层数据结构: Arraylist 底层使用的是 Object 数组;LinkedList 底层使用的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
插入和删除是否受元素位置的影响:
ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
LinkedList 采用链表存储,所以,如果是在头尾插入或者删除元素不受元素位置的影响(add(E e)、addFirst(E e)、addLast(E e)、removeFirst() 、 removeLast()),近似 O(1),如果是要在指定位置 i 插入和删除元素的话(add(int index, E element),remove(Object o)) 时间复杂度近似为 O(n) ,因为需要先移动到指定位置再插入。
是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
内存空间占用: ArrayList 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。比较
HashSet、LinkedHashSet和TreeSet三者的异同。【⭐⭐⭐】
HashSet、LinkedHashSet 和 TreeSet 都是 Set 接口的实现类,都能保证元素唯一,并且都不是线程安全的。
HashSet、LinkedHashSet 和 TreeSet 的主要区别在于底层数据结构不同。HashSet 的底层数据结构是哈希表(基于 HashMap 实现)。LinkedHashSet 的底层数据结构是链表和哈希表,元素的插入和取出顺序满足 FIFO。TreeSet 底层数据结构是红黑树,元素是有序的,排序的方式有自然排序和定制排序。
底层数据结构不同又导致这三者的应用场景不同。
HashSet 用于不需要保证元素插入和取出顺序的场景
LinkedHashSet 用于保证元素的插入和取出顺序满足 FIFO 的场景
TreeSet 用于支持对元素自定义排序规则的场景。HashMap 多线程操作导致死循环问题。【⭐⭐⭐】jdk 1.8 后解决了这个问题,但是还是不建议在多线程下使用
HashMap,因为多线程下使用HashMap还是会存在其他问题比如数据丢失。并发环境下推荐使用ConcurrentHashMap。HashMap 的长度为什么是 2 的幂次方。【⭐⭐⭐】主要是考虑到了对运算效率的提升。
为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648 到 2147483647,前后加起来大概 40 亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个 40 亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ (n - 1) & hash”。(n 代表数组长度)。这也就解释了 HashMap 的长度为什么是 2 的幂次方。
这个算法应该如何设计呢?
我们首先可能会想到采用%取余的操作来实现。但是,重点来了:“取余(%)操作中如果除数是 2 的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是 2 的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是 2 的幂次方。HashMap、HashTable、以及ConcurrentHashMap的区别。【⭐⭐⭐⭐⭐】:现在面试的超高频考点。当面试官问到这个问题的时候,展现你背面试八股文能力的机会来了。你可以展开去讲在 Java7 和 Java8 中HashMap分别采用什么数据结构,为什么 Java8 把之前的头插法改成了尾插法,怎样实现扩容,为什么负载因子是0.75,为什么要用红黑树等等一系列的东西。只要面试官不打断我,我在这个知识点上能背到面试官下班。
ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。
底层数据结构: JDK1.7 的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构跟 HashMap1.8 的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;
实现线程安全的方式(重要): ① 在 JDK1.7 的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 到了 JDK1.8 的时候已经摒弃了 Segment 的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6 以后 对 synchronized 锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在 JDK1.8 中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。Map
HashMap: JDK1.8 之前 HashMap 由数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间
LinkedHashMap: LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:《LinkedHashMap 源码详细分析(JDK1.8)》
Hashtable: 数组+链表组成的,数组是 Hashtable 的主体,链表则是主要为了解决哈希冲突而存在的
TreeMap: 红黑树(自平衡的排序二叉树)
系统学习
推荐大家跟着《Java 编程的逻辑》来学习 Java 基础知识。这个阶段 Java 基础弱的同学应该好好把握住。趁还有时间,边看书边把书中的实例跟着敲一敲,把书中的技术都用一用,体会一下 Java 的编程思想。
这本书真正做到了深入浅出的讲解 Java 知识,内容有深度,但描述通俗易懂。极力推荐。

学习内容按照《Java 编程逻辑》的目录进行介绍:
- 第 1 章:编程基础 : 这一章的学习内容有,数据类型和变量、赋值、基本运算、条件执行、循环、函数用法、函数调用基本原理。这些都是 Java 最基础的,肯定要会。
- 第 2 章:理解数据背后的二进制 : 主要讲了二进制、字符的编码与乱码等计算机领域必备的基础知识。
- 第 3 章->第 5 章 类 : 这里是面试的高频考点。封装、继承、多态三大特性,重载、重写,哪些可以被继承、哪些不可以被继承等等经常会被面试官问到。所以这三章要认真的学习。多体会面向对象的编程思想,也能帮助大家写出高质量的代码。
- 第 6 章 异常 :这块不用学的太细,但是基础的把异常捕获抛出是要学会的,写代码也经常要用。对常见异常类型要掌握。
- 第 7 章 常用基础类 :
String相关的好好看一下,不止对String的概念要熟,对String的操作也要熟。虽然面试的手撕代码过程中大部分面试官是允许你查 API 的,但是涉及到String的操作还是尽量别查了,面试官可能会觉得你平常的代码量太少了。其它的常用基础类基本了解就好。 - 第 9 章->第 12 章 集合部分 :集合部分是 Java 后端面试的一个超高频考点。
ArrayList、LinkedList、HashMap、队列的操作要熟练,写代码题时经常要用到这几个容器。另外对上述几个集合的实现原理,所采用的数据结构要了解清楚,也要体会一下这些集合设计的好在哪里。 - 第 15 章->第 20 章 并发部分 :并发部分是各个大厂面试的超高频考点。常问的考点有线程的基本概念、
Synchronized、线程的协作机制、CAS、ReentrantLock、ConcurrentHashMap、线程池。要充分理解上述知识点的实现思想,并且记牢这些知识点的工作流程。另外最好能看一些上述考点在实际场景中的应用。如果能把这些技术恰当的应用在你的项目里,并解决了实际问题,面试官会比较惊喜的。并发这部分内容会在后面单独抽一篇文章详细介绍。 - 第 21 章 -> 第 24 章 :反射、注解、动态代理、类加载机制这快如果有时间的话学一下,然后结合着
Spring的实现原理进行回答,可以当作你面试的亮点。
如果你比较喜欢看视频的话,推荐你看一下尚硅谷的《Java 基础教程系列》。
这应该是我看过最全的 Java 基础课程之一了,内容不光涵盖 Java 方方面面,还涉及到数据结构、设计模式、JVM 内存结构等知识。
如果你的 Java 基础没有打好的话,也可以选择对应的章节进行学习。
看看这个点赞、投币、收藏、转发量是多么恐怖!
尚硅谷算得上是比价良心的培训机构了,免费开源了很多免费且高质量的教学视频,帮助了很多小伙伴学习编程。
公众号“CS 指南”后台回复“尚硅谷”可以领取尚硅谷这个系列完整的视频教程。
