归纳总结一些面试中常问的知识点~
目录
- 知识点提纲
- (一)Java基础
- (1)抽象类和接口的成员变量有什么差别
- (2)抽象类和接口的区别
- (3)StringBuffer\String\StringBuilder三者区别
- 相同点
- 不同点
- 其他
- (4)mysql 的 int 数据类型都有哪些?从小到大说一下,各占多少字节【字节跳动一面】
- (5)集合与数组的区别
- (6) 集合类(Collection、Map)底层实现
- List
- HashMap
- HashSet
- TreeMap
- (7) 集合类其他知识点
- (二)多线程并发
- (1)进程/线程回顾
- (三)JVM
- (四)设计模式
- *(五)JavaEE基础+框架
- (1)get和post区别
- (2)session和cookie区别
- (3)bean生命周期
- (六)计算机网络
- (1)TCP三次握手四次挥手
- (七)数据结构+算法
- (八)数据库
- (1)索引底层实现
- (2)行锁表锁
- (3)MVCC
- (4)事务隔离级别
- (5)SQL查询优化
- (九)操作系统
- *(十)分布式、消息队列、其他框架
- 一、Dubbo
- (十一)场景/架构设计
知识点提纲
- Java基础、集合基础(HashMap等源码)
- JavaEE(基础,Servlet,Tomcat等)
- 多线程并发
- JVM
- 设计模式
- 数据结构
- 算法:剑指offer、LeetCode400+(包括Top100、面试Top145…)
- 计算机网络
- 操作系统原理
- Linux
- 数据库(原理,MySql,redis)
- 大型网站技术架构
- *框架(Spring,SpringMVC,Nginx)
- *分布式(Zookeeper,Dubbo,Hadoop)
- *网络编程(Socket,NIO,Netty)
- *消息队列(Kafka)
(一)Java基础
博客(Github\CSDN等)+ 书(Java编程思想+JAVA核心技术卷1)
不建议对着书一点点看,记不住且浪费时间
重点
重点 | 复习指南 |
面向对象特性、数据类型、继承、重载、重写、反射、泛型、注解、异常… | 最好有个Java整体的知识体系结构 ,按照体系整理,复习时注意看一些JDK源码,包括 HashMap,ArrayList等容器源码,还有JDK concurrent包的一些源码,面试高频 |
(1)抽象类和接口的成员变量有什么差别
抽象类
public abstract class Person01 {
public int age = 10;
public String name;
/**
* 得到年龄
* @param age
*/
public abstract void getAge(int age);
}
接口
UserService .class是一个接口,
public interface Person {
// 默认修饰符是public static final 相当于常量,必须要初始化
int age = 20;
/**
* 得到年龄
* @param age
*/
void getAge(int age);
}
抽象类的成员变量可以是变量,也可以是常量,不能用abstract
修饰;接口的成员变量必须是常量
,默认修饰符是public static final
(2)抽象类和接口的区别
- 抽象类(abstract class)中的方法可以有实现,接口(interface)中的方法不能有实现;抽象类中的抽象方法必须被派生类重写,其他的普通方法可以不被重写,而接口中的所有方法,都必须被实现类重写;(补充:抽象类可以有抽象方法也可以有普通方法,有抽象方法的类必须被声明为抽象类)
- 抽象类(abstract class)中还可以有变量、常量、静态方法或构造方法,他们的修饰符不一定要是public,默认是protected;而接口(interface)默认修饰符就是public(因此,这个public不需要手动加上),且接口中的成员变量只能是静态常量,没有静态方法和构造方法
- 子类只能继承(extends)一个抽象类,却能实现(implements)多个接口
(3)StringBuffer\String\StringBuilder三者区别
相同点
底层都是使用char[]
存储
不同点
String :不可变
的字符序列
StringBuffer:可变
的字符序列,线程安全
的,效率低
。
- 初始化时底层创建了一个
长度为16
的数组 - 扩容问题:扩容为原来的2倍+2,同时将原有数组中的元素复制到新的数组中
StringBuilder:可变
的字符序列,线程不安全
的,效率高
建议开发中使用StringBuilder/StringBuffer;他们的效率 ——> StringBuilder > StringBuffer > String
【补充String的不可变性】
- 对字符串重新赋值,需要
重新指定内存区域
赋值,不能使用原有的value进行赋值 - 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
- 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
其他
-
String s = new String("abc")
方式创建对象,在内存中创建了几个对象?
创建了两个,一个是堆空间中new结构,另一个是char[] 对应的常量池中的数据:“abc”
(4)mysql 的 int 数据类型都有哪些?从小到大说一下,各占多少字节【字节跳动一面】
类型 | 字节数 |
tinyint | 1 |
smallint | 2 |
mediumint | 3 |
int | 4 |
bigint | 8 |
- java 中的 int 都有哪些?字节占多少?
类型 | 字节数 |
byte | 1 |
short | 2 |
int | 4 |
long | 8 |
(5)集合与数组的区别
- 数组:它的
长度固定
,适合存储类型一致
的数据 - 集合:可以对
数量不固定
的数据进行操作
(6) 集合类(Collection、Map)底层实现
List
- List存储
有序的,可重复
的数据——> 动态数组,替换原有的数组
- ArrayList:
线程不安全
的,效率高
,底层使用Object[] elementData存储(数组存储) - LinkedList:对于频繁的插入,删除操作,使用此类效率比ArrayList高;底层使用双向链表
- Vector:List接口的古老实现类,
线程安全
的,效率低
;底层使用Object[] elementData存储
- 底层实现
ArrayList
- jdk7以前:初始化时底层创建了长度为10的Object数组,如果添加导致底层容量不足,则扩容,默认情况下,
扩容为原来长度的1.5倍
,需要将原来的数据复制到新的数组 - jdk8:初始化时
没有指定数组长度
,add()操作时才创建长度为10
的数组,后续的扩容,复制操作和jdk7以前一致(原来的1.5倍)。
【补充Vector】
- jdk7和8底层在初始化时都创建了
长度为10
的数组,扩容方面为原来的2倍
HashMap
- 【jdk7以前】
数组+链表
-
初始化
底层创建长度为16
的一维数组Entry[] table - 执行put(key1,value1)操作时,首先调用key1所在类的hashCode,计算哈希值。此哈希值经过某种算法计算后,得到在Entry数组中的存放位置。如果此位置上为空,直接添加成功。——> 情况1
- 如果此位置上不为空,意味着此位置上存在一个或多个数据(以链表形式存在),比较key1和已经存在的一个或多个哈希值
- 如果key1的哈希值和已经存在的数据哈希值都不相同,此时添加成功。——> 情况2
- 如果key1的哈希值和已经存在的数据哈希值都相同,继续比较:调用key1所在类的equals(key2)方法比较
- 如果equals()返回false:此时添加成功。——> 情况3
- 如果equlas()返回true:使用value1替换value2
- 【补充】
- 关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储
- 在不断添加的过程中,会涉及到扩容的问题;默认的扩容方式:扩容为原来容量的2倍,复制原来的数据
- 【jdk8 以后】
数组+链表+红黑树
- 初始化创建Node[] 数组,但不指定数组长度;首次调用put()方法时,底层创建长度为16的数组
- 当数组的某一索引位置上的元素以链表形式存在的个数
>8
且当前数组的长度>64
时,此时此索引位置上的所有数据改为使用红黑树
。
HashSet
HashSet的内部采用了HashMap作为数据存储,HashSet其实就是在操作HashMap的key
所有放入hashSet存储的元素实际上都是由HashMap的key保存,它的value是一个PRESENT(静态的Object对象)
其他的操作也是基于HashMap的
TreeMap
按照添加的key-value对进行排序,实现排序遍历,此时考虑key的自然排序或定制排序,底层使用红黑树
(7) 集合类其他知识点
- List/LinkedList如果要查询O(1)怎么设计
- ArrayList/LinkedList 前者扩容是迭代器会有什么问题吗
- 说一下红黑树
- 自己写一个HashMap走双亲委派会被覆盖吗
【双亲委派】
- 首先说下Java中的
类加载器
,由下至上分别是自定义类加载器
,应用程序类加载器(AppClassLoader)
,扩展类加载器(ExtClassLoader)
,启动类加载器(BootstrapClassLoader)
- 自己自定义的类需要加载,类会首先发送给应用程序加载器(AppClassLoader);但它不会直接加载,会一级级向上,委派父类加载器完成,直到顶层。Bootstrap ClassLoader会在jdk/lib目录下去搜索是否存在,因为这是用户自己写的Test类,是不会存在jdk下的,所以给子类加载器一个反馈,这样再一级级往下,直到APPClassLoader收到反馈,只能自己去尝试加载。
- 简言之,自己定义的类,一级级向上直到启动类加载器,最后再回来到应用程序类加载器。
这么做的原因
- 是为了安全性,避免用户自己编写的类动态替换Java的一些核心类,比如String。
- 避免了类的重复加载,因为JVM中区分不同类,不仅仅是根据类名,相同的class文件被不同的ClassLoader加载就是不同的两个类。
【解答】
不会覆盖jre中的hashMap,因为首先起作用的是启动类加载器(BootstrapClassLoader)
- Hashmap怎么遍历的
遍历所有的key-value
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while(iterator1.hasNext()){
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry)obj;
System.out.println(entry.getKey()+"------->"+entry.getValue());
}
Set keySet = map.keySet();
Iterator iterator2 = keySet.iterator();
while(iterator2.hasNext()){
Object key = iterator2.next();
Object value = map.get(key);
System.out.println(key+"---------->"+value);
}
- Hashmap是线程安全的吗,会有什么问题
- 不是,多线程并发时对某个对象中HashMap类型的实例变量进行操作时,可能会出现各种不符合预期的问题。比如线程A希望插入一个key-value到一个hashmap中,当获取到对应的链表结点位置时,此时线程A的时间片已经用完,A计算到的位置被线程B占用,并插入数值。而线程A切换回来的时候,不知道线程B已经插入了数据,仍将元素插入,会将线程B插入的元素覆盖掉,多线程背景下,put 方法存在数据覆盖的问题
- Hashmap和HashTable的区别
-
Hashmap
是线程不安全
的,效率高
。HashTable
是线程安全
的,效率低
。 -
Hashmap
能存储null
的key和value,HashTable不能存储null的key和value
- 介绍下concurrentHashMap
- 在
jdk1.7
中,使用分段锁的思想,将hashmap进行分割,把hashmap中的哈希数组切分成小数组(Segment
),每个小数组有n个HashEntry组成,其中小数组继承自ReentrantLock -
jdk1.8
中,采用的是CAS+synchronized
来保证并发安全,synchronized只锁定当前链表或红黑二叉树的首节点,只要hash不冲突,就不会并发。
- 一致性hash底层用的什么数据结构
【什么是一致性hash】
-
普通hash算法
就是把存储的key取hash然后再对节点数取模之后判断key所在节点的位置;其中,节点数会发生变化,比如扩容/宕机,导致原有缓存失效,需要重新迁移数据,这里有详细说明 -
一致性hash
其实是普通取模hash算法的改良版,其hash计算方法没有变化,但是hash空间发生了变化,由原来的线性的变成了环
。缓存节点通过hash计算之后得到在hash环中的位置;key通过hash计算之后得到所在环的位置,然后顺时针方向找到第一个节点,这个节点就是存放key的节点
- String ,List ,Map初始化大小及扩容大小比较
String | List | Map | |
初始化方式及容量 | char[]数组,初始化长度为16的数组 | Object[]数组,初始化长度为10 | jdk7 Entry[] 数组,初始化长度为16,jdk8 Node[]数组,长度为16 |
扩容大小 | 默认扩容为原来的2倍+2 | 默认扩容为原来的1.5倍 | 默认扩容为原来的2倍 |
(二)多线程并发
书,Java高并发程序设计,Java并发编程的艺术,Java并发编程实战。看书+一些博客
重点
重点 | 复习指南 |
锁、volatile、线程池、Java内存模型、AQS、阻塞队列、原子操作类… | 面试时可能会要你实现多个线程交替执行,实现阻塞队列这些,读些JDK源码可以加深理解 |
(1)进程/线程回顾
- 什么是进程/线程、举例
- 线程状态?
- 新建(NEW):新创建了一个线程对象,但还没有调用start()方法
- 运行(Runnable):Java线程中将就绪(ready) 和 运行中(running)两种状态笼统的称为
运行
。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。 - 阻塞(Blocked):线程阻塞于锁
- 等待(Waiting):等待其他线程做出一些特定动作(通知或中断)。
- 超市等待(Timed_Waiting):该状态不同于WAITING,它可以在指定的时间后自行返回
- 终止(Terminated):该线程已经执行完毕。
- wait/sleep的区别
- 什么是并发/并行
(三)JVM
书:深入理解Java虚拟机,这本书写的很好,Java面试必备,前七章都是重点
重点
重点 | 复习指南 |
内存结构、GC、对象生命周期、类加载、双亲委派模型、调优命令、OOM异常排查、字节码… | 所有面试必问JVM、很多时候不仅要求简单地回答,会问的很深,一定要理解,复习时可以自己试试参数调优 |
(四)设计模式
书(Head First设计模式)+博客。
Java知识点里设计模式必问,时间不够23种不用都看,至少掌握几种
重点
重点 | 复习指南 |
单例,代理,工厂,观察者,策略,模板方法,设配器,装饰者… | 复习时要找到每种设计模式在JDK或者Spring等框架中的对应场景,面试时有时候也会问项目里用到哪些设计模式。还有一些相似的设计模式的异同(比如装饰者、适配器和外观模式的异同);手写单例模式,画UML图也是常见的问题。 |
单例模式工厂模式观察者模式策略模式装饰者模式 代理模式
模板方法
设配器模式
*(五)JavaEE基础+框架
书(深入分析Java Web技术内幕)+博客+视频(Spring、SpringMVC等)
这一块内容不是必须的,会很加分,内容很多很杂,建议对着博客和视频看
重点
重点 | 复习指南 |
Sevlet生命周期,get和post区别,session和cookie区别,Spring源码(IOC,AOP、事务、bean生命周期、SpringMVC)… | Spring基本面试必问,源码特别特别多,可能会看不下去,但是如果项目里有涉及到网站之类的,源码还是能看多少是多少。有时间Hibernate、Mybatis、SpringBoot这些也可以看一下 |
(1)get和post区别
- 传送方式:get通过地址栏传输,post通过报文传输
- 传送长度:get参数有长度限制(受限于url长度),而post无限制
-
GET
产生一个TCP数据包
;POST
产生两个TCP数据包
。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据),简言之,POST请求有一个验证的过程第一次发送验证,再决定是否发送第二次请求
。 - POST相比GET更加安全。Get是不安全的,因为在传输过程,数据被放在请求的URL中;Post的所有操作对用户来说都是不可见的。 但是这种做法也不时绝对的,大部分人的做法也是按照上面的说法来的,但是也可以在get请求加上 request body,给 post请求带上 URL 参数。
(2)session和cookie区别
-
Session
是在服务端
保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件
中; -
Cookie
是客户端保存用户信息
的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。
(3)bean生命周期
对象创建,初始化,销毁
本篇帮你更清楚的了解bean的生命周期及涉及到的相关方法;其实呢,准备面试也是帮我们更好的理解某个知识,底层原理了解了才能举一反三,更有效率的解决问题
(六)计算机网络
书(图解HTTP+TCP/IP详解卷1)+博客
计网的知识点也很多,跟着博客看快一点
重点
重点 | 复习指南 |
网络模型、TCP三次握手四次挥手、滑动窗口、拥塞窗口、UDP协议、http请求流程、https加密解密、DNS解析… | 重点放在TCP和HTTP,非常高频,要注意会画TPC连接的状态图,每个状态名称和作用也要记住;HTTP的报文格式、状态码,版本区别等等也是重点 |
(1)TCP三次握手四次挥手
本章讲述了TCP三次握手四次挥手、http请求流程
(七)数据结构+算法
书(大话数据结构)+刷题(剑指offer、leetcode)+视频(左程云算法课)
这一块没啥好说的,刷题就完事,技术岗笔试做出来基本全靠算法题,面试手撕代码也绝对是少不了的
【大神经验分享】
先把牛客网上的剑指offer66道题刷完,再刷leetcode的高频题,以及左神的算法课据说也讲的很好,很多人是刷题+看视频
题量300左右一般笔试面试肯定没问题,我本人是刷了剑指的66+LC400道,高频的刷了两三遍,面试手撕代码都过了。
题目怎么刷全看个人习惯,记得看leetcode讨论区的高频解答。刷第一遍的时候肯定很慢,并且刷完会忘,建议早点开始刷题,刷第二遍的时候能快速写出来
【我为什么去不了大厂呢】
学历是一方面,另一方面就是算法了,真是我的死穴!!!!蓝瘦
(八)数据库
书(MySQL技术内幕innodb存储引擎+redis设计实现+Redis深度历险)+ 视频(尚硅谷的系列不错,MySql学习手册,MySQL高级篇等)
关系型数据库看MySQL、高性能MYSQL那本书太厚内容很深,不建议刚开始就看。非关系型数据库Redis作为缓存蛮高频的,项目里经常有,在掌握MySQL的基础上最好看一下
重点
重点 | 复习指南 |
1. MySQL重点:基本sql语句,索引底层实现,行锁表锁,MVCC、事务隔离级别,SQL查询优化、主从复制,分表分区… 2. Redis重点:底层数据结构,淘汰机制,持久化RDB/AOF、单线程内部原理,Redis集群,分布式锁,高级数据结构… | 数据库和缓存的一致性经常在面试中会问,这个方面通常会深挖,如果做过项目会深挖项目实现,没做过项目会让你给出解决方案并分析优缺点,数据库这块内容还是非常重要的。 |
(1)索引底层实现
(2)行锁表锁
(3)MVCC
(4)事务隔离级别
(5)SQL查询优化
(九)操作系统
书(操作系统原理+鸟哥的Linux私房菜基础篇)+博客
操作系统原理一定要掌握,除此之外如果有写Linux经验就更好了,没有的话至少要把OS原理说清楚
重点
重点 | 复习指南 |
进程线程状态,调度,同步,通信,死锁;内存管理,虚拟内存,页置换;磁盘设备管理。 Linux重点:常用命令,网络IO模型,套接字函数,select/poll/epoll异同,文件系统,内部碎片和外部碎片,性能调优… | 会Linux是加分项,可能会在面试的时候问一些常用命令,包括sed/awk写一些表达式,或者问一些调优的命令。 |
*(十)分布式、消息队列、其他框架
这一块非必须,了解一些更好。现在面试要求越来越高,面试官会问你是否了解/用过一些分布式、消息队列或框架的东西,大部分是结合项目问的。
重点
分布式内容涉及的很多,比如zookeeper、Dubbo、Hadoop、Spring Cloud之类的;
消息队列也有kafka,RocketMQ等,除此之外还涉及到比如NIO的netty框架,Web框架,Tomcat,Nginx等
个人建议针对自己的项目有选择地深入学习(包括看源码,技术文档等),不用方方面面都会,但项目里用到的技术一定要弄清楚。建议平时多看看技术博客和技术公众号,扩展一下自己的知识面。
一、Dubbo
- 说下对dubbo,zk分布式的理解(dubbo的结构,dubbo和zk的关系)
- dubbo中服务调用的几种形式
- 对Dubbo的理解,结构是什么样的
- RPC过程,怎么知道要调用的远程主机的方法
- 自己设计一个rpc框架要考虑什么
- 怎么把一个异步过程的返回结果转为同步的
(十一)场景/架构设计
书(大型网站技术架构:核心原理与案例分析)+平时知识积累
重点
面试中最能考察水平的就是场景设计题,面试官会给一个具体的场景,让你完成对应系统的设计,现场面的话还可能会让你画架构图/类图等,比如聊天室,秒杀系统,主要是看看你是否对问题考虑的全面以及能不能善用自己掌握的知识点。
大型网站技术架构这本书讲的还不错,高性能,高可用,伸缩性,可拓展性,安全性都有对应的案例,平时也可以多看看技术博客所说的案例,背题背书没有用,主要都是看平时积累