用Java创建对象的所有不同方式有哪些?


178

前几天与同事进行了交谈。

使用构造函数是显而易见的,但是还有其他方法吗?


2
如有疑问,请查看语言规范。12.5新类的创建实例java.sun.com/docs/books/jls/third_edition/html/... 15.9类实例创建表达式java.sun.com/docs/books/jls/third_edition/html/...
互联网朋友

9
只有3种:普通c-tor(新关键字),clone()和Unsafe.allocateInstance(Class)。其余的称为其中之一。反射被编译为c-tor调用,反序列化为Unsafe.allocateInstance(Class)。您可以创建自己的API,最终将调用其中一个。
bestsss 2011年

2
@ bestsss- Unsafe是Java特定于实现的详细信息,在规范中未提及。这是完全有可能建立一个不使用编译反射到代码,使用兼容的Java实现newcloneUnsafe.allocateInstance
templatetypedef

2
您可以检查链接, codesandlogics.com / 2017/01 / ways
Pragya

Answers:


288

有四种在Java中创建对象的方法:

。使用new关键字
这是在Java中创建对象的最常见方法。几乎99%的对象都是通过这种方式创建的。

 MyObject object = new MyObject();

。使用Class.forName()
如果我们知道类的名称,并且它具有公共的默认构造函数,则可以通过这种方式创建一个对象。

MyObject object = (MyObject) Class.forName("subin.rnd.MyObject").newInstance();

Ç。使用clone()
clone()可用于创建现有对象的副本。

MyObject anotherObject = new MyObject();
MyObject object = (MyObject) anotherObject.clone();

d。使用object deserialization
对象反序列化只是从序列化的形式创建对象。

ObjectInputStream inStream = new ObjectInputStream(anInputStream );
MyObject object = (MyObject) inStream.readObject();

您可以从这里阅读它们。


10
所以实际上只有两种方式:调用构造函数(使用new,clone()或反射)和不调用构造函数的反序列化。
AlexR 2011年

13
@AlexR:Object.clone()也不会调用构造函数。
axtavt 2011年

1
既然这似乎是最重要的答案,您是否可以将数组的创建作为子案例添加到A和B?(有关详细信息,请参见我的答案)。
圣保罗Ebermann

反序列化确实会调用构造函数,只是不是最派生的类型。
Tom Hawtin-大头钉

2
您还应该提到Constructor归纳类Class.newInstance
templatetypedef

68

有多种方法:

  • 通过Class.newInstance
  • 通过Constructor.newInstance
  • 通过反序列化(使用最派生的不可序列化基类的no-args构造函数)。
  • 通过Object.clone不调用构造函数)。
  • 通过JNI(应该调用构造函数)。
  • 通过任何其他new为您调用的方法。
  • 我猜您可以将类加载描述为创建新对象(例如interned String)。
  • 一个文字数组,作为声明中初始化的一部分(没有数组的构造函数)。
  • “ varargs”(...)方法调用中的数组(没有数组的构造函数)。
  • 非编译时间常数字符串连接(在典型的实现上,至少会产生四个对象)。
  • 导致运行时创建和引发异常。例如throw null;"".toCharArray()[0]
  • 哦,当然还有原语的装箱(除非缓存)。
  • JDK8应该具有lambda(本质上是简洁的匿名内部类),它们被隐式转换为对象。
  • 为了完整性(和PaŭloEbermann),new关键字也有一些语法。

6
您应该添加的“正常方式”,太:-)
圣保罗Ebermann

@PaŭloEbermann太老了,太酷了。(我认为这个问题的意思是“使用构造函数(尽管上面的大多数(但不是全部)都使用了/ a构造函数)。)
Tom Hawtin-公告

实际上,只有3种真正的方法可以做到,为此我添加了评论
bestsss 2011-02-25

3
您错过了一个:java.misc.Unsafe.allocateInstance()。尽管由于多种原因,这很讨厌。实际上,反序列化不使用no-args构造函数。它在引擎盖下使用allocateInstance或等效的黑魔法。
Stephen C

最佳答案,但JNI AllocObject().不会调用构造函数。
罗恩侯爵

25

在Java语言中,创建对象的唯一方法是通过显式或隐式调用其构造函数。使用反射会导致对构造函数方法的调用,反序列化会使用反射来调用构造函数,工厂方法将对构造函数的调用包装为抽象实际的构造,而克隆同样是包装的构造函数调用。


1
不正确 反序列化不会显式或隐式调用类的构造函数。
罗恩侯爵

2
我不应该写“其构造函数”和“构造函数”,而应该写“构造函数”和“构造函数”。在反序列化的情况下,始终会调用第一个适用的no-arg构造函数。
困惑

1
默认的克隆实现不调用任何构造函数。
Didier L

如果这是我的克隆方法实现“ return super.clone();”。然后它将不调用构造函数。
MATEEN

13

是的,您可以使用反射创建对象。例如,String.class.newInstance()将为您提供一个新的空String对象。


1
如果我使用它,它要求我将其包含在try / catch块中。
GuruKulki 2010年

2
是的,在很多情况下都可以引发异常。有关可能出现问题的示例,请参见JavaDoc for newInstance()。
ThomasLötzer2010年

11

用Java创建对象的方法有五种,

1.使用new关键字 →构造函数被调用

Employee emp1 = new Employee();

2.使用newInstance()Class →构造函数的方法调用

Employee emp2 = (Employee) Class.forName("org.programming.mitra.exercises.Employee")
                                .newInstance();

也可以写成

Employee emp2 = Employee.class.newInstance();

3.使用newInstance()Constructor →构造函数的方法被调用

Constructor<Employee> constructor = Employee.class.getConstructor();
Employee emp3 = constructor.newInstance();

4.使用clone()方法 →不调用构造函数

Employee emp4 = (Employee) emp3.clone();

5.使用反序列化 →无需构造函数调用

ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
Employee emp5 = (Employee) in.readObject();

前三个方法new关键字都newInstance()包含构造函数调用,而后两个克隆和反序列化方法在不调用构造函数的情况下创建对象。

所有上述方法都具有与之关联的不同字节码。有关示例和更多详细说明(例如,所有这些方法的字节码转换),请参见使用示例的Java中创建对象的不同方法。

但是,可以说创建数组或字符串对象也是创建对象的一种方式,但是这些事情仅特定于某些类并且由JVM直接处理,而我们可以使用这5种方式创建任何类的对象。


请披露任何隶属关系,请勿将网站用作通过发布来推广您的网站的方式。请参阅我如何写一个好的答案?
伊薇特



6

如果您是java的新手,应该注意到这一点,每个对象都继承自Object

受保护的本机对象clone()抛出CloneNotSupportedException;


@stacker:您能否解释一下这与创建一个新对象有什么关系?谢谢。
ryanprayogo,2010年

4
@ryanprayogo clone()将返回一个新对象(即使该对象是调用clone()的对象的克隆),并且实际上是创建新对象而不调用构造函数的唯一方法。
ThomasLötzer,2010年

6

另外,您可以数据反序列化为对象。这不会通过类Constructor进行!


更新:感谢汤姆在您的评论中指出!迈克尔也做了实验。

它经过最派生的不可序列化超类的构造函数。
当该类没有no-args构造函数时,反序列化时将引发InvalidClassException。

有关所有情况的完整处理,请参见Tom的答案;-)
还有其他方法可以在不使用Java中使用“ new”关键字的情况下创建对象


1
它确实通过了一个构造函数(最派生的不可序列化超类的no-arg构造函数)。
Tom Hawtin-大头钉

1
@汤姆哇-我不知道,尝试了一下。显然,当最派生的不可序列化超类没有 no-args构造函数时,它将导致InvalidClassException 被序列化到流中并在反序列化时抛出!-多么奇怪?
Michael Borgwardt

6

有一种类型的对象,不能通过普通的实例创建机制(调用构造函数)来构造:Arrays。创建数组

 A[] array = new A[len];

要么

 A[] array = new A[] { value0, value1, value2 };

正如肖恩(Sean)在评论中所说,这在语法上类似于构造函数调用,并且在内部它不多于分配和零初始化(或在第二种情况下使用显式内容初始化)内存块,并带有一些标头来指示内存块。类型和长度。

将参数传递给varargs方法时,也会隐式创建(并填充)数组。

第四种方式是

 A[] array = (A[]) Array.newInstance(A.class, len);

当然,克隆和反序列化在这里也适用。

标准API中有许多创建数组的方法,但实际上它们都使用一种(或多种)这些方法。


当然,您不能定义Array构造函数,但除此之外,该机制是相同的new关键字。Array.newInstance是这里唯一的新机制
肖恩·帕特里克·弗洛伊德

@Sean:是相同的关键字,但是我敢说这是一个完全不同的内部机制。
圣保罗Ebermann

当然是这样。但是另一方面,数组创建的不同版本在内部几乎相同。刚意识到您的答案是从2011年开始。对不起,您很老旧:-)
肖恩·帕特里克·弗洛伊德

@Sean:没问题,我在这个时候做了一些语法修复。
圣保罗Ebermann

干得好,这里没有人讨论数组!
MATEEN

5

如果我们详尽无遗的其他方式。

  • 在Oracle JVM上是Unsafe.allocateInstance(),它创建实例而不调用构造函数。
  • 使用字节码操作,你可以添加代码anewarraymultianewarraynewarraynew。可以使用ASM或BCEL之类的库来添加这些文件。Oracle Java附带了一个bcel版本。同样,这不会调用构造函数,但是您可以将构造函数作为单独的调用来调用。


4

反思也将为您完成这项工作。

SomeClass anObj = SomeClass.class.newInstance();

是创建类的新实例的另一种方法。在这种情况下,您还需要处理可能引发的异常。


4
  • 使用new运算符(从而调用构造函数)
  • 使用反射clazz.newInstance()(再次调用构造函数)。或clazz.getConstructor(..).newInstance(..)(再次使用构造函数,但您可以选择其中一个)

通过调用对象类的构造函数来概括答案(一种主要方法)。

更新:另一个答案列出了不使用构造函数的两种方式-反序列化和克隆。


4

用Java创建对象的方法有五种:

1.使用`new`关键字:

这是用Java创建对象的最常用方法。几乎99%的对象都是通过这种方式创建的。

MyObject object = new MyObject();//normal way

2.使用工厂方法:

ClassName ObgRef=ClassName.FactoryMethod();

例:

RunTime rt=Runtime.getRunTime();//Static Factory Method

3.通过使用克隆概念:

通过使用clone()clone()可用于创建现有对象的副本。

MyObjectName anotherObject = new MyObjectName();
MyObjectName object = anotherObjectName.clone();//cloning Object

4.使用`Class.forName()`:

如果我们知道该类的名称,并且它具有公共的默认构造函数,则可以通过这种方式创建一个对象。

MyObjectName object = (MyObjectNmae) Class.forName("PackageName.ClassName").newInstance();

例:

String st=(String)Class.forName("java.lang.String").newInstance();

5.使用对象反序列化:

对象反序列化只不过是从其序列化形式创建对象。

ObjectInputStreamName inStream = new ObjectInputStreamName(anInputStream );
MyObjectName object = (MyObjectNmae) inStream.readObject();

(4)仅Class.forName()在您还没有课程的情况下才需要,而在其他所有情况下都需要。它还不需要no-args构造函数:如果您知道正确的参数,则可以使用多种方法来调用任何公共构造函数。而且您至少还保留了另外两种方法。
罗恩侯爵

2
(2)工厂方法只是获取对象的一种模式。但是在内部,它使用“ new”关键字创建对象。
Karthik Bose

男人为什么这么多人说工厂方法创建对象,你们从哪里学来的?
MATEEN

3

您还可以克隆现有对象(如果它实现Cloneable)。

Foo fooClone = fooOriginal.clone (); 

2

方法一

使用新关键字。这是在Java中创建对象的最常见方法。几乎99%的对象都是通过这种方式创建的。

Employee object = new Employee();

方法二

使用Class.forName()。Class.forName()为您提供类对象,这对于反射很有用。该对象具有的方法由Java定义,而不是由编写类的程序员定义。每个班级都是相同的。调用newInstance()会为您提供该类的实例(即,调用Class.forName(“ ExampleClass”)。newInstance()等效于调用new ExampleClass()),您可以在其上调用该类定义的方法,访问可见字段等。

Employee object2 = (Employee) Class.forName(NewEmployee).newInstance();

Class.forName()将始终使用调用者的ClassLoader,而ClassLoader.loadClass()可以指定其他ClassLoader。我相信Class.forName也会初始化加载的类,而ClassLoader.loadClass()方法不会立即执行此操作(直到第一次使用它才会初始化)。

另一个必须阅读:

Java:示例简单Java枚举示例的线程状态介绍

方法3

使用clone()。clone()可用于创建现有对象的副本。

Employee secondObject = new Employee();
Employee object3 = (Employee) secondObject.clone();

方法4

使用newInstance()方法

Object object4 = Employee.class.getClassLoader().loadClass(NewEmployee).newInstance();

方法5

使用对象反序列化。对象反序列化只不过是从序列化形式创建对象。

// Create Object5
// create a new file with an ObjectOutputStream
FileOutputStream out = new FileOutputStream("");
ObjectOutputStream oout = new ObjectOutputStream(out);

// write something in the file
oout.writeObject(object3);
oout.flush();

// create an ObjectInputStream for the file we created before
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("crunchify.txt"));
Employee object5 = (Employee) ois.readObject();

不要对非代码文本使用代码格式。有比这些更多的方法。阅读其他答案。“几乎99%”只是猜测。
罗恩侯爵

嗨,EJP,很抱歉对此错误...我说过,这是一种创建对象的方法,但并不是说正确的方法。它只是一种模型。.很抱歉,我是新手,也是stackoverflow的
新手

0

从API用户的角度来看,构造函数的另一种替代方法是静态工厂方法(如BigInteger.valueOf()),尽管对于API作者(技术上来说是“真实的”),仍然使用构造函数来创建对象。


-1

完全取决于您通过create的含义,但其他一些含义是:

  • 克隆方法
  • 反序列化
  • 反射(Class.newInstance())
  • 反射(构造对象)

2
3和4是同一机制的不同别名
Sean Patrick Floyd

-2

还有ClassLoader.loadClass(s​​tring),但这并不经常使用。

如果您想成为一名全面的律师,由于数组的.length属性,数组在技术上是对象。因此初始化数组会创建一个对象。


1
loadClass(String name)返回生成的Class对象,该对象是yes,但不是该类的对象。如果给出示例,那么我们可以在整个Java库中找到许多这样的示例,但是这些示例是特定于类的。检查youtu.be/gGGCmrD6Qpw
nanosoft

-3

我们可以通过5种方式创建对象:

  1. 由新运营商
  2. 通过反射(例如,Class.forName()之后是Class.newInstance())
  3. 通过工厂方法
  4. 通过克隆
  5. 通过反射api

3
Class.forName()加载类而不是创建对象。
罗恩侯爵

反射?当然,您的意思是反思。
Stephen C

您将如何从工厂方法创建对象,内部实现可能会再次使用新的关键字对吗?为什么你有两次反思?它会更有意义,如果你确实给一些exampls
MATEEN

-5

我们也可以这样创建对象:

String s ="Hello";

没有人讨论。


这是创建原始数据类型的方式,这只是Java在后台提供的一种灵活性,它不使用“ new”关键字。这与new关键字相同。
Madusudanan 2014年

印第安纳州Madhusudan,在新操作符的帮助下,对象应始终存储在堆中,在这种情况下,“ Hello”是应存储在字符串池中的对象。而且String是一个类,而不是原始数据类型。
Deepak Sharma 2014年

这不会创建对象。它为现有对象分配引用。该对象已经由编译器和类加载器创建。
罗恩侯爵
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.