我是 Guide 哥,一 Java 后端开发,半个全栈,自由的少年。
这篇文章是一位 女读者 (加粗!太难得)的面试阿里的经历分享,虽然第二面面完就失败了,但是这样的经历对自己帮助还是很大的。
下面的一些问题非常具有代表性,部分问题我简单做了修改(有些问题表述的不那么准确)。这些问题对于大家用于自测或者准备面试都很有帮助。
我只给出了少部分问题的参考答案,因为自己真的抽不出有时间来把每一个问题都细心解答一遍了。单单是回答了下面的少部分问题,就从昨晚 9 点一直忙到 12 点半。
有答案需求的小伙伴,评论区安排,需要的人多的话,我这周末花时间把一份顶好的参考答案都整出来!
小声 BB:写参考答案其实挺难的,相比于面试的时候回答问题来说。很多面试官自己问的问题可能连自己都不清楚,哈哈哈!单单是回答了下面的少部分问题,就从昨晚 9 点一直忙到 12 点半。
自我介绍就不说了,每一面都会让你说。
这个面试前肯定要准备的,Guide 哥的文章中也提到了很多次,我准备的还算充分,面试官比较满意。
—第 4 题参考答案—
JDK 动态代理只能只能代理实现了接口的类,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。
—第 5 题参考答案—
Java 语言中的类、方法、变量、参数和包等都可以注解标记,程序运行期间我们可以获取到相应的注解以及注解中定义的内容,这样可以帮助我们做一些事情。比如说 Spring 中如果检测到说你的类被 @Component
注解标记的话,Spring 容器在启动的时候就会把这个类归为自己管理,这样你就可以通过 @Autowired
注解注入这个对象了。
—第 6 题参考答案—
反射介绍:
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
反射的优缺点如下:
为什么框架需要反射技术?
在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。
举例:
Class.forName()
通过反射加载数据库的驱动程序;更多 Java 基础相关的问题,请参考这篇顶好顶完善的文章: 「Java 面试题精华集」Java 基础知识篇(2020 最新版)附 PDF 版 ! 。
集合框架相关的问题的答案,请参考这篇顶好顶完善的文章: 「Java 面试题精华集」1w 字的 Java 集合框架篇(2020 最新版)附 PDF 版 ! ,里面介绍的非常详细非常棒!
—第 6 题参考答案—
线程死锁描述的是这样一种情况:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。
—第 7 题参考答案—
通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。如果想实现每一个线程都有自己的专属本地变量该如何解决呢? JDK 中提供的ThreadLocal
类正是为了解决这样的问题。 ThreadLocal
类主要解决的就是让每个线程绑定自己的值,可以将ThreadLocal
类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。
如果你创建了一个ThreadLocal
变量,那么访问这个变量的每个线程都会有这个变量的本地副本,这也是ThreadLocal
变量名的由来。他们可以使用 get()
和 set()
方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。
再举个简单的例子:
比如有两个人去宝屋收集宝物,这两个共用一个袋子的话肯定会产生争执,但是给他们两个人每个人分配一个袋子的话就不会出现这样的问题。如果把这两个人比作线程的话,那么 ThreadLocal 就是用来避免这两个线程竞争的。
ThreadLocal
最终的变量是放在了当前线程的 ThreadLocalMap
中,并不是存在 ThreadLocal
上,ThreadLocal
可以理解为只是ThreadLocalMap
的封装,传递了变量值。 我们可以把 ThreadLocalMap
理解为ThreadLocal
类实现的定制化的 HashMap
。 ThrealLocal
类中可以通过Thread.currentThread()
获取到当前线程对象后,直接通过getMap(Thread t)
可以访问到该线程的ThreadLocalMap
对象。
每个Thread
中都具备一个ThreadLocalMap
,而ThreadLocalMap
可以存储以ThreadLocal
为 key ,Object 对象为 value 的键值对。
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
......
}
比如我们在同一个线程中声明了两个 ThreadLocal
对象的话,会使用 Thread
内部都是使用仅有那个ThreadLocalMap
存放数据的,ThreadLocalMap
的 key 就是 ThreadLocal
对象,value 就是 ThreadLocal
对象调用set
方法设置的值。
ThreadLocalMap
是ThreadLocal
的静态内部类。
—第 2 题参考答案—
周志明先生在《深入理解 Java 虚拟机》第二版中 P92 如是写道:
“老年代 GC(Major GC/Full GC),指发生在老年代的 GC……”
上面的说法已经在《深入理解 Java 虚拟机》第三版中被改正过来了。感谢 R 大的回答:
总结:
针对 HotSpot VM 的实现,它里面的 GC 其实准确分类只有两大种:
部分收集 (Partial GC):
整堆收集 (Full GC):收集整个 Java 堆和方法区。
—第 3 题参考答案—
方法区也被称为永久代。很多人都会分不清方法区和永久代的关系,为此我也查阅了文献。
《Java 虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它。那么,在不同的 JVM 上方法区的实现肯定是不同的了。 方法区和永久代的关系很像 Java 中接口和类的关系,类实现了接口,而永久代就是 HotSpot 虚拟机对虚拟机规范中方法区的一种实现方式。 也就是说,永久代是 HotSpot 的概念,方法区是 Java 虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久代这一说法。
—第 4 题参考答案—
JDK 1.8 的时候,方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间,元空间使用的是直接内存。
https://blogs.oracle.com/poonam/about-g1-garbage-collector,-permanent-generation-and-metaspace
下面是一些常用参数:
-XX:MetaspaceSize=N //设置 Metaspace 的初始(和最小大小)
-XX:MaxMetaspaceSize=N //设置 Metaspace 的最大大小
与永久代很大的不同就是,如果不指定大小的话,随着更多类的创建,虚拟机会耗尽所有可用的系统内存。
为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?
https://plumbr.io/handbook/garbage-collection-in-java
1.整个永久代有一个 JVM 本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。
当你元空间溢出时会得到如下错误:
java.lang.OutOfMemoryError: MetaSpace
你可以使用 -XX:MaxMetaspaceSize
标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。-XX:MetaspaceSize
调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。
2.元空间里面存放的是类的元数据,这样加载多少类的元数据就不由 MaxPermSize
控制了, 而由系统的实际可用空间来控制,这样能加载的类就更多了。
3.在 JDK8,合并 HotSpot 和 JRockit 的代码时, JRockit 从来没有一个叫永久代的东西, 合并之后就没有必要额外的设置这么一个永久代的地方了。
—第 5 题参考答案—
主要进行 gc 的区域是堆,就 HotSpot 虚拟机来说,永久代会发生 gc (full gc),但是,元空间使用的是直接内存不会发生 gc。
—第 1 题参考答案—
说到分层,我们先从我们平时使用框架开发一个后台程序来说,我们往往会按照每一层做不同的事情的原则将系统分为 三层(复杂的系统分层可能会更多):
复杂的系统需要分层,因为每一层都需要专注于一类事情。我们的网络分层的原因也是一样,每一层只专注于做一类事情。
为什么计算机网络要分层呢? ,我们再来较为系统的说一说:
说到计算机网络分层,我想到了计算机世界非常非常有名的一句话,这里分享一下:
计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决,计算机整个体系从上到下都是按照严格的层次结构设计的。
Guide 哥注:如果一层不够那就加两层吧!
—第 2 题参考答案—
TCP/IP 4 层模型:
需要注意的是,我们并不能将 TCP/IP4 层模型 和 OSI7 层模型完全精确地匹配起来,不过可以简单将两者对应起来,如下图所示:
—第 3 题参考答案—
HTTP 协议 属于应用层的协议。
HTTP 协议是基于 TCP 协议的,发送 HTTP 请求之前首先要建立 TCP 连接也就是要经历 3 次握手。目前使用的 HTTP 协议大部分都是 1.1。在 1.1 的协议里面,默认是开启了 Keep-Alive 的,这样的话建立的连接就可以在多次请求中被复用了。
另外, HTTP 协议是”无状态”的协议,它无法记录客户端用户的状态 一般我们都是通过 Session 来记录客户端用户的状态。
Guide 的女读者挺少的,10 个关注我的人中只有一个女性读者。
最近在这篇文章:《最强(细)校招/社招求职指南:隔壁小姐姐已经收到字节意向书,你的秋招还没开始?》中还逮到了一个拿到的字节意向书的女读者(颜值又高,技术又好,太厉害了!),我真的不要太开心了!然后,就厚脸皮地去让这位小姐姐帮忙写一下面经(后续可能会安排上)。
还不是为了你们?我真是操碎了心啊!
我是Guide哥,Java后端开发,半个全栈,自由的少年。一个三观比主角还正的技术人。我们下期再见!