Java【反射】

目录

1、什么是反射

2、必须一定了解的基础

3、怎么运用反射


1、什么是反射

反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并获得类信息。

举个栗子?来理解:

已知某个类A,类A中有个无参的方法print,该类A完成类名为"com.zsh.reflect.A"(字符串)

例子1:
A a = new A();
a.print();
例子2:
public static void main(String[] args) throws Exception{
    Class c = Class.forName("com.zsh.reflect.A");//获得类对象
    Method method = c.getMethod("print", new Class[] {});//获得无参方法
    //或者Method method = c.getMethod("print");//获得无参方法
    Constructor constructor = c.getConstructor();//获得构造函数
    Object object = c.newInstance();//创建该类的对象实例
    //object 类是所有类的父类,包括Class类
    method.invoke(object, new Object[]{});//调用无参的print方法
}

上面两个例子中,例1知道要运行的类,而例2是在运行时通过字符串才得知要运行的类,即在一开始并不知道要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。反射就是像例2那样在运行时刻动态加载类,从反射中获得类对象,进而得到需要的类信息。

※ 例子2中一个要注意的点:print方法一定要定义为public,否则无法用getMethod()方法获得。若不添加上public,则用getDeclaredMethod()获得方法(这里看不懂的先往下看会有讲解的)

 

2、必须一定要了解的基础

在面向对象里,万事万物皆对象

类也是对象,是 java.lang.Class 类的实例对象。

任何一个类都是Class类的实例对象,这个实例对象有三种表示方式:(设类名为Foo)【这里是重点】

  • 任何一个类都有一个隐含的静态成员变量class   eg:Class c1 = Foo.Class
  • 已知该类的对象,通过getClass方法   eg:Class c2 = foo1.getClass()
  • 已知完整类名   eg:Class c3 = null;  c3 = Class.forName("com.imooc.reflect.Foo");

c1 == c2 == c3 都代表了Foo类的类类型(即Class的对象),一个类只可能是Class类的一个实例对象

完全可以通过类的类类型创建该类的对象实例(即通过c1 / c2 / c3来创建Foo类的实例)

Foo foo = (Foo)c1.newInstance();// 前提:需要有无参构造函数

是什么类的类类型,就是什么类的对象,需强制类型转换

 

Class.forName("类全称") 不仅表示了类的类类型,还代表了动态加载类

  • 编译时刻加载类:静态加载类。new创建对象是静态加载类,在编译时刻就需要加载所有可能使用到的类。
  • 运行时刻加载类:动态加载类。通过动态加载类,在运行时加载可解决该问题,写一个接口(功能性类)。

 

例子:
Class c1 = int.class;// int的类类型                                    Class c2 = String.class;// String类的类类型
Class c3 = double.class;// double数据类型的类类型        Class c4 = Double.class;//Double类的类类型
Class c5 = void.class;
打印:
c1.getName() → int     c2.getName() → java.lang.String     c5.getName() → void
c2.getSimpleName() → String(得到不包含包名的类的名称)

 

3、怎么运用反射

要获取类的信息,首先要获取类的类类型(类信息eg:类名、类方法、类成员变量、类构造函数等等)

Class c = obj.getClass();// (Object是一切类的父类)传递的是哪个子类的对象,c就是该类的类类型

【1】获取类的名称:c.getName()

【2】获取类的方法:Method[]  ms = c.getMethods;// 或 c.getDeclaredMethods()

【3】获取方法的返回值类型的类类型:Class returnType = ms[i].getReturnType(); returnType.getName();
         获取方法名称:ms[i].getName()
         获取参数类型(即参数列表的类型的类类型)Class[] paramTypes = ms[i].getParameterTypes();
                                                                                 for(Class class1:paramTypes){ class1.getName();}

 

(1)获取类的成员变量的信息

成员变量也是对象,是java.lang.reflect.Field类的对象,Field类封装了关于成员变量的操作

  • getField():获取的是所有的public的成员变量的信息
  • getDeclaredFields():获取的是该类自己声明的成员变量的信息
Field[] fs = c.getDeclaredFields();
for(Field field:fs){ //使用for循环
    Class FieldType = field.getType();
    String typeName = fieldType.getName(); 得成员变量的类型的类类型
    String fieldName = field.getName(); 得成员变量名称
}

(2)获取类的构造函数的信息

打印对象的构造函数的信息,首先获取它的类类型,构造函数也是对象,java.lang.Constructor中封装了构造函数的信息

  • getConstructors:获取所有的public的构造函数。eg:Constructor[]  cs = c.getConstructors();
  • getDeclaredConstructors:得到所有自己声明的构造函数。eg:constructor.getName();

获取构造函数的参数列表(得参数列表的类类型)
Class[] paramTypes = constructor.getParameterTypes();for(Class class1:paramTypes){ class1.getName();}

(3)获取类的某个方法的信息

方法的名称和方法的参数列表才能唯一决定某个方法。要获取方法,就是获取类的信息,首先要获取类类型。方法也是对象

  • getMethods:返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口的公共 member 方法。获得所有的public方法(包括继承的类或接口的方法)。
  • getDeclaredMethods:返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。获得所有不包括继承的方法。所有该类自己声明的方法。
A a1 = new A();
Class c = a1.getClass();
Method m = c.getMethod("method_name", new Class[]{int.Class, int.Class});
Method m = c.getMethod("method_name", int.Class, int.Class); //method_name为方法名

获取方法时要try-catch,可能会抛出异常,因为方法可能不存在
上述例子中,用m对象来进行方法调用,和a1.method_name调用效果相同

method.invoke(对象,参数列表):通过调用invoke方法来执行对象的某个方法

  • 有参:Object o = m.invoke(a1, new Object[]{x1,x2});//x1和x2为参数
  • 无参:Method m2 = c.getMethod("method_name2", new Class[]{});  m2.invoke(a1, new Object[]{});

 

※ 反射的操作都是编译之后的操作,运行时才执行

举个栗子?:

ArrayList list1 = new ArrayList();
Class c1 = list1.getClass();

ArrayList<String> list2 = new ArrayList<String>();
Class c2 = list2.getClass();

c1 == c2的结果为true,说明编译之后集合的泛型是去泛型化的
Java中的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了(验证:可以通过方法的反射来操作,绕过编译)
若用list1.add(20);//无法加入,提示错误,因为泛型为String类型
若用反射:Method m = c2.getMethod("method_name", Object.class); m.invoke(list2, 20);//这时可以加入,因为反射是在运行时执行的,可以绕过编译阶段的检查,即绕过泛型,但不可用foreach遍历,因为会类型转换错误,可用System.out.println(list2);

 

推荐一个讲反射的慕课:https://www.imooc.com/learn/199

  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值