1、Class类
在面向对象的世界里,万事万物皆对象。类是对象,类是java.lang.Class类的实例对象。
任何一个类都是Class的实例对象,这个实例对象有三种表示方法:
//1、任何一个类都有一个隐含的静态成员class Class c1 = Foo.class; //2、已经知道该类的对象,通过getClass方法 Class c2 = f.getClass(); /** * c1,c2表示了Foo类的类类型(Class Type) * 类是class类的实例对象 * 我们称这个对象为该类的的类类 */ //3、通过Class.forName方法 Class c3 = null; try { c3 = Class.forName("cn.fedomn.demo.Foo"); } catch (Exception e) { e.printStackTrace(); } /** * 我们可以通过类的类类型创建该类的对象实例-->通过c1,c2,c3创建Foo类型的对象 */ try { Foo f2 = (Foo)c1.newInstance();//需要有无参数的构造方法 } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); }
2、动态加载类
首先搞清楚,动态加载类和静态加载类的区别
- 静态加载类:编译时刻加载类
- 动态加载类:运行时刻加载类(如功能型类 数据库驱动,需要用时才加载进来)
注意:new 创建对象 是静态加载类,在编译时刻就需要加载所有的可能使用到的类
动态加载类,解决用的时候才加载该类,如下
其中cn.fedomn.demo.Word类implements OfficeAble
public class Office { public static void main(String[] args){ try { //动态加载类,在运行时刻加载 Class c = Class.forName("cn.fedomn.demo.Word"); //通过类型转换,创建该类对象 OfficeAble oa = (OfficeAble)c.newInstance(); oa.start(); } catch (Exception e) { e.printStackTrace(); } } }
3、获取方法信息
public class ClassUtil { public static void printClassMessage(Object obj){ //首先获取类的类类型 Class c = obj.getClass(); //获取类的名称 System.out.println("类的名称是:"+c.getName()); /** * Method类,方法对象 * 一个成员对象就是一个Method对象 * getMethods()方法获取的是所以public的函数,包括父类继承而来的 * getDeclaredMethods()方法获取的是所有该类自己声明的方法,不问访问权限 */ Method[] ms = c.getMethods();c.getDeclaredMethods(); for(int i=0;i<ms.length;i++){ //获取方法返回值类型--->得到方法的返回值类型 的 类类型 Class returnType = ms[i].getReturnType(); System.out.print(returnType.getName()+" "); //得到方法的名称 System.out.print(ms[i].getName()+"("); //获取参数类型--->得到的是参数列表类型 的 类类型 Class[] paramTypes = ms[i].getParameterTypes(); for(Class class1 : paramTypes){ System.out.print(class1.getName()+","); } System.out.println(")"); } } }
4、获取成员变量和构造函数信息
/** *获取成员变量信息 */ public static void printFieldMessage(Object obj) { Class c = obj.getClass(); /** * 成员变量也是对象 * java.lang.reflect.Field * Field类封装了关于成员变量的操作 * getField()方法获取所有public的成员变量的信息 * getDeclaredField()获取的是该类自己声明的成员变量的信息 */ Field[] fs = c.getDeclaredFields(); for(Field field : fs){ //得到成员变量的类型的类类型 Class fieldType = field.getType(); String typeName = fieldType.getName(); //得到成员变量的名称 String fieldName = field.getName(); System.out.println(typeName+" "+fieldName); } } /** * 获取对象构造函数的信息 */ public static void printConMessage(Object obj){ Class c = obj.getClass(); /** * 构造函数也是对象 * java.lang.Constructor中封装了构造函数的信息 * getConstructors获取所有的public的构造函数 * getDeclaredConstructors得到所有的构造函数 */ Constructor[] cs = c.getDeclaredConstructors(); for(Constructor constructor : cs){ System.out.print(constructor.getName()+"("); //获取构造函数的参数列表-->得到参数列表的类类型 Class[] paramTypes = constructor.getParameterTypes(); for(Class class1 : paramTypes){ System.out.print(class1.getName()+","); } System.out.println(")"); } }
5、方法反射操作
method.invoke(对象,参数列表)
public class MethodDemo1 { public static void main(String args[]){ //要获取print(int,int)方法 //1、要获取一个方法就是获取类的信息,获取类的信息 首先获取类的类类型 A a = new A(); Class c = a.getClass(); /** * 2、获取方法 名称和参数列表来决定 * getMethod获取的是public方法 * getDeclaredMethod获取自己声明的方法 */ try { Method m = c.getMethod("print", int.class,int.class); //方法的反射操作 //用m对象来进行方法调用 //等同于a.print(10, 20); //方法如果没有返回值返回null,有返回值返回具体返回值 try { Object o = m.invoke(a, 10,20); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } } } class A{ public void print(int a,int b){ System.out.println(a+b); } public void print(String a,String b){ System.out.println(a.toUpperCase()+","+b.toLowerCase()); } }
6、通过反射了解集合泛型的本质
注意:反射的操作(class method field)都是在编译时候的操作,是运行时刻执行的
下例中:对ArrayList<String>插入int类型的值,就是利用反射invoke,绕过编译实现插入。
public static void main(String args[]){ ArrayList list1 = new ArrayList(); ArrayList<String> list2 = new ArrayList<String>(); //list2.add(20);错误只能插入String list2.add("hello"); Class c1 = list1.getClass(); Class c2 = list2.getClass(); System.out.println(c1==c2); //反射的操作都是编译之后的操作 /** * c1==c2返回true,说明编译之后集合的泛型是去泛型化的 * Java中集合的泛型,是防止错误输入的,只在编译阶段有效 编译后就无效了 * 验证:通过方法的反射来操作,绕过编译 */ try { Method m = c2.getMethod("add", Object.class); m.invoke(list2, 10);//绕过编译操作 就绕过了泛型 插入成功 //这个时候就不能for-each遍历了 System.out.println(list2); } catch (Exception e) { } }