Java基本七:编译、加载


            我们知道,Java语言是基于Java虚拟机(JVM)中运行的。当我们编译Java类时,平台和机器把它 编译转化为字节码的形式,并将其保存为一个.class文件。之后当我们用这个类的时候,Java的类加载器(ClassLoader)就会把它加载到内存。

            有三种类型的Java内置ClassLoader.

            引导类加载器(Bootstrap Class Loader):用于加载jdk内部类。通常加载rt.jar和其它的核心类,例如java.lang.*包类。

            扩展类加载器(Extensions Class Loader):它加载来自于jdk扩展的目录的类,通常是$JAVA_HOME/lib/ext目录。

            系统类加载器(System Class Loader):它加载的当前类路径的类可以被设置的同时,调用 可以使用-cp或者-classpath,命令行选项的程序。

           ClassLoader是分级的,当一个类被加载,那么先找其父类加载器去加载,在运行时,保持这种方式,不断往上一级一级的加载,如果父类加载器没有找到类,再由加载器本身去加载类。

        

              那么输入以下ClassLoader:

                 System.out.println(String.class.getClassLoader());
             System.out.println(ZipInfo.class.getClassLoader());

              System.out.println(Connection.class.getClassLoader());   

              System.out.println(Gun.class.getClassLoader());

               输出为:

        null
       sun.misc.Launcher$ExtClassLoader@6fd7bd04
       sun.misc.Launcher$AppClassLoader@2ac510e3
       sun.misc.Launcher$AppClassLoader@2ac510e3

          其中Gun 类就是之前声明的枪类。

          可以看出引导类加载器加载的类在jdk的lib/目录下不包括ext目录部分,但是无法得到类加载器,这是为什么呢?不为什么,Java规定的。

          扩展类加载器就是加载jdk里面lib/ext目录下的。ExtClassLoader.

          系统类加载器就是加载我们自己创建的类和引入的三方的库类等。AppClassLoader.

          由此可以看出我们自己定义的所有类,引用的其它库类都是APPclassLoader来加载的。

          Java类的加载就知道了,那么类的静态加载和动态加载是怎么回事呢?

         Java类静态加载,就是我们通常的new 一个对象的时候,这个对象在编译的时候就已经被提供了。

         可以这么理解Java的动态加载。在Java运行时,类生成的对象,没有被预先提供。而去加载这个类。

        

         动态编译使用类文件

        ClassLoader加载的类是.class文件到内存。那么从.java文件编译到.class。我们怎么主动去做?把它编译成.class文件。Java给提供了一些方法。

         首先我们创建一个Dagger类:

       public class Dagger {

    
    private String name;
    private String address;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}


并放到D盘的sun目录下;

        

            JavaCompiler complier = ToolProvider.getSystemJavaCompiler();     
            StandardJavaFileManager filemanager =   
                    complier.getStandardFileManager(null, null, null);  
            Iterable it = filemanager.getJavaFileObjects("D:\\sun\\Dagger.java");    
            CompilationTask task = complier.getTask(null, filemanager, null, null, null, it);  
            task.call();  //
            try {
                filemanager.close();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } 
           执行完这些代码后,会发现多了Dagger.class:

     

          那么我们怎么使用这个Dagger.class呢?

          我们的.java类文件不是在项目的库中或者项目的java源目录中,而是在其它目录,其它地方。比如:项目在D盘创建,而我需要的另一个.java文件在C盘,那么怎么用C盘的这个类呢?

          由我们的类加载器完成:

          File file=new File("D:\\sun\\");
               
             URL url=null;
            try {
                url = file.toURL();
            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
             URLClassLoader urlClassLoader=new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader());
                Class<?> loadClass = urlClassLoader.loadClass("Dagger");
                Object newInstance = loadClass.newInstance();
                Field declaredField = loadClass.getDeclaredField("name");
                declaredField.setAccessible(true);
                declaredField.set(newInstance, "瑞士军刀");
                Method method = loadClass.getMethod("getName");
                Object invoke = method.invoke(newInstance);
                System.out.println(invoke);

             注意:里面的try cache太多,省略。

             输出为:

            瑞士军刀

             可以看出,我们可以直接调用来自于各个地方的.class类文件,包括从网络中获取的,也就是说,在更新程序的时候,只需要更新几个类的这种,就简单的多了,程序运行时就可以升级。不必重启程序。

             显然,我们需要的不仅仅是一个或者几个类 的改变。会是一个jar包的形式。

           动态加载jar包

            新建一个类:

            package com.test2.util;

public class Gun {
    private String name;
    private int bullet;
    private String address;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getBullet() {
        return bullet;
    }
    public void setBullet(int bullet) {
        this.bullet = bullet;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "{"+name+","+bullet+","+address+"}";
    }
    
}

        把这个包打成jar包的形式,暂且命名test2.jar.放到d盘的sun目录下。

        那么引用jar包

          
               File file=new File("D:\\sun\\test2.jar");
               
             URL url=null;
            try {
                url = file.toURL();
            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
             URLClassLoader urlClassLoader=new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader());
       
                Class<?> loadClass = urlClassLoader.loadClass("com.test2.util.Gun");
                
                Object newInstance = loadClass.newInstance();
                Field name = loadClass.getDeclaredField("name");
                Field bullet=loadClass.getDeclaredField("bullet");
                Field address=loadClass.getDeclaredField("address");
                name.setAccessible(true);
                bullet.setAccessible(true);
                address.setAccessible(true);
                name.set(newInstance, "手枪");
                bullet.set(newInstance, 20);
                address.set(newInstance, "美国");
                Method method = loadClass.getMethod("toString");
                Object invoke = method.invoke(newInstance);
                System.out.println(invoke);

             注意:里面的try cache太多,省略。

              执行这段代码输出:

               {手枪,20,美国}

             

                是不是很屌  ?其实这就是热部署基本的原理。在线更新程序,程序不需要重启。





      

         

         

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值