Java 高级开发必修知识---反射
文章开始之前 提一下:
java反射操作其实就是主要围绕Class,Field,Methon,Constructor等几个类来操作其中的方法
万物皆对象。但基本数据类型 和 使用static修饰的成员不是对象
但基本数据类型的封装类是对象
Class类的使用
1) 在面向对象的世界里,万事万物皆对象
A. Java语言中,普通数据类型,静态成员不是对象,其他皆对象
B. 每一个类也是对象
C. 类是java.lang.Class类的实例对象
There is a class named Class
对象的表示:
普通类对象表示:
Foo foo = new Foo();
Class类实例对象表示:
//Foo也是一个实例对象,是Class类的实例对象
任何一个类都是Class的实例对象,这个实例对象有三种表达方式
1. 任何一个类都有一个隐含的静态成员变量class:
Class c1 = Foo.class;
2. 已经指定该类的对象通过getClass方法
Foo foo = new Foo();
Class c2 = foo.getClass();
官网:c1/c2 表示了Foo类的类类型(class type)
一个类,本身就是一个对象,它是Class类的对象
万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们称为该类的类类型
3. ForName(“类的全称”);
Class c3 = null;
c3=Class.forName(“包名+.某类名”);
不管是那种表达方式,都是Class类的对象,都是Foo类的类类型。所以:
C1=c2=c3
完全可以通过类的类类型创建该类的对象(创建Foo的实例对象,但要做强制类型转换,向下转型)
前提要求:需要有无参数的构造方法
Comont comont = new Comont(); Class c1 = comont.getClass(); Class c2 = Comont.class; try { Comont c = (Comont) c2.newInstance(); } catch (InstantiationException | IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(c1 == c2); comont.start();
动态加载类
Class.forName(“类的全称”);
1. 不仅表示了类的类类型,还代表动态加载类
2. 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类
(1) New 对象是静态加载类,在变异时刻就需要加载所有的可能使用到的类
(2) 动态加载类,在运行时刻加载
编译不会报错
运行时刻报错。找不到该类
Class c = Class.forName(arg[0]);
共同实现接口类 cc = (共同实现接口类) c.newInstance();
3. 使用记事本开发可明显区分
Java 类 要运行类:动态加载类,不需要重新编译测试类,直接运行即可
功能性的类:尽量使用动态加载
基本数据类型也有类类型
Class c1 = int.class; Class c2 = String.class;//String类的字节码
数据类型和包装类的类类型不同
Void也是类
Class c3 = void.class;
基本数据类型
Void关键字
都存在类类型
方法也是对象,方法是Method的对象
反射:某类的字节码表示
获取方法信息
1. c.getName()
(1) 基本数据类型返回类型名
(2) 类返回包名+类名类的名称
2. c1.getSimpleName()
(1) 返回不带包名的类的名称
栗子:通过反射可以获取到任何类的信息
需求:打印类的信息,获取类的 成员函数
package cn.pro; import java.lang.reflect.Method; /** * * @author: 房上的猫 * * @time: 下午5:34:45 * * @博客地址: https://www.cnblogs.com/lsy131479/ * */ public class mymain { public static void printClassMessage(Object obj) { // 要获取类的信息 首先要获取类的类类型 // 形参obj 该对象所属类的信息 Class c = obj.getClass();// 传递的是哪个子类的对象 c就是该 子类的类类型 // getClass()方法:native修饰代表 存在不是java 代码 ,调度 外用 ///该方法java内调用底层c语言实现 // 获取累的名称 System.out.println("类的名称是:" + c.getName()); // Method类是方法对象 // 一个成员方法就是一个Method // getMethods()方法获取的是所有的public修饰的函数,包括父类 继承而来的 Method[] ms = c.getMethods(); // c.getDeclaredMethods()获取的是所有该类自己声明的方法,不 问访问权限.所有。所有。所有 String[] name = new String[ms.length]; for (int i = 0; i < ms.length; i++) { // 得到方法的返回值类型--得到的是返回值类型的类类型 Class returnType = ms[i].getReturnType(); // 得到返回值名字 String returnName = returnType.getName(); // 得到方法的名称 name[i] = ms[i].getName(); // 获取参数列表类型--得到的是参数列表的类型的类类型 Class[] parameterTypes = ms[i].getParameterTypes(); Int params=parameterTypes.length; String[] paramNames = new String[params]; for (int j = 0; j < params; j++) { // 得到参数列表名 paramNames[j] = ms[j].getName(); } } } }
通过反射可以获取任何类的类信息
比较牛逼
获取类的成员变量构造函数信息
成员变量也是对象
是Java.lang.reflect.Field的对象
Field类封装了关于成员变量的操作
栗子:通过反射可以获取到任何类的信息
需求:打印类的信息,获取类的成员变量
package cn.reflect; import java.lang.reflect.Field; /** * * @author: 房上的猫 * * @time: 下午3:49:32 * * @博客地址: https://www.cnblogs.com/lsy131479/ * */ public class myField { public static void printFieldMessage(Object obj) { Class c = obj.getClass(); // getFidlds() 方法获取的是类的所有的public的成员变量信息 Field[] fs = c.getFields(); // getDeclaredFields() 获取的是该类自己声明的成员信息 不问访问权限.所有。所有。所有 // Field[] fs = c.getDeclaredFields(); for (Field field : fs) { // 得到成员变量的类型的类类型 Class fieldType = field.getType(); // 得到成员变量类型的名字 String typeName = fieldType.getName(); // 得到成员变量的名字 String fieldName = field.getName(); // } } }
构造函数也是对象
是Java.lang.Constructor的对象 其中封装了构造函数的信息
栗子:通过反射可以获取到任何类的信息
需求:打印类的信息,获取类的构造函数信息
package cn.reflect; import java.lang.reflect.Constructor; /** * * @author: 房上的猫 * * @time: 下午3:49:32 * * @博客地址: https://www.cnblogs.com/lsy131479/ * */ public class myCon { public static void printConMessage(Object obj) { Class c = obj.getClass(); // getConstructors() 获得所有的共有的构造方法 Constructor[] cs = c.getConstructors(); // getDeclaredConstructors() 得到所有的构造方法(必须是自己声明的) 不问访问权限.所有。所有。所有 // Constructor[] cs = c.getDeclaredConstructors(); for (Constructor constructor : cs) { // 获取构造函数名 String name = constructor.getName(); // 获取构造函数的参数列表------>得到的是参数列表的类类型 Class[] parameterTypes = constructor.getParameterTypes(); for (Class class1 : parameterTypes) { // 获取构造函数参数列表名 String name2 = class1.getName(); } } } }
其他
Class类还可以获取类的其他信息,这里将不再详细讲解
想要了解的可以创建Class类的实例对象
Class c = obj.getClass();
c.get...
自行查看并尝试
或阅读帮助文档,查看Class类的所有API
记住一点:在任何情况下想要获取一个类的信息,首先要得到这个类的类类型
得到类的类类型,得到这个类的类信息就轻而易举得到了
方法的反射
1. 如何获取某个方法?
方法的名称和方法的参数列表才能唯一决定某个方法
2. 方法反射的操作
Method.invoke(对象,参数列表)
栗子:
package cn.reflect; import java.lang.reflect.Method; /** * * @author: 房上的猫 * * @time: 下午4:25:25 * * @博客地址: https://www.cnblogs.com/lsy131479/ * */ public class MethodDemo1 { public static void main(String[] args) throws Exception { // 要获取print(int,int)方法 A a = new A(); /* * 1.要获取一个方法就是要获取一个类的信息,获取类的信息首先要获取类的类类型 */ Class c = a.getClass(); /* * 2.获取方法 名称和参数列表 * * getMethod(name, parameterTypes)获取的是public的方法 * * c.getDeclaredMethod(name, parameterTypes)获取的是所有自己声明的方法 不问访问权限 * * * 参数解析: 1.name:方法名 2.parameterTypes:参数列表类型(0或多个) */ Method m = c.getMethod("print", new Class[] { int.class, int.class }); /* * 方法的反射操作 * * a.print(10,20);方法的反射操作是用m对象来进行方法调用 和a.print调用的效果 * * 方法如果没有返回值返回null 有返回值返回具体的返回值 返回object类型,需要做强制类型转换 */ Object o = m.invoke(a, new Object[] { 10, 20 }); // 就等于调用了print(int,int)方法 /* * 如果方法无参数列表则: * * c.getMethod("print"); m.invoke(a); */ } } class A { public void print(int a, int b) { System.out.println(a + b); } public void print(String a, String b) { // a的大写形式 and b的小写形式 System.out.println(a.toUpperCase() + "," + b.toLowerCase()); } }
升华操作:
通过反射了解集合泛型的本质
通过Class,Method来认识泛型的本质
相信读者们看到这里心中一定会有这样两个疑问:
什么是泛型?
泛型什么时候有效?
那我们探讨一下这两个话题:
package cn.reflect; import java.lang.reflect.Method; import java.util.ArrayList; /** * * @author: 房上的猫 * * @time: 下午6:31:38 * * @博客地址: https://www.cnblogs.com/lsy131479/ * */ public class MethodDemo2 { public static void main(String[] args) { // 普通集合 ArrayList list = new ArrayList<>(); // 泛型集合 集合内只能存放'<>'尖括号内类型的值 ArrayList<String> list1 = new ArrayList<>(); // 集合的泛型防止错误输入 // 利用反射了解集合泛型 Class c1 = list.getClass(); Class c2 = list1.getClass(); list1.add("1"); System.out.println(c1 == c2); /* * 反射的操作都是编译之后的操作(运行时) * * c1==c2结果返回true 表示两个集合类运行时都是同一类类型 * * 说明编译之后集合的泛型是去泛型化的(编译完之后就没有泛型存在了) * * java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了 * * 验证:可以通过方法的反射来操作,绕过编译 */ try { Method m = c2.getMethod("add", Object.class); m.invoke(list1, 100);// 大胆猜想:绕过编译操作就绕过了泛型 // 集合大小返回2 .说明:绕过编译阶段后可以向泛型集合添加任何类型的值 System.out.println(list1.size()); // 尝试查看泛型集合内的值 发现值已经添加进去 System.out.println(list1); /* * 在这里如果使用for增强遍历将会抛异常 * * 因为后面添加的100是int类型的值 * * 而集合泛型是String类型 * * 使用for增强遍历内部其实做出的操作是:集合的每个值都使用一个集合泛型类型的数据类型来接受遍历 * 而int类型使用String类型来接受,众所周知将会报错(这里的泛型类型是String) * * 会有类型转换错误 */ } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
结论:
反射(Class,Method,Field,,Constructor ... )的操作都是绕过编译,都是在运行时刻来执行的
凡事都是一把双刃剑,都有利弊两面,不可能有事物十全十美,反射是好,但也不要盲目使用
反射优缺点
(2)与Java动态编译相结合,可以实现无比强大的功能
缺点:
(1)使用反射的性能较低
(2)使用反射相对来说不安全
(3)破坏了类的封装性,可以通过反射获取这个类的私有方法和属性
ok...到此为止!!!后续如果有新的想法将会继续总结。。敬请期待
(C) 房上的猫 。 保留所有权利。
https://www.cnblogs.com/lsy131479/
如需转载,请注明出处!!!