2年java,华为面试,一面 挂
一位深圳的球友,上周去华为的面经,准备不充分,最后就是面试没通过。
昨晚上,咱们通了电话,一起总结了这次面试失败的原因。
失败的原因有三点:
1.自我介绍现场发挥(临时组织语言,时间太短)
2.八股文背的太少
3.慌乱不堪(主要是前面两个原因直接导致的)
面试过程
自我介绍
这位球友在做自我介绍时,就是自己想到哪里说到哪里,没亮点,有点拼凑的意思,另外,自我介绍不到30秒就结束了。
面试官,当时懵逼的回了一句,结束了?言外之意就是,你的简历我都还没有看完,你的
自我介绍
却已经over了。
建议:绝多数人适用的方式,先给自己写一个自我介绍,抽时间背下来,面试的时候带有节奏性
的背就阔以了。
Java创建对象有几种方式?
java中提供了以下四种创建对象的方式:
new创建新对象 通过反射机制 采用clone机制 通过序列化机制
这道题简单,球友回答满分。
深拷贝和浅拷贝有什么区别?
浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象. 深拷贝:被复制对象的所有变量都含有与原来的对象相同的值.而那些引用其他对象的变量将指向被复制过的新对象.而不再是原有的那些被引用的对象.换言之.深拷贝把要复制的对象所引用的对象都复制了一遍.
球友这道题回答的还行,面试官点点头说,差不多吧。
五分钟 掌握 原型模式 (文末送书)
BIO、NIO、AIO 有什么区别?
BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。 NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。 AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
这道题回到的马马虎虎,AIO讲的不是很让面试官满意。
如何停止一个正在运行的线程?
1、使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
2、使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法。
3、使用interrupt方法中断线程。
球友翻车,突然忘了interrupt这个单词了,稀里糊涂了就结束了这道面试题,面试就切换到下一个面试题了。
synchronized 和 ReentrantLock 有什么不同?
相似点
这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的.
区别
这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成。
synchronized经过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。
由于ReentrantLock
是java.util.concurrent
包下提供的一套互斥锁,相比Synchronized
,ReentrantLock
类提供了一些高级功能,主要有以下3项:
等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于
Synchronized
来说可以避免出现死锁的情况。公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,
Synchronized
锁非公平锁,ReentrantLock
默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。锁绑定多个条件,一个
ReentrantLock
对象可以同时绑定对个对象。
这道题,回答了中规中矩,讲的都是一些比较浅,不够深入的比较。于是面试官,开始放低面试难度。
说说进程和线程的区别?
进程是一个“执行中的程序”,是系统进行资源分配和调度的一个独立单位。 线程是进程的一个实体,一个进程中拥有多个线程,线程之间共享地址空间和其它资源(所以通信和同步等操作线程比进程更加容易) 线程上下文的切换比进程上下文切换要快很多。
(1)进程切换时,涉及到当前进程的CPU环境的保存和新被调度运行进程的CPU环境的设置。 (2)线程切换仅需要保存和设置少量的寄存器内容,不涉及存储管理方面的操作。
这道题,球友之前有专门的准备过,得到了面试官的认可。
线程池的原理是什么?
大致原理如下图:
1,线程池内部会获取activeCount, 判断活跃线程的数量是否大于等于corePoolSize(核心线程数量),如果没有,会使用全局锁锁定线程池,创建工作线程,处理任务,然后释放全局锁;
2,判断线程池内部的阻塞队列是否已经满了,如果没有,直接把任务放入阻塞队列;
3,判断线程池的活跃线程数量是否大于等于maxPoolSize,如果没有,会使用全局锁锁定线程池,创建工作线程,处理任务,然后释放全局锁;
4,如果以上条件都满足,采用饱和处理策略处理任务。
注意:使用全局锁是一个严重的可伸缩瓶颈,在线程池预热之后(即内部线程数量大于等于corePoolSize),任务的处理是直接放入阻塞队列,这一步是不需要获得全局锁的,效率比较高。
这道题中第二步和第三步,球友给搞反了,面试官当时就打断了,是这样吗?脸一下就红了,不知道到底错哪里(这是我们一起聊得时候,他把第二步和第三步给搞错了,我猜当时面试官知道他错了,但没点穿)。
熟悉CAS原理?有什么缺点?
CAS叫做CompareAndSwap,比较并交换,主要是通过处理器的指令来保证操作的原子性,它包含三个操作数:
变量内存地址,V表示 旧的预期值,A表示 准备设置的新值,B表示
当执行CAS指令时,只有当V等于A时,才会用B去更新V的值,否则就不会执行更新操作。
CAS有三个缺点
ABA问题:ABA的问题指的是在CAS更新的过程中,当读取到的值是A,然后准备赋值的时候仍然是A,但是实际上有可能A的值被改成了B,然后又被改回了A,这个CAS更新的漏洞就叫做ABA。只是ABA的问题大部分场景下都不影响并发的最终效果。
Java中有AtomicStampedReference来解决这个问题,他加入了预期标志和更新后标志两个字段,更新时不光检查值,还要检查当前的标志是否等于预期标志,全部相等的话才会更新。
循环时间长开销大:自旋CAS的方式如果长时间不成功,会给CPU带来很大的开销。
只能保证一个共享变量的原子操作:只对一个共享变量操作可以保证原子性,但是多个则不行,多个可以通过AtomicReference来处理或者使用锁synchronized实现。
这道题球友回答的还是很好的,面试官都连续说了几次“
对、对、对
”
推荐:66道并发多线程面试题,你不来个666吗?
你们项目中为什么使用Spring框架?
这么问的话,就直接说Spring框架的好处就可以了。比如说Spring有以下特点:
轻量:Spring 是轻量的,基本的版本大约2MB。 控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。 面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。 容器:Spring 包含并管理应用中对象的生命周期和配置。 MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。 事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。 异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。
但是球友没背过,所以也就是随便说说什么IOC、AOP就结束了。
谈谈你对Spring的AOP理解
AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。
Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。
注意:图中的implements和extend。即一个是接口,一个是实现类。
当然也可以使用AspectJ,Spring AOP中已经集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。使用AOP之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样可以大大简化代码量。我们需要增加新功能也方便,提高了系统的扩展性。日志功能、事务管理和权限管理等场景都用到了AOP。
这个题也算是万年的沙雕面试题,球友也是吱吱呜呜的回答了(语言没组织好),面试官也没说什么"嗯,嗯"就结束了。
说一下JVM的内存模型
JVM在执行Java程序时,会把它管理的内存划分为若干个的区域,每个区域都有自己的用途和创建销毁时间。如下图所示,可以分为两大部分,线程私有区和共享区。
下图是根据自己理解画的一个JVM内存模型架构图:
JVM内存分为线程私有区和线程共享区。
线程私有区
1、程序计数器
当同时进行的线程数超过CPU数或其内核数时,就要通过时间片轮询分派CPU的时间资源,不免发生线程切换。这时,每个线程就需要一个属于自己的计数器来记录下一条要运行的指令。如果执行的是JAVA方法,计数器记录正在执行的java字节码地址,如果执行的是native方法,则计数器为空。
2、虚拟机栈
线程私有的,与线程在同一时间创建。管理JAVA方法执行的内存模型。每个方法执行时都会创建一个桢栈来存储方法的的变量表、操作数栈、动态链接方法、返回值、返回地址等信息。栈的大小决定了方法调用的可达深度(递归多少层次,或嵌套调用多少层其他方法,-Xss参数可以设置虚拟机栈大小),栈的大小可以是固定也可以动态扩展。
如果请求的栈深度大于最大可用深度,则抛出stackOverflowError;
如果栈是可动态扩展的,但没有内存空间支持扩展,则抛出OutofMemoryError。
使用jclasslib工具可以查看class类文件的结构。下图为栈帧结构图:
一个线程对应一个虚拟机栈,一个虚拟机栈对应多个栈帧,每个栈帧的的入栈和出栈表示一个方法的调用。
3、本地方法栈
与虚拟机栈作用相似。但它不是为Java方法服务的,而是本地方法(C语言)。由于规范对这块没有强制要求,不同虚拟机实现方法不同。
线程共享区
1、方法区
线程共享的,用于存放被虚拟机加载的类的元数据信息,如常量、静态变量和即时编译器编译后的代码。若要分代,算是永久代(老年代),以前类大多“static”的,很少被卸载或收集,现回收废弃常量和无用的类。其中运行时常量池存放编译生成的各种常量。(如果hotspot虚拟机确定一个类的定义信息不会被使用,也会将其回收。回收的基本条件至少有:所有该类的实例被回收,而且装载该类的ClassLoader被回收)。
2、堆
存放对象实例和数组,是垃圾回收的主要区域,分为新生代和老年代。刚创建的对象在新生代的Eden区中,经过GC后进入新生代的S0区中,再经过GC进入新生代的S1区中,15次GC后仍存在就进入老年代。这是按照一种回收机制进行划分的,不是固定的。若堆的空间不够实例分配,则OutOfMemoryError(OOM)
。
这道题,球友就是瞎扯了一通,扯到后面,面试官打断了话题(实在是听不下去了),JVM这一块就没问了。
推荐:2万字!JVM核心知识总结,赠送18连环炮
一次完整的 HTTP 请求所经历的步骤?
1、DNS解析(通过访问的域名找出其IP 地址,递归搜索)。 2、HTTP 请求,当输入一个请求时,建立一个 Socket 连接发起 TCP的 3 次握手。如果是 HTTPS 请求,会略微有不同。 3.1、客户端向服务器发送请求命令(一般是 GET 或 POST 请求)。这个是补充内容,面试一般不用回答。客户端的网络层不用关心应用层或者传输层的东西,主要做的是通过查找路由表确定如何到达服务器,期间可能经过多个路由器,这些都是由路由器来完成的工作,我不作过多的描述,无非就是通过查找路由表决定通过那个路径到达服务器。客户端的链路层,包通过链路层发送到路由器,通过邻居协议查找给定 IP 地址的 MAC 地址,然后发送 ARP 请求查找目的地址,如果得到回应后就可以使用 ARP 的请求应答交换的 IP 数据包现在就可以传输了,然后发送 IP 数据包到达服务器的地址。 3.2、客户端发送请求头信息和数据。 4.1、服务器发送应答头信息。 4.2、服务器向客户端发送数据。 5、服务器关闭 TCP 连接(4次挥手)。这里是否关闭 TCP 连接,也根据 HTTP Keep-Alive 机制有关。同时,客户端也可以主动发起关闭 TCP 连接。 6、客户端根据返回的HTML、CSS、JS进行渲染。
这是球友的最后一道题,面试官听他扯了一会,然后 “
行吧,今天的面试就到这里,你先回去,后面有结果会通知你的
”.
后记
背面试题是不推荐的学习方法,但如果我们需要面试,那肯定逃不过背面试题。不是说恐话,初级、中级、甚至高级都需要背面试题。
欢迎大家加入我的知识星球,一起背面试题。
这个社会太卷了,你不背,别人背。
机会总是留给有准备的人
推荐阅读