目录
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对象表示一个数组类型,则名字的内部形式为:表示该数组嵌套深度的一个或多个 ‘[’ 字符加元素类型名。元素类型名的编码如下:
Element | Type Encoding |
---|---|
byte | B |
short | S |
int | I |
long | J |
float | F |
double | D |
boolean | Z |
char | C |
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的空参构造器,原因:
- 便于通过反射,创建运行时类的对象
- 子类继承此运行时类,默认调用super()时,保证父类有此构造器
建议编写类时保留无参构造:
- 本类创建对象方便
- 子类继承时方便,子类构造器默认调用父类的无参构造器
- 反射创建对象方便
如果想不受访问修饰符的限制,调用任一构造器都能创建类的实例:
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); // 执行方法
}