反射是各类框架实现的核心,框架开发原理=反射机制+xml解析。
一、反射机制含义
在运行状态中,对于任意一个类,都能够获取它的属性和方法;对于任意一个对象,都能够调用它的方法,这种动态获取信息和调用对象的机制称为java语言的反射机制。
二、反射机制四种功能
1.类
(1)判断任意一个对象是否属于某个类(找到对象对应的类);
Date date = new Date();
Class<?>classs = date.getClass();
System.out.println(classs);
运行结果:class java.util.Date
(2)运行时构造某个类的一个对象。
tip:实例化对象由两种方式:一种关键字new,另一种反射实现
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException{
Class<?>cls = Class.forName("Book");
Book book = (Book) cls.newInstance();
book.setName("云边有个小卖部");
book.setPrice(20.00);
System.out.println(book);
}
class Book{
private String name;
private double price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
运行结果:Book{name='云边有个小卖部', price=20.0}
2.对象
(3)判断任意一个对象具有的属性和方法;
(4)运行时调用任意一个对象的方法。
通过反射调用setter、getter发现:有了反射之后,只要有Object对象,以及要操作的属性名称就可以直接利用反射完成了,这个就是setter、getter命名标准的定义由来
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException{
Class<?>cls3 = Class.forName("Book");
String fieldName1 = "name";
String fieldName2 = "price";
String nameValue = "语文";
Double priceValue = 30.00;
Object obj = cls3.newInstance();
Method setMethod1 = cls3.getMethod("set"+ initcap(fieldName1),String.class);
// System.out.println(initcap(fieldName1));
Method setMethod2 = cls3.getMethod("set"+ initcap(fieldName2),double.class);
Method getMethod1 = cls3.getMethod("get"+ initcap(fieldName1));
Method getMethod2 = cls3.getMethod("get"+ initcap(fieldName2));
setMethod1.invoke(obj,nameValue);
setMethod2.invoke(obj,priceValue);
System.out.println(getMethod1.invoke(obj));
System.out.println(getMethod2.invoke(obj));
}
class Book{
private String name;
private double price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
运行结果:语文 30.0
三、反射机制好处
不管有多少个子类,工厂类都可以使用,这就是反射机制所带来的好处,而在日后的开发之中,如果发现有时候需要写出完整的“包.类”,就表示此处使用了反射机制。
下面通过传统的工厂模式说明反射机制的好处。
代码实例:
public class Test2 {
public static void main(String[] args) {
Books bk = Factory.getInstance("MathBook");
System.out.println(bk.getName());
Books bkk = Factory.getInstance("EnglishBook");
System.out.println(bkk.getName());
}
}
interface Books{
public String getName();
}
class MathBook implements Books{
@Override
public String getName() {
return "数学书";
}
}
class YuwenBook implements Books{
@Override
public String getName() {
return "语文书";
}
}
class EnglishBook implements Books{
@Override
public String getName() {
return "英语书";
}
}
class Factory{
public static Books getInstance(String className){
if ("MathBook".equals(className)) {
return new MathBook();
}else if ("YuwenBook".equals(className)) {
return new YuwenBook();
}
return null;
}
}
}
运行结果:
可以看到如果需要添加Books的子实现类,则需要修改整个工厂类,这样会导致高耦合。
调整后工厂类代码:
class Factory2{
public static Books getInstance(String className){
Books books = null;
try {
books = (Books)Class.forName(className).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return books;
}
}
运行结果:
数学书
英语书
可见,无需修改工厂类,新增Books子类后,依旧可以正确生成子类实例。
四、有参构造和无参构造的不同反射实例化方式
无参:
Class<?>cls = Class.forName("Book"); Book book = (Book) cls.newInstance();
有参:
Class<?> cls2 = Class.forName("Fruit"); Constructor<?>cons = cls2.getConstructor(String.class,String.class); Fruit fruit = (Fruit) cons.newInstance("苹果","apple");
//反射实例化对象:只有无参构造方法,没有有参构造
Class<?>cls = Class.forName("Book");
Book book = (Book) cls.newInstance();
book.setName("云边有个小卖部");
book.setPrice(20.00);
System.out.println(book);
//反射实例化对象,有有参构造
Class<?> cls2 = Class.forName("Fruit");
// Constructor<?>cons = cls2.getDeclaredConstructor(String.class,String.class);
Constructor<?>cons = cls2.getConstructor(String.class,String.class);
Fruit fruit = (Fruit) cons.newInstance("苹果","apple");
System.out.println(fruit);
class Book{
private String name;
private double price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
class Fruit{
private String name;
private String EnName;
public Fruit(String name, String enName) {
this.name = name;
EnName = enName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEnName() {
return EnName;
}
public void setEnName(String enName) {
EnName = enName;
}
@Override
public String toString() {
return "Fruit{" +
"name='" + name + '\'' +
", EnName='" + EnName + '\'' +
'}';
}
}
如果存在有参构造方法,依旧使用无参时候的实现方式,会出现如下异常:
实例:
Class<?> cls2 = Class.forName("Fruit");
Fruit fruit1 = (Fruit) cls2.newInstance();
fruit1.setName("香蕉");
fruit1.setEnName("bananna");
System.out.println(fruit1);
运行结果:
所以:类中提供无参构造方法会更加方便一些,这一点就是在简单Java类之中要求提供无参构造的关键因素。