java反射
一、Class类的使用
在面向对象的世界里,万事万物皆对象。所以一个具体的类(比如我们创建的User类)也是一个对象,那么类是谁的对象呢?类是java.lang.Class的对象,通过源码我们发现Class是私有的,不能通过new创建。但是可以通过以下三种方法实现。
我们首先创建一个User类,然后实现创建此类类型的方法:
package com.ht; public class User { public void print(){ System.out.println("hello world!"); } }
import com.ht.User; public class Main { public static void main(String[] args) { User user=new User(); user.print(); //第一种获取类的方法(通过类的一个实例创建) Class u1=user.getClass(); //第二种获取类的方法(通过类的隐藏的静态class属性创建) Class u2=User.class; //第三种获取类的方法 (这里有一个异常的抛出,通过Ctrl+Alt+t,快捷键直接打出代码) Class u3=null; try { u3=Class.forName("com.ht.User");//必须是类的完整路径名 } catch (ClassNotFoundException e) { e.printStackTrace(); } //以上u1,u2,u3表示User这个类的类类型 //通过比较我们知道这三个值是相等的,而且通过类类型也可以实例化出其具体的对象 System.out.println(u1==u2); //true System.out.println(u2==u3); //true try { User xiaoming=(User)u2.newInstance(); //需要有无参的构造函数 xiaoming.print(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
二、 动态加载类
通过Class.forName(“类的全名”)这种情况,我们就可以对类进行动态加载。
设计出一个公共的接口类,然后让我们需要的类实现这个接口,每次编译我们实现的类,然后运行主程序就可。
//ioffice作为接口,定义了start()方法,凡是实现了这个方法的类,我们都认为是office程序。
package com.ht; public interface Ioffice { public void start(); }
//Word的实现
package com.ht; public class Word implements Ioffice{ public void start() { System.out.println("word...start..."); } }
//主程序
import com.ht.Ioffice; public class Main { public static void main(String[] args) { try { Class c=Class.forName(args[0]); Ioffice ioffice=(Ioffice)c.newInstance(); ioffice.start(); } catch (Exception e){ } } }
经过编译后,输入具体的类名,就可以动态的加载了。
三、获取类的方法信息
我们通过一个对象,就能够拿到此对象的类类型,然后通过类类型获取此类的内部方法
//我们实现一个方法,传递一个对象,获取信息
class ClassUnit{ public static void getDetails(Object obj){ System.out.println(obj.getClass()); //打印出传递进来对象的类类型 Class c=obj.getClass(); //通过获取相应的类类型,来获取类名,方法名,参数名等等 System.out.println(c.getName()); Method[] methods=c.getDeclaredMethods();//c.getMethods(); //获取所有的方法的集合 //以下通过循环拿到每个方法的名称 for (int i=0;i<methods.length;i++){ Class returnType= methods[i].getReturnType(); //得到一个方法的返回类型 System.out.print("方法为:"+returnType.getName()+" "); //得到一个方法返回值类型的名称 System.out.print(methods[i].getName()+" (");//得到一个方法名 Class[] pataTypes=methods[i].getParameterTypes();//获取参数的类类型 for (Class patatype:pataTypes) { System.out.print(patatype.getName()+","); //每个方法的类型名 } System.out.println(")"); } } }
//此处传递的对象,既可以是系统基础类型,也可以是我们自己创建的类对象
public class Main { public static void main(String[] args) { int a=9; ClassUnit.getDetails(a); System.out.println("-------------"); Word word=new Word(); ClassUnit.getDetails(word); } }
//获取到的结果如下
同样我们也可以获取成员变量信息和构造函数的信息
四、方法的反射
我们通过Method.invoke(Object obj对象, Object... args参数列表)方法,可以进行方法反射的操作
比如我们有以下类,类中有不同的方法实现:
class ClassUnit{ public void print(){ System.out.println("hello"); } public int print(int a,int b){ return a+b; } public void printf(String a,String b){ System.out.println(a+b); } }
public static void main(String[] args) { ClassUnit c=new ClassUnit();//假设我们已经有了这么个对象 Class aClass= c.getClass(); //首先通过对象,获取其类类型 try { Method m=aClass.getMethod("print",new Class[]{int.class,int.class});//此方法需要有try操作,万一没有这个方法或者参数不对的情况呢 Method m2=aClass.getMethod("print"); Method m3=aClass.getMethod("printf",new Class[]{String.class,String.class}); try { Object o= m.invoke(c,new Object[]{10,20}); Object o2=m2.invoke(c); Object o3=m3.invoke(c,new Object[]{"hello","world"}); System.out.println(o); } catch (Exception e) { e.printStackTrace(); } } catch (NoSuchMethodException e) { e.printStackTrace(); } }
五、通过反射了解集合泛型的本质
java中集合的泛型是防止错误的输入,只在编译阶段有效。验证:我们通过方法的反射,绕过编译,就可以将不同的类型数据插入到集合中。
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; public class Main { public static void main(String[] args) { ArrayList arrayList1=new ArrayList(); ArrayList<String> arrayList2=new ArrayList<String>(); arrayList2.add("hekko"); //arrayList2.add(200);//错误 Class c1=arrayList1.getClass(); Class c2=arrayList2.getClass(); System.out.println(c1==c2); //true try { Method m2= c2.getMethod("add", Object.class); try { m2.invoke(arrayList2,200); //这样就能将200加进String的集合中 System.out.println(arrayList2.size()); System.out.println(arrayList2); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } catch (NoSuchMethodException e) { e.printStackTrace(); } } }
以上代码得到的结果如下:
人生无处不代码,没有代码不人生。