一、反射的概念:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
二、Class类
万事万物皆对象,我们平时定义的类是Class类的实例对象。
获取Class类的实例对象的三种方式:
package com.example.javarefrech; /** * Created by zhengyg on 2018/5/21. */ public class ClassDemo1 { public static void main(String[] args){ //Foo 的实例对象如何表示 Foo foo1=new Foo(); //Foo这个类也是一个实例对象,Class类的实例对象,表示方法 //任何一个类都是Class的实例对象,这个实例对象有三种表示方式 //第一种表示方式--》实际在告示我们任何一个类都有一个隐含的静态成员变量class Class c1= Foo.class; //第二种表达方式,已经知道类的对象通过getClass方法 Class c2= foo1.getClass(); /** * 官网c1 和c2表示了Foo类的类类型(class type) * 万事万物皆对象 * 类也是对象,是Class类的实例对象 * 这个对象我们称为该类的类类型 */ System.out.println(c1==c2); //第三种表达方式 Class c3 = null; try { c3 = Class.forName("com.example.javarefrech.Foo"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(c1==c3); //我们完全可以通过类的类类型创建该类的对象实例--》通过c1、c2、c3创建Foo类的实例对象 try { Foo foo = (Foo) c1.newInstance();//前提是这个类有无参数的构造函数 foo.print(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } //测试类 class Foo{ public void print(){ System.out.println("Foo"); } }
测试结果:
true
trueFoo
三、Java动态加载类
Class.forNome("类的全称")
. 不仅代表了类的类类型,还代表了动态加载类
. 编译时刻加载类是静态加载类、运行时刻加载类是动态加载类
四、Java获取类方法、获取成员变量和构造函数的信息:
/** * 要获取类的信息首先我们要获得该类的类类型 * Created by zhengyg on 2018/5/21. */ public class ClassUtil { /** * 打印类的信息,包括类的成员函数、成员变量 * */ public static void printClassMethodMessage(Object obj){ //要获取类的信息 , 首先获取类的类类型 Class c= obj.getClass(); //获取类的名字 System.out.println("类的名称是:"+c.getName()); /** * Method 类,方法对象 * 一个成员方法就是一个Method对象 * getMethods();获取的是所有的Public方法,包括父类的 * getDeclareMethods() */ Method[] ms =c.getMethods(); //得到方法的返回值类型的类类型 for (int i = 0;i<ms.length;i++){ Class returnType = ms[i].getReturnType(); System.out.println(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(")"); } } /** *打印类成员的信息 */ public static void printClassFieldsMessage(Object obj){ Class c =obj.getClass(); //获取大对象的类名 System.out.println("类的名称是:"+c.getName()); /** * 成员变量也是对象 * java.lang.reflect.Field * Field 类封装了关于成员变量的操作 * getFields()方法获取的是所有的Public的成员变量的信息 * Field[] fs = c.getDeclaredFields();该类自己声明的信息 */ 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(")"); } } }
测试类:
public class ClassDemo2 { public static void main(String[] args){ String s = "Hello"; ClassUtil.printClassMethodMessage(s); System.out.println("======================"); Integer n =2; ClassUtil.printClassFieldsMessage(n); System.out.println("================="); ClassUtil.printConMessage(s); System.out.println("================="); ClassUtil.printConMessage(new Integer(1)); } }
五、Java反射的基本操作:
通过反射调用一个类的方法:
/** * Created by zhengyg on 2018/5/21. */ public class MethodDemo1 { public static void main(String[] args){ /** * 要获取print(int ,int)方法 * 1.要获取一个方法就是获取类的信息,获取类的信息就要获得类的类类型 */ A a1 = new A(); Class c = a1.getClass(); /** * 2.获取方法 名称和参数列表来决定 * getMethod获取的是Public方法 * getDeclcaredMethod获取自己声明的方法 */ try { // c.getMethod("print",new Class[]{int.class,int.class}); Method m= c.getMethod("print",int.class,int.class); /** * 方法的反射操作 * a1.print(10,20) ;方法的反射操作是用 */ // Object o=m.invoke(a1,new Object[]{10,20}); Object o=m.invoke(a1,10,20); Method m1=c.getMethod("print",String.class,String.class); Object o1= m1.invoke(a1,"Hello","WOrld"); Method m2=c.getMethod("print"); Object o2=m2.invoke(a1); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException 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()); } public void print(){ System.out.println("Hello World!!"); } }
测试结果:
30
HELLOworldHello World!!
六、通过反射了解集合泛型的本质:
public class MethodDemo2 { public static void main(String[] args){ ArrayList list=new ArrayList(); ArrayList<String> list1 = new ArrayList<String>(); list1.add("Hello"); Class c1 = list.getClass(); Class c2 = list1.getClass(); System.out.println(c1 == c2); //反射都是编译之后的操作 /** * c1==c2结果返回true说明了编译之后集合的泛型是去泛型的 * java中集合的泛型,是防止错误输入的,只在编译的阶段有效, * 绕过编译就无效了 * 验证:我们可以通过方法的反射来操作,绕过编译 */ try { Method m = c2.getMethod("add",Object.class); m.invoke(list1,100); System.out.println(list1.size()); System.out.println(list1); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
测试结果:
true
2
[Hello, 100]