== equals, hasCode,instance
== 两个对象的引用完全相同(即同一对象),hashCode()以C++实现,返回对象的内存地址
try-Catch-final
任何执行try 或者catch中的return语句之前,都会先执行finally语句,如果finally存在的话。如果finally中有return语句,那么程序就return了,所以finally中的return是一定会被return的,编译器把finally中的return实现为一个warning。
如果都有return 优先级为 final > try = catch > return
正则表达式
String类中的各种方法(matches()、replaceAll()、replaceFirst()、split())和Pattern对象
Pattern p = Pattern.compile(``".*?(?=\\()"``);
IO流(NIO,BIO,)
- 分类
- 阻塞:读写数据时客户端会发生阻塞
- 非阻塞
- 多路复用:Selector的线程不断轮询多个Socket的状态,只有在Socket有读写事件时,才会通知用户线程进行I/O读写操作。
- 信号驱动:户线程发起一个I/O请求操作时,系统会为该请求对应的Socket注册一个信号函数,然后用户线程可以继续执行其他业务逻辑;在内核数据就绪时,系统会发送一个信号到用户线程,用户线程在接收到该信号后,会在信号函数中调用对应的I/O读写操作完成实际的I/O请求操作
- 异步:类似信号驱动,但用户线程不需要关心整个I/O操作是如何进行的,只需发起一个请求,在接收到内核返回的成功或失败信号时说明I/O操作已经完成,直接使用数据即可。
- NIO(非阻塞IO)
- Buffer为所有的原始类型提供 (Buffer)缓存支持。
- Charset字符集编码解码解决方案
- Channel一个新的原始 I/O抽象,用于读写Buffer类型,通道可以认为是一种连接,可以是到特定设备,程序或者是网络的连接。
List,Queue, Set,Map
反射
动态加载类,jvm中需要另一个类的方法实现时,动态加入相应类
注解:
@Override
int,Integer
int 基本数据类型, Integer,int的封箱引用
String, StringBuffer
String 固定长度的多个字符,stringBuffer可以修改的多个字符
Lambda
规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法,常与@FuncitonalInterface注解一同使用原文链接
@FunctionalInterface
public interface NoReturnMultiParam {
void method(int a, int b);
}
public static void main(String[] args){
NoReturnNoParam noReturnNoParam = () -> {
System.out.println("NoReturnNoParam");
};
noReturnNoParam.method();
}
native方法
非Java实现的java方法。以其他语言实现的Java方法即为native方法。
jvm
垃圾回收机制
- 识别
- 可达性分析算法:判断对象的引用链是否可达
- 引用计数算法:判断对象的引用数量,数量为零回收
- 回收
- 标记清除算法:该算法首先从根集合进行扫描,对存活的对象对象标记,标记完毕后,再扫描整个空间中未被标记的对象并进行回收
- 复制算法:将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
- 标记整理算法:标记过程类似标记清除算法,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,类似于磁盘整理的过程,该垃圾回收算法适用于对象存活率高的场景(老年代)
- 分段收集算法:不同的对象的生命周期(存活情况)是不一样的,而不同生命周期的对象位于堆中不同的区域,因此对堆内存不同区域采用不同的策略进行回收可以提高 JVM 的执行效率。新生代(标记复制),老年代(标记整理,清除)
- 内存
- 新生代的目标就是尽可能快速的收集掉那些生命周期短的对象,一般情况下,所有新生成的对象首先都是放在新生代的
- 老年代存放的都是一些生命周期较长的对象,就像上面所叙述的那样,在新生代中经历了N次垃圾回收后仍然存活的对象就会被放到老年代中。
- 永久代主要用于存放静态文件,如Java类、方法等。
- 回收器
- Serial收集器(复制算法): 新生代单线程收集器,标记和清理都是单线程,优点是简单高效;
- Serial Old收集器 (标记-整理算法): 老年代单线程收集器,Serial收集器的老年代版本;
- ParNew收集器 (复制算法): 新生代收并行集器,实际上是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现;
- Parallel Scavenge收集器 (复制算法): 新生代并行收集器,追求高吞吐量,高效利用 CPU。吞吐量 = 用户线程时间/(用户线程时间+GC线程时间),高吞吐量可以高效率的利用CPU时间,尽快完成程序的运算任务,适合后台应用等对交互相应要求不高的场景;
- Parallel Old收集器 (标记-整理算法): 老年代并行收集器,吞吐量优先,Parallel Scavenge收集器的老年代版本;
- CMS(Concurrent Mark Sweep)收集器(标记-清除算法): 老年代并行收集器,以获取最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短GC回收停顿时间。
- G1(Garbage First)收集器 (标记-整理算法): Java堆并行收集器,G1收集器是JDK1.7提供的一个新收集器,G1收集器基于“标记-整理”算法实现,也就是说不会产生内存碎片。此外,G1收集器不同于之前的收集器的一个重要特点是:G1回收的范围是整个Java堆(包括新生代,老年代),而前六种收集器回收的范围仅限于新生代或老年代。
- 原文链接
多线程
[原文链接](https://blog.csdn.net/tanmomo/article/details/99671622)
- 创建线程有哪几种方式?
- 继承Thread类(真正意义上的线程类),是Runnable接口的实现。
- 实现Runnable接口,并重写里面的run方法。
- 使用Executor框架创建线程池。Executor框架是juc里提供的线程池的实现。
- synchronized 和 volatile 的区别是什么?
- volatile 是变量修饰符;synchronized 是修饰类、方法、代码段。
- volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。
- volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
数据库
数据结构
Array ,ArrayList
Array和ArrayList的不同点:
Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
Array大小是固定的,ArrayList的大小是动态变化的。
ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢
hashMap原理
- 哈希冲突
二叉树
- 红黑树
手撕代码
反转链表
```java
public ListNode ReverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode p = null;
ListNode q = null;
while(head != null){
//做循环,如果当前节点不为空的话,始终执行此循环,此循环的目的就是让当前节点从指向next到指向pre
//如此就可以做到反转链表的效果
//先用next保存head的下一个节点的信息,保证单链表不会因为失去head节点的原next节点而就此断裂
q = head.next;
//保存完next,就可以让head从指向next变成指向pre了,代码如下
head.next = p;
//head指向pre后,就继续依次反转下一个节点
//让pre,head,next依次向后移动一个节点,继续下一次的指针反转
p = head;
head = q;
}
return p;
}
```
三十个人围成一圈,数到5退出,求最后一个退出的
```java
```
矩阵蛇形遍历
```java
public static void way(int n){
int[][] arry = new int[n][n];
for(int i = 0; i < n; i++){ //初始化
for(int j = 0; j < n; j++){
arry[i][j] = (i-1)*n+j+1;
}
}
int i = 0, j = 0;
StringBuilder sb = new StringBuilder();
sb.append(arry[0][0]+" "); //第一个元素
boolean isDown = false; //判断是自上而下还是自下而上,默认自上而下
while(i < n && j < n){
if(i+1 == n && j+1 == n){ //结束跳出
break;
}
if(i-1 < 0|| i+1 == n){ //右
sb.append(arry[i][++j]+" ");
isDown = false;
}else if(j-1 < 0 || j+1 == n){ //下
sb.append(arry[++i][j]+" ");
isDown = true;
}else{
if(isDown){
sb.append(arry[++i][++j]+" ");
}else{
sb.append(arry[--i][--j]+" ");
}
}
}
System.out.println(sb);
}
```
操作系统
分页,分段区别
分页和分段有许多相似之处,比如两者都不要求作业连续存放.但在概念上两者完全不同,主要表现在以下几个方面:[原文链接](https://blog.csdn.net/wangrunmin/article/details/7967293?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161931624016780357247739%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=161931624016780357247739&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-7967293.first_rank_v2_pc_rank_v29&utm_term=%E5%88%86%E9%A1%B5%E5%92%8C%E5%88%86%E6%AE%B5)
(1)页是信息的物理单位,分页是为了实现非连续分配,以便解决内存碎片问题,或者说分页是由于系统管理的需要.段是信息的逻辑单位,它含有一组意义相对完整的信息,分段的目的是为了更好地实现共享,满足用户的需要.
(2)页的大小固定,由系统确定,将逻辑地址划分为页号和页内地址是由机器硬件实现的.而段的长度却不固定,决定于用户所编写的程序,通常由编译程序在对源程序进行编译时根据信息的性质来划分.
(3)分页的作业地址空间是一维的.分段的地址空间是二维的.
线程,进程
进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。进程一般由程序,数据集合和进程控制块三部分组成
线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位
一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)[原文链接](https://www.cnblogs.com/qianqiannian/p/7010909.html)
死锁,银行家算法
- 产生条件:(1) **互斥条件**:一个资源每次只能被一个进程使用。(2) **占有且等待**:一个进程因请求资源而阻塞时,对已获得的资源保持不放。(3)**不可强行占有**:进程已获得的资源,在末使用完之前,不能强行剥夺。(4) **循环等待条件**:若干进程之间形成一种头尾相接的循环等待资源关系。
- 处理死锁
- 死锁预防:通过设置某些限制条件,去破坏死锁的四个条件中的一个或几个条件,来预防发生死锁。但由于所施加的限制条件往往太严格,因而导致系统资源利用率和系统吞吐量降低。
- 死锁避免:允许前三个必要条件,但通过明智的选择,确保永远不会到达死锁点,因此死锁避免比死锁预防允许更多的并发。**银行家算法**
- 死锁检测:不须实现采取任何限制性措施,而是允许系统在运行过程发生死锁,但可通过系统设置的检测机构及时检测出死锁的发生,并精确地确定于死锁相关的进程和资源,然后采取适当的措施,从系统中将已发生的死锁清除掉。
- 死锁解除:与死锁检测相配套的一种措施。当检测到系统中已发生死锁,需将进程从死锁状态中解脱出来。常用方法:撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程。死锁检测盒解除有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。
[原文链接](https://blog.csdn.net/qq_42883292/article/details/106151240)
计算机网络
三次握手,四次挥手
1. 第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
2. 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
3. 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
---
1. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2. 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3. 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
5. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
[原文链接](https://blog.csdn.net/qq_38950316/article/details/81087809)
OSI七层架构
| 网络模型 | 网络协议 |
| 应用层 | HTTP,FTP,SMTP |
| 表示层 | TelNet |
| 会话层 | DNS |
| 传输层 | TCP,UDP |
| 网络层 | IP |
| 数据链路层 | |
| 物理层 | IEEE 802.1A |
设计模式
Spring
- spring, springMVC:spring > springMVC
- Struse,MVC架构
- Hiberanate:ORM(对象/关系数据库映射)一种,将类映射到数据表
- Spring Boot
- https://blog.csdn.net/weixin_45136046/article/details/90768687
- https://www.cnblogs.com/3xmq/p/springboot.html
参考链接
[最新Java面试题,常见面试题及答案汇总](https://blog.csdn.net/fangchao2011/article/details/89203535)