如果想改变 那就趁早吧 趁着你还想改变的时候

如果不想后悔 面试的话还是不要莽 还是要准备一段时间的
结合各路面经和小伙伴们的经验以及自己的理解
下面是作为一个还未经历过面试的蒟蒻整理的一些知识碎片

1.Object方法

  • .equals()
  • .hashCode()
  • .toString()
  • .getClass()
  • .wait()
  • .notify()
  • .notifyAll()

2.OSI七层模型

  • 物理层 (集线器,网关)
  • 数据链路层 (mac)
  • 网络层(ip,arp)
  • 传输层(tcp,udp)
  • 会话层
  • 表示层
  • 应用层(ftp,http)
1
2
3
4
5
6
7
物理层:通过媒介传输比特,确定机械及电气规范(比特Bit)
数据链路层:将比特组装成帧和点到点的传递(帧Frame)
网络层:负责数据包从源到宿的传递和网际互连(包PackeT)
传输层:提供端到端的可靠报文传递和错误恢复(段Segment)
会话层:建立、管理和终止会话(会话协议数据单元SPDU)
表示层:对数据进行翻译、加密和压缩(表示协议数据单元PPDU)
应用层:允许访问OSI环境的手段(应用协议数据单元APDU)

3.tcp&udp区别

tcp 保证可靠 udp不可靠 tcp慢 udp快

4.tcp三次握手

  1. 客户端发送一个带SYN标志的TCP报文到服务器。这是三次握手过程中的报文1
  2. 服务器端回应客户端的,这是三次握手中的第2个报文,这个报文同时带ACK标志和SYN标志。因此它表示对刚才客户端SYN报文的回应;同时又标志SYN给客户端,询问客户端是否准备好进行数据通讯。
  3. 客户必须再次回应服务段一个ACK报文,这是报文段3
1
2
3
因为TCP是全双工的,其实正常的时候,是需要双方通道都要测试是否打开成功才行.
然而因为在第二次握手的时候,服务器不仅发送了确认报文,而且还发送了要与客户端相关的报文SYN,询问客户
端是否准备数据通讯,这样就省了一个报文的发送

为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

1
2
3
4
5
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用
,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没
有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能
还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报
文和FIN报文多数情况下都是分开发

为什么不是一次或两次那

1
2
只有两次连接时:如果在网络情况不是很好的时候,客户端发出的包延时了,客户端以为丢失了,但是后面传到服
务器端,服务器正常接收建立了连接,但此时是一个空的建立连接了。--(超强室友总结)

5.事务特性

  1. Atomicity 原子性 指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生
  2. Consistency 一致性 一致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏
  3. Isolation 隔离性 多个事务并发访问时,事务之间是隔离的
  4. Durability 持久性 意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚

6.事务隔离级别

  1. Read uncommitted 未提交读
  2. Read committed 提交读
  3. Repeatable read 可重复读
  4. Serializable 序列化
Name 脏读 不可重复读 幻读
Read uncommitted
Read committed ×
Repeatable read × ×
Serializable × × ×

mysql默认隔离级别 Repeatable read

一些脏读、不可重复读、幻读的例子

7.mysql索引类型

  • Hash索引 键值对 不支持范围查询
  • B+树索引

B+树(平衡多路查找树):
B+Tree所有索引数据都在叶子结点上,并且增加了顺序访问指针,每个叶子节点都有指向相邻叶子节点的
指针。
这样做是为了提高区间查询效率
减少磁盘I/O读取 只需要一次读入

8.链表

都知道 但是面试时能否一句话说清楚
链表就是带着指针的结构体,含数据域和指向下一个节点的地址

9.HashMap

实现:数组+链表 拉链法 线程不安全 自动扩容
JDK8以前 一个类:Entry
JDK8之后 Node和TreeNode
在Java 8中如果hash值相同的key数量大于指定值(默认是8)时使用平衡树来代替链表,这会将get()方法的性能从O(n)提高到O(logn)

为什么线程不安全

  1. 多线程同时put时 hash值一样 又因为是链表 必然会遗失其中一个
  2. 扩容时 复制原数组被覆盖
  3. 且在扩容时会产生死循环

10.ConcurrentHashMap

JDK8以前 因为它包含一个segment数组,将数据分段存储,给每一段数据配一把锁,也就是所谓的锁分段技术。
JDK8以后 CAS+Synchronized compareAndSwapObject 适用场景:读者数量超过写者
比较详细的介绍
什么是CAS

11.JVM内存模型

所有线程共享数据区: 方法区
线程隔离数据区 虚拟机栈 本地方法栈 程序计数器

方法区:jdk8+ 字符串常量 永久代->元空间
用于存储已被虚拟机加载的类信息

JVM栈:局部变量表、操作数栈、动态链接、方法出口。
每创建一个线程,虚拟机就会为这个线程创建一个虚拟机栈
每调用一个方法就会为每个方法生成一个栈帧 存储局部变量表、操作数栈、动态链接、方法出口等信息
生命周期和线程是相同的

12.类加载原理

ClassLoader用来加载class文件的

  • Bootstrap ClassLoader (C++编写 用于加载rt.jar、resources.jar、charsets.jar和class等)
  • Extention ClassLoader
  • AppClassLoader

双亲委派机制

一个ClassLoader查找资源时,先看看缓存是否有,有就从缓存中获取,否则委托给父加载器。然后递归,父加载器继续看缓存,直到Bootstrap ClassLoader,如果Bootstrap ClassLoader找到了,直接返回,如果没有找到,则一级一级返回,最后到达自身去查找这些对象

13.GC机制

  • 引用计数法
    缺点:如果一个对象A持有对象B,而对象B也持有一个对象A,那发生了类似操作系统中死锁的循环持有。这种情况下A与B的counter恒大于1,会使得GC永远无法回收这两个对象

  • 可达性分析
    GC roots
    如果一个对象到GC Roots没有任何引用链相连时,则说明此对象不可用

哪些对象可作为GC roots

  1. 虚拟机栈中引用的对象
  2. 方法区类静态属性引用的对象
  3. 方法区常量池引用的对象
  4. 本地方法栈JNI引用的对象

垃圾收集

标记清除、标记整理、分代:年轻代、老年代

  1. 年轻代的GC(存放实例化的对象)
    年轻代分为三个区:Eden和两个存活区(Survivor0和Survivor1),分别占内存的80%、10%、10%
    使用“停止-复制 Stop-and-copy”清理法
    (将Eden区和一个Survivor中仍然存活的对象拷贝到另一个Survivor中)当Eden区满时,就执行一次MinorGC,并将剩余存活的对象都添加到Surivivor0,回收Eden中的没有存活的对象。当Surivivor0页都满了的时候,就将仍然存活的存到Surivivor1中,回收Surivivor0中的对象Surivivor0和Surivivor1依次去存,当两个存活区切换了几次后(HotSpot默认是15次),将仍然存活的对象复制到老年代。
  2. 老年代的GC(存放较大的实例化的对象和在年轻代中存活了足够久的对象)
    老年代GC用的是标记-整理算法,即标记存活的对象,向一端移动,保证内存的完整性,然后将未标记的清掉。当老年代不够用时,也会执行Major GC,即Full GC。

14.JVM有趣的机制(自认为挺有趣的一个点)

JVM怎么判断一个文件是否是class文件?JVM的做法是读取前4个字节转换成16进制数,判断是否等于0xCAFEBABE这个数。注意到这个单词了麽?
“cafebabe”,代表着国外一种咖啡品牌

15.AOP

运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
应用场景:

  • 读取缓存
  • 日志持久化

16.IOC&DI

DIP:依赖倒置原则
思路:IOC:控制反转
实现:DI 依赖注入
IOC容器:Spring中的BeanFactory

Example:
车依赖于车身 车身依赖于底盘
当new Car() 需要先new dipan() 再new cheshen()
当需要改变底盘的尺寸时 修改整个上层所有类的构造函数
依赖注入,就是把底层类作为参数传入上层类,实现上层类对下层类的“控制”

  • 与工厂模式类比:
    IOC Container可以直接隐藏具体的创建实例的细节,在我们来看它就像一个工厂
    跟普通工厂模式相比 这个是动态的

17.tomcat处理的请求是哪层

http 处于应用层
GET HEAD POST OPTIONS PUT DELETE TRACE CONNECT
http methods

18.乐观锁和悲观锁

乐观锁 数据进行提交更新的时候,才会正式对数据的冲突与否进行检测 version 为了解决cas的ABA问题
悲观锁 在整个数据处理过程中,将数据处于锁定状态

悲观锁的流程:
在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。
如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。
如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。
其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。

19.synchronized和concurrent包下的锁

synchronized JVM实现 修饰非静态方法或代码块为对象锁 修饰静态方法为类锁
ReentrantLock JDK实现(多了中断一个正在等候获得锁的线程的方法等)

20.volatile

解决可见性 多线程时 每个线程都有本地内存 保存着共享变量的副本
修改是对自己副本的修改且不会立刻刷新到主存去
禁止指令重拍序(单例模式双重检验后还需加volatile)

  1. 对volatile变量的写会立即刷新到主存
  2. 对volatile变量的读会读主存中的新值
i++语义是i=i+1
分为2个步骤
步骤1:读取i=0
步骤2:计算i+1=1,并重新赋值给i
那么可能存在2个线程同时读取到i=0,并计算出结果i=1然后赋值给i
那么就得不到预期结果i=2。
这个问题说明了2个问题:
1.i++这种操作不是原子操作
2.volatile 并不会有锁的特性

21.Spring和SpringMVC常用注解

Spring:

  • @Autowired
  • @Service

SpringMVC:

  • @Controller
  • @RequestMapping
  • @ResponseBody

22.设计模式

  • 单例
  1. 懒汉
  2. 饿汉
  3. 双重校验锁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Singleton {
    private Singleton(){};
    private volatile static Singleton singleton;
    public static Singleton getSingleton(){
    if(singleton==null){
    synchronized (Singleton.class){
    if(singleton==null){
    singleton=new Singleton();
    }
    }
    }
    return singleton;
    }
    }
  4. 静态内部类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Singleton {  
    private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance() {
    return SingletonHolder.INSTANCE;
    }
    }

23.ThreadPoolExecutor

  • 线程池的基本概念
  • 拒绝策略

24.redis数据类型

  1. string set
  2. hash hset/hmset
  3. list lpush/rpush
  4. set sadd
  5. zset zadd

25.redis缓存解决数据不一致性

读从redis读,写从mysql写,之后通知redis对应的缓存失效,去数据库缓存新数据
如果是分布式哪 涉及的就多了 目前没考虑过= =

26.CopyOnWriteArrayList