Java面经整理(一)

Java面经整理(一)

1、Java线程同步哪几种方式?什么情况下用哪种方式?

  1. synchronized关键字

    • 同步方法
    • 同步代码块

    通过加synchronized关键字实现多个线程同时访问共享资源时出现的问题,当有申请者申请该资源时,如果资源没有被占用,就给这个申请者使用,否则不能使用该资源。

  2. 使用重入锁reentrantLock()

  3. 使用局部变量ThreadLocal实现线程同步

    如果使用thread管理变量,那么每一个使用该变量的线程都会获得该变量的副本,这样每一个线程可以随意修改自己的变副本,不会对其他线程产生影响。

  4. 使用原子变量实现线程同步

注:ThreadLocal与同步机制
a.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。
b.前者采用以"空间换时间"的方法,后者采用以"时间换空间"的方式

那么lock和synchronized的区别对比如下:
1)synchronized 在成功完成功能或者抛出异常时,虚拟机会自动释放线程占有的锁;而Lock对象在发生异常时,如果没有主动调用unLock()方法去释放锁,则锁对象会一直持有,因此使用Lock时需要在finally块中释放锁;
2)lock接口锁可以通过多种方法来尝试获取锁包括立即返回是否成功的tryLock(),以及一直尝试获取的lock()方法和尝试等待指定时间长度获取的方法,相对灵活了许多比synchronized;
3) 通过在读多,写少的高并发情况下,我们用ReentrantReadWriteLock分别获取读锁和写锁来提高系统的性能,因为读锁是共享锁,即可以同时有多个线程读取共享资源,而写锁则保证了对共享资源的修改只能是单线程的。

2、Java集合了解吗?

JAVA集合类主要分为两大体系:Collection和Map体系。其中Collection又派生出List、Set、Queue等三大体系。
其中,List主要用来存放一些有序、可重复的元素;主要实现类有:ArrayList、LinkedList、Vector和Stack。
① ArrayList是基于数组实现的,增删改比较慢,查询比较快。
② LinkedList是基于链表实现的,与ArrayList正好相反,它的增删改比较快,查询比较慢。
③ Vector也是基于数组实现的,但它是线程安全的类,一般不推荐使用。
④ Stack是Vector的派生类,是“栈”数据结构的应用,具有后进显出的特点,也是线程安全的,一般不推荐使用,因为效率比较低。(那么在多线程环境中,如何保证线程安全那?通过代码来保证同步)
Set主要用来存放一些无序、不可重复的元素。主要实现类有:HashSet、TreeSet、LinkedHashSet。
① HashSet按照Hash算法来存储集合元素,具有很好的存取和查找性能,常用。
② TreeSet是SortedSet接口的实现类,采用红黑树数据结构存储集合元素,可以保证集合元素处于有序状态,不是元素插入顺序,而是元素实际大小数据。
③ LinkedHashSet是HashSet的子类,根据Hash值决定元素存放位置,同时使用链表维护元素次序,以插入顺序保存元素。性能低于HashSet,但是迭代访问Set里全部元素时具有很好的性能。
Queue是基于队列实现的,通常采用先进先出存储元素。主要实现类有PriorityQueue、ArrayDeque实现类。
① PriorityQueue 标准的队列实现类,按照元素实际大小重新排序保存。最后取出的是最小元素,而不是最先插入的元素。
② ArrayDeque 基于数组实现的双端队列。
Map 保存的键值对数组。主要实现类有HashMap、HashTable、LinkedHashMap.
① HashMap 非线程安全类,可以使用Null作为键或值。
② HashTable 线程安全类,不能使用Null作为键或值。
③ LinkedHashMap.是HashMap的子类。

3、Hashmap线程安全吗?

不安全

在高并发场景下,我们通常采用另一个集合类ConcurrentHashMap

1.Hashmap在插入元素过多的时候需要进行Resize,Resize的条件是

HashMap.Size >= Capacity \ LoadFactor

2.Hashmap的Resize包含扩容和ReHash两个步骤,ReHash在并发的情况下可能会形成链表环。

4、ArrayList和LinkedList的区别?

  1. LinkedList和ArrayList的差别主要来自于Array和LinkedList数据结构的不同。ArrayList是基于数组实现的,LinkedList是基于双链表实现的。
  2. 因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的,可以直接返回数组中index位置的元素,因此在随机访问集合元素上有较好的性能。Array获取数据的时间复杂度是O(1),但是要插入、删除数据却是开销很大的,因为这需要移动数组中插入位置之后的的所有元素。
  3. 相对于ArrayList,LinkedList的随机访问集合元素时性能较差,因为需要在双向列表中找到要index的位置,再返回;但在插入,删除操作是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。
  4. LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。

5、HTTP协议了解吗?常用状态码Get和Post的区别?

HTTP(超文本传输协议)是应用层上的一种客户端/服务端模型的通信协议,它由请求和响应构成,且是无状态的。

Get和Post的区别:

  • GET 请求在浏览器退回时是无害的,而 POST 会再次提交请求
  • GET 产生的 URL 地址可以被收藏,而 POST 不可以
  • GET 请求会被浏览器主动缓存,POST 不会,除非手动设置
  • GET 请求只能进行 url 编码,POST 支持多种编码方式
  • GET 请求参数会被完整的保留在浏览器的历史记录里,POST 则不会
  • GET 请求在 URL 中传送的参数的长度是有限制的(2k-4k),POST 没有
  • 对于参数的数据类型,GET 只接受 ASCII S字符,POST 没有限制
  • GET 没有 POST 安全,GET 的参数直接暴露在 URL 中,不能用来传递敏感信息
  • GET 参数通过 URL 传递,POST 参数在 Request Body 中

HTTP 状态码:

  • 1xx: 指示信息 - 表示请求已接收,需要请求者继续执行操作。

  • 2xx: 成功 - 操作被成功接收并处理。

  • 3xx: 重定向 - 为完成请求,必须进行更进一步的操作。

  • 4xx: 客户端错误 - 请求语法有误或请求无法实现。

  • 5xx: 服务端错误 - 服务器未能实现合法的请求。

6、说一说三次握手和四次挥手的过程?

三次握手:

  • 第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN。此时客户端处于 SYN_SENT 状态。

    首部的同步位SYN=1,初始序号seq=x,SYN=1的报文段不能携带数据,但要消耗掉一个序号。

  • 第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_RCVD 的状态。

    在确认报文段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y。

  • 第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。

    确认报文段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第二个报文段所以要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。

为什么需要三次:

  • 第一次握手:客户端发送网络包,服务端收到了。
    这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
  • 第二次握手:服务端发包,客户端收到了。
    这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
  • 第三次握手:客户端发包,服务端收到了。
    这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

四次挥手:

  • 第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。
    即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。
  • 第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。
    即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。
  • 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
    即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。
  • 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
    即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。

为什么需要四次:

  • 因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。

为什么需要等待2MSL:

  1. 保证客户端发送的最后一个ACK报文段能够到达服务端。
  2. 防止“已失效的连接请求报文段”出现在本连接中。

7、进程与线程

进程
进程是程序的一次执行过程,是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间。

线程
线程是CPU调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

进程和线程的关系
线程是进程的一部分
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程

进程和线程的区别

根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位

开销方面:每个进程都有独立的代码和数据空间(程序上下文),进程之间切换开销大;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小

所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)

内存分配:系统为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源

包含关系:线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程

8、进程的调度算法有哪些?

  • 先来先服务调度算法

  • 短作业优先调度算法

  • 优先级调度算法

  • 高响应比优先调度算法

  • 时间片轮转调度算法

  • 多级反馈队列调度算法

9、解释下多态

面向对象编程有三大特性:封装、继承、多态。

1、Java 中实现多态的机制是什么?

靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

2、Java多态有什么优势?

(1)可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。

(2)可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。

(3)接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。

(4)灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。

(5)简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。

3、运行时多态存在的必要条件是什么?

(1)要有继承(包括接口的实现);(2)要有重写;(2)父类引用指向子类对象。

10、从输入 URL 到页面加载完成的过程中都发生了什么事情?

  1. 浏览器查询缓存,如果缓存存在跳到第9步。
  2. 浏览器询问操作系统服务器的IP地址。
  3. 操作系统做DNS查询,返回IP地址给浏览器。
  4. 浏览器打开对服务器的TCP连接(如果是HTTPS协议的话会更复杂)。
  5. 浏览器通过TCP连接发送HTTP请求。
  6. 浏览器接收HTTP响应并且可能关掉TCP连接,或者是重新使用连接处理新请求。
  7. 浏览器检查HTTP响应是否为一个重定向(3xx 结果状态码 ),一个验证请求(401),错误(4xx 5xx)等等,这些都是不同响应的正常处理(2xx)
  8. 如果响应可缓存,将存入缓存。
  9. 浏览器解码响应(例如:如果它是gzziped压缩)。
  10. 浏览器决定如何处理这些响应(例如,它是HTML页面,一张图片,一段音乐)。
  11. 浏览器展现响应,对未知类型还会弹出下载对话框。

注意

DNS域名解析采用的是递归查询的方式,过程是,先去找DNS缓存->缓存找不到就去找根域名服务器->根域名又会去找下一级,这样递归查找之后,找到了,给我们的web浏览器。

11、Cookie和Session的区别

Cookie的工作原理

(1)浏览器端第一次发送请求到服务器端
(2)服务器端创建Cookie,该Cookie中包含用户的信息,然后将该Cookie发送到浏览器端
(3)浏览器端再次访问服务器端时会携带服务器端创建的Cookie
(4)服务器端通过Cookie中携带的数据区分不同的用户

Session的工作原理

(1)浏览器端第一次发送请求到服务器端,服务器端创建一个Session,同时会创建一个特殊的Cookie(name为JSESSIONID的固定值,value为session对象的ID),然后将该Cookie发送至浏览器端
(2)浏览器端发送第N(N>1)次请求到服务器端,浏览器端访问服务器端时就会携带该name为JSESSIONID的Cookie对象
(3)服务器端根据name为JSESSIONID的Cookie的value(sessionId),去查询Session对象,从而区分不同用户。

区别对比

(1)cookie数据存放在客户的浏览器上,session数据放在服务器上
(2)cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,如果主要考虑到安全应当使用session
(3)session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用cookie
(4)单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的cookie不能3K。
(5)所以:将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在cookie中

posted @ 2021-05-21 14:40  Barrymeng  阅读(651)  评论(0)    收藏  举报