反射机制的认识:
"反":就是指可以利用对象找到对象的出处;
- 取得class对象:public final Class<?> getClass()
范例:观察反
public class test {
public static void main(String args[]) throws Exception {
Date date = new Date();
System.out.println(date.getClass()); // class java.util.Date
}
}
getClass找到了这个类的完整名称;
Class类对象的实例化:
java.lang.Class是一个类,这个类是反射操作的源头,即:所有的类都要从此类开始进行;有3种实例化方式:
- 调用Object类中的getClass()方法;(几乎很少用)
public class test {
public static void main(String args[]) throws Exception {
Date date = new Date();
Class<?> cls = date.getClass();
System.out.println(cls); // class java.util.Date
}
}
- 使用 "类.class" 取得;(用于Hibernate、MyBatis、Spring)
public class test {
public static void main(String args[]) throws Exception {
Class<?> cls = Date.class;
System.out.println(cls); // class java.util.Date
}
}
之前是在类产生了实例化之后调用class类对象,这种方式并没有实例化对象就直接调用了;
- 调用Class类一个方法:
|- public static Class<?> forName(string className) throws ClassNotFoundException;
public class test {
public static void main(String args[]) throws Exception {
Class<?> cls = Class.forName("java.util.Date");
System.out.println(cls); // class java.util.Date
}
}
此时可以不用import导入一个明确的类,而类名称是用string的形式进行描述;
反射实例化对象:
范例:利用反射实例化对象
package com.sunny.work;
class Book {
public Book() {
System.out.println("*************");
}
@Override
public String toString() {
return "这是一本书!";
}
}
public class test {
public static void main(String args[]) throws Exception {
Class<?> cls = Class.forName("com.sunny.work.Book");
Object obj = cls.newInstance(); // 使用new调用无参构造
Book book = (Book) obj;
System.out.println(book);
}
}
有了反射之后,对象实例化可以不仅仅使用关键字 new ,可以使用反射进行对象实例化,但是这不意味 new 被完全取代了;
在任何程序中,new 是造成耦合的最大元凶;例如:工厂设计模式吃水果的例子,如果一开始只有一个水果苹果,需要一个new来实例化进而输出 "吃苹果" ,现在又加了一个橘子类要输出 "吃橘子" ,按理来说要再写一个橘子类,if else用到 equals 进行判断匹配,这时候只要新增一个类就要重写工厂类,但是如果使用反射,只是根据传入的类名进行取得,所以不必每次更改工厂类的代码;完成了解耦合操作,可扩展性十分强。
调用构造方法:
范例:调用有参构造
package com.sunny.work;
import java.lang.reflect.Constructor;
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 "书名:" + this.title + ", 价格:" + this.price;
}
}
public class test {
public static void main(String args[]) throws Exception {
Class<?> cls = Class.forName("com.sunny.work.Book");
Constructor<?> con = cls.getConstructor(String.class, double.class); //类名
Object obj = con.newInstance("Java开发", 79.8);
System.out.println(obj);
}
}
所以说,不管Java里面有多少构造方法,请至少保留一个无参构造,有参构造太麻烦了;
调用普通方法:
类中的普通方法只有在实例化对象之后才可以调用,并且实例化对象的方式有3种:new、克隆、反射;
范例:反射调用方法
package com.sunny.work; //title.java
class Book {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
package com.sunny.work; //test.java
import java.lang.reflect.Method;
public class test {
public static void main(String args[]) throws Exception {
String fieldName = "title"; // 要操作的成员
Class<?> cls = Class.forName("com.sunny.work.Book");
Object obj = cls.newInstance(); //必须给出实例化对象
Method setMet = cls.getMethod("set" + initcap(fieldName), String.class);
Method getMet = cls.getMethod("get" + initcap(fieldName));
setMet.invoke(obj, "Java开发"); // 等价于Book类中的setTitle方法
System.out.println(getMet.invoke(obj));
}
public static String initcap(String str) {
return str.substring(0, 1).toUpperCase() + str.substring(1); // 首字母转大写操作
}
}
此时完全看不到具体的操作类型,也就是说利用反射可以实现任意类的指定方法的调用;
反射调用成员:
类中的属性一定是在本类实例化对象之后才可以分配内存空间;