Java常见面试题
1.http多路复用
http/2新特性
完全采用二进制协议:头信息和数据体都是二进制的,统称为帧(frame)
支持多路复用(multiplexing)
支持头部压缩(header compression)
支持服务器推送(server push)
什么是多路复用?
在 HTTP 1.1 中,发起一个请求是这样的:
浏览器请求//static.mtime.cn/a.js-->解析域名-->HTTP连接-->服务器处理文件-->返回数据-->浏览器解析、渲染文件
浏览器请求//static.mtime.cn/b.js-->解析域名-->HTTP连接-->服务器处理文件-->返回数据-->浏览器解析、渲染文件
...
这个流程最大的问题是,每次请求都需要建立一次 HTTP 连接,也就是我们常说的3次握手4次挥手,这个过程在一次请求过程中占用了相当长的时间。
为了解决这个问题, HTTP 1.1 中提供了 Keep-Alive,允许我们建立一次 HTTP 连接,来返回多次请求数据。
在HTTP/2中,如果在一个TCP连接内同时发起多个请求,每个消息可以被拆成互不依赖的帧 并且各帧之间交错发送,然后在另一端重新把帧组装起来。这个特性就叫做多路复用。
2.select、poll、epoll的原理与区别
目前的常用的IO复用模型有三种:select,poll,epoll。
(1)select==>时间复杂度O(n)
select 的核心功能是调用tcp文件系统的poll函数,不停的查询,若是没有想要的数据,主动执行一次调度(防止一直占用cpu),直到有一个链接有想要的消息为止。从这里能够看出select的执行方式基本就是不一样的调用poll,直到有须要的消息为止。
(2)poll==>时间复杂度O(n)
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,而后查询每一个fd对应的设备状态,若是设备就绪则在设备等待队列中加入一项并继续遍历,若是遍历完全部fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了屡次无谓的遍历。poll还有一个特色是“水平触发”,若是报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
(3)epoll==>时间复杂度O(1)
epoll的两种工作方式:1.水平触发(LT)2.边缘触发(ET)
epoll一样只告知那些就绪的文件描述符,并且当咱们调用epoll_wait()得到就绪文件描述符时, 返回的不是实际的描述符,而是一个表明就绪描述符数量的值,你只须要去epoll指定的一 个数组中依次取得相应数量的文件描述符便可,这里也使用了内存映射技术,这 样便完全省掉了这些文件描述符在系统调用时复制的开销。
三者对比与区别:
一、select,poll实现须要本身不断轮询全部fd集合,直到设备就绪,期间可能要睡眠和唤醒屡次交替。而epoll其实也须要调用epoll_wait不断轮询就绪链表,期间也可能屡次睡眠和唤醒交替,可是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,可是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就好了,这节省了大量的CPU时间。这就是回调机制带来的性能提高。
二、select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,而且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,并且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并非设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省很多的开销。
3.cookie和session的详解与区别
什么是Cookie?
Cookie实际上是一小段的文本信息,,保存在浏览器端的。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
Cookie的不可跨域名性:
很多网站都会使用Cookie。例如,Google会向客户端颁发Cookie,Baidu也会向客户端颁发Cookie。那浏览器访问Google会不会也携带上Baidu颁发的Cookie呢?或者Google能不能修改Baidu颁发的Cookie呢?
答案是否定的。Cookie具有不可跨域名性。根据Cookie规范,浏览器访问Google只会携带Google的Cookie,而不会携带Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。
Cookie在客户端是由浏览器来管理的。浏览器能够保证Google只会操作Google的Cookie而不会操作Baidu的Cookie,从而保证用户的隐私安全。浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。Google与Baidu的域名不一样,因此Google不能操作Baidu的Cookie。
需要注意的是,虽然网站images.google.com与网站www.google.com同属于Google,但是域名不一样,二者同样不能互相操作彼此的Cookie。
如果用户是在自己家的电脑上上网,登录时就可以记住他的登录信息,下次访问时不需要再次登录,直接访问即可。实现方法是把登录信息如账号、密码等保存在Cookie中,并控制Cookie的有效期,下次访问时再验证Cookie中的登录信息即可。
保存登录信息有多种方案。最直接的是把用户名与密码都保持到Cookie中,下次访问时检查Cookie中的用户名与密码,与数据库比较。这是一种比较危险的选择,一般不把密码等重要信息保存到Cookie中。
什么是session?
Session是服务器端使用的一种记录客户端状态的机制,保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。Session对象是在客户端第一次请求服务器的时候创建的,客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
Session保存在服务器端。为了获得更高的存取速度,服务器一般把Session放在内存里。
每个用户都会有一个独立的Session。需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未生成Session,也可以使用request.getSession(true)强制生成Session。
Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session“活跃(active)”了一次。
虽然Session保存在服务器,对客户端是透明的,它的正常运行仍然需要客户端浏览器的支持。这是因为Session需要使用Cookie作为识别标志。HTTP协议是无状态的,Session不能依据HTTP连接来判断是否为同一客户,因此服务器向客户端浏览器发送一个名为JSESSIONID的Cookie,它的值为该Session的id(也就是HttpSession.getId()的返回值)。Session依据该Cookie来识别是否为同一用户。
该Cookie为服务器自动生成的,它的maxAge属性一般为–1,表示仅当前浏览器内有效,并且各浏览器窗口间不共享,关闭浏览器就会失效。
因此同一机器的两个浏览器窗口访问服务器时,会生成两个不同的Session。但是由浏览器窗口内的链接、脚本等打开的新窗口(也就是说不是双击桌面浏览器图标等打开的窗口)除外。这类子窗口会共享父窗口的Cookie,因此会共享一个Session。
URL地址重写:
URL地址重写是对客户端不支持Cookie的解决方案。URL地址重写的原理是将该用户Session的id信息重写到URL地址中。服务器能够解析重写后的URL获取Session的id。这样即使客户端不支持Cookie,也可以使用Session来记录用户状态。HttpServletResponse类提供了encodeURL(Stringurl)实现URL地址重写,
cookie和session的区别:
1、cookie数据存放在客户的浏览器上,session数据放在服务器上.
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。
3、设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。(Session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型)
4.死锁
什么是死锁?
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
产生死锁的原因,主要包括:
系统资源不足;
程序执行的顺序有问题;
资源分配不当等。
产生死锁的四个必要条件:
互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
如何解决死锁?
死锁示例代码:
//程序中有两个线程,线程1锁住了str1,获得锁之后休眠1秒钟,这个时候线程2锁住了str2,也进行休眠操作。
//线程1休眠完了之后去锁str2,但是str2已经被线程2给锁住了,这边只能等待,同样的道理,线程2休眠完之后也要去锁str1,同样也会等待,这样死锁就产生了。
public class Deadlock {
public static String str1 = "str1";
public static String str2 = "str2";
public static void main(String[] args){
Thread a = new Thread(() -> {
try{
while(true){
synchronized(Deadlock.str1){
System.out.println(Thread.currentThread().getName()+"锁住 str1");
Thread.sleep(1000);
synchronized(Deadlock.str2){
System.out.println(Thread.currentThread().getName()+"锁住 str2");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
});
Thread b = new Thread(() -> {
try{
while(true){
synchronized(Deadlock.str2){
System.out.println(Thread.currentThread().getName()+"锁住 str2");
Thread.sleep(1000);
synchronized(Deadlock.str1){
System.out.println(Thread.currentThread().getName()+"锁住 str1");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
});
a.start();
b.start();
}
}
如果我们将下面这2行代码的值改成一样,死锁还会存在吗?
public static String str1 = "str1";
public static String str2 = "str1";
答案:不会,为什么?在声明一个对象作为锁的时候要注意字符串类型锁对象,因为字符串有一个常量池,如果不同的线程持有的锁是具有相同字符的字符串锁时,两个锁实际上同一个锁。
5.java基本数据类型及默认值
6.Servlet生命周期
Servlet生命周期分为三个阶段:
1)初始化阶段: 调用init()方法
2)响应客户请求阶段:调用service()方法
3)终止阶段:调用destroy()方法
Tomcat与Servlet的工作流程:
步骤:
1、Web Client 向Servlet容器(Tomcat)发出Http请求
2、Servlet容器接收Web Client的请求
3、Servlet容器创建一个HttpRequest对象,将Web Client请求的信息封装到这个对象中。
4、Servlet容器创建一个HttpResponse对象
5、Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象与HttpResponse对象作为参数传给 HttpServlet 对象。
6、HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息。
7、HttpServlet调用HttpResponse对象的有关方法,生成响应数据。
8、Servlet容器把HttpServlet的响应结果传给Web Client。
Servlet与JSP的比较
1、有许多相似之处,都可以生成动态网页。
2、JSP的优点是擅长于网页制作,生成动态页面比较直观,缺点是不容易跟踪与排错。
3、Servlet是纯Java语言,擅长于处理流程和业务逻辑,缺点是生成动态网页不直观。