zoukankan      html  css  js  c++  java
  • Java学习笔记54(反射详解)

    反射概念:

    java反射机制是在运行状态中,对于任意一个类,都能知道所有属性和方法

    对于任意一个对象都能调用它的任意一个方法和属性,这种动态获取和调用的功能称为java的反射机制

    实际作用:

    已经完成一个java程序,但是想再添加新功能,又不能修改源码,这时候就用到反射机制了

    获取class文件的三种方式:

    简单地自定义一个Person类:

    package demo;
    
    public class Person {
        public String name;
        private int age;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        private Person(int age, String name) {
            this.name = name;
            this.age = age;
        }
    
        public void eat() {
            System.out.println("人吃饭");
        }
    
        public void sleep(String s, int a, double d) {
            System.out.println("人在睡觉" + s + "....." + a + "....." + d);
        }
    
        private void playGame() {
            System.out.println("人在打游戏");
        }
    
        public String toString() {
            return "Person [name=" + name + ", age=" + age + "]";
        }
    
        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;
        }
    
    }
    View Code

    获取person类class文件(三种方法本质上获取的是同一个对象):

    package demo;
    
    public class ReflectDemo {
        public static void main(String[] args) {
            //1.对象获取
            Person p = new Person();
            Class c = p.getClass();
            System.out.println(c);
            //2.类名获取
            Class c1 = Person.class;
            System.out.println(c1);
            
            System.out.println(c==c1);//true
            System.out.println(c.equals(c1));//true
            //只存在一个class文件,两种方式都是获得同一个对象
            
            //3.Class类的静态方法,参数注意带着包名防止重名
            try {
                Class c2 =     Class.forName("demo.Person");
                System.out.println(c2);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    反射获取空参构造方法:

    package demo;
    
    import java.lang.reflect.Constructor;
    
    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException {
            Class c = Class.forName("demo.Person");
            function1(c);
            function2(c);
        }
    
        private static void function1(Class c) {
            // 获得所有公共权限(public)的构造器
            Constructor[] cons = c.getConstructors();
            for (Constructor con : cons) {
                System.out.println(con);
                // 输出:
                // public demo.Person(java.lang.String,int)
                // public demo.Person()
            }
        }
    
        public static void function2(Class c) {
            // 获取空参构造器并执行(toString方法)
            try {
                Constructor con = c.getConstructor();
                Object obj = con.newInstance();
                System.out.println(obj);
                // 输出:Person [name=null, age=0]
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    反射获取有参构造方法:

    package demo;
    
    import java.lang.reflect.Constructor;
    
    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            Class c = Class.forName("demo.Person");
            Constructor con = c.getConstructor(String.class, int.class);
            System.out.println(con);
            Object object = con.newInstance("张三", 20);
            System.out.println(object);
        }
    }
    /*输出:
    public demo.Person(java.lang.String,int)
    Person [name=张三, age=20]
    */

    发现上边的方式代码量偏大,快捷一些的方式:

    前提:被反射的类,必须具有空参构造方法,且构造方法权限必须是public

    package demo;
    
    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            Class c = Class.forName("demo.Person");
            // Class类中定义方法, T newInstance() 直接创建被反射类的对象实例
            Object obj = c.newInstance();
            System.out.println(obj);
        }
    }

    反射获取私有构造方法(日常开发不建议使用,这种方法了解即可):

    package demo;
    
    import java.lang.reflect.Constructor;
    
    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            Class c = Class.forName("demo.Person");
            // 获取所有构造方法(包括私有的)
            // Constructor[] cons = c.getDeclaredConstructors();
            Constructor con = c.getDeclaredConstructor(int.class, String.class);
            con.setAccessible(true);// 取消权限,破坏了封装性
            Object object = con.newInstance(18, "张三");
            System.out.println(object);
            // Person [name=张三, age=18]
        }
    }

    反射获取类的成员变量并修改:

    package demo;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    
    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            Class c = Class.forName("demo.Person");
    
            // 获得所有public成员
            Field[] fields = c.getFields();
            // 获得所有成员变量
            Field[] fields2 = c.getDeclaredFields();
            for (Field field : fields2) {
                System.out.println(field);
            }
    
            // 获取指定成员变量并修改
            Field field = c.getField("name");
            Object object = c.newInstance();
            field.set(object, "张三");
            System.out.println(object);
            // 输出:Person [name=张三, age=0]
        }
    }

    反射获取成员方法并执行:

    package demo;
    
    import java.lang.reflect.Method;
    
    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            Class c1 = Class.forName("demo.Person");
            // 获得所有public方法
            /*
             * Method[] methods = c1.getMethods(); 
             * for(Method method:methods){
             * System.out.println(method); }
             */
    
            // 获取指定方法运行(空参)
            Method method = c1.getMethod("eat");
            Object obj = c1.newInstance();
            method.invoke(obj);
            // 输出:人吃饭
    
            // 有参
            Method method1 = c1.getMethod("sleep", String.class, int.class, double.class);
            Object obj1 = c1.newInstance();
            method1.invoke(obj1, "休息", 10, 10.11);
            // 输出:人在睡觉休息.....10.....10.11
    
            // 可以利用前面提到的方法暴力运行私有方法
        }
    }

    反射的泛型擦除:

    package demo;
    
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    
    public class ReflectTest {
        public static void main(String[] args) throws Exception {
            ArrayList<String> array = new ArrayList<String>();
            // 通常添加方式
            array.add("a");
            array.add("1");
    
            Class class1 = array.getClass();
            Method method = class1.getMethod("add", Object.class);
            method.invoke(array, 100);
            method.invoke(array, 666.666);
            method.invoke(array, 0.1);
            System.out.println(array);
            // 输出:[a, 1, 100, 666.666, 0.1]
        }
    }

    反射实现通过配置文件运行:

    有时候想改源码,但是不能改源码,可以这样做:

    自定义三个类:

    package demo;
    
    public class Person {
        public void eat(){
            System.out.println("人在吃饭");
        }
    }
    View Code
    package demo;
    
    public class Student {
        public void study(){
            System.out.println("学生在学习");
        }
    }
    View Code
    package demo;
    public class Worker {
        public void job(){
            System.out.println("上班族在工作");
        }
    }
    View Code

    配置文件:config.properties

    #className=demo.Student
    #methodName=study
    className=demo.Person
    methodName=eat
    #className=demo.Worker
    #methodName=job

    测试类:

    package demo;
    
    import java.io.FileReader;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    /*
     *  调用Person方法,调用Student方法,调用Worker方法
     *  类不清楚,方法也不清楚
     *  通过配置文件实现此功能
     *    运行的类名和方法名字,以键值对的形式,写在文本中
     *    运行哪个类,读取配置文件即可
     *  实现步骤:
     *    1. 准备配置文件,键值对
     *    2. IO流读取配置文件  Reader
     *    3. 文件中的键值对存储到集合中 Properties
     *        集合保存的键值对,就是类名和方法名
     *    4. 反射获取指定类的class文件对象
     *    5. class文件对象,获取指定的方法
     *    6. 运行方法
     */
    public class Test {
        public static void main(String[] args) throws Exception{
            //IO流读取配置文件
            FileReader r = new FileReader("config.properties");
            //创建集合对象
            Properties pro = new Properties();
            //调用集合方法load,传递流对象
            pro.load(r);
            r.close();
            //通过键获取值
            String className = pro.getProperty("className");
            String methodName = pro.getProperty("methodName");
            //反射获取指定类的class文件对象
            Class c = Class.forName(className);
            Object obj = c.newInstance();
            //获取指定的方法名
            Method method = c.getMethod(methodName);
            method.invoke(obj);
        }
    }
  • 相关阅读:
    Linux下VFP NEON浮点编译
    硬浮点 VFP
    程序员如何避免猝死?
    程序员谨防加班猝死之十大建议
    linux系统调用和库函数调用的区别
    彻底抛弃脚本录制,LR脚本之使用web_custom_request函数自定义
    LoadRunner监控mysql利器-SiteScope(转)
    linux mysql 数据库开启外部访问设置指南
    Java Web自定义MVC框架详解 (转)
    Jenkins+Ant+Jmeter搭建持续集成的接口测试平台(转)
  • 原文地址:https://www.cnblogs.com/xuyiqing/p/8366483.html
Copyright © 2011-2022 走看看