JAVA反射机制

Java动态加载机制

动态加载就是需要某个类的时候才对其进行加载,而不是对所有的类加载完成后才开始执行main方法。(需要看虚拟机详细的加载输入,需要对虚拟机输入 -verbose:class 参数)

public class Test{
    public static void main(String[] args){
        new A();//看到A才加载A,此时B还没有被加载
        System.out.println("=======");//横线会被打印在加载A和加载B之间   
        new B();//看到B才加载B
 
        new C();
        new C();//new了两次C却只有一次输出
 
        new D();
        new D();//new两次D就两次输出
    }
}
 
 
class A{
}
 
class B{
}
 
class C{
    static{//在加载完成后被调用,且只调用一次
        System.out.println("静态语句块,不管new多少个对象只会执行一次");
    }
}
 
class D{
    {//动态语句块,和构造方法差不多,用得少
        System.out.println("没每new一个对象执行一次");
    }
}

JDK中的ClassLoader

包含四个ClassLoader,bootstrap class loader是用本地语言写的,别的class loader用java写的。

bootstrap class loader加载别的class loader,然后别的class loader被加载完成之后,再去加载另外的class。

在java中,每个class都有一个Class对象,当编译完一个.class文件后,就会产生一个Class对象,用以描述这个类的类型信息

package com.scriptwang;
public class Test{
    public static void main(String[] args) throws Exception{
        //三种方法可以拿到Test类的Class对象
        Class a = new com.scriptwang.Test().getClass();//getClass继承自Object
        Class b = com.scriptwang.Test.class;//个人理解相当于每个类都有一个Class class的成员变量,是public的,通过类名.属性名可以拿到该成员变量。
        Class c = Class.forName("com.scriptwang.Test");//通过Class类的forName("完整的类名")
 
 
        //拿到加载Test类的CLassLoader的名字
        /*
            Class c = Test.class;//返回Test类的Class对象
            ClassLoader cl = c.getClassLoader();//返回ClassLoader实例
            Class d = cl.getClass();//拿到ClassLoader类的Class对象
            (ClassLoader本身也是一个java类,getClass方法继承自Object类)
            String name = d.getName();//拿到Class对象返回的类的名字
        */
        System.out.println(Test.class.getClassLoader().
                getClass().getName());
 
    }
}

ClassLoader的层次关系(不是继承,继承是从类的角度说的,层次是对象和对象之间的关系)

application class loader对象里面有一个引用,叫做Parent,指向extension class loader,同理,extension class loader有一个Parent指向bootstrap class loader(可以这么认为,因为bootstrap class loader是用本地的语言写的,不能被指向)

通过getParent方法可以拿到上一个层次的class loader,因此可以用循环遍历class loader对象。不是类继承,不是类继承,不是类继承,重要的事情说三遍。

设置这样的层次关系的目的:当加载一个类的时候,会通过检查(一直检查到root class loader)上一层次的Class Loader是否加载,如果上一个层次加载过了,那么当前的Class Loader就不会加载这个类。比如:自己写的java.lang.String永远不会被加载!这样可以保证安全性。

public class Test{
    public static void main(String[] args){
        //拿到加载Test类的CLassLoader
        ClassLoader c = Test.class.getClassLoader();
         
        //通过循环遍历ClassLoader
        while (c != null ){
            System.out.println(c.getClass().getName());
            c = c.getParent();//拿到上一层次的ClasLoader
        }
    }
}

反射机制

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

JAVA反射(放射)机制:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods

import java.lang.reflect.Method;
 
public class Test{
    public static void main(String[] args) throws Exception{
        String className = "Test";
        Class c = Class.forName(className);//通过class名拿到Class对象
        Object instance = c.newInstance();//通过Class对象new这个类的对象
 
        Method[] methods = c.getMethods();//通过Class对象获得Method对象
        for (int i=0;i<methods.length;++i){
            if (methods[i].getName() == "m"){
                methods[i].invoke(instance,12);
            }
        }
    }    
    public void m(int i){
        System.out.println(i);
    }
}
 
/*
forName会抛出ClassNotFoundException
newInstance会抛出InstantiationException、IllegalAccessException
invoke会抛出IllegalAccessException、InvocationTargetException
*/

比如这样一个例子,有一个接口叫做Animal,它有方法enjoy、eat、sleep,所有实现了这个接口的具体类(比如Cat、Dog)都拥有这些方法,因此,在通过反射机制new具体对象的时候,反射机制的代码并不用改变(因为具体的类的方法都是依据接口固定化了的)。

public interface Animal {//定义一个接口
   public void enjoy(String thing);
   public void eat();
}

public class Dog implements Animal{//实现了Animal接口的Dog
   public void enjoy(String thing){
       System.out.println("Dog is enjoy " + thing);
   }
   public void eat(){
       System.out.println("Dog is eatting");
   }
}

public class Cat implements Animal {//实现了Animal借口的Cat
   public void enjoy(String thing){
       System.out.println("Cat is enjoy "+ thing);
   }
   public void eat(){
       System.out.println("Cat is eatting");
   }
}

import java.lang.reflect.Method;//引入相关的类
public class UserClass {
   public static void main(String[] args) throws Exception{
      //不管从配置文件获得的类名是Cat还是Dog都可以传进来,只要实现了Animal接口的具体类都可以传入,以下的代码照样适用,因为它们都实现了Animal接口(里面的方法申明都是一样的,只不过每个类具体的实现方式不一样而已,这样就实现了很好的扩展性)
       String className = "Cat";
       Class c =  Class.forName(className);
       Object o = c.newInstance();
       Method[] methods = c.getMethods();
       for (int i=0;i<methods.length;++i){
           if (methods[i].getName() == "enjoy"){
               methods[i].invoke(o,"playing");
           }
           if (methods[i].getName() == "eat"){
               methods[i].invoke(o);
           }
       }

   }
}

反射机制的一些相关应用(创建对象、访问Field、调用方法、重写toString)

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
  
/**
 * Created by Script Wang on 2016/11/27.
 */
  
class Student {
    private String name;
    private int age;
    Student (){
        name = "";
        age = 0;
    }
    Student (String name,int age){
        this.name = name;
        this.age = age;
    }
  
    public void m(){
        System.out.println("I am invoked ===== !!!");
    }
  
  
    /**
     *
     * @return
     * 利用反射重写toString
     */
    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder();
  
        Field[] fields = this.getClass().getDeclaredFields();//拿到所有成员变量
  
        //循环成员变量,将变量名值添加到sb中
        for (int i=0;i<fields.length;i++){
            sb.append(fields[i].getName());//拿到当前Field的名字
            sb.append(" = ");//添加等号
            try{
                sb.append(fields[i].get(this));//拿到当前Field的值
            }catch (IllegalAccessException e){
                e.printStackTrace();
            }
  
            //如果遇到最后一个变量,则末尾不添加分号
            if (i != fields.length - 1){
                sb.append(" ; ");
            }
        }
  
        //返回sb的toString
        return sb.toString();
    }
  
}
  
public class TestReflect {
    public static void main(String[] args){
        test1();//打印示例:name = SW ; age = 21
        test2();//打印示例:name = SW ; age = 22
        test3();//打印示例:I am invoked ===== !!!
    }
  
    /**
     * 用反射中的Constructor构建对象
     */
    public static void test1(){
        Class clazz = null;
        Constructor con = null;
        Student s = null;
        try{
            clazz = Class.forName("Student");//抛ClassNotFoundException
            con = clazz.getDeclaredConstructor(String.class,int.class);//抛NoSuchMethodException
            s = (Student) con.newInstance("SW",21);//抛剩下的三个Exception
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }catch (NoSuchMethodException e){
            e.printStackTrace();
        }catch (InstantiationException e){
            e.printStackTrace();
        }catch (IllegalAccessException e){
            e.printStackTrace();
        }catch (InvocationTargetException e){
            e.printStackTrace();
        }
  
        System.out.println(s);
    }
  
  
  
    /**
     * 用反射为成员变量赋值构建对象(需要被构建对象的类有空的构造方法)
     * 需要知道构造函数需要哪些类型的参数
     *
     */
    public static void test2(){
        Class clazz = null;
        Object obj = null;
        Field[] fields = null;
  
        try{
            clazz = Class.forName("Student");//抛ClassNotFoundException
            obj = clazz.newInstance();//抛剩下的两个Exception
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }catch (InstantiationException e){
            e.printStackTrace();
        }catch (IllegalAccessException e){
            e.printStackTrace();
        }
  
        //拿到所有Field并循环
        fields = clazz.getDeclaredFields();
        for (Field f : fields){
            f.setAccessible(true);//这句相当重要,可以访问private的变量
            try {
                //get方法会抛IllegalAccessException
                if (f.get(obj) instanceof String){
                    f.set(obj,"SW");//为当前obj 赋值
                } else if (f.get(obj) instanceof Integer){
                    f.set(obj,22);
                }
            }catch (IllegalAccessException e){
                e.printStackTrace();
            }
        }
  
        System.out.println( (Student)obj );
  
    }
  
  
    /**
     * 利用反射调用方法
     */
    public static void test3(){
        Class clazz = null;
        Object obj = null;
        Method[] methods = null;
  
        try{
            clazz = Class.forName("Student");//抛ClassNotFoundException
            obj = clazz.newInstance();//抛剩下的两个Exception
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }catch (InstantiationException e){
            e.printStackTrace();
        }catch (IllegalAccessException e){
            e.printStackTrace();
        }
  
        //拿到所有的Method对象并循环
        methods =clazz.getDeclaredMethods();
        for (Method m : methods){
            if (m.getName().equals("m")){
                m.setAccessible(true);
                try {
                    m.invoke(obj);//调用obj的方法,抛出以下两个Exception
                }catch (IllegalAccessException e){
                    e.printStackTrace();
                } catch (InvocationTargetException e){
                    e.printStackTrace();
                }
  
            }
        }
  
    }
  
  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码狂魔v

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值