当前位置 : 首页 » 文章分类 :  开发  »  Java-反射

Java-反射

java反射笔记


概述

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

反射机制主要提供了以下功能:

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

相关API
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中

  • Class 类:java.lang.Class,代表一个类。
  • Field 类:java.lang.reflect.Field,代表类的成员变量(成员变量也称为类的属性)。
  • Method 类:java.lang.reflect.Method,代表类的方法。
  • Constructor 类:java.lang.reflect.Constructor,代表类的构造方法。

Java反射机制详解
http://www.cnblogs.com/lzq198754/p/5780331.html

JAVA反射和代理机制
http://www.cnblogs.com/pepcod/archive/2013/02/16/2913526.html


反射工具

ReflectionUtils Spring反射工具类

org.springframework.util.ReflectionUtils

findField 查询字段(包含private及父类private字段)

public static Field findField(Class<?> clazz, String name)
public static Field findField(Class<?> clazz, @Nullable String name, @Nullable Class<?> type)

根据字段名 name 查询 clazz 类的字段,包含本类 private 字段及全部父类 private 字段,还可以指定查找字段的类型,内部是通过循环调用子类及父类的 getDeclaredFields() 方法实现的


reflections 开源库

ronmamo / reflections
https://github.com/ronmamo/reflections

获取带有指定注解的类

new Reflections("my.package").getTypesAnnotatedWith(MyAnnotation.class)

https://stackoverflow.com/questions/259140/scanning-java-annotations-at-runtime
Scanning Java annotations at runtime

获取带有指定注解的全部方法

Reflections reflections = new Reflections("my.package", Scanners.MethodsAnnotated);
Set<Method> methods = reflections.getMethodsAnnotatedWith(MyAnnotation.class)

Class类

Class类是Reflection API 中的核心类

java.lang.Class<T>

public final class Class<T>
extends Object
implements Serializable, GenericDeclaration, Type, AnnotatedElement

类型参数T:由此 Class 对象建模的类的类型。例如,String.class 的类型是 Class<String>。如果将被建模的类未知,则使用 Class<?>

获取Class对象

Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象。 要想使用反射,首先需要获得待处理类或对象所对应的Class对象
获取某个类或某个对象所对应的Class对象的常用的3种方式:

  • 使用Class类的静态方法forName
    Class.forName(“java.lang.String”);
  • 使用每个类都有的.class属性
    String.class;
  • 使用每个对象都有的getClass()方法
    在java.lang.Object类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。
    String s = “aa”; Class<?> clazz = s.getClass();

获取Class对象的三种方法示例

package com.masi.reflect;

public class GetGlassTest {
    public static void main(String[] args) throws Exception {
        Class<?> class1 = null;
        Class<?> class2 = null;
        Class<?> class3 = null;
        class1 = Class.forName("com.masi.reflect.GetGlassTest"); // 一般采用这种形式
        GetGlassTest ggt = new GetGlassTest();
        class2 = ggt.getClass();
        class3 = GetGlassTest.class;
        System.out.println("类名称   " + class1.getName());
        System.out.println("类名称   " + class2.getName());
        System.out.println("类名称   " + class3.getName());
    }
}

执行结果:

类名称   com.masi.reflect.GetGlassTest
类名称   com.masi.reflect.GetGlassTest
类名称   com.masi.reflect.GetGlassTest

getName()

public String getName()
以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
如果此类对象表示的是非数组类型的引用类型,则返回该类的二进制名称,Java Language Specification, Second Edition 对此作了详细说明。
如果此类对象表示一个基本类型或 void,则返回的名字是一个与该基本类型或 void 所对应的 Java 语言关键字相同的 String。
如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 ‘[‘ 字符加元素类型名

元素类型名的编码如下

Element Type Encoding
boolean Z
byte B
char C
class or interface Lclassname;
double D
float F
int I
long J
short S

类或接口名 classname 是上面指定类的二进制名称。

示例

String.class.getName()
     returns "java.lang.String"
byte.class.getName()
     returns "byte"
(new Object[3]).getClass().getName()
     returns "[Ljava.lang.Object;"
(new int[3][4][5][6][7][8][9]).getClass().getName()
     returns "[[[[[[[I"

类二进制名称示例

"java.lang.String"
"javax.swing.JSpinner$DefaultEditor"
"java.security.KeyStore$Builder$FileBuilder$1"
"java.net.URLClassLoader$3$1"

forName()

public static Class<?> forName(String className)
返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于:Class.forName(className, true, currentLoader)
其中className是所需类的完全限定名; true表示该类将被初始化;currentLoader 表示当前类的定义类加载器。

  • 抛出:
  • LinkageError - 如果链接失败
  • ExceptionInInitializerError - 如果此方法所激发的初始化失败
  • ClassNotFoundException - 如果无法定位该类

public static Class<?> forName(String name, boolean initialize, ClassLoader loader)
使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。(以 getName 所返回的格式)给定一个类或接口的完全限定名,此方法会试图定位、加载和链接该类或接口。指定的类加载器用于加载该类或接口。如果参数 loader 为 null,则该类通过引导类加载器加载。只有 initialize 参数为 true 且以前未被初始化时,才初始化该类。
如果 name 表示一个基本类型或 void,则会尝试在未命名的包中定位用户定义的名为 name 的类。因此,该方法不能用于获得表示基本类型或 void 的任何 Class 对象。
如果 name 表示一个数组类,则会加载但不初始化该数组类的组件类型。

  • 参数:
  • name - 所需类的完全限定名
  • initialize - 是否必须初始化类,默认为true
  • loader - 用于加载类的类加载器
  • 返回:表示所需类的类对象
  • 抛出:
  • LinkageError - 如果链接失败
  • ExceptionInInitializerError - 如果该方法激发的初始化失败
  • ClassNotFoundException - 如果指定的类加载器无法定位该类

例如,在一个实例方法中,表达式: Class.forName(“Foo”)
等效于: Class.forName(“Foo”, true, this.getClass().getClassLoader())
注意,此方法会抛出与加载、链接或初始化相关的错误。
注意,此方法不检查调用者是否可访问其请求的类。
如果 loader 为 null,也存在安全管理器,并且调用者的类加载器不为 null,则此方法通过 RuntimePermission(“getClassLoader”) 权限调用安全管理器的 checkPermission 方法,以确保可以访问引导类加载器。


Class.forName()用法详解

主要功能
Class.forName(xxx.xx.xx) 返回的是一个类
Class.forName(xxx.xx.xx) 的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段

下面,通过解答以下几个问题的来详细讲解下 Class.forName() 的用法。
一、什么时候用Class.forName()?
先来个热身,给你一个字符串变量,它代表一个类的包名和类名,你怎么实例化它?你第一想到的肯定是new,但是注意一点:
A a = (A)Class.forName(“pacage.A”).newInstance();
这和你 A a = new A(); 是一样的效果。

现在言归正传。
动态加载和创建 Class 对象,比如想根据用户输入的字符串来创建对象时需要用到:
String str = “用户输入的字符串”;
Class t = Class.forName(str);
t.newInstance();

在初始化一个类,生成一个实例的时候,newInstance() 方法和 new 关键字除了一个是方法,一个是关键字外,最主要有什么区别?它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。那么为什么会有两种创建对象方式?这主要考虑到软件的可伸缩、可扩展和可重用等软件设计思想。

Java 中工厂模式经常使用 newInstance() 方法来创建对象,因此从为什么要使用工厂模式上可以找到具体答案。 例如:

Class c = Class.forName("Example");
factory = (ExampleInterface)c.newInstance();

其中 ExampleInterface 是 Example 的接口,可以写成如下形式:

String className = "Example";
Class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

进一步可以写成如下形式:

String className = readfromXMlConfig();//从xml 配置文件中获得字符串
Class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

上面代码已经不存在 Example 的类名称,它的优点是,无论 Example 类怎么变化,上述代码不变,甚至可以更换 Example 的兄弟类Example2 , Example3 , Example4……,只要他们继承ExampleInterface 就可以。

从 JVM 的角度看,我们使用关键字 new 创建一个类的时候,这个类可以没有被加载。但是使用 newInstance() 方法的时候,就必须保证:
1、这个类已经加载;
2、这个类已经连接了。
而完成上面两个步骤的正是 Class 的静态方法 forName() 所完成的,这个静态方法调用了启动类加载器,即加载 java API 的那个加载器。

现在可以看出,newInstance() 实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。

二、new 和Class.forName()有什么区别?
其实上面已经说到一些了,这里来做个总结:
首先,newInstance( )是一个方法,而new是一个关键字;
其次,Class下的newInstance()的使用有局限,因为它生成对象只能调用无参的构造函数,而使用new关键字生成对象没有这个限制
简言之:
newInstance(): 弱类型,低效率,只能调用无参构造。
new: 强类型,相对高效,能调用任何public构造。
Class.forName(“”)返回的是类。
Class.forName(“”).newInstance()返回的是object 。

三、为什么在加载数据库驱动包的时候有用的是Class.forName(),却没有调用newInstance()?
在 Java 开发特别是数据库开发中,经常会用到 Class.forName() 这个方法。
通过查询 Java Documentation 我们会发现使用 Class.forName() 静态方法的目的是为了动态加载类。
通常编码过程中,在加载完成后,一般还要调用 Class 下的 newInstance() 静态方法来实例化对象以便操作。因此,单单使用 Class.forName() 是动态加载类是没有用的,其最终目的是为了实例化对象。

有数据库开发经验朋友会发现,为什么在我们加载数据库驱动包的时候有的却没有调用 newInstance() 方法呢?
即有的jdbc连接数据库的写法里是 Class.forName(xxx.xx.xx); 而有一些是:Class.forName(xxx.xx.xx).newInstance(),为什么会有这两种写法呢?
刚才提到,Class.forName(“”); 的作用是要求 JVM 查找并加载指定的类,首先要明白,java里面任何class都要装载在虚拟机上才能运行,而静态代码是和class绑定的,class装载成功就表示执行了你的静态代码了,而且以后不会再走这段静态代码了
而我们前面也说了,Class.forName(xxx.xx.xx) 的作用就是要求 JVM 查找并加载指定的类,如果在类中有静态初始化器的话,JVM必然会执行该类的静态代码段。
而在 JDBC 规范中明确要求这个 Driver 类必须向 DriverManager 注册自己,即任何一个 JDBC Driver 的 Driver 类的代码都必须类似如下:

public class MyJDBCDriver implements Driver {
static {
    DriverManager.registerDriver(new MyJDBCDriver());
  }
}

既然在静态初始化器的中已经进行了注册,所以我们在使用JDBC时只需要Class.forName(XXX.XXX);就可以了。

四、Class.forName()做了什么?
假设一个类以前从来没有被装进内存过,Class.forName(String className)这个方法会做以下几件事情:
1、装载。将字节码读入内存,并产生一个与之对应的java.lang.Class类对象
2、连接。这一步会验证字节码,为static变量分配内存,并赋默认值(0或null),并可选的解析符号引用(这里不理解没关系)
3、初始化。为类的static变量赋初始值,假如有static int a = 1;这个将a赋值为1的操作就是这个时候做的。除此之外,还要调用类的static块。(这一步是要点)
Class.forName(String className)方法会将这三步都做掉,如下面的例子:

package com.ticmy.oracle;

public class TestClinit {
    public static void main(String[] args) throws Exception {
        Class.forName("com.ticmy.oracle.ABC");
    }
}
class ABC {
    private static int a = getNum();
    static {
        System.out.println("this is static block");
    }
    public static int getNum() {
        System.out.println("getNum");
        return 1;
    }
}

程序的运行结果是:

getNum
this is static block

isPrimitive()

public boolean isPrimitive()

判定指定的 Class 对象是否表示一个基本类型。
有九种预定义的 Class 对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即 boolean、byte、char、short、int、long、float 和 double。

这些对象仅能通过下列声明为 public static final 的变量访问,也是使此方法返回 true 的仅有的几个 Class 对象。

返回:当且仅当该类表示一个基本类型时,才返回 true
从以下版本开始:JDK1.1

判断Class是否Java基本类型

@Test
public void test() {
    System.out.println(boolean.class.isPrimitive()); // true
    System.out.println(byte.class.isPrimitive()); // true
    System.out.println(char.class.isPrimitive()); // true
    System.out.println(short.class.isPrimitive()); // true
    System.out.println(int.class.isPrimitive()); // true
    System.out.println(long.class.isPrimitive()); // true
    System.out.println(float.class.isPrimitive()); // true
    System.out.println(double.class.isPrimitive()); // true
    System.out.println(void.class.isPrimitive()); // true

    System.out.println(Boolean.class.isPrimitive()); // false
    System.out.println(Byte.class.isPrimitive()); // false
    System.out.println(Character.class.isPrimitive()); // false
    System.out.println(Short.class.isPrimitive()); // false
    System.out.println(Integer.class.isPrimitive()); // false
    System.out.println(Long.class.isPrimitive()); // false
    System.out.println(Float.class.isPrimitive()); // false
    System.out.println(Double.class.isPrimitive()); // false
}

getDeclaredFields() 返回本类声明的字段(包含private,不包含父类的)

public Field[] getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类或接口不声明任何字段,或者此 Class 对象表示一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组。

判断一个bean的所有属性是否都为空

package com.nio.uds.user.utils;

import com.google.common.collect.Sets;
import java.lang.reflect.Field;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

@Slf4j
public class BeanUtils {
    /**
     * 判断 object 是否所有属性都是null或空白
     * @param object
     * @param ignoreFields 忽略的属性名
     * @return true:属性全为空白
     */
    public static boolean isAllFieldsBlank(Object object, String... ignoreFields) {
        if (null == object) {
            return true;
        }
        try {
            Field[] fields = object.getClass().getDeclaredFields();
            Set<String> ignoreFieldSet = ignoreFields != null ? Sets.newHashSet(ignoreFields) : Sets.newHashSet();
            for (Field field : fields) {
                if (!ignoreFieldSet.contains(field.getName())) {
                    field.setAccessible(true);
                    Object value = field.get(object);
                    if (value != null) {
                        if (value instanceof String && StringUtils.isBlank((String)value)) {
                            continue;
                        }
                        return false;
                    }
                }
            }
        } catch (Exception e) {
            log.error("Failed to checkFieldNull");
        }
        return true;
    }
}

Java反射获取属性上的注解内容

返回类型clazz的属性列表,如果属性加了 JsonProperty 注解,使用注解中的value() 值做属性名,如果没有此注解,使用原有字段名。

public static List<String> getFieldNameByClazz(Class<?> clazz) {
    List<String> fieldNameList = Lists.newArrayList();
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        if (field.isAnnotationPresent(JsonProperty.class)) {
            JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);
            fieldNameList.add(jsonProperty.value());
        } else {
            fieldNameList.add(field.getName());
        }
    }
    return fieldNameList;
}

java中自定义注解并通过反射获取注解属性值
https://blog.csdn.net/Raynaing/article/details/53442984

getDeclaredFields()按声明顺序获得属性列表

需要把一个Bean导出到Excel,Bean中每个字段作为Excel中的一列,为了方便别人读取,Excel的列顺序是固定的。
需要用字段名做表头,导出的代码中把表头写死也可以,但显得太笨了,想着用Bean字段上的Jackson注解 JsonProperty 作为该字段的表头,但是Java反射的 getDeclaredFields() 获取类型属性方法是不保证返回顺序的,虽然大多数情况下都和声明顺序一致。
为了解决这个问题,可以自定义一个字段排序注解,例如 @FieldOrder(order = 1),反射时拿到order参数进行排序,但还要新自定义注解,嫌麻烦。
后来看到 Jackson 的 JsonProperty 注解有个排序字段参数 index ,就想着借用他的index参数来实现排序。

Bean定义:

@JsonInclude(Include.NON_NULL)
public class UserBean {
    @JsonProperty(value = "user_id", index = 1)
    private Long userId;

    @JsonProperty(value = "user_name", index = 2)
    private String userName;

    @JsonProperty(value = "mobile", index = 3)
    private String mobile;
}

获取类属性列表的方法如下,兼容了没加 @JsonProperty 注解的字段。

/**
 * 获取类属性列表
 * @param clazz
 * @return
 */
public List<String> getFieldNameByClazz(Class<?> clazz) {
    List<Field> fieldList = getOrderedFieldList(clazz.getDeclaredFields());
    List<String> fieldNameList = fieldList.stream().map(field -> {
        if (field.isAnnotationPresent(JsonProperty.class)) {
            return field.getAnnotation(JsonProperty.class).value();
        } else {
            return field.getName();
        }
    }).collect(Collectors.toList());
    return fieldNameList;
}

/**
 * 对字段列表按@JsonProperty.index排序
 * @param fields
 * @return
 */
public List<Field> getOrderedFieldList(Field[] fields) {
    Map<Integer, Field> orderIdFieldNameMap = Maps.newTreeMap();
    for (int i = 0; i < fields.length; i++) {
        if (fields[i].isAnnotationPresent(JsonProperty.class)) {
            JsonProperty jsonProperty = fields[i].getAnnotation(JsonProperty.class);
            orderIdFieldNameMap.put(jsonProperty.index(), fields[i]);
        } else {
            orderIdFieldNameMap.put(i + 1, fields[i]);
        }
    }
    List<Field> orderedFieldList = orderIdFieldNameMap.values().stream().collect(Collectors.toList());
    return orderedFieldList;
}

反射机制按顺序获取getDeclaredFields()/getDeclaredMethods()
https://blog.csdn.net/Shenpibaipao/article/details/78510849

获取某个类的全部属性

package com.masi.reflect;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class FieldTest extends superTestClass implements Serializable{
    private static final long serialVersionUID = -2862585049955236662L;
    public String publicField;
    private String[] privateStrArray;
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.masi.reflect.FieldTest");
        System.out.println("======getDeclaredFields()获取本类声明的全部属性==========");
        // 取得本类的全部属性
        Field[] field = clazz.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            // 权限修饰符
            int mo = field[i].getModifiers();
            String priv = Modifier.toString(mo);
            // 属性类型
            Class<?> type = field[i].getType();
            System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";");
        }

        System.out.println("=======getFields()获取本类以及实现的接口或者父类的public属性========");
        // 取得实现的接口或者父类的属性
        Field[] filed1 = clazz.getFields();
        for (int j = 0; j < filed1.length; j++) {
            // 权限修饰符
            int mo = filed1[j].getModifiers();
            String priv = Modifier.toString(mo);
            // 属性类型
            Class<?> type = filed1[j].getType();
            System.out.println(priv + " " + type.getName() + " " + filed1[j].getName() + ";");
        }
    }
}

class superTestClass{
    public String superfiled;
    private int superInt;
}

执行结果:

======getDeclaredFields()获取本类声明的全部属性==========
private static final long serialVersionUID;
public java.lang.String publicField;
private [Ljava.lang.String; privateStrArray;
=======getFields()获取本类以及实现的接口或者父类的public属性========
public java.lang.String publicField;
public java.lang.String superfiled;

getDeclaredField() 按名称查找本类字段

public Field getDeclaredField(String name)
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 name 参数是一个 String,它指定所需字段的简称。注意,此方法不反映数组类的 length 字段。
参数:
name - 字段名
返回:
此类中指定字段的 Field 对象
抛出:
NoSuchFieldException - 如果找不到带有指定名称的字段。
NullPointerException - 如果 name 为 null
SecurityException - 如果存在安全管理器 s,并满足下列任一条件:
调用 s.checkMemberAccess(this, Member.DECLARED) 拒绝访问已声明字段
调用者的类加载器不同于也不是当前类的类加载器的一个祖先,并且对 s.checkPackageAccess() 的调用拒绝访问该类的包


getFields() 返回所有public字段(包含父类的)

public Field[] getFields()
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共public字段。返回数组中的元素没有排序,也没有任何特定的顺序。如果类或接口没有可访问的公共字段,或者表示一个数组类、一个基本类型或 void,则此方法返回长度为 0 的数组。
特别地,如果该 Class 对象表示一个类,则此方法返回该类及其所有超类的公共字段。如果该 Class 对象表示一个接口,则此方法返回该接口及其所有超接口的公共字段


getField() 按名称查找public字段(包含父类的)

public Field getField(String name)
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 name 参数是一个 String,用于指定所需字段的简称。
要反映的字段由下面的算法确定。设 C 为此对象所表示的类:

如果 C 声明一个带有指定名的公共字段,则它就是要反映的字段。
如果在第 1 步中没有找到任何字段,则该算法被递归地应用于 C 的每一个直接超接口。直接超接口按其声明顺序进行搜索。
如果在第 1、2 两步没有找到任何字段,且 C 有一个超类 S,则在 S 上递归调用该算法。如果 C 没有超类,则抛出 NoSuchFieldException。
请参阅 Java Language Specification 的第 8.2 和 8.3 节。

参数:
name - 字段名
返回:
由 name 指定的该类的 Field 对象
抛出:
NoSuchFieldException - 如果没有找到带有指定名的字段。
NullPointerException - 如果 name 为 null
SecurityException - 如果存在安全管理器 s,并满足下列任一条件:
调用 s.checkMemberAccess(this, Member.PUBLIC) 拒绝访问字段
调用者的类加载器不同于也不是当前类的类加载器的一个祖先,并且对 s.checkPackageAccess() 的调用拒绝访问该类的包


getDeclaredMethods() 返回所有本类声明的方法

public Method[] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类或接口不声明任何方法,或者此 Class 对象表示一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组。类初始化方法 <clinit> 不包含在返回数组中。如果该类声明带有相同参数类型的多个公共成员方法,则它们都包含在返回的数组中。

getDeclaredMethods()和getMethods()的区别

getDeclaredMethods() 获取当前类的所有声明的方法,包括public、protected和private修饰的方法。需要注意的是,这些方法一定是在当前类中声明的,从父类中继承的不算,实现接口的方法由于有声明所以包括在内。
getMethods() 获取当前类和父类的所有public的方法。这里的父类,指的是继承层次中的所有父类。比如说,A继承B,B继承C,那么B和C都属于A的父类。

此区别同样适用 getFields() 和 getDeclaredFields()

获取某个类的全部方法

package com.masi.reflect;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class MethodTest implements Serializable {
    private static final long serialVersionUID = -2862585049955236662L;
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.masi.reflect.MethodTest");

        System.out.println("======getDeclaredMethods()获取本类声明的全部方法==========");
        Method method[] = clazz.getDeclaredMethods();
        for (int i = 0; i < method.length; ++i) {
            Class<?> returnType = method[i].getReturnType(); //返回类型
            Class<?> para[] = method[i].getParameterTypes(); //参数类型数组
            int temp = method[i].getModifiers(); //修饰符
            System.out.print(Modifier.toString(temp) + " ");
            System.out.print(returnType.getName() + "  ");
            System.out.print(method[i].getName() + " "); //方法名称
            System.out.print("(");
            //打印参数表
            for (int j = 0; j < para.length; ++j) {
                System.out.print(para[j].getName() + " " + "arg" + j);
                if (j < para.length - 1) {
                    System.out.print(",");
                }
            }
            Class<?> exce[] = method[i].getExceptionTypes(); //抛出异常类型数组
            if (exce.length > 0) {
                System.out.print(") throws ");
                for (int k = 0; k < exce.length; ++k) {
                    System.out.print(exce[k].getName() + " ");
                    if (k < exce.length - 1) {
                        System.out.print(",");
                    }
                }
            } else {
                System.out.print(")");
            }
            System.out.println();
        }

        System.out.println("=======getMethods()获取本类以及实现的接口或者父类的public方法========");
        Method method_all[] = clazz.getMethods();
        for (int i = 0; i < method_all.length; ++i) {
            Class<?> returnType = method_all[i].getReturnType();
            Class<?> para[] = method_all[i].getParameterTypes();
            int temp = method_all[i].getModifiers();
            System.out.print(Modifier.toString(temp) + " ");
            System.out.print(returnType.getName() + "  ");
            System.out.print(method_all[i].getName() + " ");
            System.out.print("(");
            for (int j = 0; j < para.length; ++j) {
                System.out.print(para[j].getName() + " " + "arg" + j);
                if (j < para.length - 1) {
                    System.out.print(",");
                }
            }
            Class<?> exce[] = method_all[i].getExceptionTypes();
            if (exce.length > 0) {
                System.out.print(") throws ");
                for (int k = 0; k < exce.length; ++k) {
                    System.out.print(exce[k].getName() + " ");
                    if (k < exce.length - 1) {
                        System.out.print(",");
                    }
                }
            } else {
                System.out.print(")");
            }
            System.out.println();
        }
    }
}

执行结果:

======getDeclaredMethods()获取本类声明的全部方法==========
public static void  main ([Ljava.lang.String; arg0) throws java.lang.Exception
=======getMethods()获取本类以及实现的接口或者父类的public方法========
public static void  main ([Ljava.lang.String; arg0) throws java.lang.Exception
public final void  wait (long arg0,int arg1) throws java.lang.InterruptedException
public final native void  wait (long arg0) throws java.lang.InterruptedException
public final void  wait () throws java.lang.InterruptedException
public boolean  equals (java.lang.Object arg0)
public java.lang.String  toString ()
public native int  hashCode ()
public final native java.lang.Class  getClass ()
public final native void  notify ()
public final native void  notifyAll ()

getMethods() 返回所有public方法

public Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共public成员方法。数组类返回从 Object 类继承的所有(公共)member 方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果此 Class 对象表示没有公共成员方法的类或接口,或者表示一个基本类型或 void,则此方法返回长度为 0 的数组。

getMethod() 根据名称和参数查找public方法

public Method getMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。name 参数是一个 String,用于指定所需方法的简称。parameterTypes 参数是按声明顺序标识该方法形参类型的 Class 对象的一个数组。如果 parameterTypes 为 null,则按空数组处理。
如果 name 是 <init><clinit>,则将引发 NoSuchMethodException。否则,要反映的方法由下面的算法确定(设 C 为此对象所表示的类):

  • 在 C 中搜索任一匹配的方法。如果找不到匹配的方法,则将在 C 的超类上递归调用第 1 步算法。
  • 如果在第 1 步中没有找到任何方法,则在 C 的超接口中搜索匹配的方法。如果找到了这样的方法,则反映该方法。

在 C 类中查找匹配的方法:如果 C 正好声明了一个具有指定名称的公共方法并且恰恰有相同的形参类型,则它就是反映的方法。如果在 C 中找到了多个这样的方法,并且其中有一个方法的返回类型比其他方法的返回类型都特殊,则反映该方法;否则将从中任选一个方法。
注意,类中可以有多个匹配方法,因为尽管 Java 语言禁止类声明带有相同签名但不同返回类型的多个方法,但 Java 虚拟机并不禁止。这增加了虚拟机的灵活性,可以用来实现各种语言特性。例如,可以使用桥方法 (brige method)实现协变返回;桥方法以及将被重写的方法将具有相同的签名,不同的返回类型。
返回:与指定的 name 和 parameterTypes 匹配的 Method 对象

getDeclaredMethod() 根据名称和参数查找本类方法

public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
name 参数是一个 String,它指定所需方法的简称, parameterTypes 参数是 Class 对象的一个数组,它按声明顺序标识该方法的形参类型。
如果在某个类中声明了带有相同参数类型的多个方法,并且其中有一个方法的返回类型比其他方法的返回类型都特殊,则返回该方法;否则将从中任选一个方法。

如果名称是 <init><clinit>,则引发一个 NoSuchMethodException。

参数:
name - 方法名
parameterTypes - 参数数组
返回:
该类与指定名和参数相匹配的方法的 Method 对象
抛出:
NoSuchMethodException - 如果找不到匹配的方法。
NullPointerException - 如果 name 为 null
SecurityException - 如果存在安全管理器 s,并满足下列任一条件:

调用 s.checkMemberAccess(this, Member.DECLARED) 拒绝访问已声明方法
调用者的类加载器不同于也不是当前类的类加载器的一个祖先,并且对 s.checkPackageAccess() 的调用拒绝访问该类的包


getConstructors()

public Constructor<?>[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。如果该类没有公共构造方法,或者该类是一个数组类,或者该类反映一个基本类型或 void,则返回一个长度为 0 的数组。
注意,此方法返回 Constructor<T> 对象的数组(即取自此类构造方法的数组)时,此方法的返回类型是 Constructor<?>[],不是预期的 Constructor<T>[]。此少量信息的返回类型是必需的,因为从此方法返回之后,该数组可能被修改以保存不同类的 Constructor 对象,而这将违反 Constructor<T>[] 的类型保证。

getAnnotation() 获取类上的指定注解

获取 Class 上指定类型的注解,如果没有返回 null

private MyAnnotation getMyAnnotation(Class clazz) {
    return (MyAnnotation) clazz.getAnnotation(MyAnnotation.class);
}

newInstance()

public T newInstance()
创建此 Class 对象所表示的类的一个新实例。如同用一个带有一个空参数列表的 new 表达式实例化该类。如果该类尚未初始化,则初始化这个类。

  • 返回:此对象所表示的类的一个新分配的实例。
  • 抛出:
  • IllegalAccessException - 如果该类或其 null 构造方法是不可访问的。
  • InstantiationException - 如果此 Class 表示一个抽象类、接口、数组类、基本类型或 void; 或者该类没有 null 构造方法; 或者由于其他某种原因导致实例化失败。
  • ExceptionInInitializerError - 如果该方法引发的初始化失败。
  • SecurityException - 如果存在安全管理器 s,并满足下列任一条件:
    (1)调用 s.checkMemberAccess(this, Member.PUBLIC) 拒绝创建该类的新实例
    (2)调用者的类加载器不同于也不是当前类的类加载器的一个祖先,并且对 s.checkPackageAccess() 的调用拒绝访问该类的包

通过反射机制实例化一个类的对象

有两种方法:

  • 1、直接通过Class对象的newInstance()方法调用类的默认构造方法实例化对象
  • 2、通过Class对象获取想要的Constructor,再通过该Constructor对象的newInstance()方法实例化对象
package net.xsoftlab.baike;
import java.lang.reflect.Constructor;
public class TestReflect {
    public static void main(String[] args) throws Exception {
        Class<?> class1 = null;
        class1 = Class.forName("net.xsoftlab.baike.User");
        // 第一种方法,实例化默认构造方法,调用set赋值
        User user = (User) class1.newInstance();
        user.setAge(20);
        user.setName("Rollen");
        System.out.println(user);
        // 结果 User [age=20, name=Rollen]
        // 第二种方法 取得全部的构造函数 使用构造函数赋值
        Constructor<?> cons[] = class1.getConstructors();
        // 查看每个构造方法需要的参数
        for (int i = 0; i < cons.length; i++) {
            Class<?> clazzs[] = cons[i].getParameterTypes();
            System.out.print("cons[" + i + "] (");
            for (int j = 0; j < clazzs.length; j++) {
                if (j == clazzs.length - 1)
                    System.out.print(clazzs[j].getName());
                else
                    System.out.print(clazzs[j].getName() + ",");
            }
            System.out.println(")");
        }
        // 结果
        // cons[0] (java.lang.String)
        // cons[1] (int,java.lang.String)
        // cons[2] ()
        user = (User) cons[0].newInstance("Rollen");
        System.out.println(user);
        // 结果 User [age=0, name=Rollen]
        user = (User) cons[1].newInstance(20, "Rollen");
        System.out.println(user);
        // 结果 User [age=20, name=Rollen]
    }
}
class User {
    private int age;
    private String name;
    public User() {
        super();
    }
    public User(String name) {
        super();
        this.name = name;
    }
    public User(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "User [age=" + age + ", name=" + name + "]";
    }
}

getClassLoader()

public ClassLoader getClassLoader()

返回该类的类加载器。有些实现可能使用 null 来表示引导类加载器。如果该类由引导类加载器加载,则此方法在这类实现中将返回 null。
如果存在安全管理器,并且调用者的类加载器不是 null,也不同于或是请求其类加载器的类的类加载器的祖先,则此方法通过 RuntimePermission(“getClassLoader”) 权限调用此安全管理器的 checkPermission 方法,以确保可以访问该类的类加载器。

如果此对象表示一个基本类型或 void,则返回 null。

返回: 加载此对象所表示的类或接口的类加载器。
抛出: SecurityException - 如果存在安全管理器,并且 checkPermission 方法拒绝对该类类加载器的访问。

判断一个类是否用户自定义类

// 判断一个类是JAVA类型还是用户定义类型,true:java类型,false:自定义类
public static boolean isJavaClass(Class<?> clz) {
   return clz != null && clz.getClassLoader() == null;
}

public static void main(String[] args) {
   System.out.println(isJavaClass(Integer.class)); // true
   System.out.println(isJavaClass(BugMeNot.class)); // false
}

为什么getClassLoader()为null是JAVA类型?

这里就要提到JVM的类加载机制:Bootstrap classLoader。

Bootstrap classLoader:采用native code实现,是JVM的一部分,主要加载JVM自身工作需要的类,如java.lang.*、java.uti.*等; 这些类位于$JAVA_HOME/jre/lib/rt.jar。Bootstrap ClassLoader不继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。

BootStrap ClassLoader加载rt.jar下的文件,也就是java最最核心的部分;然后由Extension ClassLoader加载ext下的文件;再有App ClassLoader加载用户自己的文件。

由于BootStrap ClassLoader是用c++写的,所以在返回该ClassLoader时会返回null。

判断一个类是JAVA类型还是用户定义类型
https://blog.csdn.net/cckevincyh/article/details/81417461


isAssignableFrom() 是否是参数的父类

public boolean isAssignableFrom(Class<?> cls)

判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。如果是则返回 true;否则返回 false。如果该 Class 表示一个基本类型,且指定的 Class 参数正是该 Class 对象,则该方法返回 true;否则返回 false。
特别地,通过身份转换或扩展引用转换,此方法能测试指定 Class 参数所表示的类型能否转换为此 Class 对象所表示的类型。有关详细信息,请参阅 Java Language Specification 的第 5.1.1 和 5.1.4 节。

参数: cls - 要检查的 Class 对象
返回: 表明 cls 类型的对象能否赋予此类对象的 boolean 值
抛出: NullPointerException - 如果指定的 Class 参数为 null。

@Test
public void test() {
    System.out.println(String.class.isAssignableFrom(Object.class)); // false
    System.out.println(Object.class.isAssignableFrom(String.class)); // true
    System.out.println(Object.class.isAssignableFrom(Object.class)); // true
}

getGenericSuperclass() 获取泛型父类的Type对象

获取直接父类的 Type 类型,如果父类是泛型,返回的是 ParameterizedType

类结构如下:

static class BaseClass<T, E> {
}

interface BaseInterface<C, D> {
}

static class SubClassA
        extends BaseClass<String, Character>
        implements BaseInterface<Integer, Long> {
}
/**
 * 获取父类泛型信息
 */
@Test
public void testGetSuperClassGenericInfo() {
    SubClassA subClassA = new SubClassA();
    ParameterizedType parameterizedType = (ParameterizedType) subClassA.getClass().getGenericSuperclass();
    System.out.println(subClassA.getClass().getSuperclass());
    System.out.println(parameterizedType);
    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
    Arrays.stream(actualTypeArguments).forEach(System.out::println);
}

结果:

class com.masikkk.common.base.GenericTest2$BaseClass
com.masikkk.common.base.GenericTest2$BaseClass<java.lang.String, java.lang.Character>
class java.lang.String
class java.lang.Character

getGenericInterfaces() 获取泛型接口的Type对象

/**
 * 获取接口泛型信息
 */
@Test
public void testGetInterfaceGenericInfo() {
    SubClassA subClassA = new SubClassA();
    Arrays.stream(subClassA.getClass().getInterfaces()).forEach(System.out::println);
    // 可以实现多个接口,所以是数组
    Arrays.stream(subClassA.getClass().getGenericInterfaces()).forEach(type -> {
        ParameterizedType parameterizedType = (ParameterizedType) type;
        System.out.println(parameterizedType);
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        Arrays.stream(actualTypeArguments).forEach(System.out::println);
    });
}

结果:

interface com.masikkk.common.base.GenericTest2$BaseInterface
com.masikkk.common.base.GenericTest2$BaseInterface<java.lang.Integer, java.lang.Long>
class java.lang.Integer
class java.lang.Long

Constructor类

java.lang.reflect.Constructor<T>

public final class Constructor<T>
extends AccessibleObject
implements GenericDeclaration, Member

Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。

getParameterTypes()

public Class<?>[] getParameterTypes()
按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor 对象所表示构造方法的形参类型。如果底层构造方法不带任何参数,则返回一个长度为 0 的数组。

newInstance()

public T newInstance(Object... initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。个别参数会自动解包,以匹配基本形参,必要时,基本参数和引用参数都要进行方法调用转换。
如果底层构造方法所需形参数为 0,则所提供的 initargs 数组的长度可能为 0 或 null。
如果构造方法的声明类是非静态上下文的内部类,则构造方法的第一个参数需要是封闭实例;请参阅Java 语言规范 第 15.9.3 节。
如果所需的访问检查和参数检查获得成功并且实例化继续进行,这时构造方法的声明类尚未初始化,则初始化这个类。
如果构造方法正常完成,则返回新创建且已初始化的实例。

  • 参数:initargs - 将作为变量传递给构造方法调用的对象数组;基本类型的值被包装在适当类型的包装器对象(如 Float 中的 float)中。
  • 返回:通过调用此对象表示的构造方法来创建的新对象
  • 抛出:
  • IllegalAccessException - 如果此 Constructor 对象实施 Java 语言访问控制并且底层构造方法是不可访问的。
  • IllegalArgumentException - 如果实参和形参的数量不同;如果基本参数的解包转换失败;如果在可能的解包后,无法通过方法调用转换将参数值转换为相应的形参类型;如果此构造方法属于枚举类型。
  • InstantiationException - 如果声明底层构造方法的类表示抽象类。
  • InvocationTargetException - 如果底层构造方法抛出异常。
  • ExceptionInInitializerError - 如果此方法引发的初始化失败。

Method 类

java.lang.reflect.Method

public final class Method
extends AccessibleObject
implements GenericDeclaration, Member

Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。

getModifiers()

public int getModifiers()
以整数形式返回此 Method 对象所表示方法的 Java 语言修饰符。应该使用 Modifier 类对修饰符进行解码。

getReturnType()

public Class<?> getReturnType()
返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。

getName()

public String getName()
以 String 形式返回此 Method 对象表示的方法名称。

getParameterTypes()

public Class<?>[] getParameterTypes()
按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。如果底层方法不带参数,则返回长度为 0 的数组。

getExceptionTypes()

public Class<?>[] getExceptionTypes()
返回 Class 对象的数组,这些对象描述了声明将此 Method 对象表示的底层方法抛出的异常类型。如果此方法没有在其 throws 子句中声明异常,则返回长度为 0 的数组。

getDeclaringClass() 返回方法所在的类

public Class<?> getDeclaringClass()
返回方法所在的类

invoke()

public Object invoke(Object obj, Object... args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。
如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。
如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。
如果底层方法是实例方法,则使用动态方法查找来调用它,这一点记录在 Java Language Specification, Second Edition 的第 15.12.4.4 节中;在发生基于目标对象的运行时类型的重写时更应该这样做。
如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。

  • 参数:
  • obj - 从中调用底层方法的对象
  • args - 用于方法调用的参数
  • 返回:
    使用参数 args 在 obj 上指派该对象所表示方法的结果。
    如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素不 被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null。
  • 抛出:
  • IllegalAccessException - 如果此 Method 对象强制执行 Java 语言访问控制,并且底层方法是不可访问的。
  • IllegalArgumentException - 如果该方法是实例方法,且指定对象参数不是声明底层方法的类或接口(或其中的子类或实现程序)的实例;如果实参和形参的数量不相同;如果基本参数的解包转换失败;如果在解包后,无法通过方法调用转换将参数值转换为相应的形参类型。
  • InvocationTargetException - 如果底层方法抛出异常。
  • NullPointerException - 如果指定对象为 null,且该方法是一个实例方法。
  • ExceptionInInitializerError - 如果由此方法引起的初始化失败。

通过反射机制调用某个类的方法

package net.xsoftlab.baike;
import java.lang.reflect.Method;
public class TestReflect {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
        // 调用TestReflect类中的reflect1方法
        Method method = clazz.getMethod("reflect1");
        method.invoke(clazz.newInstance());
        // Java 反射机制 - 调用某个类的方法1.
        // 调用TestReflect的reflect2方法
        method = clazz.getMethod("reflect2", int.class, String.class);
        method.invoke(clazz.newInstance(), 20, "张三");
        // Java 反射机制 - 调用某个类的方法2.
        // age -> 20. name -> 张三
    }
    public void reflect1() {
        System.out.println("Java 反射机制 - 调用某个类的方法1.");
    }
    public void reflect2(int age, String name) {
        System.out.println("Java 反射机制 - 调用某个类的方法2.");
        System.out.println("age -> " + age + ". name -> " + name);
    }
}

Field类

java.lang.reflect.Field

public final class Field
extends AccessibleObject
implements Member

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

getModifiers()

public int getModifiers()
以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符(例如private, final, static等)。应该使用Modifier类的对这些修饰符进行解码。

getType()

public Class<?> getType()
返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。

getName()

public String getName()
返回此 Field 对象表示的字段的名称。

set()

public void set(Object obj, Object value)
将指定对象变量上此 Field 对象表示的字段设置为指定的新值。如果底层字段的类型为基本类型,则对新值进行自动解包。
进行此操作的方式如下:
如果底层字段是静态字段,则忽略 obj 变量;它可能为 null。
否则底层字段是一个实例字段。如果指定对象变量为 null,则该方法将抛出一个 NullPointerException。如果指定对象变量不是声明底层字段的类或接口的实例,则该方法将抛出一个 IllegalArgumentException。
如果此 Field 对象实施 Java 语言访问控制,并且底层字段是不可访问的,则该方法将抛出一个 IllegalAccessException。
如果底层字段为 final 字段,则该方法将抛出一个 IllegalAccessException,除非 setAccessible(true) 已经继承该字段并且该字段是一个非静态字段。在通过程序的其他部分可以访问类的实例之前,只有使用空白 final 字段反序列化或重构类的实例期间,以这种方式设置 final 字段才有意义。在其他任何上下文中使用该方法都可能会有不可预知的结果,包括程序的其他部分继续使用该字段的原始值的情况。
如果底层字段的类型为某一基本类型,则可以尝试使用解包转换将新值转换为基本类型的值。如果该尝试失败,则此方法将抛出一个 IllegalArgumentException。
如果在进行可能的解包之后,无法通过某一标识或扩展转换将新值转换为底层字段的类型,则此方法将抛出一个 IllegalArgumentException。
如果底层字段是静态的,并且声明该字段的类尚未初始化,则初始化这个类。
字段被设置为可能已解包并扩大的新值。
如果字段隐藏在 obj 的类型中,则根据前面的规则设置字段的值。

  • 参数:
  • obj - 应该修改其字段的对象
  • value - 正被修改的 obj 的字段的新值
  • 抛出:
  • IllegalAccessException - 如果底层字段是不可访问的。
  • IllegalArgumentException - 如果指定对象不是声明底层字段(或者其子类或实现者)的类或接口的实例,或者解包转换失败。
  • NullPointerException - 如果指定对象为 null 并且字段是一个实例字段。
  • ExceptionInInitializerError - 如果此方法引起的初始化失败。

get()

public Object get(Object obj)
返回指定对象上此 Field 表示的字段的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。
底层字段的值是按以下方式获得的:
如果底层字段是一个静态字段,则忽略 obj 变量;它可能为 null。
否则,底层字段是一个实例字段。如果指定的 obj 变量为 null,则该方法将抛出一个 NullPointerException。如果指定对象不是声明底层字段的类或接口的实例,则该方法将抛出一个 IllegalArgumentException。
如果此 Field 对象强制实施 Java 语言访问控制,并且底层字段是不可访问的,则该方法将抛出一个 IllegalAccessException。如果底层字段是静态的,并且声明该字段的类尚未初始化,则初始化这个类。
否则,从底层实例字段或静态字段中获取该值。如果该字段是一个基本类型字段,则在返回前将该值包装在一个对象中,否则照原样返回。
如果字段隐藏在 obj 的类型中,则根据前面的规则获得字段的值。

  • 参数:
  • obj - 从中提取所表示字段的值的对象
  • 返回:
  • 对象 obj 中的所表示字段的值;在返回之前,基值包装在一个适当的对象中
  • 抛出:
  • IllegalAccessException - 如果底层字段是不可访问的。
  • IllegalArgumentException - 如果指定对象不是声明底层字段(或者其子类或实现者)的类或接口的实例。
  • NullPointerException - 如果指定对象为 null 并且字段是一个实例字段。
  • ExceptionInInitializerError - 如果此方法引起的初始化失败。

通过反射机制操作某个类的属性

package net.xsoftlab.baike;
import java.lang.reflect.Field;
public class TestReflect {
    private String proprety = null;
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
        Object obj = clazz.newInstance();
        // 可以直接对 private 的属性赋值
        Field field = clazz.getDeclaredField("proprety");
        field.setAccessible(true);
        field.set(obj, "Java反射机制");
        System.out.println(field.get(obj));
    }
}

AccessibleObject类

java.lang.reflect.AccessibleObject

public class AccessibleObject
extends Object
implements AnnotatedElement

AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。
它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。
在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。

setAccessible()

public void setAccessible(boolean flag)
将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。
抛出: SecurityException - 如果请求被拒绝。

当反射对象的 accessible 标志设为 true 时,则表示反射的对象在使用时应该取消 Java 语言访问检查。反之则检查。
由于JDK的安全检查耗时较多,所以通过 setAccessible(true) 的方式关闭安全检查来提升反射速度。

通过反射在类外访问类的private属性/方法/构造方法?

获取对应的 Method 或 Field 对象后,通过 setAccessible(true) 设为可访问的,即取消访问检查,就行了。

public class ReflectTest {
    @Test
    public void testAccessPrivateField() throws Exception {
        Person person = new Person("masikkk", 28);

        // 访问 private 成员变量
        Field nameField = Person.class.getDeclaredField("name");
        nameField.setAccessible(true);
        System.out.println(nameField.get(person));
        nameField.set(person, "madaimeng");
        System.out.println(nameField.get(person));

        // 访问 private 方法
        Method happyBirthdayMethod = Person.class.getDeclaredMethod("happyBirthday", String.class);
        happyBirthdayMethod.setAccessible(true);
        System.out.println(happyBirthdayMethod.invoke(person, " 生日快乐"));
    }
}

class Person {
    private String name;
    private int age;

    Person() {
    }

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private String happyBirthday(String word) {
        return age + word;
    }
}

PropertyDescriptor类

java.beans.PropertyDescriptor

public class PropertyDescriptor
extends FeatureDescriptor

PropertyDescriptor()

public PropertyDescriptor(String propertyName, Class<?> beanClass) throws IntrospectionException

通过调用 getFoo 和 setFoo 存取方法,为符合标准 Java 约定的属性构造一个 PropertyDescriptor。
因此如果参数名为 “fred”,则假定 writer 方法为 “setFred”,reader 方法为 “getFred”(对于 boolean 属性则为 “isFred”)。注意,属性名应该以小写字母开头,而方法名称中的首写字母将是大写的。

参数:
propertyName - 属性的编程名称。
beanClass - 目标 bean 的 Class 对象。例如 sun.beans.OurButton.class。
抛出: IntrospectionException - 如果在内省期间发生异常。

getReadMethod

public Method getReadMethod()

获得应该用于读取属性值的方法。
返回: 应该用于读取属性值的方法。如果无法读取该属性,则可能返回。

通过PropertyDescriptor的getReadMethod方法读取属性值

public void printFieldValue(Class<?> clazz) {
  Field[] fields = clazz.getDeclaredFields();
  for (int i=0; i<fields.length; i++) {
      PropertyDescriptor pd = new PropertyDescriptor(fields[i].getName(), clazz);
      Method getMethod = pd.getReadMethod();
      if(getMethod.invoke(person) != null) {
          System.out.println(fields[i].getName()+"----"+getMethod.invoke(person));
      }
  }
}

getWriteMethod

public Method getWriteMethod()

获得应该用于写入属性值的方法。
返回: 应该用于写入属性值的方法。如果无法写入该属性,则可能返回。


上一篇 CSS-基础

下一篇 Java-Annotation

阅读
评论
13.2k
阅读预计54分钟
创建日期 2017-03-18
修改日期 2023-08-28
类别
目录
  1. 概述
  2. 反射工具
    1. ReflectionUtils Spring反射工具类
      1. findField 查询字段(包含private及父类private字段)
    2. reflections 开源库
      1. 获取带有指定注解的类
      2. 获取带有指定注解的全部方法
  3. Class类
    1. 获取Class对象
      1. 获取Class对象的三种方法示例
    2. getName()
    3. forName()
      1. Class.forName()用法详解
    4. isPrimitive()
      1. 判断Class是否Java基本类型
    5. getDeclaredFields() 返回本类声明的字段(包含private,不包含父类的)
      1. 判断一个bean的所有属性是否都为空
      2. Java反射获取属性上的注解内容
      3. getDeclaredFields()按声明顺序获得属性列表
      4. 获取某个类的全部属性
    6. getDeclaredField() 按名称查找本类字段
    7. getFields() 返回所有public字段(包含父类的)
    8. getField() 按名称查找public字段(包含父类的)
    9. getDeclaredMethods() 返回所有本类声明的方法
      1. getDeclaredMethods()和getMethods()的区别
      2. 获取某个类的全部方法
    10. getMethods() 返回所有public方法
    11. getMethod() 根据名称和参数查找public方法
    12. getDeclaredMethod() 根据名称和参数查找本类方法
    13. getConstructors()
    14. getAnnotation() 获取类上的指定注解
    15. newInstance()
      1. 通过反射机制实例化一个类的对象
    16. getClassLoader()
      1. 判断一个类是否用户自定义类
    17. isAssignableFrom() 是否是参数的父类
    18. getGenericSuperclass() 获取泛型父类的Type对象
    19. getGenericInterfaces() 获取泛型接口的Type对象
  4. Constructor类
    1. getParameterTypes()
    2. newInstance()
  5. Method 类
    1. getModifiers()
    2. getReturnType()
    3. getName()
    4. getParameterTypes()
    5. getExceptionTypes()
    6. getDeclaringClass() 返回方法所在的类
    7. invoke()
      1. 通过反射机制调用某个类的方法
  6. Field类
    1. getModifiers()
    2. getType()
    3. getName()
    4. set()
    5. get()
      1. 通过反射机制操作某个类的属性
  7. AccessibleObject类
    1. setAccessible()
    2. 通过反射在类外访问类的private属性/方法/构造方法?
  8. PropertyDescriptor类
    1. PropertyDescriptor()
    2. getReadMethod
      1. 通过PropertyDescriptor的getReadMethod方法读取属性值
    3. getWriteMethod

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论