1、认识反射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
Java反射机制主要提供了以下功能:在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法。
根据对象找到类:
import java.util.Date;
public class ReflectionDemo1 {
public static void main(String[] args) throws Exception {
Date date = new Date();
Class<?> cls = date.getClass(); // 通过Java反射机制得到类的包名
System.out.println(cls);
}
}
【结果】
在本程序之中使用的getClass()方法是由Object类所定义的:
public final Class<?>getClass();
,此方法返回的对象类型为Class,而Class是反射操作的源头。但是如果要想取得Class类的实例化对象在Java之中有三种方式:方式一:利用Object类的getClass()方法,但是要求必须先产生指定类的对象才可以,几乎不用。
Date date = new Date(); Class<?> cls = date.getClass() ; System.out.println(cls);
方式二:利用“类.class”的形式取得Class类的对象,在Hibernate上使用。
Class<?> cls = java.util.Date.class; System.out.println(cls);
方式三:利用Class类提供的一个方法完成,在系统架构中使用:
Class<?> cls = Class.forName("java.util.Date"); System.out.println(cls);
package com.xy.test2; // 包名
class Book {
private String title;
private double price;
public void setTitle(String title) {
this.title = title;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Book[title = " + title + ", price = " + price + "]";
}
}
public class ReflectionDemo2 {
@SuppressWarnings("deprecation")
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.xy.test2.Book"); // 用(包名.类名)访问
Book book = (Book)cls.newInstance(); // 实例化一个对象
book.setPrice(79.8);
book.setTitle("Java开发实践经典");
System.out.println(book);
}
}
【结果】
现在可以发现如果要想实例化对象可以有两种形式,一种是通过关键字new,另外一种是通过反射机制完成。
如果要想更好地观察出这两种方式的特点,最好的做法就是通过工厂设计模式完成验证。
interface Book {
public String getTitle();
}
class MathBook implements Book {
@Override
public String getTitle() {
return "数学类图书";
}
}
class Factory {
public static Book getInstance(String className) {
if("mathbook".equals(className)) {
return new MathBook();
}
return null;
}
}
public class ReflectionDemo3 {
public static void main(String[] args) throws Exception {
Book book = Factory.getInstance("mathbook");
System.out.println(book.getTitle());
}
}
【结果】
这个时候,如果要想增加新的子类,则一定要修改工厂类。因为工厂类之中需要使用关键字new实例化,所以在这种情况下,发现关键字new依然会造成耦合,那么如果说现在使用的是反射机制呢?
使用反射机制的工厂模式
package com.xy.test2;
interface Book {
public String getTitle();
}
class MathBook implements Book {
@Override
public String getTitle() {
return "数学类图书";
}
}
class ComputerBook implements Book {
@Override
public String getTitle() {
return "计算机类图书";
}
}
class Factory {
@SuppressWarnings("deprecation")
public static Book getInstance(String className) {
Book book = null;
try {
book = (Book)Class.forName(className).newInstance();
}
catch(Exception e) {
e.printStackTrace();
}
return book;
}
}
public class ReflectionFactory {
public static void main(String[] args) throws Exception {
Book book = Factory.getInstance("com.xy.test2.ComputerBook"); // 用(包名.类名)访问
System.out.println(book.getTitle());
}
}
【结果】
不管有多少个子类,工厂类都可以使用,这就是反射机制所带来的好处,而在日后的开发之中,如果发现有时候需要写出完整的“包.类”,就表示此处使用了反射机制。
反射的其他操作
在之前已经学习了反射机制进行对象实例化的操作过程,但是不要忘记,在之前的类中所提供的都是无参构造方法。
如果此时类中没有提供无参构造方法,只提供了有参构造方法,则就必须明确地调用指定的构造方法才可以通过反射实例化对象。
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws
NoSuchMethodException, SecurityException;
//在Constructor类之中提供有一个实例化对象方法。
public T newInstance(Object... initargs) throws
InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
调用构造方法取得实例化对象,如下例:
package com.xy.test2;
import java.lang.reflect.Constructor;
class Book {
private String title;
private double price;
public Book(String title, double price) {
super();
this.title = title;
this.price = price;
}
@Override
public String toString() {
return "Book[title = " + title + ", price = " + price + "]";
}
}
public class ReflectionDemo4 {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.xy.test2.Book"); // 用(包名.类名)访问
Constructor<?> cons = cls.getConstructor(String.class, double.class);
Book book = (Book)cons.newInstance("Java开发经典实践", 79.8); // 实例化一个对象
System.out.println(book);
}
}
【结果】
很明显,此时类之中还是提供无参构造方法会更加方便一些,这一点就是在简单Java类之中要求提供无参构造的关键因素。
在之前针对于属性的操作明确给出了要求,利用setter、getter设置和取得,而且对于setter、getter要求其必须按照指定的格式编写。而之所以存在这样的要求,也是因为反射机制的原因。
//此时可以使用Class类的如下方法取得方法的对象。
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException;
//取得了Method类的对象之后可以利用以下方法进行方法的反射调用。
public Object invoke(Object obj, Object... args) throws IllegalAccessException,
IllegalArgumentException, InvocationTargetException;
package com.xy.test2;
import java.lang.reflect.Method;
class Book {
private String title;
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
}
public class ReflectionDemo5 {
public static void main(String[] args) throws Exception {
String filedName = "title"; // 要操作的属性
String titleValue = "Java开发实践经典";
Class<?> cls = Class.forName("com.xy.test2.Book");
Object obj = cls.newInstance(); // 产生对象可以分配堆内存
Method setMethod = cls.getMethod("set" + initcap(filedName), String.class);
Method getMethod = cls.getMethod("get" + initcap(filedName));
setMethod.invoke(obj, titleValue); // 对象.setTitle()调用
System.out.println(getMethod.invoke(obj)); // 对象.getTitle()调用
}
public static String initcap(String str) {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
}
【结果】
发现有了反射之后,只要有Object对象,以及要操作的属性名称就可以直接利用反射完成了,这个就是setter、getter命名标准的定义由来。
框架开发原理 = 反射机制 + XML解析