JAVA常见面试题(2019-04-05)
(一)Java基础
1.Java语言的特性
答:面向对象,安全性,多线程,跨平台
2.Java的跨平台原理(说说Java的可移植性)
答:①java中.java源代码经过java虚拟机编译以后变成了.class字节码文件,
②java的技术大牛和伟人们,对应不同的系统平台开发了不同的JVM虚拟机
③字节码文件在不同虚拟机中运行是一样的,从而达到了跨平台的效果
3.JDK 和 JRE ,JVM有什么区别?
答:①JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的 开发环境和运行环境。
②JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的 运行提供了所需环境。
③JVM:是一种用于计算机设备的规范,是一个虚拟出来的计算机,它的作 用是仿真模仿真实计算机的各种功能来实现java代码运行。
4.请说说==和equals的区别
答:== 的作用
- 基本类型:比较的是值是否相同。
- 引用类型:比较的是引用地址是否相同。
equals 的作用:比较的都是值是否相同。但是只能在对象中使用
5.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
答:①不一定,比较两个对象是否相等时,先比较hashcode()方法,再相等的 情况下,比较equals(),如果都相等这两个对象一定相等
②如果equals()相等,这hasCode一定相等
6.this,super,static,final这几个关键字的作用
答:①this表示当前正在调用方法的对象,
this的作用:
- 区分成员字段与局部变量的二义性
- 在构造方法的第一句调用自己的构造方法
- 作为返回值
- 作为参数传递
- 调用自己的方法
②super表示持有当前子类对象调用父类类型的对象;
super的作用:
1,super调用非私有字段及方法
2,super区分子类与父类字段二义性
3,在子类的构造方法第一句调用父类构造方法
4,特殊情况;子类构造方法中,如果没有显示的写出对父类构造方法 的调用,会隐式的调用父类的无参数构造方法!
③Static:修饰字段的作用:该字段被该类的全部对象所共享;访问:类名. 字段名
修饰普通方法作用:该类的方法不能被子类所覆写;访问:类名.方 法名
修饰内部类作用:该类只能在外部类的成员;访问:当做一个普通 的字段和方 法进行访问。
④Final:修饰成员字段和局部变量的作用:
成员变量会变成全局变量,它的值将不会改变。会大写
修饰方法作用:修饰方法:该方法不能被覆写
private修饰的方法,私有的不能被覆写
static修饰的方法,不能被覆写
final修饰的方法不能被覆写!
修饰类作用:该类将变成最终类,不会被继承没有子类。
父类方法不满足子类需求的时候需要方法复写!
7.请说说基本数据类型和引用数据类型的区别(int和integer)
答:①Integer是int提供的封装类,而int是Java的基本数据类型
②Integer默认值是null,而int默认值是0
③声明为Integer的变量需要实例化,而声明为int的变量不需要实例化
④Integer类型变量里面存储的是对象的地址引用,而int是直接存储的变量数值
8.请说说String/StringBuffer/StringBuilder 他们的区别和相同点
答:不同的:
①首先最大的区别是Stirng长度是不可变的,而StringBuffer和StringBuilder是可变的
②其次String和StringBuffer是线程安全的,而StringBuilder是非线程安全的,但是 执行的效率高
相同点:
①都是类,都表示字符序列都实现CharSequence (字符序列的接口)是字符串容 器
②这三个类没有直接或者间接的继承关系
- java 中的 Math.round(-1.5) 等于多少?
答:向下取舍:等于-1,就是靠近中间的那个数。
- String str="i"与 String str=new String(“i”)一样吗?
答:不一样:第一种方式,会先去内存中查找有没有“i”的字符串,有的话就直接引用地址,没有再创建对象,而第二种会直接创建对象,每次都是新对象,效率会慢点。
11.有哪些方式可以把一个int转成一个字符串
答:①Stirng.valueOf(int i)调用Sting类中的一个方法
②Integer.toString(i)在Inlerger类调用toString()方法
③i+""字符串拼接
12.如何将字符串反转?
答:1.将字符从后往前的append起来(定义一个空字符数组,然后从后面遍历, 再字符串拼接)
- 取得当前字符并和之前的字符append起来
- String 类的常用方法都有那些?
答: 字符串的长度leagth(),求字符串中某一字符的值,charAt(索引),截取小段字符串substring(开始索引,结束索引)
字符串比较
1)public int compareTo(String anotherString)//该方法是对字符串内容按字典顺序进行大小比较,通过返回的整数值指明当前字符串与参数字符串的大小关系。若当前对象比参数大则返回正整数,反之返回负整数,相等返回0。
2)public int compareToIgnore(String anotherString)//与compareTo方法相似,但忽略大小写。
3)public boolean equals(Object anotherObject)//比较当前字符串和参数字符串,在两个字符串相等的时候返回true,否则返回false。
4)public boolean equalsIgnoreCase(String anotherString)//与equals方法相似,但忽略大小写
字符串中字符的替换
1)public String replace(char oldChar, char newChar)//用字符newChar替换当前字符串中所有的oldChar字符,并返回一个新的字符串。
2)public String replaceFirst(String regex, String replacement)//该方法用字符replacement的内容替换当前字符串中遇到的第一个和字符串regex相匹配的子串,应将新的字符串返回。
3)public String replaceAll(String regex, String replacement)//该方法用字符replacement的内容替换当前字符串中遇到的所有和字符串regex相匹配的子串,应将新的字符串返回。
字符串中字符的大小写转换
1)public String toLowerCase()//返回将当前字符串中所有字符转换成小写后的新串
2)public String toUpperCase()//返回将当前字符串中所有字符转换成大写后的新串
14.常见的数组排序的方式有哪些?你比较喜欢哪种,为什么?
答:冒泡排序、直接选择顺序、反转顺序
比较喜欢直接选择排序,因为时间短易上
15.抽象类必须要有抽象方法吗
答:如果这个类已经申明为抽象类,这个类可以没有抽象方法,但是这个类不能实例化。如果这个类没有申明为抽象类,但是有抽象方法,那么必须申明为抽象类,否则编译就不能通过。
16.普通类和抽象类有哪些区别?
答:1.普通类可以实例化对象,抽象类不能
2.抽象类除开有普通类有的成员外,还可以有抽象方法
3.抽象类如果被继承的话,那么子类必须覆写抽象方法,前提是抽象类有抽 象方法。
17.抽象类能使用 final 修饰或者private吗?
答:抽象类是必须被继承的,所有final和abstract不能同时使用,而private修饰以后这个类就变为内部类,它是可以被外部类访问到的,所有private和abstract可以同时使用。
18.接口和抽象类有什么区别?
答:共同点:
- 都不能实例化对象,
- 抽象类中方法子类必须实现全部,不然子类也是抽象类;接口也是,实现类不能全部实现,实现类也是抽象类。
不同点:
- 接口中只能声明方法,而且是公共的,抽象类中,既可以抽象方法声明,也可以有普通方法的实现
- 接口中变量只能是公共的静态常量,抽象类中变量是普通变量
- 接口可以多继承,类只能是单继承
- 接口是一种设计,抽象类是重构
19.java 中 IO 流分为几种?
答:输入流:input--FileInputStream--FileReader--PrintStream把数据从磁盘读入到内存中
输出流:output--FileOutputSrteam--FileWriter--PrintStream数据从内存中读入到磁盘中
输入缓存流:BufferedInputStream--BufferedReader
输出缓存流:BufferedOutputStream--BufferedWriter
字节流转字符流(从磁盘到内存):InputStreamReader
字符流转字节流(从内存到磁盘):OutputStreamWriter
缓存流:可以包装输入输出流
转换流:可以包装缓存流和输入输出流
20.BIO、NIO、AIO 有什么区别?
答:同步阻塞IO(BIO)
我们熟知的Socket编程就是BIO,一个socket连接一个处理线程(这个线程负责这个Socket连接的一系列数据传输操作)。阻塞的原因在于:操作系统允许的线程数量是有限的,多个socket申请与服务端建立连接时,服务端不能提供相应数量的处理线程,没有分配到处理线程的连接就会阻塞等待或被拒绝
同步非阻塞IO(NIO)
New IO是对BIO的改进,基于Reactor模型。我们知道,一个socket连接只有在特点时候才会发生数据传输IO操作,大部分时间这个“数据通道”是空闲的,但还是占用着线程。NIO作出的改进就是“一个请求一个线程”,在连接到服务端的众多socket中,只有需要进行IO操作的才能获取服务端的处理线程进行IO。这样就不会因为线程不够用而限制了socket的接入。客户端的socket连接到服务端时,就会在事件分离器注册一个 IO请求事件 和 IO 事件处理器。在该连接发生IO请求时,IO事件处理器就会启动一个线程来处理这个IO请求,不断尝试获取系统的IO的使用权限,一旦成功(即:可以进行IO),则通知这个socket进行IO数据传输。
异步阻塞IO(AIO)
NIO是同步的IO,是因为程序需要IO操作时,必须获得了IO权限后亲自进行IO操作才能进行下一步操作。AIO是对NIO的改进(所以AIO又叫NIO.2),它是基于Proactor模型的。每个socket连接在事件分离器注册 IO完成事件 和 IO完成事件处理器。程序需要进行IO时,向分离器发出IO请求并把所用的Buffer区域告知分离器,分离器通知操作系统进行IO操作,操作系统自己不断尝试获取IO权限并进行IO操作(数据保存在Buffer区),操作完成后通知分离器;分离器检测到 IO完成事件,则激活 IO完成事件处理器,处理器会通知程序说“IO已完成”,程序知道后就直接从Buffer区进行数据的读写。
20.TCP与UDP的区别
答:① 基于连接与无连接 ;对系统资源的要求(TCP较多,UDP少): UDP程序结构较简 单 ; 流模式与数据报(Datagram)模式 ;
②UDP和TCP协议的主要区别是两者在如何实现信息的可靠传递方面不同。
TCP协议中包含了专门的传递保证机制,当数据接收方收到发送方传来的信息时,会自 动向发送方发出确认消息;发送方只有在接收到该确认消息之后才继续传送其它信息, 否则将一直等待直到收到确认信息为止。
20.final, finally, finalize的区别?
答:①final是修饰符号,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。内部类要访问局部变量,局部变量必须定义成final类型
②finally是异常处理结构的一部分,表示总是会执行
③finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。该方法最多只被调用一次,但JVM不保证此方法总被调用
21.Files的常用方法都有哪些?
答:
boolean createNewFile();创建文件。当File对象对应的是文件且不存在时,可以调用此方法创建
boolean mkdirs();创建多级目录。当File对象对应的是目录且不存在时,可以调用此方法创建,创建目录后,创建文件。
boolean exists();判断File对象对应的文件或目录是否存在
boolean isFile();boolean isDirectory();判断File对象对应的是文件还是目录,前提是这个文件存在,不然都返回false
File[] listFiles();获得File对象对应的目录下的所有文件和目录,要求File对象对应的是目录且必须存在,不能是文件,也不能不存在,否则得到的会是nul
String getAbsolutePath();返回File对象的绝对路径
long length();返回File对象对应的文件或者目录的大小,以byte为单位
Boolean delete()删除File对象指定的文件或者目录,删除成功返回true,不成功返回false。删除文件时,如果文件正在被使用,则不会删除成功。删除目录时,目录是空的才能成功。
21.谈谈你对面向对象的三大特征的理解
答:封装 使代码更加的安全,控制代码的访问权限。
提供字段私有化
提供get set 方法可以(后期控制逻辑,和框架注入值和取值都是通过这)
提供一个无参构造方法
为什么用写一个JavaBean的无参构造方法,因为在有一个子类来调用这个JavaBean类的话,当这个子类的中构造方法没有显示来调用JavaBean父类的构造方法的时候,那么会隐式的调用JavaBean中的无参构造方法。
继承:将很多类中的共同的部分,提炼出来形成一个父类,其他类通过继承的方式来实现代码的复用,提高编程的效率。
语法:B extends A{ 继承的东西}
继承哪些东西:非私有化字段和方法。
特点:一个子类只能有一个父类
一个类如果没哟显示写出继承的关系,那么一定继承一个Object类。
可以多层继承关系。
多态:①一个事物的多种形态
②一个事物在编译时和运行时的类型不一致
③一个子类的对象装在父类的类型里面,在编译时是父类的类型,在运行时是子类的类型。
22.单例模式的产生方式。
答:①单例模式是为了避免重复产生实例对象而设计的一种模式,该模式只会产生一个 实例
单例模式:整个程序只能允许有唯一的对象。
懒汉:对外提供一个公共的获取对象的方法,当调用者想要对象的时候我再生成,但是 会有线程安全问题,可以用静态代码块或者Synchnorized 解决
饿汉:直接在我内部生成一个静态对象,要的时候就调用get方法给你,不会有线 程安全问题
枚举:枚举本身就是一个单例
②语法:
三步骤:
- 私有化静态成员变量
- 私有化构造方法
- 公共的静态方法getInstance
23.public,private,protected及default的区别?
修饰符 同类 同包 子类 其他
public √ √ √ √
protected √ √ √ ×
default √ √ × ×
private √ × × ×
24.说说你对JavaBean的理解(其实就是封装)
Javabean这个概念来至于面向对象三大特征之一的封装里面,是一个符合一定规范的java类,是可以重复使用的组件。
特点:类名必须用public修饰,必须要有一个public修饰的无参构造器,所有的字段都用private修饰,并且每个字段都想外提供一对set、geter方法
25.重写和重载的区别
①方法重写
相同方法名,相同返回值,相同参数列表,子类写了一个和父类一模一样的方法。重写方法的修饰符不能比父类方法严格,也不能是private。重写是运行时活动
②方法重载(overlading)
方法名相同、参数不同(类型不同或者顺序不同)、与返回值无关。 重载是编译时活动
来了也是一样。
26.值传递与引用传递
①值传递:
在方法调用的时候,实参将自己的一份拷贝赋给形参,在方法内,对该参数值的修改不 影响原来实参。
②引用传递
在方法调用的时候,实参将自己的地址传递的形参,此时方法内对该参数值的改变,就 是对该实参的实际操作。(基本类型的数组是引用传递)
java中可以看成都是值传递
(二)容器
28.java中容器有哪些?
答:变量,数组,List集合,底层是数组实现,Set无序,元素不可以重复,Map无序,元素不可以重复。其实大的来说类也是容器。
29.Collection 和 Collections 有什么区别?
答:①Collection是集合的接口,提供对集合对象基本操作的通用接口方法。这个接口在java类中有很多实现,意义是各种具体的集合实例提供统一的操作方法。
②Collections是包装类,是一个集合的工具类,提供操作集合的静态方法,无法实例化。
30.Iterator和Iterable的区别和作用
答:①Iterable中封装了Iterator接口,只要实现了Iterable接口的类,就可以调用Iterable 里面的方法,获得Iterator迭代器,Iterable和Iterator都是接口
②Iterator是迭代器类,而Iterable是为了只要实现该接口就可以使用foreach,进行迭 代
③集合Collection、List、Set都是Iterable的实现类,所以他们及其他们的子类都可以使 用foreach进行迭代。
④Iterator中和核心的方法next(),hasnext(),remove(),都是依赖当前位置,如果这些集合 直接实现Iterator,则必须包括当前迭代位置的指针。当集合在方法间进行传递的时候, 由于当前位置不可知,所以next()之后的值,也不可知。而当实现Iterable则不然,每 次调用都返回一个从头开始的迭代器,各个迭代器之间互不影响。
31.Comparable和Comparator的区别
答:①如果一个类实现了Comparable接口,可以认为这个类的对象具有自然排序的能力(本 质就是这个对象可以调用比较的方法compareTo),这种比较和排序的规则就是自然排 序
②Comparator 是一个比较器的接口(标准),必须得有进行比较的方法 :compare(Object o1,Object 自定义一个类实现Comparator接口,其中写比较规则 ---》 比较器的模板
我们现在需要的是一个具体的比较器对象
32.Iterator 和 ListIterator 有什么区别?
答:1. ListIterator有add()方法,可以向List中添加对象,而Iterator不能
2. ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。
3. ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
4. 都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。
33.List、Set、Map 之间的区别是什么?
答:①List接口元素是线性方式存放,可以重复元素;
ArrayList:长度可变的数组,特点底层是有序的数组,所以查询数据速度快,删除和插入 数据慢。
LinkList:底层采用链表数据结构,特点是查询数据慢,删除和插入数据快。
②Set接口对象不按特定方式排序,也没有重复元素;
HashSet:底层是采用hash表存取集合对象,特点存取速度都快。
TreeSet:底层是采用树结构存取集合对象,特点存取速度也快。
③Map接口每个元素都包含键对象和值对象,无重复元素;
HashMap:底层是散列表实现,特点插入和查询速度是固定的
34.HashMap 和 Hashtable 有什么区别?
答:①继承不同,都是实现Map
②HashMap不同步线程不安全,而HashTable刚好相反
③HashTable不允许key和value出现null,而HashMap可以key为null
④遍历方式不同
35.ArrayList和LinkedList以及HashSet TreeSet 底层的实现原理和两种的区别
答:1,ArrayList和LinkedList的区别和底层的原理
①ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
②对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针,效率 较低
③对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数 据。移动的次数太多
④默认情况下就是顺序存储,先添加的元素在前面,后添加的元素在后面,不同的是ArrayList底层是通过数组来维护,LinkedList底层是通过链表来维护。这两种方式都可以重复添加相同的元素,根据应用情况不同自行选择
36.HashSet 和TreeSet区别和底层的原理
①HashSet是基于hash表基于树数据结构来实现的,而TreeSEt是基于树数据结构来实 现的
②HashSet和TreeSet中的元素都是以Key值存储的,在所有的键值对中,Key值是不能 重复的
36.Array 和 ArrayList 有何区别?
答:①Array是数组的工具类,里面有操作数组的静态方法
②ArrayList是List接口的实现类,是一个动态的数组容器
37.说说HashSet的实现原理
答:①是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
38.在 Queue 中 poll()和 remove()有什么区别?
答:remove() 和 poll() 方法都是从队列中删除第一个元素。如果队列元素为空,调用remove() 的行为与 Collection 接口的版本相似会抛出异常,但是新的 poll() 方法在用空集合调用时只是返回 null。因此新的方法更适合容易出现异常条件的情况
39.哪些集合类是线程安全的?
答:①vector比ArrayList多个同步线程安全,但是效率极低,现在已经不适用了,因为web中效率也就是页面的响应速度是优先考虑的
②statck:堆栈类,先进后出,线程安全
③Hashtable比HashMap多个同步线程安全,
④Enumeration:枚举,就是单例的也是线程安全
40.怎么确保一个集合不能被修改?
答:因为如果通过添加final的方式,是无法new对象,但是里面的value值可以被改变,如果想值也不被改变的话就使用 map = Collections.unmodifiableMap(map);工具类进行,这样你再修改值的话就会报出异常。
(三)容器
41.并行和并发有什么区别?
答:并发是指一个处理器同时处理多个任务。多个线程轮流切换获得cpu执行时间。
并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。
并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。
来个比喻:并发是一个人同时轮吃三个馒头,而并行是三个人同时吃三个馒头。
42.线程和进程的区别?
答:①地址空间:同一个进程中线程共享进程的地址空间,而其他进程是独立的地址空间
资源空间:同一个进程中线程共享本进程的内存和cup,I/O等,而其他进程是独立的地 址空间
②一个进程崩溃后,不会影响其他进程,但是一个进程中线程崩溃后整个进程崩溃。
③进程是一个程序的运行入口,可以独立运行,而线程是进程的组成单位,不能独立执 行
④线程执行开销小,不利于资源的管理和保护,而进程资源开销大,但是能够进行资源 管理和保护。
43.守护线程是什么?
答:再换一种说法,如果有用户自定义线程存在的话,jvm就不会退出——此时,守护线程也不能退出,也就是它还要运行,干嘛呢,就是为了执行垃圾回收的任务啊。
44.创建线程有哪几种方式?
答:①继承方式:class xxx extends Tread
②实现方式:class xxx implements Runalbe
③继承和实现方式的区别:
- 继承方式具有局限性,因为继承具有单继承性,不利于类业务扩展
- 实现方式,扩展性更强,因为实现具有多实现性,有利于业务的扩展
- 实现方式,能够被所有的Teade对象所共享
④创建启动的步骤流程
- 明确线程的主体,哪些代码需要放到线程体里面
- 自定义一个类implements Runable接口(extends Tread)
- 覆写run方法,里面放1中的代码
- 创建一个自定义类的对象 t
- 以t为参数构造一个Tread对象 tt;(继承方式没有这一步)
- 启动线程 tt.start()
45.说一下 runnable 和 callable 有什么区别?
答:相同点:都是接口,都可以编写多线程,都采用Thread.start()启动线程。
不同点:runnable 没有返回值;callable返回执行结果,Future,配合可以采用异步的结果,
Runnable不能抛出异常,只能自己处理;Callable可以抛出异常。
46.线程有哪些状态?
答:1、新状态:线程对象已经创建,还没有在其上调用start()方法。
2、可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。
3、运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
4、等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到可运行状态。
5、死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
47.sleep() 和 wait() 有什么区别?
答:sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
48.notify()和 notifyAll()有什么区别?
答:notify只会通知一个在等待的对象,而notifyAll会通知所有在等待的对象,并且所有对象都会继续运行,还有区别是:notify()是对对象同步要求,而notifyAll()只有一个线程有资格运行。
49.线程的 run()和 start()有什么区别?
答:区别就是线程中5种状态,start()方法是告诉线程。我已经准备好了可以运行了。Run()方法是当CPU真正切换这个线程运行时调用这个方法。线程的不同状态执行的方法。
50.创建线程池有哪几种方式?
答:1.创建大小不固定的线程池
2.创建数量固定的线程池
3.创建单线程的线程池
4.创建定时的线程池
51.线程池都有哪些状态?
1、RUNNING
(1) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
(02) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!
2、 SHUTDOWN
(1) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
(2) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。
3、STOP
(1) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
(2) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
4、TIDYING
(1) 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
(2) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
5、 TERMINATED
(1) 状态说明:线程池彻底终止,就变成TERMINATED状态。
(2) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
52.线程池中 submit()和 execute()方法有什么区别?
答:两者都是执行线程,不同的是submit和execute接受参数不同,submit执行后由返回值,submit方便exception的返回。
53.在 java 程序中怎么保证多线程的运行安全?
答:主要通过synchronized和lock机制实现的是:
1.原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized);
2.可见性:一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile);
3.有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序,
54.多线程锁的升级原理是什么?
答:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,JVM 让其持有偏向锁,并将threadid 设置为其线程 id,再次进入的时候会先判断 threadid 是否尤其线程 id 一致,如果一致则可以直接使用,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,不会堵塞,执行一定次数之后就会升级为重量级锁,进入堵塞,整个过程就是锁升级的原理。
锁的升级的目的:在 Java 6 之后优化 synchronized 的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,减低了锁带来的性能消耗。
锁升级是为了减低了锁带来的性能消耗
55.什么是死锁?
答:•互斥(Mutual exclusion):存在这样一种资源,它在某个时刻只能被分配给一个执行绪(也称为线程)使用;
•持有(Hold and wait):当请求的资源已被占用从而导致执行绪阻塞时,资源占用者不但无需释放该资源,而且还可以继续请求更多资源;
•不可剥夺(No preemption):执行绪获得到的互斥资源不可被强行剥夺,换句话说,只有资源占用者自己才能释放资源;
•环形等待(Circular wait):若干执行绪以不同的次序获取互斥资源,从而形成环形等待的局面,想象在由多个执行绪组成的环形链中,每个执行绪都在等待下一个执行绪释放它持有的资源。
56.怎么防止死锁?
答:不难看出,在死锁的四个必要条件中,第二、三和四项条件比较容易消除。通过引入事务机制,往往可以消除第二、三两项条件,方法是将所有上锁操作均作为事务对待,一旦开始上锁,即确保全部操作均可回退,同时通过锁管理器检测死锁,并剥夺资源(回退事务)。这种做法有时会造成较大开销,而且也需要对上锁模式进行较多改动。
消除第四项条件是比较容易且代价较低的办法。
57.ThreadLocal 是什么?有哪些使用场景?
答:1.ThreadLocal用来解决多线程程序的并发问题
2.ThreadLocal并不是一个Thread,而是Thread的局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都
可以独立地改变自己的副本,而不会影响其它线程所对应的副本.
3.从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。
4.线程局部变量并不是Java的新发明,Java没有提供在语言级支持(语法上),而是变相地通过ThreadLocal的类提供支持.
58.说一下 synchronized 底层实现原理?
答:代码块同步是使用monitorenter和monitorexit指令实现, monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处, JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个 monitor 与之关联,当且一个monitor 被持有后,它将处于锁定状态。线程执行到 monitorenter 指令时,将会尝试获取对象所对应的 monitor 的所有权,即尝试获得对象的锁,就是类是在每个代码块前面发给令牌,执行完后会检查令牌,这个模块在同时刻只有该线程执行
59.synchronized 和 volatile 的区别是什么?
答:volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
volatile仅能使用在变量级别,synchronized则可以使用在变量,方法。volatile仅能实现变量的修改可见性,但不具备原子特性。volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞。volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化。
60.synchronized 和 Lock 有什么区别?
答:总的来说,lock更加灵活。 主要相同点:Lock能完成synchronized所实现的所有功能 不同: 1.ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此更有扩展性。在多个条件变量和高度竞争锁的地方
61.synchronized 和 ReentrantLock 区别是什么?
答:和lock的实现差不多,但是可以实现其他的复杂功能
(四)反射
62.什么是反射?
答:动态的获取对象的信息,包括所属类,自己中包含的字段和属性,也能够动态的修改这些属性,所以反射机制是java语言被看着动态语言的关键特性。
63.什么是 java 序列化?什么情况下需要序列化?
答:因为现在几乎所有的程序都涉及网络I/O和磁盘的I/O,而数据传输是以字节为单位的,所以传输的数据都必须能够序列化为字符。
序列化:就是把java对象中存储的数据内存还是磁盘中的内容,能够序列化为能够传输的字节流。
反序列化:就是把二级制数据,反序列化为对象数据。
意义:就是方便传输和存储。
64.动态代理是什么?有哪些应用?
答:就是将我们需要动态代理的类,重新生成一个子类和本类,然后底层的框架就是使用这个新生成的代理类,把我们写的代码和一些不变化的功能代码动态的添加进代理类中,这样功能就得到了加强,我们以后需要添加业务和修改业务的话,不用编写哪些已经写好的代码
,使用动态代理可以灵活的添加我们的代码,而又不需要改变原来的代码。
应用:spring的一系列框架中都使用的了动态代码,Jpa中也是使用的动态代理。各种框架都有使用动态代码。
65.怎么实现动态代理?
答:主要是使用java中提供的Proxy类的newProxyInstance()方法,该方法需要三个参(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h),其中分别为:
①:该对象的类加载器classloader
②:被代理类的类对象
③:一个InvocationHandler 处理器接口
然后实现InvocationHandler中有一个方法:invoke(Object proxy, Method method, Object[] args) ,通过显式调用可以实现对代理对象的类方法进行操作,这里主要是采用反射的原理,SrpingAop就是利用这一点进行实现的。
66.获得字节码对象的方式,以及好处和坏处
答:①3种,class.forname(类的完全限定名);对象.getClass();类型.class
②好处是可以获得对象运行时动态的类型和数据,坏处是破坏类的封装性
(五)对象拷贝
67.为什么要使用克隆?
答:提高对象的安全性,而又不改变本来对象的属性,然后使用对象引用地址就可以使用我们拷贝的地址。
68.如何实现对象克隆?
答:有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆,
69.深拷贝和浅拷贝区别是什么?
答:简单的来说就是,在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误!,
浅克隆只是产生一个新的指针,还是同一个内存空间,而深度克隆在增加的指针同时增加一样的空间。
(六)Java Web
70.jsp 和 servlet 有什么区别?
答:1.jsp本质就是servlet,jsp经过编译以后就是servlet类,JVM只认识java类不能识别Jsp代码,Web容器会把Jsp编译成JVM识别的java类代码。
2.jsp擅长展示页面内容展示,servlet擅长逻辑控制
3.servlet中没有内置对象,Jsp中的内置对象通过HttpServletRequest,等对象获得。
4.其实就是把jsp页面的java脚本会嵌入到一个类中,servlet是一个完整的类。
71.cookie和session存在解决什么问题
答:①在Web发展历史中,session与cookie都是伟大的存在,其初衷都是为了记住用户 在网站上的浏览信息,如果没有其他替代品的出现,几乎所有web站点都离不开session 与cookie。
②Http协议是无状态的,也就导致服务器无法分辨是谁浏览了网页。为了维持用户在 网站的状态,比如登陆、购物车等,出现了先后出现了四种技术,分别是隐藏表单域、 URL重写、cookie、session
72.cookie和session区别
答:①存储数据量方面:session 能够存储任意的 java 对象,cookie 只能存储 String 类 型的对象
②cookie在客户端session在服务端。因Cookie在客户端所以可以编辑伪 造,不是 十分安全。
③Session过多时会消耗服务器资源,大型网站会有专门Session服务器, Cookie 存在客户端没问题。
④域的支持范围不一样,比方说a.com的Cookie在a.com下都能用,而 www.a.com 的Session在api.a.com下都不能用,解决这个问题的办法是 JSONP或者跨域资源共享。
⑤Session的实现方式和Cookie有一定关系。建立一个连接就生成一个 session id, 打开几个页面就好几个了,这里就用到了Cookie,把session id存在Cookie中,每 次访问的时候将Session id带过去就可以识别了.
73.如果浏览器禁用的cookie之后,怎么解决
答:重写URL
http://localhost/session/single;jsessionid=372EC71FF0939E7C74B533B071714C5E
resp.encodeURL("路径") -->资源重写解决这个问题
74.jsp九大内置对象有哪些?
答:
request:封装客户端的请求,其中包含来自 get 或 post 请求的参数;
response:封装服务器对客户端的响应;
pageContext:通过该对象可以获取其他对象;
session:封装用户会话的对象;
application:封装服务器运行环境的对象;
out:输出服务器响应的输出流对象;
config:Web 应用的配置对象;
page:JSP 页面本身(相当于 Java 程序中的 this);
exception:封装页面抛出异常的对象
75.动态包含和静态包含的区别?
答:①静态包含:<%@ include file="被包含页面"%>
②动态包含:<jsp:include page="被包含页面" flush="true">
③区别:JSP在WEB服务器中都要转换成一个java文件,java文件再编译 成一个class文件,当请求JSP页面的时候,实际上运行的是这个class 文 件。
④静态包含:只生成一个java文件,动态包含:生成多个class文件。
76.Page指令中errorPage与isErrorPage的区别?
答:①errorPage是当这个页面访问出现程序错误时,页面跳转到我们指定的错误页面上
②isErrPage是标记这个页面出现错误时,可以产生一个Excption的错误对象
77.购物车的实现方式有哪些?
答:方式一:使用cookie做购物车
问题:(cookie是保存在电脑上,如果换电脑,信息就没有呢)
方式二:使用session做购物车
问题:一样问题,还不如cookie,比如选了很多商品之后,浏览器一关闭,就不存在
方式三:使用cookie+数据库(使用比较多)
如果没有登录,选商品在购物车,关了,下次进来商品还是存在的,
78.重定向和转发的区别,对应的方法是什么?
①响应次数. 地址栏的改变与否 是否带参数 , 说明他们都是跳转页面的方法
②重定向:redirect,告诉浏览器请求另一个地址,地址栏url改变
③转 发:forward,请求不中断,转发到另一个资源,请求另一个地址后再把结果返回 给客户端,地址栏url不改变
79.servletconfig和servletcontext
servletconfig:单个servlet中关于web.xml中初始化参数配置,init方法传入的参数。
servletcontext:容器维护的一块存储空间,存放当前环境的一些信息和servlet相关信息,表示一个作用域。是整个servlet
80.Tomcat 的class 加载的优先顺序一览
第一步:加载JVM 类库。一般是加载由虚拟机提供的基本的运行时类和系统扩展目录($JAVA_HOME/jre/lib/ext)下的JAR 包。
第二步:加载系统环境变量的类库。这个加载器用来加载CLASSPATH 环境变量中指定的类。
第三步:加载Tomcat 下面common 文件夹下面的公共类库。
第四步:加载自己需要的catalina 类库。
第五步:webapps 下面自己应用的类库,包括webapp1、webapp1......等。
81.说一下 jsp 的 4 种作用域?
答:page,当前页面,就是跳转到别的页面就失效了。
Request,当前的仅仅一次回话,就是这一次请求范围内有效。
Session,浏览器进程,就是在页面没有关闭下,跳转到所有页面都是有效的。
Application,服务器进程,就是服务器没有重启的话,数据就有效不会丢失和覆盖。
82.说一下 session 的工作原理?
答:就是打开浏览器到关闭浏览器期间,称为一次会话就是一个session,它是保存在服务器端的,但是第一次浏览器请求的时候,服务器会自动分配一个Id,和session是唯一对应的,来识别这个用户,由于session是存储在服务器端,当sesssion是大数据量的不适合存储。
83.如果客户端禁止 cookie 能实现 session 还能用吗?
答:Cookie与 Session,一般认为是两个独立的东西,Session采用的是在服务器端保持状态的方案,而Cookie采用的是在客户端保持状态的方案。但为什么禁用Cookie就不能得到Session呢?因为Session是用Session ID来确定当前对话所对应的服务器Session,而Session ID是通过Cookie来传递的,禁用Cookie相当于失去了Session ID,也就得不到Session了。但是通过PhP的时候,id的传输可以不依赖cookie,而其他的必须AsP的方案的时候是依赖的。
84.如何避免 sql 注入?
答:最常见的是在业务逻辑层中执行关键字检查。如果包含SQL的关键字,例如 *、or、select、delete等等关键字就进行替换;最有效的仍然是使用SQL变量进行查询,避免使用字符串来拼接SQL字符串。
85.什么是 XSS 攻击,如何避免?
答:跨站点脚本攻击类似于上面提到的CSRF。实际上,原则是将一段JavaScript代码注入网页。然后当其他用户访问该页面时,他们将运行黑客编写的JS代码来实现一些帐户控制。解决方案关键字判断当有一个关键字如脚本、src来代替损坏;返回内容时进行转码,转码尖括号采用Unicode编码格式,如下面的截图:
86.什么是 CSRF 攻击,如何避免?
答:跨站请求伪造,就是伪造用户的请求,可以让浏览器发送Get请求。浏览器转发请求,由于用户已经登录,就会携带正确的cookie调换到访问连接实现相关功能。解决方案是向表单添加字段标记。黑客无法猜出已经改变的令牌,所以即使你添加一个UUID令牌,将它保存到服务器会话,黑客也不可能猜出随机令牌,就无法构造出能实现功能的链接了。
86.什么是动态网页和静态网页?
答:①静态网页:随着html代码的生成,页面的内容和显示效果就基本上不会发生变化了 ——除非你修改页面代码。
②动态网页:页面代码虽然没有变,但是显示的内容却是可以随着时间、环境或者数据库操作的结果而发生改变的。
86.Get和Post区别?
答:post
相对安全
大小没有限制
无法保留提交书签
表单提交
get
不安全(会在地址栏显示)
get的总字符数是有限的
超连接的时候用
86.说说C/S 与B/S的区别?
答:①C/S
开发语言:VB,Delphi,VC++,C#,Java awt/swing等.
示例:比如桌面QQ,扫雷,拱猪,LOL等运行在桌面的程序.
特点:在服务端主要就是一个数据库,把所有业务逻辑以及界面的渲染操作交给客户端完成.
优点:较安全,用户界面很丰富,用户体验不错等
缺点:每次升级都需要重新安装,针对于不同的操作系统开发,可移植性很差.
②B/S
开发语言:JSP,ASP,PHP等
示例:WebQQ,淘宝,京东....
特点:基于浏览器访问的应用,把业务逻辑交给服务端完成,客户端仅仅只做界面渲 染和数据交换.BS是特殊的CS,此时浏览器充当了客户端.基于HTTP协议的.
优点:只开发服务端,可以跨平台,移植性很强等.
缺点:安全性较低,用户体验较差等.
86.Servlet生命周期是什么?
答:执行流程: 构造器-->init方法--> 循环[ service方法 ]--->destory方法(正常关闭 Tomcat)
1:构造器先执行,创建Servlet对象:init,service,destory方法,都是非static 方法,都得使 用对象调用.
2:一个Servlet类在整个生命周期中最多只有一个对象.
3:init在构造器执行之后,立马执行,只执行一次,为对象做初始化操作.
4:service方法在每次请求都会执行.
5:destroy方法只有在正常关闭Tomcat时,才会执行,不要期望该方法一定 执行,不要在该方法编写扫尾操作.
6:Servlet类必须使用public修饰,Servlet的构造器,必须是公共无参数的. 原因在下一 个知识点.
86.Servlet执行流程是什么?
答:①从浏览器输入地址(发起请求)开始分析: http://localhost:80/xx/hello
②web.xml内容分析:
-
-
- 找到url-pattern的值为 /hello
- 通过上一步中对应servlet-name 找到对应的servlet标签
- 找到对应的Servlet的完全限定名 cn.itsource._01hello.HelloServlet
-
仅仅是一个字符串而已,交给Tomcat进行处理通过反射来创建对象并调用方法
③Tomcat对Servlet的创建:
-
-
- 通过上获得 servlet的完全限定名为参数使用反射来创建一个对象
-
Class clz = Class.forName(“cn.itsource._01hello.HelloServlet”)
Clz.newInstance() 要求自己的Servlet类必须有公共的无参数的构造方法
-
-
- 如果是第一次访问,就会创建一个Servlet的对象,并缓存起来
- 2
- 。。。N 直接先从缓存中看有没有,有直接使用调用service方法
-
④
(七)异常
87.throw 和 throws 的区别?
答:thow是一个关键字,在方法中抛出异常。Thows是方法前面抛出方法执行 中可能出现的异常。
Thow是抛出一个异常对象,thows可以抛出多个异常类。
88.final、finally、finalize 有什么区别?
答:final是修饰字端和类,finally是异常的关键字。就是在抓住异常后会执行的语句
Finalize是object类中垃圾回收方法。
89.try-catch-finally 中哪个部分可以省略?
答:try不能省略,catch-finally不能同时省略。
90.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
答:会执行,但是会返回最后一个有return语句的值,return以后变为常量,不是变量
91.常见的异常类有哪些?
①java中的异常是Throwable,而它又派生了两个子类:Error和Exception,
Error表示应用程序本身无法克服和恢复的一种严重问题,例如,说内存溢 出和线程死锁等系统问题。
Exception表示程序还能够克服和恢复的问题,其中又分为IOException和 RuntimeException
②IOException表示已受检异常,需要使用try/catch块捕获(编译时,我们已知怀疑有异常)
③RuntimeException表示运行时异常,指程序存在bug
常见的运行时异常分为
NullPointerException
IllegalStateException
ArrayIndexOutOfBoundsException
ClassCastException
ClassNotFoundException
(八)网络
92.http 响应码 301 和 302,400,500,200 代表的是什么?有什么区别?
答:200-请求成功,
- 被请求的资源已永久移动到新位置。服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置,
- 请求的资源临时从不同的URI响应请求,但请求者应继续使用原有位置来进行以后的请求,
- 表示语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求;请求参数有误。一般是后台封装前台传参的格式问题,
500-服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。
93.forward 和 redirect 的区别?
答:forward:是内部请求转发,会携带请求的参数进行转发请求,同时可以访问服务器中WEB-INF中的资源。不能跨域请求
Redirect:是浏览器重新发送请求,不会携带请求参数进行转发,不能访问服务器中WEB-INF中的资源,能跨域请求。
94.简述 tcp 和 udp的区别?
答:1,tcp有链接,udp无连接2tcp链接资源多,相对udp少3,tcp数据结构复杂,相对udp简单4tcp数据正确性高,udp可能丢失数据。
95.tcp 为什么要三次握手,两次不行吗?为什么?
答:两次不行,为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤
如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认
96.说一下 tcp 粘包是怎么产生的?
答:现象:TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
原因:(1)发送方原因
我们知道,TCP默认会使用Nagle算法。而Nagle算法主要做两件事:1)只有上一个分组得到确认,才会发送下一个分组;2)收集多个小分组,在一个确认到来时一起发送。
所以,正是Nagle算法造成了发送方有可能造成粘包现象。
(2)接收方原因
TCP接收到分组时,并不会立刻送至应用层处理,或者说,应用层并不一定会立即处理;实际上,TCP将收到的分组保存至接收缓存里,然后应用程序主动从缓存里读收到的分组。这样一来,如果TCP接收分组的速度大于应用程序读分组的速度,多个包就会被存至缓存,应用程序读时,就会读到多个首尾相接粘到一起的包。
97.OSI 的七层模型都有哪些?
答:1,物理协议层 2,数据链路层(HDLC) 3,网络协议层(IP,AppleTalk) 4,传输协议层(TCP,UDP,SPX) 5,会话协议层(RPC) 6,表示层()7,应用协议层()。
简称后的网络7层协议有:物理和链路层统称为数据链路层,网络层,传输层,会话层和表示层,应用层统称为应用层。
98.get 和 post 请求有哪些区别?
答:这两种请求是HTTP协议中常用的请求,Get请求把表单的数据显式地放在URI中,并且对长度和数据值编码有所限制.Post请求把表单数据放在HTTP请求体中,并且没有长度限制.
99.如何实现跨域?
答:同域: 简单的解释就是相同域名,端口相同,协议相同
跨域:demo2: 基于script标签实现跨域
让远程js知道它应该调用的本地函数叫什么名字,只要服务端提供的js脚本是动态生成的就好了,这样前台只需要传一个callback参数过去告诉服务端,我需要XXX代码,于是服务端就会得到相应了.
demo3: 基于jquery跨域
那么如何用jquery来实现我们的跨域呢???jquery已经把跨域封装到ajax上了,而且封装得非常的好,使用起来也特别方便
100.说一下 JSONP 实现原理?
答:JSONP的最基本的原理是:动态添加一个<script>标签,而script标签的src属性是没有跨域的限制的。这样说来,这种跨域方式其实与ajax XmlHttpRequest协议无关了。
(九)设计模式
101.说一下你熟悉的设计模式?
答:总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
102.简单工厂和抽象工厂有什么区别?
答:在遵循开闭原则的条件下,简单工厂对增加新的产品无能为力,因为新增加的新产品只能通过修改工厂的方法来实现。
(十)Spring/Spring MVC
103.为什么要使用 spring?
1.方便解耦,便于开发(Spring就是一个大工厂,可以将所有对象的创建和依赖关系维护通过IOC都交给spring管理)
2.spring支持aop编程(spring提供面向切面编程,可以很方便的实现对程序进行权限拦截和运行监控等功能)
3.声明式事务的支持(通过配置就完成对事务的支持,不需要手动编程)
4.方便程序的测试,spring 对junit4支持,可以通过注解方便的测试spring 程
5.方便集成各种优秀的框架()
6.降低javaEE API的使用难度(Spring 对javaEE开发中非常难用的一些API 例如JDBC,javaMail,远程调用等,都提供了封装,是这些API应用难度大大降低)
104.解释一下什么是 aop?
答:概念:AOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP编程的有效补充。
作用是:使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。
从而避免了在业务逻辑的代码中混入很多的系统相关的逻辑——比如权限管理,事物管理,日志记录等等。
组成部分:Aspect:切面,比如权限管理是一个切面,权限管理也是一个切面
Join point:连接点,就是在哪个位置切入程序。
Advice:通知,切面在某个连接点执行的操作
PointCut:切点,符合切点表达式的连接点,就是真正被切入的程序位置。
实现原理:AOP分为静态和动态,
静态AOP是指AspectJ实现的AOP,他是将切面代码直接编译到Java类文件中。
动态AOPI是指将切面代码进行动态织入实现的AOP。
Spring的AOP代理技术有两种:JDK必须实现一个接口,CGLIB不需要实现接口,都能实现代理模式,生成一个代理对象
105.解释一下什么是 ioc?
答:IOC就是一种设计思想,不是一种技术。
●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了
106.spring 有哪些主要模块?
答Spring有七大功能模块,分别是Spring Core,AOP,ORM,DAO,MVC,WEB,Context。
1,Spring Core
Core模块是Spring的核心类库,Spring的所有功能都依赖于该类库,Core主要实现IOC功能,Sprign的所有功能都是借助IOC实现的。
2,AOP
AOP模块是Spring的AOP库,提供了AOP(拦截器)机制,并提供常用的拦截器,供用户自定义和配置。
3,ORM
Spring 的ORM模块提供对常用的ORM框架的管理和辅助支持,Spring支持常用的Hibernate,ibtas,jdao等框架的支持,Spring本身并不对ORM进行实现,仅对常见的ORM框架进行封装,并对其进行管理
4,DAO模块
Spring 提供对JDBC的支持,对JDBC进行封装,允许JDBC使用Spring资源,并能统一管理JDBC事物,并不对JDBC进行实现。(执行sql语句)
5,WEB模块
WEB模块提供对常见框架如Struts1,WEBWORK(Struts 2),JSF的支持,Spring能够管理这些框架,将Spring的资源注入给框架,也能在这些框架的前后插入拦截器。
6,Context模块
Context模块提供框架式的Bean访问方式,其他程序可以通过Context访问Spring的Bean资源,相当于资源注入。
7,MVC模块
WEB MVC模块为Spring提供了一套轻量级的MVC实现,在Spring的开发中,我们既可以用Struts也可以用Spring自己的MVC框架,相对于Struts,Spring自己的MVC框架更加简洁和方便。
107.spring 常用的注入方式有哪些?
答:有构造方法注入,注解注入,gettersetter方式
108.spring 中的 bean 是线程安全的吗?
答:原生的bean基本上安全的,其他spring管理的bean,需要看它的作用域,单例模式等安全的
109.spring 支持几种 bean 的作用域?
答:五种:
singleton作用域,单例是默认的值Spring 只会为每一个bean创建一个实例,并保持bean的引用.
prototype作用域:每一次请求(将其注入到另一个bean中,或执行getBean()方法)都会产生一个新的bean实例,相当于new操作
request作用域:表示该针对每一次HTTP请求都会产生一个新的bean,仅适用于WebApplicationContext环境
session作用域:表示该针对每一次HTTP会话请求都会产生一个新的bean,仅适用于WebApplicationContext环境
globalSession作用域:用于 portlet 容器,因为每个 portlet 有单独的 session,globalsession 提供一个全局性的 http session。
110.spring 自动装配 bean 有哪些方式?
答:no:默认值,表示没有自动装配,应使用显式 bean 引用进行装配。
byName:它根据 bean 的名称注入对象依赖项。
byType:它根据类型注入对象依赖项。
构造函数:通过构造函数来注入依赖项需要设置大量的参数。
autodetect:容器首先通过构造函数使用 autowire 装配,如果不能,则通过 byType 自动装配。
111.spring 事务实现方式有哪些?
答:第一种方式:每个Bean都有一个代理,第二种方式:所有Bean共享一个代理基类,第三种方式:使用拦截器,第四种方式:使用tx标签配置的拦截器
第五种方式:全注解在Service层类上加上@Transcational,现在最常用的方式
112.说一下 spring 的事务隔离?
答:就是事务的传播机制,
@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
113.说一下 spring mvc 运行流程?
答:1.前端用户发送请求到服务器,请求被拦截器拦截后,被SpringMVC前端核心控制器Dispatcharservlet捕获
2.DispatchServlet根据请求路径查询具体的Handler,handler会调用具体的方法处理这个请求
3.HandlerMapping返回一个HandlerExcutionChain给DispatchServlet
4.DispatchServlet调用HandlerAdapter适配器,HandlerAdapter调用具体的Handler处理业务
5.Handler处理结束返回一个具体的ModelAndView给适配器,适配器将ModelAndView给DispatchServlet
6.DispatchServlet把视图名称给ViewResolver视图解析器
7.ViewResolver返回一个具体的视图给DispatchServlet
8.DispatchServlet把数据模型填充到具体的视图当中,然后响应给前端用户。
114. spring mvc 有哪些组件?
答:前置控制器 DispatcherServlet。
处理映射器 HandlerMapping。
处理适配器 HandlerAdapter。
处理器 Handler
模型和视图 ModelAndView。
视图解析器 ViewResolver。
115. @RequestMapping 的作用是什么?
答:将 http 请求映射到相应的类/方法上。
116. @Autowired 的作用是什么?
答@Autowired 它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作,通过@Autowired 的使用来消除 set/get 方法
(十一)Spring Boot/Spring Cloud
117.什么是 spring boot?
答:springboot是一套maven组合,我们只需要导入一个模块jar包,maven底层会自动导入它相关的jar包,spring boot 是为 spring 服务的,是用来简化新 spring 应用的初始搭建以及开发过程的
118.为什么要用 spring boot?
答:配置简单,很少的配置,底层自动一整套配置
独立运行,创建一个主线程启动内置Tomcat服务器
自动装配,
无代码生成和 xml 配置
提供应用监控
易上手
提升开发效率
119.spring boot 核心配置文件是什么?
答:bootstrap (. yml 或者 .properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,且 boostrap 里面的属性不能被覆盖;
application (. yml 或者 . properties):用于 spring boot 项目的自动化配置。
120.spring boot 配置文件有哪几种类型?它们有什么区别?
答:有两种配置文件,分别是application.properties和application.yml
区别:properties是传统的xml格式读取数据,而yml结尾的是是有严格的写规则的
1.不同“等级” 用冒号隔开
2. 次等级的前面是空格,不能使用制表符(tab)
3. 冒号之后如果有值,那么冒号和值之间至少有一个空格,不能紧贴着
121.spring boot 有哪些方式可以实现热部署?
答:方式一:使用pom.xml中插件包的方式进行热部署,但是实际上还是重启服务,只不过内部有机制,会使重启的效率高很多。
运行方法二
如果使用的run as – java application的话,那么还需要做一些处理。 spring-loader-1.2.4.RELEASE.jar下载下来,放到项目的lib目录中,然后把IDEA的run参 数里VM参数设置为: -javaagent:.\lib\springloaded-1.2.4.RELEASE.jar -noverify 然后启动 就可以了,这样在run as的时候,也能进行热部署
122.什么是 spring cloud?
答:springcloud是一个RPM微服务架构,它是基于springboot,把原来的单体项目,变为多个微服务架构,微服务好处有,编码,测试,部署等变的很简单,还有项目可以容错高,扩展简单而且节约资源,针对性扩展某个服务。Springcloud有一系列组件可以保证多个服务之间的通信和相互协调为用户提供最终的服务。
123.spring cloud 的核心组件有哪些?
答:1.发现服务的注册中心eureka
2.服务负载均衡ribbon,feign
3.断路器 hystri和feign
4.路由网关 zuul
5.配置中心 config
124.spring cloud 核心组件的作用是什么?
答:1.注册中心作用是记录每个服务,并且每隔一段时间就去检测该服务心跳是否健康,同时还有其他的服务审查等,给其他的组件提供服务列表
2.负载均衡作用是平衡访问服务量,有很多平衡的策略,比如轮番访问服务器,随机访问服务器等,自己可以自定义平衡访问规则。
3.断路器作用就是一个服务出现问题时候,就是前台访问不到后,断路器会自动的降级处理,返回一个托底的数据,同时会隔离和其他服务器的关联,不会出现雪崩现象导致整个微服务都瘫痪。
4.路由网关可以在分发访问的每一个资源,而且可以在访问每一个微服务资源进行拦截和过滤认证等操作,我们前端只需要访问路由服务即可,然后路由网关会自动转发我们的请求
5.配置中心作用就是管理我们所有的微服务配置文件,在github上面进行统一的管理,然后每一个微服务可以下载配置文件
(十二)Hibernate
125.为什么要使用 hibernate?
答:1.hibernate对jdbc访问数据库做了很多封装,简化访问数据库的繁琐重复的工作
2.hibernate是一个基于jdbc的优秀主流的orm框架,它极大的提高了开发的效率
3.hibernate是一个轻量级框架,它的性能非常的好,它支持很多主流的数据库,包括mysql,orcle等,也支持多对一,多对多等复杂的关系
4.hibernate它有很多提高性能的方式,自己有一级和,也支持第三方的二级缓存。
126.jpa 和 hibernate 有什么区别?
答:jpa是一个orm框架的规范,而hibernate是jap的具体实现,就好比一个是接口一个是实现类。而且hibernate前期是个人开发使用的因为后来火了以后,apache就让这个人去编写了jpa规范,实现就是用hibernate。
127.什么是 ORM 框架?
答:就是对象关系映射框架,就是把数据库中的数据关系,映射为java中的对象。
128.hibernate 中如何在控制台查看打印的 sql 语句?
答:hibernate中可以在相应的配置文件中,配置一下hibernate.show_sql=true,它会自动打印sql语句
129.hibernate 有几种查询方式?
答:QBC:条件查询方式,HQL:面向对象查询方式,SQL:结构化查询方式
130.hibernate 实体类可以被定义为 final 吗?
答:可以,但是如果将hibernate的实体类定义为final以后,当hibernate需要使用代理模式在延迟加载查询来提高性能的时候,就无法生效了,因为java是不允许对final修饰的类进行扩展,所有hibernate的代理就无法使用了,还有一种解决方案就是在持久化类中实现一个代理接口,这样也可以使用的代理模式。
131.在 hibernate 中使用 Integer 和 int 做映射有什么区别?
答:应该没什么区别,因为如果使用int的话,hibernate底层会自动装箱为Integer类型,只是一般使用Integer,这样也符合面向对象的编程,hibernate底层使用代理模式的时候必须是对象。
132.hibernate 是如何工作的?
答:1.通过Configuration()方法读取并且解析hibernateConfig.xml的配置文件
2.有配置文件里面的<mapping resource=”hibernate.xml”/>读取并解析映射信息
3.通过config.buldSessionFactory(),创建SessionFactory
4.通过SessionFactory.openSession(),打开Session
5.通过Session.beginTransacion(),创建事务Transaction
6.通过持久层persistence的方法进行操作数据库
7.Session.getTranscation().commit()提交事务
8.关闭Session和SessionFactory
132.get()和 load()的区别?
答:load()方法:用此方法来查询一个对象的时候,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,只有当我们要使用这个对象,得到其它属性时,这个时候才会发出sql语句,从数据库中去查询我们的对象。
get()方法:就是立即会发送sql语句到数据库中查询出来。
133.说一下 hibernate 的缓存机制?
答:一级缓存:命中条件是同一个SessionFactotry,同一个Session,同一个实体类id;而且由于一级缓存是hibernate自己内置的不可卸载,默认就有。一定使用Session缓存
二级缓存:命中条件是同一个SessionFactory,同一个实体类id;二级缓存是使用第三方插件完成,可以选择开启或者不开启,而且有缓存场景
134.hibernate 对象有哪些状态?
答:临时状态:在数据库中没有,在Session缓存中没有
持久化状态:在数据库中有,在Session缓存中也有
游离状态:在数据库中有,在Session缓存中没有
删除状态:数据库没有,Session缓存中也没有
135.在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?
答:1.getCurrentSession 就是:如果有已经使用的,用旧的,如果没有,建新的。
2.openSession 从字面上可以看得出来,是打开一个新的session对象,而且每次使用都是打开一个新的session,
136.hibernate 实体类必须要有无参构造函数吗?为什么?
答:必须有无参构造
- Hibernate框架会调用这个默认构造方法来构造实例对象,即Class类的newInstance方法 ,这个方法就是通过调用默认构造方法来创建实例对象的 。
- Hibernate底层大量使用反射,就会使用到无参构造方法创建实例对象,而且使用代理模式的时候同样会使用无参构造方法
(十三)Mybatis
137.mybatis 中 #{}和 ${}的区别是什么?
答:#{}会自动将传入的数据的值注入到{}中,#方式很大程度上防止sql的注入。
${}会直接将传入的字符串拼接到sql中,$方式无法防止sql的注入。能用#就别使用$,不过MyBatis排序动态传参数时候,必须使用$而不是#
138.mybatis 有几种分页方式?
答:方式一:查询出全部数据,然后在查询出结果的list中截取需要的部分(假分页)
方式二:拼接sql语句分页,每次动态传入分页的两个参数
方式三:创建拦截器分页,拦截mybatis接口方法ByPage结束的语句
方式四:使用分页插件,其实和创建拦截器方式原理一样
139. RowBounds 是一次性查询全部结果吗?为什么?
答:RowBounds 表面是在“所有”数据中检索数据,其实并非是一次性查询出所有数据,因为 MyBatis 是对 jdbc 的封装,在 jdbc 驱动中有一个 Fetch Size 的配置,它规定了每次最多从数据库查询多少条数据,假如你要查询更的数据,它会在你执行 next()的时候,去查询更多的数据。就好比你去自动取款机取 10000 元,但取款机每次最多能取 2500 元,所以你要取 4 次才能把钱取完。只是对于 jdbc 来说,当你调用 next()的时候会自动帮你完成查询工作。这样做的好处可以有效的防止内存溢出。
140. MyBatis 逻辑分页和物理分页的区别是什么?
答:逻辑分页是一次性查询很多数据,然后再在结果中检索分页的数据。这样做弊端是需要消耗大量的内存、有内存溢出的风险、对数据库压力较大。
物理分页是从数据库查询指定条数的数据,弥补了一次性全部查出的所有数据的种种缺点,比如需要大量的内存,对数据库查询压力较大等问题。
141. MyBatis 是否支持延迟加载?延迟加载的原理是什么?
答:MyBatis 支持延迟加载,设置 lazyLoadingEnabled=true 即可。
延迟加载的原理的是调用的时候触发加载,而不是在初始化的时候就加载信息。比如调用 a. getB(). getName(),这个时候发现 a. getB() 的值为 null,此时会单独触发事先保存好的关联 B 对象的 SQL,先查询出来 B,然后再调用 a. setB(b),而这时候再调用 a. getB(). getName() 就有值了,这就是延迟加载的基本原理。
142. 说一下 MyBatis 的一级缓存和二级缓存?
答:一级缓存:基于 PerpetualCache 的 HashMap 本地缓存,它的声明周期是和 SQLSession 一致的,有多个 SQLSession 或者分布式的环境中数据库操作,可能会出现脏数据。当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认一级缓存是开启的。
二级缓存:也是基于 PerpetualCache 的 HashMap 本地缓存,不同在于其存储作用域为 Mapper 级别的,如果多个SQLSession之间需要共享缓存,则需要使用到二级缓存,并且二级缓存可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态)。
开启二级缓存数据查询流程:二级缓存 -> 一级缓存 -> 数据库。
缓存更新机制:当某一个作用域(一级缓存 Session/二级缓存 Mapper)进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。
143. MyBatis 和 hibernate 的区别有哪些?
答: 灵活性:MyBatis 更加灵活,自己可以写 SQL 语句,使用起来比较方便。可以控制性能
可移植性:MyBatis 有很多自己写的 SQL,因为每个数据库的 SQL 可以不相同,所以可移植性比较差。
学习和使用门槛:MyBatis 入门比较简单,使用门槛也更低。
二级缓存:hibernate 拥有更好的二级缓存,它的二级缓存可以自行更换为第三方的二级缓存。
144. MyBatis 有哪些执行器(Executor)?
答:MyBatis 有三种基本的Executor执行器:
SimpleExecutor:每执行一次 update 或 select 就开启一个 Statement 对象,用完立刻关闭 Statement 对象;
ReuseExecutor:执行 update 或 select,以 SQL 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后不关闭 Statement 对象,而是放置于 Map 内供下一次使用。简言之,就是重复使用 Statement 对象;
BatchExecutor:执行 update(没有 select,jdbc 批处理不支持 select),将所有 SQL 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行 executeBatch()批处理,与 jdbc 批处理相同。
145. MyBatis 分页插件的实现原理是什么?
答:分页插件的基本原理是使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 SQL,然后重写 SQL,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。
146. MyBatis 如何编写一个自定义插件?
答:自定义插件实现原理,MyBatis 自定义插件针对 MyBatis 四大对象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)进行拦截:
Executor:拦截内部执行器,它负责调用 StatementHandler 操作数据库,并把结果集通过 ResultSetHandler 进行自动映射,另外它还处理了二级缓存的操作;
StatementHandler:拦截 SQL 语法构建的处理,它是 MyBatis 直接和数据库执行 SQL 脚本的对象,另外它也实现了 MyBatis 的一级缓存;
ParameterHandler:拦截参数的处理;
ResultSetHandler:拦截结果集的处理。
自定义插件实现关键
MyBatis 插件要实现 Interceptor 接口,接口包含的方法,如下:
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
setProperties 方法是在 MyBatis 进行配置插件的时候可以配置自定义相关属性,即:接口实现对象的参数配置;
plugin 方法是插件用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理,可以决定是否要进行拦截进而决定要返回一个什么样的目标对象,官方提供了示例:return Plugin. wrap(target, this);
(十四)RabbitMQ
147. RabbitMQ 的使用场景有哪些?
答:抢购活动,削峰填谷,防止系统崩塌。
延迟信息处理,比如 10 分钟之后给下单未付款的用户发送邮件提醒。
解耦系统,对于新增的功能可以单独写模块扩展,比如用户确认评价之后,新增了给用户返积分的功能,这个时候不用在业务代码里添加新增积分的功能,只需要把新增积分的接口订阅确认评价的消息队列即可,后面再添加任何功能只需要订阅对应的消息队列即可。
148. RabbitMQ 有哪些重要的角色?
答:RabbitMQ 中重要的角色有:生产者、消费者和代理:
生产者:消息的创建者,负责创建和推送数据到消息服务器;
消费者:消息的接收方,用于处理数据和确认消息;
代理:就是 RabbitMQ 本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。
149. RabbitMQ 有哪些重要的组件?
答: ConnectionFactory(连接管理器):应用程序与Rabbit之间建立连接的管理器,程序代码中使用。
Channel(信道):消息推送使用的通道。
Exchange(交换器):用于接受、分配消息。
Queue(队列):用于存储生产者的消息。
RoutingKey(路由键):用于把生成者的数据分配到交换器上。
BindingKey(绑定键):用于把交换器的消息绑定到队列上。
150. RabbitMQ 中 vhost 的作用是什么?
答:vhost:每个 RabbitMQ 都能创建很多 vhost,我们称之为虚拟主机,每个虚拟主机其实都是 mini 版的RabbitMQ,它拥有自己的队列,交换器和绑定,拥有自己的权限机制。
151. RabbitMQ 的消息是怎么发送的?
答:首先客户端必须连接到 RabbitMQ 服务器才能发布和消费消息,客户端和 rabbit server 之间会创建一个 tcp 连接,一旦 tcp 打开并通过了认证(认证就是你发送给 rabbit 服务器的用户名和密码),你的客户端和 RabbitMQ 就创建了一条 amqp 信道(channel),信道是创建在“真实” tcp 上的虚拟连接,amqp 命令都是通过信道发送出去的,每个信道都会有一个唯一的 id,不论是发布消息,订阅队列都是通过这个信道完成的。
152. RabbitMQ 怎么保证消息的稳定性?
答:提供了事务的功能。
通过将 channel 设置为 confirm(确认)模式。
153. RabbitMQ 怎么避免消息丢失?
答:把消息持久化磁盘,保证服务器重启消息不丢失。
每个集群中至少有一个物理磁盘,保证消息落入磁盘。
154. 要保证消息持久化成功的条件有哪些?
答:声明队列必须设置持久化 durable 设置为 true.
消息推送投递模式必须设置持久化,deliveryMode 设置为 2(持久)。
消息已经到达持久化交换器。
消息已经到达持久化队列。
以上四个条件都满足才能保证消息持久化成功。
155. RabbitMQ 持久化有什么缺点?
答:持久化的缺地就是降低了服务器的吞吐量,因为使用的是磁盘而非内存存储,从而降低了吞吐量。可尽量使用 ssd 硬盘来缓解吞吐量的问题。
156. RabbitMQ 有几种广播类型?
答:direct(默认方式):最基础最简单的模式,发送方把消息发送给订阅方,如果有多个订阅者,默认采取轮询的方式进行消息发送。
headers:与 direct 类似,只是性能很差,此类型几乎用不到。
fanout:分发模式,把消费分发给所有订阅者。
topic:匹配订阅模式,使用正则匹配到消息队列,能匹配到的都能接收到。
157. RabbitMQ 怎么实现延迟消息队列?
答:延迟队列的实现有两种方式:
通过消息过期后进入死信交换器,再由交换器转发到延迟消费队列,实现延迟功能;
使用 RabbitMQ-delayed-message-exchange 插件实现延迟功能。
158. RabbitMQ 集群有什么用?
答:集群主要有以下两个用途:
高可用:某个服务器出现问题,整个 RabbitMQ 还可以继续使用;
高容量:集群可以承载更多的消息量。
159. RabbitMQ 节点的类型有哪些?
答:磁盘节点:消息会存储到磁盘。
内存节点:消息都存储在内存中,重启服务器消息丢失,性能高于磁盘类型。
150. RabbitMQ 集群搭建需要注意哪些问题?
答:各节点之间使用“--link”连接,此属性不能忽略。
各节点使用的 erlang cookie 值必须相同,此值相当于“秘钥”的功能,用于各节点的认证。
整个集群中必须包含一个磁盘节点。
151. RabbitMQ 每个节点是其他节点的完整拷贝吗?为什么?
答:不是,原因有以下两个:
存储空间的考虑:如果每个节点都拥有所有队列的完全拷贝,这样新增节点不但没有新增存储空间,反而增加了更多的冗余数据;
性能的考虑:如果每条消息都需要完整拷贝到每一个集群节点,那新增节点并没有提升处理消息的能力,最多是保持和单节点相同的性能甚至是更糟。
152. RabbitMQ 集群中唯一一个磁盘节点崩溃了会发生什么情况?
答:如果唯一磁盘的磁盘节点崩溃了,不能进行以下操作:
不能创建队列
不能创建交换器
不能创建绑定
不能添加用户
不能更改权限
不能添加和删除集群节点
唯一磁盘节点崩溃了,集群是可以保持运行的,但你不能更改任何东西。
153. RabbitMQ 对集群节点停止顺序有要求吗?
答:RabbitMQ 对集群的停止的顺序是有要求的,应该先关闭内存节点,最后再关闭磁盘节点。如果顺序恰好相反的话,可能会造成消息的丢失。
(十五)Kafka
152. kafka 可以脱离 zookeeper 单独使用吗?为什么?
答:kafka 不能脱离 zookeeper 单独使用,因为 kafka 使用 zookeeper 管理和协调 kafka 的节点服务器。
153. kafka 有几种数据保留的策略?
答:kafka 有两种数据保存策略:按照过期时间保留和按照存储的消息大小保留。
154. kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理?
答:这个时候 kafka 会执行数据清除工作,时间和大小不论那个满足条件,都会清空数据。
155. 什么情况会导致 kafka 运行变慢?
答:cpu 性能瓶颈
磁盘读写瓶颈
网络瓶颈
156. 使用 kafka 集群需要注意什么?
答:集群的数量不是越多越好,最好不要超过 7 个,因为节点越多,消息复制需要的时间就越长,整个群组的吞吐量就越低。
集群数量最好是单数,因为超过一半故障集群就不能用了,设置为单数容错率更高。
(十六)Zookeeper
157. zookeeper 是什么?
答:zookeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 google chubby 的开源实现,是 hadoop 和 hbase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
158. zookeeper 都有哪些功能?
答:集群管理:监控节点存活状态、运行请求等。
主节点选举:主节点挂掉了之后可以从备用的节点开始新一轮选主,主节点选举说的就是这个选举的过程,使用 zookeeper 可以协助完成这个过程。
分布式锁:zookeeper 提供两种锁:独占锁、共享锁。独占锁即一次只能有一个线程使用资源,共享锁是读锁共享,读写互斥,即可以有多线线程同时读同一个资源,如果要使用写锁也只能有一个线程使用。zookeeper可以对分布式锁进行控制。
命名服务:在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。
159. zookeeper 有几种部署模式?
答:zookeeper 有三种部署模式:
单机部署:一台集群上运行;
集群部署:多台集群运行;
伪集群部署:一台集群启动多个 zookeeper 实例运行。
160. zookeeper 怎么保证主从节点的状态同步?
答:zookeeper 的核心是原子广播,这个机制保证了各个 server 之间的同步。实现这个机制的协议叫做 zab 协议。 zab 协议有两种模式,分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,zab 就进入了恢复模式,当领导者被选举出来,且大多数 server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader 和 server 具有相同的系统状态。
161. 集群中为什么要有主节点?
答:在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,所以就需要主节点。
162. 集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?
答:可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。
163. 说一下 zookeeper 的通知机制?
答:客户端端会对某个 znode 建立一个 watcher 事件,当该 znode 发生变化时,这些客户端会收到 zookeeper 的通知,然后客户端可以根据 znode 变化来做出业务上的改变。
(十七)MySQL
164. 数据库的三范式是什么?
答:第一范式:强调的是列的原子性,即数据库表的每一列都是不可分割的原子数据项。
第二范式:要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。
第三范式:任何非主属性不依赖于其它非主属性。
165. 一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 MySQL 数据库,又插入了一条数据,此时 id 是几?
答:表类型如果是 MyISAM ,那 id 就是 18。
表类型如果是 InnoDB,那 id 就是 15。
InnoDB 表只会把自增主键的最大 id 记录在内存中,所以重启之后会导致最大 id 丢失。
166. 如何获取当前数据库版本?
答:使用 select version() 获取当前 MySQL 数据库版本。
167. 说一下 ACID 是什么?
答:Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
168. char 和 varchar 的区别是什么?
答:char(n) :固定长度类型,比如订阅 char(10),当你输入"abc"三个字符的时候,它们占的空间还是 10 个字节,其他 7 个是空字节。
chat 优点:效率高;缺点:占用空间;适用场景:存储密码的 md5 值,固定长度的,使用 char 非常合适。
varchar(n) :可变长度,存储的值是每个值占用的字节再加上一个用来记录其长度的字节的长度。
所以,从空间上考虑 varcahr 比较合适;从效率上考虑 char 比较合适,二者使用需要权衡。
169. float 和 double 的区别是什么?
答:float 最多可以存储 8 位的十进制数,并在内存中占 4 字节。
double 最可可以存储 16 位的十进制数,并在内存中占 8 字节。
170. MySQL 的内连接、左连接、右连接有什么区别?
答:内连接关键字:inner join;左连接:left join;右连接:right join。
内连接是把匹配的关联数据显示出来;左连接是左边的表全部显示出来,右边的表显示出符合条件的数据;右连接正好相反。
171. MySQL 索引是怎么实现的?
答:索引是满足某种特定查找算法的数据结构,而这些数据结构会以某种方式指向数据,从而实现高效查找数据。
具体来说 MySQL 中的索引,不同的数据引擎实现有所不同,但目前主流的数据库引擎的索引都是 B+ 树实现的,B+ 树的搜索效率,可以到达二分法的性能,找到数据区域之后就找到了完整的数据结构了,所有索引的性能也是更好的。
172. 怎么验证 MySQL 的索引是否满足需求?
答:使用 explain 查看 SQL 是如何执行查询语句的,从而分析你的索引是否满足需求。
explain 语法:explain select * from table where type=1。
173. 说一下数据库的事务隔离?
答:MySQL 的事务隔离是在 MySQL. ini 配置文件里添加的,在文件的最后添加:
transaction-isolation = REPEATABLE-READ
可用的配置值:READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE。
READ-UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)。
READ-COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读)。
REPEATABLE-READ:可重复读,默认级别,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读)。
SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。
不可重复读 :是指在一个事务内,多次读同一数据。
幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。
174. 说一下 MySQL 常用的引擎?
答:InnoDB 引擎:InnoDB 引擎提供了对数据库 acid 事务的支持,并且还提供了行级锁和外键的约束,它的设计的目标就是处理大数据容量的数据库系统。MySQL 运行的时候,InnoDB 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎是不支持全文搜索,同时启动也比较的慢,它是不会保存表的行数的,所以当进行 select count(*) from table 指令的时候,需要进行扫描全表。由于锁的粒度小,写操作是不会锁定全表的,所以在并发度较高的场景下使用会提升效率的。
MyIASM 引擎:MySQL 的默认引擎,但不提供事务的支持,也不支持行级锁和外键。因此当执行插入和更新语句时,即执行写操作的时候需要锁定这个表,所以会导致效率会降低。不过和 InnoDB 不同的是,MyIASM 引擎是保存了表的行数,于是当进行 select count(*) from table 语句时,可以直接的读取已经保存的值而不需要进行扫描全表。所以,如果表的读操作远远多于写操作时,并且不需要事务的支持的,可以将 MyIASM 作为数据库引擎的首选。
175. 说一下 MySQL 的行锁和表锁?
答:MyISAM 只支持表锁,InnoDB 支持表锁和行锁,默认为行锁。
表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发量最低。
行级锁:开销大,加锁慢,会出现死锁。锁力度小,发生锁冲突的概率小,并发度最高。
176. 说一下乐观锁和悲观锁?
答:乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。
数据库的乐观锁需要自己实现,在表里面添加一个 version 字段,每次修改成功值加 1,这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就不修改,这样就实现了乐观锁。
177. MySQL 问题排查都有哪些手段?
答:使用 show processlist 命令查看当前所有连接信息。
使用 explain 命令查询 SQL 语句执行计划。
开启慢查询日志,查看慢查询的 SQL。
178. 如何做 MySQL 的性能优化?
答:为搜索字段创建索引。
避免使用 select *,列出需要查询的字段。
垂直分割分表。
选择正确的存储引擎。
178.你一般都使用什么方式进行数据备份,在你之前的项目中备份的周期都是多少?
答:一般都是把项目的数据备份到云盘中,还有利用Local 本地硬盘间的备份
LPT 网络硬盘间的备份,备份周期具体看项目的进度
(十八)Redis
179. Redis 是什么?都有哪些使用场景?
答:Redis 是一个使用 C 语言开发的高速缓存菲关系型数据库-NOSql-Redis。
Redis 使用场景:
记录帖子点赞数、点击数、评论数;
缓存近期热帖;
缓存文章详情信息;
记录用户会话信息。
180. Redis 有哪些功能?
答:数据缓存功能
分布式锁的功能
支持数据持久化
支持事务
支持消息队列
181. Redis 和 memecache 有什么区别?
答:存储方式不同:memecache 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小;Redis 有部份存在硬盘上,这样能保证数据的持久性。
数据支持类型:memcache 对数据类型支持相对简单;Redis 有复杂的数据类型。
使用底层模型不同:它们之间底层实现方式,以及与客户端之间通信的应用协议不一样,Redis 自己构建了 vm 机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
value 值大小不同:Redis 最大可以达到 1gb;memcache 只有 1mb。
182. Redis 为什么是单线程的?
答:因为 cpu 不是 Redis 的瓶颈,Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且 cpu 又不会成为瓶颈,那就顺理成章地采用单线程的方案了。
关于 Redis 的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求。
而且单线程并不代表就慢 nginx 和 nodejs 也都是高性能单线程的代表。
183. 什么是缓存穿透?怎么解决?
答:缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
184. Redis 支持的数据类型有哪些?
答:Redis 支持的数据类型:string(字符串)、list(列表)、hash(字典)、set(集合)、zset(有序集合)。
185. Redis 支持的 Java 客户端都有哪些?
答:支持的 Java 客户端有 Redisson、jedis、lettuce 等。
186. jedis 和 Redisson 有哪些区别?
答:jedis:提供了比较全面的 Redis 命令的支持。
Redisson:实现了分布式和可扩展的 Java 数据结构,与 jedis 相比 Redisson 的功能相对简单,不支持排序、事务、管道、分区等 Redis 特性。
187. 怎么保证缓存和数据库数据的一致性?
答:合理设置缓存的过期时间。
新增、更改、删除数据库操作时同步更新 Redis,可以使用事物机制来保证数据的一致性。
188. Redis 持久化有几种方式?
答:Redis 的持久化有两种方式,或者说有两种策略:
RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。
AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。
189. Redis 怎么实现分布式锁?
答:Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序也要占“坑”的时候,占用成功了就可以继续执行,失败了就只能放弃或稍后重试。
占坑一般使用 setnx(set if not exists)指令,只允许被一个程序占有,使用完调用 del 释放锁。
190. Redis 分布式锁有什么缺陷?
答:Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题。
191. Redis 如何做内存优化?
答:尽量使用 Redis 的散列表,把相关的信息放到散列表里面存储,而不是把每个字段单独存储,这样可以有效的减少内存使用。比如将 Web 系统的用户对象,应该放到散列表里面再整体存储到 Redis,而不是把用户的姓名、年龄、密码、邮箱等字段分别设置 key 进行存储。
192. Redis 淘汰策略有哪些?
答:volatile-lru:从已设置过期时间的数据集(server. db[i]. expires)中挑选最近最少使用的数据淘汰。
volatile-ttl:从已设置过期时间的数据集(server. db[i]. expires)中挑选将要过期的数据淘汰。
volatile-random:从已设置过期时间的数据集(server. db[i]. expires)中任意选择数据淘汰。
allkeys-lru:从数据集(server. db[i]. dict)中挑选最近最少使用的数据淘汰。
allkeys-random:从数据集(server. db[i]. dict)中任意选择数据淘汰。
no-enviction(驱逐):禁止驱逐数据。
193. Redis 常见的性能问题有哪些?该如何解决?
答:主服务器写内存快照,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以主服务器最好不要写内存快照。
Redis 主从复制的性能问题,为了主从复制的速度和连接的稳定性,主从库最好在同一个局域网内。
(十九)JVM
194. 说一下 JVM 的主要组成部分?及其作用?
答:类加载器(ClassLoader)
运行时数据区(Runtime Data Area)
执行引擎(Execution Engine)
本地库接口(Native Interface)
组件的作用: 首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
195. 说一下 JVM 运行时数据区?
答:不同虚拟机的运行时数据区可能略微有所不同,但都会遵从 Java 虚拟机规范, Java 虚拟机规范规定的区域分为以下 5 个部分:
程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;
Java 虚拟机栈(Java Virtual Machine Stacks):用于存储局部变量表、操作数栈、动态链接、方法出口等信息;
本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;
Java 堆(Java Heap):Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;
方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。
196. 说一下堆栈的区别?
答:功能方面:堆是用来存放对象的,栈是用来执行程序的。
共享性:堆是线程共享的,栈是线程私有的。
空间大小:堆大小远远大于栈。
197. 队列和栈是什么?有什么区别?
答:队列和栈都是被用来预存储数据的。
队列允许先进先出检索元素,但也有例外的情况,Deque 接口允许从两端检索元素。
栈和队列很相似,但它运行对元素进行后进先出进行检索。
198. 什么是双亲委派模型?
答:在介绍双亲委派模型之前先说下类加载器。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立在 JVM 中的唯一性,每一个类加载器,都有一个独立的类名称空间。类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。
类加载器分类:
启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;
其他类加载器:
扩展类加载器(Extension ClassLoader):负责加载<java_home style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; outline: 0px !important;">\lib\ext目录或Java. ext. dirs系统变量指定的路径中的所有类库;</java_home>
应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。
199. 说一下类加载的执行过程?
答:类加载分为以下 5 个步骤:
加载:根据查找路径找到相应的 class 文件然后导入;
检查:检查加载的 class 文件的正确性;
准备:给类中的静态变量分配内存空间;
解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
初始化:对静态变量和静态代码块执行初始化工作。
200. 怎么判断对象是否可以被回收?
答:一般有两种方法来判断:
引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;
可达性分析:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
201. Java 中都有哪些引用类型?
答:强引用:发生 gc 的时候不会被回收。
软引用:有用但不是必须的对象,在发生内存溢出之前会被回收。
弱引用:有用但不是必须的对象,在下一次GC时会被回收。
虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用 PhantomReference 实现虚引用,虚引用的用途是在 gc 时返回一个通知。
202. 说一下 JVM 有哪些垃圾回收算法?
答:标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。
203. 说一下 JVM 有哪些垃圾回收器?
答:Serial:最早的单线程串行垃圾回收器。
Serial Old:Serial 垃圾回收器的老年版本,同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案。
ParNew:是 Serial 的多线程版本。
Parallel 和 ParNew 收集器类似是多线程的,但 Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系统的吞吐量。
Parallel Old 是 Parallel 老生代版本,Parallel 使用的是复制的内存回收算法,Parallel Old 使用的是标记-整理的内存回收算法。
CMS:一种以获得最短停顿时间为目标的收集器,非常适用 B/S 系统。
G1:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项。
204. 详细介绍一下 CMS 垃圾回收器?
答:CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。
CMS 使用的是标记-清除的算法实现的,所以在 gc 的时候回产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被降低。
205. 新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
答:新生代回收器:Serial、ParNew、Parallel Scavenge
老年代回收器:Serial Old、Parallel Old、CMS
整堆回收器:G1
新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。
206. 简述分代垃圾回收器是怎么工作的?
答:分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。
新生代使用的是复制算法,新生代里有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1,它的执行流程如下:
把 Eden + From Survivor 存活的对象放入 To Survivor 区;
清空 Eden 和 From Survivor 分区;
From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。
每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。
老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。
207. 说一下 JVM 调优的工具?
答:JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。
jconsole:用于对 JVM 中的内存、线程和类等进行监控;
jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。
208. 常用的 JVM 调优的参数都有哪些?
答:-Xms2g:初始化推大小为 2g;
-Xmx2g:堆最大内存为 2g;
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息。