什么是反射
Java反射机制是在运行状态中,对于任意类,都能知道这个类的全部属性和方法,对于任意对象,都能够调用它的任何一个方法或属性。这种动态获取的信息以及动态调用对象的方法的功能,称为Java语言的反射机制。
Class类
Class 是JDK提供的一个类,完整路径为 java.lang.Class,Class是反射能够实现的基础。对于每一个类,Java虚拟机都会初始化出一个Class类型的对象,Java 中的所有类型包括基本类型(int, long, float 等),即使是数组也有与之对应的 Class 类的对象。每当我们编写并且编译一个新创建的类就会产生一个对应Class对象,并且这个Class对象会被保存在同名.class文件中。当我们new一个新对象或者引用静态成员变量时,JVM中的类加载器系统会将对应Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象,创建我们需要实例对象或者提供静态变量的引用值。
Class类对象实例化
获取Class对象的三种方式,如下:
- 利用Object类中提供的getClass方法获取实例化对象,但是需要获取一个类的实例化对象后,才可以获取Class类实例。
- 使用“类.class” 形式获取指定类 或接口的Class实例化对象,但需要导入包,不导包就抛编译错误。
- 使用Class类内部提供的forName(className)获取Class类对象,动态加载类,className需要是类的全限定名。
Class类对象实例化
public class TestDemo {
public static void main(String args []) {
// new Node() 产生一个Node对象,一个Class对象
Node node = new Node();
// Class 类是反射机制的根源,可以通过Object类中所提供的方法获取Class实例
// 利用Object类中提供的getClass方法获取实例化对象,需要获取一个类的实例化对象后 才可以获取Class类实例,已经有实例化对象了,再反射没啥意思。
Class <?> cla1 = node.getClass();
System.out.println(cla1);
// 使用“类.class” 形式获取指定类 或接口的Class实例化对象,但需要导入包,不导包就抛编译错误。
Class <?> cla2 = Node.class; //获取Class类实例化对象
System.out.println(cla2 == cla1);
// 使用Class类内部提供的forName()获取Class类对象,简单,最常用
try {
Class cla3 = Class.forName("Node");
System.out.println(cla2 == cla3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Node{}
执行上述代码,输出结果为:
class Node
true
true
反射机制与工厂设计模式
通常一个接口可能不止有一个子类,使用工厂设计模式的主要特点是解决接口与子类之间因直接使用关键字new所造成的耦合问题,但是传统的工厂设计操作中会存在两个严重的问题。
- 传统工厂设计属于静态工厂设计,需要根据传入的参数并结合大量的分支语句来判断所需要实例化的子类,当一个接口或抽象类扩充子类时必须修改工厂类结构,否则将无法获取新的子类实例,如下:
class Node{}
class Fatory{
/**
* 因fatory 没有属性 因此定义static方法 。
* 获取IFood接口实例化对象,利用此方法对外隐藏子类
*/
private Fatory(){} // 工厂类,没有产生实例化的意义,避免产生实例化对象——构造方法私有化
/**
* 传统工厂设计属于静态工厂设计,需要根据传入的参数并结合大量的分支语句来判
* 断所需要实例化的子类,当一个接口或抽象类扩充子类时必须修改工厂类结构,
* 否则将无法获取新的子类实例。
*
*/
public static IFood eFood(String food) {
if ("面包".equals(food)) {
return new Break();
}else if ("牛奶".equals(food)) {
return new Milk();
}
else {
System.out.println("不存在该食物");
return null ;
}
}
}
- 工厂设计只能够满足一个接口或抽象类获取实例化对象的需求,如果有更多的接口或抽象类定义时将需要定义更多的工厂类或扩充工厂类中的satic方法。
利用反射机制解决第一个问题
package DemoRunable.example;
import java.io.IOException;
public class FileDemo {
public static void main(String args []) throws IOException, ClassNotFoundException {
System.out.println();
IFood food = Fatory.eFood("DemoRunable.example.Break");
food.eat();
}
}
interface IFood{
public abstract void eat();// 食物核心作用 被食用
}
class Break implements IFood{
@Override
public void eat() {
System.out.println("手撕面包");
}
}
class Milk implements IFood{
@Override
public void eat() {
System.out.println("喝牛奶");
}
}
class Fatory{
/**
* 因fatory 没有属性 因此定义static方法 。
* 获取IFood接口实例化对象,利用此方法对外隐藏子类
*/
private Fatory(){} // 工厂类,没有产生实例化的意义,避免产生实例化对象——构造方法私有化
/**
*
* @param className 实例化对象名称
* @return 如果子类存在则返回指定类的接口实例化对象
*/
public static IFood eFood(String className) {
IFood food = null;
try {
food = (IFood) Class.forName(className).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return food;
}
/*
public static IFood eFood(String food) {
if ("面包".equals(food)) {
return new Break();
}else if ("牛奶".equals(food)) {
return new Milk();
}
else {
System.out.println("不存在该食物");
return null ;
}
}
}
*/
}
使用泛型解决解决第二个问题。
package DemoRunable.example;
import java.io.IOException;
public class FileDemo {
public static void main(String args []) throws IOException, ClassNotFoundException {
System.out.println();
IFood food = Fatory.eFood("DemoRunable.example.Break",IFood.class);
food.eat();
IOther other = Fatory.eFood("DemoRunable.example.OtherOne",IOther.class);
other.send();
}
}
interface IFood{
public abstract void eat();// 食物核心作用 被食用
}
class Break implements IFood{
@Override
public void eat() {
System.out.println("手撕面包");
}
}
class Milk implements IFood{
@Override
public void eat() {
System.out.println("喝牛奶");
}
}
interface IOther{
public abstract void send();
}
class OtherOne implements IOther{
@Override
public void send(){
System.out.println("开心");
}
}
class OtherTwo implements IOther{
@Override
public void send(){
System.out.println("很开心");
}
}
class Fatory{
/**
* 因fatory 没有属性 因此定义static方法 。
* 获取IFood接口实例化对象,利用此方法对外隐藏子类
*/
private Fatory(){} // 工厂类,没有产生实例化的意义,避免产生实例化对象——构造方法私有化
/**
*
* @param className 实例化对象名称
* @param clazz 返回实例化对象类型
* @param <T> 泛型,使得工厂类适用所有类型
* @return 如果子类存在则返回指定类的接口实例化对象
*/
public static <T> T eFood(String className,Class<T> clazz) {
T food = null;
try {
food = (T) Class.forName(className).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return food;
}
}