Java反射获取类的加载器、修饰符、属性、构造器、方法、注解等信息

1. 反射概念

JVM会为每一个加载到内存的class文件创建一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了类的完整的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构。
将java类的各个组成部分封装为对象,可以在程序运行过程中操作这些对象,这就是java的反射机制。
反射具体能干那些事:

1、在运行时能够获取任意类型的详细信息

2、在运行时能够创建任意引用数据类型的对象

3、在运行时读取某个注解信息

3、在运行时读取某个类的泛型实参

5、在运行时为对象的属性赋值,或者获取对象的属性的值

6、在运行时可以调用对象的方法

……

2. 获取Class对象的方式

想用反射机制执行相关操作,必须先获得类的Class对象

获取Class对象的方式:

(1)类型名.class

**前提:**若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高

**实例:**Class clazz = String.class;

(2)对象.getClass()

**前提:**已知某个类的实例,调用该实例的getClass()方法获取Class对象

**实例:**Class clazz = “java”.getClass();

(3)Class.forName(“全限定类名”)

**前提:**已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,多用于配置文件,将类名定义在配置文件中。读取文件,加载类

**实例:**Class clazz = Class.forName(“java.lang.String”);

(4)类加载器对象.loadClass(“类型全名称”)

ClassLoader cl = this.getClass().getClassLoader();

Class clazz4 = cl.loadClass(“类的全类名”);

同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

哪些类型可以有Class对象?

(1)class: 外部类,内部类(成员内部类,静态内部类,局部内部类,匿名内部类)

(2)interface:接口

(3)[]:数组,所有具有相同元素类型和维数的数组共享同一个Class 对象

(4)enum:枚举

(5)annotation:注解@interface

(6)primitive type:8种基本数据类型

(7)void

Class c1 = int.class;//基本数据类型

Class c2 = void.class;//void类型

Class c3 = String.class;//类
Class c4 = Object.class;//类
Class c5 = Class.class;//类
Class c6 = Comparable.class;//接口

//只要元素类型与维度一样,就是同一个Class
Class c7 = int[].class;
int[] arr1 = new int[5];
int[] arr2 = new int[10];
System.out.println(arr1.getClass() == c7); //true
System.out.println(arr2.getClass() == c7); //true
Class c8 = String[].class;
Class c9 = int[][].class;
System.out.println(c7 == c8); //false
System.out.println(c7 == c9); //false

Class c10 = Override.class;//注解
Class c11 = ElementType.class;//枚举

3.从Class对象中获取信息

用于测试的类:
Person类:

package com.reflect.demo;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Student类,继承Person类,实现Serializable、Comparable接口:

package com.reflect.demo;

import java.io.Serializable;

public class Student extends Person implements Serializable, Comparable<Student> {

    private String stuNo;
    private String major;

    public Student() {
    }

    public Student(String name, int age, String stuNo, String major) {
        super(name, age);
        this.stuNo = stuNo;
        this.major = major;
    }

    public String getStuNo() {
        return stuNo;
    }

    public void setStuNo(String stuNo) {
        this.stuNo = stuNo;
    }

    public String getMajor() {
        return major;
    }

    public void setMajor(String major) {
        this.major = major;
    }

    public void study() {
        System.out.println("好好学习,天天向上");
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + this.getName() + '\'' +
                ", age='" + this.getAge() + '\'' +
                ", stuNo='" + stuNo + '\'' +
                ", major='" + major + '\'' +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        return 0;
    }
}

测试类:

public class ClassInfoTest {
    private Class clazz;
    @Before
    public void test1() throws ClassNotFoundException {
        clazz = Class.forName("com.reflect.demo.Student");
        //System.out.println(clazz); //class com.reflect.demo.Student
    }
}

3.1 获取类的加载器

public ClassLoader getClassLoader():返回该类型的类加载器,返回 null 表示引导类加载器。如果此对象表示一个基本类型或 void,则返回 null

@Test
public void test2() {
    ClassLoader classLoader = clazz.getClassLoader();
    System.out.println(classLoader);

    System.out.println(String.class.getClassLoader()); //null 表示引导类加载器

    System.out.println(int.class.getClassLoader()); //null
}

3.2 获取包名和类名

public Package getPackage():获取此类的包,然后可以通过Package实例对象的 getName() 方法获取包名。

public String getName():以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。

// 获取包名和类名
@Test
public void test3() {
    //获取包名
    Package pkg = clazz.getPackage();
    System.out.println(pkg); //package com.reflect.demo
    System.out.println(pkg.getName()); //com.reflect.demo

    // 获取类名
    String name = clazz.getName();
    System.out.println(name); //com.reflect.demo.Student
    System.out.println(clazz.getSimpleName()); //Student 简单类名
    System.out.println(Comparator.class.getName()); //java.util.Comparator
    System.out.println(int.class.getName()); //int
    System.out.println(void.class.getName()); //void
    System.out.println(long[].class.getName()); //[I
    System.out.println(Object[].class.getName()); //[Ljava.lang.Object;
    System.out.println(String[][].class.getName()); //[[Ljava.lang.String;
    System.out.println(Comparator[][].class.getName()); //[[Ljava.util.Comparator;
    System.out.println(Target[][].class.getName()); //[[Ljava.lang.annotation.Target;
}

如果此Class对象表示的是非数组类型的引用类型,则返回该类的二进制名称,即包.类名。

如果此Class对象表示一个基本类型或 void,则返回该基本类型或 void 所对应的 Java 语言关键字的字符串名称。

如果此Class对象表示一个数组类型,则名字的内部形式为:表示该数组嵌套深度的一个或多个 ‘[’ 字符加元素类型名。元素类型名的编码如下:

ElementType Encoding
byteB
shortS
intI
longJ
floatF
doubleD
booleanZ
charC
class or interface“L” + 类名 + “;”

3.3 获取类型修饰符

public int getModifiers():返回此类或接口以整数编码的 Java 语言修饰符

// 获取类的修饰符
@Test
public void test4() {
    int mod = clazz.getModifiers();
    System.out.println("Student类型的修饰符的整数编码:" + mod); //1

    System.out.println("Student类的修饰符:" + Modifier.toString(mod)); //public
    System.out.println(Modifier.isPublic(mod)); //true
    System.out.println(Modifier.toString(String.class.getModifiers())); //public final
    System.out.println(Modifier.toString(ElementType.class.getModifiers())); //public final
    System.out.println(Modifier.toString(InputStream.class.getModifiers())); //public abstract
    System.out.println(Modifier.toString(Collection.class.getModifiers())); //public abstract interface
    System.out.println(Modifier.toString(Target.class.getModifiers())); //public abstract interface
}

java.lang.reflect.Modifier 类 :提供了 static 方法和常量来解码类和成员的修饰符。修饰符集被表示为整数,用不同的二进制位表示不同的修饰符。
修饰符对应的二进制,只有一位是 1 其余都是 0:

//                                          十六进制                对应的二进制
public static final int PUBLIC           = 0x00000001;           //0000 0000 0001

public static final int PRIVATE          = 0x00000002;           //0000 0000 0010

public static final int PROTECTED        = 0x00000004;           //0000 0000 0100

public static final int STATIC           = 0x00000008;           //0000 0000 1000

public static final int FINAL            = 0x00000010;           //0000 0001 0000

public static final int SYNCHRONIZED     = 0x00000020;           //0000 0010 0000

public static final int VOLATILE         = 0x00000040;           //0000 0100 0000

public static final int TRANSIENT        = 0x00000080;           //0000 1000 0000

public static final int NATIVE           = 0x00000100;           //0001 0000 0000

public static final int INTERFACE        = 0x00000200;           //0010 0000 0000

public static final int ABSTRACT         = 0x00000400;           //0100 0000 0000

public static final int STRICT           = 0x00000800;           //1000 0000 0000

public static String toString(int mod) :将传入的整数与修饰符常量编码依次进行 按位与 运算,若结果不为0,则表示有该整数对应的修饰符

public static String toString(int mod) {
    StringBuilder sb = new StringBuilder();
    int len;

    if ((mod & PUBLIC) != 0)        sb.append("public ");
    if ((mod & PROTECTED) != 0)     sb.append("protected ");
    if ((mod & PRIVATE) != 0)       sb.append("private ");

    /* Canonical order */
    if ((mod & ABSTRACT) != 0)      sb.append("abstract ");
    if ((mod & STATIC) != 0)        sb.append("static ");
    if ((mod & FINAL) != 0)         sb.append("final ");
    if ((mod & TRANSIENT) != 0)     sb.append("transient ");
    if ((mod & VOLATILE) != 0)      sb.append("volatile ");
    if ((mod & SYNCHRONIZED) != 0)  sb.append("synchronized ");
    if ((mod & NATIVE) != 0)        sb.append("native ");
    if ((mod & STRICT) != 0)        sb.append("strictfp ");
    if ((mod & INTERFACE) != 0)     sb.append("interface ");

    if ((len = sb.length()) > 0)    /* trim trailing space */
        return sb.toString().substring(0, len-1);
    return "";
}

类允许的修饰符:

private static final int CLASS_MODIFIERS =
    Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
    Modifier.ABSTRACT       | Modifier.STATIC       | Modifier.FINAL   |
    Modifier.STRICT;

接口允许的修饰符:

private static final int INTERFACE_MODIFIERS =
    Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
    Modifier.ABSTRACT       | Modifier.STATIC       | Modifier.STRICT;

构造器允许的修饰符:

private static final int CONSTRUCTOR_MODIFIERS =
    Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE;

方法允许的修饰符:

private static final int METHOD_MODIFIERS =
    Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
    Modifier.ABSTRACT       | Modifier.STATIC       | Modifier.FINAL   |
    Modifier.SYNCHRONIZED   | Modifier.NATIVE       | Modifier.STRICT;

字段允许的修饰符:

private static final int FIELD_MODIFIERS =
    Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
    Modifier.STATIC         | Modifier.FINAL        | Modifier.TRANSIENT |
    Modifier.VOLATILE;

参数允许的修饰符:

private static final int PARAMETER_MODIFIERS =
    Modifier.FINAL;

访问修饰符:

static final int ACCESS_MODIFIERS =
    Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;

3.4 获取父类和父接口

public Class<? super T> getSuperclass():返回表示此 Class 对象所表示的实体(类、接口、基本类型或 void)的超类的 Class。如果此 Class 表示 Object 类、一个接口、一个基本类型或 void,则返回 null。如果此对象表示一个数组类型,则返回表示 Object 类的 Class 对象。

public Class<?>[] getInterfaces():返回此 Class 对象所表示的类或接口实现的接口。如果此对象表示一个类,则返回值是一个数组,它包含了表示该类所实现的所有接口的对象。数组中接口对象顺序与声明时接口名顺序一致。 如果此对象表示一个不实现任何接口的类或接口,或表示一个基本类型或 void,则此方法返回一个长度为 0 的数组

//获取父类
@Test
public void test5() {
    Class superclass = clazz.getSuperclass();
    System.out.println(superclass); //class com.reflect.demo.Person
    System.out.println(Object.class.getSuperclass()); //null
    System.out.println(Comparator.class.getSuperclass()); //null
    System.out.println(int.class.getSuperclass()); //null

    System.out.println(String[].class.getSuperclass()); //class java.lang.Object
    System.out.println(Student[][].class.getSuperclass()); //class java.lang.Object
    System.out.println(Comparator[][].class.getSuperclass()); //class java.lang.Object
}
//获取父接口
@Test
public void test6() {
    Class[] interfaces = clazz.getInterfaces();
    System.out.println(Arrays.toString(interfaces)); //[interface java.io.Serializable, interface java.lang.Comparable]
    System.out.println(Arrays.toString(List.class.getInterfaces())); //[interface java.util.Collection]
}

3.5 获取类的成员变量

所有的类型在内存中都是Class对象,每一个成员变量都是一个Field对象。

public Field[] getFields():返回此 Class 对象所表示的类或接口的所有可访问公共字段(包括继承的公共字段)的数组。数组中的元素没有排序,也没有任何特定的顺序。

@Test
public void test7() {
    Field[] fields = clazz.getFields();
    int index = 0;
    for (Field field : fields) {
        index++;
        int fMod = field.getModifiers();
        System.out.println(index + ":");
        System.out.println("成员变量:" + field);
        System.out.println("成员变量的修饰符:" + Modifier.toString(fMod));
        System.out.println("成员变量的数据类型:" + field.getType().getName());
        System.out.println("成员变量的名称:" + field.getName());
    }
}

public Field[] getDeclaredFields():返回此 Class 对象所表示的类或接口所声明的所有字段的数组,包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段。数组中的元素没有排序,也没有任何特定的顺序。

@Test
public void test8() {
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        System.out.println(field);
    }
}

public Field getField(String name):返回此 Class 对象所表示的类或接口的指定名称的公共成员字段,包括继承的公共字段;如果不存在对应的字段,则会抛出 NoSuchFieldException

public Field getDeclaredField(String name):返回此 Class 对象所表示的类或接口所声明的指定名称字段,包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段;;如果不存在对应的字段,则会抛出 NoSuchFieldException

@Test
public void test9() throws NoSuchFieldException {
    Field stuNo = clazz.getDeclaredField("stuNo");
    System.out.println(stuNo);
    int fMod = stuNo.getModifiers();
    System.out.println(Modifier.toString(fMod));
}

3.6 获取构造器

public Constructor<?>[] getConstructors():获取所有公共构造方法

public Constructor<?>[] getDeclaredConstructors():获取声明的所有构造方法,包括公共、保护、默认(包)访问和私有构造方法

//获取所有公共构造方法
@Test
public void test10() {
    Constructor[] constructors = clazz.getConstructors();
    int index = 0;
    for (Constructor constructor : constructors) {
        index++;
        int cMod = constructor.getModifiers();
        System.out.println(index + ":");
        System.out.println("构造方法:" + constructor);
        System.out.println("构造方法的修饰符:" + Modifier.toString(cMod));
        System.out.println("构造方法的名称:" + constructor.getName());
        System.out.println("构造方法的形参:" + Arrays.toString(constructor.getParameters()));
    }
}

public Constructor<T> getConstructor(Class<?>... parameterTypes):根据参数类型获取公共构造方法

  • 如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException
  • 参数是可变参数,调用此方法时,
    • 如果不写参数获取的是空参构造
    • 如果写参数,给定的参数必须是参数类型对应的Class对象
      • 比如:参数:String name, int age
      • 调用此方法:String.class, int.class

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):根据参数类型获取声明的指定构造方法,包括公共、保护、默认(包)访问和私有构造方法

//根据参数类型获取公共构造方法
Constructor constructor = clazz.getConstructor(String.class, int.class, String.class, String.class);
System.out.println(constructor);

3.7 获取方法

public Method[] getMethods():获取所有公共成员方法,包括继承的公共方法

public Method[] getDeclaredMethods():获取声明的所有成员方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

public Method getMethod(String name, Class<?>... parameterTypes):获取指定名称和形参类型的的公共成员方法,包括继承的公共方法。如果是无参方法,那么parameterTypes 可以不传或者传null。因为可能存在重载的方法,所以在一个类中唯一确定一个方法,需要方法名和形参类型列表。

public Method getDeclaredMethod(String name, Class<?>... parameterTypes):获取声明的指定名称和形参类型的的成员方法,不包括继承的方法。

@Test
public void test11() {
    Method[] declaredMethods = clazz.getDeclaredMethods();
    int index = 0;
    for (Method declaredMethod : declaredMethods) {
        index++;
        int mMod = declaredMethod.getModifiers();
        System.out.println(index + ":");
        System.out.println("成员方法:" + declaredMethod);
        System.out.println("成员方法的修饰符:" + Modifier.toString(mMod));
        System.out.println("成员方法的名称:" + declaredMethod.getName());
        System.out.println("成员方法的形参:" + Arrays.toString(declaredMethod.getParameters()));
        System.out.println("成员方法的返回值类型:" + declaredMethod.getReturnType());
        Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes();
        System.out.println("成员方法抛出的异常类型:" + Arrays.toString(exceptionTypes));
    }
}

3.8 获取注解信息

public <T extends Annotation> T getAnnotation(Class<T> annotationClass):获取元素上指定类型的注解

public Annotation[] getAnnotations() :返回此元素上存在的所有注解

public Annotation[] getDeclaredAnnotations():获取某元素上存在的所有注解,不包括从父类继承的注解

//声明注解
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) //为了能够在运行期获取到注解信息,Retention的参数需设为RUNTIME
@interface MyAnnotation {
    String value();
}
//使用注解
@MyAnnotation("java")
class MyClass {
    @MyAnnotation("hello")
    private String info;
}

@Test
public void test12() throws NoSuchFieldException {
    Class MyClazz = MyClass.class;
    //获取注解对象
    MyAnnotation annotation = (MyAnnotation) MyClazz.getAnnotation(MyAnnotation.class);
    //获取注解的value属性值
    String value = annotation.value();
    System.out.println("value:" + value);

    Field info = MyClazz.getDeclaredField("info");
    MyAnnotation infoAnnotation = info.getAnnotation(MyAnnotation.class);
    System.out.println(infoAnnotation.value());
}

3.9 获取泛型

class Father<T, U> {

}

class Son extends Father<String, Integer> {

}

@Test
public void test13() {
    Class sonClass = Son.class;
    Class superclass = sonClass.getSuperclass();
    System.out.println(superclass); //此时获得的Class对象没有泛型

    //Type genericSuperclass = sonClass.getGenericSuperclass();
    ParameterizedType genericSuperclass = (ParameterizedType) sonClass.getGenericSuperclass();
    System.out.println(genericSuperclass); 
    Type[] types = genericSuperclass.getActualTypeArguments();
    for (Type type : types) {
        System.out.println(type);
    }
}

4. 用获取到的信息执行相关操作

4.1 通过反射创建类的对象

方式一:Class类中定义了方法 public T newInstance() 来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类必须有默认的无参数构造方法,而执行newInstance()方法时实际上是利用默认无参构造器来创建该类的实例。注意,构造器需给外部足够的访问权限,否则调用者会抛出 IllegalAccessException 非法访问异常

@Test
public void test12() throws IllegalAccessException, InstantiationException {
    Object o = clazz.newInstance();
    System.out.println(o);
}

方式二:先使用Class对象获取指定的Constructor对象,再调用Constructor对象的 public T newInstance(Object... args) 方法来创建该Class对象对应类的实例。通过这种方式可以选择使用某个类的指定构造器来创建实例。注意,构造器需给外部足够的访问权限,否则调用者会抛出 IllegalAccessException 非法访问异常

@Test
public void test15() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class, String.class, String.class);
    //constructor.setAccessible(true);
    Object o = constructor.newInstance("zhangsan", 15, "2015111", "计算机");
    System.out.println(o);
}

在javabean中要求提供一个public的空参构造器,原因:

  1. 便于通过反射,创建运行时类的对象
  2. 子类继承此运行时类,默认调用super()时,保证父类有此构造器

建议编写类时保留无参构造:

  1. 本类创建对象方便
  2. 子类继承时方便,子类构造器默认调用父类的无参构造器
  3. 反射创建对象方便

如果想不受访问修饰符的限制,调用任一构造器都能创建类的实例:

java.lang.reflect.AccessibleObject 类中有一个方法 public void setAccessible(boolean flag) ,参数为 true 表示取消 Java 语言访问检查,使得因为原本访问修饰符的限制无法访问的成员也可以访问;参数为 false 则表示应该执行 Java 语言访问检查

  • Constructor 类、Method 类 继承 Executable 抽象类, Executable 抽象类和 Field 类继承 AccessibleObject 类,因此Constructor 类、Method 类、 Field 类的对象都可以调用 public void setAccessible(boolean flag) 方法。
@Test
public void test15() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class, String.class, String.class);
    constructor.setAccessible(true);
    Object o = constructor.newInstance("zhangsan", 15, "2015111", "计算机");
    System.out.println(o);
}

4.2 获取或设置某个对象的属性值

获取对象的属性值

如果对象的属性是基本类型:既可以使用 public xxx getXxx(Object obj) 方法(此处的Xxx对应8种基本数据类型),也可以使用 public Object get(Object obj)方法

如果对象的属性是引用数据类型,则直接使用 public Object get(Object obj) 方法

设置对象的属性值

如果对象的属性是基本类型:既可以使用 public void setXxx(Object obj, Xxx value) 方法(此处的Xxx对应8种基本数据类型),也可以使用 public void set(Object obj, Object value)方法

如果对象的属性是引用数据类型,则直接使用 public void set(Object obj, Object value) 方法

@Test
public void test16() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
    Object o = clazz.newInstance();
    System.out.println(o);
    Field stuNo = clazz.getDeclaredField("stuNo");
    stuNo.setAccessible(true);
    //设置stuNo属性的值
    stuNo.set(o, "2015001");
    System.out.println(o);
	//获取stuNo属性的值
    Object value1 = stuNo.get(o);
    System.out.println(value1); //2015001
}

4.3 调用方法

当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()等方法获取全部方法或指定方法。每个Method对象对应一个方法,获得Method对象后,程序就可以通过该Method对象的 public Object invoke(Object obj, Object... args) 方法来调用对应方法。

  • Object 对应原方法的返回值,若原方法无返回值,此时返回null

  • 若原方法为静态方法,此时形参Object obj为null

  • 若原方法形参列表为空,则Object[] args为null

@Test
public void test15() throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
    Object o = clazz.newInstance();
    Method method = clazz.getDeclaredMethod("study"); //获取 study方法对象
    method.invoke(o); // 执行方法
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值