zoukankan      html  css  js  c++  java
  • Java反射机制

    一切的操作都将使用Object完成,类,数组的引用都可以使用Object来接收

    1,认识Class类

    以前要是想知道一个类中的属性,成员变量,方法等等的信息的话,需要通过new这个类的对象才能得到这些信息,但是在Java中,也允许一个实例化对象找到一个类的完整的信息,这个类就是Class类,Class类是继承Object类的.

    正常方式:  引入需要的"包.类"名称 -->通过new实例化-->取得实例化对象

    反射方式:  实例化对象-->getClass()方法(从Object类中继承而来),这里是得到Class类对象-->得到完整的"包.类"名称

    getClass()这个方法的返回值是Class类,实际上该类是Java反射的源头

    Class类中没有定义构造方法,那么得到Class类对象的方法有3种:

    (1)使用forName()的静态方法实例化对象  (2) 类.class  (3)对象.getClass()

    package cn.reflect;
    
    class Demo{
        
    }
    public class RefDemo01 {
        public static void main(String[] args) throws ClassNotFoundException {
            Class c1 = Class.forName("cn.reflect.Demo");
            Class c2 = Demo.class;
            Class c3 = new Demo().getClass();
            System.out.println("类名称:"+c1.getName());
            System.out.println("类名称:"+c2);
            System.out.println("类名称:"+c3);
        }
    }

    3种实例化Class对象的方式是一样的,但是forName()这个方式比较常用,它只需要传入类所在的位置的名称的字符串参数就可以了,这样使程序具有很大的灵活性.

    2,Class类的使用

    2.1通过无参构造函数实例化对象

    Class类在开发中最常用的是,利用forName这个方法,传入完整的包.类的路径的字符串形式,来实例化一个类的对象

    想要通过Class本身来实例化其他类的对象,则可以使用Class类中的newInstance()方法来完成,但是要保证被实例化的类中存在这一个无参的构造方法

    (1)forName传入需要实例化的类的完整包.类名称

    (2)使用newInstance()方法,是实例化需要被实例化的对象,需要强转

    package cn.reflect;
    
    class Person{
        private String name;
        private int age;
        public Person(){
            
        }//被实例化的对象必须存在无参构造方法
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        
    }
    public class RefInstance {
    
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
            Class clazz = Class.forName("cn.reflect.Person");
            Person p = (Person)clazz.newInstance();
            p.setName("wjd");
            p.setAge(25);
            System.out.println("name:"+p.getName()+"  age:"+p.getAge());//name:wjd  age:25
        }
    
    }

    上述代码中,Person类并没有new一个新的对象,而是通过Class类,来完成了对Person类中的属性的访问.一般做在使用Class类来实例化对象时,一定要在类中编写无参的构造方法

    2.2调用有参构造实例化对象

    (1)通过Class类中的getConstructors()取得本类中的所有的构造方法

    (2)向构造方法中传递一个对象数组中去,里面包含了构造方法是所需的各个参数

    (3)通过Constructor实例化对象

    package cn.reflect;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    class Worker{
        private String name;
        private int age;
        public Worker(String name,int age){
            this.setName(name);
            this.setAge(age);
        }
        public void setName(String name){
            this.name = name;
        }
        public String getName(){
            return name;
        }
        
        public void setAge(int age){
            this.age = age;
        }
        public int getAge(){
            return age;
        }
    }
    public class InstanceDemo01 {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            Class c = Class.forName("cn.reflect.Worker");
            Constructor[] con = c.getConstructors();
            Worker worker = (Worker)con[0].newInstance("吴杰栋",25);//Worker中构造方法中有参数的,这里实例化的时候传参数,注意顺序,只有一个构造函数,所以数组中的0就可以
            System.out.println("age:"+worker.getAge() +" name:"+ worker.getName());
        }
    
    }

    在上述代码中,newInstance()方法是Constructor类中的, 查看API可以发现这个方法中传入的参数都是对象,25其实是Integer对象(在使用是进行了自动拆箱)

    在java类中是有可能存在多个构造函数的,它们只是构造函数中的参数类型不同而已,在API中,查阅到Class类中newIntance()这个类只能获取到带有无参构造函数的对象,那么带有参数的构造函数的时候,我们该如何获取该类的对象呢?

    (1),构造函数没有参数的时候,产生该类对象,直接通过Class类中的newIntance()来获取

    (2).构造函数有参数的时候,通过Class类中getConstructor()方法,在方法中传入构造函数参数类型的class文件,返回值是一个Constructor对象,查阅API发现在Constructor这个类中,也有一个带这参数类型的newIntance的方法,这样就能获取到带有参数类型的构造函数的类的对象了

    package cn.reflect;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    //拿Person类中的构造函数
    public class ReflectDemo2 {
        public static void main(String[] args) throws Exception {
            //createNewObject();
            createNewObject_2();
        }
        
        public static void createNewObject_2() throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            String name = "cn.bean.Person";
            Class<?> clazz = Class.forName(name);
            //获取到了Person中带有参数类型的构造方法
            Constructor<?> con = clazz.getConstructor(int.class,String.class);
            //这里通过constructor类中的方法来初始化类
            Object obj = con.newInstance(23,"wjd");
        }
    
        public static void createNewObject() throws Exception{
            String name = "cn.bean.Person";
            //找寻该文件的类文件并加载进内存,并产生class对象
            Class<?> clazz = Class.forName(name);
            //产生该类的对象
            Object obj = clazz.newInstance();
        }
    }

     

    3,反射的应用---得到类的属性

    在反射中得到一个类的完成结构的话,使用到java.lang.reflect包中的以下几个类

    (1) Constructor:表示类中的构造方法 (2)Field:表示类中的属性  (3)Method:表示类中的方法

    Constructor类中常用的方法

    序号

    方法

    描述

    1

    public int getModifiers() 得到构造函数的修饰符

    2

    public String getName() 得到构造函数的名称

    3

    public Class<?>[] getParameterTypes() 得到构造函数中的参数类型

    4

    public String toString() 返回此构造函数的信息

    5

    public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException 向构造函数中传入参数,实例化对象,就是需要拿到有参数构造函数的类的对象的时候,就需要用到这个

    Field类中常用的方法

    序号

    方法

    描述

    1 public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException 得到一个对象中属性的内容
    2 public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException 设置指定对象中属性的具体内容
    3 public int getModifiers() 得到属性的修饰符
    4 public String getName() 返回此属性的名称
    5 public String toString() 返回此Field类的信息
    6 public void setAccessible(boolean flag) throws SecurityException 设置一个属性是否被外部访问(需要访问私有属性需要用这个方法true一下)暴力访问
    7 public static void setAccessible(AccessibleObject[] array, boolean flag) throws SecurityException 设置一组属性是否被外部访问

    Method类中常用的方法

    序号 方法 描述
    1 public int getModifiers() 得到方法的访问修饰符
    2 public String getName() 得到方法的名称
    3 public Class<?>[] getParameterTypes() 得到方法的全部参数类型
    4 public Class<?> getReturnType() 得到方法的全部返回值类型
    5 public Class<?>[] getExceptionTypes() 得到一个方法全部抛出的类型
    6 public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException 当通过反射得到类的对象,类的方法的名称,方法中的参数的时候,用这个方法,我们就能完美的运行该类

    这三个类都是AccessibleObject类的子类

    以上三个类基本覆盖了反射所需要用到的类包括类中的方法,必须要熟练记忆

    在反射操作中,得到一个类中的全部属性,有两种不同的操作

    (1)得到实现的接口或者父类中的公共属性     在Class类中的getFields()方法

    (2)得到本类中的所有属性,包括私有属性      在Class类中的getDeclaredFields()

    根据以前学过的知识,要想操作一个类中的属性,肯定得要先得到一个类的对象,那么在反射中,也不例外,也需要通过newIntance()来得到一个类的对象,这样才能去取得类中的属性

    总之你想得到我的属性,必须给我对象

    package cn.reflect;
    
    import java.lang.reflect.Field;
    
    //拿Person类中的字段
    public class ReflectDemo3 {
    
        /**
         * @param args
         * @throws Exception 
         */
        public static void main(String[] args) throws Exception {
            getField();
        }
    //拿到字节码文件的中的字段
        public static void getField() throws Exception {
            Class<?> clazz = Class.forName("cn.bean.Person");
            //拿到Person类中的age对象,返回的是一个Field的对象
        //Field f = clazz.getField("age");//只能获取共有的的
            Field field = clazz.getDeclaredField("age");//可以获取本类.包含私有
            //对私有的属性,取消其权限的检查,暴力访问
            field.setAccessible(true);
            //设置属性的时候,必须要用对象来设置属性,所以必须先弄个对象出来
            Object obj = clazz.newInstance();
            field.set(obj, 23);
            Object o = field.get(obj);
            System.out.println(o);
        }
    
    }

     

    4,反射的应用---得到类中的方法

    得到类中的方法,是使用Class类中的getMethod()方法,此方法返回的是一个Method类的对象数组

    需要运行一个类

    (1)需要先得到该类的方法,方法中的参数的class对象

    (2)然后得到该类的对象

    (3)调用Method中的invoke方法,将对象,和方法的参数传入该方法

    package cn.reflect;
    
    import java.lang.reflect.Method;
    
    //获取Person类中的方法
    public class ReflectDemo4 {
        public static void main(String[] args) throws Exception {
        //    getMethod();
            getMethod_2();
        }
    
        public static void getMethod_2() throws Exception {
            Class<?> clazz = Class.forName("cn.bean.Person");
            Method method = clazz.getMethod("paramMethod", String.class,int.class);
            //方法要运行起来,也需要对象的支持,所以要造个对象出来
            Object obj = clazz.newInstance();
            //Method中有个方法,能传入对象和方法,这样的话,方法就能完美的运行起来了
            method.invoke(obj, "wjd",23);
        }
    
        public static void getMethod() throws Exception {
            Class<?> clazz = Class.forName("cn.bean.Person");
            Method[] methods = clazz.getMethods();//这个获取的都是共有的方法
            methods = clazz.getDeclaredMethods();//只能获取本类中的方法,但是包含了私有的方法
            for(Method method : methods){
                System.out.println(method);
            }
        }
    }

    5,反射的应用---得到类中的的字段(暴力访问的话,可以得到类中的私有字段)

    package cn.reflect;
    
    import java.lang.reflect.Field;
    
    //拿Person类中的字段
    public class ReflectDemo3 {
    
        /**
         * @param args
         * @throws Exception 
         */
        public static void main(String[] args) throws Exception {
            getField();
        }
    //拿到字节码文件的中的字段
        public static void getField() throws Exception {
            Class<?> clazz = Class.forName("cn.bean.Person");
            //拿到Person类中的age对象,返回的是一个Field的对象
        //Field f = clazz.getField("age");//只能获取共有的的
            Field field = clazz.getDeclaredField("age");//可以获取本类.包含私有
            //对私有的属性,取消其权限的检查,暴力访问
            field.setAccessible(true);
            //设置属性的时候,必须要用对象来设置属性,所以必须先弄个对象出来
            Object obj = clazz.newInstance();
            field.set(obj, 23);
            Object o = field.get(obj);
            System.out.println(o);
        }
    
    }
  • 相关阅读:
    "INVALID" is not a valid start token
    Win+R 快速启动程序
    assert False 与 try 结合 在开发中的使用
    token的分层图如下
    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError
    获取控制台的错误信息 onerror
    状态git
    icmp
    git commit前检测husky与pre-commit 提交钩子
    git diff
  • 原文地址:https://www.cnblogs.com/driverwjd/p/3890862.html
Copyright © 2011-2022 走看看