目录
基础知识:
1. java中的基本数据类型有哪些?
答:byte, short, int, long, float, double, char, boolean. 剩下的都是引用类型(reference type)
2. String类可以被继承吗?
答:不可以. 因为String类是final类.
3. String和StringBuilder,StringBuffer的区别?
答:String是只读字符串,String引用的字符串内容是不能被改变的.而StringBuffer和StringBuilder是可变字符串.StringBuilder和StringBuffer的用法相同, 区别是StringBuffer被synchronized修饰,是线程安全的,但效率比StringBuilder低。
4.构造器是否可以被重写?
答: 构造器不能被继承,因此不能被重写,但是可以被重载.
5.抽象类和接口的相同点和不同点.
答: 1)抽象类和接口都不能实例化对象,但是可以定义抽象类和接口类型的引用.
2)继承抽象类和实现接口都要对其中的抽象方法全部实现
3)接口比抽象类更加抽象,抽象类中可以定义构造器,可以有抽象方法和具体方法.
4)接口中方法全部都是抽象方法.
5)抽象类中的成员可以是private,protected,public,接口全部都是public
6)抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量.
7)有抽象方法的类必须声明为抽象类,而抽象类未必要有抽象方法.
6. java中会存在内存泄露吗?
答:理论上java不会存在内存泄露的问题,应为有垃圾回收机制(GC).然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,因此会导致内存泄露. 例如hibernated的Session中的对象属于持久态,垃圾回收器不会回收这些对象,这些对象中有可能存在无用的垃圾对象.如果关闭不及时,一级缓存就可能导致内存泄露.
7.final,finally, finalize有什么区别?
答:
- final用于声明属性、方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
- finally是异常处理语句结构的一部分,表示总是执行
- finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。
8. List,Map,Set 三个接口存取元素时,各自有什么特点?
答:
- List以特定的索引来存取元素,可以有重复元素
- Set不能存放重复元素.
- Map保存键值对的映射,映射关系可以是一对一或多对一.
- Set和Map容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储理论存取时间复杂度是O(1).
9. 线程的sleep()方法和yield()方法有什么区别?
答:
- sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;
- yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
- 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
- sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
- sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。
10. 转发(forward)和重定向(redirect)的区别?
答:forward是容器中控制权的转向,是服务器请求资源,服务器直接访问目标地址的URL,把那个URL 的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址redirect就是服务器端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,因此从浏览器的地址栏中可以看到跳转后的链接地址,很明显redirect无法访问到服务器保护起来资源,但是可以从一个网站redirect到其他网站。
11.Cookie和Session的区别:
1)cookie数据存放在客户的浏览器上,session数据放在服务器上。
2)cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。
3)session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
4)单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
所以个人建议:将登陆信息等重要信息存放为session;其他信息如果需要保留,可以放在cookie中。
12.位运算符可能会出现的笔试题目:
1)交换两个变量的值,不准出现第三方变量。例如:int a = 3;int b = 5;
/*
第一种方式: 定义第三方变量。
int temp = a; //3
a = b; //a = 5
b = temp;
方式2:相加法, 缺点: 两个int类型的数据相加,有可能会出现超出int的表示范围。
a = a+b; // a =8
b = a-b; //b = 8 - 5 = 3
a = a-b; // a = 8 - 3 = 5
方式3: 可以使用异或。 缺点: 逻辑不清晰。
*/
a = a^b; // a = 3^5
b = a^b; // b = (3^5)^5 = 3
a = a^b; // a = (5^3)^3 = 5
2)取出一个二进制数据的指定位数。要求读取该二进制数据的低4位。则用与的方式来做,即低4位用1去与,其他位用0去与运算。
13.解释内存中的栈(stack)、堆(heap)和静态区(static area)的用法。
答:通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而通过new关键字和构造器创建的对象放在堆空间;程序中的字面量(literal)如直接书写的100、”hello”和常量都是放在静态区中。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,理论上整个内存没有被其他进程使用的空间甚至硬盘上的虚拟内存都可以被当成堆空间来使用。
如:String str =
new
String(
"hello");
语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而”hello”这个字面量放在静态区。
14.为什么使用数据索引能提高效率?
答:
1)数据索引的存储是有序的
2)在有序的情况下,通过索引查询一个数据是无需遍历索引记录的
3)极端情况下,数据索引的查询效率为二分法查询效率,趋近于 log2(N)
15.重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?
答:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。
16.抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰?
答:都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
17.请说出与线程同步以及线程调度相关的方法。
答:
- wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
- notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
- notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态。
提示:关于Java多线程和并发编程的问题,建议大家看我的另一篇文章《关于Java并发编程的总结和思考》。
补充:Java 5通过Lock接口提供了显式的锁机制(explicit lock),增强了灵活性以及对线程的协调。Lock接口中定义了加锁(lock())和解锁(unlock())的方法,同时还提供了newCondition()方法来产生用于线程之间通信的Condition对象;此外,Java 5还提供了信号量机制(semaphore),信号量可以用来限制对某个共享资源进行访问的线程的数量。在对资源进行访问之前,线程必须得到信号量的许可(调用Semaphore对象的acquire()方法);在完成对资源的访问后,线程必须向信号量归还许可(调用Semaphore对象的release()方法)。
18. 编写多线程程序有几种实现方式?
答:Java 5以前实现多线程有两种实现方法:一种是继承Thread类;另一种是实现Runnable接口。两种方式都要通过重写run()方法来定义线程的行为,推荐使用后者,因为Java中的继承是单继承,一个类有一个父类,如果继承了Thread类就无法再继承其他类了,显然使用Runnable接口更为灵活。
补充:Java 5以后创建线程还有第三种方式:实现Callable接口,该接口中的call方法可以在线程执行结束时产生一个返回值,代码如下所示:
19. Java中如何实现序列化,有什么意义?
答:序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)。
要实现序列化,需要让一个类实现Serializable接口,该接口是一个标识性接口,标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通过writeObject(Object)方法就可以将实现对象写出(即保存其状态);如果需要反序列化则可以用一个输入流建立对象输入流,然后通过readObject方法从流中读取对象。序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆。
20.Java中有几种类型的流?
答:字节流和字符流。字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。在java.io 包中还有许多其他的流,主要是为了提高性能和使用方便。
关于Java的I/O需要注意的有两点:一是两种对称性(输入和输出的对称性,字节和字符的对称性);二是两种设计模式(适配器模式和装潢模式)。另外Java中的流不同于C#的是它只有一个维度一个方向。
21.如何通过反射创建对象?
答:
- 方法1:通过类对象调用newInstance()方法,例如:String.class.newInstance()
- 方法2:通过类对象的getConstructor()或getDeclaredConstructor()方法获得构造器(Constructor)对象并调用其newInstance()方法创建对象,例如:String.class.getConstructor(String.class).newInstance(“Hello”);
22. MyBatis中使用#和$书写占位符有什么区别?
答:#将传入的数据都当成一个字符串,会对传入的数据自动加上引号;$将传入的数据直接显示生成在SQL中。注意:使用$占位符可能会导致SQL注射攻击,能用#的地方就不要使用$,写order by子句的时候应该用$而不是#。
23.MyBatis中的动态SQL是什么意思?
答:对于一些复杂的查询,我们可能会指定多个查询条件,但是这些条件可能存在也可能不存在,例如在58同城上面找房子,我们可能会指定面积、楼层和所在位置来查找房源,也可能会指定面积、价格、户型和所在位置来查找房源,此时就需要根据用户指定的条件动态生成SQL语句。如果不使用持久层框架我们可能需要自己拼装SQL语句,还好MyBatis提供了动态SQL的功能来解决这个问题。MyBatis中用于实现动态SQL的元素主要有:
- if
- choose / when / otherwise
- trim
- where
- set
- foreach
24.什么是IoC和DI?DI是如何实现的?
答:IoC叫控制反转,是Inversion of Control的缩写,DI(Dependency Injection)叫依赖注入,是对IoC更简单的诠释。控制反转是把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的”控制反转”就是对组件对象控制权的转移,从程序代码本身转移到了外部容器,由容器来创建对象并管理对象之间的依赖关系。IoC体现了好莱坞原则 – “Don’t call me, we will call you”。依赖注入的基本原则是应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由容器负责,查找资源的逻辑应该从应用组件的代码中抽取出来,交给容器来完成。DI是对IoC更准确的描述,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。
举个例子:一个类A需要用到接口B中的方法,那么就需要为类A和接口B建立关联或依赖关系,最原始的方法是在类A中创建一个接口B的实现类C的实例,但这种方法需要开发人员自行维护二者的依赖关系,也就是说当依赖关系发生变动的时候需要修改代码并重新构建整个系统。如果通过一个容器来管理这些对象以及对象的依赖关系,则只需要在类A中定义好用于关联接口B的方法(构造器或setter方法),将类A和接口B的实现类C放入容器中,通过对容器的配置来实现二者的关联。
依赖注入可以通过setter方法注入(设值注入)、构造器注入和接口注入三种方式来实现,Spring支持setter注入和构造器注入,通常使用构造器注入来注入必须的依赖关系,对于可选的依赖关系,则setter注入是更好的选择,setter注入需要类提供无参构造器或者无参的静态工厂方法来创建对象。
25.解释一下什么叫AOP(面向切面编程)?
答:AOP(Aspect-Oriented Programming)指一种程序设计范型,该范型以一种称为切面(aspect)的语言构造为基础,切面是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点(crosscutting concern)。
”横切关注”是会影响到整个应用程序的关注功能,它跟正常的业务逻辑是正交的,没有必然的联系,但是几乎所有的业务逻辑都会涉及到这些关注功能。通常,事务、日志、安全性等关注就是应用中的横切关注功能。
26.Spring中自动装配的方式有哪些?
答:
- no:不进行自动装配,手动设置Bean的依赖关系。
- byName:根据Bean的名字进行自动装配。
- byType:根据Bean的类型进行自动装配。
- constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误。
- autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配。
说明:自动装配没有自定义装配方式那么精确,而且不能自动装配简单属性(基本类型、字符串等),在使用时应注意。
27.Spring中的自动装配有哪些限制?
答:
- 如果使用了构造器注入或者setter注入,那么将覆盖自动装配的依赖关系。
- 基本数据类型的值、字符串字面量、类字面量无法使用自动装配来注入。
- 优先考虑使用显式的装配来进行更精确的依赖注入而不是使用自动装配。
28. 线程和进程的区别?
答:线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。
29.B+树索引和哈希索引的区别?
答:
1)B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的节点间有指针相互链接,是有序的;
2)哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可,是无序的。
30.Runnable接口和Callable接口的区别?
答:Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
31.volatile关键字的作用?
答:
1)使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,一定是最新的数据。
2)对任意单个volatile关键字修饰的变量的读/写具有原子性,即不可被中断的一个或一系列操作。
32.sleep方法和wait方法有什么区别 ?
答:sleep方法和wait方法都可以用来放弃CPU一定的时间,不同点在于如果线程持有某个对象的监视器,sleep方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器。
33.什么是乐观锁和悲观锁?
答:
1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁;
2)悲观锁:还是像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁。
34.垃圾回收的优点和原理,并考虑2种回收机制?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
答:
1)优点:可以有效的防止内存泄漏,有效的使用可以使用的内存;
2)原理:对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆中的所有对象。通过这种方式确定哪些对象是“可达的”,哪些对象是“不可达的”。当GC确定一些对象为“不可达”时,GC就有责任回收这些内存空间。
3)回收机制:分代复制垃圾回收、标记垃圾回收和增量垃圾回收。
4)垃圾回收器可以马上回收内存。
5)程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定执行。
35.请说出 ArrayList和Vector的区别?
答:主要从两方面来说:
1)同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程不安全的,不是同步的。
2)数据增长:当需要增长时,Vector默认增长为原来的一倍,而ArrayList却是原来的一半。
36.HashMap和Hashtable的区别?
答:
- Hashtable是线程安全,而HashMap则非线程安全;
- HashMap可以使用null作为key,而Hashtable则不允许null作为key;
- HashMap是对Map接口的实现,HashTable实现了Map接口和Dictionary抽象类。
37. 动态代理的概念,实现动态代理的方式以及区别?
答:动态代理是指运行时动态生成代理类。方式包含:JDK和Cglib动态代理;区别:JDK动态代理是通过接口的方式实现的,而Cglib动态代理是通过继承当前类的子类实现的。
38. 如何避免SQL注入?
答:
- 使用预处理PreparedStatement
- 使用正则表达式过滤掉字符中的特殊字符
39. Spring常用的注入方式有哪些?
答:
- Setter属性注入
- 构造方法注入
- 注解方式注入
40. Spring事务实现方式?
答:
- 声明式事务:包含两种实现方式,分别为基于xml配置文件的方式和基于注解的方式(即@Tranasction)
- 编码方式:提供编码的形式管理和维护事务
41. 为什么要用 spring boot?
答:
- 配置简单
- 独立运行
- 无代码生成和XML配置
- 自动装配
- 提供应用监控
- 易上手
- 提升开发效率
42. spring cloud 断路器的作用是什么?
答:在分布式架构中,断路器模式的作用是类似的,当某个服务单元发生故障之后,通过断路器的故障监控,向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
43. spring cloud 的核心组件有哪些?
答:
- Eureka:服务注册于发现
- Feign:基于动态代理机制,根据注解和选择的机器,拼接请求 url 地址,发起请求
- Ribbon:实现负载均衡,从一个服务的多台机器中选择一台
- Hystrix:提供线程池,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题
- Zuul:网关管理,由 Zuul 网关转发请求给对应的服务
44. 进程之间的通信方式?管道和命名管道的区别?
答:
(1)进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。
IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。
(2)主要区别在于被创建和访问的方式上。 命名管道可以在无关的进程之间交换数据,管道只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间);管道不属于其他任何文件系统,并且只存在于内存中,而命名管道有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
45. 什么是死锁?死锁产生的原因?死锁的四个必要条件?怎么破坏死锁?
答:
(1)多个并发进程因争夺系统资源而产生相互等待的现象。
(2)原因:
- 系统资源的竞争:系统资源的竞争导致系统资源不足,以及资源分配不当,导致死锁。
- 进程运行推进顺序不合适:进程在运行过程中,请求和释放资源的顺序不当,会导致死锁。
(3)必要条件:
- 互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束;
- 占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源;
- 不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来;
- 循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。
(4)解除办法:
- 抢占资源:从一个或多个进程中抢占足够数量的资源分配给死锁进程,以解除死锁状态。
- 终止(或撤销)进程:终止或撤销系统中的一个或多个死锁进程,直至打破死锁状态。
Java集合相关:
1. 常用的集合有哪些?
- Set:实现类有:HashSet、TreeSet、LinkedHashSet和SortedSet
- List:实现类有:ArrayList、LinkedList和Vector
- Map:实现类有:HashMap、LinkedHashMap、TreeMap、Hashtable和ConcurrentHashMap
2. HashMap和HashTable的区别?
- HashMap是线程不安全的,而HashTable是线程安全的
- HashMap允许键和值都为null,而HashTable不允许键和值为null
- HashMap继承自AbstractMap类,而HashTable继承自Dictionary类
3. HashMap的工作原理?
HashMap基于hashing原理,通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,然后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
4. HashMap的put方法的原理或过程?
HashMap在调用put方法的时候,HashMap使用Key的hashCode()和哈希算法来找出存储key-value对的索引。如果索引处为空,则直接插入到对应的数组中,否则,判断key是否相等,相等就将value替换,不相等就判断是否是红黑树,若是,则以红黑树方式插入,否则遍历链表,找到key那么就修改value,没找到就新建节点插到链表尾部,最后判断链表长度是否大于8,来判断是否需要将链表转为红黑树。
5. HashMap的get方法的原理或过程?
HashMap在调用put方法的时候,HashMap使用Key的hashCode()和哈希算法来找出存储key-value对的索引,如果索引处为空,则直接返回空,否则,判断key是否相等,相等就直接返回value,不相等则判断是否是红黑树,是则遍历整棵树来查找,不是则遍历这个链表来查找。
6. 为什么HashMap中链表长度大于8就要转为红黑树?
HashMap中的元素初始化是链表保存的,其查找性能是O(n),而树结构能将查找性能提升到O(log(n))。当链表长度很小时,遍历速度也非常快,但当链表长度不断变长,查找性能会有一定影响,所以需要转成红黑树。
7. HashMap的扩容机制?
当HashMap中的元素个数超过数组容量大小*负载因子(默认为0.75),就会进行数组扩容,默认情况数组大小为16,即当HashMap中的元素个数超过16*0.75=12时,就会把数组的大小扩展为2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常耗性能的操作。
8. HashMap在JDK1.7和JDK1.8中有哪些不同?
- 存储结构不同:1.7版用的是数组+链表,而1.8版用的是数组+链表+红黑树
- 初始化方式不同:1.7版使用单独函数inflateTable(),而1.8版直接集成到了扩容函数resize()中
- Hash值计算方式不同:1.7版本的扰动处理=9次扰动=4次位运算+5次异或运算,而1.8版本的扰动处理=2次扰动=1次位运算+1次异或运算
- 存放数据的规则不同:1.7版本在无冲突时,数据存放在数组,有冲突时,存放在链表;而1.8版本在无冲突时,数据存放在数组,有冲突并且链表长度小于8时,存放在链表,有冲突且链表长度大于8时,树化并存放在红黑树
- 插入数据方式不同:1.7版本采用头插法(先将原位置的数据移到后1位,再插入数据到该位置),而1.8版本采用尾插法(直接插入数据到链表尾部或红黑树)
- 扩容后存储位置的计算方式不同:1.7版本全部按照原来方法计算,而1.8版本按照扩容后的规律计算
9. HashMap是怎么解决哈希冲突的?
- 使用链地址法(使用散列表)来链接拥有相同hash值得数据
- 使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均
- 引入红黑树进一步降低遍历的时间复杂度,使得遍历更快
10. ConcurrentHashMap和Hashtable的区别?
- ConcurrentHashMap实现了ConcurrenMap接口,继承了AbstractMap类;Hashtable实现了Map接口,继承了Dictionary类
- ConcurrentHashMap默认初始化长度为16,扩容加载因子为0.75,一旦大于0.75*16之后,就会调用resize()进行扩容,扩容2倍,即32;Hashtable默认初始化长度为11,达到扩容条件后将扩容到2n+1,即23
- ConcurrentHashMap使用锁分段技术,允许多个修改操作并发进行,而 Hashtable的同步锁synchronized是针对整张Hash表的,即每次锁住整张表让线程独占
11. ArrayList与Vector的区别?
- 同步性:Vector是线程安全的,ArrayList是线程不安全的
- 数据增长:但需要增长时,Vector默认增长为原来的一倍,而ArrayList却是原来的一半
12. ArrayList与LinkedList 的区别?
- LinkedList 实现了List和Deque接口,一般称为双向链表,ArrayList实现了List接口,动态数组
- LinkedList 在插入和删除数据时效率更高,ArrayList在查找数据时效率更高
- LinkedList 比ArrayList需要更多的内存
13. Array 和 ArrayList 有什么区别?什么时候该应 Array 而不是 ArrayList 呢?
(1)区别:
- Array可以包含基本类型和对象类型,ArrayList只能包含对象类型
- Array大小是固定的,ArrayList的大小是动态变化的
- ArrayList提供了更多的方法和特性,比如addAll()等等
(2)对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
14. HashSet是如何保证数据不可重复的?
HashSet的底层其实就是HashMap,HashSet实现Set接口并把数据作为HashMap的key,而value值一直使用一个相同的虚值来保存,HashMap的key本身就不允许重复,从而保证了数据的不可重复性。
红黑树详解链接:https://blog.csdn.net/u011240877/article/details/53329023
Java面试摘取
一、Java 基础知识
1. 面向对象的特性有哪些?
答:封装、继承、多态。
2. Java 中 Override 和 Overload 有什么区别?
- 重写(Override):方法名、参数、返回值相同;子类方法不能缩小父类方法的访问权限,且只能比父类抛出更少的异常;被覆盖的方法不能为 private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
- 重载(Overload):同一个类中可以有多个名称相同的方法,但这些方法的参数类型、个数、顺序至少有一个不相同。
追问:什么情况下方法不能被重写?
答:方法被定义为 final 的时候不能被重写。
3. 抽象类和接口有什么区别?
(1)抽象类:
- 抽象类使用 abstract 修饰;
- 抽象类不能使用 new 关键字来实例化对象;
- 抽象类可以含有抽象方法,也可以不包含抽象方法,抽象类中可以有具体的方法;
- 抽象类中的抽象方法只有方法体,没有具体实现。
(2)接口:
- 接口使用 interface 修饰;
- 接口不能被实例化;
- 一个类只能继承一个抽象类,但是可以实现多个接口;
- 接口中方法均为抽象方法;
- 接口中方法是抽象方法,不能实现。
追问:什么情况下选择接口,什么情况下选择抽象类?
答:当要创建不带任何方法定义和成员变量的基类,应该选择接口;当有方法定义和成员变量的时候,应该选择抽象类。
4. JRE、JDK、JVM 有什么区别?
- JRE: Java 运行环境,为 Java 的运行提供了所需环境。
- JDK: Java 开发工具包,提供了 Java 的开发环境和运行环境,JDK 包含 JRE 还有 Java 编译器、Java 调试和分析工具等。
- JVM 是 Java 虚拟机,负责将 Java 字节码转换为机器码,正是 Java 能够跨平台的原因。它还提供了内存管理、垃圾回收和安全机制等。
5. 值传递和引用传递有什么区别?
- 值传递:指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了。
- 引用传递:指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。
6. JDK 中常用的包有哪些?
- java.lang:这个是系统的基础类;
- java.io:这里面是所有输入输出有关的类,比如文件操作等;
- java.nio:为了完善 io 包中的功能,提高 io 包中性能而写的一个新包;
- java.net:这里面是与网络有关的类;
- java.util:这个是系统辅助类,特别是集合类;
- java.sql:这个是数据库操作的类。
7. 访问修饰符 public、protected、 default、private 的区别?
在这里插入图片描述
8. 数据基础类型有哪些?
答:基础类型有 8 种:byte、boolean、char、short、int、float、long、double,注意 String 不属于基础类型, 属于对象。
9. final 有什么用?
答:用于修饰类、属性和方法;凡是引用 final 关键字的地方皆不可修改!
- 修饰类:表示该类不能被继承;
- 修饰方法:表示方法不能被重写;
- 修饰变量:表示变量只能一次赋值以后,值不能被修改。
二、Java 核心知识
10. 常用的集合类有哪些?
答:最常用的集合类是 List 和 Map。
- List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。
- Map 提供了一个更通用的元素存储方法。Map 集合类用于存储元素对(称作” 键” 和” 值”),其中每个键映射到一个值。
- Map 接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap 等;
- List 接口的实现类主要有:ArrayList、LinkedList、Vector 等。
11. HashMap 和 Hashtable 有什么区别?
- HashMap 运行 key 和 value 为 null,而 Hashtable 不允许。
- Hashtable 是线程安全的,而 HashMap 是非线程安全的。
12. HashMap 的实现原理是什么?
答:HashMap 基于 Hash 算法实现的,我们通过 put(key,value) 存储,get(key) 来获取。当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,存储时,如果出现 hash 值相同的 key,此时有两种情况,如果 key 相同,则覆盖原始值;如果 key 不同,即出现冲突,则将当前的 key-value 放入链表中。获取时,直接找到 hash 值对应的下标,在进一步判断 key 是否相同,从而找到对应值。
13. ConcurrentHashMap 的实现原理是什么?
ConcurrentHashMap 是 Java1.5 中引用的一个线程安全的支持高并发的 HashMap 集合类。该类包含两个静态内部类 HashEntry 和 Segment,前者用来封装映射表的键值对,后者用来充当锁的角色。Segment 采用了非常精妙的“分段锁”策略,每个 Segment 对应一个 HashEntry 数组,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 锁。
14. ArrayList 和 LinkedList 的区别是什么?
- ArrayList 的实现是基于数组,LinkedList 的实现是基于双向链表;
- 对于随机访问,ArrayList 优于 LinkedList;
- 对于插入和删除操作,LinkedList 优于 ArrayList,因为当元素被添加到 LinkedList 任意位置的时候,不需要像 ArrayList 那样重新计算大小或者是更新索引;
- LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
15. 如何实现 Array 和 List 之间的转换?
- Array 转 List: Arrays. asList(array) ;
- List 转 Array:List 的 toArray() 方法。
16. 在 Queue 中 poll() 和 remove() 有什么区别?
答:两者都是返回第一个元素,并在队列中删除返回的对象。不同的是如果没有元素 poll() 会返回 null,而 remove() 会直接抛出异常。
17. 哪些集合类是线程安全的?
答:Vector、Hashtable 都是线程安全的,而 HashMap 是非线程安全的,不过 Java. util. concurrent 并发包中,ConcurrentHashMap 是线程安全的。
18. 什么是 Java 反射?
答:在 Java 运行时环境中,对于任意一个类,能够知道这个类有哪些属性和方法,对于任意一个对象,能够调用它的任意一个方法。
19. Java 怎么实现动态代理?
答:Java 中,常用的动态代理实现方式有两种,一种是利用 JDK 反射机制生成代理,另外一种是使用 CGLIB 代理。JDK 代理必须要提供接口,而 CGLIB 则不需要,可以直接代理类,是基于继承当前类的子类实现的。
三、Java 必问专题
20. 进程和线程有什么区别?
- 进程:是具有一定独立功能的程序、它是系统进行资源分配和调度的一个独立单位,重点在系统调度和单独的单位,也就是说进程是可以独 立运行的一段程序。
- 线程:线程进程的一个实体,是 CPU 调度和分派的基本单位,他是比进程更小的能独立运行的基本单位。
- 两者关系:一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程,资源分配给进程,同一进程的所有线程共享该进程的所有资源。
追问:GC(垃圾回收)知道吗?
答:垃圾回收线程是特殊的守护线程,守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
21. 创建线程有哪几种方式?
答:创建线程有三种方式:
- 继承 Thread 类重写 run 方法;
- 实现 Runnable 接口;
- 实现 Callable 接口。
22. 什么是多线程,多线程的优劣?
答:(1)多线程:多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务。
(2)多线程的好处:
- 可以提高 CPU 的利用率。
- 在多线程程序中,一个线程必须等待的时候,CPU 可以运行其它的线程而不是等待,这样就大大提高了程序的效率。也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
(3)多线程的劣势:
- 线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
- 多线程需要协调和管理,所以需要 CPU 时间跟踪线程;
- 线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题。
23. 你知道怎么创建线程池吗?
答 :创建线程池的方式有多种,这里你只需要答 ThreadPoolExecutor 即可。
ThreadPoolExecutor() 是最原始的线程池创建,也是阿里巴巴 Java 开发手册中明确规范的创建线程池的方式。
24. 线程池的常用参数有了解吗?
答:ThreadPoolExecutor 包含以下七个参数:
- corePoolSize:线程池中的核心线程数
- maximumPoolSize:线程池中允许存在的工作线程的最大数量
- keepAliveTime:闲置超时时间
- unit:超时时间的单位(时/分/秒等)
- workQueue:线程池中的任务队列
- threadFactory:为线程池提供创建新线程的线程工厂
- rejectedExecutionHandler:线程池任务队列超过 maxinumPoolSize 之后的拒绝策略
前两个参数可以这么理解,如果运行的线程少于 corePoolSize,Executor 则可以不排队直接添加新的线程;如果大于等于 corePoolSize,则将请求加入队列,如果无法加入,则创建新线程;如果超出 maxinumPoolSize,任务被拒绝。
25. 怎么保证线程安全?
- 使用安全类,比如 Java. util. concurrent 下的类;
- 使用锁,例如 synchronized、Lock。
26. synchronized 和 Lock 有什么区别?
- synchronized 会自动释放锁,Lock 需手工释放锁(unlock() 方法释放锁),否则容易造成线程死锁;
- synchronized 是 Java 的关键字,在 JVM 层面上;Lock 是一个类;
- synchronized 无法判断是否获取锁的状态,Lock 可以判断是否获取到锁。
27. 什么是死锁?
答:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。
追问:怎么避免?
- 加锁顺序,一个线程如果要获取多个锁,必须按照一定的顺序去获取;
- 加锁时限,一个线程尝试去获取锁,如果在指定的时间内获取不到,就放弃等待锁,并释放自己现在所持有的锁,然后随机等待一定时间,再去获取锁。
28. ThreadLocal 是什么?有哪些使用场景?
答:
(1)当使用 ThreadLocal 维护变量时,ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
(2)ThreadLocal 的经典使用场景是 session 管理(不同的线程对应不同的 session)和数据库链接(为每个线程创建不同的链接)。
29. 你了解哪些设计模式?
- 创建型模式,共五种:简单工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式。
- 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
- 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
30. 简单工厂模式和抽象工厂模式有什么区别?
- 简单工厂模式:是由一个工厂对象创建产品实例,简单工厂模式的类一般是使用静态方法,通过不同的参数的创建不同的对象实例,可以生产结构中的任意产品,不能增加新的产品。
- 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需制定他们具体的类,用来生产不同产品族的全部产品,对于增加新的产品,无能为力;支持增加产品族。
四、Java 框架
31. Spring 框架的优点有哪些?
- Spring 提供 IoC 技术,控制反转,容器会帮你管理依赖的对象,将对象的创建交给了 Spring,简化了开发,降低了代码之间的耦合性和侵入性。
- Spring 提供了面向切片编程,这样对某一类的问题,比如日志,异常统一处理。
- Spring 提供了事务支持,我们只需通过配置就可以完成对事物管理。
- 集成各种优秀的框架,提供了对各种优秀框架如(Hibernate,MyBatis)等的直接支持。
32. 什么是 Spring AOP?
答:即面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。主要体现在事务处理、日志管理、权限控制、异常处理等方面。
33. 什么是 Spring IoC?
答:即 inverse of control(控制反转),是 Spring 的核心,是一种创建对象的思想。将创建对象的权力交给 Spring 容器,其实就是让 Spring 容器帮你创建对象,而你不需要在 Java 代码中 new 对象了。
34. Spring 常用的注入方式有哪些?
- setter 注入
- 构造方法注入
- 注解注入
35. Spring 自动装配 bean 有哪些方式?
Spring 配置文件中 节点的 autowire 参数可以控制 bean 自动装配的方式:
- default:默认的方式和“no”方式一样
- no:不自动装配,需要使用节点或参数
- byName:根据名称进行装配
- byType:根据类型进行装配
- constructor:根据构造函数进行装配
36. @Autowired 是做什么用?
答:@Autowired 是一个注释,它可以对类成员变量、方法及构造函数进行标注,让 Spring 完成 bean 自动装配的工作。
37. Spring Boot 有什么优势?
- Spring Boot 继承了 Spring 优点,使得开发更加方便快捷。
- 简化编码
- 简化配置
- 简化部署
- 提供了监控系统
- 上手快,开发效率高
38. Spring Boot 配置文件有哪几种类型?
答:配置文件有 . properties 格式和 . yml 格式,它们主要的区别是书法风格不同。
39. Spring Cloud 是什么?
答:Spring Cloud 是基于 Spring Boot 的一整套实现微服务的框架。它它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,提供了微服务开发所需的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等组件。
40. Spring Cloud 的核心组件有哪些?
- Eureka:服务注册和服务发现。
- Hystrix:断路器,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
- Zuul:网关,提供动态路由、监控、弹性、安全等边缘服务的框架。
- SpringCloudConfig:配置中心,配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置。
- SpringCloudBus:消息总线,用于在集群(例如,配置变化事件)中传播状态变化。
当然,除了以上还有很多,就不一一列举了。
41. MyBatis 和 Hibernate 的区别有哪些?
- 灵活性:MyBatis 更加灵活,MyBatis 可以进行更为细致的 SQL 优化。
- 可移植性: Hibernate 数据库移植性很好,MyBatis 的数据库移植性不好,不同的数据库需要写不同 SQL。
- 技术门槛:MyBatis 入门比较简单。
- 二级缓存: Hibernate 有更好的二级缓存机制,可以使用第三方缓存。MyBatis 本身提供的缓存机制不佳。
五、分布式系统基础中间件
42. ZooKeeper 是什么?
答:ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 HBase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
43. ZooKeeper 集群选主和同步的机制是什么?
答:ZooKeeper 的核心是原子广播,这个机制保证了各个 Server 之间的同步,实现这个机制的协议叫做 ZAB 协议。ZAB 协议有两种模式:分别是恢复模式和广播模式。
- 恢复模式,ZooKeeper 配置为集群模式时,系统启动或者是当前 Leader 崩溃或者是当前 Leader 丢失大多数的 Follower,ZK 进入恢复模式,恢复模式需要重新选举出一个新的 Leader,当领导者被选举出来,且大多数 Server 的完成了和 Leader 的状态同步以后,恢复模式就结束了。
- 广播模式,状态同步保证了 Leader 和所有 Server 都具有相同的系统状态。这时候当 Server 加入 ZooKeeper 集群后,会先在恢复模式下启动该 Server,发现 Leader 后,并和 Leader 进行状态同步,待到同步结束,它也参与消息广播,即进入广播状态。ZooKeeper 服务一直维持在 Broadcast 状态,直到 Leader 崩溃了或者 Leader 失去了大部分的 Followers 支持,才会进入恢复模式,从新选举 Leader。
44. 集群中有 3 台服务器,宕机 1 台,这个时候 ZooKeeper 还可以使用吗?
答:可以,根据选票过半的选举原则,因此集群节点最好为奇数;单数服务器只要没超过一半的宕机就可以继续使用;所以 ZooKeeper 集群的容灾数量 =(集群总节点数 - 1)/2;也就是说 3 台服务器集群的话,最多允许 1 台宕机。
45. Redis 是什么?都有哪些使用场景?
答:
(1)Redis 是一个使用 C 语言开发的高效缓存内存数据库。
(2)Redis 使用场景:
- 缓存热点信息
- 缓存用户会话信息
46. 你使用过 Redis 的哪些常用功能?
- 数据缓存
- 分布式锁
47. Redis 支持的数据类型有哪些?
答:Redis 支持的数据类型:string、list、hash、set、zset(sorted set)。
48. Redis 和 Memcache 有什么区别?
- Redis 支持数据的持久化,而 Memcache 不行,因为 Redis 可以把数据持久化在硬盘,而 Memcache 只能放在内存。
- 支持的数据类型不同:Redis 有复杂的数据类型,Memcache 对数据类型支持相对简单。
- value 值大小不同:Redis 最大可以达到 512mb;Memcache 只有 1mb。
49. 什么是缓存穿透?怎么解决?
答:
(1)缓存穿透:
指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
(2)解决方案:
缓存空对象。如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
50. 缓存和数据库之间怎么保证双写一致性?
- 读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况,但串行化之后,就会导致系统的吞吐量会大幅度的降低,实际上一般不用这个办法。
- 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应;更新的时候,先删除缓存,然后再更新数据库,可以使用事物机制来保证数据的一致性。然后还要合理的设置缓存过期时间。
51. Redis 持久化有几种方式?
- RDB(Redis Database):原理是将 Reids 在内存中的数据库记录定时 dump 到磁盘上的 RDB 持久化)。
- AOF(Append Only File):原理是将 Reids 的操作日志以追加的方式写入文件。
52. Redis 怎么实现分布式锁?
答:Redis 分布式锁一般使用 setnx(set if not exists)指令,只允许被一个程序占有,占用成功了就可以继续执行,失败了就只能放弃或稍后重试,使用完调用 del 释放锁。
伪代码示例(这里只是简述,实际应用没有那么简单的,感兴趣的同学可以详细查一下资料):
加锁:setnx(key,threadId)
设置超时时间:expire(key,60)
解锁:del(key)
53. 消息队列的使用场景有哪些?
- 秒杀活动,削峰填谷(双十一),延迟业务处理。
- 模块解耦,把两个模块独立,比如支付完成后,给用户返积分,这个时候不用在支付模块里添加新增积分的功能,只需要把新增积分的接口添加到支付确认的消息队列即可,后面再添加任何功能只需要订阅对应的消息队列即可。
54. 消息队列包含哪些核心角色?
- 生产者(producer):负责产生消息;
- 消费者(Consumer):负责消费消息;
- 消息代理(Message Broker):负责存储消息和转发消息。
55. JMS 和消息队列的关系?
答:JMS(Java Messaging Service)是 Java 平台上有关面向消息中间件的技术规范,通过提供标准的产生、发送、接收消息的接口简化企业应用的开发,翻译为 Java 消息服务。它是 Java EE 中定义的一组标准 API,它自身并不是一个消息服务系统。
56. ActiveMQ 的基本使用?
- Linux 下安装 ActiveMQ:下载 activemq 压缩包(apache-activemq-5.13.0-bin.tar.gz)解压,执行 apache-activemq-5.13.0/bin/activemq,检测 activemq 是否安装成功:netstat -an | grep 61616。
- ActiveMQ 前端控制台端口默认 8161 端口,在 jetty.xml 中的 jettyPort 对应的 port 可以改成你想要设置的端口。部署完毕后可以打开 http://localhost:8161/admin/ 进入控制台页面,账号密码默认 admin。
- 在 conf/activemq.xml 中 61616 的默认端口来自 transportConnector 对应的 URI 地址,如果想要修改或新增端口,也可以在这个地方新增或修改。
- ActiveMQ 启动命令为 activemq start,停止命令为 activemq stop,查看运行状态命令为 activemq status。
57. ActiveMQ 消息传输模式
- 点对点模式:
在点对点模型中,一个消费者对应一个生产者,生产者将消息放入队列中,当消费者请求队列中的消息时,消息会从队列中取出,并投递给消费者,消息投递后会从队列中删除,这样就可以保证消息只能投递给一个接收者。而且消息发送客户端与接收客户端没有时间上的依赖,发送客户端可以在任何时刻发送信息到队列,而不需要知道接收客户端是不是在运行。
- 发布/订阅模式
一个生产者产生消息发送后,可以被多个消费者进行接收。消息会发送给一个主题,与队列类似,多个接收者都可以监听一个主题。但与队列不同的是,消息主题的所有订阅者都会接收到此消息。并且必须创建一个订阅者之后,才能消费发布者的消息,而且为了消费消息,订阅者必须保持运行的状态。
58. ActiveMQ 的消息持久化策略
- 消息持久性对于可靠消息传递来说是一种比较好的方法,即时发送者和接受者不是同时在线或者消息中心在发送者发送消息后宕机了,在消息中心重启后仍然可以将消息发送出去。消息持久性的原理很简单,就是在发送消息出去后,消息中心首先将消息存储在本地文件、内存或者远程数据库,然后把消息发送给接受者,发送成功后再把消息从存储中删除,失败则继续尝试。
- ActiveMQ 的消息持久化的默认机制是 KahaDB,还有 JDBC 不过一般不用,KahaDB 可用于任何场景, 提高了性能和恢复能力。消息存储使用一个事务日志和仅仅用一个索引文件来存储它所有的地址。KahaDB 是一个专门针对消息持久化的解决方案, 它对典型的消息使用模式进行了优化。在 Kaha 中,数据被追加到 data logs 中。当不再需要 log 文件中的数据的时候, log 文件会被丢弃。
- 配置方式:在 conf/activemq.xml 文件中
六、MySQL
59. 数据库的三范式是什么?
- 第一范式:强调的是列的原子性,即列不能够再分成其他几列;
- 第二范式:一是表必须有一个主键;二是没有包含在主键中的列必须完全依 赖于主键,而不能只依赖于主键的一部分;
- 第三范式:任何非主属性不依赖于其它非主属性。
60. ACID 分别指的是什么?
- 原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败;
- 一致性(Consistent):事务结束后系统状态是一致的;
- 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态;
- 持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。
61. SQL 的内连接、左连接、右连接有什么区别?
答:内连接是把匹配的关联字段显示出来;左连接会读取左边数据表的全部数据,即使右边数据表没有对应数据。右连接会读取右边数据表的全部数据,即使左边数据表没有对应数据。
62. MySQL 索引是怎么实现的?
答:MySQL 官方对索引的定义为:索引(Index)是帮助 MySQL 高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是一种数据结构,而这些数据结构会以某种方式指向数据,从而实现高效查找数据。目前主流的数据库引擎的索引都是 B+ 树实现的,索引的性能也是更好的。
63. 怎么验证 MySQL 的索引是否满足需求?
答:需要根据查询需求来判断配置哪种索引,确定索引后,可以通过 explain 命令来查看执行计划,确认是否满足需求。explain 语法:explain select * from table。
64. 说一下数据库的乐观锁和悲观锁?
- 乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
- 悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
65. MySQL 问题排查都有哪些手段?
- 使用 show PROCESSLIST 命令查看当前连接信息,通过 MySQL 的端口定位是哪个线程占用(netstat -ntp |grep 46888),通过线程号排查是哪个应用(ps -ef | grep pid);
- 使用 explain 命令查询 SQL 语句执行计划;
- 开启慢查询日志,查看慢查询的 SQL。
66. 如何做 MySQL 的性能优化?
- 合理创建索引;
- 禁止使用 select *,列出查询字段;
- 定期归档数据并拆分表。
七、JVM
67. 你了解类加载过程吗?
答:类加载的过程包括了加载、验证、准备、解析、初始化五个阶段:
- 加载:通过一个类的全限定名来获取其定义的二进制字节流,将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构,在堆中生成一个代表这个类的 Class 对象,作为方法区中这些数据的访问入口;
- 验证:主要作用就是确保被加载的类的正确性,主要包含文件格式的验证,元数据验证,字节码验证和符号引用验证;
- 准备:主要为类变量分配内存并设置初始值;
- 解析:主要是虚拟机将常量池中的符号引用转化为直接引用的过程。符号引用指的是一个标示,而直接引用是指向内存中的地址;
- 初始化:主要为类的静态变量赋予正确的初始值,JVM 负责对类进行初始化,主要对类变量进行初始化。
68. 什么是双亲委派模型?
当收到加载一个类的请求时,子类加载器并不会马上去加载,而是依次去请求父类加载器加载,一直往上请求到最高类加载器:启动类加载器。当启动类加载器加载不了的时候,依次往下让子类加载器进行加载。当达到最底下的时候,如果还是加载不到该类,就会出现 ClassNotFound 的异常。
69. 你熟悉 JVM 哪些垃圾回收算法?
- 标记—清除算法:标记无用对象,然后进行清除回收。缺点:会带来另一个新的问题:内存碎片化。
- 复制算法:首先还是先标记处待回收内存和不用回收的内存,然后将不用回收的内存复制到新的内存区域,这样旧的内存区域就可以全部回收,而新的内存区域则是连续的,解决了内存碎片化的问题,但是缺点是会损失掉部分系统内存,一般是新生代使用。
- 标记—整理算法:首先还是“标记”,标记过后,将不用回收的内存对象压缩到内存一端,此时即可直接清除边界处的内存,一般是老年代使用。
70. 你熟悉 JVM 哪些垃圾回收器?
答:这里简述几个经典常用的即可:
- Serial:单线程串行垃圾回收器。
- ParNew:多线程的串行垃圾回收器。
- Parallel 和 ParNew 类似是多线程的,但 Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系统的吞吐量。
- CMS:一种以获得最短回收停顿时间为目标的收集器,采用标记—清除算法。
71. 你熟悉 JVM 哪些调优参数?
- -Xms2g 堆初始值 2G
- -Xmx2g 堆最大可用内存 2G
- -XX:NewRatio=4 新生代: 老年代内存比例 = 1:4
- -XX:PrintGC 触发 GC 时打印日志
- -XX:PrintGCDetails 触发 GC 时打印详细日志
- -XX:UseSerialGC 使用 Serial 串行回收器
- -XX+UseParNewGC 使用 ParNew + Serial Old 垃圾回收器组合
八、如何准备技术面试
1. 明确技术方向,匹配岗位要求
学历、工作年限、技能、项目经历、软实力。找准定位,是要做技术还是偏管理。划重点:做技术就不要提管理。面试官非常讨厌技术不扎实的人,明明是面试技术,却总是提管理。这里不是说不需要管理能力,而是要分清楚主次,只有你的技术使面试官满意了,管理能力才能为你加分。
2. 盘点个人价值,找亮点
在 HR 们的眼中,我们都是商品,你要把自己的价值以最直观的方式体现出来,不要扯其他没用的。明确了自己有什么价值,你需要问自己一个问题:以行业标准衡量,你觉得你是哪个领域的专家?因为你只有是某个领域或者方向的专家,性价比高,HR 们才会觉得这笔买卖很划算。不要自惭形秽,你之所以平庸,是因为你没有深挖自己的亮点,在简历最后一栏自我评价里,把亮点一一列举出来,如果你实在是找不到,那总做过项目吧,能吃苦吧,善与人相处吧,逐条写出来。
划重点:项目经验多不代表能力强,要总结而不是叙述,千万不要逐个项目长篇叙述业务模块,面试官对你做过的项目业务逻辑根本不感兴趣,你需要把项目中你用过的技术列举出来,最好是只写最近的或者你最有成就感的项目,把你了解的技术栈全都写上。
3. 内推和猎头,机会大很多
选择公司也很重要,是互联网公司还是传统企业,传统企业相对重视业务,而互联网公司比较重视技术。公司主要是做什么产品、用的什么技术 。如果你的资历不够,学历双非,但技术很强,想去大厂也不是没机会。划重点:找内推、找猎头推荐要比自己投简历要机会大得多。
4. 知己知彼百战不殆
薪资、福利、平台优势、晋升机制、工作环境、人际关系、工作强度、社会地位等等,把这些全都列出来,做个表格,排好优先级。现在业界对于跳槽涨薪的幅度大都控制在 30% 以内,了解一下你的城市、这个企业这个技术方向的平均薪酬来参考。划重点:知己知彼才好跟 HR 谈薪。
5. 一份简历闯天下?不可取
不同的企业、岗位需要的技能不同,你需要制定有针对性的简历。
划重点:
不要随便写与岗位不匹配的技能;
只写项目中你负责的工作即可;
简历中的内容不要坑自己,每一句都要做到心中有数;
把项目成果最好能量化体现。
6. 练习自我介绍
直接划重点:
- 限时 3 分钟,不要太长不知所云,也不要太短显得你没准备;
- 姓名、年龄、学校、学历、工作年限,一个都不能落下,不要因为学校或者学历一般而不介绍,躲不过去的不如坦然面对;
- 两三句话概括你做过的项目,千万不要摊开了讲,否则你下面就不要说了,面试官一般会直接打断你,进入问答环节;
- 以总述的方式来讲你所擅长的技术,注意只谈技术别扯其他,诸如管理能力、组织能力、兴趣爱好等等千万别扯。
最后,拿得出手的经典项目准备一个,刷刷面试题,还要准备一些非技术类问题,比如这个必问的经典问题:你为什么离职?关于这个问题,说心里话,没必要那么固执。不要回答“上个公司加班太多,工作生活不能兼顾诸如此类 “,这样回答虽然没毛病,但是却不是标准答案,试问哪个互联网公司不加班?如果有,谁不想去?这个问题标准答案是:我的个人职业规划的原因,我需要一个更大的平台来施展,不断地见识、学习和成长。
最后还要准备一两个问面试官的问题,标准答案:贵公司当前使用的主流技术栈有哪些?如果我顺利通过面试,我将会加入哪个团队?我未来的工作方向是什么?千万别扯别的,诸如你们公司薪酬大概是多少?(保密的不知道吗),你们加班多不多?(废话)。管住好奇心,这些问题等你过了技术面试,留着 HR 来回答你。
大家看看,面试前都要准备那么多,如果是资深工程师岗位或者技术专家岗位,你可能起码要准备 3 个月,即便是初/中级工程师,面试前也要认真准备 1 个月,这样你的面试成功率才会高很多。