反射机制
1.观察 Class 类的使用
2.利用反射改善工程设计模式
3.反射操作类结构
content (内容)
1.认识反射
既然是反,那么就有正。正常的思路中只有知道一个类之后才可以产生实例化对象
范例:正常的操作
package cn.mysterious.study3; import java.util.Date; public class ReflexMechanism { public static void main(String[] args) { // TODO Auto-generated method stub java.util.Date data = new Date(); System.out.println(data); } }
所谓的反指的就是通过对象找到来源,那么在程序中,对象要想找到它的来源就必须依靠 Object 类提供的一个方法完成:
public final Class<?> getClass()
范例:观察反
package cn.mysterious.study3; import java.util.Date; public class ReflexMechanism { public static void main(String[] args) { // TODO Auto-generated method stub java.util.Date data = new Date(); System.out.println(data.getClass()); } }
现在发现通过 getClass() 取得的 Class 类对象,就可以直接找到该对象的来源
2.实例化 Class 类
Class 类是反射操作的源头,也就是说所有的反射操作都应该通过Class 完成,但是最有意思的是这个类的对象可以通过三种实例化
第一种:利用 Object 类中的 getClass() 方法 ,必须有明确的实例化对象后可以调用
package cn.mysterious.study3; import java.util.Date; public class ReflexMechanism { public static void main(String[] args) { // TODO Auto-generated method stub java.util.Date data = new Date(); Class<?> cls = data.getClass(); System.out.println("全名:" + cls.getName()); System.out.println("类名:" + cls.getSimpleName()); } }
第二种:利用“ 类.class ”取得 Class 类的实例化对象,等到学习到 Hibernate , MyBatis 的时候会使用到此模式
package cn.mysterious.study3; import java.util.Date; public class ReflexMechanism { public static void main(String[] args) { // TODO Auto-generated method stub Class<?> cls = Date.class; System.out.println("全名:" + cls.getName()); System.out.println("类名:" + cls.getSimpleName()); } }
(这个不知道怎么说 一个是有 import 调用好包 一个是出错?)
package cn.mysterious.study3; public class ReflexMechanism { public static void main(String[] args)throws Exception { // TODO Auto-generated method stub Class<?> cls = java.util.Date.class; System.out.println("全名:" + cls.getName()); System.out.println("类名:" + cls.getSimpleName()); } }
第三种:利用 Class 类的 static 方法取得
实例化 Class 对象: public static Class<?> forName(String className)throws ClassNotFoundException
package cn.mysterious.study3; public class ReflexMechanism { public static void main(String[] args)throws Exception { // TODO Auto-generated method stub Class<?> cls = Class.forName("java.util.Date"); // 字符串 System.out.println("全名:" + cls.getName()); System.out.println("类名:" + cls.getSimpleName()); } }
现在最为重要的模式就是使用 Class.forName() 方法,它的好处是可以直接写字符串
3.通过反射实例化对象(核心)
如果要产生实例化对象,本质上来讲必须有关键字 new才可以实现,但是这一切在有了反射之后变了
在 Class 类中提供有如下一个方法:public T newInstance()throws InstantiationException,IllegalAccessException
范例:正常实例化对象
package cn.mysterious.study3; import java.util.Date; public class ReflexMechanism { public static void main(String[] args) { // TODO Auto-generated method stub Date data = new Date(); System.out.println(data); } }
范例:反射实例化对象
package cn.mysterious.study3; import java.util.Date; public class ReflexMechanism { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("java.util.Date"); // 字符串 Date data = (Date) cls.newInstance();// 实例化对象:new Date() System.out.println(data); } }
下面再观察一个自定义的类i对象实例化
范例:自定义实例化对象
package cn.mysterious.study3; class Person{ public Person(){ System.out.println("*** 新的 Person类对象实例化 ***"); } @Override public String toString() { // TODO Auto-generated method stub return "人"; } } public class ReflexMechanism { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("cn.mysterious.study3.Person"); // 字符串 Person p = (Person) cls.newInstance();// 实例化对象:new Date() System.out.println(p); } }
反射实例化对象最终也要调用无参构造方法进行实例化操作
4.工厂设计模式的修正(理解)
工厂设计模式的本质就在于要想取得接口的实例化对象不要直接使用关键字 new ,而应该通过工厂取得
(总结:只要是自己写的接口要想取得实例化对象,99%要写工厂类)
范例:传统工厂设计
package cn.mysterious.study3; interface Fruit{ public void eat(); } class Apple implements Fruit{ @Override public void eat() { // TODO Auto-generated method stub System.out.println("吃"); } } class Factory{ public static Fruit getInstance(String classNaame) { if ("apple".equalsIgnoreCase(classNaame)) { return new Apple(); } return null; } } public class ReflexMechanism { public static void main(String[] args) throws Exception { Fruit f = Factory.getInstance("apple"); f.eat(); } }
但是现在对于工厂设计模式却出现了一个挑战,如果喜爱女子增加了子类,那么意味着工厂类需要修改
那么假如这个接口可能源源不断的生产新的子类。所以这个时候的工厂类设计就存在有新的问题了
那么现在的关键性问题就在于new上了,new是照成本次设计失败的最大元凶。所以这个时候就可以通过反射来实现此时的工厂类
package cn.mysterious.study3; interface Fruit{ public void eat(); } class Apple implements Fruit{ @Override public void eat() { // TODO Auto-generated method stub System.out.println("吃苹果"); } } class Orange implements Fruit{ @Override public void eat() { // TODO Auto-generated method stub System.out.println("吃橙子"); } } class Factory{ public static Fruit getInstance(String classNaame) { try { return (Fruit) Class.forName(classNaame).newInstance(); } catch (Exception e) { // TODO: handle exception return null; } } } public class ReflexMechanism { public static void main(String[] args) throws Exception { Fruit f = Factory.getInstance("cn.mysterious.study3.Orange"); f.eat(); } }
以上的代码只是分析出了关键字 new 实例化对象与构造方法实例化对象的区别,但是并不意味着不使用 关键字 new
5.调用构造方法
利用 Class 类中的 newInstance 方法其主要的目的是可以取得类的实例化对象,但是这种取得的操作本身是存在有问题的。因为它只能调用类中的去参构造。
如果类中没有提供无参构造方法,那么就必须明确的调用指定的构造方法实现对象的实例化操作
要想实现这样的操作,那么必须要找到类中的构造方法定义:
public Constructor<T> getConstructor(Class<?>... parameterTypes)throws NoSuchMethodException, SecurityException
找到了 java.lang.reflect.Constructor 类之中在此类中提供有一个实例化对象的方法:
实例化对象:
public T newInstance(Object... initargs)
throws InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
范例:调用指定构造实例化对象
package cn.mysterious.study3; import java.lang.reflect.Constructor; class Person { private String name; private int age; public Person(String name,int age){ this.name = name; this.age = age; } public void setAge(int age) { this.age = age; } @Override public String toString() { // TODO Auto-generated method stub return "姓名:" + this.name + ",年龄:" + this.age + " "; } } public class ReflexMechanism { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("cn.mysterious.study3.Person"); Constructor<?> cons = cls.getConstructor(String.class,int.class); Person per = (Person) cls.newInstance();// 实例化对象:new Date() System.out.println(per); } }
还是类中提供无参构造省事
6.调用方法
类中的普通方法是在取得本类的实例化对象之后才可以进行调用的操作。所以即便使用了反射进行方法的调用
那么也必须使用 newInstance() 取得实例化对象,在 Class 类中定义有如下取得方法对象的操作
取得方法:
public Method getMethod(String name,Class<?>... parameterTypes)
throws NoSuchMethodException,SecurityException
getMethod() 方法返回的是 java.lang.reflect.Method 类对象,在这个类中有一个最为重要的方法:
调用方法:
public Object invoke(Object obj,Object... args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException package cn.mysterious.study3; import java.lang.reflect.Constructor; import java.lang.reflect.Method; class Person { private String name; public Person(String name){ this.name = name; } public String getName() { return name; } } public class ReflexMechanism { public static void main(String[] args) throws Exception { String attribute = "name"; // 要求? value = "少爷" Class<?> cls = Class.forName("cn.mysterious.study3.Person"); Object obj = cls.newInstance(); // 实例化对象 Method setMrthod = cls.getMethod("set" + initcap(attribute), String.class); Method getMrthod = cls.getMethod("get" + initcap(attribute)); setMrthod.invoke(obj, "少爷"); // 等价于: 对象。setName("少爷") System.out.println(getMrthod.invoke(obj)); } public static String initcap(String str){ return str.substring(0,1).toLowerCase() + str.substring(1); } }
以后程序中如果给了属性名称,类的完整名称,而后可以进行自动的赋值操作,那么基本上就是反射机制在里面起作用
7.调用成员
在类中的成员需要考虑两种因素(Class 类定义的方法)
父类继承而来的成员: public Field getField(String name)throws NoSuchFieldException,SecurityException
本类定义的成员: public Field getDeclaredField(String name)throws NoSuchFieldException,SecurityException
在java中使用 java.lang.reflect.Field 类描述成员,在这个类中有两个方法:
取得成员内容: public Object get(Object obj)throws IllegalArgumentException,IllegalAccessException
设置成员内容: public void set(Object obj,Object value)throws IllegalArgumentException,IllegalAccessException
但是需要注意的是所有属性几乎都要使用 private 封装,所以要想解决封装的困扰
可以利用 Field 父类的方法完成( java.lang.reflect.AccessibleObject ):
取消封装: public void setAccessible(boolean flag)throws SecurityException
范例:直接进行属性调用
package cn.mysterious.study3; import java.lang.reflect.Field; import javax.naming.NameNotFoundException; class Person { // (想法:可不可以继承,继承 person) private String name; } public class ReflexMechanism { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("cn.mysterious.study3.Person"); Object obj = cls.newInstance(); // 实例化对象 Field nameField = cls.getDeclaredFields(); nameField.setAccessible(true); // private 消失了 nameField.set(obj,"少公子"); // 对象。name = “少公子”; System.out.println(nameField.get(obj)); } }
几乎类中的一切定义形式都可以通过反射完成
总结
在基础学习阶段对于反射有个认识就行了,暂时不需要编写代码
Class Constructor Method Field 类的作用应该有个印象
反射与工厂设计模式结合的产物