前言
前面我们讲了反射对象的获取和类的加载过程,接下来我们得讲下如何使用反射的class对象!
一、获取运行时类的结构
通过反射可以获取运行时类的完整结构:
①FIeld(属性)
②Method(方法)
③Constructor(构造器)
④Superclass(超类)
⑤Interface(接口)
⑥Annotation(注解)
示例代码如下:
Person类:
import lombok.Data;
@Data
public class Person extends Object{
private String name;
private int high;
private String like;
public Integer age;
public Person(){
}
private void priTest(String name){
}
public Person(String name){
}
}
测试代码:
public class test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException {
Class c1 = Class.forName("com.dbright.Java反射.Person");//forName获取
Person person = new Person();
Class c2 = person.getClass();//对象.class获取
Class c3 = Person.class;//类.对象
//获取类的名字
System.out.println("c1"+c1.getName());
System.out.println("c2"+c2.getName());
System.out.println("c3"+c3.getName());
System.out.println("简单:"+c1.getSimpleName());
System.out.println("* ****************************************************");
//获取类的属性
Field[] fields = c1.getFields();//只能获取public属性
for (Field field : fields) {
System.out.println("getFields:"+field);
}
Field[] fields1 = c1.getDeclaredFields();//获取所有属性
for (Field field : fields1) {
System.out.println("getDeclaredFields:"+field);
}
//获取指定名称的属性
System.out.println("getDeclaredField:"+c1.getDeclaredField("name"));
System.out.println("* ****************************************************");
//获得类的方法--包含父类的方法
Method[] methods = c1.getMethods();//只能获得公共方法
for (Method method : methods) {
System.out.println("getMethods:"+method);
}
Method[] methods1 = c1.getDeclaredMethods();//获得所有方法
for (Method method : methods1) {
System.out.println("getDeclaredMethods"+method);
}
//获得指定方法
Method method = c1.getMethod("getName",null);//无参直接传个null即可
Method method1 = c1.getMethod("setName", String.class);//带参传参数的对象即可
System.out.println("获得指定方法1:"+method);
System.out.println("获得指定方法2:"+method1);
System.out.println("* ****************************************************");
//获得构造器,注释不再加,和上面一致,公共和私有
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("getConstructors:"+constructor);
}
Constructor[] constructors1 = c1.getDeclaredConstructors();
for (Constructor constructor : constructors1) {
System.out.println("getDeclaredConstructors:"+constructor);
}
//获取指定的构造器--根据参数来确定是哪一个构造器
System.out.println("获取指定的构造器:"+c1.getConstructor());
System.out.println("获取指定的构造器:"+c1.getConstructor(String.class));
}
}
执行结果:
二、动态创建对象执行方法
上面介绍了如何通过反射获取类的属性、方法和构造器等。那么如何动态的创建对象并且 执行 方法呢?
1、创建类的对象:调用Class对象的newInstance()方法
- 类必须有一个无参构造器
- 类的构造器的访问权限需要足够
2、只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作
- 通过Class类的getDeclaredConstructor(Classs … parameterTypes)取得本类的指定形参类型的构造器
- 像构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数
- 通过Constructor实例化对象
3、调用指定的方法:通过反射调用类中的方法,通过Method类完成
- 通过Class类的getMethod(String name,Class … parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型
- 之后使用Object invoke(Object obj,Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息
4、Object invoke(Object obj, Object … args)
- Object对应原方法的返回值,若原方法无返回值,此时返回null
- 若原方法为静态方法,此时形参Object obj可为null
- 若原方法形参列表为空,则Object[] args为null
- 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法
5、setAccessible
- Method和Field、Constructor对象都有setAccessible()方法
- setAccessible作用是启动和禁止访问安全检查的开关
- 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查
- 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调 用,那么请设置为true
- 使得原本无法访问的私有成员也可以访问
- 参数值为false则指示反射的对象应该实施Java语言访问检查
代码示例:
前置代码:
import lombok.Data;
@Data
public class Person extends Object{
private String name;
private int high;
private String like;
public Integer age;
public Person(){
}
private void priTest(String name){
}
public Person(String name){
this.name = name;
}
}
测试代码:
/**
* 通过反射动态的创建对象
*/
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得class对象
Class c = Class.forName("com.dbright.Java反射.Person");
//构造一个对象,本质上是调用了无参构造器
Person person = (Person) c.newInstance();
System.out.println("person:"+person);
//通过有参构造器创建对象
Constructor constructor = c.getDeclaredConstructor(String.class);
Person person1 = (Person) constructor.newInstance("ding");//需要重写tostring方法
System.out.println("person1:"+person1);
//通过反射获取和操作方法
System.out.println("************************************************************");
Person person2 = (Person) c.newInstance();
Method setLike = c.getDeclaredMethod("setLike", String.class);
setLike.invoke(person2,"钓鱼");
System.out.println("person2:"+person2);
//通过反射操作属性
System.out.println("************************************************************");
Person person3 = (Person) c.newInstance();
Field name = c.getDeclaredField("name");
//关掉权限检测:不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法
name.setAccessible(true);
name.set(person3,"丁大大");
System.out.println("name:"+name.getName());
}
}
执行结果:
性能测试:
那么这两种方法有什么区别呢?
public class Test02 {
public static void main(String[] args) throws NoSuchMethodException {
test1();
test2();
test3();
}
/**
*普通方式调用
*/
public static void test1(){
Person person = new Person();
//获取当前系统时间
long startTime = System.currentTimeMillis();
for (int i=0; i<2000000000; i++){
person.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式调用所需时间:"+(endTime-startTime)+"ms");
}
/**
*反射方式调用
*/
public static void test2() throws NoSuchMethodException {
Person person = new Person();
Class c = person.getClass();
Method getName = c.getDeclaredMethod("getName",null);
//获取当前系统时间
long startTime = System.currentTimeMillis();
for (int i=0; i<2000000000; i++){
getName.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式调用所需时间:"+(endTime-startTime)+"ms");
}
/**
*反射方式调用 关闭检测
*/
public static void test3() throws NoSuchMethodException {
Person person = new Person();
Class c = person.getClass();
Method getName = c.getDeclaredMethod("getName",null);
//获取当前系统时间
long startTime = System.currentTimeMillis();
getName.setAccessible(true);//关闭安全检测
for (int i=0; i<2000000000; i++){
getName.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式调用所需时间:"+(endTime-startTime)+"ms");
}
}
执行结果如下:
结论: 使用反射调用时效率比较低,如果需要频繁使用反射调用的前提,请关闭安全检测!!!
三、反射操作泛型
- Java采用泛型擦出的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除
- 为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型
- ParameterizedType:表示一种参数化类型,比如Collection
- GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
- TypeVariable:是各种类型变量的公共夫接口
- WildcardType:代表一种通配符类型表达式
测试代码如下:
public class Test03 {
//通过反射获取泛型
public void test01(Map<String, Person> map, List<Person> list){
System.out.println("test01");
}
public Map<String, Person> test02(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method method = Test03.class.getMethod("test01", Map.class, List.class);
Type[] geners = method.getGenericParameterTypes();
for (Type gener : geners) {
System.out.println("*" + gener);
if (gener instanceof ParameterizedType){
Type[] actuals = ((ParameterizedType)gener).getActualTypeArguments();
for (Type actual : actuals) {
System.out.println(actual);
}
}
}
method = Test03.class.getMethod("test02", null);
Type geners1 = method.getGenericReturnType();
if (geners1 instanceof ParameterizedType){
Type[] actuals = ((ParameterizedType)geners1).getActualTypeArguments();
for (Type actual : actuals) {
System.out.println(actual);
}
}
}
}
执行结果:
四、反射操作注解
前面我们学习了注解,接下来就看下注解和反射怎么使用?
public class Test04 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c = Person1.class;
//通过反射获取注解
Annotation[] annotations = c.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("annotation:"+annotation);
}
//获取注解的value的值
classAno classAno1 = (classAno) c.getAnnotation(classAno.class);
String value = classAno1.value();
System.out.println("value:"+value);
//获得类的指定注解
Field field = c.getDeclaredField("name");
methodAno methodAno1 = field.getAnnotation(methodAno.class);
System.out.println("name:"+methodAno1.name());
System.out.println("type:"+methodAno1.type());
System.out.println("length:"+methodAno1.length());
}
}
/**
* 测试类
*/
@Data
@classAno("Person1")
class Person1{
@methodAno(name = "丁大大",type = "超人类",length = 2)
private String name;
private int high;
private String like;
public Integer age;
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface classAno{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface methodAno{
String name();
String type();
int length();
}
执行结果:
四、总结:
第一遍看完可能有点懵,多看几遍加深理解吧!
在实际项目中多多使用,看看框架的底层加强自身的理解,冰冻三尺非一日之寒,有个大概的理解和印象即可!后面在实际中逐渐加强理解,这样才能融会贯通!
Java注解和反射扩展:Java注解和反射学习汇总
路漫漫其修远兮,吾必将上下求索~
如果你认为i博主写的不错!写作不易,请点赞、关注、评论给博主一个鼓励吧**转载请注明出处哦**
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/17120.html