Java反射机制
一、反射概述
1、反射简介
在一个程序加载完类后,在堆内存的方法区内就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。可以通过这个对象看到对应类的内部结构,所以称之为反射。
2、反射的作用
反射机制允许程序在执行期间借助于Refletion API获取任何类的内部信息,并能够直接操作任意对象的内部属性和方法。
二、Class类
1、Class类介绍
在 Object 类中定义了以下的方法,此方法将被所有子类继承:
public final Class getClass()
以上的方法返回值的类型是一个 Class 类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即可以通过对象反射求出类的名称。
- Class本身也是一个类
- Class对象只能由系统建立
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
2、获取Class类的实例的四种方法
1)对象获取Class类
String str="www.baidu.com";
Class clazz=str.getClass();
2)类的属性获取Class类
Class clazz=String.class;
3)全类名获取Class类
Class clazz=Class.forName("java.lang.String");
4)类加载器获取Class类(了解)
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“类的全类名”);
3、Class对象应用
3.1 创建类的对象
通过Class对象获取对应的构造器方法,然后用构造器方法创建对象:
//1、根据全类名获取对应的Class对象
String name = "java.lang.String";
Class clazz=null;
clazz = Class.forName(name);
//2、调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class);
//3、通过Constructor的实例创建对应类的对象,并初始化类的属性
String s = (String) con.newInstance("我是ssl");
System.out.println(s);
3.2 构造器相关应用
//获取全部public的构造器
Constructor[] constructors = clazz.getConstructors();
//获取全部的构造器(包括private类型的)
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
Constructor constructor = constructors[0];
//获取单个构造器的修饰符,例如public、private
constructor.getModifiers();
//获取单个构造器的方法名称
String name = constructor.getName();
//获取单个构造器的参数类型
Class[] parameterTypes = constructor.getParameterTypes();
3.3 类中方法相关应用
Class clazz = People.class;
People people=new People();
Class clazz1 = people.getClass();
Class clazz2 = Class.forName("People");
//获取Class对象所表示的类中定义的所有方法(包括private类型方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
//获取Class对象所表示的类中声明为public的方法
Method[] methods = clazz.getMethods();
Method method = methods[0];
//获取单个方法的返回值
Class<?> returnType = method.getReturnType();
//获取单个方法全部参数
Class<?>[] parameterTypes = method.getParameterTypes();
//获取单个方法的修饰符
int modifiers = method.getModifiers();
//获取单个方法的所有异常信息
Class<?>[] exceptionTypes = method.getExceptionTypes();
3.4 类中字段相关应用
Class clazz = People.class;
//获取Class对象所表示类中所有的字段
Field[] declaredFields = clazz.getDeclaredFields();
//获取Class对象所表示类中所有的public类型的字段
Field[] fields = clazz.getFields();
Field field = fields[0];
//获取单个字段的修饰符
int modifiers = field.getModifiers();
//获取单个字段的属性类型,例如int、String
Class type = field.getType();
//获取单个字段的名称
String name = field.getName();
4、Class对象调用运行时类的指定结构
4.1 调用指定的字段
Class clazz = People.class;
//创建对象
People people = (People) clazz.newInstance();
//获取name这个字段
Field name = clazz.getDeclaredField("name");
//设置属性是可访问的
name.setAccessible(true);
//获取people这个对象的name字段的值
Object o = name.get(people);
System.out.println(o);
//把people这个对象的name字段进行赋值
name.set(people,"ssl");
System.out.println(people.toString());
4.2 调用指定的方法
需要注意的是:如果调用的方法是static方法,传入的对象可以为null
Class clazz = People.class;
//创建对象
People people = (People) clazz.newInstance();
//获取people对象里的show这个方法,它没有参数
Method method = clazz.getDeclaredMethod("show");
//设置方法是可访问的
method.setAccessible(true);
//调用获取的方法
Object result = method.invoke(people);
System.out.println(result);
//调用静态方法,可以省略对象名称,通过class对象直接调用即可
Method show_static = clazz.getDeclaredMethod("show_static");
show_static.setAccessible(true);
//调用的方法是static类型的,不需要对象,所以可以传入null
Object invoke = show_static.invoke(null);
System.out.println(invoke);
4.3 调用指定的构造器
Class clazz = People.class;
//创建对象
People people = (People) clazz.newInstance();
//获取people对象里的带有单个参数的构造器
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
//设置构造器是可访问的
declaredConstructor.setAccessible(true);
//通过获取的构造器创建对象
People new_people = (People) declaredConstructor.newInstance("SSL");
System.out.println(new_people.toString());
4.4 获取相关泛型
获取运行时类的父类:
Class clazz = People.class;
//获取运行时类的父类
Class superclass = clazz.getSuperclass();
System.out.println(superclass.getName();
获取带泛型的父类和泛型的类型:
//假设People类继承了Person<String>
Class clazz = People.class;
//获取clazz表示的类的带泛型的父类,返回值是Person<String>
Type genericSuperclass = clazz.getGenericSuperclass();
//获取这个带泛型的父类的泛型类型
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = paramType.getActualTypeArguments();
//这里只有一个泛型为String,返回值是java.lang.String
String typeName = actualTypeArguments[0].getTypeName();
//也可以如下方式
String name = ((Class) actualTypeArguments[0]).getName();
4.5 获取相关注解
自定义注解类:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablemi{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldmi{
String name();
String type();
int length();
}
定义实体类:
@Tablemi("mi")
class Student2 {
@Fieldmi(name = "id", type = "int", length = 10)
private int id;
@Fieldmi(name = "age", type = "int", length = 3)
private int age;
@Fieldmi(name = "name", type = "varchar", length = 5)
private String name;
public Student2() {
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
}//并提供get和set方法
获取类/字段/方法的所有注解:
Class c1 = Class.forName("Student2");
//通过反射获取注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
获取对应的某个注解:
//获的注解的value值
Tablemi tablemi = (Tablemi)c1.getAnnotation(Tablemi.class);
String value = tablemi.value();
System.out.println(value);
//获得字段指定的注解
Field field = c1.getDeclaredField("id");
Fieldmi fieldmi = field.getAnnotation(Fieldmi.class);
System.out.println(fieldmi.name());
System.out.println(fieldmi.type());
System.out.println(fieldmi.length());
判断注解是否存在:
boolean annotationPresent = c1.isAnnotationPresent(Tablemi.class);
System.out.println(annotationPresent);
4.6 其他API
方法 | 说明 |
---|---|
public Class<?>[] getInterfaces() | 获取此对象表示的类实现的接口 |
public Class<? Super T> getSuperclass() | 获取此对象表示的类的父类的Class |
Package getPackage() | 获取此对象表示的类所在的包 |
5、关于setAccessible方法的使用
方法、字段、构造器都有setAccessible()方法。
setAccessible用于启动和禁用访问安全检查的开关。
参数值为true表示反射的对象在使用的时候应该取消Java语言的访问检查。优点是:提高反射的效率,如果代码中必须使用反射,而该句代码需要频繁的被调用,那么就最好设置为true;第二是使得原本无法访问的私有成员、方法、构造器也可以访问。
参数值为false时则表示反射的对象应该实施Java语言访问检查,如果为private等则不可以被访问。