Java 反射机制详解(下)

Posted justbobo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 反射机制详解(下)相关的知识,希望对你有一定的参考价值。

续:Java 反射机制详解(上)

三、怎么使用反射 

   想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。

   获取字节码文件对象的三种方式。

   1、Class class1= Class.forName("全限定类名");  //通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。

   2、Class class2= User.class;    //当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己, 该类处于字节码阶段

   3、Class class3= user.getClass();    //通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段。

代码实例:

技术图片
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
System.out.println("第一种:"+class1.getName());
//可以根据 实例对象获取我们想要的信息
Class class2 = User.class;
System.out.println("第二种:"+class2.getName());

User user = new User();
Class class3 = user.getClass();
System.out.println("第三种:"+class3.getName());
}

输出:
第一种:com.reflect.test.User
第二种:com.reflect.test.User
第三种:com.reflect.test.User
获取字节码文件对象

 有了字节码文件对象才能获得类中所有的信息,我们在使用反射获取信息时,也要考虑使用上面哪种方式获取字节码对象合理,视不同情况而定。下面介绍Class类的功能。

首先生成一个User类对象

技术图片
package com.reflect.test;

public class User {

    private String name ;
    private int age;
    private String sex;

    public User() {
    }

    public User(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    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;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User{" +
                "name=‘" + name + ‘\\‘‘ +
                ", age=" + age +
                ", sex=‘" + sex + ‘\\‘‘ +
                ‘}‘;
    }
}
User.java

3.1 通过字节码对象创建实例对象

public  static void main(String[] args) throws  ClassNotFoundException,InstantiationException,IllegalAccessException{
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class classz =Class.forName("com.reflect.test"); //forname 里面的参数完整文件路径,如果查不到改路径下的文件 会报 classNotFound异常
//创建实例,
User user = (User) classz.newInstance();
//可以根据 实例对象获取我们想要的信息
}

 3.2获取指定构造器方法。constructor对象

知道对象构造函数的参数情况下,可以直接获取指定构造函数。

//User类还没有加载,在源文件阶段 获取其字节码文件对象
 Class class1 =Class.forName("com.reflect.test.User");
//获取无参构造函数
Constructor constructor1 = class1.getConstructor();
//获取有参构造函数
Constructor constructor2 = class1.getConstructor(String.class,int.class,String.class);
User user1=(User)constructor1.newInstance();
System.out.println("user1:  " + user1);
User user2 = (User) constructor2.newInstance("张三",23,"男");
System.out.println("user1:  "+ user2);

输出:

  user1: User{name=‘null‘, age=0, sex=‘null‘}
  user1: User{name=‘张三‘, age=23, sex=‘男‘}

如果不知道 User类有哪些构造方法,以及参数,可以这样获取全部构造方法

//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
//获取所有构造方法
Constructor[] constructors = class1.getConstructors();
//遍历所有构造方法
int index = 0;
for(Constructor constructor : constructors){
Class[] parameterTypes = constructor.getParameterTypes();
index =index+1;
System.out.println("第" + index +"个构造函数");
for (Class parameterType : parameterTypes){
    //获取构造函数中的参数类型
    System.out.print(parameterType.getName()+",");
}
输出:

第1个构造函数
第2个构造函数
java.lang.String,int,java.lang.String,

3.2获取成员变量并使用。Field对象

 获取指定成员变量

//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
//获取实例对象
User user = (User) class1.newInstance();
//获取成员变量class1.getField(name); 通过name获取指定变量
// 如果变量属性是私有的 那么应该使用class1.getDeclaredField(name);
Field field = class1.getDeclaredField("name");
//因为属性是私有的,获得对象属性后,还要打开其私有权限
field.setAccessible(true);  // 这里也变相的破解了 面向对象的封装性
//对其变量进行操作
field.set(user,"张三");
//
System.out.println(field.get(user));
System.out.println(user.getName());
输出:
张三
张三

Class.getField(String)方法可以获取类中的指定字段(可见的), 如果是私有的可以用getDeclaedField("name")方法获取,通过set(obj, "李四")方法可以设置指定对象上该字段的值, 如果是私有的需要先调用setAccessible(true)设置访问权限,用获取的指定的字段调用get(obj)可以获取指定对象中该字段的值

获取全部属性 变量

//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
//获取实例对象
User user = (User) class1.newInstance();
user.setName("zhangsan");
user.setAge(23);
user.setSex("男");
//获取全部成员变量
Field[] fields = class1.getDeclaredFields();

for ( Field field : fields){
    field.setAccessible(true);
    System.out.println(field.get(user));
}
输出:
zhangsan
23

 3.3 获得成员方法并使用。Method对象

技术图片
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
//获取实例对象
User user = (User) class1.newInstance();
user.setAge(23);
user.setSex("男");
/**
 * Method getMethod(String name, Class<?>... parameterTypes)
 * name : 为方法名字
 * parameterTypes:方法的参数,为class类型,比如参数类型为String,则填string.class
 * 没有则不填
 */
/**
 * Object invoke(Object obj, Object... args)
 * obj:方法的对象
 * args:实际的参数值,没有则不填
 */
Method method1 = class1.getMethod("getAge");
System.out.println("method1 方法:" +method1.getName());
System.out.println("调用方法 : "+method1.invoke(user));
Method method2 = class1.getMethod("getSex");
System.out.println("method2 方法:" +method2.getName());
System.out.println("调用方法 : "+method2.invoke(user));
//我们将 User 类的setName()方法 改为私有的private void setName(String name) {this.name = name; }

Method method3 = class1.getDeclaredMethod("setName", String.class);
method3.setAccessible(true);
method3.invoke(user,"zhangsan"); //可以调用 user对象的 私有方法setName
System.out.println("user.getName :" + user.getName()); // 获取 name属性值

输出:
method1 方法:getAge
调用方法 : 23
method2 方法:getSex
调用方法 : 男
user.getName :zhangsan
View Code

Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以获取类中的指定方法,    

如果为私有方法,则需要打开一个权限。setAccessible(true);

用invoke(Object, Object...)可以调用该方法,

同理可以 获取全部方法

技术图片
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
//获取实例对象
User user = (User) class1.newInstance();
user.setAge(23);
user.setSex("男");

Method [] methods = class1.getDeclaredMethods();
for (Method method : methods){
    method.setAccessible(true);
    System.out.println("方法名:"+method.getName());//获取 方法名
    Class [] parameterTypes = method.getParameterTypes();//这里又回到了上面的获取参数代码
    for (Class parameterType : parameterTypes){
    System.out.println("获取参数名:"+parameterType.getName());
    }

}
输出:
方法名:toString
方法名:getName
方法名:setName
获取参数名:java.lang.String
方法名:getSex
方法名:getAge
方法名:setSex
获取参数名:java.lang.String
方法名:setAge
获取参数名:int
View Code

3.4 获得该类的所有接口

    Class[] getInterfaces():确定此对象所表示的类或接口实现的接口

    返回值:接口的字节码文件对象的数组

以上是关于Java 反射机制详解(下)的主要内容,如果未能解决你的问题,请参考以下文章

Java反射机制详解

[转]Java反射机制详解

Java 反射详解 转载

Java反射机制详解

Java-反射机制(超详解)

反射机制