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

    一、什么是反射?

    在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有),这种动态获取的信息,及调用对象的方法的功能就称为Java的反射机制。

    二、获取类对象

    1、什么是类对象?

      创建Dog类和Cat类

    public class Dog {
        private String name;
        private int age;
        public Dog() {
    
        }
        public Dog(String name, int age) {
            this.name = name;
            this.age = age;
        }    
        /*getter and setter*/
    }
    public class Cat {
        private String name;
        private String hobby;
        /*getter and setter*/
    }

      实例化两个Dog类的对象

    Dog dog1 = new Dog("大黄", 5);
    Dog dog2 = new Dog("小黑", 3);

    在理解类对象之前,先说我们熟悉的对象之间的区别:

           dog1和dog2都是Dog对象,他们的区别在于,各自有不同的名称、年龄

    然后说说类之间的区别

      Dog和Cat都是类,他们的区别在于有不同的方法,不同的属性

    类对象,就是描述一个类都有什么属性,什么方法的。所有的类,都存在一个类对象,这个类对象用于提供类本身的信息.

    2、获取类对象的三种方式

       1. Class.forName
       2. Dog.class
       3. new Dog().getClass()

      try {
                Class class1 = Class.forName("yingshe.Dog");
                System.out.println(class1);
                Class class2 = Dog.class;
                System.out.println(class2);
                Class class3 = new Dog().getClass();
                System.out.println(class3);
    
                System.out.println(class1 == class2);
                System.out.println(class1 == class3);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

    执行结果

      在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。

    3、获取类对象的时候会导致类属性被初始化

      Dog类加上代码

    static String msg;
    
        static {
            System.out.println("初始化msg");
            msg="被初始化了";
        }

      获取类对象

      无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)

     

    三、创建对象

      与传统的通过new 来获取对象的方式不同,反射机制,会先拿到Hero的“类对象”,然后通过类对象直接创建对象或获取“构造器对象” ,再通过构造器对象创建一个对象

       T newInstance()
        创建由此 类对象表示的类的新实例。

      Constructor getConstructor()
        返回一个 Constructor对象

    import java.lang.reflect.Constructor;
    
    public class Test2 {
        public static void main(String[] args) {
            //获取类对象
            Class classDog = Dog.class;
            try {
                //用类对象直接创建
                Dog dog1 = (Dog)classDog.newInstance();
                dog1.setName("大黄");
                dog1.setAge(5);
                System.out.println(dog1);
    
                //获取构造器再创建
                //构造器
                Constructor constructor = classDog.getConstructor();
                //通过构造器实例化
                Dog dog2 = (Dog) constructor.newInstance();
                dog2.setName("小黑");
                dog2.setAge(4);
                System.out.println(dog2);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    四、访问属性

     为了访问属性,将name修改为public属性

    对于private修饰的成员,需要使用setAccessible(true)才能访问和修改。

    通过反射修改属性值

    import java.lang.reflect.Field;
    
    public class Test3 {
        public static void main(String[] args){
            Dog dog =new Dog();
            //使用传统方式修改name的值为大黄
            dog.name = "大黄";
            System.out.println(dog.name);
            try {
                //获取类Dog的名字叫做name的字段
                Field f1= dog.getClass().getDeclaredField("name");
                //修改这个字段的值
                f1.set(dog, "小黑");
                //打印被修改后的值
                System.out.println(dog.name);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    五、部分函数

    1、Field[] getFields()
      返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段、类对象。
    2、Field[] getDeclaredFields()
      动态的返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象

    增加Dog属性

    public class Dog {
        public String name;  //public
        private int age;  private
        String health;   //默认
        protected int weigth;  //protected
        //其他函数
    }
    import java.lang.reflect.Field;
    
    public class Test4 {
        public static void main(String[] args){
            Class classDog=Dog.class;
            Field[] fields1 = classDog.getFields();
            System.out.println("类的public属性");
            for (Field f : fields1){
                System.out.println(f.getName());
            }
            Field[] fields2 = classDog.getDeclaredFields();
            System.out.println("类的所有属性");
            for (Field f : fields2){
                System.out.println(f.getName());
            }
        }
    }

    执行结果

    3、Method[] getMethods()

      返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。
    4、Method[] getDeclaredMethods()
      返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。

    5、Object invoke(Object obj, Object… args)
      在具有指定参数的 方法对象上调用此 方法对象表示的底层方法

    例如:取得setName方法映射给method,method调用invoke函数第一个对象为操作对象,第二个参数为参数(可以是数组)

    import java.lang.reflect.Method;
    
    public class Test5 {
        public static void main(String[] args) {
            Class classDog = Dog.class;
            try {
                Dog dog = (Dog) classDog.newInstance();
                Method method = classDog.getMethod("setName", String.class);
                method.invoke(dog, "名字");
                System.out.println(dog.name);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    六、反射有什么用?

    1、首先准备两个业务类,这两个业务类很简单,就是各自都有一个业务方法,分别打印不同的字符串

    package reflection; 
    public class Service1 { 
        public void doService1(){
            System.out.println("业务方法1");
        }
    }
    package reflection; 
    public class Service2 { 
        public void doService2(){
            System.out.println("业务方法2");
        }
    }

    2、非反射方式

    当需要从第一个业务方法切换到第二个业务方法的时候,使用非反射方式,必须修改代码,并且重新编译运行,才可以达到效果

    package reflection; 
    public class Test { 
        public static void main(String[] args) {
    //      new Service1().doService1();
            new Service2().doService2();
        }
    }

    3、反射方式

    使用反射方式,首先准备一个配置文件,就叫做spring.txt吧, 放在src目录下。 里面存放的是类的名称,和要调用的方法名。
    在测试类Test中,首先取出类名称和方法名,然后通过反射去调用这个方法。
    当需要从调用第一个业务方法,切换到调用第二个业务方法的时候,不需要修改一行代码,也不需要重新编译,只需要修改配置文件spring.txt,再运行即可。
    这也是Spring框架的最基本的原理,只是它做的更丰富,安全,健壮。

    spring.txt

    class=yingshe.Service1
    method=doService1

    TestF.java

    import java.io.File;
    import java.io.FileInputStream;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    public class TestF {
    
        @SuppressWarnings({"rawtypes", "unchecked"})
        public static void main(String[] args) throws Exception {
    
            //从spring.txt中获取类名称和方法名称
            File springConfigFile = new File("g:\spring.txt");
            Properties springConfig = new Properties();
            springConfig.load(new FileInputStream(springConfigFile));
            String className = (String) springConfig.get("class");
            String methodName = (String) springConfig.get("method");
    
            //根据类名称获取类对象
            Class clazz = Class.forName(className);
            //根据方法名称,获取方法对象
            Method m = clazz.getMethod(methodName);
            //获取构造器
            Constructor c = clazz.getConstructor();
            //根据构造器,实例化出对象
            Object service = c.newInstance();
            //调用对象的指定方法
            m.invoke(service);
    
        }
    }

    执行结果

  • 相关阅读:
    java.util.concurrent学习
    mysql慢查优化总结
    mysql怎么限制某些查询语句的执行?
    数据库操作提交事务如果不关闭,会有什么样的后果?
    apache的500错误是写到哪个文件里面
    apache也可以做负载均衡,跟nignx的区别是什么?
    ajax提交请求为啥url要用这个函数encodeURI
    MySQL性能调优与架构设计读书笔记
    java枚举的作用
    linux的命令
  • 原文地址:https://www.cnblogs.com/GG-Bond/p/10644278.html
Copyright © 2011-2022 走看看