------- android培训、java培训、期待与您交流! ----------
第一节:需要掌握的知识点
1. 了解反射的作用
2. 掌握Class类的操作及使用
3. 可以通过反射取得一个类的实例化对象。
4. 可以通过反射取得类的结构,可以通过反射进行属性方法的调用。
5. 通过反射调用类中方法的原理.
6. 工厂设计的改进,重点是掌握其思想.
第二节:反射概述以及反射的具体方法
1. 认识反射:
l 如果要想使用一个类,则必须找到这个类,之后通过此类产生实例化对象,必须先有类,后有对象,如果现在要想通过一个对象找到所在类?
2. 在Object类中有一个方法:
l public final Class <?> getClass()
3. 认识Class类:在反射机制中Class类是一个操作的源头,所有的反射操作从此类展开,但是如果要想实例化此类对象可以通过以下三种途径完成。
l 通过Object类中的getClass方法: public final Class<?> getClass();
l 通过类.class的形式:Person.class
l 通过类的静态方法:
public static calss<?> forName(StringclassName) throws ClassNotFoundException
4. 范例:验证 实例化对象.getClass()
publicclass ClassInstance1 {
publicstaticvoid main(String[] args) {
Personp = new Person();//实例化对象
Class<?>c = p.getClass();//为class对象实例化
System.out.println(c.getName());
}
}
5. 范例:验证 类.class
public class ClassInstance2 {
public static void main(String[] args) {
Class<?> c= Person.class;
System.out.println(c.getName());
}
}
6. 范例:验证 class.forName();
public class ClassInstance3 {
public static void main(String[] args)throws ClassNotFoundException {
Class<?> c= Class.forName("Person");
System.out.println(c.getName());
}
}
虽然有三种方法,但是最常用的是forName()方法,其次就是.class的形式.
第三节:通过Class类实例化对象
1. 在Class的使用中实例化对象是最常用的一种操作,而且所有的框架各个程序的实现原理都是依靠Class类完成的.
2. 实例化无参构造的类(只有一个无参构造函数):
l 根据包.类名称实例化Class对象
l 通过Class类中的以下方法:public T newInstance()
public class ClassInstance4 { public static void main(String[] args)throws Exception { Person p = null; Class<?> c = null; c = Class.forName("Person"); p = (Person)c.newInstance(); System.out.println(p); } } |
3. 实例化没有无参构造的类(调用指定构造):
l 如果要想得到一个类的构造方法,可以使用:
public Constructor<?>[] getConstructors()
l 示例:
import java.lang.reflect.Constructor; public class ClassInstance4 { public static void main(String[] args)throws Exception { Person p = null; Class<?> c = null; c = Class.forName("Person");//得到Person类 Constructor<?> cs[] = c.getConstructors();//得到全部的构造函数 p = (Person)cs[0].newInstance("张三",12); System.out.println(p); } } |
l 注意:getConstructors()方法只能得到Person类的权限为public的构造方法.
l 为了开发的简便,一定要在类中定义无参的构造方法.
第四节:通过Class类取得完整结构(了解)
1. 通过Class类中的很多方法,可以轻易的取得一个类中定义的全部的构造方法,普通方法,常量,变量等等.
现在假设有如下的类:
package org.lxh.ClassInstance2; interface Info{ public static final String AUTHOR ="小七"; public String getInfo(); public String say(String name,String content); public void sayHello(); } public class Person implements Info{ private String name; private int age; public Person(){} public Person(String name){ this.name = name; } public Person(String name,int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String toString() { return "姓名:"+this.name+"年龄:"+this.age; } public String getInfo() { return "Hello World"; } public String say(String name, String content) { return name +"说了" + content; }
public void sayHello() { System.out.println("Hello --> "+AUTHOR); } } |
2. 取得继承的父类:
l 通过publicClass<? super T> getSuperclass()
package org.lxh.ClassInstance2; public class getSuperClass { public static void main(String[] args)throws ClassNotFoundException { Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); Class<?> sc = c.getSuperclass(); System.out.println(sc.getName()); } } |
|
此时Person类没有继承任何父类就是Object类.
3. 取得实现的全部接口:
l 通过publicClass<?>[] getInterfaces()
package org.lxh.ClassInstance2; public class getInterfaces { public static void main(String[] args)throws ClassNotFoundException { Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); Class<?> inters[] = c.getInterfaces(); for(Class<?> inter : inters ){ System.out.println(inter.getName()); } } } |
|
4. 取得一个类的全部构造方法:
l 之前已经实现了取得全部的构造方法:
package org.lxh.ClassInstance2; import java.lang.reflect.Constructor; public class getConstrutors { public static void main(String[] args)throws ClassNotFoundException { Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); Constructor<?> cons[] = c.getConstructors(); for(Constructor<?> con : cons){ System.out.println(con.getName()); } } } | 5. package org.lxh.ClassInstance2; 6. import java.lang.reflect.Constructor; 7. public class getConstrutors { 8. public static void main(String[] args)throws ClassNotFoundException { 9. Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); 10. Constructor<?> cons[] = c.getConstructors(); 11. for(Constructor<?> con : cons){ 12. System.out.println(con.getName()); 13. } 14. } 15. } |
l 以上确实得到了全部的构造方法,但是有弊病,此时是通过toString方法自动输出的,如果现在直接通过getName方法取得,则发现只能取得构造方法的名字,而访问的修饰符和参数全部不见.所以,如果想要拼凑出完成的构造方法,还需要一下几个方法的支持.
l 得到访问修饰符:publicint getModifers()
l 得到全部参数: TypeVariable<Class<T>>[]getParameterTypes();
package org.lxh.ClassInstance2; import java.lang.reflect.Constructor; public class getConstrutors { public static void main(String[] args)throws ClassNotFoundException { Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); Constructor<?> cons[] = c.getConstructors(); for(Constructor<?> con : cons){ //得到修饰符 int mod = con.getModifiers(); //得到全部的参数 Class<?> params[] = con.getParameterTypes(); System.out.print(mod+" "); System.out.print(con.getName()+"( "); for(int x=0; x<params.length;x++){ System.out.print(params[x].getName()); if(x<params.length-1) System.out.print(", "+params[x].getName()); } System.out.println(")"); } } } |
|
此时的输出结果如下:
1 org.lxh.ClassInstance2.Person( ) 1 org.lxh.ClassInstance2.Person( java.lang.String,java.lang.String, int) 1 org.lxh.ClassInstance2.Person( java.lang.String) |
从此结果中可以发现,访问修饰符以数字表示,在实际开发中,访问权限都是以数字形式表现出来的,这样程序比较好写.而且对于public static final是三个值相加的结果,所以如果要想正确的还原一个方法的修饰符,则必须使用一个Modifer的类进行还原.
5. 取得一个类的全部方法:
l 根据: Method[] getMethods()
l 范例:得到Person类的全部方法,(其中包含从Object继承的方法)
package org.lxh.ClassInstance2; import java.lang.reflect.Method; public class getMethods {
public static void main(String[] args)throws ClassNotFoundException { Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); //取得全部的方法 Method ms[] = c.getMethods(); for(Method m : ms){ System.out.println(m); } } |
l 取得一个返回值类型: Class<?> getReturnType()
l 取得方法的全部参数: Class<?>[] getParameterTypes()
l 取得方法的抛出异常:Class<?>[] getExceptionTypes()
package org.lxh.ClassInstance2; import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class getMethods2 { public static void main(String[] args)throws ClassNotFoundException { Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); //取得全部的方法 Method ms[] = c.getMethods(); for(Method m : ms){ //取得访问权限 String mod = Modifier.toString(m.getModifiers()); //取得方法名称 String metName = m.getName(); //取得返回值类型 Class<?> ret = m.getReturnType(); //取得全部的异常 Class<?> exc[] = m.getExceptionTypes(); //取得全部参数 Class<?> params[] = m.getParameterTypes(); System.out.print(mod+" "); System.out.print(ret+" "); System.out.print(metName); System.out.print("( "); for(int x=0; x<params.length;x++){ System.out.print(params[x].getName()); if(x<params.length-1) System.out.print(", "+params[x].getName()); } System.out.print(" ) "); if(exc.length>0){ System.out.print("throws ");
for(int x=0; x<exc.length;x++){ System.out.print(exc[x].getName()); if(x<exc.length-1) System.out.print(", "+exc[x].getName()); } }
System.out.println(); } } } 4. |
6. 取得一个类的全部属性:
l 得到继承而来的公共属性: Field[] getFields()
l 得到自定义的属性: Field[] getDeclaredFields()
package org.lxh.ClassInstance2;
import java.lang.reflect.Field; import java.lang.reflect.Modifier;
public class getFields { public static void main(String[] args)throws ClassNotFoundException { Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); { Field fields[] = c.getFields(); for(Field field : fields){ String mod = Modifier.toString(field.getModifiers()); Class<?> type = field.getType(); System.out.print(mod+" "); System.out.print(type.getName()+" "); System.out.println(field.getName()+" ");
} } { Field fields[] = c.getDeclaredFields(); for(Field field : fields){ String mod = Modifier.toString(field.getModifiers()); Class<?> type = field.getType(); System.out.print(mod+" "); System.out.print(type.getName()+" "); System.out.println(field.getName()+" ");
} }
}
} |
以上就是开发工具的核心原理,但是此种操作在实际开发中很少使用,只做了解.
第五节:反射的进一步应用(理解)
在正常情况下士通过对象.方法()的形式调用类中的指定方法,那么实际上可以通过反射机制完成类中方法的调用.
在Class类中使用以下的方法取得要调用方法的Method对象:
l Method getMethod(String name,Class<?>... parameterTypes)
l 之所以要传可变参数,主要是一个方法在使用的时候可能存在多个调用参数,所以在此处必须指定好参数的类型.
l 取得Method对象以后通过如下方法进行调用:
l Object invoke(Object obj, Object... args)
package org.lxh.ClassInstance3; import java.lang.reflect.*; public class InvokeMethod { public static void main(String[] args)throws Exception{ Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); //调用Person类中sayHello方法 Method m = c.getMethod("sayHello"); //invoke方法中必须有实例化的对象 m.invoke(c.newInstance()); } } |
以上的调用只是调用了类中没有参数,没有返回值的方法,下面看一下调用Person类中的getInfo()方法,此方法存在返回值.
package org.lxh.ClassInstance3; import java.lang.reflect.*; public class InvokeMethod2 { public static void main(String[] args)throws Exception{ Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); //调用Person类中getInfo方法 Method m = c.getMethod("getInfo"); //invoke方法中必须有实例化的对象 String str = (String)m.invoke(c.newInstance()); System.out.print(str); } } |
继续使用以上操作,调用Person类中的say方法,此方法有返回值,有参数类型.
package org.lxh.ClassInstance3; import java.lang.reflect.*; public class InvokeMethod3 { public static void main(String[] args)throws Exception{ Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); //调用Person类中getInfo方法 Method m = c.getMethod("say",String.class,String.class); //invoke方法中必须有实例化的对象 String str = (String)m.invoke(c.newInstance(),"小7",",要百折不挠"); System.out.print(str); } } |
第六节:通过反射调用类中的getter和sertter
1. 类中在定义的时候就已经明确的声明了,必须存在getter和sertter方法,之所以这样定义主要的目的是为了反射的操作.
package org.lxh.ClassInstance3; import java.lang.reflect.*; public class InvokeMethod3 { public static void main(String[] args)throws Exception{ Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); Object o = c.newInstance(); set("name","小7",o,String.class); set("age",30,o,int.class); String name = (String)get("name",o); int age = (Integer)get("age",o); System.out.println(name+" "+age); } /** * @param name属性名称 * @param value属性值 * @param obj要操作的对象 * @throws NoSuchMethodException * @throws SecurityException */ public static void set(String name, Object value, Object obj,Class<?> type) throws Exception { // 属性名称首字母应该大写 Method met = obj.getClass().getMethod("set" +initStr(name), type); // 调用方法 met.invoke(obj, value); } public static Object get(String name,Object obj) throws Exception{ Method met = obj.getClass().getMethod("get"+initStr(name)); Object value = met.invoke(obj); return value; } public static String initStr(String name) { StringBuilder sb = new StringBuilder(); sb.append(name.substring(0, 1).toUpperCase()).append(name.substring(1)); return sb.toString(); } } |
以后的学习中会经常看见这种类似的操作原理出现,本身在实际开发中开发者一般不会去直接编写此代码.
第七节:通过反射直接操作属性
1. 在反射机制中,不光可以操作方法,还可以直接操作类中的属性.
package org.lxh.ClassInstance3; import java.lang.reflect.*; public class InvokeMethod4 { public static void main(String[] args)throws Exception{ Class<?> c = Class.forName("org.lxh.ClassInstance2.Person"); Object obj = c.newInstance(); Field nameField = c.getDeclaredField("name"); nameField.setAccessible(true); nameField.set(obj, "小7"); System.out.println(nameField.get(obj)); } } |
从以上程序中可以发现,属性也是可以通过代码设置访问权限的,但是以上的操作基本上是不建议的,以为直接操作属性不安全.
第八节:反射的进一步应用
反射的应用必须结合到一个完整的设计模式之中.例如工厂设计模式.
package org.factoryDemo1; interface Fruit{ public void eat(); } class Apple implements Fruit{ public void eat(){ System.out.println("吃苹果"); } } class Orange implements Fruit{ public void eat() { System.out.println("吃橘子"); } } class Factory{ public static Fruit getInstance(String className){ Fruit f = null; if(className.equals("apple")){ f = new Apple(); } if(className.equals("orange")){ f = new Orange(); } return f; } } public class FactoryDemo1 { public static void main(String[] args) { Fruit f = Factory.getInstance("apple"); f.eat(); } } |
以上程序实现了工厂操作,但是否存在问题?所有的问题集中在工厂操作中,因为每次只要一增加子类,则必须修改工厂,那么这个时候就可以更具反射机完成.
通过Class类修改:
package org.factoryDemo1; interface Fruit{ public void eat(); } class Apple implements Fruit{ public void eat(){ System.out.println("吃苹果"); } } class Orange implements Fruit{ public void eat() { System.out.println("吃橘子"); } } class Factory{ public static Fruit getInstance(String className){ Fruit f = null; try { f = (Fruit) Class.forName(className).newInstance(); } catch (Exception e) {} return f; } } public class FactoryDemo1 { public static void main(String[] args) { Fruit f = Factory.getInstance("org.factoryDemo1.Apple"); f.eat(); } } |
在以上的操作中工厂类是完全不用修改的,但是也存在问题.每次在操作的时候后都必须输入很长的”包.类”名称,所以使用时很不方便.
最好的做法是通过一个别名表示完整的”包.类”名称,而且在类增加的时候别名也可以自动增加,所以如果要想完成这样的操作,则可以使用属性类配置.
package org.factoryDemo2; import java.io.*; import java.util.*; interface Fruit{ public void eat(); } class Apple implements Fruit{ public void eat(){ System.out.println("吃苹果"); } } class Orange implements Fruit{ public void eat() { System.out.println("吃橘子"); } } class PropertiesOperate{//属性操作类 private Propertiespro= null; private File f = new File("D:"+File.separator+"Fruit.Properties"); public PropertiesOperate(){ this.pro =new Properties(); if(f.exists()){//文件存在 try { //读取 pro.loadFromXML(new FileInputStream(f)); } catch (Exception e) { e.printStackTrace(); } }else{ this.save(); } } private void save(){ this.pro.setProperty("apple","org.factoryDemo2.Apple"); this.pro.setProperty("orange","org.factoryDemo2.Orange"); try { this.pro.storeToXML(new FileOutputStream(this.f),"Fruit"); } catch (Exception e) { e.printStackTrace(); } } public Properties getProperties(){ returnthis.pro; } } class Factory{ public static Fruit getInstance(String className){ Fruit f = null; try { f = (Fruit) Class.forName(className).newInstance(); } catch (Exception e) {} return f; } } public class FactoryDemo1 { public static void main(String[] args) { Properties pro = new PropertiesOperate().getProperties(); Fruit f = Factory.getInstance(pro.getProperty("apple")); f.eat(); } } |
从以上的操作代码中可以发现,程序通过一个配置文件,可以控制程序的执行,也就是达到了配置文件与程序相分离的目的.
那么这种设计思想一直到今天还在使用者,包括各个开发框架:Struts,Spring,Hibernate,而最新的程序设计理论,是将注释写入到代码之中,让注释起到程序控制的作用,那么要想实现此操作,则要使用Annotation.