提出问题

java中的动态编译和静态编译如何理解???

解决问题

java 注解和静态方法执行顺序 java静态方法和实例方法_实例

1.概念

静态编译:一次性编译。在编译的时候把你所有的模块都编译进去。

动态编译:按需编译。程序在运行的时候,用到那个模块就编译哪个模块。

2.实例

例一:

/**
 * @Author 阿毅
 * Created by Ay on 2016/01/05.
 */
class Ay{
    public static void main(String[] args) {
        //如编译后的Boy.class文件消失,会报错误:java.lang.NoClassDefFoundError
        Boy boy = new Boy();
        boy.sayHello();  
    }
}

class Boy implements Person{
    public void sayHello(){
        System.out.println("say hello .....");
    }
}

interface Person{
    void sayHello();
}

例二:

比如开发一个阅读器,支持txt,pdf,doc三种格式。我们把读txt,读pdf,读doc定义为三个功能模块。

静态编译:我想看个txt,点击应用程序图标以后,三个功能都加载进来了。在这里,另外两个模块的作用就是占用系统资源。

动态编译:我想看个txt,点击应用程序,判断格式,只加载读txt模块,使用读txt模块。。。
显然,动态编译1速度快,2节省了系统资源,3利于今后拓展。。。。。。。。

例三:

把下面的例子拷贝到上面的main方法中:

/**
 * @Author 阿毅
 * Created by Ay on 2016/01/05.
 */
public class Ay{
    public static void main(String[] args) {
        System.out.println("请输入您要输入的类的名字");  
        //输入一个类的名字  
        java.util.Scanner sc = new java.util.Scanner(System.in);   
        String s = sc.next();  
        //加载这个类  
        Class c = Class.forName(s);  
        //获得这个类的实例化对象  
        Object obj = c.newInstance();  
        //强制转型  
        Person person = (Person)obj;  
        person.sayHello();
    }

}

我们把编译文件Boy.class删除,上面代码编译并未报错。但是运行时候会出现NoClassDefFoundError异常,这时候我们用把删除掉的Boy.class重新放回原处,重新运行,这时候,代码就可以正常运行。

这样我们可以在不停止程序运行的情况下,动态地加载一个类。我认为这体现出了Java的动态性。通过上面的例子,我认为java语言是一种“半动态”性的语言。

3.最简单的动态编译例子

/**
 * @Author 阿毅
 * Created by Ay on 2016/01/05.
 */
public class Ay{
    public static void main(String[] args) throws Exception{
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int flag = compiler.run(null, null, null,"D:\\HelloWorld.java");
        System.out.println(flag == 0 ? "编译成功" : "编译失败");
    }
}


/**
 * D盘放置的类的内容
 * Created by HuangWenYi on 2017/1/6.
 */
public class HelloWorld {

    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

解释一下:
第一个参数:为java编译器提供参数
第二个参数:得到java编译器的输出信息
第三个参数:接受编译器的错误信息
第四个参数:可变参数(是一个String数组)能传入一个或多个java源文件
返回值:0表示编译成功,非0表示编译失败

4.动态运行编译好的类

/**
 * @Author 阿毅
 * Created by Ay on 2016/01/05.
 */
public class Ay{
    public static void main(String[] args) throws Exception{
        //获得系统的java编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        //编译文件,编译成功返回 0 否则 返回 1
        int flag = compiler.run(null, null, null,"D:\\HelloWorld.java");
        System.out.println(flag == 0 ? "编译成功" : "编译失败");
        //指定class路径,默认和源代码路径一致,加载class
        URLClassLoader classLoader = new URLClassLoader(new URL[]{new URL("file:/d:/")});
        Object printer = classLoader.loadClass("HelloWorld").newInstance();
        System.out.println(printer.toString());

    }
}

运行结果

编译成功
HelloWorld@4c583ecf

5.慎用动态编译

  • 在框架中谨慎使用
  • 不要在要求高性能的项目使用
    动态编译毕竟需要一个编译过程,与静态编译相比多了一个执行环节,因此在高性能项目中不要使用动态编译。
  • 动态编译要考虑安全问题
    它是非常典型的注入漏洞,只要上传一个恶意Java程序就可以让你所有的安全工作毁于一旦。
  • 记录动态编译过程
    建议记录源文件、目标文件、编译过程、执行过程等日志,不仅仅是为了诊断,还是为了安全和审计,对Java项目来说,空中编译和运行是很不让人放心的,留下这些依据可以更好地优化程序。