>网上看到的一些面试题,自己整理一下答案,查缺补漏。
### Java 基础
#### 1.0 JAVA中的几种基本数据类型是什么,各自占用多少字节。
byte, short, int, long, float, double, char, boolean
数据类型|字节
:--|:--
boolean|1 bit,不到一个字节
byte|8 bit,1字节
short|16 bit,2字节
char|16 bit,2字节
int|32 bit,4字节
float|32 bit,4字节
long|64 bit,8字节
double|64 bit,8字节
#### 1.1 String类能被继承吗,为什么。
String类不能被继承,因为String类有final修饰符,final修饰的类不能被继承。
#### 1.2 String,Stringbuffer,StringBuilder 的区别。
String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
>简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:
String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
String S1 = “This is only a” + “ simple” + “test”; 其实就是:
String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去做
>
>**在大部分情况下 StringBuffer > String**
StringBuffer
Java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含“startle”,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含“starlet”。
在大部分情况下 StringBuilder > StringBuffer
java.lang.StringBuilde
java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。
#### 1.3 ArrayList 和 LinkedList 有什么区别。
- **ArrayList,采用数组数据结构的List,创建一个数组a,加索引即角标就可以访问到数据,数组在内容中是一段连续的数据,可以支持随机访问**。a则表示数组的内存地址,索引则是数据所处位置距离第一个元素的偏移量,如a[0]表示当前第一个元素,和a指的是一个位置,所以无论任何位置,只需要两步,找到a的位置,然后获取偏移量即可访问到数据,时间复杂度是O(1)。**数组创建时需要指定长度,ArrayList可以一直增加是因为当超过长度时,会新创建一个新的数组,把原来的数据拷贝进去,然后将老的数组抛弃掉。ArrayList支持随机访问,实现了RandomAccess接口。查询多的情况使用ArrayList,当需要删除数据时,当前数据的后续数据角标都发现改变,所以时间复杂度是O(n-i),所以适合用在查询多,增删少的情况下。**
- **LinkedList,采用链表数据结构的List,不支持随机,在创建时并没有指定长度,使用时是由系统分配内存,所以在内存中的位置是随机**。LinkedList在添加数据时不光会记录当前数据,还会记录上个元素的位置,所以通过上个元素访问这个元素,通过一个个元素互相指向形成一个链条一样的结构。当需要访问一个位置的数据时,只能通过第一个元素,一步一步寻找,时间复杂度是O(n),不能随机访问。所以**查询的效率很慢,当删除时,只需要将数据删除后,再下个元素的指向到上个元素即可,删除的时间复杂度是O(1),所以适合用在频繁增删的情况下。**
具体可以看以前转的一篇文章:[关于一次List的面试](https://lixj.fun/archives/2020-04-30-11-56-32)
之前写过的ArrayList相关内容:[ArrayList的扩容机制](https://lixj.fun/archives/arraylist%E7%9A%84%E6%89%A9%E5%AE%B9%E6%9C%BA%E5%88%B6)
#### 1.4 讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当 new 的时候,他们的执行顺序。
顺序为:
1. 父类静态变量、
2. 父类静态代码块、
3. 子类静态变量、
4. 子类静态代码块、
5. 父类非静态变量(父类实例成员变量)、
6. 父类构造函数、
7. 子类非静态变量(子类实例成员变量)、
8. 子类构造函数。
代码验证可以看这个:[Java基础-类的实例化顺序](https://lixj.fun/archives/java%E5%9F%BA%E7%A1%80-%E7%B1%BB%E7%9A%84%E5%AE%9E%E4%BE%8B%E5%8C%96%E9%A1%BA%E5%BA%8F)
#### 1.5 用过哪些 Map 类,都有什么区别,HashMap 是线程安全的吗,并发下使用的 Map是什么,他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。
用过 HashMap、LinkedHashMap、TreeMap、
具体查看[这个](https://lixj.fun/archives/2020-12-11-15-54-16)
[笔记(三) - Java集合](https://lixj.fun/archives/%E7%AC%94%E8%AE%B0%E4%B8%89-java%E9%9B%86%E5%90%88)
HashMap不是线程安全的,并发下应该使用 ConcurrentHashMap,
#### 1.6 JAVA8的 ConcurrentHashMap 为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。
#### 1.7 有没有有顺序的 Map 实现类,如果有,他们是怎么保证有序的。
Hashmap和Hashtable 都不是有序的。
**TreeMap和LinkedHashmap都是有序的。(TreeMap默认是key升序,LinkedHashmap默认是数据插入顺序)**
**TreeMap是基于比较器Comparator来实现有序的。**
**LinkedHashmap是基于链表来实现数据插入有序的。**
#### 1.8 抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么。
之前有写过,点击 [**这里**](https://lixj.fun/archives/java%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0#%E6%8A%BD%E8%B1%A1%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%8C%BA%E5%88%AB) 查看
#### 1.9 继承和聚合的区别在哪。
- 继承
指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识,在设计时一般没有争议性;

- 聚合
聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即has-a的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享;比如计算机与CPU、公司与员工的关系等;表现在代码层面,和关联关系是一致的,只能从语义级别来区分;

在Family类中包含一个Child.并且包含Child的get,set方法,可以只先创建Family然后通过构造器或者get,set给Child赋值
参考:[继承、实现、依赖、关联、聚合、组合的联系与区别](https://blog.csdn.net/kevin_darkelf/article/details/11371353)
#### 2.0 IO 模型有哪些,讲讲你理解的 nio ,他和 bio,aio 的区别是啥,谈谈 reactor 模型。
具体看 [**面试题之IO 模型有哪些,讲讲你理解的 nio ,他和 bio,aio 的区别是啥,谈谈 reactor 模型。**](https://lixj.fun/archives/%E9%9D%A2%E8%AF%95%E9%A2%98%E4%B9%8Bio%E6%A8%A1%E5%9E%8B%E6%9C%89%E5%93%AA%E4%BA%9B%E8%AE%B2%E8%AE%B2%E4%BD%A0%E7%90%86%E8%A7%A3%E7%9A%84nio%E4%BB%96%E5%92%8Cbioaio%E7%9A%84%E5%8C%BA%E5%88%AB%E6%98%AF%E5%95%A5%E8%B0%88%E8%B0%88reactor%E6%A8%A1%E5%9E%8B)
#### 2.1 反射的原理,反射创建类实例的三种方式是什么。
关于反射可以看这个[**反射相关内容**](https://lixj.fun/archives/2020-09-22-10-46-13)
- 方式一
通过对象的getClass方法进行获取。这种方式需要具体的类和该类的对象,以及调用getClass方法。
```
Class class2 = foo1.getClass();
System.out.println(class1==class2);//true'
```
- 方式二
任何数据类型(包括基本数据类型)都具备着一个静态的属性class,通过它可直接获取到该类型对应的Class对象。这种方式要使用具体的类,然后调用类中的静态属性class完成,无需调用方法,性能更好。
```
Class class1 = Foo.class;
```
- 方式三
通过Class.forName()方法获取。这种方式仅需使用类名,就可以获取该类的Class对象,更有利于扩展。
```
Class class3 = null;
try {
class3 = Class.forName("com.imooc.reflect.Foo");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(class2==class3);//true
```
#### 2.2 反射中,Class.forName 和 ClassLoader 区别。
在java中Class.forName()和ClassLoader都可以对类进行加载。
区别:
1. Class.forName除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
2. 而classloader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
看简书文章讲的比较细致,[**反射中,Class.forName和ClassLoader区别**](https://www.jianshu.com/p/2dbfec55c987)
#### 2.3 描述动态代理的几种实现方式,分别说出相应的优缺点。
#### 2.4 动态代理与 cglib 实现的区别。
#### 2.5 为什么CGlib 方式可以对接口实现代理。
#### 2.6 final 的用途。
1. **被final修饰的类不可以被继承**
2. **被final修饰的方法不可以被重写**
3. **被final修饰的变量不可以被改变**
另外还有:
1. 被final修饰的方法,JVM会尝试为之寻求内联,这对于提升Java的效率是非常重要的。因此,假如能确定方法不会被继承,那么尽量将方法定义为final的
2. 被final修饰的常量,在编译阶段会存入调用类的常量池中
#### 2.7 写出三种单例模式实现。
详细的看 [**设计模式之单例模式**](https://lixj.fun/archives/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%8D%95%E4%BE%8B%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F)
#### 2.8 如何在父类中为子类自动完成所有的 hashcode 和 equals实现?这么做有何优劣。
#### 2.9 请结合OO 设计理念,谈谈访问修饰符 public、private、protected、default 在应用设计中的作用。
#### 3.0 深拷贝和浅拷贝区别。
#### 3.1 数组和链表数据结构描述,各自的时间复杂度。
#### 3.2 error和exception 的区别,CheckedException,RuntimeException 的区别。
#### 3.3 请列出5 个运行时异常。
ClassCastException(类转换异常)
IndexOutOfBoundsException(数组越界)
NullPointerException(空指针)
ArrayStoreException(数据存储异常,操作数组时类型不一致)
还有IO操作的BufferOverflowException异常
#### 3.4 在自己的代码中,如果创建一个 java.lang.String 类,这个类是否可以被类加载器加载?为什么。
在《深入理解java虚拟机》一书中有这样一段话,“即使自定义了自己的类加载器,强行使用defineClass()方法去加载一个以‘java.lang’开头的类也不会成功,如果尝试这样做的话,将会收到一个由虚拟机自己抛出的‘java.lang.SecurityException:Prohibited package name:java.lang’异常”。
所以如果创建一个java.lang.String的类,是不能被类加载器加载的。因为虚拟机会抛出异常。
说到类加载,就必须对类加载机制非常熟悉。
类加载使用的是双亲委派模型,当你想要加载一个类的时候,必须先给你的父加载器,它再去想办法加载,如果它不能加载,再告诉我们,我们自己想办法。
所以,在java中java.lang.String肯定在上层的ClassLoader被加载过了,所以你自己写的完全没有机会加载。
#### 3.5 说一说你对 java.lang.Object 对象中 hashCode 和 equals 方法的理解。在什么场景下需要重新实现这两个方法。
#### 3.6 在 jdk1.5 中,引入了泛型,泛型的存在是用来解决什么问题。
泛型主要针对向下转型时所带来的安全隐患,其核心组成是在声明类或接口时,不设置参数或属性的类型。
#### 3.7 这样的a.hashcode() 有什么用,与 a.equals(b)有什么关系。
#### 3.8 有没有可能 2 个不相等的对象有相同的 hashcode。
有可能,两个不相等的对象可能会有相同的 hashcode 值,这就是为什么在hashmap 中会有冲突。相等 hashcode 值的规定只是说如果两个对象相等,必
须有相同的 hashcode 值,但是没有关于不相等对象的任何规定。
#### 3.9 Java 中的 HashSet 内部是如何工作的。
#### 4.0 什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。
#### 4.1 java8 的新特性。
Java8 新增了非常多的特性,我们主要讨论以下几个:
- Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
- 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
- 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
- 新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
- Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
- Date Time API − 加强对日期与时间的处理。
- Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
- Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。
### JVM
#### 4.2 什么情况下会发生栈内存溢出。
#### 4.3 JVM 的内存结构,Eden 和 Survivor 比例。
#### 4.4 JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分Eden和Survivor。
#### 4.5 JVM 中一次完整的 GC流程是怎样的,对象如何晋升到老年代,说说你知道的几种主要的 JVM 参数。
#### 4.6 你知道哪几种垃圾收集器,各自的优缺点,重点讲下 cms和 G1,包括原理,流程,优缺点。
#### 4.7 垃圾回收算法的实现原理。
以前写过的 [**垃圾回收算法**](https://lixj.fun/archives/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95)
#### 4.8 当出现了内存溢出,你怎么排错。
#### 4.9 JVM 内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作内存等。
#### 5.0 简单说说你了解的类加载器,可以打破双亲委派么,怎么打破。
#### 5.1 讲讲JAVA的反射机制。
#### 5.2 你们线上应用的 JVM 参数有哪些。
#### 5.3 g1 和 cms区别,吞吐量优先和响应优先的垃圾收集器选择。
#### 5.4 怎么打出线程栈信息。
### 开源框架
#### 5.5 简单讲讲 tomcat 结构,以及其类加载器流程,线程模型等。
#### 5.6 tomcat如何调优,涉及哪些参数。
[Tomcat 调优及 JVM 参数优化](https://blog.csdn.net/ldx891113/article/details/51735171)
[springboot内置tomcat参数调优](https://lixj.fun/archives/springboot%E5%86%85%E7%BD%AEtomcat%E5%8F%82%E6%95%B0%E8%B0%83%E4%BC%98)
#### 5.7 讲讲 Spring加载流程。
#### 5.8 Spring AOP的实现原理。
#### 5.9 讲讲 Spring事务的传播属性。
#### 6.0 Spring如何管理事务的。
#### 6.1 Spring怎么配置事务(具体说出一些关键的 xml 元 素 )。
#### 6.2 说说你对 Spring 的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop的实现原理,说说 aop 中的几个术语,它们是怎么相互工作的。
#### 6.3 Springmvc 中 DispatcherServlet 初始化过程。
#### 6.4 netty的线程模型,netty如何基于 reactor 模型上实现的。
#### 6.5 为什么选择 netty。
#### 6.6 什么是TCP粘包,拆包。解决方式是什么。
#### 6.7 netty的 fashwheeltimer 的用法,实现原理,是否出现过调用不够准时,怎么解决。
#### 6.8 netty的心跳处理在弱网下怎么办。
#### 6.9 netty的通讯协议是什么样的。
#### 7.0 springmvc 用到的注解,作用是什么,原理。
#### 7.1 springboot 启动机制。

- 1.通过 SpringFactoriesLoader加载 META-INF/spring.factories⽂件,获取并创建 SpringApplicationRunListener对象
- 2.然后由 SpringApplicationRunListener来发出 starting 消息
- 3.创建参数,并配置当前 SpringBoot 应用将要使用的 Environment
- 4.完成之后,依然由 SpringApplicationRunListener来发出 environmentPrepared 消息
- 5.创建 ApplicationContext
- 6.初始化 ApplicationContext,并设置 Environment,加载相关配置等
- 7.由 SpringApplicationRunListener来发出 contextPrepared消息,告知SpringBoot 应用使用的 ApplicationContext已准备OK
- 8.将各种 beans 装载入 ApplicationContext,继续由SpringApplicationRunListener来发出 contextLoaded 消息,告知 SpringBoot 应用使用的 ApplicationContext已装填OK
- 9.refresh ApplicationContext,完成IoC容器可用的最后⼀步
- 10.由 SpringApplicationRunListener来发出 started 消息
- 11.完成最终的程序的启动
- 12.由 SpringApplicationRunListener来发出 running 消息,告知程序已运行起来了
### 操作系统
#### 7.2 Linux 系统下你关注过哪些内核参数,说说你知道的。
这个比较复杂
[Linux内核参数配置](https://www.cnblogs.com/bodhitree/p/5756719.html)
[Linux内核参数优化](https://blog.51cto.com/flandycheng/476769)
#### 7.3 Linux下 IO 模型有几种,各自的含义是什么。
#### 7.4 epoll 和poll 有什么区别。
[面试经典问题---select、poll、epoll之间有什么区别](https://blog.csdn.net/hust_dxxxd/article/details/50906149)
#### 7.5 平时用到哪些 Linux命令。
head、tail、tar、ssh、sftp、telnet、curl、wget、cp、mv、sed、su
cd、ls、grep、find、rm、kill、ps、cat、chown、chmod、
#### 7.6 用一行命令查看文件的最后五行。
tail -5f filename
#### 7.7 用一行命令输出正在运行的 java 进程。
ps -ef|grep java
#### 7.8 介绍下你理解的操作系统中线程切换过程。
#### 7.9 进程和线程的区别。
#### 8.0 top 命令之后有哪些内容,有什么作用。
#### 8.1 线上 CPU爆高,请问你如何找到问题所在。

记录(三)