学习任务:

视频学习

再谈工厂模式

反射入门程序

分析反射的过程

反射的应用场景

在 “工厂模式” 章节我们基于面向接口编程的方法实现了工厂模式,工厂模式将使用方与类之间解耦合,如下图:

(关于 ” 面向接口编程实现工厂模式“ 请参考“工厂模式”章节的内容。)

image-20200930060903547

下边是曾经实现的一个简单工厂的例子,本例实现的功能是根据支付类型生产不同的支付对象,比如:当支付类型为ali则工厂会生产支付宝支付对象,当支付类型为weixin则工厂会生产微信支付对象。

支付接口及支付类如下,本例主要演示工厂模式,支付接口中方法暂时为空。

package com.pbteach.javase.oop.reflect;

/**
 * 支付基础接口
 * @author 攀博课堂
 * @version v1.0
 */
public interface Pay {

}
package com.pbteach.javase.oop.reflect;

/**
 * 支付宝支付类
 * @author 攀博课堂
 * @version v1.0
 */
public class PbAliPay implements Pay {

}
package com.pbteach.javase.oop.reflect;

/**
 * 微信支付类
 * @author 攀博课堂
 * @version v1.0
 */
public class PbWeixinPay implements Pay {

}
package com.pbteach.javase.oop.reflect;

/**
 * 银联支付类
 * @author 攀博课堂
 * @version v1.0
 */
public class PbUnionpay implements Pay {

}

工厂的代码如下:

package com.pbteach.javase.oop.reflect;

import java.util.Scanner;

/**
 * 支付对象工厂
 * @author 攀博课堂
 * @version v1.0
 */
public class PbPayFactory {
	//根据支付类型获取支付服务对象
		public static Pay getPay(String payType) {
			Pay pay = null;
			switch (payType) {
	            case "ali":
	                pay=new PbAliPay();
	                break;
	            case "weixin":
	                pay=new PbWeixinPay();
	                break;
	            case "union":
	                pay=new PbUnionpay();
	                break;
	            default:
	                break;
			}
			return pay;
		}
		
		public static void main(String[] args) {
			Scanner sc = new Scanner(System.in);
			while(true) {
				System.out.println("请输入支付类型:");
				System.out.println("ali:支付宝");
				System.out.println("weixin:微信支付");
				System.out.println("union:银联支付");
				System.out.println("exit:退出");
				String input = sc.nextLine();
				if("exit".equals(input)) {
					break;
				}
				//从工厂中获取支付对象
				Pay pay = PbPayFactory.getPay(input);
				System.out.println(pay);
			}
		}
}

测试:

请输入支付类型:
ali:支付宝
weixin:微信支付
union:银联支付
exit:退出
ali
com.pbteach.javase.oop.reflect.PbAliPay@4aa298b7
请输入支付类型:
ali:支付宝
weixin:微信支付
union:银联支付
exit:退出
weixin
com.pbteach.javase.oop.reflect.PbWeixinPay@7d4991ad
请输入支付类型:
ali:支付宝
weixin:微信支付
union:银联支付
exit:退出

上边的代码看似没有问题,但是我们思考一个问题,接口会有很多实现类,比如:支付宝支付类定义了1.0版本的实现 PbAliPay,在工厂中通过如下方法构造对象:

Pay pay=new PbAliPay();

软件难免要进行升级,当PbAliPay实现了2.0版本呢?基于面向接口编程的思想会增加新的2.0版本的实现类,构造对象方式如下:

Pay pay=new PbAliPay2();

如果直接修改代码更改为2.0的实现类,则需要重新编译、启动系统,对于一些更改频繁的内容最好能不用重启即可更改,这样有利于提高生产效率及用户体验。

有没有一种方式无需重新启动程序即可构造一个新类的对象呢?

Java中提供反射机制,可解决上述需求。

Java反射机制可以在运行时去操作一个类,可以解析出类的构造方法、成员方法、成员变量等对象,并可以操作这些对象,如果想在运行时构造一个新类的对象,通过反射可以解析出新类的构造方法,通过构造方法即可创建对象。

下边代码实现了在运行时输入类名即可构造一个对象。

如下代码:

package com.pbteach.javase.oop.reflect;

import java.lang.reflect.Constructor;
import java.util.Scanner;

/**
 * TODO(说明此类的作用)
 * @author 攀博课堂
 * @version v1.0
 */
public class PbPayFactory2 {
	//根据支付类型获取支付服务对象
		public static Pay getPay(String payType) {
			Pay pay = null;
			switch (payType) {
	            case "ali":
	                pay=new PbAliPay();
	                break;
	            case "weixin":
	                pay=new PbWeixinPay();
	                break;
	            case "union":
	                pay=new PbUnionpay();
	                break;
	            case "newclass":
	            	pay=createObj();
	            	break;
	            default:
	                break;
			}
			return pay;
		}
		
		public static void main(String[] args) {
			Scanner sc = new Scanner(System.in);
			while(true) {
				System.out.println("请输入支付类型:");
				System.out.println("ali:支付宝");
				System.out.println("weixin:微信支付");
				System.out.println("union:银联支付");
				System.out.println("newclass:输入类名");
				System.out.println("exit:退出");
				String input = sc.nextLine();
				if("exit".equals(input)) {
					break;
				}
				Pay pay = PbPayFactory2.getPay(input);
				System.out.println(pay);
			}
		}
		
		public static Pay createObj() {
			Scanner sc = new Scanner(System.in);
			System.out.println("请输入类名:");
			String className = sc.nextLine();
			try {
				 //要操作一个类首先获取类的Class对象
				 Class clazz = Class.forName(className);
				 //通过class对象获取构造方法
				 Constructor constructor = clazz.getConstructor();
				 //通过构造方法(对象)创建对象
				 Pay obj = (Pay) constructor.newInstance();
				 return obj;
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
			
		}
}

测试:

第一步:运行程序,为运行时通过类名创建对象创造条件

请输入支付类型:
ali:支付宝
weixin:微信支付
union:银联支付
newclass:输入类名
exit:退出

第二步:创建新类PbAliPay2

package com.pbteach.javase.oop.reflect;

/**
 * 支付宝支付类
 * @author 攀博课堂
 * @version v1.0
 */
public class PbAliPay2 implements Pay {

}

第三步:输入类名创建对象

请输入支付类型:
ali:支付宝
weixin:微信支付
union:银联支付
newclass:输入类名
exit:退出
newclass
请输入类名:
com.pbteach.javase.oop.reflect.PbAliPay2
com.pbteach.javase.oop.reflect.PbAliPay2@4aa298b7

通过测试发现在运行时只要知道类的全限定名即可创建一个对象,通过反射创建对象就不需要通过new的方法创建对象。

反射是Java的一种操作类的机制,它可以在运行时获取这个类的属性、方法等信息从而去操作这个类。

如下图所示:

image-20210218165004194

下边分析反射的过程:

1、编写Java源文件

2、编译class字节码文件

3、通过反射在内存获取Class对象。

 //要解剖一个类首先获取类的Class对象
Class clazz = Class.forName(className);

Class对象包括了该类的详细信息。

4、通过Class对象得到class的构造方法,成员方法,成员变量等对象。

//通过class对象获取构造方法对象
 Constructor constructor = clazz.getConstructor();

5、通过构造方法对象创建该类的对象

 //通过构造方法(对象)创建对象
Pay obj = (Pay) constructor.newInstance();

反射机制通常应用在框架或系统架构的基础代码中,比如:系统架构使用工厂模式作为创建对象实例的方案,通常是将类的名称配置 在配置文件中,通过读取配置文件中的类名从而通过反射创建对象,供项目的其它对象使用。

所以,建议Java程序员学习反射,日后在项目开发中使用反射技术对基础代码的抽取与封装。

提问-攀博课堂
我要提问 不会就问,有效沟通
关注公众号,加入微信群交流提问。 攀博课堂官方公众号
问答列表,查看本知识点所有问题