整理面试
来源于:https://mp.weixin.qq.com/s/UXjDP1xmkhBTaTObe8aH2Q
1.java事件机制包括哪三个部分?分别介绍。
事件、事件监听器、事件源
2、为什么要使用线程池?
首先,服务器创建和销毁工作线程的开销很大,如果服务器与很多客户端通信,并且与每个客户端通信的时间很短,那么就会在创建和销毁线程的时候造成很大的开销。(小而多,且频繁的任务)
其次,活动的线程也消耗系统资源,如果线程的创建数量没有限制,当大量的客户连接服务器的时候,就会创建出大量的工作线程,他们会消耗大量的内存空间,导致系统的内存空间不足,影响服务器的使用。(不加以控制耗尽内存资源)
最后,如果线程的数目固定,并且每个线程都有很长的生命周期,那么线程切换也就是固定的,这样就会给服务器减轻很多压力,但是如果频繁的创建和销毁线程,必将导致频繁的切换线程,使得线程之间的切换不再遵循系统的固定切换周期,线程切换的开销也会增大很多(维护一组固定的线程能减轻服务器压力)
3、线程池有什么作用?
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。
4、说说Java中几种常见的线程池及使用场景。
newCachedThreadPool(弹性缓存线程池) 底层:返回ThreadPoolExecutor实例
通俗:当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程来执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务;若池中线程空闲时间超过指定大小,则该线程会被销毁
适用:执行很多短期异步的小程序或者负载较轻的服务器
newFixedThreadPool 底层:返回ThreadPoolExecutor实例
通俗:创建可容纳固定数量线程的池子,每隔线程的存活时间是无限的,当池子满了就不在添加线程了;如果池中的所有线程均在繁忙状态,对于新任务会进入阻塞队列中(无界的阻塞队列)
适用:执行长期的任务,性能好很多
newSingleThreadExecutor(单线程线程池) 底层:FinalizableDelegatedExecutorService包装的ThreadPoolExecutor实例
通俗:创建只有一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中(无界的阻塞队列)
适用:一个任务一个任务执行的场景
NewScheduledThreadPool(定时器线程池): 底层:创建ScheduledThreadPoolExecutor实
通俗:创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构
适用:周期性执行任务的场景(轮巡)
备注
一般如果线程池任务队列采用LinkedBlockingQueue(无界)队列的话,那么不会拒绝任何任务(因为队列大小没有限制),这种情况下,ThreadPoolExecutor最多仅会按照最小线程数来创建线程,也就是说线程池大小被忽略了。
如果线程池任务队列采用ArrayBlockingQueue队列的话,那么ThreadPoolExecutor将会采取一个非常负责的算法,比如假定线程池的最小线程数为4,最大为8所用的ArrayBlockingQueue最大为10。随着任务到达并被放到队列中,线程池中最多运行4个线程(即最小线程数)。即使队列完全填满,也就是说有10个处于等待状态的任务,ThreadPoolExecutor也只会利用4个线程。如果队列已满,而又有新任务进来,此时才会启动一个新线程,这里不会因为队列已满而拒接该任务,相反会启动一个新线程。新线程会运行队列中的第一个任务,为新来的任务腾出空间
spring 中的线程池
Spring中的ThreadPoolTaskExecutor是借助于JDK并发包中的java.util.concurrent.ThreadPoolExecutor来实现的.
spring线程池与JDK线程池区别
spring线程池是通过JDK的线程池ThreadPoolExecutor的实现。但是具有如下两个优点
(1)不需要自己设置阻塞队列
只需要设置阻塞队列大小,不需要指定那种阻塞队列。
(2)自动关闭线程池
通过实现DisposableBean的destroy方法
5、线程池都有哪几种工作队列?
无界队列
队列大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列做为阻塞队列时要尤其当心,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM
有界队列
类是遵循FIFO原则的队列如ArrayBlockingQueue,另一类是优先级队列如PriorityBlockingQueue。PriorityBlockingQueue中的优先级由任务的Comparator决定。
6、怎么理解无界队列和有界队列?
有界队列也满,线程池执行了拒绝操作。
无界队列
无界队列会保持快速增长,直到耗尽系统内存。
7、线程池中的几种重要的参数及流程说明。
corePollSize:核心线程数。 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
maximumPoolSize:最大线程数。表明线程中最多能够创建的线程数量
keepAliveTime:空闲的线程保留的时间。
BlockingQueue<Runnable>:阻塞队列,存储等待执行的任务。参数有ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue可选
ThreadFactory:线程工厂,用来创建线程
RejectedExecutionHandler:队列已满,而且任务量大于最大线程的异常处理策略。有以下取值
8、什么是反射机制?
* 1.在运行时判断任意一个对象所属的类。
* 2.在运行时构造任意一个类的对象。
* 3.在运行时判断任意一个类所具有的成员变量和方法。
* 4.在运行时调用任意一个对象的方法。
这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
9、说说反射机制的作用。
1、反射最重要的用途就是开发各种通用框架。
1、获得Class对象 使用Class类的forName静态方法 调用某个对象的getClass()方法
2、判断是否为某个类的实例 用instanceof关键字来判断是否为某个类的实例
3、创建实例 使用Class对象的newInstance()方法来创建Class对象对应类的实例。
4、获取方法 getDeclaredMethods()
5、获取构造器信息
6、获取类的成员变量(字段)信息
getFiled: 访问公有的成员变量
getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量
getFileds和getDeclaredFields用法
7、调用方法 invoke()
注意: 由于反射会额外消耗一定的系统资源 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
1、你怎么理解http协议?
HTTP是Hyper Text Transfer Protocol(超文本传输协议) 默认端口号是80
协议中规定了客户端应该按照什么格式给服务器发送请求,同时也约定了服务端返回的响应结果应该是什么格
同时 HTTP 是一种无状态的协议,协议本身不记录客户端的历史请求记录。
参照:https://www.jianshu.com/p/b5993a20292a 讲解比较详细
12、说说http协议的工作流程。
(1)浏览器分析链接所指向页面的URL。
(2)浏览器向DNS服务器请求解析URL的IP地址。
(3)域名系统DNS解析出URL对应的IP地址。
(4)浏览器与服务器建立TCP连接(默认端口号80)。
(5)浏览器发出HTTP的GET请求报文。
(6)服务器通过HTTP响应报文把相应的文件发送给浏览器。
13、http有哪些请求提交方式?
POST、GET、HEAD、PUT 等
14、http中的200,302,403,404,500,503都代表什么状态?
200 - 服务器成功返回网页 404 - 请求的网页不存在 503 - 服务器超时
15、http get和post有什么区别?
最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。 ... GET请求在URL中传送的参数是有长度限制的,而POST么有。 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。 GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息
16、你怎么理解cookie和session,有哪些不同点?
cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围 若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失
当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识
(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来
使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相
关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应
中返回给客户端保存。
cookie数据存放在客户的浏览器上,session数据放在服务器上。 cookie不是很安全 session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
18、什么是https,说说https的工作原理?
HTTPS全称为Hypertext Transfer Protocol over Secure Socket Layer,及以安全为目标的HTTP通道,简单说就是HTTP的安全版本,HTTPS的安全基础就是TLS/SSL
原理:客户端和服务端的交互 先通过非对称加密进行认证,在经过对称加密进行交互
19、什么是http代理服务器,有什么用?
代理服务器大多被用来连接INTERNET(国际互联网)和Local Area Network(局域网)。
22、说说Java虚拟机的生命周期及体系结构。
当启动一个Java程序时,一个虚拟机实例就诞生了,当程序关闭退出时,这个虚拟机实例随之消亡。JVM实例通过main()方法来运行一个Java程序。
JVM内部有两种线程:守护线程与非守护线程。守护线程通常是由虚拟机自己使用的,比如垃圾回收线程。当该程序所有的非守护线程都终止时,JVM实例将自动退出
25、分布式系统你会考虑哪些方面?
1. 服务接口的设计至关重要
2. 后台升级要做到对用户透明
3. 应用方设计时候需要衡量后台服务失败的影响
39、为什么不把基本类型放堆中呢?
一:在方法中声明的变量,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束
二:在类中声明的变量是成员变量,也叫全局变量,放在堆中的(因为全局变量不会随着某个方法执行结束而销毁)放在堆中
41、Java中有没有指针的概念?
java中没有指针。 java中有引用(reference)c/c++中有指针
42、Java中,栈的大小通过什么参数来设置?
JVM参数-堆分配参数总结
-Xms:设置最小堆值
-Xmx:设置最大堆值
-Xmn:设置年轻代值(设置它等于最小值和最大值相同)
-XX:NewSize:设置年轻代最小值
-XX:MaxNewSize:设置年轻代最大值
-Xss:设置线程栈值大小
-XX:PermSize:设置永久代最小值
-XX:MaxPermSize:设置永久代最大值
-XX:SuriviorRatio:设置年轻代中Eden与s0的比例
-XX:NewRatio:设置老年代与年轻代的比例。
-XX:MinHeapFreeRatio:设置堆空间最小空闲比例。当堆空间的空闲比例小于这个数值时,JVM变主动申请内存空间。
-XX:MaxHeapFreeRation:设置堆空间最大空闲比例。当堆空间的空闲比例大于这个数值时,JVM会压缩堆空间,得到一个较小的堆空间。
-XX:TargetSuriviorRatio:设置surivior空间使用率,当surivior空间使用率达到这个数值时,会将对应的对象送入老年代。
参见:https://blog.csdn.net/junchenbb0430/article/details/78407085 参数分配的影响
45、讲一讲垃圾回收算法。
标记-清除法(Mark-Sweep) (被标记的是有引用的)
标记-清除算法是现代垃圾回收算法的思想基础,后面提到的所有算法都是基于该算法的思想。从名字就能看出,该算法分两个阶段进行——“标记”和“清除”。首先通过根对象标记所有可达的对象,然后清除所有未被标记的不可达对象。该算法有一个比较大的缺点,就是容易产生内存碎片。过多的内存碎片对于大对象的内存分配,效率非常低(碎片最直接的问题就是会导致无法分配大块的内存空间)
什么是可达?(理解为 能找到引用)
复制算法(Copying)
复制算法基于标记-清除算法并对其产生过多内存碎片的缺点进行了优化。复制算法将内存空间分成两等份(如下图的A和B),每次只使用其中的一块,当垃圾回收的时候,将A中的可达对象复制到B中,然后清空A中的所有对象。这样就避免了产生内存碎片的情况,但这种算法的缺点也是显而易见的,那就是太浪费空间。
标记整理算法
光从名字上就能看出,该算法继承自标记-清除算法。该算法在标记和清除之间又加了一个操作——压缩。首先将标记所有可达对象,然后将所有可达对象压缩(或者叫移动)到内存的一端,最后将边界以外的空间全部清空。这样既避免了产生内存碎片,又不需要空出一块内存空间,一举两得。
JVM中采用的并不是某一种回收算法,而是多种算法组合使用,因为任何一种算法都不是完美的,都有自身的优缺点,有自己适用的场景。需要把他们放到合适的地方,这样才能各尽其能,达到一个最好的效果
48、讲一讲内存分代及生命周期。
为什么要分代?
如果说堆内存没有区域划分,所有新创建的对象和生命周期很长的对象放在一个区域,随着对象越来越多触发了JVM的垃圾回收机制,而每次回收都要遍历所有的对象,这个时间成本是难以想象的,严重影响GC效率。
新生代
新生代中的对象存活时间短,只需要在新生代区域中频繁进行GC,全部的新生对象都会在新生代
老年代
老年代也称Old区。老年代的对象是由新生代中存活多次,或者是特殊原因(原因有多种,后续介绍)从新生代直接转移来的
永久代
永久代也称Permanent区或方法区。存储类信息、常量、静态变量以及即时编译器编译后的代码等等数据
针对HotSpot VM的实现
Minor GC:新生代GC(Minor GC):指发生在新生代的垃圾收集动作
Full GC:收集整个堆,包括young gen、old gen、perm gen
Young GC:只收集young gen的GC Old GC:只收集old gen的GC。
49、什么情况下触发垃圾回收?
1. 执行 system.gc()的时候 (其实并不会马上进行垃圾回收,甚至不一定会执行垃圾回收)
2.老年代空间不足 永久代空间不足 new 一个大对象,新生代放不下,直接到老年代,空间不够,触发FullGC
怎么避免频繁GC
1. 不要频繁的new 对象 2. 不要显示的调研system.gc() 3. 不要用String+ 使用StringBuilder 4.不要使用Long Integer 尽量使用基本类型
5.少用静态变量 不会回收 6.可以使用null 进行回收
如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现
55、如何进行JVM调优?有哪些方法?
JVM参数: GC的时间足够的小 GC的次数足够的少 发生Full GC的周期足够的长
56、如何理解内存泄漏问题?有哪些情况会导致内存泄露?如何解决?
无法被释放的内存,累加的够多就会内存溢出
在For循环中,我们不断的生成新的对象,然后将其添加到Vector对象中,之后将o引用置空。问题是当o引用被置空后,如果发生GC,我们创建的Object对象是否能够被GC回收呢?答案是否定的。因为,GC在跟踪代码栈中的引用时,会发现v引用,而继续往下跟踪,就会发现v引用指向的内存空间中又存在指向Object对象的引用。也就是说尽管o引用已经被置空,但是Object对象仍然存在其他的引用,是可以被访问到的,所以GC无法将其释放掉。如果在此循环之后,Object对象对程序已经没有任何作用,那么我们就认为此Java程序发生了内存泄漏
59、为了解决数据库服务器的负担,如何做数据库的分布?
mycat,和 Sharding
62、讲讲CAP理念。
CAP原则是NOSQL数据库的基石
分布式系统的CAP理论:理论首先把分布式系统中的三个特性进行了如下归纳:
一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
在分布式系统的设计中,没有一种设计可以同时满足一致性,可用性,分区容错性 3个特性
对于分布式数据系统,分区容忍性是基本要求,否则就失去了价值
C - Consistent ,一致性A - Availability ,可用性P - Partition tolerance ,分区容错性分布式系统之所以叫分布式,是因为提供服务的各个节点分布在不同机器上,相互之间通过网络交互。那么必然存在网络故障断开的风险,这个网络断开的专业场景成为网络分区。
在网络分区发生时,两个分布式节点之间无法进行通信,那么我们对一个节点进行的修改操作将无法同步到另外一个节点,所以数据的「一致性」将无法满足,因为两个分布式节点的数据不再保持一致。除非我们牺牲「可用性」,也就是暂停分布式节点服务,在网络分区发生时,不再提供修改数据的功能,直到网络状况完全恢复正常再继续对外提供服务。或者为了保证可用性,而牺牲数据一致性。
所以,CAP一句话就是,在网络分区时,不能同时保证可用性和一致性。
为了保证分布式中间件的可用性,大部分中间件会支持最终一致性
分布式锁是cp模型,redis 是ap模型
参照:https://www.cnblogs.com/duanxz/p/5229352.html 讲解cap
63、怎么理解强一致性、单调一致性和最终一致性?
从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性。对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。如果能容忍后续的部分或者全部访问不到,则是弱一致性。如果经过一段时间后要求能访问到更新后的数据,则是最终一致性。
66、谈一谈一致性哈希算法。
是为了解决服务器的变动造成hash结果不一致的问题,无论增加机器最终hash要一致,结果不受影响
参照:https://zhuanlan.zhihu.com/p/34985026
70、OSI有哪七层模型?TCP/IP是哪四层模型。
应用层:应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。
传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。
互连网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)。
网络接口层:对实际的网络媒体的管理,定义如何使用实际网络(如Ethernet、Serial Line等)来传送数据。
osi七层结构:
应用层
表示层
会话层
传输层
网络层
数据链路层
物理层
equals 和hashcode 如果hashcode不同 则不进行equals比较 重写hashcode equals必须要重写
== 比较地址 equals比较内容
Strin的equals重写 原理(先进行地址比较,地址不相等就进行值(内容)的比较,值相等equals的2个对象就相等,地址相等2个对象就相等 )
4、&和&&的区别
$:只要有一个值为0结果为0,否则结果为1。 $$ :都为真才是真
5、Collection 和 Collections的区别
1、java.util.Collection 是一个集合接口
2、java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
7、String s = new String("xyz");创建了几个String Object
答案:两个,一个是字符串字面量"xyz"所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,另一个是通过new String(String)创建并初始化的、内容与"xyz"相同的实例
9、short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错
// s1 = s1+1;//报错,因为s1+1结果是int类型,等号左边是short类型,所以要强转
10、Java有没有goto 没有
Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?
用equals()而不用==来区分 set里面存放的是对象的引用,所以当两个元素只要满足了equals()时就已经指向同一个对象,
16、List, Set, Map是否继承自Collection接口
答:List,Set是,Map不是。 map是自己的一个接口
20、构造器Constructor是否可被override
构造器Constructor不能被继承,因此不能重写Overriding,但可以被重载Overloading。
24、两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对
1) 对象相等则hashCode一定相等; 2) hashCode相等对象未必相等
String st1 = new String(“abc”);
答案是:在内存中创建两个对象,一个在堆内存,一个在常量池
例子
String st1 = new String("abc");
String st2 = "abc";
System.out.println(st1 == st2);
System.out.println(st1.equals(st2));
答案:false 和 true
由于有前面两道提内存分析的经验和理论,所以,我能快速得出上面的答案。==比较的st1和st2对象的内存地址,由于st1指向的是堆内存的地址,st2看到“abc”已经在常量池存在,就不会再新建,所以st2指向了常量池的内存地址,所以==判断结果输出false,两者不相等。第二个equals比较,比较是两个字符串序列是否相等,由于就一个“abc”,所以完全相等。
30、float型float f=3.4是否正确?
答案:不正确。
原因:精度不准确,应该用强制类型转换,如下所示:float f=(float)3.4 或float f = 3.4f
在java里面,没小数点的默认是int,有小数点的默认是 double;
34、谈谈final, finally, finalize的区别
final 可以修饰类、方法、变量。 finally 通常是用来保证代码一定被执行的 finalize 是java.lang.Object的一个方法, 是用来保证对象在被垃圾回收之前完成特定资源的回收,现在finalize已经不被建议使用,
38、运行时异常与一般异常有何异同
error:一般是指java虚拟机相关的问题,如系统崩溃、虚拟机出错误、动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中
运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
46、描述一下JVM加载class文件的原理机制?
JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的
当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象
从Java 2(JDK 1.2)开始,类加载过程采取了父亲委托机制
47、排序都有哪几种方法?请列举
一、冒泡排序
十种排序算法总结(冒泡、插入、选择、希尔、归并、堆、快速,计数,桶,基数)
基本思想是:两两比较相邻记录的关键字,如果反序则交换 冒泡排序时间复杂度最好的情况为O(n),最坏的情况是O(n^2)
七、快速排序
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。时间复杂度为O(nlogn)
48、JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?
throws 用在方法上 throw 用在
垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收
对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的"。当GC确定一些对象为"不可达"时,GC就有责任回收这些内存空间。可以。程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。
54、静态变量和实例变量的区别?
在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。
在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量
55、什么是java序列化,如何实现java序列化?
序列化:把对象转换为字节序列的过程称为对象的序列化。 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
有些信息我们想让他持久的保存起来,那么这个就叫序列化。
就是把内存里面的这些对象给变成一连串的字节(bytes)描述的过程。
60、说出一些常用的类,包,接口,请各举5个
常用的接口 List set Map
常用的类: String Integer HashMap java.util.Date, stringbuffer
常用的包: java.io(inputstream) java.sql(date) java.lang(object) java.util (list)
多线程
4、Runnable接口和Callable接口的区别
Callable接口的call()方法可以有返回值(通过Future接口的get()方法,不过此方法是阻塞性的),而Runnable接口的run()方法没有返回值
Callable接口的call()方法可以声明抛出异常,而Runnable接口的run()方法不可以声明抛出异常
创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。
这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。
如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。
而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果
9、一个线程如果出现了运行时异常会怎么样
如果异常没有被捕获该线程将会停止执行
13、ThreadLocal有什么用
而ThreadLocal则用于线程间的数据隔离 通过ThreadLocal的set()方法设置到线程的ThreadLocal.ThreadLocalMap里的是是线程自己要存储的对象,其他线程不需要去访问,也是访问不到的
17、怎么检测一个线程是否持有对象监视器
在java.lang.Thread中有一个方法叫holdsLock(),它返回true如果当且仅当当前线程拥有某个具体对象的锁。
20、ReadWriteLock是什么
读写锁分为读锁和写
写锁和写锁之间需要互斥,也就是说,如果只是读数据,就可以多个线程同时读,但是如果你要写数据,就必须互斥,使得同一时刻只有一个线程在操作。
21、FutureTask是什么
FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。
25、不可变对象对多线程有什么帮助
创建后状态不能被修改的对象叫作不可变对象。不可变对象天生就是线程安全的。
27、如果你提交任务时,线程池队列已满,这时会发生什么
ThreadPoolExecutor's中的submit()方法会抛出一个RejectedExecutionException异常啦
32、什么是CAS
CAS(比较与交换,Compare and swap) 是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步
40、高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池?
newCachedThreadPool(弹性缓存线程池) newSingleThreadExecutor(单线程线程池) newFixedThreadPool
数据库
5、与Oracle相比,mysql有什么优势?
oracle
性能:Oracle 性能高
缺点:
对硬件的要求很高;
价格比较昂贵;
管理维护麻烦一些;
操作比较复杂,需要技术含量较高
mysql
优点:
体积小、速度快、总体拥有成本低,开源;
支持多种操作系统;
是开源数据库,提供的接口支持多种语言连接操作
8、请简洁描述mysql中InnoDB支持的四种事务隔离级别名称,以及逐级之间的区别?
读未提交(read uncommitted):可以读取其他 session 未提交的脏数据。
读已提交(read committed):允许不可重复读取,但不允许脏读取。提交后,其他会话可以看到提交的数据。
可重复读(repeatable read):禁止不可重复读取和脏读取、以及幻读(innodb 独有)。
串行(serializable):事务只能一个接着一个地执行,但不能并发执行。事务隔离级别最高。
不同的隔离级别有不同的现象,并有不同的锁定/并发机制,隔离级别越高,数据库的并发性就越差。
33、LIKE和REGEXP操作有什么区别?
其中like要求整个数据都要匹配,而REGEXP只需要部分匹配即可。
也就是说,用Like,必须这个字段的所有内容满足条件,而REGEXP只需要有任何一个片段满足即可
mysql索引采用 索引的数据结构(B Tree 、 B+Tree)
B树
1.所有非叶子结点至多拥有两个儿子(Left和Right);
2.所有结点存储一个关键字;
3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树;左小右大
B+Tree
B+Tree只有最下面的叶子节点才是它的数据,每个节点可以多条数据,查找1是执行3次IO,查找2同样也是执行3次IO ,MySQL查询数据最多IO次数3-5次(当然数据量越大,树的高度也会增长,IO也会增加)
34、BLOB和TEXT有什么区别?
TEXT与BLOB的主要差别就是BLOB保存二进制数据,TEXT保存字符数据。
52、mysql里记录货币用什么字段类型好?
货币在数据库中MySQL常用Decimal和Numric类型表示 值作为字符串存储,而不是作为二进制浮点数,以便保存那些值的小数精度
不使用float或者double的原因:因为float和double是以二进制存储的,所以有一定的误差。
54、mysql有关权限的表都有哪几个?
user权限表:记录允许连接到服务器的用户帐号信息,里面的权限是全局级的。
db权限表:记录各个帐号在各个数据库上的操作权限。
table_priv权限表:记录数据表级的操作权限。
columns_priv权限表:记录数据列级的操作权限。
host权限表:配合db权限表对给定主机上数据库级操作权限作更细致的控制。这个权限表不受GRANT和REVOKE语句的影响。
GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方,优化收集方法的思路
第一种:标记清除 适合在老年代进行垃圾回收,比如CMS收集器就是采用该算法进行回收的。
第二种:标记整理 适合老年代进行垃圾收集
第三种:复制算法 适合新生代区进行垃圾回收
参照:https://blog.csdn.net/fateruler/article/details/81158510
9、几种常用的内存调试工具:jmap、jstack、jconsole、jhat
10、类加载的几个过程?
1) 加载:根据查找路径找到相应的class文件,然后导入。类的加载方式分为
隐式加载和显示加载两种。隐式加载指的是程序在使用new关键词创建对象时,会隐式的调用类的加载器把对应的类加载到jvm中。显示加载指的是通过直接调用class.forName()方法来把所需的类加载到jvm中。
2) 检查:检查夹加载的class文件的正确性。
3) 准备;给类中的静态变量分配内存空间。
4) 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址。
5) 初始化:对静态变量和静态代码块执行初始化工作。
13、简述java垃圾回收机制?
只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫描那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。
19、什么是类加载器,类加载器有哪些?
引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。
扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
参见:https://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html
20、简述java内存分配与回收策率以及Minor GC和Major GC
新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具
备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快(适合采用复制算法)
老年代 GC(Major GC / Full GC):指发生在老年代的 GC,出现了 Major GC,经常
会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里
就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10
倍以上。
pring Aop中四个重要概念,切点,切面,连接点,通知
1. 通知: 就是我们编写的希望Aop时执行的那个方法。我们通过Aop希望我们编写的方法在目标方法执行前执行,或者执行后执行。
2. 切点:切点就是我们我们配置的满足我们条件的目标方法。比如我们规定:名字前面是select开头的才执行我们自定义的通知方法。那么这些select开头的方法就是切点。
3. 连接点:连接点可以说是切点的全集。切点是连接点的子集。也可以理解为,连接点是我们没有定义那个select开头规则时,满足条件的全部的方法。
4. 切面:切面是切点和通知的组合称谓,就是变相给组合起了个名字。
spring AOP 之一:spring AOP功能介绍
参照:https://www.cnblogs.com/duanxz/p/6754606.html
aop 配置 可以配置事物,可以配置apsect 切面上进行通知
aop实现了 jdk的动态代理和cglib 的动态代理 2种方式 jdk的代理 实现类需要实现接口才能代理 ,cglib 可以直接代理某个类 进行方法拦截
spring mvc 面试题答案 蚂蚁金福 https://zhuanlan.zhihu.com/p/59377434
SpingMvc中的控制器的注解一般用那个,有没有别的注解可以替代
一般用@Conntroller注解,表示是表现层,不能用用别的注解代替.
8、 @RequestMapping注解用在类上面有什么作用?
@RequestMapping 有两种标注方式,一种是标注在类级别上,一种是标注在方法级别上。标注在方法上时,value 表示访问该方法的 URL 地址。标注在类上时,value 相当于一个命名空间,即访问该 Controller 下的任意方法都需要带上这个命名空间。
参照https://www.cnblogs.com/caoyc/p/5635173.html
11、怎么样在方法里面得到Request,或者Session?
可以通过httpservletrequst放在方法参数上 也可以 通过(Map)ActionContext.getContext().get("request")
springmvc 方法获取前台的参数 注解有
@RequestParam @PathVariable @RequestBody 返回的有@ResponseBody 表示服务器返回的时候以一种什么样的方式进行返回, 将内容或对象作为 HTTP 响应正文返回,值有很多,一般设定为json
15、SpringMVC怎么样设定重定向和转发的?
return "forward:/userHome" return "redirect:/"; 通过forward , redirect
17、SpringMvc 用什么对象从后台向前台传递数据的?
答:通过 ModelMap 对象,可以在这个对象里面用 put 方法,把对象加到里面,前台就可以过 el 表达式拿到。
21、当一个方法向 AJAX 返回特殊对象,譬如 Object,List 等,需要做什么处理?
答:要加上@ResponseBody 注解
22、SpringMvc 里面拦截器是怎么写的?
答:有两种写法,一种是实现接口,另外一种是继承适配器类,然后在 SpringMvc 的配置文配置拦截器即可:<!-- 配置 SpringMvc 的拦截器 --><mvc:interceptors>
netty
1.BIO、NIO和AIO的区别?
BIO是一个连接一个线程。
NIO是一个请求一个线程。
AIO是一个有效请求一个线程。
Java BIO (传统io): 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
Java NIO(改进后的io) : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写);
异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持异步IO操作API);
阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回);
非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成)
参照:https://blog.csdn.net/skiof007/article/details/52873421
参见:https://blog.csdn.net/anxpp/article/details/51512200 更详细
3.Netty的特点?
Netty是一个高性能、异步事件驱动的NIO框架 Netty的所有IO操作都是异步非阻塞的
Netty的特点
(1) 使用更高效的socket底层,对epoll空轮询引起的cpu占用飙升在内部进行了处理,避免了直接使用NIO的陷阱,简化了NIO的处理方式。
(2) 采用多种decoder/encoder 支持,对TCP粘包/分包进行自动化处理
(3) 可使用接受/处理线程池,提高连接效率,对重连、心跳检测的简单支持
(4)可配置IO线程数、TCP参数, TCP接收和发送缓冲区使用直接内存代替堆内存,通过内存池的方式循环利用ByteBuf
(5) 通过引用计数器及时申请释放不再引用的对象,降低了GC频率
(6)使用单线程串行化的方式,高效的Reactor线程模型
(7) 无锁化的串行设计,采用环形数组缓冲区实现无锁化并发编程
(8)大量使用了volitale、使用了CAS和原子类、线程安全类的使用、读写锁的使用
参见:https://blog.csdn.net/baiye_xing/article/details/73136174
6.了解哪几种序列化协议?
1、XML 2、JSON 3、Fastjson 等 参见:https://blog.csdn.net/baiye_xing/article/details/73249819
dubbo
4、dubbo都支持什么协议,推荐用哪种?
Dubbo支持dubbo、rmi、hessian、http、webservice、thrift、redis等多种协议,但是Dubbo官网是推荐我们使用Dubbo协议的。
5、Dubbo需要 Web 容器吗?
dubbo服务容器是一个standalone的启动程序,因为后台服务不需要Tomcat或JBoss等Web容器的功能, 服务容器只是一个简单的Main方法,
并加载一个简单的Spring容器,用于暴露服务。 服务容器的加载内容可以扩展,内置了spring, jetty, log4j等加载
10、Dubbo有哪几种配置方式?
根据 DUBBO 官方文档,配置 DUBBO 有 4 种方式,分别是:
1. XML 配置文件方式(常用的方式)
2. properties 配置文件方式
3. annotation 配置方式
4. API 配置方式
11、Dubbo核心的配置有哪些?
生产者,提供者 参见:https://www.cnblogs.com/wangzhuxing/p/9735258.html
12、在Provider 上可以配置的 Consumer 端的属性有哪些?
timeout group version registry
13、Dubbo启动时如果依赖的服务不可用会怎样?
Dubbo缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止Spring初始化完成,以便上线时,能及早发现问题,默认check=true。否则可以指定不检查
14、Dubbo推荐使用什么序列化框架,你知道的还有哪些?
dubbo默认使用的是
Hessian序列化
15、Dubbo默认使用的是什么通信框架,还有别的选择吗?
Dubbo使用的是netty ,reactor模式采用nio的方式通信,主流通信框架包括netty,mina,Grizzly
MYBATIS
2、讲下MyBatis的缓存
一、mybatis和同是持久层的hibernate一样,都存在着缓存机制,今天来说一下mybatis的缓存机制。
查询缓存来缓存数据,从而达到提高查询性能的要求,以提高我们项目的效率!!
二、mybatis的缓存机制有两级:
(1)一级缓存:一级缓存mybatsi已近为我们自动开启,不用我们手动操作,而且我们是关闭不了的!!但是我们可以手动清除缓存。(SqlSession级别)
(2)二级缓存:二级缓存需要我们手动开启。(全局级别)
四:二级缓存
二级缓存的作用:通过度一级缓存的了解。推挤缓存是基于同一个SqlSesion类的实例对象的。但是,有些时候在wenb工厂中将会执行查询操作的方法分装在某个Service方法中,当查询完一次后,
Service方法结束,此时SqlSession类的实例对象就会关闭,一级缓存就会被清空。此时若再次调用用Service方法查询同一个信息,此时异界缓存就是空的,从而无法从缓存中获取信息!!
因此,我们可以使用二级缓存,二级缓存存在与Mapper实例中,当多个SqlSession类的实例对象加载相同的mapper文件,并执行其中国的SQL配置时,他们就共享一个Mapper缓存。
当某个SqlSession类的实例对象执行了增,删,改,等改变数据的操作时,Mapper实例都会清空其二级缓存!
简单来说就是 多个线程执行相同的sql 会从缓存中取 ,即使不是同一个sqlsession
hibernate的查询
有creatsqlquery(写sql的) createquery(查询对象的)
shiro 配置权限的方式有几种,分别解释一下
4种
1,在配置文件中对过滤的url进行权限的添加, 2. 在service方法上配置注解@RequiresPermissions(“user:delete”)
3. 在jsp页面中使用标签 <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
4.编程方式实现用户权限控制
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("admin")){
//有权限
}else{
//无权限
}
参见:https://blog.csdn.net/qq_38263083/article/details/82716702
17、MyBatis里面的动态Sql是怎么设定的?用什么语法?
MyBatis中用于实现动态SQL的元素主要有:if 、choose、 when、otherwise、trim、where、set 、foreach。
shiro的实现原理? 核心原理过滤器
subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
securityManager:安全管理器,主体进行认证和授权都是通过securityManager进行。
authenticator:认证器,主体进行认证最终通过authenticator进行的。
authorizer:授权器,主体进行授权最终通过authorizer进行的。
sessionManager:web应用中一般是用web容器对session进行管理,shiro也提供一套session管理的方式。
SessionDao: 通过SessionDao管理session数据,针对个性化的session数据存储需要使用sessionDao。
cache Manager:缓存管理器,主要对session和授权数据进行缓存,比如将授权数据通过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理。
realm:域,领域,相当于数据源,通过realm存取认证、授权相关数据。
spring中有spring security (原名Acegi),是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。
shiro不依赖于spring,shiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shiro。
使用shiro实现系统的权限管理,有效提高开发效率,从而降低开发成本
mybatis 使用resultmap 实现一个查询
关联关系
一对多:
用collection 标签关联
多对一:
用association 标签关联
参见:https://blog.csdn.net/qq_33561055/article/details/78861131
mapper 代理如何实现的
采用Mapper动态代理方法只需要编写相应的Mapper接口(相当于Dao接口),那么Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
1、Mapper.xml文件中的namespace与mapper接口的全类名相同。
2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同。
3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同。
4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。
动态代理的实现 省去了获取sqlsession的步骤,在方法执行前进行动态执行
参见:https://blog.csdn.net/xiaokang123456kao/article/details/66476828
参见:https://blog.csdn.net/xiaokang123456kao/article/details/76228684
缓存的实现原理
?????
ConcurrentHashMap和Hashtable的区别
Hashtable和ConcurrentHashMap有什么分别呢?它们都可以用于多线程的环境,但是当Hashtable的大小增加到一定的时候,性能会急剧下降,
因为迭代时需要被锁定很长的时间。因为ConcurrentHashMap引入了分割(segmentation),不论它变得多么大,仅仅需要锁定map的某个部分,
而其它的线程不需要等到迭代完成才能访问map。简而言之,在迭代的过程中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map。
hashmap 线程不安全的 因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap。
hashtable 每一步操作都有锁,线程安全但是很慢性能低
HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。
因为当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,可能会进入阻塞或轮询状态。
如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低。
ConcurrentHashMap 线程安全的 利用锁分段技术
HashTable容器在竞争激烈的并发环境下表现出效率低下的原因,是因为所有访问HashTable的线程都必须竞争同一把锁,
那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,
从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术。
执行put方法的线程会获得锁,只有当此线程的put方法执行结束后才会释放锁,根据多线程的知识,获得锁的线程会通知其他试图操作put方法的线程,并通知其他线程出于等待状态,
直到释放锁后,其他线程才会去重新竞争锁。这一点保证了ConcurrentHashMap的线程安全。
线程安全的比hashtable 性能更高
参见:https://blog.csdn.net/sbq63683210/article/details/51679790
设计一个简单权限需要几张表
5张 user表 user-role表 role表 role-author表 author表
描述一下登录需要哪些步骤
arraylist 如何扩容
初始化会被分配10个长度当超出长度后在扩容1.5倍
ArrayList是采取延迟分配对象数组大小空间的,当第一次添加元素时才会分配10个对象空间,
当添加第11个元素的时候,会扩容1.5倍,当添加到16个元素的时候扩容为15*1.5=22,以此类推。
当我们在明确对象的大致数目时候提前指定初始化数组的大小是一个非常明智的选择
hashmap 如何扩容
默认的是长度*负载因子(0.75)
当map中包含的Entry的数量大于等于threshold = loadFactor * capacity的时候,且新建的Entry刚好落在一个非空的桶上,此刻触发扩容机制,将其容量扩大为2倍
AOP的实现原理
spring框架对于这种编程思想的实现基于两种动态代理模式,分别是 JDK动态代理 及 CGLIB的动态代理,这两种动态代理的区别是 JDK动态代理需要目标对象实现接口,而 CGLIB的动态代理则不需要
动态代理模式:动态代理类的源码是在程序运行期间,通过 JVM 反射等机制动态生成。代理类和委托类的关系是运行时才确定的
使用 JDK 生成的动态代理的前提是目标类必须有实现的接口。但这里又引入一个问题,如果某个类没有实现接口,就不能使用 JDK 动态代理。所以 CGLIB 代理就是解决这个问题的。
CGLIB 是以动态生成的子类继承目标的方式实现,在运行期动态的在内存中构建一个子类
CGLIB 使用的前提是目标类不能为 final 修饰。因为 final 修饰的类不能被继承。
现在,我们可以看看 AOP 的定义:面向切面编程,核心原理是使用动态代理模式在方法执行前后或出现异常时加入相关逻辑。
AOP 是基于动态代理模式。
AOP 是方法级别的。
AOP 可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。
参照:http://www.importnew.com/31318.html
数据结构:
关于树:https://zhuanlan.zhihu.com/p/27700617
java io流的操作
参见:https://juejin.im/post/5af79bcc51882542ad771546
java io流中涉及的设计模式
private static final String SEPARATOR = File.separator;
File file = new File("e:" + SEPARATOR + "io" + SEPARATOR + "test.txt");
//1、获得子节输入流
FileInputStream fileInputStream=new FileInputStream(file);
2、构造转换流
InputStreamReader inputStreamReader=new InputStreamReader(fileInputStream);
3、 构造缓冲字符流
BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
//备注1、2两步骤体现出了适配器模式
//2步骤体现了InputStreamReader类具有将子节输入流转换为字符输入流的功能
//2、3两步骤体现了装饰模式(wrapper包装模式)
io中的适配器模式
由于InputStream是字节流不能享受到字符流读取字符那么便捷的功能,因此借助
InputStreamReader将其转为Reader子类,因此可以拥有便捷操作文本文件方法。
OutputStream同理。
io中的装饰(包装)模式
将InputStream字节流包装为BufferedReader过程就装饰的过程。一开始
InputStream只有read一个字节的方法,包装为Reader之后拥有read一个字符的功
能,在包装成BufferedReader之后就拥有read一行字符串功能。OutputStream同理
最后说一点,我们作为程序员,研究问题还是要仔细深入一点的。当你对原理了解的有够透彻,开发起来也就得心应手了,很多开发中的问题和疑惑也就迎刃而解了,
而且在面对其他问题的时候也可做到触类旁通。当然在开发中没有太多的时间让你去研究原理,开发中要以实现功能为前提,可等项目上线的后,
你有大把的时间或者空余的时间,你大可去刨根问底,深入的去研究一项技术,为觉得这对一名程序员的成长是很重要的事情。
1.请列举出在JDK中几个常用的设计模式?
享元模式
java.lang.Integer(其它基本类型包装类(除去Float,Double)也如此,还有BigDecimal)
Integer.valueOf()方法
byte,short,int,long,boolean,char的包装型在类加载到JVM时,已经缓存了制定范围的对象引用,因为值的设定使用的是static块或者常量。其中char的范围为:0~127;boolean的值为true和false;其它默认范围都是-127~128。其中int的上限127可以调整,这需要调整JVM的参数。
同时利用了享元模式的还有String这个类,因为生存的每个字符串都是不可变的。
.迭代器模式
很多集合已经使用了迭代器进行遍历。(Iterator)
建造者模式
java.lang.StringBuilder,这是一个final类
public StringBuilder append(String str)方法,这一方法是对父类的覆写。
类功能:用于一个不可更改的字符序列。
方法功能:根据现有字符序列和追加字符,通过系统拷贝方法System.arraycopy生成一个新的字符序列。
适配器模式
java.util.Arrays
public static List asList(T… a)方法
类功能:此类包含了大量对数组操作的方法。
方法功能:将一个引用类型的数组转为一个List。从而可以使用List类的操作来操作数组对象,但是有一点要注意:就是不能使用add(),remove()操作,因为返回的list底层是基于数组的,数组结构是不能更改的。 list类就是这里的适配器,通过这个适配器,对数组的直接操作变为间接操作。
参见:https://blog.csdn.net/caoxiaohong1005/article/details/79961539
.在 Java 中,什么叫观察者设计模式(observerdesign pattern)?
发布订阅模式是最常用的一种观察者模式的实现,并且从解耦和重用角度来看,更优于典型的观察者模式
在发布订阅模式中,发布者和订阅者之间多了一个发布通道;一方面从发布者接收事件,另一方面向订阅者发布事件;订阅者需要从事件通道订阅事件
7.在 Java 中,为什么不允许从静态方法中访问非静态变量?
因为静态方法的调用不是通过实例对象进行的,所以在静态方法中没有this指针,不能访问所属类的非静态变量和方法,只能访问方法体内的局部变量、自己的参数和静态变量。
成员变量分为实例变量和静态变量。
10.举例说明什么情况下会更倾向于使用抽象类而不是接口?
使用模板方法设计模式要使用抽象类
nginx
nginx负载均衡的算法怎么实现的
nginx 的 upstream目前支持 4 种方式的分配
1)、轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
2)、weight
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
2)、ip_hash
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
3)、fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
4)、url_hash(第三方)
nginx内置策略包含加权轮询和ip hash
加权轮询算法分为先深搜索和先广搜索,那么nginx采用的是先深搜索算法,即将首先将请求都分给高权重的机器,直到该机器的权值降到了比其他机器低,才开始将请求分给下一个高权重的机器;
参见:http://dujianjian.win/2018/05/24/nginx/#nginx%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E7%9A%84%E7%AE%97%E6%B3%95%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0%E7%9A%84
springmvc 方法跳转
重定向:"redirect:/url" request的作用域被改变了 就不是一个回话了
转发: "forward:/url" request的作用域还是同一个 还是同一个回话参数共享
参见:https://blog.csdn.net/u013041642/article/details/72190223
spring mvc 支持如下的返回方式:ModelAndView, Model, ModelMap, Map,View, String, void。
参见:https://www.cnblogs.com/xiepeixing/p/4243801.html
static synchronize 方法锁(类锁) 和直接synchronize 方法锁(对象锁) 不冲突 互相执行各自的
对象可以有任意个,多以与类不冲突 static 修饰的是共享的不是线程安全的
常用设计模式
参见:https://mp.weixin.qq.com/s/OAEtpM-h3OT_bBYgvyvE0w
观察者模式核心是:观察者要注册到被观察者里面,一旦被观察者变更了消息,就会通知到所有的被注册进来的用户,现在的注册中心,
redis,zookeeper,都有发布与订阅功能,就是类似此原理
在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。
当数据对象被加上排它锁时,其他的事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。
数据库利用这两 种基本的锁类型来对数据库的事务进行并发控制。
对于UPDATE、DELETE、INSERT语句,Innodb会自动给涉及的数据集加排他锁(X);对于普通SELECT语句,Innodb不会加任何锁。
mysql的共享锁(Share Lock)
Mysql会对查询结果中的每行都加共享锁
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。
用法
SELECT 条件 from 表 LOCK IN SHARE MODE;
排他锁(eXclusive Lock)
Mysql会对查询结果中的每行都加排他锁
排他锁又称写锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
用法
SELECT 条件 from 表 FOR UPDATE;
innodb默认是行锁 在更改操作自动加上行锁,如果操作的行没有索引那么会自动加上表锁 找到行之后再变行锁
意向锁
意向锁
意向锁是表级锁,其设计目的主要是为了在一个事务中揭示下一行将要被请求锁的类型。InnoDB中的两个表锁:
意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁
意向排他锁(IX):类似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。
意向锁是InnoDB自动加的,不需要用户干预。
对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(X);对于一般的Select语句,InnoDB不会加任何锁,
事务可以通过以下语句给显示加共享锁或排他锁
公平锁、非公平锁
公平锁(Fair):加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得。
非公平锁(Nonfair):加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待。(synchronize)
ReentrantLock锁内部提供了公平锁与分公平锁内部类之分,默认是非公平锁,
可重入锁
Lock lock1 = new ReentrantLock();
可重入锁,也叫做递归锁,指的是同一线程外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。反之不可重入锁
使用:tryLock 避免死锁
synchronized:可重入锁
java.util.concurrent.locks.ReentrantLock:可重入锁;
分段锁
ConcurrentHashMap
乐观锁/悲观锁
悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。
悲观锁在Java中的使用,就是利用各种锁。
乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。
读写锁:
分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,
可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,
只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁
参见:https://www.cnblogs.com/zzlp/p/5174745.html
CAS
使用:private static final Unsafe unsafe = Unsafe.getUnsafe();//compareAndSwapInt 简称cas
Compare And Swap.比较并交换.java中的同步器就是基于CAS技术实现的,为什么它能保证操作的同步性呢?因为是原子操作的一种,
所以可以在多线程环境下来实现数据的交换操作不被打断.
CAS缺点
1. ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,
但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
2. 循环时间长开销大。
3. 只能保证一个共享变量的原子操作。
什么是AQS
AQS(AbstractQueuedSynchronizer),AQS是JDK下提供的一套用于实现基于FIFO等待队列的阻塞锁和相关的同步器的一个同步框架
可见 CountDownLatch 是基于AQS框架来实现的一个同步器.类似的同步器在JUC下还有不少。
java 中的锁:https://www.cnblogs.com/qifengshi/p/6831055.html
、getCurrentSession()与openSession()的区别?
* 采用getCurrentSession()创建的session会绑定到当前线程中,而采用openSession(),创建的session则不会
* 采用getCurrentSession()创建的session在commit或rollback时会自动关闭,而采用openSession(),创建的session必须手动关闭
事务 Spring read-only="true" 只读事务的一些概念
应用场合:
如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性;
如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持。
【注意是一次执行多次查询来统计某些信息,这时为了保证数据整体的一致性,要用只读事务】
数据库的死锁:
死锁的第一种情况
一个用户A 访问表A(锁住了表A),然后又访问表B;另一个用户B 访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,
它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。
死锁的第二种情况
用户A查询一条纪录,然后修改该条纪录;这时用户B修改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,
而用户B里的独占锁由于A 有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁
死锁的第三种情况
如果在事务中执行了一条不满足条件的update语句,则执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞
。类似的情 况还有当表中的数据量非常庞大而索引建的过少或不合适的时候,使得经常发生全表扫描,最终应用系统会越来越慢,最终发生阻塞或死锁。
参见:https://uule.iteye.com/blog/2422193