java面试
谈谈你对Java平台的理解?
java是一种面向对象编程语言,一大特点是“write once,run anywhere”,是通过javac编译得到.class的字节码文件在jvm内嵌的解释器转化为机器码指令(汇编指令,汇编指令属于硬件原语,此时并不能直接被CPU进行执行,依旧需要借用硬件层面的翻译操作,将汇编指令转化为cpu可识别的二进制指令。等待CPU的调度执行,最后影响对应栈中的数据信息)。Java 通过字节码和 Java 虚拟机(JVM)这种跨平台的抽象,屏蔽了操作系统和硬件的细节,这也是实现“一次编译,到处执行”的基础。不像c语言在不同环境下底层函数差异需更改代码适应,无法做到java的适应所有机器的一次编译。在主流的java版本,java的运行方式也为server模式,编译方式默认是混合模式,故称java是半编译半解释型语言,解释是一行行解释执行class中的字节码指令转为机器码指令,编译是指jit(Just-In-Time)模块,在热门的hotspot虚拟机中存在c1和c2编译器,将常用的代码以方法为单位编译成机器码指令,不用每次都解释,加快了运行速度。最新java也出了AOT(Ahead-of-Time Compilation)是类似c编译的提前编译功能,在程序运行前将class文件直接转为机器码指令。java的优秀有部分来源于优秀的jvm,每次的java版本更新不仅在更新java语言和新功能,并在jvm的优化上也下了很大的功夫(垃圾收集器的更新)。jvm也已经不仅仅适应用java语言,也出现了Clojure、Scala、Groovy、JRuby、Jython 等大量 JVM 语言,只要符合jvm规范的class文件,就可以在jvm上运行。另外一大特点就是垃圾收集(GC, Garbage Collection),Java 通过垃圾收集器(Garbage Collector)回收分配内存,大部分情况下,程序员不需要自己操心内存的分配和回收。
Exception和Error有什么区别?
相对完善的异常处理机制也是java语言的一大特色,异常的总类是Throwable类,只有此类的实例才可以实现对异常的抛出和捕获,对异常的划分了Exception和Error2个子类。Error是正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。Exception是程序正常运行中,可预料的意外情况,可能并且应该被捕获,进行相应处理。Exception又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。不检查异常就是所谓的运行时异常,类似 NullPointerException、ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。异常捕获和处理的小细节:1.尽量不要捕获类似 Exception 这样的通用异常,而是应该捕获特定异常,异常也是重要的信息;2.Throw early, catch late 原则,throw early是为了更精确的定位到异常位置,至于“catch late”,其实是我们经常苦恼的问题,捕获异常后,需要怎么处理呢?最差的处理方式,就是我前面提到的“生吞异常”,本质上其实是掩盖问题。如果实在不知道如何处理,可以选择保留原有异常的 cause 信息,直接再抛出或者构建新的异常抛出去。在更高层面,因为有了清晰的(业务)逻辑,往往会更清楚合适的处理方式是什么。3.try-catch 代码段会产生额外的性能开销,或者换个角度说,它往往会影响 JVM 对代码进行优化,所以建议仅捕获有必要的代码段,尽量不要一个大的 try 包住整段的代码;与此同时,利用异常控制代码流程,也不是一个好主意,远比我们通常意义上的条件语句(if/else、switch)要低效。4.Java 每实例化一个 Exception,都会对当时的栈进行快照,这是一个相对比较重的操作。如果发生的非常频繁,这个开销可就不能被忽略了。异常实例的构造十分昂贵。这是由于在构造异常实例时,Java 虚拟机便需要生成该异常的栈轨迹(stack trace)。该操作会逐一访问当前线程的 Java 栈帧,并且记录下各种调试信息,包括栈帧所指向方法的名字,方法所在的类名、文件名,以及在代码中的第几行触发该异常。故我们需对uncheck的异常进行代码处理,避免产生这些异常。
谈谈final、finally、 finalize有什么不同?
这些单词相近,但用法无交集。final可以修饰类,变量,方法,在修饰类上表明此类无法被继承,修饰变量分为修饰基础变量和引用变量,在修饰基础变量就是常量或标量,修饰引用变量就是地址不能更改,但对象里面的属性等并没有限制。修饰方法则方法不能被子类重写。如何实现immutable(不可变)的类呢:1.此类用final修饰,为了防止子类绕过限制;2.类的属性需要添加private和final属性,并没有setter方法;3.类的构造方法时给引用属性赋值时是用深度拷贝,这样即使外部引用的任何变化都对此类的无影响;4.类的getter方法需要实现copy-on-write的实现。finally是异常处理时用来关闭连接等资源。finalize是Object中用来对象被回收前需要执行的。实现了 finalize 方法的对象是个“特殊公民”,JVM 要对它进行额外处理。finalize 本质上成为了快速回收的阻碍者,可能导致你的对象经过多个垃圾收集周期才能被回收。
强引用、软引用、弱引用、幻象引用有什么区别?
不同的引用类型,主要体现的是对象不同的可达性(reachable)状态和对垃圾收集的影响。
强引用就是我们平常常用的引用变量,在gc时这些引用对象不会被回收,即使内存溢出,导致报出OutOfMemoryError异常。
软引用是在内存不够情况下必回收的对象引用。
弱引用是在gc下必回收的引用对象。
幻想引用跟上三者不同,并不能通过幻想引用获取原始对象,幻想引用提供了对象被finalize后,做某些事的机制。
软引用和弱引用常用在缓存中,弱引用处理非强制的映射关系,比如优惠劵映射用户列表的弱引用,用户总列表中强引用下删除了某用户,导致优惠劵映射用户列表的弱引用相应的用户在gc后自动删除。
Java 反射机制,动态代理是基于什么原理?
java是静态的强类型语言,但是因为提供了反射等机制,也具备了部分动态类型语言的能力。反射机制赋予程序在运行时自省(introspect,官方用语)的能力。通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时修改类定义。关于代理:通过代理静默地解决一些业务无关的问题,比如远程调运、安全、事务、日志、资源关闭……让应用开发者可以只关心他的业务。代理实现的方式:1.静态代理,书写一个静态的类,相当于在之前的类上套了一层,这样我们就可以在不改变之前的类的前提下去对原有功能进行扩展。缺点:每个业务类都要对应一个代理类,不灵活并且类堆积。 动态代理:运行时自动生成代理对象。缺点是生成代理对象和调用代理方法都要额外花费时间。动态代理分成jdk代理和cglib代理。jdk代理:基于Java反射机制实现,必须要实现了接口的业务类才能用这种办法生成代理对象。cglib代理:基于ASM机制实现,通过动态生成业务类的子类作为代理类。
int和Integer有什么区别?
int是基础数据类型,Integer是引用类型,jdk1.5后存在自动装箱和拆箱的语法糖机制,本质是在编译时候调运Integer的静态方法ValueOf,intValue。一般ValueOf方法是new 一个新的封装对象,但常用的值会做缓存比如Integer里面的内部类IntegerCache,-128到127在Integer,Short,Byte,Long,Boolean.TRUE和Boolean.false,Character的0-127。封装类是对象,相比与基本数据类型更占内存,更多IO操作得到真正存储的值。
对象的内存结构是什么样的?占用多少字节?
在HotSpot虚拟机中,对象在内存中存储的布局都可以分为3块区域。
- 对象头(Header)
包含两部分信息:- Mark Word:用于存储对象自身的运行时数据,如:哈希码(HashCode),GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等,
- 类型指针:即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
另外,如果对象是Java数组,那在对象头中还必须有一块用国语记录数组长度的数据。
- 实例数据(Instance Data):对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。
- 对齐填充(Padding):对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。使对象的大小必须是8字节的整数倍。
- 对象类型
- 对象头(Header)
- MarkWord,4字节
- Class对象指针,4字节
- 实例数据(Instance Data)
- 对齐数据(Padding), 按8个字节对齐
- 对象头(Header)
- 数组类型
-对象头(Header)
- MarkWord,4字节
- Class对象指针,4字节- 数组长度,4字节
- 实例数据(Instance Data)
- 对齐数据(Padding), 按8个字节对齐