嗯?

在后台经常会收到这样一类私信,大致是这样描述的:



看来关于「程序员找对象」这个话题,非常有必要用一篇文章来专门梳理和归纳一下了。


择日不如撞日,今天就把这件事情给安排了吧。

可以说,方法多得很!


new一个对象

用关键字new进行对象的创建,几乎是写代码时最常用的操作之一了,比如:

Sheep sheep1 = new Sheep();Sheep sheep2 = new Sheep( "codesheep", 18, 65.0f );

通过new的方式,我们可以调用类的无参或者有参构造方法来实例化出一个对象。

表面上看,简简单单new一下对象就有了,但面试时如果仅仅答到这一层,大概率会扑街,因为比这个更重要的是new对象时的原理和流程,因为JVM这个牵线红娘在背后默默地帮我们做了很多工作。

说到new一个对象的具体流程,用一张图可大致描述成如下所示:


  1. 首先,当我们new一个对象时,比如Sheep sheep = new Sheep()JVM首先就回去检查Sheep这个符号引用所代表的类是否已经被加载过,如果没有就要执行对应类的加载过程;
  2. 声明类型引用很简单,比如Sheep sheep = new Sheep()就会声明一个Sheep类型的引用sheep
  3. 第一步类加载完成以后,对象所需的内存大小其实就已经确定下来了,接下来JVM就会在堆上为对象分配内存;
  4. 所谓的属性“0”值初始化非常好理解,即为实例化对象的各个属性赋上默认初始化“0”值,比如int的初始化0值就是0,而一个对象的初始化0值就是null;
  5. 接下来JVM会进行对象头的设置,这里面就主要包括对象的运行时数据(比如Hash码、分代年龄、锁状态标志、锁指针、偏向线程ID、偏向时间戳等)以及类型指针(JVM通过该类型指针来确定该对象是哪个类的实例);
  6. 属性的显示初始化也好理解,比如定义一个类的时候,针对某个属性字段手动的赋值,如:private String name = "codesheep"; 就在这时候给初始化上;
  7. 最后是调用类的构造方法来进行进行构造方法内描述的初始化动作。

应该说,经过了这一系列步骤,一个新的可用对象方才得以诞生。


反射出一个对象

学过Java反射机制的都知道,只要能拿到类的Class对象,就可以通过强大的反射机制来创造出实例对象了。

拿到Class对象有三种方式:

  • 类名.class
  • 对象名.getClass()
  • Class.forName(全限定类名)

有了Class对象之后,接下来就可以调用其newInstance()方法来创建一个对象,就像这样:

Sheep sheep3 = (Sheep) Class.forName( "cn.codesheep.article.obj.Sheep" ).newInstance();Sheep sheep4 = Sheep.class.newInstance();

当然,这种方式的局限性也有目共睹,因为使用的是类的无参构造方法来创建的对象。

所以比这个更进一步的方式是通过java.lang.relect.Constructor这个类的newInstance()方法来创建对象,因为它可以明确指定某个构造器来创建对象。

比如,在我们拿到了类的Class对象后,就可以通过getDeclaredConstructors()函数来获取到类的所有构造函数列表,这样我们就可以调用对应的构造函数来创建对象了,就像这样:

Constructor<?>[] constructors = Sheep.class.getDeclaredConstructors();Sheep sheep5 = (Sheep) constructors[0].newInstance(); Sheep sheep6 = (Sheep) constructors[1].newInstance( "codesheep", 18, 65.1f );

而且,如果我们想明确获取类的某个构造函数,也可以在getDeclaredConstructors()函数里直接指定构造函数传参类型来精确控制,就像这样:

Constructor constructor = Sheep.class.getDeclaredConstructor( String.class, Integer.class, Float.class );Sheep sheep7 = (Sheep) constructor.newInstance( "codesheep", 18, 65.2f );

克隆出一个对象

对象克隆在我们日常写代码的时候基本上是刚性需求,基于一个对象克隆出另一个对象,这也是写Java代码时十分常见的操作。

关于对象拷贝这一知识点,之前我已经写过了,详细梳理过一篇:《一个工作三年的同事,居然还搞不清深拷贝、浅拷贝...》,里面详细梳理了对象赋值、拷贝、深拷贝、浅拷贝等系列知识点,本文便不再赘述了。


反序列化出一个对象

关于对象「序列化和反序列化」这个知识点,重要且有用,但听很多朋友反映初学时有点糊。当我们作序列化和反序列化操作时,背后也会创建对象,关于「序列化和反序列化」这个知识点的详细理解+梳理,之前我也写过了,链接在此:序列化/反序列化,我忍你很久了,淦!。


Unsafe黑魔法

Unsafe类这个名字一听就有点悬了,的确,我们平时的业务代码里接触得好像并不多。

我们都知道写Java代码,很少会去操作位于底层的一些资源,比如内存等这些。而位于sun.misc.Unsafe包路径下的Unsafe类提供了一种直接访问系统资源的途径和方法,可以进行一些底层的操作。比如借助Unsafe我们就可以分配内存、创建对象、释放内存、定位对象某个字段的内存位置甚至并修改它等等。

可见这玩意误用时的破坏力是很大的,所以一般也都是受控使用的。业务代码里很少能看到它的身影,但是JDK内部的一些诸如ioniojuc等包中的代码里还是有不少关于它的身影存在的。

Unsafe类中有一个allocateInstance()方法,通过其就可以创建一个对象。为此我们只需要获取到一个Unsafe类的实例对象,我们自然就可以调用allocateInstance()来创建对象了。

那如何才能获取到一个Unsafe类的实例对象呢?

大致瞅一眼Unsafe类的源码我们就会发现,它是一个单例类,其构造方法是私有的,所以直接构造是不太现实了:

public final class Unsafe {

    private static final Unsafe theUnsafe;

    // ... 省略 ...

    private static native void registerNatives();

    private Unsafe() {    }

    @CallerSensitive    public static Unsafe getUnsafe() {        Class var0 = Reflection.getCallerClass();        if (!VM.isSystemDomainLoader(var0.getClassLoader())) {            throw new SecurityException("Unsafe");        } else {            return theUnsafe;        }    }

    // ... 省略 ...

}

而且获取单例对象的入口函数getUnsafe()上也做了特殊标记,意思是只能从引导加载的类才可以调用该方法,这意味着该方法也是供JVM内部使用的,外部代码直接使用会报类似这样的异常:

Exception in thread "main" java.lang.SecurityException: Unsafe

走投无路,我们只能再次重拾强大的反射机制来创建Unsafe类的实例了:

Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);Unsafe unsafe = (Unsafe) f.get(null);

然后接下来我们就可以愉快地利用它来创建对象了:

Sheep sheep8 = (Sheep) unsafe.allocateInstance( Sheep.class );

对象的隐式创建场景

当然除了上述这几种显式地对象创建场景之外,还有一些我们并没有进行手动对象创建的隐式场景,举几个常见例子。

Class类实例隐式创建

我们都知道JVM虚拟机在加载一个类的时候,也都会创建一个类对应的Class实例对象,很明显这一过程是JVM偷偷地背着我们干的。

字符串隐式对象创建

典型的,比如定义一个String类型的字面变量时,就可能会引起一个新的String对象的创建,就像这样:

String name = "codesheep";

还常见的比如String+号连接符也会隐式地导致新String对象的创建等:

String str = str1 + str2;

自动装箱机制

这种例子也有很多,比如在执行类似如下代码时:

Integer codeSheepAge = 18;

其触发的自动装箱机制就会导致一个新的包装类型的对象在后台被隐式地创建出来。

函数可变参数

比如像下面这样,当我们使用可变参数语法int... nums来描述一个函数的入参时:

public double avg( int... nums ) {    double sum = 0;    int length = nums.length;    for (int i = 0; i<length; ++i) {        sum += nums[i];    }    return sum/length;}

从表面上看,函数的调用处可以传入各种离散参数参与计算:

avg( 2, 2, 4 );avg( 2, 2, 4, 4 );avg( 2, 2, 4, 4, 5, 6 );

而背地里可能会隐式地产生一个对应的数组对象进行计算。


总而总之,很多场景下对象的隐式创建也是数见不鲜,我们最起码要做到心中大致有数。


后 记

所以看完文章,再回到文章开头提到的问题,你还觉得Java程序员搞对象是件难事吗?这么多花里胡哨的对象生成法还不够你用的么。


咳咳,玩笑归玩笑,这其实是面试时最常问到的基础问题之一。有时候面试官冷不丁问一句:“在Java里,你有哪些方式可以创建一个对象呢?”。所以针对该问题,这篇来好好梳理和归纳一下。

好啦,一个小小的对象创建就能扯出这么多的花样,好在经过一番梳理和总结,也更便于掌握和理解了。

就这样吧,下篇见。

Java程序员“找对象”攻虐相关推荐

  1. Java程序员找工作很难吗?可能没有get这些内容

    Java程序员找工作很难吗?可能没有get这些内容 五分钟阅读下方文章 经常面试一些候选人,整理了下我面试使用的题目,陆陆续续整理出来的题目很多,所以每次会抽一部分来问.答案会在后面的文章中逐渐发布出 ...

  2. 新手Java程序员找工作更看重Java项目经验?

    动力节点IT培训,全真项目实操实训,贯穿八大行业,彻底帮助学员摆脱纸上谈兵的尴尬,一技成,天下行. 新手Java程序员找工作更看重Java项目经验?这个问题,想必是很多学Java的朋友也特别好奇的地方 ...

  3. 1—6年java程序员涨薪攻略,值得一看

    很多人在问我,程序员如何拿高薪,如何做到年薪40W+,其实总结出来还是一句话,你的技术决定你的能力以及薪资. 那么什么样的技术人才才能拿到一份Java行业里面的高薪呢?下面是我的一个总结技术. 必需掌 ...

  4. java程序员找工作前需要做什么准备?

    找工作前需要准备的杀手锏 高考前,我们要练兵考试和集训."临阵磨枪不快也光 ",找工作前,我们也必须要花很多精力去完成一些必要的准备. "不打无准备之仗", 精 ...

  5. 学习java 程序员技术练级攻略 作者:酷壳

    好文章要转载:http://blog.csdn.net/androidmylove/article/details/8990828 文章结束给大家来个程序员笑话:[M] 月光博客6月12日宣布了< ...

  6. 为什么程序员找对象这么难?

    不知从何时起程序员越来越难找妹子,看来这IT民工也是越来越难当了! 为什么找老婆都成问题了呢?按理说程序员这行当工资也不低啊,好歹也算个都市白领吧!从我加入程序员这行当这么多年来看,主要有以下几点原因 ...

  7. java程序员找工作前需要准备的杀手锏有哪些?

    高考前,我们要练兵考试和集训."临阵磨枪不快也光 ",找工作前,我们也必须要花很多精力去完成一些必要的准备. "不打无准备之仗", 精心准备和训练会对你有相当正 ...

  8. 计算机本科毕业后还需要参加培训吗?我来聊聊java程序员找工作的经验

    很多人在学习完计算机专业后,会发现在找到满意的工作时,还需要参加培训班并花费大量的时间和金钱,这让他们倍感不解.为什么在学习了四年的计算机专业之后,还需要参加培训班呢?难道这四年的时间和精力白费了吗? ...

  9. 金三银四已过,三年Java程序员找不到工作?这样的建议请你接受

    最近有一个程序员朋友有很大的困惑,是一个在深圳干了三年Java后端开发的程序员,找了好久的工作都没有找到.针对他的情况,下面是我给他的一些建议,同时也希望大家能有所收获. 从整个市场环境看:市场经济是 ...

最新文章

  1. SQL 中 PROCEDURE,TRIGGER,FUNCTION简单应用
  2. DFS——深度优先搜索基础
  3. [Swift]LeetCode859. 亲密字符串 | Buddy Strings
  4. 计算机网络(七)-物理层设备
  5. php基础教学笔记,php学习笔记:基础知识
  6. jquery对文本赋值和取值_jQuery对表单元素的取值和赋值操作代码
  7. Mysqldump命令参数介绍
  8. 加入docker管理员_如何使系统管理员和开发人员同意Docker
  9. C语言 指针与字符串
  10. java 通配符泛型_java泛型常用通配符实例解析
  11. 三菱伺服电机马达使用注意事项
  12. VTK-Wight 之vtkIncrementalOctreePointLocator简介
  13. 利用本地安全策略全面禁止360等特定厂商软件的安装与运行
  14. 高长勇老师:如何去构建企业的魂
  15. 一、ArcGIS Server篇:利用ArcGIS Server发布动态地图服务
  16. 最有元宇宙“面相“的Discord, 及腾讯/阿里/网易/百度/字跳元宇宙可行性路径分析
  17. egret新手引导反向遮罩
  18. 计算机操作系统之进程与线程
  19. c语言中被3除 余数为2,五年级奥数题:带余数除法(B)
  20. python +pygame 制作五子连珠小游戏

热门文章

  1. Rust之linux for windows 交叉编译gtk-rs
  2. win7服务器系统很卡,win7系统看斗鱼直播很卡的解决方法
  3. 英文文献的一些有意思的写法
  4. 列出每位学生的各科成绩,要求输出格式:姓名、学号、语文成绩、数学成绩、英语成绩,SQL怎么编写?
  5. 专访网易美术专家:《阴阳师》的美术,怎么用巧劲儿制造高级感?
  6. uni-app实现拼多多样式的拼单轮播功能
  7. 前端短路( ||)计算
  8. ae破碎效果在哪_利用Ae制作出破碎效果的详细步骤
  9. “su”和“su -”的不同
  10. 魅族note6 root方法