java的反射机制
所谓的"反"就是指可以利用对象找到对象的出处,在Object类里面提供有一个方法:
• 取得Class对象:public final Class<?> getClass();
Class类对象实例化:
java.lang.Class是一个类, 这个类是反射操作的源头,即:所有的反射都要从此类开始进行,而最关键的是这个类有三种实例化方式
• 调用Object类中的getClass()方法: Class<?> clazz = new Date().getClass();
• 调用"类.class" : Class<?> clazz = Date.class;
• 调用类Class提供的一个方法: Class<?> clazz = Class.forName("java.util.Date");
反射实例化对象:
先有Class对象
• 实例化对象方法: public T newInstance()
class Book{
public Book() {
System.out.println("********** Book类的无参构造方法 **********");
}
@Override
public String toString() {
return "这是一本书";
}
}
public class TestClazz {
public static void main(String[] args)throws Exception {
Class<?> clazz = Class.forName("com.feng.Clazz.Book");
Object o = clazz.newInstance();
}
}
在任务的开发中, new 是造成耦合的最大元凶,一切的耦合都起源于new
工厂设计模式:每增加一个类就要去修改工厂类,如果随时都可能增加子类?
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 name){ if ("apple".equals(name)){ return new Apple(); } else if ("orange".equals(name)){ return new Orange(); } return null; } } public class TestFactoryPattern { public static void main(String[] args) { Fruit fruit = Factory.getInstance("apple"); fruit.eat(); } }
解决办法: 做反射
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 name) throws Exception{ Fruit f = (Fruit)Class.forName(name).newInstance(); return f; } } public class TestFactoryPattern { public static void main(String[] args) throws Exception { Fruit fruit = Factory.getInstance("com.feng.factory.Orange");//全类名 fruit.eat(); } }
使用反射调用构造:
在之前所编写的代码中, 实际上发现都默认使用了类中的无参构造方法,如果类中不提供无参的构造方法呢?
类中不存在无参构造
class Book{ private String title; private double price; public Book(String title, double price) { this.title = title; this.price = price; } @Override public String toString() { return "Book{" + "title='" + title + '\'' + ", price=" + price + '}'; } }
由于类中不存在无参构造方法,下面的程序是错误的
public class TestClazz {
public static void main(String[] args)throws Exception {
Class<?> clazz = Class.forName("com.feng.Clazz.Book");
Object o = clazz.newInstance(); //相当于使用new调用无参构造实例化对象
Book book = (Book) o;
System.out.println(book);
}
}
Exception in thread "main" java.lang.InstantiationException: com.feng.Clazz.Book at java.lang.Class.newInstance(Class.java:427) at com.feng.Clazz.TestClazz.main(TestClazz.java:33) Caused by: java.lang.NoSuchMethodException: com.feng.Clazz.Book.<init>() at java.lang.Class.getConstructor0(Class.java:3082)
所以在这种情况下,只能够调用有参构造方法:
• 在Class类中, 有一种方法可以取得全部构造: public Constructor<T>[] getConstructors()
• 在Class类中, 有一种方法可以取得指定参数(参数为类型)顺序的构造: public Constructor<T> getConstructor(Class<?> ..parameterTypes)
以上两个方法返回的都是“java.lang.reflect.Constructor”类的对象,在这个类中提供有一个明确传递有参构造内容的实例化对象方法:
• public T newInstance(Object...initargs) // 参数是具体的入参
class Book{ private String title; private double price; public Book(String title, double price) { this.title = title; this.price = price; } @Override public String toString() { return "Book{" + "title='" + title + '\'' + ", price=" + price + '}'; } } public class TestClazz { public static void main(String[] args)throws Exception { Class<?> clazz = Class.forName("com.feng.Clazz.Book"); Constructor<?> constructor = clazz.getConstructor(String.class, double.class); Object b = constructor.newInstance("java开发", 20.2); System.out.println(b); } }
反射调用方法:
方法的调用: 一个类产生实例化对象之后才可以调用。实例化对象的三种方式:
在Class类里面提供有以下取得类中Method的操作:
• 取得一个类中的全部方法: public Method[] getMethods();
• 取得一个类中的指定方法: public Method getMethod(Sting methodName, Class<?> ....parameterType) // parameterType: 参数类型
以上的两个操作返回的是Method类的对象, 在这个类里面,重点关注一个方法“
• public Object invoke(Object obj, Object...args)
obj: 实例化对象
args: 参数内容
利用反射调用方法:
class Book{ private String title; public Book() { } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } } public class TestClazz { public static void main(String[] args)throws Exception {
//要操作的成员 String fieldName = "title"; Class<?> clazz = Class.forName("com.feng.Clazz.Book");
//必须给出实例化对象 Object obj = clazz.newInstance(); Method setMethod = clazz.getMethod("set" + initCap(fieldName), String.class); Method getMethod = clazz.getMethod("get" + initCap(fieldName)); //等价于:Book类对象.setTitle("Java开发") setMethod.invoke(obj, "Java开发"); System.out.println(getMethod.invoke(obj)); } public static String initCap(String str){ return str.substring(0,1).toUpperCase() + str.substring(1); } }
此时完全看不见具体的操作类型,也就是利用反射可以实现任意类的指定方法的调用
反射调用成员:
类中的属性一定要在本类实例化对象之后才可以分配内存空间。在Class类里面提供取得成员的方法
• 取得全部员: public Field[] getDeclaredFields();
• 取得指定成员: public Field getDeclaredField(String name);
参数name : 成员的名称
返回的类型是Field类, 在这个类里面有两个重要的方法:
• 取得属性内容: public Object get(Object obj);
参数obj: 实例化对象
• 设置属性内容: public void set(Object obj, Object value);
参数obj: 实例化对象
参数value: 参数内容
在AccessibleObject类下面:(jdk1.8之后修改)
• Executable: 下面继承了Constructor、 Method
• Field :
在这个类里面提供有一个方法:
• 设置是否封装: public void setAccessible(boolean flag)
反射调用属性:
class Book{ private String title; } public class TestClazz { public static void main(String[] args)throws Exception { Class<?> clazz = Class.forName("com.feng.Clazz.Book"); Object obj = clazz.newInstance();//必须给出实例化对象 Field field = clazz.getDeclaredField("title"); //封装取消应该设置在赋值之前 field.setAccessible(true); field.set(obj, "Java开发"); System.out.println(field.get(obj)); } }
构造方法和普通方法也同样可以取消封装,只不过很少这样去做,而且对于属性的访问还是建议使用setting和getting方法完成;
总结:
1. 实例化对象的方式又增加了一种反射
2. 反射调用类结构只是开始