仅作为面试知识点记录,具体课程请在慕课网搜索剑指Java面试-Offer直通车
文章目录
- Java知识考点
- 谈谈你对Java的理解
- Compile Once, Run Anywhere如何实现
- 为什么JVM不直接将源码解析成机器码去执行
- JVM如何加载.class文件
- 谈谈反射
- 类从编译到执行的过程
- 谈谈ClassLoader
- 谈谈类加载器的双亲委派机制
- 类的加载方式
- 你了解Java的内存模型吗
Java知识考点
谈谈你对Java的理解
- 平台无关性
- GC
- 语言特性
- 面向对象
- 类库
- 异常处理
Compile Once, Run Anywhere如何实现
- 编译时
- 运行时
Java源码首先被编译成字节码,再由不同平台的JVM进行解析,Java语言在不同的平台上运行时不需要进行重新编译,Java虚拟机在执行字节码的时候,把字节码转换成具体平台上的机器指令
为什么JVM不直接将源码解析成机器码去执行
- 准备工作:每次执行都需要各种检查
- 兼容性:也可以将别的语言解析成字节码
JVM如何加载.class文件
Java虚拟机
- Class Loader:依据特定格式,加载class文件到内存
- Execution Engine:对命令进行解析
- Native Interface:融合不同开发语言的原生库为Java所用
谈谈反射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为JAVA语言的反射机制。
写一个反射的例子
Robot.java
package com.interview.javabasic.reflect;
public class Robot {
private String name;
public void sayHi(String helloSentence){
System.out.println(helloSentence + " " + name);
}
private String throwHello(String tag){
return "Hello " + tag;
}
static {
System.out.println("Hello Robot");
}
}
ReflectSample.java
package com.interview.javabasic.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectSample {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
Class rc = Class.forName("com.interview.javabasic.reflect.Robot");
Robot r = (Robot) rc.newInstance();
System.out.println("Class name is " + rc.getName());
Method getHello = rc.getDeclaredMethod("throwHello", String.class);
getHello.setAccessible(true);
Object str = getHello.invoke(r, "Bob");
System.out.println("getHello result is " + str);
Method sayHi = rc.getMethod("sayHi", String.class);
sayHi.invoke(r, "Welcome");
Field name = rc.getDeclaredField("name");
name.setAccessible(true);
name.set(r, "Alice");
sayHi.invoke(r, "Welcome");
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println(System.getProperty("java.class.path"));
}
}
类从编译到执行的过程
- 编译器将Robot.java源文件编译为Robot.class字节码文件
- ClassLoader将字节码转换为JVM中的Class<Robot>对象
- JVM利用Class<Robot>对象实例化为Robot对象
谈谈ClassLoader
ClassLoader在Java中有着非常重要的作用,它主要工作在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流。它是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。
模拟ClassLoader
MyClassLoader.java
public class MyClassLoader extends ClassLoader {
private String path;
private String classLoaderName;
public MyClassLoader(String path, String classLoaderName) {
this.path = path;
this.classLoaderName = classLoaderName;
}
//用于寻找类文件
@Override
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
//用于加载类文件
private byte[] loadClassData(String name) {
name = path + name + ".class";
InputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(new File(name));
out = new ByteArrayOutputStream();
int i = 0;
while ((i = in.read()) != -1) {
out.write(i);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return out.toByteArray();
}
}
ClassLoaderCheck.java
public class ClassLoaderChecker {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
MyClassLoader m = new MyClassLoader("/Users/baidu/Desktop/", "myClassLoader");
Class c = m.loadClass("Wali");
System.out.println(c.getClassLoader());
}
}
谈谈类加载器的双亲委派机制
为什么要使用双亲委派机制去加载类
- 避免多份同样字节码的加载
类的加载方式
- 隐式加载:new
- 显式加载:loadClass,forName等
loadClass和forName的区别
类的装载过程
你了解Java的内存模型吗
内存简介
- 32位处理器:2^32的可寻址范围
- 64位处理器:2^64的可寻址范围
地址空间的划分
- 内核空间
- 用户空间
JVM内存模型-JDK8
- 线程私有:程序计数器、虚拟机栈、本地方法栈
- 线程共享:MetaSpace、Java堆
程序计数器(Program Counter Register)
- 当前线程所执行的字节码行号指示器(逻辑)
- 改变计数器的值来选取下一条需要执行的字节码指令
- 和线程是一对一关系即“线程私有”
- 对Java方法计数,如果是Native方法则计数器值位Undefined
- 不会发生内存泄漏
Java虚拟机栈(Stack)
- Java方法执行的内存模型
- 包含多个栈帧
局部变量表和操作数栈
- 局部变量表:包含方法执行过程中的所有变量
- 操作数栈:入栈、出栈、复制、交换、产生消费变量
执行add(1,2)
递归为什么会引发java.lang.StackOverflowError异常
- 递归过深,栈帧数超出虚拟栈深度
本地方法栈
- 与虚拟机栈相似,主要作用于标注了native的方法
元空间(MetaSpace)与永久代(PermGen)的区别
- 元空间使用本地内存,而永久代使用的是jvm的内存
MetaSpace相比PermGen的优势
- 字符串常量池存在永久代中,容易出现性能问题和内存溢出
- 类和方法的信息大小难以确定,给永久代的大小指定带来困难
- 永久代会为GC带来不必要的复杂性
- 方便HotSpot与其他JVM如Jrockit的集成
Java堆(Heap)
-
对象实例的分配区域
-
GC管理的主要区域
JVM三大性能调优参数-Xms -Xmx -Xss的含义
- -Xss:规定了每个线程虚拟机栈(堆栈)的大小
- -Xms:堆的初始值
- -Xmx:堆能达到的最大值
Java内存模型中堆和栈的区别——内存分配策略
- 静态存储:编译时确定每个数据目标在运行时的存储空间需求
- 栈式存储:数据区需求在编译时未知,运行时模块入口前确定
- 堆式存储:编译时或运行时模块入口都无法确定,动态分配
Java内存模型中堆和栈的区别
-
联系:引用对象、数组时,栈里定义变量保存堆中目标的首地址
-
管理方式:栈自动释放,堆需要GC
-
空间大小:栈比堆小
-
碎片相关:栈产生的碎片远小于堆
-
分配方式:栈支持静态和动态分配,而堆仅支持动态分配
-
效率:栈的效率比堆高