,嗯?经常在后台收到这样一类私信,大致描述如下:看来非常有必要用一篇文章来梳理总结一下“程序员找合伙人”这个话题。择日不如撞日,今天就把这件事安排一下吧。可以说是有很多种方法!new使用关键字new创建一个对象,这几乎是写代码时最常用的操作之一,例如:Sheepsheep1=newSheep();绵羊sheep2=newSheep("codesheep",18,65.0f);通过new方法,我们可以调用类的无参或有参构造函数来实例化一个对象。表面上看,简单的new对象你就拥有了,但是如果你面试的时候只回答这个级别,大概率你会扑街,因为比这更重要的是new对象的原理和过程,因为JVM是牵头媒人,默默的帮我们在幕后做了很多工作。说到new一个对象的具体过程,一张图大概可以这样描述:首先,当我们new一个对象的时候,比如Sheepsheep=newSheep(),JVM会先回过头来检查这个symbol是什么Sheep的引用表示该类是否已经加载,如果没有,则执行对应类的加载过程;声明类型引用很简单,例如Sheepsheep=newSheep()会声明一个Sheep类型的引用sheep;第一步类加载完成后,实际上已经确定了对象所需的内存大小,然后JVM会在堆上为对象分配内存;所谓属性“0”值初始化很好理解,就是给实例化对象的每个属性赋默认值,初始化“0”值,比如int的初始值为0,以及对象的初始值为空;然后JVM会设置对象头,主要包括对象的运行时数据(如Hashcode、Generationalage、锁状态标志、锁指针、偏向线程ID、偏向时间戳等)和类型指针(JVM使用这个类型指针来确定对象是哪个类实例);属性的显示初始化也很好理解,比如定义一个类,手动给某个属性字段赋值,比如:privateStringname="codesheep";此时初始化它;最后调用类的构造函数来执行构造函数中描述的初始化。应该说经过这一系列的步骤,一个新的可用对象就诞生了。反射一个对象学过Java反射机制的人都知道,只要能拿到类的Class对象,就可以通过强大的反射机制创建实例对象。获取Class对象的方式有3种:类名。类对象名称。getClass()类。forName(fullyqualifiedclassname)在你有了Class对象之后,你可以调用它的newInstance()方法来创建一个对象,就像这样:Sheepsheep3=(Sheep)Class.forName("cn.codesheep.article.obj.Sheep").newInstance();绵羊sheep4=Sheep.class.newInstance();当然,这种方法的局限性是有目共睹的,因为创建的对象使用类的无参数构造函数。所以比这更进一步的方法是通过java.lang.relect.Constructor类的newInstance()方法来创建对象,因为它可以显式指定一个构造函数来创建对象。比如我们得到类的Class对象后,可以通过getDeclaredConstructors()函数得到该类所有构造函数的列表,这样我们就可以调用对应的构造函数来创建对象,像这样:Constructor>[]constructors=Sheep.class.getDeclaredConstructors();绵羊sheep5=(Sheep)constructors[0].newInstance();sheepsheep6=(Sheep)constructors[1].newInstance("codesheep",18,65.1f);另外,如果我们想显式获取某个类的某个构造函数,也可以直接在中指定构造函数的参数类型getDeclaredConstructors()函数用于精确控制,像这样:Constructorconstructor=Sheep.class.getDeclaredConstructor(String.class,Integer.class,Float.class);绵羊sheep7=(Sheep)constructor.newInstance("codesheep",18,65.2f);克隆一个对象对象克隆基本上是我们每天写代码时的硬性要求,基于一个对象克隆另一个对象,这也是写Java代码时很常见的操作。关于对象复制的知识点,之前写过,详细整理了一篇文章:《一个工作三年的同事,居然还搞不清深拷贝、浅拷贝...》,整理了对象赋值、复制、深复制、浅复制等一系列知识点详细,本文不再赘述。对象反序列化对象“序列化与反序列化”的知识点很重要也很有用,但是听很多朋友说对于初学者来说有点迷惑。当我们进行序列化和反序列化操作时,也会在后台创建对象。关于“序列化与反序列化”知识点的详细理解+梳理,我写过,链接在这里:序列化/反序列化,忍你好久了,甘!.Unsafe黑魔法Unsafe类这个名字有点让人迷惑。确实,在我们平时的业务代码中,似乎与它的接触并不多。我们都知道,在写Java代码的时候,我们很少去操作一些底层的资源,比如内存。位于sun.misc.Unsafe包路径下的Unsafe类提供了直接访问系统资源的途径和方法,可以进行一些低级操作。比如借助Unsafe,我们可以分配内存,创建对象,释放内存,定位对象某个字段的内存位置,甚至修改等等,可见这个东西的破坏力是非常大的时候误用,所以一般都是在控制下使用。在业务代码中很少见到,但是在JDK内部的一些包如io、nio、juc中还是有很多关于它的代码。Unsafe类中有一个allocateInstance()方法,通过它可以创建一个对象。为此,我们只需要获取一个Unsafe类的实例对象,自然而然地调用allocateInstance()来创建该对象即可。那么如何才能得到Unsafe类的实例对象呢?快速看一下Unsafe类的源码,我们会发现它是一个单例类,其构造方法是private的,直接构造是不现实的:publicfinalclassUnsafe{privatestaticfinalUnsafetheUnsafe;//...omitted...privatestaticnativevoidregisterNatives();privateUnsafe(){}@CallerSensitivepublicstaticUnsafegetUnsafe(){Classvar0=Reflection.getCallerClass();如果(!VM.isSystemDomainLoader(var0.getClassLoader())){thrownewSecurityException("Unsafe");}else{返回不安全;}}//...省略...}并且获取单例对象的入口函数getUnsafe()也被特别标示,表示只能从bootstrap可以加载的类中调用该方法,表示这个方法也被JVM内部使用。如果直接使用外部代码,会报类似这样的异常:Exceptioninthread"main"java.lang.SecurityException:Unsafe走投无路,只能重拾强大的反射机制,创建Unsafe类的实例:Fieldf=Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);Unsafeunsafe=(Unsafe)f.get(null);然后我们就可以愉快的使用它来创建对象了:Sheepsheep8=(Sheep)unsafe.allocateInstance(Sheep.class);当然,对象的隐式创建场景是除了上面提到的显式对象创建场景之外的此外,还有一些隐含的场景我们不手动创建对象。这里有几个常见的例子。我们都知道,JVM虚拟机在加载一个类的时候,也会创建一个该类对应的Class实例。Object,很明显这个过程是JVM在背后偷偷干的。字符串隐式对象创建是典型的。例如,当定义一个String类型的字面量变量时,可能会导致创建一个新的String对象,像这样:Stringname="codesheep";同样常见的比如StringConnectors的+号也会隐式导致创建新的String对象等:Stringstr=str1+str2;自动装箱机制的例子有很多,比如当执行类似下面的代码时:IntegercodeSheepAge=18;它触发的自动加载box机制导致在幕后隐式创建一个包装器类型的新对象。函数变量参数,比如下面,当我们使用变量参数语法int...nums来描述函数的输入参数时:publicdoubleavg(int...nums){doublesum=0;intlength=nums.length;for(inti=0;i
