黑马程序员技术交流社区

标题: newInstance()方法和new关键字的区别 [打印本页]

作者: 张小明    时间: 2011-7-28 00:45
标题: newInstance()方法和new关键字的区别
学过张老师讲的反射,知道Class类有个newInstance() 方法,创建此 Class 对象所表示的类的一个新实例,下面就是我的编程实例:
  1. import java.lang.reflect.*;
  2. public class test {
  3.    
  4.     public static void main(String[] args)throws Exception {
  5.       Class c = Class.forName("person");
  6.       person p1 = (person) c.newInstance();
  7.       person p2 = new person();
  8.       p1.smile();
  9.       p2.laugh();
  10.     }
  11. }

  12.                 class person {
  13.                         private String name;
  14.                         private int age;
  15.                         public person(){}
  16.                         public person(String name, int age) {
  17.                                 this.name = name;
  18.                                 this.age = age;
  19.                         }
  20.                         public void smile(){
  21.                                 System.out.println("hehe");
  22.                                 }
  23.                         public void laugh(){
  24.                         System.out.println("heihei");
  25.                         }
  26.                         public String toString(){
  27.                                 return name+":"+age;
  28.                                 }
  29. }
复制代码
这样看起来它和用new关键字使用起来一样,实际上在初始化一个类生成实例时,newInstance()方法和new关键字有什么区别吗?
作者: 詹季春    时间: 2011-7-28 00:57
在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法一个是关键字外,
最主要的区别是创建对象的方式不同。newInstance()使用类加载机制,new是创建一个新类。

从JVM角度看,使用new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证这个类已加载且类已经连接了。[code=java]String className="test";
Class c=Class.forName(className);
factory=(ExampleInterface)c.newInstance();[/code]最后
newInstance():若类型。低效率,只能调用无参构造方法,适用于工厂模式;
new:强类型。相对高效。能调用任何public构造方法。


可以参考这个帖子http://bbs.itheima.com/thread-532-1-1.html
[ 本帖最后由 詹季春 于 2011-07-28  00:59 编辑 ]
作者: 匿名    时间: 2011-7-28 01:11
在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法一个是关键字外,最主要的区别是创建对象的

方式不同。newInstance()使用类加载机制,new是创建一个新类。


从JVM角度看,使用new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证这个类已加载

且类已经连接了。

String className="test"; Class c=Class.forName(className); factory=(ExampleInterface)c.newInstance();?

newInstance():若类型。低效率,只能调用无参构造方法,适用于工厂模式;

new:强类型。相对高效。能调用任何public构造方法。
作者: 匿名    时间: 2011-7-28 11:34
标题: 提问也得有智慧
楼主的问题在百度和谷歌有很多答案的了,为何不自己先解答呢?如果你寻找过答案了,就不会问如此问题了。
网络有很多的参考答案的,如[url]http://www.diybl.com/course/3_program/java/javajs/20090215/155546.html[/url]。

当遇到问题时,我们该怎么做?
第1步、自己思考,并动手编写一些例子来印证自己所思;
第2步、查API文档,看看这问题中元素在API中是如何定义的。就像楼主所提的问题涉及的Class、newInstance() 方法,API中是这样描述的:“
public final class Class<T>extends Objectimplements Serializable, GenericDeclaration, Type, AnnotatedElementClass 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

以下示例使用 Class 对象来显示对象的类名:[code] void printClassName(Object obj) {
         System.out.println("The class of " + obj +
                            " is " + obj.getClass().getName());
     }[/code]”
Class类的newInstance()方法的描述:
public T newInstance()
              throws InstantiationException,
                     IllegalAccessException创建此 Class 对象所表示的类的一个新实例。如同用一个带有一个空参数列表的 new 表达式实例化该类。如果该类尚未初始化,则初始化这个类。
注意,此方法传播 null 构造方法所抛出的任何异常,包括已检查的异常。使用此方法可以有效地绕过编译时的异常检查,而在其他情况下编译器都会执行该检查。 Constructor.newInstance 方法将该构造方法所抛出的任何异常包装在一个(已检查的)InvocationTargetException 中,从而避免了这一问题。


返回:
此对象所表示的类的一个新分配的实例。
抛出:
IllegalAccessException - 如果该类或其 null 构造方法是不可访问的。
InstantiationException - 如果此 Class 表示一个抽象类、接口、数组类、基本类型或 void; 或者该类没有 null 构造方法; 或者由于其他某种原因导致实例化失败。
ExceptionInInitializerError - 如果该方法引发的初始化失败。
SecurityException - 如果存在安全管理器 s,并满足下列任一条件:
调用 s.checkMemberAccess(this, Member.PUBLIC) 拒绝创建该类的新实例
调用者的类加载器不同于也不是当前类的类加载器的一个祖先,并且对 s.checkPackageAccess() 的调用拒绝访问该类的包 。

第3步、通过第2步了解基本知识后,如果还有疑问,比如说new关键词的作用,因在API文档中无此详细描述,那百度和谷歌这2大帮手就得派上场了。
在百度的搜索栏里面输入“java new关键词”,结果出来后,你会惊喜的发现,就在第一搜索结果里有你现在比较需要的东西,或者说很感兴趣的知识——
“Java中new关键字与类加载器原理_百度文库”
[quote]
"new"可以说是Java开发者最常用的关键字,我们使用new创建对象,使用new并通过类加载器来实例化任何我们需要的东西,但你是否深入了解过new在编译的瞬间都做了什么?

  在Java中使用new关键字创建对象变得很容易了,事实上,对这些事情你是不需要考虑的。需要访问一个文件吗?只需要创建一个新的File实例:new File(“build.properties”),对于大多数Java开发人员而言,这就是他们需要知道的一切,是不是很简单呢?!但当你使用了多个类加载器时,问题就不一样了。

  这是我一年多来的第一反应,我就是不想知道这些东西,但奇怪的是,类加载器其实非常简单,大多数Java开发人员都知道编译时通过Java文件生成.class类文件,然后由Java虚拟机(JVM)载入这些编译后的类,这就是类加载器最基本的功能,但是和线程一样,问题不是理解他们做什么,而是让它们一起工作。

  你听到过多少次“这是类加载器的问题”?我承认我听到过很多次,我自己也说过很多次。只要你的应用程序中不止一个类加载器,你不得不担心哪些类可以相互看到对方,这很容易成为一场噩梦。有关类加载器行为我将另外用一篇文章来说,现在我们还是回到new关键字吧。

  当你创建一个新对象时,JVM首先加载类,当你使用new时这是透明的,问题是使用什么类加载器?以及为什么要使用它?

  设想一个Grails情景,我们有一套基于Gant的构建系统,载入构建脚本并执行它们,我们以实例化一个Jetty服务器并启动它作为示例,对象的创建顺序是这样的:
[img]http://images.51cto.com/files/uploadimg/20100308/084954658.jpg[/img]
对象的创建顺序

  事实上,上图展示的仅仅是一个简化的真实情况。

  前3个类都在我们将要调用的生成类加载器的类路径下,因此是一次性将生成时需要用到的所有类全部加载了,但Jetty的Server类怎么加载呢?最重要的是要知道Server类必须要通过加载Grails Web应用程序相同的类加载器加载,虽然你可以将你自己的类加载器嵌入到服务器中,如果与加载Server的不一样,将会出现可怕的类加载器问题。

  考虑到这一点,让我们看看如果RunApp脚本使用new创建服务器实例会发生什么:


  def server = new org.mortbay.jetty.Server() ... server.start()

  现在你应该问你自己“加载Server类该使用什么类加载器?”,这是一个关键问题,因为它决定了使用什么类加载器加载整个Web应用程序,也就决定了应用程序的运行时应该使用和依赖的类路径,在这种情况下,无论使用什么类加载器加载RunApp脚本,new操作符都会有效地授权给


  this.getClass().getClassLoader()
  我们的例子是什么意思呢?它意味着生成类加载器被用于加载Server类,因此也必须用于加载Web应用程序类,换句话说,所有应用程序的运行时依赖必须包括在生成类加载器中。你可能会问,这样会不会有问题?回答是有一个潜在的问题和一个实际的问题。

  潜在的问题是类冲突,如果Web应用程序依赖一个已经存在于生成系统中不同版本的库会怎么样?如果所有Apache XML API库都在类路径下,这是一个特殊的问题,绝对会导致大破坏。

  实际的问题是在类路径中JAR文件越多,JVM寻找类的时间就越长,这意味着启动时间就越长,这也是OSGi设计要解决的问题之一,为什么要将JAR放在生成类路径下呢?生成时本身是不需要它们的。

  解决办法是确定类加载器的边界,使用映射实例化你的对象:


def runtimeClassLoader = new URLClassLoader(...)   
def server = runtimeClassLoader.loadClass("org.mortbay.jetty.Server").newInstance()   
...   
server.start() 
  在Groovy中这是很容易办到的,因为start()方法是在运行时评估的,但Java需要知道编译时类型,你不能这样做:


ClassLoader runtimeClassLoader = new URLClassLoader(...)   
Server server = (Server)    
runtimeClassLoader.loadClass("org.mortbay.jetty.Server").newInstance()   
...   
server.start() 
  因为在第2行你会得到一个ClassCastException,服务器的声明性类型是由this.getClass().getClassLoader()加载的,但new实例是由另一个不同的类加载器加载的,不同的类加载器意味着不同的类,因此你必须使用映射调用方法和访问你需要的字段。

  正如你所看到的,正常情况下,你不要考虑new操作符,但只要你要处理多个类加载器,你必须要仔细了解它们的行为,关键是制定出合适的类加载器边界,然后使用映射来加载和实例化在那些边界上的类。听起来好像是些不必要的额外工作,但在应用程序/框架的可靠性方面可以得到真正的改善。
[/quote]

第4步、如果在经过上面的探索答案的过程后,依旧没有找到自己满意的答案,那就来一个更直接的搜索呗。
在百度中搜索“newInstance()方法和new关键字的区别”,你发现有很多相同或者相似的主题,进入后,有不少参考答案。
第5步、如果,我说如果,在百度和谷歌经过千方百计的搜索仍然无果后,你就来黑马发帖提问吧。
所谓人多力量大,大家全部去思考,去查找答案,或百度或谷歌或api或仅靠自己研究,反正,最后你会得到一个结果的。
PS:直接由别人给出的结果或者说答案是罪不值钱的,因为那东西网络漫天飞,只是别人的理论;而经过自己思考、实践与探索出来的答案才是属于自己的,探索答案的过程是一笔宝贵的经验,它是别人给不了你的!所以遇到问题时不能轻易的在论坛发问,要先进过自己的思考和探答案,最后的选择才是向别人发问!就算发问,问的问题也不能在百度或者谷歌上随便一搜就有答案的问题,因为这是不尊重回答者的!虽然回答你的问题可以拿取积分,但也有人对此不屑一顾的。在你深究这个问题之后提出的新问题,就不是很笼统和表面的了。所以提问也得有智慧,就如文章How To Ask Questions The Smart Way作者所说的[url]http://www.linuxforum.net/doc/smartq-grand.html[/url]。
作者: 匿名    时间: 2011-7-29 14:25
标题: 回复 覃俊瑞
你好,覃俊瑞,你提到的解决问题的方法很好,是我们解决问题的正常途径。
   但是,在论坛里面讨论我们可以懂得更多些,知识只有不停的交流,不停的验证,才会得到巩固或者更新。所以,在论坛里面多问问我觉得也挺好的,只要问题不那么太easy,能让大家有发挥的,也是好的嘛。
   楼主的问题詹季春和陈艳艳都已经回答了,而且很简明,很精准,本人表示受教,顶一个
作者: 匿名    时间: 2011-7-29 14:46
new 一个关键字,用于来创建实例对象用的,newInstance()方法用于动态的类加载机制,通过其方法可以动态的创建对象,反射中的一个重要方法
作者: 匿名    时间: 2011-7-29 15:07
顶楼上所有的人




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2