JAVA 反射机制....
PS:最近数据库算是学完了,总算是有时间回头复习一下以前学的东西了...最近这几天好好复习了一下反射机制,顺便也做个总结...JAVA的学习基本都没有去写...慢慢补上吧...
学习内容:
1.反射机制的概念...
2.反射机制的作用...
3.如何去使用反射机制...
4.反射机制的优势...
1.反射机制:
反射机制,想必大家都不陌生,但是估计大家也就是知道个概念...那么到底什么是反射机制,估计大家对这个概念有点模糊不清...简单的概念就是:对于我们定义的每一个类,在任何的时刻,我们都能够知道这个类里面的属性和方法...对于任何一个对象,都能够调用这个类中的方法...这就是反射机制的基本概念..
2.反射机制实现的功能:
在运行时判断任意一个对象所属的类,在运行时构造任意一个类的对象,在运行时判断任意类所具有的方法和属性,在运行时调用任意一个对象的方法
生成动态代理...
3.如何使用反射机制:
我们如何去使用反射机制呢?
反射机制里一个特点就是实例化class对象,因为任意一个类对象都是class的实例...那么如何实例化class对象呢?三种方法:
i.通过forname()方法...
ii.对象.getclass();
iii.类.class;
在程序运行时通过实例化的对象,然后对象使用反射来调用类内的方法或者是属性...这样就实现了动态的获取信息...
package Fanshe; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Scanner; class person{ private String name; private String sex; private int age; person(){ } public person(String name,String sex,int age){ super(); this.name=name; this.sex=sex; this.age=age; } private void setname(String name){ this.name=name; } public String getsex(){ return sex; } public void setsex(String sex){ this.sex=sex; } public int getage(){ return age; } public void setage(){ this.age=age; } public String toString(){ return "姓名 :"+name+"年龄: "+age; } } class reflectdemo{ reflectdemo(){ Scanner cin=new Scanner(System.in); String classpath=cin.nextLine();//需要输入类的完整路径...我的路径是Fanshe.person try { //三种方式实现对象的实例化 Class cla=Class.forName(classpath); // Class cla_1=person.class; // person p=new person(); // Class cla_2=p.getClass(); Method [] method=cla.getDeclaredMethods();//定义一个数组来保存类中所有的方法... System.out.println("=====类中的方法有====="); for(Method meth:method){ System.out.println(meth.toString()); } System.out.println("=====方法获取结束====="); Field [] field=cla.getDeclaredFields(); //保存类中所有属性 System.out.println("=====类中内部的属性====="); for(Field fie:field){ System.out.println(fie.toString()); } System.out.println("=====属性获取结束====="); Constructor [] con=cla.getDeclaredConstructors();//类中所有的构造函数... System.out.println("=====获取构造函数====="); for(Constructor c:con){ System.out.println(c.toString()); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println("路径输入错误"); } } } public class fanshe_2 { public static void main(String[] args) { // TODO Auto-generated method stub reflectdemo ref=new reflectdemo(); } }
上述代码通过使用反射机制来获取类中的方法和属性...反射这东西我还是用代码说话更好一些,所以我还是都使用代码来让大家理解的更深刻一些...
package Fanshe_cll; import java.lang.reflect.Method; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.annotation.Annotation; class reflectdemo{ //私有构造函数 private reflectdemo(String name,int age){ } reflectdemo(){ } //公有构造函数 public reflectdemo(String name){ } public void info(){ } public void info(String str){ } private void set(){ } class inner{ }//内部类... } import Fanshe_cll.reflectdemo.inner; public class clear1 { public static void main(String[] args) throws Exception { // TODO Auto-generated method stub reflectdemo r=new reflectdemo(); Class claz_1=r.getClass(); System.out.println(claz_1.toString()); Class<reflectdemo> cla=reflectdemo.class; //使用泛型,用class来实例化对象.. System.out.println(cla.toString()); Class claz=Class.forName("Fanshe_cll.reflectdemo"); System.out.println(claz.toString()); System.out.println("=====类中的构造函数====="); //获取全部的构造函数.... Constructor []constructor=cla.getDeclaredConstructors();//返回类中所有的构造函数,无论是公有的还是私有的...并且这个保存是按照倒序保存的..
就是constructor[0]保存的是最后一个构造函数...而constructor[length-1]保存的是第一个构造函数...比如说我们有一个person类,类中有四个构造方法...
public Person(){}
public Person(String name){this.name=name;}
public Person(int age){this.age=age;}
public Person(String name,int age){this.age=age;this.name=name;}
我们在主函数来调用这几种方法...
Class<?> cla=Person.class;
Constructor []con=cla.getDeclaredConstructors();
Person per_1=(Person) con[0].newInstance("剑神",20);
Person per_2=(Person) con[1].newInstance(20);
Person per_3=(Person) con[2].newInstance("剑圣");
Person per_4=(Person) con[3].newInstance(); 必须这样传参才行,如果把他们倒过来,那么必定会出现java.lang.illegalArgumentException非法参数异常..
for(Constructor con:constructor){ System.out.println(con.toString()); } System.out.println("公有构造函数"); //获取公有的构造函数.... Constructor []constructor_1=cla.getConstructors();//这个就和上面不同了,这个保存着类中所有的公有构造函数 for(Constructor con_1:constructor_1){ System.out.println(con_1.toString()); } System.out.println("获取全部方法"); //获取全部的方法.... Method [] method =cla.getDeclaredMethods(); for(Method meth:method){ System.out.println(meth.toString()); } //获取公有的方法.... System.out.println("获取公有方法"); Method [] method_1=cla.getMethods(); for(Method meth_1:method_1){ System.out.println(meth_1.toString());//这个输出异常处理的函数..这说明有些方法是带有异常的... } //获取指定的某个方法.... System.out.println("获取某个指定的方法"); Method method_2=cla.getMethod("info",null);//获取参数为空的info方法.. System.out.println(method_2.toString()); Method method_3=cla.getMethod("info", String.class);//获取指定参数为字符串的方法... System.out.println(method_3.toString()); // Method method_4=cla.getMethod("info", reflectdemo.class); System.out.println("-----------------"); // System.out.println(method_4.toString()); System.out.println("-----------------"); //获取注释 System.out.println("获取注释"); Annotation []annotations =cla.getAnnotations(); for(Annotation ant:annotations){ System.out.println(ant.toString()); } //获取包信息 System.out.println("获取包信息"); Package package_1=cla.getPackage(); System.out.println(package_1.toString()); //获取内部类 System.out.println("获取内部类"); Class [] cla_1=cla.getDeclaredClasses(); for(Class clazz : cla_1){ System.out.println(clazz.toString()); } //调用父类 System.out.println("调用父类"); Class cla_2=cla.getSuperclass(); System.out.println(cla_2.toString()); //内部类对象... Class cla_3=Class.forName("Fanshe_cll.reflectdemo$inner"); // Class cla_4=inner.class; //获取内部类的外部类 System.out.println("使用内部类来获取外部类信息"); System.out.println(cla_3.getDeclaringClass()); System.out.println("--------------"); System.out.println(cla_3.getPackage()); System.out.println(cla_3.getSuperclass()); } }
我们上面只是返回类中的方法...基本没有进行调用,那么下面就对上面的那些方法进行调用...
package Fanshe_c; import java.lang.reflect.Method; class person{ //这个类大家基本不用看,是一个很普通的类,之所以粘出来是因为自己定义了好几个person类,怕产生混淆.... private String name; private String sex; private int age; person(){ } public person(String name,String sex,int age){ super(); this.name=name; this.sex=sex; this.age=age; } public void setname(String name){ this.name=name; } public String getname(){ return name; } public String getsex(){ return sex; } public void setsex(String sex){ this.sex=sex; } public int getage(){ return age; } public void setage(int age){ this.age=age; } public String toString(){ return "姓名 :"+name+"年龄: "+age; } } public class cll_2 { public static void main(String[] args) throws Exception{ // TODO Auto-generated method stub Class cla=person.class; person p=new person();//定义一个对象..使用new关键字.. Method method=cla.getMethod("setname",String.class); method.invoke(p, "剑神");//这句话还可以写成 method.invoke(cla.newInstance(),"剑神");下面凡是和这句话类似的都可以使用这种方法,使用newInstance()的默认构造函数去实例化一个对象.. Method method_1=cla.getMethod("setage",int.class); method_1.invoke(p, 20); Method method_2=cla.getMethod("setsex", String.class); method_2.invoke(p, "男"); //调用输出方法... Method method_3=cla.getMethod("getname", null); System.out.println((method_3.invoke(p, null)).toString()); Method method_4=cla.getMethod("getsex", null); System.out.println((method_4.invoke(p,null)).toString()); Method method_5=cla.getMethod("getage", null); System.out.println((method_5.invoke(p, null)).toString()); } }
通过上面的代码,大家难免会产生一个疑问,就是使用new和newInstance去创建对象到底有什么区别呢??这个我也在这里进行下解释...
new与newInstance()的区别
首先一个是关键字,一个是方法...new是一个关键字,使用关键字来新建一个对象的时候没有过多的限制...而newInstance()是一个方法,使用newInstance()创建对象的时候,类一定要有一个默认的无参构造方法...并且这个类必须要被加载,否则的话JVM首先会将这个类进行加载后,然后我们才能使用newInstance()来新建一个对象...那么大家就会问了,那给出两个创建对象的方法,是不是有点多此一举了呢?这个当然是否定的...使用newInstance()...其实在一定情况下是很有好处的...大家来看:
Class c = Class.forName("darker"); factory = (AInterface)c.newInstance(); 其中AInterface是darker的接口,在看下面 String className = "darker"; Class c = Class.forName(className); factory = (AInterface)c.newInstance(); 进一步的进行扩展... String className = readfromXMlConfig;//从xml 配置文件中获得字符串 Class c = Class.forName(className); factory = (AInterface)c.newInstance(); 上面代码把类名darker彻底的去掉了,这个优点估计大家都看出来了,这样当我们的darker类做怎样的修该,我们这句话永远保持不变...如果使用new就没办法了吧??
这就是newInstance()在一定的地方使用的好处...
简单的介绍一下setAccessible()函数...
package Fanshe_fuzhi; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.util.Calendar; class Person { //这个代码目的是为了简单的介绍一下setAccessible()函数。。 private String name; private String gender; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSay(){ return"姓名:"+this.name+" \t性别:"+this.gender+"\t年龄:"+this.age; } } public class Fanshe_quanxian { public static void main(String[] args) throws Exception{ // TODO Auto-generated method stub Class<?> cla=Person.class; Field name=cla.getDeclaredField("name"); System.out.println(name); name.setAccessible(true); //这个函数的参数是boolean类型,当值为true的时候,那么JVM将忽略访问时的权限...如果为false,那么就不进行忽略... name.set(cla.newInstance(),"三丰"); Field gender=cla.getDeclaredField("gender"); gender.setAccessible(true); gender.set(cla.newInstance(),"重阳"); Field age=cla.getDeclaredField("age"); age.setAccessible(true); age.setInt(cla.newInstance(), 20); Method getsay=cla.getMethod("getSay"); Object obj=getsay.invoke(cla.newInstance()); System.out.println(obj.toString()); } }
Modifyers函数...很简单,没什么东西...
package Fanshe_modifier; import Fanshe_Person.Person; import java.lang.reflect.Field; import java.lang.reflect.Modifier; public class Modify { public static void main(String[] args) { // TODO Auto-generated method stub Class<?> cla = Person.class; Field[] field = cla.getDeclaredFields(); for (int i = 0; i < field.length; i++) { int mo = field[i].getModifiers(); System.out.println(mo); String priv = Modifier.toString(mo); System.out.println(priv); Class<?> type =field[i].getType(); System.out.println(type.getName()); System.out.println(field[i].getName()); /* * 直接toString就可以返回方法的名字... * field[i].toString * */ } } }
动态创建和访问数组...
package Array; import java.lang.reflect.Array; public class Array_1 { public static void main(String[] args) { // TODO Auto-generated method stub Object obj=Array.newInstance(int.class,3); /* *一维数组的创建与赋值.. */ Array.set(obj, 0, 20); Array.set(obj, 1, 30); int obj_1=Array.getLength(obj); System.out.println(obj_1); Object obj_2=Array.get(obj, 0); System.out.println(obj_2); Array.set(obj, 2, 40); int obj_3=Array.getLength(obj); System.out.println(obj_3); Object two_arr=Array.newInstance(int.class, 10,10); /* * 二维数组的赋值:先找到第一维的下标值..然后再传参.. * */ Object firstindex=Array.get(two_arr, 0); Array.set(firstindex, 0, 20); } }
动态修改数组的大小...
package Array; import java.lang.reflect.Array; public class Array_xiugailength { public static void main(String[] args) { // TODO Auto-generated method stub int temp[]={0,1,2,3,4,5,6,7,8,9}; int newtemp[]=(int [])arrayinc(temp,15); for(int i=0;i<newtemp.length;i++){ System.out.println(newtemp[i]); } } public static Object arrayinc(Object obj,int len){ Class<?>arr=obj.getClass().getComponentType(); Object newArr=Array.newInstance(arr, len); return newArr; } }
4.反射机制的应用场合:
大家不禁会这样问,什么时候能够使用到反射机制呢,并且它的应用场合是什么...并且它的优势到底在哪里呢?
package Fanshefactory; interface factory_1{ public abstract void show(); } class A implements factory_1{ public void show(){ System.out.println("A"); } } class B implements factory_1{ public void show(){ System.out.println("B"); } } /* * 当增添类去实现接口的时候,我们就需要改变factory,当我们添加的类过多的时候 * 我们的修改量就会很大...可以使用反射进行解决... * */ class Factory{ public static factory_1 getInstance(String fname){ factory_1 f=null; if("A".equals(fname)){ f=new A(); } if("B".equals(fname)){ f=new B(); } return f; } } public class Fanshe_factory_1 { public static void main(String[] args) { // TODO Auto-generated method stub factory_1 f=Factory.getInstance("A"); f.show(); } } package Fanshe_factory1; interface factory_2{ public abstract void show(); } class AA implements factory_2{ public void show(){ System.out.println("A"); } } class BB implements factory_2{ public void show(){ System.out.println("B"); } } class Factory_1{ public static factory_2 getInstance(String fname){ factory_2 f=null; try { Class cla=Class.forName(fname); f=(factory_2)cla.newInstance(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return f; } } public class Fanshe_factory_2 { public static void main(String[] args) throws Exception{ // TODO Auto-generated method stub factory_2 f=Factory_1.getInstance("Fanshe_factory1.AA"); if(f!=null){ f.show(); } } }
这两个代码形成对比,一个是未使用反射机制的工厂模式,一个是使用了反射机制的工厂模式...估计大家看完没什么过多的感觉...但是这里比如说我们要添加更多的类去实现我们暴露出的接口,那么我们Factory类里的判断部分也要进行大量的修改...这么做会代码冗余...那么用了反射的我们就没必要进行修改了,因为我们是使用class实例化的对象,class是字节码文件,无论类如何变,这个字节码文件只有一个,那么这样就可以直接实例化对象....我们没必要进行一个一个判断了....这就是反射的优势,在一定程度上解决代码冗余,并且可以动态的去获取类的信息...这样我们就感觉java就像动态语言一样..因此反射机制是很强大的一个东西...