zoukankan      html  css  js  c++  java
  • java反射

    1.反射机制是什么

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

    2.反射机制能做什么

    • 在运行时判断任意一个对象所属的类;

    • 在运行时构造任意一个类的对象;

    • 在运行时判断任意一个类所具有的成员变量和方法;

    • 在运行时调用任意一个对象的方法;

    • 生成动态代理。

    3.反射机制的相关API

    通过一个对象获得完整的包名和类名

    //第一种,任何一个类都有一个隐含的静态成员变量class
    Class c1 = Person.class;
    
    //第二种,已经知道该类的对象,通过getClass()获得
    Class c2 = person.getClass();
    
    //第三种,Class类的forName()方法
    Class c3 = Class.forName("Person");
    public class TestReflect {
        public static void main(String[] args) throws Exception {
            Class<?> class1 = null;
            Class<?> class2 = null;
            Class<?> class3 = null;
            //获得类名 一般有三种方式
            class1 = Class.forName("com.yuan.TestReflect");
            class2 = new TestReflect().getClass();
            class3 = TestReflect.class;
            System.out.println("类名称   " + class1.getName());
            System.out.println("类名称   " + class2.getName());
            System.out.println("类名称   " + class3.getName());
        }
    }
    View Code

    获取一个对象的父类与实现的接口

    clazz.getSuperclass()

    clazz.getInterfaces()

    import java.io.Serializable;
    public class TestReflect implements Serializable {
        private static final long serialVersionUID = -2862585049955236662L;
        public static void main(String[] args) throws Exception {
            Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
            // 取得父类
            Class<?> parentClass = clazz.getSuperclass();
            System.out.println("clazz的父类为:" + parentClass.getName());
            // clazz的父类为: java.lang.Object
            // 获取所有的接口
            Class<?> intes[] = clazz.getInterfaces();
            System.out.println("clazz实现的接口有:");
            for (int i = 0; i < intes.length; i++) {
                System.out.println((i + 1) + ":" + intes[i].getName());
            }
            // clazz实现的接口有:
            // 1:java.io.Serializable
        }
    }
    View Code

    获取某个类中的全部构造函数

    A.1:获取构造方法的数组:

        public Constructor<?>[] getConstructors():获得所有公共构造方法

        public Constructor<?>[] getDeclaredConstructors():获得所有构造方法

    A.2:获取单个构造方法(用于非私有的构造方法)

        public Constructor<T> getConstructor(Class<?>... parameterTypes)

        参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象

    A.3:获取单个构造方法(用于私有的构造方法)

        public Constructor<T> getDeclaredtConstructor(Class<?>... parameterTypes)

    B.1:初始化构造方法的实例:(用于非私有的构造方法)

        public T newInstance(Object... initargs)  注意:T表示的是泛型,Object... initargs表示的是你要实例化的指定参数

        使用此 Constructor对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

    B.2:初始化构造方法的实例:(用于私有的构造方法)   

        先用: public void setAccessible(boolean flag):flag的值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。

        再用: public T newInstance(Object... initargs) 

    public Constructor<?>[] getConstructors():获得所有公共构造方法

    Constructor[] c1 = c.getConstructors();
    System.out.println(c1);// 返回的是地址值,证明得到的是个数组对象
         // 遍历这个数组
    for (Constructor ct : c1) {
        System.out.println(ct);
    }  //获得Person类中的所有公共构造方法
    View Code

    public Constructor<?>[] getDeclaredConstructors():获得所有构造方法

    Constructor[] c1 = c.getDeclaredConstructors();
        //遍历数组
    for(Constructor ct : c1){
        System.out.println(ct);
    } //获得Person类的所有构造方法
    View Code

    public Constructor<T> getConstructor(Class<?>... parameterTypes):获得指定的构造方法

    获得无参构造方法:

    Constructor c1 = c.getDeclaredConstructor();
    System.out.println(c1);
    View Code

    获得带参构造方法:

    //获取指定的构造方法,需要该方法的参数列表
    Constructor c1 = c.getDeclaredConstructor(String.class,int.class,String.class);
    System.out.println(c1);
    View Code

    通过反射机制实例化一个类的对象

    通过反射获取公共带参构造方法并使用:

    //获取指定的构造方法,需要该方法的参数列表
            Constructor c1 = c.getDeclaredConstructor(String.class,int.class,String.class);
            System.out.println(c1);
            
            // 通过带参构造方法对象创建对象
            // public T newInstance(Object... initargs):
            //有泛型,前面没使用泛型,先用Objcet
            Object b = c1.newInstance("张三",13,"家乡");
            System.out.println(b); //Person [name=张三, age=13, address=家乡]
    View Code

    通过反射获取私有带参构造方法并使用:

    //获取指定的私有构造方法
            Constructor c1 = c.getDeclaredConstructor(String.class);
            
            //public void setAccessible(boolean flag):
            //flag的值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
            c1.setAccessible(true);
            
            // public T newInstance(Object... initargs) 
            Object ob = c1.newInstance("张三");
            System.out.println(ob);
    View Code

    获取某个类的全部属性

    getDeclaredFields()获得某个类的所有申明的字段,即包括public、private和proteced,但是不包括父类的申明字段。

    getFields()获得某个类的所有的公共(public)的字段,包括父类。

    import java.io.Serializable;
    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;
    public class TestReflect implements Serializable {
        private static final long serialVersionUID = -2862585049955236662L;
        public static void main(String[] args) throws Exception {
            Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
            System.out.println("===============本类属性===============");
            // 取得本类的全部属性
            Field[] field = clazz.getDeclaredFields();
            for (int i = 0; i < field.length; i++) {
                // 权限修饰符
                int mo = field[i].getModifiers();
                String priv = Modifier.toString(mo);
                // 属性类型
                Class<?> type = field[i].getType();
                System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";");
            }
             
            System.out.println("==========实现的接口或者父类的属性==========");
            // 取得实现的接口或者父类的属性
            Field[] filed1 = clazz.getFields();
            for (int j = 0; j < filed1.length; j++) {
                // 权限修饰符
                int mo = filed1[j].getModifiers();
                String priv = Modifier.toString(mo);
                // 属性类型
                Class<?> type = filed1[j].getType();
                System.out.println(priv + " " + type.getName() + " " + filed1[j].getName() + ";");
            }
        }
    }
    View Code

    获取某个类的全部方法

    import java.io.Serializable;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    public class TestReflect implements Serializable {
        private static final long serialVersionUID = -2862585049955236662L;
        public static void main(String[] args) throws Exception {
            Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
            Method method[] = clazz.getMethods();
            for (int i = 0; i < method.length; ++i) {
                Class<?> returnType = method[i].getReturnType();
                Class<?> para[] = method[i].getParameterTypes();
                int temp = method[i].getModifiers();
                System.out.print(Modifier.toString(temp) + " ");
                System.out.print(returnType.getName() + "  ");
                System.out.print(method[i].getName() + " ");
                System.out.print("(");
                for (int j = 0; j < para.length; ++j) {
                    System.out.print(para[j].getName() + " " + "arg" + j);
                    if (j < para.length - 1) {
                        System.out.print(",");
                    }
                }
                Class<?> exce[] = method[i].getExceptionTypes();
                if (exce.length > 0) {
                    System.out.print(") throws ");
                    for (int k = 0; k < exce.length; ++k) {
                        System.out.print(exce[k].getName() + " ");
                        if (k < exce.length - 1) {
                            System.out.print(",");
                        }
                    }
                } else {
                    System.out.print(")");
                }
                System.out.println();
            }
        }
    }
    View Code

    通过反射机制调用某个类的方法

    打破封装 实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问

    由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的

    共有方法

    import java.lang.reflect.Method;
    public class TestReflect {
        public static void main(String[] args) throws Exception {
            Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
            // 调用TestReflect类中的reflect1方法
            Method method = clazz.getMethod("reflect1");
            method.invoke(clazz.newInstance());
            // Java 反射机制 - 调用某个类的方法1.
            // 调用TestReflect的reflect2方法
            method = clazz.getMethod("reflect2", int.class, String.class);
            method.invoke(clazz.newInstance(), 20, "张三");
            // Java 反射机制 - 调用某个类的方法2.
            // age -> 20. name -> 张三
        }
        public void reflect1() {
            System.out.println("Java 反射机制 - 调用某个类的方法1.");
        }
        public void reflect2(int age, String name) {
            System.out.println("Java 反射机制 - 调用某个类的方法2.");
            System.out.println("age -> " + age + ". name -> " + name);
        }
    }
    View Code

    私有方法

    Object person = class1.newInstance();
    Method setName = class1.getDeclaredMethod( "setName", String.class ) ;
    setName.setAccessible( true );
    setName.invoke( person , "jack" ) ;
    View Code

    通过反射机制操作某个类的属性

    import java.lang.reflect.Field;
    public class TestReflect {
        private String proprety = null;
        public static void main(String[] args) throws Exception {
            Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
            Object obj = clazz.newInstance();
            // 可以直接对 private 的属性赋值
            Field field = clazz.getDeclaredField("proprety");
            field.setAccessible(true);
            field.set(obj, "Java反射机制");
            System.out.println(field.get(obj));
        }
    }
    View Code

    4.综合训练

    反射操作属性和方法

    package com.app;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class T1 {
    
        public static void main(String[] args) {
    
            try {
                //创建类
                Class<?> class1 = Class.forName("com.app.Person");
    
                //创建实例
                Object person = class1.newInstance();
    
                //获得id 属性
                Field idField = class1.getDeclaredField( "id" ) ;
    
                //打破封装  实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问  
                //由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的  
                idField.setAccessible( true );
    
                //给id 属性赋值
                idField.set(  person , "100") ;
    
                //获取 setName() 方法
                Method setName = class1.getDeclaredMethod( "setName", String.class ) ;
                //打破封装 
                setName.setAccessible( true );
    
                //调用setName 方法。
                setName.invoke( person , "jack" ) ;
    
                //获取name 字段
                Field nameField = class1.getDeclaredField( "name" ) ;
                //打破封装 
                nameField.setAccessible( true );
    
                //打印 person 的 id 属性值
                String id_ = (String) idField.get( person ) ;
                System.out.println( "id: " + id_ );
    
                //打印 person 的 name 属性值
                String name_ = ( String)nameField.get( person ) ;
                System.out.println( "name: " + name_ );
                
                //获取 getName 方法
                Method getName = class1.getDeclaredMethod( "getName" ) ;
                //打破封装 
                getName.setAccessible( true );
                
                //执行getName方法,并且接收返回值
                String name_2 = (String) getName.invoke( person  ) ;
                System.out.println( "name2: " + name_2 );
    
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace() ;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    }
    View Code
    id: 100
    name: jack
    name2: jack
    运行结果

    静态属性、静态方法调用

    package com.app;
    
    public class Util {
    
        public static String name = "json" ;
    
        /**
         * 没有返回值,没有参数
         */
        public static void getTips(){
            System.out.println( "执行了---------1111");
        }
    
        /**
         * 有返回值,没有参数
         */
        public static String getTip(){
            System.out.println( "执行了---------2222");
            return "tip2" ;
        }
    
        /**
         * 没有返回值,有参数
         * @param name
         */
        public static void getTip( String name ){
            System.out.println( "执行了---------3333 参数: " + name );
        }
    
        /**
         * 有返回值,有参数
         * @param id
         * @return
         */
        public static String getTip( int id ){
            System.out.println( "执行了---------4444 参数: " + id );
            if ( id == 0 ){
                return "tip1 444 --1 " ;
            }else{
                return "tip1 444 --2" ;
            }
        }
    
    }
    View Code
    package com.app;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class T1 {
    
        public static void main(String[] args) {
    
            try {
                //创建类
                Class<?> class1 = Class.forName("com.app.Util");
    
                //获取 nameField 属性
                Field nameField = class1.getDeclaredField( "name" ) ;
                //获取 nameField 的值
                String name_ = (String) nameField.get( nameField ) ;
                //输出值
                System.out.println( name_ );
    
    
                //没有返回值,没有参数
                Method getTipMethod1 = class1.getDeclaredMethod( "getTips"  ) ; 
                getTipMethod1.invoke( null  ) ;
                
                //有返回值,没有参数
                Method getTipMethod2 = class1.getDeclaredMethod( "getTip"  ) ; 
                String result_2 = (String) getTipMethod2.invoke( null  ) ;
                System.out.println( "返回值: "+ result_2 );
                
                //没有返回值,有参数
                Method getTipMethod3 = class1.getDeclaredMethod( "getTip" , String.class  ) ; 
                String result_3 = (String) getTipMethod3.invoke( null , "第三个方法"  ) ;
                System.out.println( "返回值: "+ result_3 );
                
                //有返回值,有参数
                Method getTipMethod4 = class1.getDeclaredMethod( "getTip" , int.class ) ; 
                String result_4 = (String) getTipMethod4.invoke( null  , 1 ) ;
                System.out.println( "返回值: "+ result_4 );
                
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace() ;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    }
    View Code
    json
    执行了---------1111
    执行了---------2222
    返回值: tip2
    执行了---------3333 参数: 第三个方法
    返回值: null
    执行了---------4444 参数: 1
    返回值: tip1 444 --2
    运行结果

    注意:

    当参数是 int 类型 和 Integer 类型,反射获取方法不一样

    • 当参数是 int 类型时。获取方法的时候需要用:int.class。不能使用 Integer.class. 会报错。
    • 当参数是 Integer类型时。获取方法的时候需要用:Integer .class。不能使用 int.class. 会报错。

    在泛型为Integer的ArrayList中存放一个String类型的对象。

    import java.lang.reflect.Method;
    import java.util.ArrayList;
    public class TestReflect {
        public static void main(String[] args) throws Exception {
            ArrayList<Integer> list = new ArrayList<Integer>();
            Method method = list.getClass().getMethod("add", Object.class);
            method.invoke(list, "Java反射机制实例。");
            System.out.println(list.get(0));
        }
    }
    View Code

    通过反射取得并修改数组的信息

    import java.lang.reflect.Array;
    public class TestReflect {
        public static void main(String[] args) throws Exception {
            int[] temp = { 1, 2, 3, 4, 5 };
            Class<?> demo = temp.getClass().getComponentType();
            System.out.println("数组类型: " + demo.getName());
            System.out.println("数组长度  " + Array.getLength(temp));
            System.out.println("数组的第一个元素: " + Array.get(temp, 0));
            Array.set(temp, 0, 100);
            System.out.println("修改之后数组第一个元素为: " + Array.get(temp, 0));
        }
    }
    View Code

    通过反射机制修改数组的大小

    import java.lang.reflect.Array;
    public class TestReflect {
        public static void main(String[] args) throws Exception {
            int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            int[] newTemp = (int[]) arrayInc(temp, 15);
            print(newTemp);
            String[] atr = { "a", "b", "c" };
            String[] str1 = (String[]) arrayInc(atr, 8);
            print(str1);
        }
        // 修改数组大小
        public static Object arrayInc(Object obj, int len) {
            Class<?> arr = obj.getClass().getComponentType();
            Object newArr = Array.newInstance(arr, len);
            int co = Array.getLength(obj);
            System.arraycopy(obj, 0, newArr, 0, co);
            return newArr;
        }
        // 打印
        public static void print(Object obj) {
            Class<?> c = obj.getClass();
            if (!c.isArray()) {
                return;
            }
            System.out.println("数组长度为: " + Array.getLength(obj));
            for (int i = 0; i < Array.getLength(obj); i++) {
                System.out.print(Array.get(obj, i) + " ");
            }
            System.out.println();
        }
    }
    View Code

    反射机制的动态代理

    // 获取类加载器的方法
    TestReflect testReflect = new TestReflect();
            System.out.println("类加载器  " + testReflect.getClass().getClassLoader().getClass().getName());
    package net.xsoftlab.baike;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    //定义项目接口
    interface Subject {
        public String say(String name, int age);
    }
    // 定义真实项目
    class RealSubject implements Subject {
        public String say(String name, int age) {
            return name + "  " + age;
        }
    }
    class MyInvocationHandler implements InvocationHandler {
        private Object obj = null;
        public Object bind(Object obj) {
            this.obj = obj;
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object temp = method.invoke(this.obj, args);
            return temp;
        }
    }
    /**
     * 在java中有三种类类加载器。
     * 
     * 1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
     * 
     * 2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jrelibext目录中的类
     * 
     * 3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
     * 
     * 如果想要完成动态代理,首先需要定义一个InvocationHandler接口的子类,已完成代理的具体操作。
     * 
     * @author xsoftlab.net
     * 
     */
    public class TestReflect {
        public static void main(String[] args) throws Exception {
            MyInvocationHandler demo = new MyInvocationHandler();
            Subject sub = (Subject) demo.bind(new RealSubject());
            String info = sub.say("Rollen", 20);
            System.out.println(info);
        }
    }
    View Code

    将反射机制应用于工厂模式

    interface fruit {
        public abstract void eat();
    }
    class Apple implements fruit {
        public void eat() {
            System.out.println("Apple");
        }
    }
    class Orange implements fruit {
        public void eat() {
            System.out.println("Orange");
        }
    }
    class Factory {
        public static fruit getInstance(String ClassName) {
            fruit f = null;
            try {
                f = (fruit) Class.forName(ClassName).newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return f;
        }
    }
    /**
     * 对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。
     * Java 工厂模式可以参考
     * http://baike.xsoftlab.net/view/java-factory-pattern
     * 
     * 现在我们利用反射机制实现工厂模式,可以在不修改工厂类的情况下添加任意多个子类。
     * 
     * 但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成。
     * 
     * java 读取 properties 配置文件 的方法可以参考
     * http://baike.xsoftlab.net/view/java-read-the-properties-configuration-file
     * 
     * @author xsoftlab.net
     */
    public class TestReflect {
        public static void main(String[] args) throws Exception {
            fruit f = Factory.getInstance("net.xsoftlab.baike.Apple");
            if (f != null) {
                f.eat();
            }
        }
    }
    View Code

    总结

    • Class类提供了四个public方法,用于获取某个类的构造方法。
    Constructor getConstructor(Class[] params)     根据构造函数的参数,返回一个具体的具有public属性的构造函数
    Constructor getConstructors()     返回所有具有public属性的构造函数数组
    Constructor getDeclaredConstructor(Class[] params)     根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
    Constructor getDeclaredConstructors()    返回该类中所有的构造函数数组(不分public和非public属性)
    • 四种获取成员方法的方法
    Method getMethod(String name, Class[] params)    根据方法名和参数,返回一个具体的具有public属性的方法
    Method[] getMethods()    返回所有具有public属性的方法数组
    Method getDeclaredMethod(String name, Class[] params)    根据方法名和参数,返回一个具体的方法(不分public和非public属性)
    Method[] getDeclaredMethods()    返回该类中的所有的方法数组(不分public和非public属性)
    • 四种获取成员属性的方法
    Field getField(String name)    根据变量名,返回一个具体的具有public属性的成员变量
    Field[] getFields()    返回具有public属性的成员变量的数组
    Field getDeclaredField(String name)    根据变量名,返回一个成员变量(不分public和非public属性)
    Field[] getDelcaredField()    返回所有成员变量组成的数组(不分public和非public属性)

    反射用途https://www.cnblogs.com/panxuejun/p/5874990.html

    参考https://www.cnblogs.com/LZL-student/p/5965991.html

    https://www.cnblogs.com/lzq198754/p/5780331.html

    http://www.cnblogs.com/zhaoyanjun/p/6074887.html

  • 相关阅读:
    html5实现全屏的api方法
    用html5(requestFullscreen) js实现点击一个按钮使浏览器全屏效果
    HTML5 中fullscreen 中的几个API和fullscreen欺骗
    杭州收集
    JS DOM -- 关于回车键盘事件执行事件
    AngularCSS 的引入: CSS On-Demand for AngularJS
    用Javascript获取页面元素的位置
    angularjs 可以加入html标签方法------ng-bind-html的用法总结(2)
    angularjs 可以加入html标签方法------ng-bind-html的用法总结(1)
    onkeypress、onkeydown、onkeyup
  • 原文地址:https://www.cnblogs.com/yanmingyuan/p/10574167.html
Copyright © 2011-2022 走看看