Java面试01
一、谈谈你对java的理解
1、平台无关性,一次编译到处运行
2、GC
3、语言特性
4、面向对象
5、类库
6、异常处理
二、Java如何做到一次编译到处运行?(如何做到平台无关性)
首先我们先来编写一个java程序:
public class ByteCodeSimple { public static void main(String[] args) { int i = 1, j = 5; i++; ++j; System.out.println(i); System.out.println(j); } }
首先我们先对这段程序使用javac 命令进行编译:
可以看到得到了对应ByteCodeSimple.class
之后再src目录下,用java命令加上包名执行class文件,我们可以看到他的执行是符合预期的。
我们如何查看.class文件呢,可以通过Idea打开,就可以自动进行反编译。
下面我们使用javap命令来进行反编译-c的意义代表反编译,如果想查询帮助指令,可以输入javap -help进行查询。
一次编译到处运行如何实现?
java文件->.class文件
Java源码首先被编译成字节码,再由不同平台的JVM进行解析,Java语言在不同的平台上运行时不需要重新进行编译,Java虚拟机在执行字节码时,把字节码转换成具体平台的机器指令。
三、JVM如何加载.class文件
JVM最值得学习:jvm内存模型,GC
Class Loader(类加载器):依据特定格式,加载class文件到内存
Execution Engine(执行引擎):对命令进行解析。
Native Interface(本地方法借口):融合不同开发语言的原生库为Java所用。
Runtime Data Area(内存模型):JVM内存空间结构模型。
结论:首先是由ClassLoader加载.class文件,之后通过Execution Engine命令进行执行。
四、什么是反射?
首先新建一个Robot类
public class Robot { private String name; public void hello1(String content) { System.out.println(content + name); } private String hello2(String content) { return "hello2" + content + name; } }
使用反射去调用Robot类中的方法:
public class ReflectSample { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //根据类名获取Class对象 Class rc = Class.forName("com.interview.reflect.Robot"); //根据类类型,去创建Robot对象 Robot robot = (Robot) rc.newInstance(); //调用方法hello1 robot.hello1("123"); //这个方法可以获得道类内部的所有方法包含公有和私有方法,却不能获得器父类继承的方法,和接口实现的方法 Method method = rc.getDeclaredMethod("hello2", String.class); //在反射私有方法时要设置这个方法的值为true method.setAccessible(true); //执行方法并获得返回值 String result = (String) method.invoke(robot, "wzy"); //输出结果 System.out.println("result:" + result); //这个方法可以获取类内的公有方法,和父类继承公有的方法; Method method1 = rc.getMethod("hello1", String.class); //调用方法hello1 method1.invoke(robot, "welcome"); //获取私有成员变量 Field name = rc.getDeclaredField("name"); //设置允许方法私有属性 name.setAccessible(true); //设置私有对象的值 name.set(robot, "yuan"); //调用hello1方法 method1.invoke(robot, "123"); } }
运行结果:
五、谈谈ClassLoader
类从编译到执行的过程
编译器将Robot.java源文件编译为Robot.class字节码文件
ClassLoader将字节码转换为JVM中的Class<Robot>对象
JVM利用Class<Robot>对象实例化为Robot对象。
ClassLoader在Java中有着非常重要的作用,它主要工作在Class的装载的加载阶段,其主要作用是从系统外部获得Class的二进制数据流,它是Java的核心组件,所有的
Class都是由ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流装载进系统,让后交给java虚拟机进行连接、初始化等操作。
通过查看源码我们可以发现,ClassLoader是一个抽象类:
其中最重要的方法就是ClassLoader中的loadClass(String name)方法
我们还可以发现ClassLoader类当中有parent成员变量,是另一个ClassLoader说名ClassLoader的种类不止一种。
ClassLoader的种类:
BootStrapClassLoader:C++编写,加载核心库java.*
ExtClassLoader:Java编写,加载扩展库javax.*(用户看的到的ClassLoader)(C:\Java\jdk\jre\lib\ext;C:\Windows\Sun\Java\lib\ext)
AppClassLoader:Java编写,加载程序所在的目录
自定义ClassLoader:Java编写,定制加载
通过查看ExtClassLoader源码,可以看到它的class的加载路径,用到才会去加载
我们对这个路径进行打印得到:
通过查看AppClassLoader源码,我们可以看到其对应的加载路径:
对这个路径进行打印,得到结果,其中就包含了我们项目本身的.class文件输出路径out.加载类路径(classpath)最重要的路径:E:\project\javabasic\out\production\javabasic;
实现自定义ClassLoader:
首先我们先创建一个类:
1 public class Wali { 2 static { 3 System.out.println("Hello Wali"); 4 } 5 }
之后单独对这个类使用javac进行编译,而不通过IDE,注意这个Wali类中没有定义package,否则执行的时候需要带着包名
创建一个MyClassLoader类
1 public class MyClassLoader extends ClassLoader { 2 /** 3 * 加载类的路径 4 */ 5 private String path; 6 /** 7 * 类加载器的名称 8 */ 9 private String classLoaderName; 10 11 /** 12 * 定义构造方法 13 * @param path 加载类的路径 14 * @param classLoaderName 类加载器的名称 15 */ 16 public MyClassLoader(String path, String classLoaderName) { 17 this.path = path; 18 this.classLoaderName = classLoaderName; 19 } 20 21 /** 22 * 用于寻找类文件 23 * @param name 24 * @return 25 */ 26 @Override 27 public Class findClass(String name) { 28 byte[] b = loadClassData(name); 29 return defineClass(name, b, 0, b.length ); 30 } 31 32 /** 33 * 将文件转换为字节流 34 * @param name 35 * @return 36 */ 37 private byte[] loadClassData(String name) { 38 //拼接出文件的名字 39 name = path + "\\" + name + ".class"; 40 InputStream in = null; 41 ByteArrayOutputStream bos = null; 42 43 try { 44 in = new FileInputStream(name); 45 bos = new ByteArrayOutputStream(); 46 int i = 0; 47 //对文件进行读取 48 while ((i = in.read()) != -1) { 49 bos.write(i); 50 } 51 } catch (IOException e) { 52 e.printStackTrace(); 53 } finally { 54 try { 55 in.close(); 56 bos.close(); 57 } catch (IOException e) { 58 e.printStackTrace(); 59 } 60 } 61 //返回字节数组 62 return bos.toByteArray(); 63 } 64 }
MyClassLoader测试类:
1 public class MyClassLoaderChecker { 2 public static void main(String[] args) throws IllegalAccessException, InstantiationException { 3 MyClassLoader myClassLoader = new MyClassLoader("E:\\project\\javabasic\\src\\com\\interview\\reflect", "myClassLoader"); 4 Class aClass = myClassLoader.findClass("Wali"); 5 //创建对象触发执行静态块代码 6 Object o = aClass.newInstance(); 7 } 8 }
运行结果: