一、Java 部分
1.如何理解 Java 的多态?其中,重载和重写有什么区别?
多态:
- 父类引用指向子类对象
- 引用指向的对象和应用调用的方法在编译期不能确定,在运行期确定
- 除非静态方法外,父类对象的引用访问的都是父类的属性和静态方法
- 父类对象的引用要访问子类的属性和方法,需要向下转型
重载:
- 方法参数列表不同
- 方法返回值可以不同(表明不同通过返回值进行方法重载)
- 方法访问修饰符可以不同(表明不同通过访问修饰符进行方法重载)
- 可以抛不同的异常(表明不同通过抛出的异常进行方法重载)
- 注:如果父类方法访问修饰符为 private,子类无法重载,只是定义了一个新的方法
重写:
- 方法参数列表相同
- 方法返回值相同
- 方法访问修饰符的限制大于被重写的方法的访问修饰符(public > protected > default > private)
- 不能抛新异常或异常不能比被重写方法的异常类型更宽泛(所抛出的异常为被重写方法的子类)
- 注:如果父类方法访问修饰符为 private,子类无法重写,否则只是定义了一个新的方法
2.谈一下 JVM 虚拟机内存分配?哪部分是线程公有的,哪部分是私有的?
内存分配:方法区(运行时常量池)、堆、虚拟机栈、本地方法栈、程序计数器
线程公有:方法区、堆
线程私有:虚拟机栈、本地方法栈、程序计数器
线程共享:
方法区:
- 存储被虚拟机加载的类版本、字段、方法、接口、等描述信息和编译器编译后的代码(字节码 .class 文件)等数据
- 常量在方法区中,因为方法区中有一个运行时常量池,存放编译器生成的各种字面量和符号引用,类加载后,会把常量池中的内容放入运行时常量池中(运行时常量池在方法区中)
- 编译期:生成的各种字面量(int i = 3,3 就是字面量)和符号引用,这些存放在常量池(常量池在字节码文件中)
- 运行时常量池不一定从字节码常量池中获取常量,可能在程序运行期间将新的常量放入池中。如:String.intern(),作用:先从方法区的运行时常量池查找是否有该值,有则返回该值引用,无则将该值加入到运行时常量池
堆:
- 虚拟机中管理的内存中最大的一块,改内存存放对象实例,几乎所有的对象实例都在这里分配内存,通常对应我们说的 new 对象
- 随着 JIT(Java In Time Compiler)编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上不再那么绝对
- 堆是垃圾收集器管理的主要区域
- 栈中存放的局部引用变量所指向的内容都在堆中存放
线程私有:
虚拟机栈:
- 生命周期和线程相同
- 描述方法执行的内存模型,每个方法会在执行的同时创建一个栈帧,用于存储局部变量表、操作数表、动态链接、方法出口等信息
- 方法的调用过程对应一个栈帧在虚拟机栈中从入栈到出栈的过程
本地方法栈:为 Native 方法服务
程序计数器:
- 当前线程所执行的字节码的行号指示器,生命周期和线程相同
- 通过改变计数器的值来选取吓一跳需要执行的字节码的指令,分支、循环、跳转、异常处理、线程恢复等基础功能依赖计数器来完成
- 线程切换不影响各自计数器的记录
- 执行的是 Java 方法,计数器记录的是正在执行的虚拟机字节码指令的地址
- 执行的是 Native 方法,计数器值为空(Undefined)
- 此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域
3.final 关键字的用法?
- final 修饰基本类型数据,数据本身不可变
- final 修饰对象,对象的引用不可变。引用指向的内容可变final 修饰类属性和类方法,不能被覆盖(重写),方法
- final 修饰类,类无法被继承