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 }

运行结果:

 

posted @ 2019-07-21 23:48  风云独舞  阅读(154)  评论(0编辑  收藏  举报