码迷,mamicode.com
首页 > 编程语言 > 详细

Java反射

时间:2019-04-05 00:30:43      阅读:158      评论:0      收藏:0      [点我收藏+]

标签:代理   lang   包含   数据   tcl   amp   tar   font   property   

什么是反射


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

java反射框架提供的功能:

  • 运行时判断任意一个对象所属的类
  • 运行时构造任意一个类的对象
  • 运行时判断任意一个类所具有的成员变量和方法
  • 运行时调用任意一个对象的方法
  • 生成动态代理

 反射的用途


 

Java 强类型语言,但是我们在运行时有了解、修改信息的需求,包括类信息、成员信息等。反射机制主要用于在运行时动态加载需要加载的对象。例如,在通用框架开发过程中,根据配置文件加载不同的类和对象,调用不同的方法。我们平时使用反射主要用于获取类型的相关信息、动态调用方法、动态构造对象、从程序集中获得类型。

预备知识


在了解java反射之前首先补充几个知识

Class类:在面向对象编程中,万物皆对象,用户编写的任何一个类都是对象,它是java.lang.class类的实例对象。即在java中存在一个类,叫class类,这个类的实例对象就是我们编写的类。

类类型(class type):用户类对应的class类的对象实例。如下文中的c、c2、c3是Student类的类类型。我们编写的.java文件经过编译后得到.class文件,即对应一个Class实例对象。通过类类型可以获得该类的对象实例以及该类的变量、方法信息。Class类的实例对象是由jvm在类加载的时候创建的且仅在第一次使用该类时加载。在运行期间,一个用户类只产生一个Class类的对象实例,一个用户类仅有一个Class对象产生,那么我们如何获取这个对象呢?

类的加载方式

1)静态加载类:编译时刻加载类,使用new创建对象,编译时加载所有可能用到的类。

2)动态加载类:运行时刻加载类,使用反射动态加载,通过类类型创建该类对象。

通过动态加载类可以方便进行程序升级,同时为提高程序的适用性,通过c.getInstance()创建对象时的强制类型转换最好转为父接口类型。

反射的基本使用方法


 一般情况下,我们在使用类的时候都是创建该类对象,通过对象调用方法。这种方式可以理解为“正”。而反射,则是在程序编写时我们并不知道要创建并初始化的类对象是什么,自然就无法使用new来创建对象,此时可以使用反射来创建对象、调用方法。

其实我们使用反射的目的就是使用某个类、调用类里的方法、获取或设置类中属性的值,这点跟我们传统使用类的目的是一致的。只是 相对于传统使用类的的方式有所区别,所以在学习反射的使用时,无非是学习如何通过反射来创建类对象、调用类的方法和属性等。下面一一作出介绍。

反射就是对任意一个类可以知道他的全部属性和方法,这些信息就存储在该类对应的Class对象实例中,所以如果想要使用某个类,那么必须先获得这个类的类类型,即获取该类对应的Class的对象实例:

获取Class的对象实例的三种方式

1)使用用户类的静态class属性

Class c = Student.class;

  任何数据类型(包括基本数据类型)都有一个静态的class属性,通过用户类的类名获得类类型。

2)使用用户类对象实例的getClass()方法

Class c2 = student1.getClass();

  getClass()是Object类的成员方法,Java类全部继承自Object类,所以任意一个类对象都有getClass()方法。通过用户类的实例获得类类型。

3)通过Class类的静态方法<常用>

//Class.forName(String className)    className为用户类包含包名的完整类名
Class c3 = Class.forName("com.example.reflact.Student")

  通过Class类名调用静态方法forName(),传入用户类的完整类名(包含包名)获得用户类。forName()方法在运行时可能会抛出ClassNotFoundException,编程时候需要注意处理异常。

  在获得类类型之后,我们就可以进一步获取这个类中的构造方法、成员方法、属性等信息,从而使用该类。

获取用户类的构造方法

前面提到过,反射就是把java类中的各种成分(方法、属性等)映射成一个个的Java对象,其中,构造方法是java.lang.Constructor类的对象。

批量获取构造方法

1 public Constructor[] getConstructors()       //获取所有"公有的"构造方法
2 public Constructor[] getDeclaredConstructors()    //获取所有的构造方法(包括私有、受保护、默认、公有)

获取单个构造方法,会抛出NoSuchMethodException异常

public Constructor getConstructor(Class... parameterTypes)//获取单个的"公有的"构造方法,
//传入构造方法若干参数的类型,注意传入的必须是类型(如int.class),返回的是描述这个构造方法的类对象
public Constructor getDeclaredConstructor(Class... parameterTypes)//获取"某个构造方法"可以是私有的,或受保护、默认、公有;

调用构造方法

Constructor-->newInstance(Object... initargs) //传入构造方法的若干具体参数

示例:

package com.jing.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args){

        try {
            Class c = Class.forName("com.jing.reflect.Student");
            Constructor[] cs = c.getDeclaredConstructors();//获取全部构造方法
            System.out.println("====批量获取全部构造方法===");
            for(Constructor constructor : cs){
                System.out.println(constructor);
            }

            System.out.println("===获取单个构造方法===");
            Constructor constructor = c.getConstructor(String.class);//获取单个构造函数
            System.out.println(constructor);
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

通过反射获取用户创建的类的对象实例

通过反射创建类对象的方式有两种:

通过Class对象的newInstance()方法,会抛出InstantiationException。只能使用无参构造方法

Student s1 = (Student)c.newInstance();

通过Constructor对象的newInstance()方法,会抛出InvocationTargetException异常。可以选择构造方法

System.out.println("===获取构造方法===");
Constructor constructor = c.getConstructor(String.class);//获取单个构造函数
System.out.println("===创建对象===");   
Student s2 = (Student)constructor.newInstance("Jing");

获得用户类中成员方法信息

成员方法是java.lang.reflect.Method类的对象

批量获取:

public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)

单个获取:

public Method getMethod(String name,Class<?>... parameterTypes):
参数:
    name : 方法名;
    Class ... : 形参的Class类型对象
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)

调用成员方法:

	Method --> public Object invoke(Object obj,Object... args):
	参数说明:
		obj : 要调用方法的对象;
		args:调用方法时所传递的实参;

示例:

            System.out.println("===获取全部成员方法===");
            Method[] methods = c.getDeclaredMethods();
            for(Method method : methods){
                Class[] paramTypes = method.getParameterTypes();//获取方法参数列表中参数类型
                System.out.print("方法名:" + method.getName() + "--方法返回类型:" + method.getReturnType() +
                        "--方法参数列表:");
                for(Class par : paramTypes){
                    System.out.print(par + "    ");
                }
                System.out.println();
            }

            System.out.println("===获取单个成员方法===");
            Method method = c.getDeclaredMethod("showAge", int.class);
            System.out.println(method);

            Object obj = c.getConstructor().newInstance();//实例化一个Student对象
            System.out.println("===调用方法===");
            method.setAccessible(true);//解除私有限定
            Object result = method.invoke(obj, 20);//此处解除了私有限定,所以参数可以使用obj
            // 或,不解除私有限定,使用s1调用方法(s1被强制类型转换为了Student类型,可以调用private变量)
            System.out.println("返回结果:" + result);

获取用户类中成员变量信息

成员变量是java.lang.reflect.Field类的对象

批量获取

public Field[] getFields():获取所有的"公有字段"
public Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;

单个获取

public Field getField(String fieldName):获取某个"公有的"字段;
public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的) ,会抛出NoSuchFieldException 异常

设置字段值

Field --> public void set(Object obj,Object value):
参数说明:
    obj:要设置的字段所在的对象;
   value:要为字段设置的值

示例:

            System.out.println("===批量获取成员变量===");
            Field[] fields = c.getDeclaredFields();
            for(Field f : fields){
                System.out.println(f);
            }

            System.out.println("===单个获取成员变量===");
            Field f = c.getDeclaredField("age");
            System.out.println("设置前年龄:" + f.get(obj));
            f.set(obj, 11);//设置成员变量值
            System.out.println("设置后年龄:" + f.get(obj));

一般情况下我们使用反射获取一个对象的步骤:

  • 获取类的 Class 对象实例
  • 根据 Class 对象实例获取 Constructor 对象
  • 使用 Constructor 对象的 newInstance 方法获取反射类对象

而如果要调用某一个方法,则需要经过下面的步骤:

  • 获取方法的 Method 对象
  • 利用 invoke 方法调用方法

完整示例代码:

用户定义Student类

package com.jing.reflect;

public class Student {

    String name;
    int age;

    public Student(){
        System.out.println("---调用无参构造方法---");
    }
    public Student(String name){
        this.name = name;
        System.out.println("---调用有参构造方法---");
    }

    public String showName(String name){
        System.out.println("show...." + name);
        return name;
    }

    private int showAge(int age){
        System.out.println("show...." + age);
        return age;
    }


}

测试类:

package com.jing.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args){

        try {
            Class c = Class.forName("com.jing.reflect.Student");
            Constructor[] cs = c.getDeclaredConstructors();//获取全部构造方法
            System.out.println("====批量获取全部构造方法===");
            for(Constructor constructor : cs){
                System.out.println(constructor);
            }
            System.out.println("===获取单个构造方法===");
            Constructor constructor = c.getConstructor(String.class);//获取单个构造函数
            System.out.println(constructor);

            System.out.println("===创建对象===");
            Student s1 = (Student)c.newInstance();
            Student s2 = (Student)constructor.newInstance("Jing");

            System.out.println("===获取全部成员方法===");
            Method[] methods = c.getDeclaredMethods();
            for(Method method : methods){
                Class[] paramTypes = method.getParameterTypes();//获取方法参数列表中参数类型
                System.out.print("方法名:" + method.getName() + "--方法返回类型:" + method.getReturnType() +
                        "--方法参数列表:");
                for(Class par : paramTypes){
                    System.out.print(par + "    ");
                }
                System.out.println();
            }

            System.out.println("===获取单个成员方法===");
            Method method = c.getDeclaredMethod("showAge", int.class);
            System.out.println(method);

            Object obj = c.getConstructor().newInstance();//实例化一个Student对象
            System.out.println("===调用方法===");
            method.setAccessible(true);//解除私有限定
            Object result = method.invoke(obj, 20);//此处解除了私有限定,所以参数可以使用obj
            // 或,不解除私有限定,使用s1调用方法(s1被强制类型转换为了Student类型)
            System.out.println("返回结果:" + result);

            System.out.println("===批量获取成员变量===");
            Field[] fields = c.getDeclaredFields();
            for(Field f : fields){
                System.out.println("成员变量类型:" + f.getType() + "  成员变量名称" + f.getName());
            }

            System.out.println("===单个获取成员变量===");
            Field f = c.getDeclaredField("age");
            System.out.println("设置前年龄:" + f.get(obj));
            f.set(obj, 11);//设置成员变量值
            System.out.println("设置后年龄:" + f.get(obj));

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

技术图片

反射的应用:

通过反射运行配置文件内容

利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改,只需要添加新类,并修改配置文件使用新类即可。

用户定义类

package com.jing.reflect;

public class Student2 {
    public void show(){
        System.out.println("Student2类");
    }
}

配置文件

className = com.jing.reflect.Student2
methodName
= show

测试类

package com.jing.reflect;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Demo {
    public static void main(String[] args) throws Exception{
        //获取Class类对象
        Class stuClass = Class.forName(getValue("className"));
        //获取show方法
        Method method = stuClass.getDeclaredMethod(getValue("methodName"));
        //获取Student构造方法,创建实例,并通过该实例调用show方法
        method.invoke(stuClass.getConstructor().newInstance());
    }
    //此方法接收一个key,在配置文件中获取相应的value
    public static String getValue(String key) throws IOException {
        Properties pro = new Properties();//获取配置文件的对象
        FileReader proIn = new FileReader("I:\\CodePractice\\reflect\\src\\com\\jing\\reflect\\pro.txt");//获取输入流
        pro.load(proIn);//将输入流加载到配置文件对象中
        proIn.close();
        return pro.getProperty(key);//返回根据key获取的value值
    }
}

运行结果

技术图片

系统升级时,不再需要Student2类,此时重新写一个Student类,在配置文件中指明,原代码不动,完成系统的升级。修改pro.txt文件,执行程序后结果:

技术图片

 

 

 

 

参考:

Java基础之—反射(非常重要)<强烈推荐>

大白话说Java反射:入门、使用、原理<强烈推荐>

反射的用途和实现

 

Java反射

标签:代理   lang   包含   数据   tcl   amp   tar   font   property   

原文地址:https://www.cnblogs.com/Jing-Wang/p/10657664.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!