Class.forName(String className) 实例化对象问题

在实验课上用xml配置文件实现工厂模式,其中用配置文件返回类实例时出现了一点小问题,故记之。

问题的描述:工厂方法模式日志记录器。某系统日志记录器要求支持多种日志记录方式,如文件日志记录(FileLog)、数据库日志记录(DatabaseLog)等,且用户可以根据要求动态选择日志记录方式,现使用工厂模式设计该系统。

    该工厂的类图如下:

实现代码:

package com.log;

//产品
interface Log {
	public void writeLog();
}


class DataBaseLog implements Log {
	public void writeLog() {
		System.out.println("写入数据库");
	}
}


class FileLog implements Log {
	public void writeLog() {
		System.out.println("写入文件");
	}
}

//工厂
interface LogFactory {
	public Log createLog();
}

class FileLogFactory implements LogFactory {
	public Log createLog(){
		return new FileLog();
	}
}

class DataBaseLogFactory implements LogFactory {
	public Log createLog(){
		return new DataBaseLog();
	}
}


public class Client{
	public static void main(String[] args) {
			//创建工厂
			LogFactory lgfactory = new DataBaseLogFactory();
			//创建日志产品
			Log lg = lgfactory.createLog();
			//调用写日志方法
			lg.writeLog();
	}
}


现需要从xml配置文件中读取具体工厂名,并返回工厂对象,这里使用java中的反射机制中的Class接口,其Class.forName(String className)方法可以返回与带有给定字符串名的类或接口相关联的Class对象,再通过Class对象的newInstance()方法创建此对象表示的类的一个新实例,即通过一个类名字符串得到类的实例。

如创建一个字符串类型的对象,其代码如下:

Class c = Class.forName("String");
Object obj = c.newInstance();
return obj;

值得注意的是这里的String类是包java.lang的类,每个java文件都会默认引入这个包。


再看配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<className>DataBaseLogFactory</className>
</config>
很简单,就是放置一个具体工厂类名。

通过获取配置文件中预先设置的类名,再利用反射机制获取类对象。通过引入DOM和反射机制后,可以在XMLUtil中实现读取XML文件并根据存储在XML文件中的类名获取对应的对象,XMLUtil类的详细代码如下:

package com.log;

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;

public class XMLUtil {
	@SuppressWarnings("unchecked")
	public static Object getBean(){
		try{
			//创建DOM文档对象
			DocumentBuilderFactory dFactory= DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = dFactory.newDocumentBuilder();
			Document doc = builder.parse(new File("D:\\Junior\\DesignPartern\\Log\\src\\com\\log\\config.xml"));
			//注意这里使用配置文件的绝对路径 	
			//获取包含结点类名的文本结点
			NodeList nl = doc.getElementsByTagName("className");
			Node classNode = nl.item(0).getFirstChild();
			String cName = classNode.getNodeValue();
			//System.out.println(cName);
			//通过类名生成实例对象并将其返回
			Class c = Class.forName(cName);			
			Object obj = c.newInstance();		
			return obj;
		} catch(Exception e){
			e.printStackTrace();
			return null;
		}
	}
}
将主测试类代码改为:

public class Client{
	public static void main(String[] args) {
		try {
			//创建工厂
			LogFactory lgfactory = (LogFactory)XMLUtil.getBean();
			//创建日志产品
			Log lg = lgfactory.createLog();
			//调用写日志方法
			lg.writeLog();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

在实验时,结果出现了错误:

java.io.FileNotFoundException: D:\Junior\DesignPartern\Log\config.xml (系统找不到指定的文件。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at sun.net.www.protocol.file.FileURLConnection.connect(Unknown Source)

后来经逐步测试发现错误出现在XMLUtil.java文件中的 

	Class c = Class.forName(cName);	

这里从配置文件获取的类名只是一个类名字符串,

Class.forName()主要功能
Class.forName(xxx.xx.xx)返回的是一个类
Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类,
也就是说JVM会执行该类的静态代码段。

我们再来看api文档中提供的关于Class.forName的详细解释:

  • forName
    public static Class<?> forName(String className)
                            throws ClassNotFoundException
    Returns the Class object associated with the class or interface with the given string name. Invoking this method is equivalent to:
    Class.forName(className, true, currentLoader)
    where currentLoader denotes the defining class loader of the current class.

    For example, the following code fragment returns the runtime Class descriptor for the class namedjava.lang.Thread:

    Class t = Class.forName("java.lang.Thread")

    A call to forName("X") causes the class named X to be initialized.

    Parameters:
    className - the fully qualified name of the desired class.
    Returns:
    the Class object for the class with the specified name.
    Throws:
    LinkageError - if the linkage fails
    ExceptionInInitializerError - if the initialization provoked by this method fails
    ClassNotFoundException - if the class cannot be located 
注意黑色加粗的部分,这里的className要使用类的路径,如上述的 java.lang.Thread,即包名.类名

修改后的XMLUtil.java文件,

try{
		...
		Class c = Class.forName("com.log."+cName);	//使用类的全路径		
		Object obj = c.newInstance();		
		return obj;
	}catch(
Exception e)
{
	e.printStackTrace();
	return null;
}


问题解决,运行结果如下:


通过解决这个问题,得出以下结论:要仔细阅读API文档提供的帮助和样例微笑

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值