zoukankan      html  css  js  c++  java
  • Java 反射原理

    一、Java 反射的定义

    反射机制是在运行状态中,

    对于任意一个类, 都能够知道这个类的所有属性和方法;

    对于任意一个对象,都能够调用它的任意一个方法或者属性;

     

    二、反射提供的功能:

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

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

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

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

    生成动态代理

     

    三、获取反射对象(反射入口)

    Class.forName(全类名) 需要抛异常 throws ClassNotFoundException

    XX.class

    对象.getClass()

     

    package com.example.demo;
    
    public class TestJavaReflect {
    
        public static void main(String[] args) throws ClassNotFoundException {
    
                Class<?> class1 = null;
                Class<?> class2 = null;
                Class<?> class3 = null;
                // 一般采用这种形式
                class1 = Class.forName("com.example.demo.TestJavaReflect"); //通过全类名需要抛异常 throws ClassNotFoundException
                class2 = new TestJavaReflect().getClass();
                class3 = TestJavaReflect.class;
                System.out.println("类名称   " + class1.getName());
                System.out.println("类名称   " + class2.getName());
                System.out.println("类名称   " + class3.getName());
    
        }
    }
    

      四、接下来看几个例子

    package com.example.demo;
    
    import java.io.Serializable;
    
    public class TestJava implements Serializable {
    
        private static final long serialVersionUID = -2862585049955236662L;
    
        public static void main(String[] args) throws Exception {
            Class<?> clazz = Class.forName("com.example.demo.TestJava");
            // 取得父类
            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
    
        }
    }
    

      上面一个例子说明:所有类的父类是java.lang.object,查看一个类实现的接口,可以用clazz.getInterfaces()

     

    通过一个对象获得完整的包名和类名,所以诸如SpringBoot加载默认文件的时候,可以用三种形式,只是最简单的写法,用对象.class最方便,既没有抛啥异常,也没有什么大小写或者括号

    package com.example.demo;
    
    import org.springframework.boot.SpringApplication;
    
    public class DemoApplication {
    
        public static void main(String[] args) throws ClassNotFoundException {
    
            DemoApplication demoApplication = new DemoApplication();
            System.out.println(demoApplication.getClass());     //class com.example.demo.DemoApplication
            System.out.println(demoApplication.getClass().getName()); //com.example.demo.DemoApplication
            System.out.println(DemoApplication.class);          //class com.example.demo.DemoApplication
            System.out.println(Class.forName("com.example.demo.DemoApplication"));  //同上
            SpringApplication.run(DemoApplication.class, args);
            SpringApplication.run(new DemoApplication().getClass(), args);
            SpringApplication.run(Class.forName("com.example.demo.DemoApplication"), args);
        }
    
    }
    

      


    实例化Class类对象

    package com.example.demo;
    
    public class TestJavaReflect {
    
        public static void main(String[] args) throws ClassNotFoundException {
    
                Class<?> class1 = null;
                Class<?> class2 = null;
                Class<?> class3 = null;
                // 一般采用这种形式
                class1 = Class.forName("com.example.demo.TestJavaReflect"); //通过全类名需要抛异常 throws ClassNotFoundException
                class2 = new TestJavaReflect().getClass();
                class3 = TestJavaReflect.class;
                System.out.println("类名称   " + class1.getName());
                System.out.println("类名称   " + class2.getName());
                System.out.println("类名称   " + class3.getName());
    
        }
    }
    

      

     


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

    用Method[] methods = clazz.getMethods(); 

    for(Method method: methods){

    system.out.println(method);

    }

    获取所有的公共的方法(1. 本类以及父类、接口中的所有方法  2. 符合访问修饰符规律)

    用Filed[] fileds = clazz.getFields(); 获取所有的公共属性

    for(Field field: fields){

    System.out.println(field);

    }


    获取某个类中的全部构造函数 - 详见下例


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


    获取某个类的全部属性

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

      


    获取某个类的全部方法


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

    package com.example.demo;
    
    import java.lang.reflect.Method;
    
    public class TestReflect06 {
    
        public static void main(String[] args) throws Exception {
            Class<?> clazz = Class.forName("com.example.demo.TestReflect06");
            // 调用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);
        }
    }
    

      


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

    package com.example.demo;
    
    import java.lang.reflect.Field;
    
    public class TestReflect07 {
    
        private String filepath = null;
        public static void main(String[] args) throws Exception {
            Class<?> clazz = Class.forName("com.example.demo.TestReflect07");
            Object obj = clazz.newInstance();
            // 可以直接对 private 的属性赋值
            Field field = clazz.getDeclaredField("filepath");
            field.setAccessible(true);
            field.set(obj, "/JasmineQian/ccc/ddd/ccc");
            System.out.println(field.get(obj));
        }
    }
    

      


    反射机制的动态代理

    package com.example.demo;
    
    
    /**
     * 在java中有三种类类加载器。
     *
     * 1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
     *
     * 2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jrelibext目录中的类
     *
     * 3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
     *
     * 如果想要完成动态代理,首先需要定义一个InvocationHandler接口的子类,已完成代理的具体操作。
     *
     */
    
    public class TestReflect08 {
    
        public static void main(String[] args) throws Exception {
            MyInvocationHandler demo = new MyInvocationHandler();
            Subject sub = (Subject) demo.bind(new SubjectImpl());
            String info = sub.showinfo("Jasmine", 20);
            System.out.println(info);
        }
    }
    
    
    package com.example.demo;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public 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;
        }
    }
    
    
    package com.example.demo;
    
    public interface Subject {
    
        String showinfo(String name, int age);
    
    }
    
    
    package com.example.demo;
    
    public class SubjectImpl implements Subject {
    
        @Override
        public String showinfo(String name, int age) {
            return name + "  " + age;
        }
    
    
    }
    

      

    六、反射机制的应用实例
    在泛型为Integer的ArrayList中存放一个String类型的对象。

    package com.example.demo.demoSample;
    
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    
    public class RefectSample01 {
    
        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));
        }
    }
    

      


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

    package com.example.demo.demoSample;
    
    import java.lang.reflect.Array;
    
    public class RefectSample02 {
    
        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("修改之后数组第1个元素为: " + Array.get(temp, 0));
            System.out.println("修改之后数组第5个元素为: " + Array.get(temp, 4));
        }
    }
    

      


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

    package com.example.demo.demoSample;
    
    import java.lang.reflect.Array;
    
    public class ReflectSample03 {
    
        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();
        }
    }
    

      


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

    package com.example.demo.demoSample;
    
    
    /**
     * 对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。
     * 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 FactoryTestReflect {
    
        public static void main(String[] args) throws Exception {
            fruit f = Factory.getInstance("com.example.demo.demoSample.AppleFruit");
            if (f != null) {
                f.eat();
            }
        }
    }
    
    package com.example.demo.demoSample;
    
    public 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;
        }
    }
    
    
    package com.example.demo.demoSample;
    
    public interface fruit {
    
        void eat();
    }
    
    
    package com.example.demo.demoSample;
    
    public class AppleFruit implements fruit {
    
        @Override
        public void eat() {
            System.out.println("Apple");
        }
    }
    

      

  • 相关阅读:
    461. Hamming Distance
    342. Power of Four
    326. Power of Three
    368. Largest Divisible Subset java solutions
    95. Unique Binary Search Trees II java solutions
    303. Range Sum Query
    160. Intersection of Two Linked Lists java solutions
    88. Merge Sorted Array java solutions
    67. Add Binary java solutions
    14. Longest Common Prefix java solutions
  • 原文地址:https://www.cnblogs.com/qianjinyan/p/11003449.html
Copyright © 2011-2022 走看看