Java反射机制

Posted gg-bond

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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反射机制的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

java 反射代码片段

深入理解java的反射机制

Java反射机制

Java核心技术梳理-类加载机制与反射