反射
一、反射是什么?
反射 (reflect)、在javav的java.lang.reflect包下,反射是java为我们提供的一种操作字节码文件的类库。
二、反射的用处?
通过反射机制可以动态的在程序运行时获取字节码文件(.class文件),并且针对字节码文件进行读取、修改等操作,实现动态的操作类,反射也是OOP原则的重要实现方式。大多数框架中都应用了反射,例如SSM三大框架等,
三、如何使用反射
如果要使用反射,首先需要知道反射中重要的类有那些。以及如何通过反射获取Class文件。
!!!!本文章只展示常用的基本的方法,其他方法请查看javaAPI文档
!!!!以下示例未包含获取匿名内部类的方法和枚举的方法,他日再补充
0. 反射判断类型
首先判断文件的类型(可能有些鸡肋)你也可以不用。
Class c = Class.forName("java.util.Map");
c.isInterface();//判断文件是否为 接口 类型
c.isAnnotation();//判断文件是否为 注解 类型
c.isEnum();//判断文件是否为 枚举 类型
//....
1. 反射相关类
相关类 | 作用 |
---|---|
java.lang.class | 字节码类 ,代表了整体的字节码文件 |
java.reflect.Constructor | 字节码中的构造方法的字节码 ,代表了字节码中的构造方法 |
java.reflect.Field | 字节码中的属性的字节码,代表了属性 |
java.reflect.Method | 字节码中的方法的字节码,代表类中的方法 |
2.通过反射获取类三种方式
第一种:
通过java.lang.class包下的静态方法: **forName(String s)**获取,
优点: 这种方法比较通用,可以获取绝大多的class对象,只要能读到路径就能获取。
缺点: 就是不能读取到类路径之外的
java方法:
public static Class<?> forName(String className)
throws ClassNotFoundException{
//........
}
使用示例:
Class<?> s = Class.forName("java.long.String");//获取String类的class对象
关于路径:
有时我们需要保证类在多种环境下的路径中也能被正确的找到,所以我们需要一种通用的方法,这种方法可以获取并且只能获取类路径(即:src目录为类路径根路径)下的类文件。
方法:
String path = Thread.currentThread().getContextClassLoader()
.getResource("全限定路径.后缀") //从根开始例如:com/ccnn/resource/a.properties 获取类时后缀为.class
.getPath();
//Thread.currentThread() ---->获取当前线程对象
//getContextClassLoader() ---->获取线程对象的类加载器对象
//getResource("文件名.后缀") ---->类加载器默认从当前根路径下作为起点加载资源
//getPath(); ---->获取路径
//直接以流的形式返回
InputStream inputStream= Thread.currentThread().getContextClassLoader().getResourceAsStream("全限定路径.后缀"); //直接以流的形式返回
path 即为获取到的类的 绝对 路径。
小知识点:
在执行Class.forName("java.long.String");
时会进行类加载,而静态代码块只会在类加载时执行,并且只会执行一次,
第二种:
在java中任何一个对象都拥有 getClass() 方法 通过对象 .getClass() 就能获取到类的Class字节码对象。
java方法:
public final native Class<?> getClass();
//所有类都默认继承了Object超类,也就默认继承了此方法。
使用示例:
Date time = new Date();
Class<?> c = time.getClass();//获取Date类的class对象
第三种:
在java中任何一个“数据类型”都拥有 Class 属性 通过对象 .Class 就能获取到数据类型的 Class字节码对象。
使用示例:
Class<?> c = int.class;//获取int的class对象
3.通过Class字节码对象操作字节码文件
3.1 使用Class字节码对象实例化对象
使用Class字节码对象实例化类比直接 new 类要更加灵活,比如:通过读取配置文件中的字符串,可以动态的实例化配置文件中字符串所指的对象,修改配置文件就能达到实例化任意对象的功能。完美体现了OOP原则,即:对修改关闭,对扩展开放
方法:
MyClass myClass = (MyClass) Class.forName("MyClass").newInstance();
3.2 反射获取类的修饰符
int codename = Class.forName("user").getModifiers();
String modifierName = Modifier.toString(codename);
System.out.println(modifierName);
3.3反射获取类名
String className = Class.forName("user").getName();
System.out.println(className);//user
3.4反射获取类的属性
3.4.1获取属性名
获取所有public修饰的属性名
Field[] field = Class.forName("user").getFields();//获取所有public修饰的属性
for (Field f :field){
System.out.println(f.getName());
}
获取某个public修饰的属性名
Field field = Class.forName("user").getField("name"); //获取某个public修饰的属性
System.out.println(field.getName());
获取任意修饰符所有属性名
Field[] field = Class.forName("user").getDeclaredFields();
for (Field f :field){
System.out.println(f.getName());
}
获取指定名称的任意修饰符的属性名
Field field = Class.forName("user").getDeclaredField("age");
System.out.println(field.getName());
3.4.2获取属性的数据类型
Field field = Class.forName("user").getDeclaredField("name");
System.out.println(field.getType());
3.4.3获取属性修饰符
Field field = Class.forName("user").getDeclaredField("name");
int codename = field.getModifiers(); //得到修饰符的代号
String modifierName = Modifier.toString(codename); //获取代号所指的修饰符
System.out.println(modifierName);
3.4.4获取和修改属性值
Class c = Class.forName("user"); //获取某个public修饰的属性
user user = (user)c.newInstance(); //通过反射实例化对象
Field field = c.getDeclaredField("name"); //获取某个属性
field.get(user); //获取属性值
//访问私有的属性需要在执行set()方法之前打破封装
//field.setAccessible(true);
field.set(user,"李四"); //修改值
System.out.println(user.getName()); //李四
3.5反射获取类的方法
3.5.0获取方法对象
Class c = Class.forName("user");
c.getMethods();//获取所有public修饰的方法,包括继承的。
c.getDeclaredMethods();//获取所有修饰符修饰的所有的方法,不包括继承的
/*在获取指定方法对象时分为三种情况*/
//1.无参的
c.getMethod("getAge");
//2.有参数的
c.getMethod("getAbc", String.class);//
//3.可以重载的方法,重载方法之间使用参数区分
c.getMethod("getAbc", String.class,int.class,boolean.class);
3.5.1获取方法修饰符
// Class.forName("user").getDeclaredMethods(); //获取所有的方法对象
Method method = Class.forName("user").getMethod("getAge");//获取指定的方法对象
int i = method.getModifiers(); //和获取属性修饰符一样都是调用“getModifiers()”方法,但是这两个并不是同一个类的方法
String mod = Modifier.toString(i);
System.out.println(mod); //public
3.5.2获取方法修返回值类型
Method method = Class.forName("user").getMethod("getA");
Class type = method.getReturnType(); //获取返回值类型
String name =type.getName();
System.out.println(name);
3.5.2获取方法名
Method method = Class.forName("user").getMethod("getA");
String methodName = method.getName();//获取方法名
System.out.println(methodName);
3.5.3获取方法名形式参数列表
Method method = Class.forName("user").getMethod("getA");
Parameter[] parameters = method.getParameters();//获取所有的形式参数
for (Parameter parameter :parameters){
//boolean result = parameter.isAnnotationPresent(NotNull.class);//某个注解是否存在
// parameter.getAnnotation(NotNull.class);//获取某个注解
//....获取注解的方法很多不一一列举请看**注解篇**
Type type =parameter.getParameterizedType();//获取形式参数类型
System.out.println(type.getTypeName());
}
//或
Class[] types = method.getParameterTypes();
for (Class c : types){
String name = c.getSimpleName();//
System.out.println(name);
}
3.5.4获取方法抛出的异常
Method method = Class.forName("user").getMethod("getAge");
Class[] exceptions = method.getExceptionTypes();//获取方法抛出的所有异常
for (Class exceptionClass : exceptions){
String exceptionName = exceptionClass.getSimpleName();
System.out.println(exceptionName);
}
3.5.5获取方法体的内容
//待补充
3.5.5反射构造方法
Class c = Class.forName("user");
c.getConstructors(); //获取无参构造方法且是public修饰的
c.getDeclaredConstructor(int.class,String.class); //获取指定参数的所有构造方法
c.getDeclaredConstructors(); //获取所有构造方法
//获取构造方法的修饰符、返回值、参数列表等 和以上方式相同。
3.5.6调用方法
Class c = Class.forName("user");
/*创建对象*/
Object obj = c.newInstance();
/*获取符合条件的方法*/
Method method = c.getMethod("getAge",String.class);
/*调用Obj方法,传入参数*/
method.invoke(obj,"参数值");//调用方法
4获取类的父类
Class c = Class.forName("user");
/*
如果这个类表示Object类、接口、原始类型或void、则返回null。
*/
c.getSuperclass();//获取父类
5获取类的接口
Class c = Class.forName("user");
/*
1.如果该对象是一个类,则返回该类实现的所有接口的对象,
2.如果该对象是一个接口,则返回接口扩展的所有接口的对象
3.如果此对象是一个不实现接口的类或接口,则该方法返回长度为0的数组
*/
Class[] classes = c.getInterfaces();
c.getAnnotatedInterfaces(); //返回带注解接口
/*
1.如果该对象是一个类,则返回直接实现的接口的对象
2.如果该对象是一个接口,则返回接口直接扩展的接口的对象
3.如果该对象是一个不实现接口的类或接口,则该方法返回长度为0的数组
*/
c.getGenericInterfaces();
6获取注解
6.1获取类或者接口的注解
Class c = Class.forName("user");
c.getAnnotatedInterfaces();//获取带注解的接口
c.getAnnotation(NotNull.class);//如果有这个注解则返回该注解,否则返回null指定类型的注解
c.getAnnotations(); //返回此元素上存在的注解
c.getAnnotatedSuperclass();//获取父类的注解
c.getAnnotationsByType(AnnotatedElement.class);//获取类上直接的某个类型的注解
c.getDeclaredAnnotations();//直接存在于此元素上的注解。 此方法忽略继承的注解
c.getDeclaredAnnotation(NotNull.class);//如果这样的注解在类上直接存在,则返回指定类型的元素注解,否则返回null。此方法忽略继承的注解。
c.getDeclaredAnnotationsByType(NotNull.class);//如果此类注解直接存在或间接存在,则返回该元素的注解(指定类型)。 此方法忽略继承的注解.
6.2获取属性的注解
Class c = Class.forName("user");
Field field = c.getDeclaredField("name");
field.isAnnotationPresent(NotNull.class);//如果此元素上存在指定类型的注解,则返回true,否则返回false。
field.getAnnotation(NotNull.class);//如果该元素存在这样的注解则返回该注解,否则返回null指定类型的注解。
field.getAnnotations();//返回此元素上存在的注解
field.getDeclaredAnnotation(NotNull.class);//如果这样的注解直接存在,则返回指定类型的元素注解,否则返回null
field.getDeclaredAnnotations();//返回直接存在于此元素上的注解
6.3获取有关于普通方法和构造的注解
Class c = Class.forName("user");
Method method = c.getMethod("getA", String.class);
method.isAnnotationPresent(NotNull.class);//如果此元素上存在指定类型的注解,则返回true,否则返回false
method.getAnnotation(NotNull.class);//返回该元素的存在如果这样的注解则返回该注解,否则返回null指定类型的注解。
method.getAnnotations();//返回此元素上存在的注解。
method.getAnnotatedReturnType();//表示使用类型来指定此可执行文件所表示的方法/构造函数的返回类型。
method.getDeclaredAnnotations();//返回 直接存在于此元素上的注解。
method.getDeclaredAnnotation(NotNull.class);//如果这样的注解直接存在,则返回指定类型的元素注解,否则返回null。
method.getDeclaredAnnotationsByType(NotNull.class);//如果此类注解直接存在或间接存在,则返回该元素的注解(指定类型)。此方法忽略继承的注解。如果在该元素上没有直接或间接存在的指定注解,则返回值为长度为0.的数组
method.getParameterAnnotations();//按照顺序返回形式参数列表的注解 显式的和隐式的
暂未完结~
欢迎指正,欢迎补充