Java面试题整理(更新中)
Java面试题整理(更新中)面向对象的基本特征:面向对象的特点:ArrayListLinkedListHashMapConcurrentHashMap跟HashMap,HashTable的对比说说synchronized的实现原理悲观锁和乐观锁volatile关键字 (一保一禁两不保)16.并发编程三要素?nacos , eureka, zookeep面向对象的基本特征:面向对象的三个基本特征是:
Java面试题整理(更新中)
JVM 内存区域:
- 程序计数器(线程私有)
可以看作是当前线程所执行的字节码的行号指示器, 如果执行java方法则为虚拟机字节码指令的地址, 如果是Native方法, 则为空 - 栈(线程私有)
Java方法执行的内存模型。
每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。 - 本地方法区(线程私有)
本地方法栈则为虚拟机使用到的Native方法服务。 - 堆(线程共享)
此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 - 方法区(永久代)(线程共享)
JDK1.8改为元数据区
它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 - 运行时常量池(Runtime Constant Pool)
隶属于方法区
用于存放编译期生成的各种字面量和符号引用,
新生代和老年代
- Java堆分为新生代和老年代
- 新生代又分为1个Eden区和2个Survivor区,通常称为From Survivor和To Survivor区。
- Eden区和Survivor区的默认比例为8:1:1
GC分类和触发条件
分类:
Partial GC :
1. young GC : 只手机新生代的GC
2. old GC: 只收集老年代的GC
3. mixed GC : 收集整个新生代和部分老年代的GC
Full GC :
收集整个GC堆的模式,包括新生代, 老年代, 永久代(如果存在的话)
触发条件
- Young GC:当新生代中的Eden区没有足够空间进行分配时会触发Young GC。
- FullGC:
1. 当执行youngGC发现新生代比老年代剩余空间大, 就会转为触发FullGC
2. 永久代没有足够的空间
3. system.gc()默认触发Full GC
面向对象的基本特征:
面向对象的三个基本特征是:封装、继承、多态。
面向对象的特点:
- 单一职责原则: 表现在高内聚
- 开放封闭的原则: 不该源代码情况下,功能可拓展
- 里氏替换原则:拓展父类,但是不修改父类的功能
- 依赖倒置原则: 程序要依赖于接口, 而不是依赖于实现
- 接口隔离原则:一个功能不能强迫的使用他不需要的接口, 多个专用接口优于一个单一的通用接口
- 良性依赖原则:不会在实际中造成危害的依赖关系,都是良性依赖
ArrayList
- ArrayList extends AbstractList implements List, RandomAccess, Cloneable, Serializable
- 实现了List,它是一个元素有序(按照插入的顺序维护元素顺序)、可重复、可以为null的集合
- 同时还实现了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速访问、复制、序列化的。
- 底层以动态array数组的形式实现的
- 默认容量为10
- 扩容:old + old >> 1 ( old + old 除以 2 的1次幂 ) = 3/2倍
- 最大:Integer.MAX_VALUE - 8
- 扩容是以Arrays.copyof()的方式拷贝的, 非常耗时
- 查询快, 增删慢
LinkedList
- LinkedList extends AbstractSequentialList implements List ,Deque,Cloneable,Serializable
- 继承自AbstractSequentialList: 只支持按顺序访问,而不像 AbstractList 那样支持随机访问。不支持 RandomAccess
- 实现了List,它是一个元素有序(按照插入的顺序维护元素顺序)、可重复、可以为null的集合
- 实现了Deque, 双端队列,支持在两端插入和删除元素
- 实现了Cloneable,Serializable 支持复制、序列化。
- 是一个双向链表没有初始化大小,也没有扩容的机制 , 最大的容量为Integer.MAX_VALUE
- 新增/删除元素就是移动一下指针, 查询的时候只能从头或者尾一个一个查
- 增删快, 查询慢
HashMap
- HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
- 底层是数组+链表+红黑树
- 链表长度特别长的时候查询特别慢,所以JDK8之后转为红黑树
- 红黑树每个节点只有红色和黑色
- 根结点是黑色
- 每个叶子节点都是黑色空节点
- 从根结点到叶子节点不能出现两个连续的红色节点
- 从任一节点出发, 到下面子节点的路径包含相同数据的黑色节点
- 红黑树查询就相对快很多
- 链表长度特别长的时候查询特别慢,所以JDK8之后转为红黑树
- 链表长度超过8,转化为红黑树,减少到6就退化为链表
- 转为红黑树前提是:数组容量超过64
- 初始化大小是 16 ,扩容因子默认0.75(可以指定初始化大小,和扩容因子)
- 扩容机制: 当前大小 和 当前容量 的比例超过了 扩容因子,就会扩容,扩容后大小为 一倍。
- JAVA Hashmap的死循环及Java8的修复
- HashMap是非线程安全的,线程安全的应该用ConcurrentHashMap。
- JDK1.7是采用的头插法,在多线程环境下有可能会使链表形成环状,从而导致死循环。
- JDK1.8做了改进,用的是尾插法,不会产生死循环。
- put过程
- 对Key求Hash,然后计算下标
- 如果没有碰撞放入桶中(碰撞的意思是计算的hash值一样,需要放进一个桶里)
- 如果发生了碰撞就存为链表
- 当总容量>64,链表的长度>8,就转为红黑树存储。当链表长度<6,再换成链表。
- 如果hash值和equals值都相等,说明是相同的key,则替换value
- 如果桶满了(容量16*加载因子0.75), 则需要进行resize(扩容2倍,之后进行重排)
- 查询的效率取决于: 散列函数,冲突处理机制,和装载因子。
- 解决碰撞:开放定址法,再散列法,链地址法,建立公共溢出区
- 常用的散列函数: 直接寻址法,数字分析法,平方取中法,折叠法, 随机数法,除留余数法,乘法取整法。
ConcurrentHashMap跟HashMap,HashTable的对比
- HashTable和HashMap的实现原理几乎一样
- HashMap不是线程安全:HashTable是线程安全的:
- HashTable线程安全给整个哈希表加了一把大锁
- ConcurrentHashMap所采用的"分段锁"思想
- ConcurrentHashMap为table数组+单向链表+红黑树的结构.
各数组结构区分
- list: 双向链表。
- vector: 动态数组
- deque:双端队列
- set: 底层是红黑树
- map: 底层红黑树
二叉树
- 二叉树: 度最大为2(左子树,右子树)
- 最优二叉树(哈夫曼树): 该树的带权路径长度达到最小
- 满二叉树:所有非叶子节点度都是2,且叶子节点再同一个层次
- 完全二叉树: 与满二叉树的前m层的结构相同
- 平衡二叉树: 左子树和右子树高度大致相同,高度差小于等于1
- AVL树
- 红黑树
说说synchronized的实现原理
- 每一个对象都有一个monitor监视器, 加锁的过程就是竞争monitor的过程。
- 当线程进入monitorenter指令后,当前对象被该线程占用,及拥有该对象的所有权。 其他线程无法拿到该对象的monitor,进入阻塞状态
- 当该线程执行monitorexit指令后,线程退出monitor,此时其他线程才可以竞争该对象的monitor
悲观锁和乐观锁
- 悲观锁是在对数据进行修改的时候就加锁
- 乐观锁是在对数据修改完成提交的时候进行冲突检测
死锁:
产生的原因: 资源竞争 进程的推进顺序非法 资源不足
产生死锁的必要条件:
互斥条件 - 请求保持条件 - 不可剥夺条件 - 环路条件
死锁的处理策略:(破坏4个必要条件之一)
鸵鸟策略(即不理睬策略)
预防策略
避免策略
检测与解除策略
volatile关键字 (一保一禁两不保)
- 保证该变量的可见行。 当一个变量被 volatile 修饰时,任何线程对它的写操作都会立即刷新到主内存中,并且会强制让缓存了该变量的线程中的数据清空,必须从主内存重新读取最新数据。
- 禁止指令重排序
- 不保证线程安全
- 不保证原子性
- 使用单线程可以保证原子性
- 也可以使用 synchronize 或者是锁的方式来保证原子性。
- 还可以用 Atomic 包中 AtomicInteger 来替换 int,它利用了 CAS 算法来保证了原子性。
并发编程三要素?
1)原子性
原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行。
2)可见性
可见性指多个线程操作一个共享变量时,其中一个线程对变量进行修改后,其他线程可以立即看到修改的结果。
3)有序性
有序性,即程序的执行顺序按照代码的先后顺序来执行。
七层网络结构
应用层 - 表示层 - 会话层 - 传输层 - 网络层 - 数据链路层 - 物理层
数据库知识点:
事务四大特性:
原子性,一致性,隔离性 ,永久性
事务的并发问题:
脏读,不可重复读 , 幻读
事务的隔离级别:
隔离级别以及 ❌ 会产生的问题 ✅ 解决的问题
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | ❌ | ❌ | ❌ |
Read committed | ✅ | ❌ | ❌ |
Repeatable read | ✅ | ✅ | ❌ |
Serializable | ✅ | ✅ | ✅ |
事务的恢复机制:
undo log : 实现原子性
- 在数据库操作前备份数据
- 每次操作都备份,IO多,性能低
redo log :
- 备份新数据
常用存储引擎
MyISAM 和InnoDB
MyISAM 和InnoDB区别
MyISAM | InnoDB | |
---|---|---|
事务 | 不支持 | 支持 |
外建 | 不支持 | 支持 |
行锁 | 不支持 | 支持 |
表锁 | 支持 | 支持 |
唯一索引 | 没有 | 必须有(没有的话使用因此的rowid) |
count | 直接返回 | 扫全表返回 |
是否聚集索引 | 非聚集索引 | 聚集索引 |
数据存放 | 索引+引用地址->数据 理解为B+树的末端是数据存放的索引地址指向数据 | 索引+数据 理解为B+树的末端是数据 |
文件 | 定义文件Frm+索引文件MyI+数据文件MyD | 定义文件Frm+数据文件IBD |
灾后恢复 | 相对困难 | 相对容易 |
delete语句 | 删表重建 | 一行行删除 |
内存 | 占用少 | 对比:多 |
特点 | 增、查多比较合适 | 更新多合适 |
数据库的索引类型
- 主键索引
- 唯一索引
- 普通索引
- 组合索引
- 全文索引
Mysql的B+Tree索引和Hash索引区别
Hash索引:
- 检索效率高,一次定位
- 不能范围查询
- 不能进行数据排序
- Hash相等的数据查询性能低于B+tree
- 不支持多级联合索引的最左前缀原则
B+Tree索引:
- 默认的索引类型
- 支持范围查询
- 支持数据排序
- 支持分组查询
SQL优化之建索引
- where中和join on中频繁出现的列
- 列的基数越大,效果越好
- 对字符串应指定前缀长度
- 匹配最左前缀原则
- 频繁更新的数据不要建立索引
聚集索引和非聚集索引
表记录的排序顺序和索引的排序
- 顺序一致: 聚集索引
- 顺序不一致: 非聚集索引
数据库的范式
- 1NF: 字短的原子性
- 2NF:满足1NF,非主属性完全依赖于候选建, 记录的唯一性
- 3NF:满足2NF,消除非主属性对候选建的传递依赖
- BCNF: 满足3NF,消除每一个属性的传递依赖。
数据库的关联查询
- 内联接:inner join
- 等值联接
- 不等联接
- 自然联接
- 外联接
- 左联接 : left join
- 右联接 : right join
- 全联接 左右所有行
- 交叉联接: 笛卡尔积 cross join
数据库的三级模式 + 两级映射
- 用户级 -> 外模式(子模式, 用户模式)
- 概念级 ->概念模式(模式, 逻辑模式)
- 物理级 ->内模式(存储模式)
用户级和概念级决定逻辑独立性
概念级和物理级决定物理独立性
redis的类型的底层实现
- 整数(Long类型数据 , 叫 int )
- 简单动态字符串SDS
- embstr:短字符串
- 双端链表:linkedList
- 字典dict
- 跳跃表:skipList
- 整数集合:intset
- 压缩列表:ziplist
- 字符串 = 整数+SDS
- 列表 = ziplist+ linkedlist
- 哈希 = ziplist + hashTable
- 集合 = intset + hashTable
- 有序集合 = ziplist + skiplist
Spring设计模式
- 简单工厂模式
由一个工厂类根据传入的参数 , 动态的决定应该创建哪一个产品 - 工厂方法
FacetoryBean 就是典型的工厂方法模式 - 单例模式
保证一个类仅有一个实例,比提供一个全局访问点
private的构造函数 + 内部静态实例化 - 适配器模式
AOP的处理中Adapter模式的例子 - 包装类
动态地给一个对象添加一些额外的职责。 - 代理类
为其他对象提供一种代理以控制对这个对象的访问。 - 观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 - 策略
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。 - 模版方法
Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
nacos , eureka, zookeep
Eureka就是一个注重可用性的服务注册中心.
Zookeeper是一个注重一致性的服务注册中心.
Eureka和Zookeeper都不能支持大量的服务实例,Eureka因为所有的服务实例在每一台Eureka Server中都保存了,大量的服务实例会产生大量的心跳检测等信息,导致Eureka Server无法支持高并发的操作。
Zookeeper的话,会将服务实例的上线下线通知到每一个服务实例,如果频繁的上下线的话,会去通知大量的服务实例,导致短时间网络压力增大,性能下降。
而Nacos 在开源版本中,服务实例注册的支撑量约为 100 万,服务的数量可以达到 10 万以上。在实际的部署环境中,这个数字还会因为机器、网络的配置与 JVM 参数的不同,可能会有所差别。
更多推荐
所有评论(0)