63. APP端特有的测试
参考:APP专项测试、APP应用测试
crash和anr的区别
1)网络测试
2)中断测试
3)安装、卸载测试
4)兼容测试
5)性能测试(耗电量、流量、内存、服务器端)
6)安全性测试(密码、sql注入、权限)
7)UI测试(横竖屏、手势、键盘等的自适应)
8)升级测试(考虑内存)
9)稳定性测试(使用monkey)
10)接口测试
11)其他测试(如上传文件、图片、视频)
64. 服务异常情况验证
65. 用什么做性能测试
66. Jmeter如何设计测试场景
67. 压测怎么做
69. UI自动化元素定位方法
参考:UI自动化元素定位
70. gpu和cpu有什么区别
71. gpu性能受哪些因素的影响
72. 共享内存,线程安全吗?怎么解决?
73. 什么情况下会发生死锁?怎么解决?
74. 进程上下文切换需要做哪些事情?上下文保存在哪里?
75. 单例模式怎么实现的?怎么判断一个实例是不是已经创建了呢?线程安全吗?加锁会有什么问题?怎么优化?
76. 视频静态解码和动态解码?
77. 逆层序遍历
78. 用户上传照片如何测试
79. 线上bug如何解决?完成处理流程?
80. 前端页面无法展示,如何找bug?
81. 服务端接口有问题怎么定位bug
82. 字符串数组最长公共子串
83. 如果UI经常变更,UI自动化怎么解决
① 对比较稳定的功能和模块设置自动化用例
84. 提一个bug需要写明什么
参考:提BUG的内容
测试环境、测试设备/型号、软件版本、bug错误类型、bug所属模块、重现概率、优先级、严重级别、复现步骤、预期结果、实际结果、日志、bug状态以及标题、bug解决人
85. 怎么保持redis数据的时效性
86. 接口实现与基类继承的差异
87. coding过程中什么才是符合标准的好代码
88. Java基本数据类型有哪些?自动装箱、拆箱?
89. 异常的分类?如何处理异常?
参考:异常的的分类以及处理
90. Mysql的索引如何实现?B+树的结构?是逻辑结构还是线性结构?分别介绍逻辑结构、线性结构的数据结构有哪些?
91. sql:一页数据20条记录,显示第五页的记录?
已知每页显示m条数据,求第n页显示的数据
提示: 关键是求每页的开始行索引
① 查询学生表,获取第n页数据的SQL语句:
select * from students limit (n-1)*m, m
② 一页数据20条记录,显示第五页的记录
select 8 from table limit (5-1)*20, 20;
92. 购物车下单功能需要进行哪些测试
93. 了解哪些排序算法?哪些排序算法效率高一些?时间复杂度如何?
94. 两个无序二叉树合并为有序二叉树
95. 贪心算法和动态规划的区别?
96. 分治思想和动态规划的区别
97. 线程资源以来死锁问题,如何避免?
98. IO多路复用
99. InnoDB的索引类型
100. MySQL的锁机制,悲观锁和乐观锁的区别
1)共享锁S和排他锁X
2)乐观锁一般是使用版本号来实现的,一般使用select … for update来实现悲观锁
101. sql:查询第50到100条数据
① 查询前100条数据
select * from A limit 0,100;
② 查询50-100条数据
select * from A limit 49,51;
select * from A limit s, n;
// s 表示起始位置,是不包含状态,也让就是说从s+1开始输出
// n 表示多少条数据
102. 依赖注入的方式
103. IOC和AOP的概念,IOC的实现机制
104. SpringBoot配置文件的加载顺序?yam和properties?
105. 讲一下collection和map
106. List和Set有什么区别?Set里面可以有null值吗?List是不是可以有多个null值
① 都继承自Collection接口
② null值
③ 重复值
④ 有序性
107. hashMap和hashTable有什么区别?Hash Map的底层原理?如何解决Hash冲突?
1)区别:
① 读写的线程安全
② null值的存储:map中key和value均可以是null,但是table中不允许存储null
2)哈希冲突避免:
哈希函数设计、负载因子调节
3)哈希冲突解决:
闭散列(线性探测,保存到下一个位置)、开散列(链表存储)
108. 一张表用a,b,c三个字段作为联合索引,一条sql语句命中了a和b是否会走索引?
参考:组合索引
组合索引 有“最左前缀”原则,遇到范围查询(>、<、between、like)就会停止匹配。
109. 基于TCP的协议
110. TLS握手过程
111. session和cookie
112. 页面置换算法LRU?还有其他的页面置换算法吗?
113. 介绍线程池以及对线程池的认识?
114. 哈希表与红黑树的认识?
115. 抽象类的使用场景
116. MySql的事务
117. 分布式锁
118. StringBuffer、StringBuilder为什么是可变的?
1)String:底层是char数组,数组是不可变长的;再者,数组是使用final修饰的,该引用不可以再指向其他对象
2)StringBuffer和StringBuilder:内部实际上是一个char[ ]数组,这个char[ ]数组没有被final修饰,StringBuffer和StringBulider的初始化容量应该为16,当存满之后会进行扩容,底层调用了数组拷贝的方法:System.arraycopy()…扩容的,所以StringBuffer/StringBuilder适用于字符串的频繁拼接操作,并且StringBuffer是线程安全的,StringBuilder是非线程安全的。(扩容机制:原数组大小*2+2)
119. 为什么TCP比UDP长一点?
① TCP 有可变长的「选项」字段,而 UDP 头部长度则是不会变化的。
② TCP是可靠传输的,其有三次握手和四次挥手,所需要使用的序列号以及确认号需要进行存储;另外,还有滑动窗口来进行流量控制等以保证数据的安全性
③ UDP的头部长度是不会改变的
120. Java怎么实现单例继承?手写线程安全的单例继承
1)定义:确保一个类只有一个实例,并提供该实例的全局访问点
2)设计要素:一个私有构造函数 (确保只能单例类自己创建实例)、一个私有静态变量 (确保只有一个实例)、一个公有静态函数 (给使用者提供调用方法)
3)类型:
① 懒汉:先不创建实例,当第一次被调用时,再创建实例,所以被称为懒汉式。
// 线程安全的懒汉模式
public class Singleton {
private static Singleton uniqueInstance;
private static singleton() {
}
public static synchronized Singleton getUinqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
② 饿汉: 先不管需不需要使用这个实例,直接先实例化好实例 (饿死鬼一样,所以称为饿汉式),然后当需要使用的时候,直接调方法就可以使用了。
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {
}
public static Singleton getUniqueInstance() {
return uniqueInstance;
}
}
③ 静态内部类
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
④双检查机制
public class MySingleton {
//使用volatile关键字保其可见性
volatile private static MySingleton instance = null;
private MySingleton(){
}
public static MySingleton getInstance() {
try {
if(instance != null){
//懒汉式
}else{
//创建实例之前可能会有一些准备性的耗时工作
Thread.sleep(300);
synchronized (MySingleton.class) {
if(instance == null){
//二次检查
instance = new MySingleton();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return instance;
}
}
4)使用场景
① 频繁实例化然后又销毁的对象,使用单例模式可以提高性能。
② 经常使用的对象,但实例化时耗费时间或者资源多,如数据库连接池,使用单例模式,可以提高性能,降低资源损坏。
③ 使用线程池之类的控制资源时,使用单例模式,可以方便资源之间的通信。
121. SpringBoot的关键注解? @SpringBootApplication是一个组合注解,是由哪些构成的?
参考:SpringBootAppication注解、SpringBootApplication注解02
SpringBoot注解
1)注解类型:
① 核心注解:@SpringBootApplication:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan
② 常用注解:@Component、@Bean、@Import、@Autowired、@Configuration、@VAlue(注入 application.properties 或 application.yml 配置的属性的值)
2)@SpringBootApplication注解:
/**
* @SpringBootApplication组合注解包含以下注解:
*
* @Target(ElementType.TYPE) :用于描述注解的使用范围
* @Retention(RetentionPolicy.RUNTIME) :保留,指定保留时间,三个枚举,分别为source,class,runtime
* @Documented:可被javadoc等工具记录,注解类型信息会保存到生成的文档中
* @Inherited:个人认为是一个成员变量,父类被子类覆盖之后无法继承,子类中可以继承父类中未被覆盖的父类注解的值
* @SpringBootConfiguration:包含@Target(ElementType.TYPE)
* @Retention(RetentionPolicy.RUNTIME) :同上述
* @Documented:同上述
* @Configuration:用于配置,包含以下注解
* @Target(ElementType.TYPE) :同上述
* @Retention(RetentionPolicy.RUNTIME) :同上述
* @Documented :同上述
* @Component:spring内置组件注解,用途是让spring容器扫描
* @EnableAutoConfiguration:开启自动配置,包含以下注解
* @Target(ElementType.TYPE):同上述
* @Retention(RetentionPolicy.RUNTIME):同上述
* @Documented:同上述
* @Inherited:同上述
* @AutoConfigurationPackage:自动配置包含注解的类
* @Import(AutoConfigurationImportSelector.class)
* @ComponentScan:开启包扫描,默认扫描同级及当前包下内容,包含以下注解
* @Retention(RetentionPolicy.RUNTIME)
* @Target(ElementType.TYPE):同上述
* @Documented:同上述
* @Repeatable(ComponentScans.class):允许在同一申明类型(类,属性,或方法)的多次使用同一个注解
*/
@SpringBootApplication
122. 编程:给你两个线程,交替修改同一个变量10次?
参考:多线程交替打印
思路:其实这类问题本质上都是线程通信问题,思路基本上都是一个线程执行完毕,阻塞该线程,唤醒其他线程,按顺序执行下一个线程。
1)synchronized + wait/notifyAll
2)join()
3)Lock
123. ThreadLocal和synchronized的区别?
124. JVM内存模型?JVM方法区主要存储哪些类型数据?
124. Spring Bean 和new 类的区别
125. 线程池由哪几种类型
126. JVM哪部分会出现OOM问题
127. 404的排查
参考:网站404、404错误排查
1)操作问题:是不是URL拼接错误
2)网络问题:是否已经连接上网络,网络性能是不是良好
3)网络屏蔽:是否给浏览器设置了黑名单,或者是访问的国外网站
4)资源问题:资源被删除、文件被移动也会导致404问题
128. 如果查看磁盘发现磁盘满了,如何排查程序错误
- 问题原因可能包括:
1)磁盘分区空间使用率达到100%
2)磁盘分区inode使用率达到100%
Ps. 磁盘分区是将一个物理硬盘分成多个逻辑区域,每个区域可以独立使用;inode是文件系统中的一个概念,用于存储文件的元数据信息如创建时间、所有者以及权限等 和数据所在的磁盘块号
3)僵尸文件:已删除的文件因为句柄被占用未释放导致相应空间未释放
yi5 如果一个文件被删除但是仍然被某个进程占用,那么相应的空间就不会被释放。这是因为在Linux系统中,文件的删除只是删除了文件名和inode之间的链接,而并没有真正删除文件的内容。只有当所有进程都关闭了对该文件的引用,即文件的链接数为0时,文件的内容才会被删除并释放相应的空间。因此,如果一个进程仍然占用着已经被删除的文件,那么该文件的链接数就不为0,相应的空间也就不会被释放。
② 句柄(Handle)是操作系统赋予给应用程序的一个标识,用于标识某个资源的引用。
4)挂载点覆盖:在原有文件系统的目录下已经存在大量文件。挂载了新磁盘后,导致使用df命令能统计到相关空间使用,而使用du命令统计不到。
使用df命令可以统计到新磁盘挂载后的空间使用情况,而使用du命令统计不到的原因是因为du命令只会计算文件或目录占用的磁盘空间,而不会计算被命令或程序占用的空间。在这种情况下,可以使用lsof命令查看哪些文件被打开并占用了磁盘空间,然后关闭这些文件或者重启相应的进程来释放空间。具体操作如下:
① 使用df命令查看磁盘空间使用情况:
df -h
② 使用du命令查看当前目录下文件占用的磁盘空间:
du -sh *
③ 使用lsof命令查看哪些文件被打开并占用了磁盘空间:
lsof | grep deleted
④ 关闭相应的文件或进程来释放空间,或者重启相应的进程。
- 解决办法
1)分区容量满
① 运行df -h
查看磁盘使用率
② 重复执行du -sh /*
命令,找到容量比较大的目录并进入目录,查找到最精确的文件或目录,再结合业务情况等进行判断,删除相关的文件或目录;也可以选择更换更大的磁盘。
2)inode容量满
① 把下面的命令写进脚本,循环执行。
for i in /*; # 遍历根目录下的所有目录
do
echo $i; # 输出目录名
find $i | wc -l; # 统计目录中的文件数并输出
done
② 逐层进入inode占用最高的目录,继续执行上述指令,逐步定位占用过高空间的文件或目录,最后进行相应清理。
3)修改inode数量
node结点中,记录了文件的大小、类型、权限、所有者、文件连接的数目、创建时间与更新时间等重要信息,还有一个比较重要的内容就是指向数据块的指针。一般情况下不需要特殊配置;如果存放文件很多,需要配置。有时磁盘空间剩余但是不能存放文件,可能是由于inode耗尽所致。
inode调整需要重新格式化磁盘,请确保已经备份好数据再执行以下操作:
① 运行以下命令查询inode使用情况:
df -i
② 运行以下命令解除挂载,假设为/opt/
umount /opt/
umount /opt/命令用于卸载/opt目录下的文件系统。如果/opt目录下的文件系统正在被使用,那么umount命令将无法卸载该文件系统。在这种情况下,可以使用fuser或lsof命令查找正在使用该文件系统的进程,并将其杀死,然后再尝试卸载文件系统。以下是一个例子:
# 查找正在使用/opt文件系统的进程:fuser -ck /opt
# 卸载/opt文件系统:umount /opt
如果umount命令仍然无法卸载文件系统,可以使用-l选项强制卸载文件系统:
umount -l /opt
③ 运行以下命令重新建立文件系统,指定inode节点数
mkfs.ext3 /dev/sdb -N 1778866
mkfs.ext3 /dev/sdb -N 1778866命令将在/dev/sdb设备上创建一个ext3文件系统,并将其格式化为具有1778866个节点的文件系统。请注意,这个命令将覆盖整个设备,而不是设备的一个分区。因此,在运行此命令之前,请确保您已经备份了设备上的所有数据
④ 运行以下命令查看修改后的inode节点数
dumpe2fs -h /dev/sdb | grep node
- 僵尸文件分析删除
如果磁盘和inode没有问题,则需要查看是否存在未被清除句柄的僵尸文件。这些文件实际上已经被删除,但是有服务程序在使用这些文件,导致这些文件一直被占用,无法释放磁盘空间。如果这些文件过多,会占用很大的磁盘空间。
① 运行以下命令查看僵尸文件占用情况
lsof | grep deleted | more
② 清除僵尸文件:
重启服务器reboot
正常停止systemctl stop servicename
或杀掉占用这些文件的服务器
kill <进程id>
ps aux | grep <进程名>
- 挂载点覆盖
先取消磁盘挂载umount path,再检查原挂载目录下的空间占用情况 du -sh path。
129. 大数据量如1TB进行排序,应该如何排序?时间复杂度以及空间复杂度是多少?
参考:海量数据排序
-
针对大数据量如1TB进行排序,可以采用外排序的方法。具体步骤如下:
① 将1TB的数据分成多个块,每个块的大小适当,可以载入内存中进行排序。
② 对每个块内的数据采用高效的内排序算法进行排序,例如快速排序、归并排序等。
③ 将排序后的块合并成一个大的有序序列。可以采用归并排序的思想,将相邻的块进行归并,直到所有块都被归并成一个有序序列,最后结果存入硬盘。 -
时间复杂度:外排序的时间复杂度主要取决于内排序算法的时间复杂度和归并排序的时间复杂度。假设内排序算法的时间复杂度为O(nlogn),归并排序的时间复杂度为O(nlogk),其中k为块的个数。则外排序的时间复杂度为O(nlogn + nlogk)。
-
空间复杂度:外排序的空间复杂度主要取决于内排序算法的空间复杂度和归并排序的空间复杂度。假设内排序算法的空间复杂度为O(1),归并排序的空间复杂度为O(n),则外排序的空间复杂度为O(n)。
-
1TB数据使用32GB内存如何排序
①、把磁盘上的1TB数据分割为40块(chunks),每份25GB。(注意,要留一些系统空间!)
②、顺序将每份25GB数据读入内存,使用quick sort算法排序。
③、把排序好的数据(也是25GB)存放回磁盘。
④、循环40次,现在,所有的40个块都已经各自排序了。(剩下的工作就是如何把它们合并排序!)
⑤、从40个块中分别读取25G/40=0.625G入内存(40 input buffers)。
⑥、执行40路合并,并将合并结果临时存储于2GB 基于内存的输出缓冲区中。当缓冲区写满2GB时,写入硬盘上最终文件,并清空输出缓冲区;当40个输入缓冲区中任何一个处理完毕时,写入该缓冲区所对应的块中的下一个0.625GB,直到全部处理完成。
130. 创建多线程的方法?多线程的使用场景?
参考:多线程的创建
- 多线程的创建
1)继续Thread类,重写run方法
2)实现Runnable接口,重写run方法
3)实现Callable接口并实现call方法,通过FutureTask的get方法来获取中间结果
4)使用线程池
5)匿名/lambda
1、创建Callable接口实现类,并实现call()方法,该方法将作为线程执行体,且该方法有返回值,再创建Callable实现类的实例;
2、使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值;
3、使用FutureTask对象作为Thread对象的target创建并启动新线程;
4、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
- 多线程使用场景
如处理多个请求或多个任务
举个栗子:访问网站,进行复杂计算等。
131. 使用线程池创建多线程的参数有哪些?核心线程和最大线程的区别?
参考:多线程参数
- 参数:
public ThreadPoolExecutor(int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程的最大存活时间
TimeUnit unit, // 存活时间单位
BlockingQueue<Runnable> workQueue, // 工作队列:新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。
ThreadFactory threadFactory, // 线程工厂:创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
RejectedExecutionHandler handler) {
// 拒绝策略
-
四种线程池:
① newSingleThreadExecutor: 创建单线程的线程池
② newFixedThreadExecutor:创建固定大小的线程池
③ newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
④ newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。 -
handler拒绝策略:
当工作队列满且线程个数达到maximunPoolSize后所采取的策略
① CallerRunsPolicy:调用者执行
② AbortPolicy:拒绝执行,直接丢弃任务,并抛出RejectedExecutionException异常。
③ DiscardPolicy:丢弃最新的任务,啥也不做
④ DiscardOldestPolicy:丢弃最老的任务,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列 -
核心线程和最大线程的区别
参考:核心线程和最大线程
每当有新的任务到线程池时,
① 第一步: 先判断线程池中当前线程数量是否达到了corePoolSize,若未达到,则新建线程运行此任务,且任务结束后将该线程保留在线程池中,不做销毁处理,若当前线程数量已达到corePoolSize,则进入下一步;
② 第二步: 判断**工作队列(workQueue)**是否已满,未满则将新的任务提交到工作队列中,满了则进入下一步;
③ 第三步: 判断线程池中的线程数量是否达到了maximumPoolSize,如果未达到,则新建一个工作线程来执行这个任务,如果达到了则使用饱和策略来处理这个任务。
注意: 在线程池中的线程数量超过corePoolSize时,每当有线程的空闲时间超过了keepAliveTime,这个线程就会被终止。直到线程池中线程的数量不大于corePoolSize为止。
(由第三步可知,在一般情况下,Java线程池中会长期保持corePoolSize个线程。)
132. 索引的作用,适用场景以及限制?
参考:索引
133. HTTP和HTTPS的区别
134. HTTPS中的S指的是什么
参考:HTTPS中的S
-
HTTPS
1)在以往的HTTP传输中数据都是以明文方式进行传输,而且HTTP协议是无状态的**,不能验证对方身份**,也不能保证数据传输完整性,如果攻击者在我们的通信中截取数据那就可以获得大量的有用信息,所以一种更加安全的协议HTTPS诞生了
2)HTTPS是在HTTP传输中增加了SSL协议,这一层协议加在了传输层与应用层之间,它使通通信更加安全,通信可以互相验证对方身份,并且保证了数据传输完整性,对通信数据进行了加密,即使被攻击者截取到了数据流,也很难解析出有用的信息。
3)而SSL不是一个搭配HTTP的协议,它是一个单独的协议,并且可以与应用层上其他协议进行搭配使用比如telenet,ftp等协议。
-
SSL工作流程
1)服务器必须拥有第三方CA机构颁发的SSL证书,而且现在的SSL证书大多不免费。
2)比如对于一个开头是HTTPS加载的网站,它会依靠公钥密码制度生成自己公钥和私钥,私钥自己保存,然后发送公钥给第三方CA机构,再经历过鉴别和审核后,第三方CA机构在这个网站的信息和公钥中进行数字签名并且返回数字证书,数字证书最少包含这个网站信息,公钥,第三方机构数字签名。
3)我们现在访问一个HTTPS的网站
① 客户端与服务器建立连接请求访问,要求建立SSL连接
② 网站收到请求后返回它的证书信息,客户端收到之后验证真假
③ 客户端审核确认后与服务器协商建立SSL连接并且请求等级,也就是信息加密等级
④ 客户端建立会话密钥对,然后利用网站证书中的公钥将会话密钥对的公钥进行加密,并发送给服务器
⑤ 服务器利用自己的私钥解密出会话密钥对的公钥。所以现在我们有了网站的公钥,网站也有了我们浏览器会话密钥对的公钥
⑥ 双方利用公钥进行数据加密传输
Ps. 1)SSL (Secure Sockets Layer 安全套接层),及其继任者传输层安全 (Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。 如今被广泛使用,如网页,电子邮件,互联网传真,即时消息和语音在IP电话(VoIP)。 其中网站是通过使用TLS来保护WEB浏览器与服务器之间的通信安全。
2)开启https的核心就是 :① 生成证书 , ②nginx配置ssl证书
135. jdk和jre的区别
1)jdk:java Development ToolKit(Java开发工具包)。JDK是整个JAVA的核心,包括了Java运行环境(Java Runtime Envirnment),一堆Java工具(javac/java/jdb等)和Java基础的类库(即Java API 包括rt.jar)。
2)jdk开发工具包下面主要包含:
·bin:最主要的是编译器(javac.exe)
·lib:类库(开发中需要使用到的一些jdk提供的一些类)
·jre:java运行环境(注意:这里的bin、lib文件夹和jre里的bin、lib是不同的)
3)总的来说,JDK是用于java程序的开发,而jre则是只能运行class而没有编译的功能。
4)jre:Java Runtime Environment,它是Java运行环境,如果你不需要开发只需要运行Java程序,那么你可以安装JRE。
5)jdk包含jre,jre中包含java虚拟机JVM等。
6)jdk中包含了jre,还有java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。
136. 集合有哪些
参考:集合(关注)、集合详解
Ps.所有集合类都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含以下三种方法:hasNext()是否还有下一个元素、next()返回下一个元素、remove()删除当前元素
137. list和set的区别
- 有序性
1)List 是有序的集合,它可以保存一组有序的元素,并且可以按照添加的顺序访问元素。例如,我们可以按照添加顺序遍历 ArrayList 中的元素。
2)而 Set 则是无序的集合,它不能保证元素的顺序,因此不能按照添加顺序访问元素。例如,HashSet 中的元素是无序的。 - 数据唯一性
List 可以保存重复的元素,而 Set 中的元素必须是唯一的,不允许重复。例如,List 中可以保存多个值相同的元素,而只有一个值的 HashSet 中无法保存第二个相同的元素。 - 数据访问方式
List 可以根据索引值(下标)访问元素,而 Set 没有提供类似索引的方式访问元素,只能通过遍历或迭代器来访问集合中的元素。 - 数据操作
List 和 Set 都支持添加、删除和遍历操作,但在具体使用时,它们的方法会略有不同。List 接口提供了一些与索引相关的方法,例如 get()、set() 方法,而 Set 接口则提供了一些独有的操作方法,例如 addAll()、retainAll()、removeAll() 等方法,用来对两个 Set 集合之间进行交、并、差等数学操作。 - 实现类
List 的实现有 ArrayList、LinkedList、Vector 等,底层数据结构各不相同;而 Set 的实现有 HashSet、TreeSet、LinkedHashSet 等,底层数据结构也各不相同。
138. super和this的区别
参考:super和this的区别
super和this的区别(关注)
-
this表示当前对象
1)this能出现在实例方法和构造方法中,表示当前对象的引用;
2)this的语法是“this.”和“this()”;
3)this不能出现在静态方法static中;
4)this大部分情况下是可以省略的;
5)this. 在区分局部变量和实例变量时不能省略;
6)this()只能出现在构造方法的第一行,通过当前的构造方法去调用“本类”中的对应的构造方法,目的是:代码复用。 -
super表示父类对象的引用
1)super能出现在实例方法和构造方法中,表示对父类的对象
2)super的语法是“super.”和“super()”;
3) super不能出现在静态方法中;
4) super大部分情况下是可以省略的;
5)super.什么时候不能省略呢?父类有,子类也有,子类想访问父类的,“super.”不能省略;
6)super()只能出现在构造方法的第一行,通过当前的构造方法去调用“父类”中的对应的构造方法,目的是:创建子类对象时,先初始化父类型特征。当子类的构造方法内第一行没有出现“super()”时,系统会默认给它加上无参数的"super()"方法。 -
补充
1)this()和super()为构造方法,作用是在JVM堆中构建出一个对象。因此避免多次创建对象,同一个方法内只能调用一次this()或super()。同时为了避免操作对象时对象还未构建成功,需要this()和super()的调用在第一行实现【以此来创建对象】,防止异常。
2)构造方法中“this()”和“super()”不能同时出现,也就是“this()”和“super()”都只能出现在构造方法的第一行。
139. 讲一下多态?动态绑定是在什么阶段完成的?
静态绑定:编译时多态,主要是使用方法重载实现的,如加法函数但是入参不一致
动态绑定:运行时多态,继承-重写-父类引用指向子类
140. JVM内存回收机制?
参考:JVM垃圾回收机制、JVM垃圾回收
JVM八股(关注!!)
-
GC垃圾回收机制
在java中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫描那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。
-
GC存活标准(判断哪些是需要被回收的)
1)引用计数法
为每个对象创建一个引用计数器counter,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。但是他有一个缺点是不能解决循环引用的问题。
2)可达性分析
从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
- 垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。
- Java 中,GC Roots 是指:
- Java 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 本地方法栈中引用的对象
- 方法区中常量引用的对象
- 方法区中类静态属性引用的对象
-
Java中的引用
1)强引用:不会被gc回收,只有当引用被设为null的时候,对象才会被回收。
2)软引用SoftReference:只有当 JVM 认为内存不足时,才会去试图回收软引用指向的对象。JVM 会确保在抛出 OutOfMemoryError 之前,清理软引用指向的对象。
3)弱引用WeakReference:当 JVM 进行垃圾回收时,无论内存是否充足,都会回收只被弱引用关联的对象。
4)虚引用/幻影引用/幽灵引用Phantom References:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。 -
GC回收机制
1)标记-清除算法:
标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
2)复制算法:
按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
3)标记-整理:
标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
4)分代算法:
① 根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。
② 当前商业虚拟机都采用分代收集的垃圾收集算法。分代收集算法,顾名思义是根据对象的存活周期将内存划分为几块。一般包括年轻代、老年代 和 永久代。
③ 大对象直接进入老年区
④ 分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。
⑤ 新生代使用的是复制算法,新生代里有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1,它的执行流程如下:
A. 在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To Survivor区”,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中。
B. 清空 Eden 和 From Survivor 分区;
C. 这时From Survivor 和 To Survivor 分区会互换角色,分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。
D. 每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。
E. 老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。
-
垃圾回收机制
141. 排序的实现以及时间复杂度
-
插入排序
1)直接插入排序:O(N2),稳定
2)希尔排序:不稳定;gap值的间隔,O(N1.25),但是时间复杂度起始并不是很确定 -
选择排序
1)直接选择排序:不稳定;其实就是有最大最小值然后进行交换,时间复杂度O(N2),两层遍历
2)堆排序:不稳定;时间复杂度O(N*logN)。
① 排升序要建大堆,排降序建小堆。(即:从小到大建大根堆)
② 建大根堆:从最后一棵子树的根节点开始,然后向下调整,child存放孩子节点最大值位置,parent是根节点
③ 排序:循环,最后一个节点与根节点交换,然后向下调整 -
交换排序
1)冒泡排序:稳定;趟数和对数的双重循环,时间复杂度O(N2)
2)快速排序:
① 挖坑法,Hoare法
② 时间复杂度:O(N*logN),logN层
③ 空间复杂度:O(logN)
④ 不稳定 -
归并排序
① 时间复杂度:O(N*logN)
② 空间复杂度:O(N)
③ 稳定
④ 其实就是分解成两部分,然后两部分同时从左边开始进行比较,结果储存在新数组中,相当于一个合并分操作。 -
海量数据排序:外部排序
① 分成n块大小为m的数据量
② 循环n次载入内存排序,将排好序的数据又写回到硬盘
③ 每次从每块硬盘上读取m/n的数据量到内存中排序,结果先放在缓冲区,缓冲区满后输出到硬盘中并清空缓冲区。
142. TCP的滑动窗口和拥塞控制
参考:TCP可靠、安全传输机制
-
重传机制(序列号、确认号)
1)超时重传:
① 数据包丢失、确认应答丢失
② RTO超时重传时间 应略大于 RTT往返时间
2)快速重传
① 不以时间为驱动,而是以数据驱动重传
② 三次同样的ACK就会触发快速重传
-
滑动窗口
1)窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。(确认应答往返时间长,效率较低)
2)窗口的实现实际上是操作系统开辟的一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。
3)累计确认或者累计应答:图中的 ACK 600 确认应答报文丢失,也没关系,因为可以通话下一个确认应答进行确认,只要发送方收到了 ACK 700 确认应答,就意味着 700 之前的所有数据「接收方」都收到了。
4)窗口大小由哪一方决定?
① TCP 头里有一个字段叫 Window,也就是窗口大小。
② 这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。
③ 所以,通常窗口的大小是由接收方的决定的。 -
流量控制
1)TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。
2) -
拥塞控制
1)流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络的中发生了什么。
2)在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大…
3)拥塞控制,控制的目的就是避免「发送方」的数据填满整个网络
4)拥塞窗口 cwnd是发送方维护的一个 的状态变量,它会根据网络的拥塞程度动态变化的。
5)发送窗口swnd的值是swnd = min(cwnd拥塞窗口, rwnd接收窗口),也就是拥塞窗口和接收窗口中的最小值。
6)那么怎么知道当前网络是否出现了拥塞呢?
其实只要「发送方」没有在规定时间内接收到 ACK 应答报文,也就是发生了超时重传,就会认为网络出现了用拥塞。
7)拥塞控制主要是四个算法:
① 慢启动
② 拥塞避免
③ 拥塞发生
④ 快速恢复 -
拥塞控制四个算法
1)慢启动:
① 慢启动的算法记住一个规则就行:当发送方每收到一个 ACK,就拥塞窗口 cwnd 的大小就会加 1。
② 慢启动涨到什么时候是个头呢?
有一个叫慢启动门限 ssthresh (slow start threshold)状态变量。
当 cwnd < ssthresh 时,使用慢启动算法。
当 cwnd >= ssthresh 时,就会使用「拥塞避免算法」。
(一般来说 ssthresh 的大小是 65535 字节)
2)拥塞避免
① 进入拥塞避免算法后,它的规则是:每当收到一个 ACK 时,cwnd 增加 1/cwnd。
② 拥塞避免呈线性增长
3)拥塞发生/重传机制
① 当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:
超时重传、快速重传
② 当发生了「超时重传」,则就会使用拥塞发生算法。这个时候,sshresh 和 cwnd 的值会发生变化:
ssthresh 设为 cwnd/2,cwnd 重置为 1
④ 「快速重传算法」:当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传。
TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 ssthresh 和cwnd 变化如下:
cwnd = cwnd/2 ,也就是设置为原来的一半;
ssthresh = cwnd;
进入快速恢复算法
4)快速恢复
① 进入快速恢复之前,cwnd和ssthresh已经被更新;然后,进入快速恢复算法如下:
拥塞窗口 cwnd = ssthresh + 3 ( 3 的意思是确认有 3 个数据包被收到了)
重传丢失的数据包
如果再收到重复的 ACK,那么 cwnd 增加 1
如果收到新数据的 ACK 后,设置 cwnd 为 ssthresh,接着就进入了拥塞避免算法
143. Socket套接字编程
参考:网络编程套接字
1)套接字(Socket)是代表计算机之间网络连接的对象,用于建立计算机之间的TCP连接,使计算机之间可以建立连接并实现网络通信。其实就是客户端和服务器端的交互。
① 客户端:Socket(host,port)
② 服务器端:ServeSocket(port),有accept()方法
2)套接字socket:就是操作系统给应用程序提供的API。(应用程序其实就相当于应用层和传输层之间进行交互的,所以此时的API其实就是传输层给应用层提供的,即socket其实是传输层的)
3)PS. 通过端口号查进程
netstat -anp | findstr 端口号
4)
144. 事务
参考:事务
- 事务是并发控制的单位,是用户定义的一个操作序列。
- 事务有四个特征: