java 高级面试部分合集


Android 面试 PDF 合集

1、说说你对 java 中反射的理解

java 中 的反射首先首先是能够获取到java 中要反射类的字节码,获取字节码有三种方法,
1)Class.forName(className)
2)类名.class
3)this.class()
然后将字节码中的方法,变量,构造函数等映射成相应的Method、Filed、Constructor 等 类,这些类提供丰富的方法被我们使用。

2、java 中的静态代理,动态代理?

待续····

3、动态代理静态代理的区别?

1)静态代理通常只是代理一个类,动态代理是代理一个接口下的多个实现类。
2)静态代理先知道要代理的是什么,动态代理不知道需要代理的是什么,只有在运行时才知道。
3)动态代理是实现JDK 里的InvocationHandler 接口的invoke方法,但注意是代理的接口,也就是你的业务必须要实现接口,通过Proxy里的new ProxyInstance 得到代理对象。
4)还有一种动态代理,CGLIB,代理的类,不需要业务继承接口,通过派生的子类来实现代理,通过在运行时,动态修改字节码到达修改类的目的。
5)AOP 编程就是基于动态代理实现的,比如著名的Spring 框架、Hibernate 框架等等,都是动态代理使用的例子。

4、Java 中常用的设计模式

Java 中一般认为有 23 种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面列 出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。

总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录 模式、状态模式、访问者模式、中介者模式、解释器模式。

5、JVM垃圾回收机制和常见算法

理论上来讲 Sun 公司只定义了垃圾回收机制规则而不局限于其实现算法,因此不同厂商生产的虚拟机采用的算法 也不尽相同。
GC(Garbage Collector)在回收对象前首先必须发现那些无用的对象,如何去发现定位这些无用的对象?

常用 的搜索算法如下:
1)引用计数器算法(废弃)
引用计数器算法是给每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用失效的时候, 计数器-1,当计数器为 0 的时候,JVM 就认为对象不再被使用,是“垃圾”了。
引用计数器实现简单,效率高;但是不能解决循环引用问问题(A 对象引用 B 对象,B 对象又引用 A 对象,但是 A,B 对象已不被任何其他对象引用),同时每次计数器的增加和减少都带来了很多额外的开销,所以在 JDK1.1 之后, 这个算法已经不再使用了。
2)根搜索算法(使用)
根搜索算法是通过一些“GC Roots”对象作为起点,从这些节点开始往下搜索,搜索通过的路径成为引用链 (Reference Chain),当一个对象没有被 GC Roots 的引用链连接的时候,说明这个对象是不可用的。
在这里插入图片描述
GC Roots 对象包括:
a) 虚拟机栈(栈帧中的本地变量表)中的引用的对象。
b) 方法区域中的类静态属性引用的对象。
c) 方法区域中常量引用的对象。
d) 本地方法栈中 JNI(Native 方法)的引用的对象。

通过上面的算法搜索到无用对象之后,就是回收过程,回收算法如下
1)标记—清除算法(Mark-Sweep)(DVM 使用的算法)
标记—清除算法包括两个阶段:“标记”和“清除”。在标记阶段,确定所有要回收的对象,并做标记。清除阶 段紧随标记阶段,将标记阶段确定不可用的对象清除。标记—清除算法是基础的收集算法,标记和清除阶段的效率不 高,而且清除后回产生大量的不连续空间,这样当程序需要分配大内存对象时,可能无法找到足够的连续空间。
在这里插入图片描述
2)复制算法(Copying)
复制算法是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上, 然后把这块内存整个清理掉。复制算法实现简单,运行效率高,但是由于每次只能使用其中的一半,造成内存的利用 率不高。现在的 JVM 用复制方法收集新生代,由于新生代中大部分对象(98%)都是朝生夕死的,所以两块内存的比 例不是 1:1(大概是 8:1)。

在这里插入图片描述
3)标记—整理算法(Mark-Compact)
标记—整理算法和标记—清除算法一样,但是标记—整理算法不是把存活对象复制到另一块内存,而是把存活对 象往内存的一端移动,然后直接回收边界以外的内存。标记—整理算法提高了内存的利用率,并且它适合在收集对象 存活时间较长的老年代。

在这里插入图片描述
4)分代收集(Generational Collection)
分代收集是根据对象的存活时间把内存分为新生代和老年代,根据各个代对象的存活特点,每个代采用不同的垃 圾回收算法。新生代采用复制算法,老年代采用标记—整理算法。垃圾算法的实现涉及大量的程序细节,而且不同的 虚拟机平台实现的方法也各不相同。

6、谈谈JVM 的内存结构和内存分配
  1. Java 内存模型
    Java 虚拟机将其管辖的内存大致分三个逻辑部分:方法区(Method Area)、Java 栈和 Java 堆。
    (1)、方法区是静态分配的,编译器将变量绑定在某个存储位置上,而且这些绑定不会在运行时改变。 常数池,源代码中的命名常量、String 常量和 static 变量保存在方法区。
    (2)、Java Stack 是一个逻辑概念,特点是后进先出。一个栈的空间可能是连续的,也可能是不连续的。 最典型的 Stack 应用是方法的调用,Java 虚拟机每调用一次方法就创建一个方法帧(frame),退出该 方法则对应的 方法帧被弹出(pop)。栈中存储的数据也是运行时确定的。
    (3)、Java 堆分配(heap allocation)意味着以随意的顺序,在运行时进行存储空间分配和收回的内存管理模型。 堆中存储的数据常常是大小、数量和生命期在编译时无法确定的。Java 对象的内存总是在 heap 中分配。 我们每天都在写代码,每天都在使用 JVM 的内存。
    2)、java 内存分配
    (1)、基础数据类型直接在栈空间分配;
    (2)、方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收;
    (3)、引用数据类型,需要用 new 来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量;
    (4)、方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完后从栈空间回收;
    (5)、局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收, 堆空间区域等待 GC 回收;
    (6)、方法调用时传入的实际参数,先在栈空间分配,在方法调用完成后从栈空间释放;
    (7)、字符串常量在 DATA 区域分配 ,this 在堆空间分配;
    (8)、数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小!
7、java 中引用类型都有哪些

Java 中对象的引用分为四种级别,这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

8、 stack (栈) 和 heap (堆) 有什么区别

1) 申请方式不同
stack:由系统自动分配。例如,声明在函数中一个局部变量 int b; 系统自动在栈中为 b 开辟空间 heap:需要程序员自己申请,并指明大小,在 c 中 malloc 函数,对于 Java 需要手动 new Object()的形式开辟

2)申请后系统的响应
stack:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 heap:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间 分配给程序。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空 闲链表中。

3)申请大小的限制
stack:栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS 下,栈的大小是 2M(也有的说是 1M,总之是一个编译时就确定的常数),如果 申请的空间超过栈的剩余空间时,将提示 overflow。因此,能从栈获得的空间较小。

heap:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的, 自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见, 堆获得的空间比较灵活,也比较大。

4)申请效率的比较:
stack:由系统自动分配,速度较快。但程序员是无法控制的。
heap:由 new 分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

5)heap 和 stack 中的存储内容
stack: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址, 然后是函数的各个参数,在大多数的 C 编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量 是不入栈的。 当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的 下一条指令,程序由该点继续运行。
heap:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

6) 数据结构层面的区别
还有就是数据结构方面的堆和栈,这些都是不同的概念。这里的堆实际上指的就是(满足堆性质的)优先队列的 一种数据结构,第 1 个元素有最高的优先权;栈实际上就是满足先进后出的性质的数学或数据结构。 虽然堆栈,堆栈的说法是连起来叫,但是他们还是有很大区别的,连着叫只是由于历史的原因。

9、Java 的类加载器的种类都有哪些?

1)、根类加载器(Bootstrap) --C++写的 ,看不到源码
2)、扩展类加载器(Extension) --加载位置 :jre\lib\ext 中
3)、系统(应用)类加载器(System\App) --加载位置 :classpath 中
4)、自定义加载器(必须继承 ClassLoader)

10、类什么时候被初始化?

1)创建类的实例,也就是 new 一个对象
2)访问某个类或接口的静态变量,或者对该静态变量赋值
3)调用类的静态方法
4)反射(Class.forName(“com.lyj.load”))
5)初始化一个类的子类(会首先初始化子类的父类
6)JVM 启动时标明的启动类,即文件名和类名相同的那个类
只有这 6 中情况才会导致类的类的初始化。
类的初始化步骤:
1)如果这个类还没有被加载和链接,那先进行加载和链接
2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一 次),那就初始化直接的父类(不适用于接口)
3)加入类中存在初始化语句(如 static 变量和 static 块),那就依次执行这些初始化语句。

11、简述Java 类加载体系之 ClassLoader 双亲委托机制

java 是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全 沙箱分别是:
1) 类加载体系
2) .class 文件检验器
3) 内置于 Java 虚拟机(及语言)的安全特性
4) 安全管理器及 Java API

主要讲解类的加载体系: java 程序中的 .java 文件编译完会生成 .class 文件,而 .class 文件就是通过被称为类加载器的 ClassLoader 加载的,而 ClassLoder 在加载过程中会使用“双亲委派机制”来加载 .class 文件,先上图:
在这里插入图片描述

BootStrapClassLoader : 启 动 类 加 载 器 , 该 ClassLoader 是 jvm 在 启 动 时 创 建 的 , 用 于 加 载 J A V A H O M E JAVA_HOME JAVAHOME/jre/lib 下面的类库(或者通过参数-Xbootclasspath 指定)。由于启动类加载器涉及到虚拟 机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不能直接通过引用进行操作。

ExtClassLoader:扩展类加载器,该ClassLoader是在sun.misc.Launcher里作为一个内部类ExtClassLoader 定义的(即 sun.misc.Launcher$ExtClassLoader),ExtClassLoader 会加载 $JAVA_HOME/jre/lib/ext 下的类 库(或者通过参数-Djava.ext.dirs 指定)。

AppClassLoader:应用程序类加载器,该 ClassLoader 同样是在 sun.misc.Launcher 里作为一个内部类 AppClassLoader 定义的(即 sun.misc.Launcher$AppClassLoader),AppClassLoader 会加载 java 环境变量 CLASSPATH 所 指 定 的 路 径 下 的 类 库 , 而 CLASSPATH 所 指 定 的 路 径 可 以 通 过 System.getProperty(“java.class.path”)获取;当然,该变量也可以覆盖,可以使用参数-cp,例如:java -cp 路 径 (可以指定要执行的 class 目录)。

CustomClassLoader:自定义类加载器,该 ClassLoader 是指我们自定义的 ClassLoader,比如 tomcat 的 StandardClassLoader 属于这一类;当然,大部分情况下使用 AppClassLoader 就足够了。

前面谈到了 ClassLoader 的几类加载器,而 ClassLoader 使用双亲委派机制来加载 class 文件的。ClassLoader 的双亲委派机制是这样的(这里先忽略掉自定义类加载器 CustomClassLoader):

1)当 AppClassLoader 加载一个 class 时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父 类加载器 ExtClassLoader 去完成。

2)当 ExtClassLoader 加载一个 class 时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给 BootStrapClassLoader 去完成。

3)如果 BootStrapClassLoader 加载失败(例如在 J A V A H O M E JAVA_HOME JAVAHOME/jre/lib 里未查找到该 class),会使用 ExtClassLoader 来尝试加载;

4)若 ExtClassLoader 也加载失败,则会使用 AppClassLoader 来加载,如果 AppClassLoader 也加载失败, 则会报出异常 ClassNotFoundException。

待续····

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值