基础篇
接口和抽象类的区别
相同:接口和抽象类都不能被实例化
不同:抽象类可以有构造方法,而接口没有
重载和重写的区别 *
重载:在一个类中,方法名相同、返回类型相同,参数不同,这就是重载。
重写:这发生在子类中,方法名相、参数列表、返回类型都相同,子类重写父类的方法。(但是final和private修饰的方法不可重写)
==和equals的区别 *
==比较基本类型,比较的是值,==比较引用类型,比较的是内存地址
equals是Object类的方法,有些类重写了equals方法,比较的是字符值
HashMap原理
在Jdk1.8之后基于数组+链表+红黑树来实现的,key不能重复,可以为null,线程不安全
(当链表长度大于8 并且数组长度大于64 变成红黑树)
当链表长度大于8 为什么变成红黑树?
因为在源码上有设置静态变量是8,上面注释解释是因为泊松分布,空间和时间的权衡后设置的值。
遍历HashMap有哪些方式
1.分别循环遍历key和value
2.迭代的方式
3.jdk8之后有了默认方法,forEach方法()
maps.forEach((k,v)->{
System.out.println(k);
System.out.println(v);
});
HashMap的扩容机制 *
数组初始化容量是16,默认的加载因子是0.75。
HashMap的扩展是HashMap用一个新的数组替换原来的数组。重新计算容量,向hashMap不停的添加元素,hashMap集合中存储的数据达到当前数组大小的75%则需要进行扩容,就创建一个大小为前一次两倍的新数组,也就是底层数组的长度总是2的n次方,以便装入更多的元素。当链表长度达到8,数据结构会变成红黑树,但数组长度没有超过64的时候,也会进行扩容。
(Node数组是HashMap静态内部类,也是HashMap里的数组)
(加载因子越靠近1,说明存的数据越多,越靠近0存的越少)
HashMap存取原理 *
1.首先根据key,通过哈希函数得到的hash值,进行二次hash,根据二次hash结果找到对应的索引位置。
2.如果这个位置有值,先进行equals方法比较,若结果为true则取代该元素,若结果为false,就将节点插入链表。
为什么HashMap底层数组的长度总是2的n次方
目的还是为了让哈希后的结果更均匀的分部,减少哈希碰撞,位运算的运算效率高于算术运算。
想要线程安全的HashMap怎么办?*
1 使用ConcurrentHashMap
2 使用HashTable
3 Collections.synchronizedHashMap()方法
ConcurrentHashMap如何保证的线程安全 *
JDK1.7: 使用分段锁,将一个Map分为了16个段,每个段都是一个小的hashmap,每次操作只对其中一个段加锁。
JDK1.8: 采用CAS+Synchronized,jdk8也保留到大数组segment,添加元素,首先判断容器是否为null,如果为null会使用volatile和CAS初始化,如果不为null会根据key计算出的该位置是否为null,也就是链表一个节点,如果为null,利用CAS设置该节点。如果不为null,就使用synchronized加锁,遍历或者添加数据。
(每次插入数据时判断在当前数组下标是否是第一次插入,是就通过CAS方式插入,然后判断f.hash是否=-1,是的话就说明其他线程正在进行扩容,当前线程也会参与扩容;删除方法用了synchronized修饰,保证并发下移除元素安全)
为什么ConcurrentHashMap集合中不允许存null的插入 *
HashMap是允许存入的,但ConcurrentHashMap不允许。
如果插入会报空指针异常,在源码添加方法第一句就判断是否null,如果为null就抛异常。
在单线程情况下,HashMap是可以通过containsKey(key)的方法来区分这个null值。
在多线程下,ConcurrentHashMap没有办法判断某一时刻值是null,还是压根就不存在,防止并发场景下的歧义问题。
HashTable与HashMap的区别 *
HashTable每个方法都用synchronized修饰,因此是线程安全的,但效率低。
HashTable的Key不允许为null,HashMap可以。
HashTable只对key进行一次hash,HashMap进行了两次Hash。
HashTable底层使用的数组加链表。
数组和链表的区别
数组:采用连续的存储元素进行存储数据,特点:查询快,插入慢
链表:是非连续打存储结构,特点:查询慢,删插快
ArrayList和LinkedList的区别 *
两者都不是线程安全
ArrayList的实现是基于数组,优点:查询快,插入慢。(数组在内存中是连续的地址,所以ArrayList查找数据更快)
LinkedList的实现是基于双向链表,优点:插入快,查询慢。(链表,在内存中是离散,不需要扩容,查找元素,从头遍历查询,所以查询慢)
如何保证ArrayList的线程安全?*
1.collentions.synchronizedList()
2.Vector (每个方法都由synchronized修饰,效率低)
3.juc下的CopyOnWriterArrayList
(不加锁,操作时为list创建一个副本,期间其它线程读取的都是原本list,写操作都在副本中进行,写入完成后,再将指针指向副本,保证线程安全)
String、StringBuffer、StringBuilder的区别 *
StringBuffer可变并且线程安全;
StringBuiler可变但线程不安全;
String是不可变的,每次操作String都会生成新的String对象,效率低。
(操作少量字符数据用 String;单线程操作大量数据用 StringBuilder;多线程操作大量数据用 StringBuffer)
hashCode和equals *
他们都是Object类的方法,hashCode()默认是用来计算hash码,
equals()默认通过地址判断两个对象是否相等,但是可能被重写用内容来比较两个对象。
如果:两个对象相等,他们的hashCode和equals一定相等,但是hashCode相等的两个对象未必相等,也就是重写equals()必须重写hashCode()。
面向对象和面向过程的区别
面向对象有封装、继承、多态性的特性
面向过程易维护、易复用、易扩展。
深拷贝和浅拷贝
浅拷贝只复制某个对象的引用,新旧对象还是共享同一块内存
深拷贝会创造一个一摸一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对对象。
什么是反射 *
反射是通过获取类的class对象,然后动态的获取到这个类的内部结构,动态的去操作类的属性和方法。
获取class对象的方法有 *
1 class.forName(类路径)
2 类.class()
3 对象的getClass()
Java创建对象得五种方式
1 使用new关键字
2 使用Class类的newInstance方法 (构造方法)
3 使用Constructor类的newInstance方法
4 使用反序列化
5 使用clone方法 (浅拷贝)
JDK 和 JRE 有什么区别
JDK(Java Development Kit),Java开发工具包
JRE(Java Runtime Environment),Java运行环境
final 在 java 中有什么作用?
用来修饰一个方法:无法被子类重写,但可以继承;
修饰类:无法被继承;
修饰一个引用:该值无法修改;
八种基本数据类型 *
byte、short、char、int、long、double、float、boolean
java 中 IO 流分为几种:
按流划分,可以分为输入流和输出流
按单位划分,可以分为字节流和字符流
BIO、NIO、AIO 有什么区别
同步阻塞BIO:一个连接一个线程
同步非阻塞NIO:它有一个缓冲区,一个请求来了,写到缓冲区内,该线程就不需要等待写完,他去处理其他事情了,它会一会回来看是否处理完
异步非阻塞AIO:它有一个缓冲区,一个请求来了,写到缓冲区内,该线程就不需要等待写完,他去处理其他事情了,等写完会通知该线程
例子:
BIO - 海底捞排队,怕过好一直等
NIO - 海底捞排队,出去逛商场,容易过号,或者回来还继续等
AIO - 海底捞排队,服务员会给你登记,你出去逛商场,有位置,服务员会叫你回来
网络系列
浏览器输入地址后做了什么
1 首先浏览器会根据域名查询ip地址,(DNS:获取域名对应的ip)
2 浏览器向web服务器发送一个Http请求
3 服务器处理请求
4 处理完返回HTML响应
5 浏览器加载显示
状态码有哪些 *
1xx:请求正在处理
2xx:请求成功处理
3xx:请求重定向 301:永久重定向 302:临时重定向 304:使用本地缓存
4xx:客户端错误 400:请求格式错误 403:没有访问权限 415:请求体过大
5xx:服务端错误
TCP是什么
是一种面向连接的通信协议,不同但互连的计算机通依靠TCP提供可靠的通信服务。
TCP与UDP的区别 *
TCP是面向连接的传输协议; 可靠的,有状态,会记录哪些数据发送,哪些被拒绝。
UDP是面向无连接的传输协议;不可靠的,无状态。
三次握手的理解 *
客户端与服务器之间数据的发送的过程当中需要创建一个TCP,请求和响应都是数据包,在一个TCP的连接上是可以发送多个http请求的。
第1次握手:客户端发送一个带有SYN(synchronize)标志的数据包给服务端;
第2次握手:服务端接收成功后,回传一个带有SYN/ACK标志的数据包传递确认信息,表示我收到了;
第3次握手:客户端再回传一个带有ACK标志的数据包,表示我知道了。
然后进行传输数据。
为甚要三次握手?
为了防止服务器端开启一些无用的连接增加服务器开销,
防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
( 为什么不使用两次握手:无法确认客户端是否真的想连接
为什么不使用四次握手:三次握手已经足够,再多一次握手只会造成更多的连接时延 )
为甚要四次挥手?
第1次挥手:客户端发送一个带有FIN标志的数据包给服务端,告诉他我要关闭的;
第2次挥手:服务端收到后,会立刻返回给客户端,来告诉客户端我收到消息了,此时他的状态更改为即将要关闭状态。
第3次挥手:服务端也要关闭连接,他会再发生一个数据包给客户端,告诉客户端我也要关闭了。
第4次挥手:客户端收到后,会立刻返回给服务端,表示自己知道了。此时服务端收到后,服务端马上关闭连接。客户端这边它有一个定时器,等时间到了也关闭了连接。
为什么是4次,如果优化只能优化第2次和底3次合并一次,因为服务端在第2次的时候有些资源没有释放,第2次和第3次之间有延迟。
还有客户端这边如果没有收到服务端也要关闭连接(第3次挥手),客户端会重发第2次。来保证完成两端的相互确认,4次是最优的方案。
什么是洪泛攻击(SYN Flood)攻击?
客户端大量伪造IP发送SYN包,模拟第一次握手,服务端接收后,会响应目标地址,基本这些IP的客户端都会拒绝连接。此时这些大量IP在服务端存在一个队列中,处于半连接状态,如果队列满了,就处理不了正常的请求了。
解决:可以利用路由器的TCP拦截功能,使用STCP协议链接。
如何理解TCP的keep-alive的原理?
一个TCP连接,如果双方都不向对方发生数据,看是否出现死连接,判断对方是否失效,默认时间是7200秒,太长了,一般我们都使用心跳机制来判断。
http与https的区别 *
http是无状态的超文本传输协议,信息是明文传输。端口是80
https协议是由http+ssl协议,可进行加密传输、身份认证的具有安全性网络协议,端口是443。
讲一下telnet的用法
用法1:telnet是检查这个端口是否打开,用法:telnet ip地址 端口号 (来检查远端的服务器端口是否打开)
用法2:telnet模拟发送http请求,用法:telnet 域名 端口号
讲一下netstat的用法
用于显示各种网络相关信息
netstat -a 显示所有选项信息
netstat -at 显示tcp相关选项信息 同理utp是 netstat -au
netstat -l 显示正在监听的服务状态 netstat -lt 显示正在监听tcp的服务状态
netstat -r 显示路由信息
谈一下tcpdump
tcpdump 命令行抓包
tcpdump -i any host 目标ip地址 (通过这个命令监听包) - 可查看TCP连接过程3次握手、4次挥手
curl 目标ip地址 (手动访问)
生产问题
生产环境服务器变慢,诊断思路和性能如何分析 *
我们首先看服务器的性能,一般变慢的原因导致是:1CPU问题、2磁盘IO问题
1. 查看整机 命令:top (如果执行完命令,按1 会看到每个CPU信息)
关注这几个属性: PID (进程id) %CPU(CPU占比) %MEM (内存占比)
load average (系统的负载均衡): 0.07, 0.16, 0.22 分别为系统的1分钟 5分钟 15分钟 如果这3个值相加除以3 大于60,说明机器负载严重
2. 查看CPU 命令:vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 3902336 221428 0 455740 1 0 1 1 0 0 0 0 99 0 0
详情:
r:运行等待CPU时间片的进程数 (不超过CPU总核得2倍) b :等待资源的进程数
us:用户进程消耗CPU的时间 (长时间在50%,程序需要优化)
sy: 系统进程消耗CPU的时间 (us + sy)大于80% 代表CPU不足
id:空闲的CPU占比
查看某个进程的CPU的信息
命令: ps -ef |grep java 得到进程id,再执行:pidstat -u 1 -p 进程id
3. 查看内存 命令:free -m
详情:total (总量) used(使用中) free(还剩) 当 used/total 大于70%,需要加内存
查看某个进程的内存信息
命令: pidstat -p 进程id -r 2 (平均每2秒执行1次)
4. 查看硬盘 命令:df -h
5. 查看磁盘I/O性能指标 命令: iostat -xdk 2 3 (2代表2秒钟执行1次 3代表执行3次)
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 1.76 1.27 0.52 1.26 18.81 17.52 40.92 0.00 2.28 1.08 2.78 0.28 0.05
dm-0 0.00 0.00 0.17 0.94 10.35 11.18 38.91 0.00 1.36 1.73 1.29 0.29 0.03
dm-1 0.00 0.00 2.12 1.59 8.46 6.34 8.00 0.05 12.37 0.81 27.80 0.06 0.02
详情:
rkB/s:每秒读取数据量kb wkB/s:每秒写取数据量kb
svctm:IO请求平均服务时间,单位毫秒 await:IO请求平均等待时间,单位毫秒(越小性能越好)
util:一秒中有百分之几的时间用于IO操作,越接近100%,代表磁盘饱和,需要优化系统或者扩大磁盘
查看每秒的IO读取情况信息
命令: pidstat -d 2 -p 20998 可查看 kB_rd/s (每秒读速度) kB_wr/s(每秒写速度)
6. 查看网络I/O网络流量 命令: ifstat 1 (1代表1秒钟执行1次,ifstat可能需要手动下载)
查看各个网卡的in和out,观察负载情况,网络读写是否正常
生产环境服务器CPU占用率过高,谈谈分析思路
1.先用top命令找出CPU占比最高的
2.通过ps-ef 或jps,定位哪个进程CPU过高,命令:ps -ef |grep java
3.定位具体线程或代码行数
命令:ps -mp 进程id -o THREAD,tid,time
USER %CPU PRI SCNT WCHAN USER SYSTEM TID TIME
root 1.0 - - - - - - 01:28:03
root 0.0 19 - futex_ - - 20998 00:00:00
解释:
TID(线程id) -m 显示全部的线程 -o 自定义参数格式
4.将线程id转换为16进制格式(如果有英文需要小写)
命令:printf "%x\n" 线程id
5.jstack进程,查询堆栈信息,可以得到哪些代码,那行有问题
命令:jstack 进程id | grep 16进制格式线程id -A60 (显示前60行代码)
JDK自带哪些JVM监控和性能分析工具有哪些
jinfo 这个命令可以查看JVM基本参数配置
jstack 打印JVM线程快照信息
GitHub操作
专业范围搜索:搜索内容 in:name:readme
搜索点赞超过500的内容:搜索内容 stars:>=500
学习搜索(代表搜索有组织写的学习类代码):awesome 搜索内容
高亮显示某行:url地址#L13-L20 (表示13-20行代码进行高亮)
项目搜索:快捷键 T