工厂模式,最重要的是反射。
简单工厂:简单使用java反射
案例:女娲造人
/
* 定义一个人类的统称
*/
public interface Human {
//首先定义什么是人类
//人是愉快的,会笑的,本来是想用smile表示,想了一下laugh更合适,好长时间没有大笑了;
public void laugh();
//人类还会哭,代表痛苦
public void cry();
//人类会说话
public void talk();
}
接口的实现类:
/**
* 黄色
*/
public class YellowHuman implements Human {
public void cry() {
System.out.println("黄色人类会哭");
}
public void laugh() {
System.out.println("黄色人类会大笑,幸福呀!");
}
public void talk() {
System.out.println("黄色人类会说话,一般说的都是双字节");
}
}
/**
* 白色
*/
public class WhiteHuman implements Human {
public void cry() {
System.out.println("白色人类会哭");
}
public void laugh() {
System.out.println("白色人类会大笑,侵略的笑声");
}
public void talk() {
System.out.println("白色人类会说话,一般都是但是单字节!");
}
}
/**
* 黑色
*/
public class BlackHuman implements Human {
public void cry() {
System.out.println("黑人会哭");
}
public void laugh() {
System.out.println("黑人会笑");
}
public void talk() {
System.out.println("黑人可以说话,一般人听不懂");
}
}
工厂要实现的就是这些接口实现类
public class HumanFactory {
//定一个烤箱,泥巴塞进去,人就出来,这个太先进了
public static Human createHuman(Class c){
Human human=null; //定义一个类型的人类
try {
//c是一个Class类对象
human = (Human)Class.forName(c.getName()).newInstance(); //产生一个人类
} catch (InstantiationException e) {
//你要是不说个人类颜色的话,没法烤,要白的黑,你说话了才好烤
System.out.println("必须指定人类的颜色");
} catch (IllegalAccessException e) { //定义的人类有问题,那就烤不出来了,这是...
System.out.println("人类定义错误!");
} catch (ClassNotFoundException e) { //你随便说个人类,我到哪里给你制造去?!
System.out.println("混蛋,你指定的人类找不到!");
}
return human;
}
}
工厂开始工作
public class NvWa {
public static void main(String[] args) {
//女娲第一次造人,白人
System.out.println("------------造出的第一批人是这样的:白人-----------------");
Human whiteHuman = HumanFactory.createHuman(WhiteHuman.class);
whiteHuman.cry();
whiteHuman.laugh();
whiteHuman.talk();
//女娲第二次造人,黑人
System.out.println("\n\n------------造出的第二批人是这样的:黑人-----------------");
Human blackHuman = HumanFactory.createHuman(BlackHuman.class);
blackHuman.cry();
blackHuman.laugh();
blackHuman.talk();
//第三批人了,黄色人类
System.out.println("\n\n------------造出的第三批人是这样的:黄色人类-----------------");
Human yellowHuman = HumanFactory.createHuman(YellowHuman.class);
yellowHuman.cry();
yellowHuman.laugh();
yellowHuman.talk()
}
}
改进一下:
01.getAllClassByInterface():
给一个接口,返回这个接口的所有实现类
//给一个接口,返回这个接口的所有实现类
public static List<Class> getAllClassByInterface(Class c){
List<Class> returnClassList = new ArrayList<Class>(); //返回结果
//如果不是一个接口,则不做处理
if(c.isInterface()){
String packageName = c.getPackage().getName(); //获得当前的名
try {
List<Class> allClass = getClasses(packageName); //获得当前包以及子包下的所有类
//判断是否是同一个接口
for(int i=0;i<allClass.size();i++){
if(c.isAssignableFrom(allClass.get(i))){ //判断是不是一个接口
if(!c.equals(allClass.get(i))){ //接口本身不加进去
returnClassList.add(allClass.get(i));
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return returnClassList;
}
isAssignableFrom
如果是A.isAssignableFrom(B)
确定一个类(B)是不是继承来自于另一个父类(A),一个接口(A)是不是实现了另外一个接口(B),或者两个类相同。主要,这里比较的维度不是实例对象,而是类本身,因为这个方法本身就是Class类的方法,判断的肯定是和类信息相关的。
02.getClasses():
从一个包中查找出所有的类,在jar包中不能查找(没有避免jar的包,这个方法没有写协议的区别,File或者jar判断)
输入的是包名
本次实验的接口和接口实现类都放在同一个包中
//从一个包中查找出所有的类,在jar包中不能查找
private static List<Class> getClasses(String packageName)
throws ClassNotFoundException, IOException {
//获得当前的classLoader
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//public String replace(char searchChar, char newChar)
// 把 . 转换成 \ 因为下面是文件操作
String path = packageName.replace('.', '/');
//URL集合,拿到path的全部URL路径
Enumeration<URL> resources = classLoader.getResources(path);
//文件集合,通过URL集合,来获取相关的全部文件,这里获取的是包所在的一个目录
List<File> dirs = new ArrayList<File>();
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
dirs.add(new File(resource.getFile()));
}
ArrayList<Class> classes = new ArrayList<Class>();
for (File directory : dirs) {
classes.addAll(findClasses(directory, packageName));
}
return classes;
}
这个方法中的用到的方法
01.Thread.currentThread().getContextClassLoader()
返回该线程的ClassLoader上下文。线程创建者提供ClassLoader上下文,以便运行在该线程的代码在加载类和资源时使用。如果没有,则默认返回父线程的ClassLoader上下文。
子线程的ClassLoader与父线程的一致
02.我们先了解下ClassLoader下的 getResources() 方法:
// 找出当前环境下/name 的所有的资源磁盘路径(包括jar包和一般文件)
public Enumeration<URL> getResources(String name) ;
一.首先说一个概念,classpath,指的是编译后的class文件、xml、properties等配置文件所在的目录。比如,如果是maven项目,classpath为“项名/target/classes”,如果是普通项目,可能是”项目名/bin”,或者”项目名/build/classes”等等。
二.先解释getResource
getResource是java.lang.Class的方法,也就是由字节码对象调用。
getResource接受一个字符串参数,如果以”/”开头,就在classpath根目录下找(不会递归查找子目录),如果不以”/”开头,就在调用getResource的字节码对象所在目录下找(同样不会递归查找子目录)。
例子:
System.out.println(Test1.class.getResource("/"));
System.out.println(Test1.class.getResource("Test1.class"));
第1句,会输出classpath的根目录。
第2句,会输出Test1.class所在目录。
输出结果:
file:/D:/eclipse-workspace/javase/bin/
file:/D:/eclipse-workspace/javase/bin/com/trs/javase/Test1.class
三.getResource与getResources
getResource与getResources 都是【加载当前类加载器以及父类加载器所在路径的资源文件】
/**
* 1.getResource
* 加载当前类加载器以及父类加载器所在路径的资源文件
* 将遇到的第一个资源文件直接返回!!!
* 比如当前工程类路径有conf/demo.properties文件,引入的第三方jar包也有这个文件
* 返回的是当前工程下的这个资源文件
**/
URL url = loader.getResource("conf/demo.properties");
/**
* 2.getResources
* 加载当前类加载器以及父类加载器所在路径的资源文件
* 将遇到的所有资源文件全部返回!
* 比如当前工程类路径有conf/demo.properties文件,引入的第三方jar包也有这个文件
* 则将这些文件全部返回
*/
@Override
public void run(String... args) throws Exception {
ClassLoader loader = ClassUtils.getDefaultClassLoader();
URL url = loader.getResource("META-INF/spring.factories");
System.out.println("Resource :" + url.getPath());
System.out.println("=====================================");
Enumeration<URL> enumeration = loader.getResources("META-INF/spring.factories");
// 打印出所有同名的资源文件
while (enumeration.hasMoreElements()) {
URL url1 = enumeration.nextElement();
System.out.println("Resources :" + url1.getFile());
}
}
由上面的输出可以看出,使用getResource时,只读取了当前模块resources下的MATE-INF/spring.factories文件。使用getResources,则读取到了所有依赖的包resources下的MATE-INF/spring.factories文件。
03.Enumeration接口
public interface Enumeration<E>
//实现 Enumeration 接口的对象,
//它生成一系列元素,一次生成一个。连续调用 nextElement 方法将返回一系列的连续元素。
例如,要输出 Vector<E> v 的所有元素,可使用以下方法:
for (Enumeration<E> e = v.elements(); e.hasMoreElements();)
System.out.println(e.nextElement());
Enumeration<E>的 boolean hasMoreElements()
测试此枚举是否包含更多的元素。
返回:
当且仅当此枚举对象至少还包含一个可提供的元素时,才返回 true;否则返回 false。
Enumeration<E>的 nextElement()
如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
返回:
此枚举的下一个元素。
抛出:
NoSuchElementException - 如果没有更多的元素存在。
04.URL类型
String getFile() 获取此 URL的文件名。
03.findClasses方法:找到该目录中的文件和目录
private static List<Class> findClasses(File directory, String packageName)
throws ClassNotFoundException {
List<Class> classes = new ArrayList<Class>();
if (!directory.exists()) {
return classes;
}
File[] files = directory.listFiles(); //得到的是一个 File 类型的数组,返回的是该目录中的文件和目录。
for (File file : files) {
if (file.isDirectory()) { //递归
assert !file.getName().contains(".");
classes.addAll(findClasses(file, packageName + "." + file.getName()));
} else if (file.getName().endsWith(".class")) { //反射
classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
}
}
return classes;
}
01.File类中的listFiles()得到的是一个 File 类型的数组,返回的是该目录中的文件和目录。
工厂的新方法:
public class HumanFactory {
public static Human createHuman(){
Human human=null; //定义一个类型的人类
//首先是获得有多少个实现类,多少个人类
List<Class> concreteHumanList =
ClassUtils.getAllClassByInterface(Human.class); //定义了多少人类
//八卦炉自己开始想烧出什么人就什么人
Random random = new Random();
int rand = random.nextInt(concreteHumanList.size());
human = createHuman(concreteHumanList.get(rand));
return human;
}
}
工厂开始工作:
public class NvWa {
public static void main(String[] args) {
for(int i=0;i<10000000000;i++){
System.out.println("\n\n------------随机产生人类了-----------------" + i);
Human human = HumanFactory.createHuman();
human.cry();
human.laugh();
human.talk();
}
}