JAVA 面试题总览
注:更新日期 2017-9-6,JAVA面试题总览下的所有的题目都是由群:329019348 的群主和群友于 整理的,我只写上自己的答案
JAVA 基础
1. JAVA 中的几种基本数据类型是什么,各自占用多少字节。
int 32bit
short 16bit
long 64bit
byte 8bit
char 16bit
float 32bit
double 64bit
boolean 1bit
2. String 类能被继承吗,为什么。
不能,因为Sting是这样定义的:public final class String extends
Object,里边有final关键字,所以不能被继承。 什么样的类不能被继承?
一,在Java中,只要是被定义为final的类,也可以说是被final修饰的类,就是不能被继承的。
二,final是java中的一个关键字,可以用来修饰变量、方法和类。用关键词final修饰的域成为最终域。用关键词final修饰的变量一旦赋值,就不能改变,也称为修饰的标识为常量。如果一个类的域被关键字final所修饰,它的取值在程序的整个执行过程中将不会改变。
三,假如说整个类都是final,就表明自己不希望从这个类继承,或者不答应其他任何人采取这种操作。换言之,出于这样或那样的原因,我们的类肯定不需要进行任何改变;或者出于安全方面的理由,我们不希望进行子类化(子类处理)。
3. String,Stringbuffer,StringBuilder 的区别。
1.如果要操作少量的数据用 = String;
2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder;
3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer;
4.速度上 StringBuilderStringBufferString;
4. ArrayList 和 LinkedList 有什么区别。
1、非线程安全的
2、ArrayList 底层是数组数据结构,查询速度快。
3、LinkedList 底层是双向链表数据结构,增加、删除速度快。
https://blog.youkuaiyun.com/Bin594505536/article/details/79288320
5. 讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数, 字段,当 new 的时候,他们的执行顺序。
• Java中的初始化顺序:
对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是 (静态变量、静态初始化块)(变量、初始化块) 构造器。
JAVA类首次装入时,会对静态成员变量或方法进行一次初始化,但方法不被调用是不会执行的,静态成员变量和静态初始化块级别相同,非静态成员变量和非静态初始化块级别相同。
• 类的实例化顺序: 初始化顺序:先初始化父类的静态代码—初始化子类的静态代码–
(创建实例时,如果不创建实例,则后面的不执行)初始化父类的非静态代码(变量定义等)—初始化父类构造函数—初始化子类非静态代码(变量定义等)—初始化子类构造函数
https://blog.youkuaiyun.com/qq_21315839/article/details/78833646
6. 用过哪些 Map 类,都有什么区别,HashMap 是线程安全的吗,并发下使用的 Map 是什么,他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。
HashMap不安全。
最常用的Map实现类有:HashMap,ConcurrentHashMap(jdk1.8),LinkedHashMap,TreeMap,HashTable;
其中最频繁的是HashMap和ConcurrentHashMap,他们的主要区别是HashMap是非线程安全的。ConcurrentHashMap是线程安全的。
并发下可以使用ConcurrentHashMap和HashTable,他们的主要区别是:
1.ConcurrentHashMap的hash计算公式:(key.hascode()^ (key.hascode() 16)) & 0x7FFFFFFF HashTable的hash计算公式:key.hascode()& 0x7FFFFFFF
7. JAVA8 的 ConcurrentHashMap 为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。
jdk8 放弃了分段锁而是用了Node锁,减低锁的粒度,提高性能,并使用CAS操作来确保Node的一些操作的原子性,取代了锁。
但是ConcurrentHashMap的一些操作使用了synchronized锁,而不是ReentrantLock,虽然说jdk8的synchronized的性能进行了优化,但是我觉得还是使用ReentrantLock锁能更多的提高性能
8. 有没有有顺序的 Map 实现类,如果有,他们是怎么保证有序的。
顺序的 Map 实现类:LinkedHashMap,TreeMap
LinkedHashMap 是基于元素进入集合的顺序或者被访问的先后顺序排序,TreeMap 则是基于元素的固有顺序 (由 Comparator 或者 Comparable 确定)。
9. 抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么。
1.抽象类可以有自己的实现方法,接口在jdk8以后也可以有自己的实现方法(default)
2.抽象类的抽象方法是由非抽象类的子类实现,接口的抽象方法有接口的实现类实现
3.接口不能有私有的方法跟对象,抽象类可以有自己的私有的方法跟对象
类不可以继承多个类,接口可以继承多个接口,类可以实现多个接口
10. 继承和聚合的区别在哪。
继承:指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识,在设计时一般没有争议性;
聚合:聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即has-a的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享;比如计算机与CPU、公司与员工的关系等;表现在代码层面,和关联关系是一致的,只能从语义级别来区分;
11. IO 模型有哪些,讲讲你理解的 nio ,他和 bio 的区别是啥,谈谈 reactor 模型。
BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
reactor模型:反应器模式(事件驱动模式):当一个主体发生改变时,所有的属体都得到通知,类似于观察者模式。
12. 反射的原理,反射创建类实例的三种方式是什么。
反射的原理:如果知道一个类的名称/或者它的一个实例对象, 就能把这个类的所有方法和变量的信息(方法名,变量名,方法,修饰符,类型,方法参数等等所有信息)找出来。
反射创建类实例的三种方式:
1.Class.forName("com.A");
2.new A().getClass();
3.A.class;
13. 反射中,Class.forName 和 ClassLoader 区别 。
class.forName()除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
14. 描述动态代理的几种实现方式,分别说出相应的优缺点。
动态代理有两种实现方式,分别是:jdk动态代理和cglib动态代理
jdk动态代理的前提是目标类必须实现一个接口,代理对象跟目标类实现一个接口,从而避过虚拟机的校验。
cglib动态代理是继承并重写目标类,所以目标类和方法不能被声明成final。
15. 动态代理与 cglib 实现的区别。
动态代理有两种实现方式,分别是:jdk动态代理和cglib动态代理
jdk动态代理的前提是目标类必须实现一个接口,代理对象跟目标类实现一个接口,从而避过虚拟机的校验。
cglib动态代理是继承并重写目标类,所以目标类和方法不能被声明成final。
16. 为什么 CGlib 方式可以对接口实现代理。
cglib动态代理是继承并重写目标类,所以目标类和方法不能被声明成final。而接口是可以被继承的。
### 17. final 的用途。
1.final修饰的对象不能被修改;
2.final修饰的类不能被继承;
3.final修饰的方法不能被重写;
18. 写出三种单例模式实现 。
/**
* 单例模式
*/
public class Singleton {
/**
* 饿汉式 单例模式
* 类在加载时就实例化,
* 提供一个公共的方法获取实例化的类
* 优点:线程安全
* 缺点:类被加载时就实例化,
* 有可能在整个代码周期都没有使用
* 且不会被回收,会一直存在
*/
private static class 饿汉式 {
private static 饿汉式 饿汉式 = new 饿汉式();
private 饿汉式() {
}
static 饿汉式 get饿汉式() {
return 饿汉式;
}
}
/**
* 懒汉式 单例模式
* 只有在真正使用的时候,才实例化
* 优点:只有在真正使用的时候才实例化
* 缺点:线程不安全
*/
private static class 懒汉式 {
private static 懒汉式 懒汉式;
private 懒汉式() {
}
static synchronized 懒汉式 get懒汉式() {
if (null == 懒汉式) {
懒汉式 = new 懒汉式();
}
return 懒汉式;
}
}
/**
* 枚举式 单例模式
* 跟 饿汉式 一样
*/
private enum 枚举式 {
枚举式;
public 枚举式 get枚举式() {
return 枚举式;
}
}
public static void main(String[] args) {
Singleton.懒汉式 懒汉式 = Singleton.懒汉式.get懒汉式();
if (懒汉式 == Singleton.懒汉式.get懒汉式()) {
System.out.println("懒汉式--单例生效");
}
Singleton.饿汉式 饿汉式 = Singleton.饿汉式.get饿汉式();
if (饿汉式 == Singleton.饿汉式.get饿汉式()) {
System.out.println("饿汉式--单例生效");
}
if (枚举式.枚举式.get枚举式() == 枚举式.枚举式.get枚举式()) {
System.out.println("枚举式--单例生效");
}
}
}
19. 如何在父类中为子类自动完成所有的 hashcode 和 equals 实现?这么做有何优劣。
父类的equals不一定满足子类的equals需求。比如所有的对象都继承Object,默认使用的是Object的equals方法,在比较两个对象的时候,是看他们是否指向同一个地址。
但是我们的需求是对象的某个属性相同,就相等了,而默认的equals方法满足不了当前的需求,所以我们要重写equals方法。
如果重写了equals 方法就必须重写hashcode方法,否则就会降低map等集合的索引速度。
20. 请结合 OO 设计理念,谈谈访问修饰符 public、private、protected、default 在应
用设计中的作用。
关键字 | 类内部 | 本包 | 子类 | 外部包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
21. 深拷贝和浅拷贝区别。
浅拷贝只拷贝指针,深拷贝就是拷贝他的值,重新生成的对像。
22. 数组和链表数据结构描述,各自的时间复杂度。
数组是将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素。
链表恰好相反,链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起。
访问数组中第 n 个数据的时间花费是 O(1) 但是要在数组中查找一个指定的数据则是 O(N) 。当向数组中插入或者删除数据的时候,最好的情况是在数组的末尾进行操作,时间复杂度是 O(1) ,但是最坏情况是插入或者删除第一个数据,时间复杂度是 O(N) 。在数组的任意位置插入或者删除数据的时候,后面的数据全部需要移动,移动的数据还是和数据个数有关所以总体的时间复杂度仍然是 O(N) 。
在链表中查找第 n 个数据以及查找指定的数据的时间复杂度是 O(N) ,但是插入和删除数据的时间复杂度是 O(1)
23. error 和 exception 的区别,CheckedException,RuntimeException 的区别。
Error(错误)表示系统级的错误和程序不必处理的异常,是java运行环境中的内部错误或者硬件问题。比如:内存资源不足等。对于这种错误,程序基本无能为力,除了退出运行外别无选择,它是由Java虚拟机抛出的。
Exception(违例)表示需要捕捉或者需要程序进行处理的异常,它处理的是因为程序设计的瑕疵而引起的问题或者在外的输入等引起的一般性问题,是程序必须处理的。
Exception又分为运行时异常,受检查异常。
RuntimeException(运行时异常),表示无法让程序恢复的异常,导致的原因通常是因为执行了错误的操作,建议终止程序,因此,编译器不检查这些异常。
CheckedException(受检查异常),是表示程序可以处理的异常,也即表示程序可以修复(由程序自己接受异常并且做出处理), 所以称之为受检查异常。
24. 请列出 5 个运行时异常。
NullPointerException
IndexOutOfBoundsException
ClassCastException
ArrayStoreException
BufferOverflowException
25. 在自己的代码中,如果创建一个 java.lang.String 类,这个类是否可以被类加载器加载?为什么。
不可以,双亲委派模式会保证父类加载器先加载类,就是BootStrap(启动类)加载器加载jdk里面的java.lang.String类,而自定义的java.lang.String类永远不会被加载到
26. 说一说你对 java.lang.Object 对象中 hashCode 和 equals 方法的理解。在什么场景
下需要重新实现这两个方法。
父类的equals不一定满足子类的equals需求。比如所有的对象都继承Object,默认使用的是Object的equals方法,在比较两个对象的时候,是看他们是否指向同一个地址。
但是我们的需求是对象的某个属性相同,就相等了,而默认的equals方法满足不了当前的需求,所以我们要重写equals方法。
如果重写了equals 方法就必须重写hashcode方法,否则就会降低map等集合的索引速度。
27. 在 jdk1.5 中,引入了泛型,泛型的存在是用来解决什么问题。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
28. 这样的 a.hashcode() 有什么用,与 a.equals(b)有什么关系。
hashcode
hashcode()方法提供了对象的hashCode值,是一个native方法,返回的默认值与System.identityHashCode(obj)一致。
通常这个值是对象头部的一部分二进制位组成的数字,具有一定的标识对象的意义存在,但绝不定于地址。
作用是:用一个数字来标识对象。比如在HashMap、HashSet等类似的集合类中,如果用某个对象本身作为Key,即要基于这个对象实现Hash的写入和查找,那么对象本身如何实现这个呢?就是基于hashcode这样一个数字来完成的,只有数字才能完成计算和对比操作。
hashcode是否唯一
hashcode只能说是标识对象,在hash算法中可以将对象相对离散开,这样就可以在查找数据的时候根据这个key快速缩小数据的范围,但hashcode不一定是唯一的,所以hash算法中定位到具体的链表后,需要循环链表,然后通过equals方法来对比Key是否是一样的。
equals与hashcode的关系
equals相等两个对象,则hashcode一定要相等。但是hashcode相等的两个对象不一定equals相等。
https://segmentfault.com/a/1190000004520827
29. 有没有可能 2 个不相等的对象有相同的 hashcode。
有可能,最简单的方法,百分百实现的方式就是重写hascode();
30. Java 中的 HashSet 内部是如何工作的。
public HashSet() {
map = new HashMap<();
}
默认使用的是HaseMap;
31. 什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。
序列化是一种用来处理对象流的机制 ,所谓对象流就是将对象的内容进行流化。
序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流;
32. java8 的新特性。
https://blog.youkuaiyun.com/yczz/article/details/50896975
JVM 知识
1. 什么情况下会发生栈内存溢出。
a.方法创建了一个很大的对象,如List,Array
b.是否产生了循环调用、死循环
c.是否引用了较大的全局变量
2. JVM 的内存结构,Eden 和 Survivor 比例。
JVM内存结构分为两种类型:
线程安全:虚拟机栈、本地方法栈、程序计数器
非线程安全:堆,方法区
虚拟机栈:每个方法被执行时,都会在内存中创建一个空间用来存储方法中的局部变量,方法的出入口等信息。
本地方法栈:每个本地方法被执行时,都会创建一个内存空间,用来存储本地方法中的局部变量,方法的出入口等信息。
程序计数器:是当前程序所执行的class文件的行号指示器,通过改变行号来决定下一段要执行的字节码指令,跳转,循环,异常处理
堆:每一个对象的创建跟分配都是在堆上面进行的,堆分为新生代,老生代。新生代有一个Eden和两个Survivor组成,默认比例是8:2。也可以使用-XXSurvivorRatio来改变百分比。
方法区:用来存放类的版本,类的方法还有static修饰的对象等信息。
3. JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor。
GC流程图
对象晋升老生代一共有三个可能:
1.当对象达到成年,经历过15次GC(默认15次,可配置),对象就晋升为老生代
2.大的对象会直接在老生代创建
3.新生代跟幸存区内存不足时,对象可能晋升到老生代
jvm参数:
-Xms:初始堆大小
-Xmx:堆最大内存
-Xss:栈内存
-XX:PermSize 初始永久带内存
-XX:MaxPermSize 最大永久带内存
4. JVM 中一次完整的 GC 流程是怎样的,对象如何晋升到老年代,说说你知道的几种主要的JVM 参数。
串行垃圾收集器:收集时间长,停顿时间久
并发垃圾收集器:碎片空间多
CMS:并发标记清除。他的主要步骤有:初始收集,并发标记,重新标记,并发清除(删除),重置
G1:主要步骤:初始标记,并发标记,重新标记,复制清除(整理)
CMS的缺点是对cpu的要求比较高。G1是将内存化成了多块,所有对内段的大小有很大的要求
CMS是清除,所以会存在很多的内存碎片。G1是整理,所以碎片空间较小
5. 你知道哪几种垃圾收集器,各自的优缺点,重点讲下 cms 和 G1,包括原理,流程,优缺点。
常用的垃圾回收算法有两种: 引用计数和可达性分析
引用计数是增加一个字段来标识当前的引用次数,引用计数为0的就是可以GC的。但是引用计数不能解决循环引用的问题
可达性分析:就是通过一系列GC ROOT的对象作为起点,向下搜索,搜索所有没有与当前对象GC ROOT 有引用关系的对象。这些对象就是可以GC的。
6. 垃圾回收算法的实现原理。
1、堆内存可以分成几块?
2、每块使用什么方法进行垃圾回收?什么时候该回收?
3、内存如何分配以及回收策略?
4、java拥有垃圾回收机制,为什么还会发生内存泄漏?
1、堆内存可以分成三块:新生代、老年代和永久代。
2、分代收集算法:标记清除、复制、标记整理算法
新生代的存活周期比较短,适合使用复制算法。
新生代:eden区域和survive0和survive1(比例是8:1:1),其中eden区域存放刚建立的对象,当eden区域内存用完时会发生GC,存活的对象存入survive0,清空eden区域。当survive0的内存也用完时,继续进行GC,保留eden和survive0区域的存活对象,将其复制到survive1区域,清空eden和survive0区域。交换survive0和survive1的角色,一直循环,直到某个survive区域不足以存放另一个survive和eden区域的存活对象时,将这个survive的对象转移到老年代区域。由于新生代对象存活时间短,需要复制的对象少,所以适合使用复制算法,;
老年代:老年代的区域和新生代的区域为2:1。当老年代内存也满了,进行一次全局回收(fullGC)。老年代对象存活率高,使用的回收方法是标记整理算法,标记存活的对象,将其移动到一端(与标记清除算法的区别),回收不存活对象。
永久代:存放静态文件,如静态类和静态方法。但是JDK1.8用元空间取代了永久代。储存在永久代的数据(方法区的数据)一直在转移,转移到堆中。Jdk1.6出现永久代益处,jdk1.7和jdk1.8出现堆溢出
3、内存分配策略
对象优先分配在eden区域;
大对象直接进入老年代;
通过引用计数算法,长期存活对象进入老年代(如对象在新生代中经历15次回收依然存在);
4、场景
使用hashmap、vector等集合类。假设初始化vector,在vector中添加对象,将对象置为null之后,创建的对象仍然不能被回收,因为此时对象有vector引用。
各种资源链接后没有关闭。
监听器的使用
7. 当出现了内存溢出,你怎么排错。
1.首先控制台查看错误日志
2.然后使用jdk自带的jvisualvm工具查看系统的堆栈日志
3.定位出内存溢出的空间:堆,栈还是永久代(jdk8以后不会出现永久代的内存溢出)。
4.如果是堆内存溢出,看是否创建了超大的对象
5.如果是栈内存溢出,看是否创建了超大的对象,或者产生了死循环。
8. JVM 内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作内存等。
重排序:jvm虚拟机允许在不影响代码最终结果的情况下,可以乱序执行。
内存屏障:可以阻挡编译器的优化,也可以阻挡处理器的优化
happens-before原则:
1:一个线程的A操作总是在B之前,那多线程的A操作肯定实在B之前。
2:monitor 再加锁的情况下,持有锁的肯定先执行。
3:volatile修饰的情况下,写先于读发生
4:线程启动在一起之前 strat
5:线程死亡在一切之后 end
6:线程操作在一切线程中断之前
7:一个对象构造函数的结束都该对象的finalizer的开始之前
8:传递性,如果A肯定在B之前,B肯定在C之前,那A肯定是在C之前。
主内存:所有线程共享的内存空间
工作内存:每个线程特有的内存空间
9. 简单说说你了解的类加载器,可以打破双亲委派么,怎么打破。
类加载器主要分为:引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader),系统类加载器(App ClassLoader)和自定义加载器(Custom ClassLoader)
双亲委派:双亲委派模式会保证父类加载器先加载类
10. 讲讲 JAVA 的反射机制。
在运行是,对于任意一个类,都能知道当前类的方法和属性,对于任意一个类,都能调用类的方法和属性,着用动态加载机制就是Java的反射机制。
11. 你们线上应用的 JVM 参数有哪些。
-XX:PermSize=128M
-XX:MaxPermSize=512m
-XX:PermSize=128M
-XX:MaxPermSize=512m
-Xms512m
-Xmx1024m
-XX:PermSize=640m
-XX:MaxPermSize=1280m
-XX:NewSize=64m
-XX:MaxNewSize=256m
-verbose:gc
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
12. g1 和 cms 区别,吞吐量优先和响应优先的垃圾收集器选择。
CMS:并发标记清除。他的主要步骤有:初始收集,并发标记,重新标记,并发清除(删除),重置
G1:主要步骤:初始标记,并发标记,重新标记,复制清除(整理)
CMS的缺点是对cpu的要求比较高。G1是将内存化成了多块,所有对内段的大小有很大的要求
CMS是清除,所以会存在很多的内存碎片。G1是整理,所以碎片空间较小
吞吐量优先:G1
响应优先:CMS
13. 怎么打出线程栈信息。
StackTraceElement[] elements = (new Throwable()).getStackTrace();
StringBuffer buf = new StringBuffer();
buf.append("Stack for " + cls.getName() + ":");
for(int i=0; i<elements.length; i++) {
buf.append("/n "
+ elements[i].getClassName()
+ "."
+ elements[i].getMethodName()
+ "("
+ elements[i].getFileName()
+ ":"
+ elements[i].getLineNumber()
+ ")");
}
System.out.println(buf.toString());
14. 请解释如下 jvm 参数的含义:-server -Xms512m -Xmx512m - Xss1024K-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly。
-server:服务器模式
-Xms512m :初始堆空间
-Xmx512m:最大堆空间
-Xss1024K :栈空间
-XX:PermSize=256m :初始永久带空间
-XX:MaxPermSize=512m :最大永久带空间
-XX:MaxTenuringThreshold=20 :对象的生命周期
XX:CMSInitiatingOccupancyFraction=80 :老年代的内存在使用到70%的时候,就开始启动CMS了
-XX:+UseCMSInitiatingOccupancyOnly:它就只会按照你设置的比率来启动CMS GC了
开源框架 知识
1. 简单讲讲 tomcat 结构,以及其类加载器流程,线程模型等。
https://blog.youkuaiyun.com/zhengzhaoyang122/article/details/82183625
2. tomcat 如何调优,涉及哪些参数 。
https://blog.youkuaiyun.com/zhengzhaoyang122/article/details/82183625
3. 讲讲 Spring 加载流程。
spring加载流程
1.监听器加载spring
2.加载配置文件
3.工厂生产实例化对象
4.放入ServletContext
springmvc加载流程
1.Servlet加载(监听器之后即执行)Servlet的init()
2.加载配置文件
3.从ServletContext拿到spring初始化springmvc相关对象
4.放入ServletContext
springmvc执行流程
1.用户请求到DispatcherServlet
2.DispatcherServlet查找HandlerMapping请求Handler并返回查找结果
3.DispatcherServlet调用HandlerAdapter执行Handler并返回执行结果
4.DispatcherServlet调用ResolverView生成视图并返回视图
5.DispatcherServlet返回给用户
4. 讲讲 Spring 事务的传播属性。
我们用Spring时,很多时候都会用到他的声明式事务,简单的在配置文件中进行一些规则配置,利用Spring的AOP功能就能轻松搞定事务问题;这里面就涉及到一个事务的传播属性问题【Propagation】,他在TransactionDefinition接口中定义,有兴趣可以看看src,共有7种选项可用:
PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED:支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。
5. Spring 如何管理事务的。
Spring事务管理主要包括3个接口,Spring事务主要由以下三个共同完成的:
1)、PlatformTransactionManager:事务管理器,主要用于平台相关事务的管理。主要包括三个方法:①、commit:事务提交。②、rollback:事务回滚。③、getTransaction:获取事务状态。
2)、TransacitonDefinition:事务定义信息,用来定义事务相关属性,给事务管理器PlatformTransactionManager使用这个接口有下面四个主要方法:①、getIsolationLevel:获取隔离级别。②、getPropagationBehavior:获取传播行为。③、getTimeout获取超时时间。④、isReadOnly:是否只读(保存、更新、删除时属性变为false--可读写,查询时为true--只读)事务管理器能够根据这个返回值进行优化,这些事务的配置信息,都可以通过配置文件进行配置。
3)、TransationStatus:事务具体运行状态,事务管理过程中,每个时间点事务的状态信息。例如:①、hasSavepoint():返回这个事务内部是否包含一个保存点。②、isCompleted():返回该事务是否已完成,也就是说,是否已经提交或回滚。③、isNewTransaction():判断当前事务是否是一个新事务
https://blog.youkuaiyun.com/qq_31582127/article/details/85228169
6. Spring 怎么配置事务(具体说出一些关键的 xml 元素)。
配置事务的方法有两种:1)、基于XML的事务配置。2)、基于注解方式的事务配置。
铺垫:1)、spring的事务管理是通过Aop的方式来实现。2)、声明式事务是spring对事务管理的最常用的方式,因为这种方式对代码的影响最小,因此也就符合非侵入式的轻量级的容器的概念;
7. 说说你对 Spring 的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop 的实现原理,说说 aop 中的几个术语,它们是怎么相互工作的。
Spring的理解:1)、Spring是一个开源框架,主要是为简化企业级应用开发而生。可以实现EJB可以实现的功能,Spring是一个IOC和AOP容器框架。
♧ 控制反转(IOC):Spring容器使用了工厂模式为我们创建了所需要的对象,我们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控制反转的思想。
♧ 依赖注入(DI):Spring使用Java Bean对象的Set方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程就是依赖注入的基本思想。
♧ 面向切面编程(AOP):在面向对象编程(OOP)思想中,我们将事物纵向抽象成一个个的对象。而在面向切面编程中,我们将一个个对象某些类似的方面横向抽象成一个切面,对这个切面进行一些如权限验证,事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。
2)、在Spring中,所有管理的都是JavaBean对象,而BeanFactory和ApplicationContext就是Spring框架的那个IOC容器,现在一般使用ApplicationContext,其不但包括了BeanFactory的作用,同时还进行了更多的扩展。
✔ 非单例注入原理:在大部分情况下,容器中的bean都是singleton类型的。如果一个singleton bean要引用另外一个singleton bean或者一个非singleton bean要引用另外一个非singleton,通常情况下将一个bean定义为另一个bean的property值就可以了。不过对于具有不同生命周期的bean来说这样做就会有问题了,比如在调用一个singleton类型bean A的某个方法时,需要引用另一个非singleton(prototype)类型的bean B,对于bean A来说,容器只会创建一次,这样就没法在需要的时候每次让容器为bean A提供一个新的的bean B实例。8. Springmvc 中 DispatcherServlet 初始化过程。
9. netty 的线程模型 netty 的线程模型 netty 如何基于 reactor 模型上实现的。
10. netty 的 fashwheeltimer 的用法,实现原理,是否出现过调用不够准时,怎么解决。
11. netty 的心跳处理在弱网下怎么办。
12. netty 的通讯协议是什么样的。
13. springmvc 用到的注解,作用是什么,原理。
14. springboot 启动机制。
http://www.cnblogs.com/zheting/p/6707035.html
操作系统
1. Linux 系统下你关注过哪些内核参数,说说你知道的。
2. Linux 下 IO 模型有几种,各自的含义是什么。
3. epoll 和 poll 有什么区别。
4. 平时用到哪些 Linux 命令。
5. 用一行命令查看文件的最后五行。
6. 用一行命令输出正在运行的 java 进程。
7. 介绍下你理解的操作系统中线程切换过程。
8. 进程和线程的区别。
9. top 命令之后有哪些内容,有什么作用。
多线程
1. 多线程的几种实现方式,什么是线程安全。
2. volatile 的原理,作用,能代替锁么。
volatile 关键字的作用 保证内存的可见性 防止指令重排
注意:volatile 并不保证原子性
可见性原理
volatile 保证可见性的原理是在每次访问变量时都会进行一次刷新,因此每次访问都是主内存中最新的版本。所以 volatile 关键字的作用之一就是保证变量修改的实时可见性。
一个非常重要的问题,是每个学习、应用多线程的Java程序员都必须掌握的。理解volatile关键字的作用的前提是要理解Java内存模型,这里就不讲Java内存模型了,可以参见第31点,volatile关键字的作用主要有两个:
(1)多线程主要围绕可见性和原子性两个特性而展开,使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,一定是最新的数据
(2)代码底层执行不像我们看到的高级语言----Java程序这么简单,它的执行是Java代码--字节码--根据字节码执行对应的C/C++代码--C/C++代码被编译成汇编语言--和硬件电路交互,现实中,为了获取更好的性能JVM可能会对指令进行重排序,多线程下可能会出现一些意想不到的问题。使用volatile则会对禁止语义重排序,当然这也一定程度上降低了代码执行效率
从实践角度而言,volatile的一个重要作用就是和CAS结合,保证了原子性,详细的可以参见java.util.concurrent.atomic包下的类,比如AtomicInteger。
3. 画一个线程的生命周期状态图。
4. sleep 和 wait 的区别。
功能差不多,都用来进行线程控制,他们最大本质的区别是:sleep()不释放同步锁,wait()释放同步缩.
还有用法的上的不同是:sleep(milliseconds)可以用时间指定来使他自动醒过来,如果时间不到你只能调用interreput()来强行打断;wait()可以用notify()直接唤起.
5. sleep 和 sleep(0)的区别。
调用sleep(0) 的时候, 如果调度器中不存在优先级 = 该线程优先级的情况下,该线程将会继续运行。否则,线程会被放入到其优先级相应的队列尾部。也就是说我们调用sleep(0) 时 ,通常会让程序里其他线程获得更多的运行时间。