您的位置:首页 > 其它

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

2017-10-09 20:13 387 查看
在实验课上用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 named
java.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文档提供的帮助和样例

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐