转:https://www.cnblogs.com/KingIceMou/p/7245446.html

一、关键字new概述

"new"可以说是Java开发者最常用的关键字,我们使用new创建对象,使用new并通过类加载器来实例化任何我们需要的东西,但你是否深入了解过new在编译的瞬间都做了什么?

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

下面是对Oracle官网文章的翻译:http://docs.Oracle.com/javase/tutorial/java/javaOO/objectcreation.html

我们都知道,一个类为对象提供了蓝图,你从一个类创建一个对象。以下语句从createobjectdemo程序创建一个对象并将其赋值给一个引用变量:

Point originOne = new Point(23, 94);

Rectangle rectOne = new Rectangle(originOne, 100, 200);

Rectangle rectTwo = new Rectangle(50, 100);

第一行创建了一个 Point 类的对象,第二个和第三个线创建一个Rectangle 矩形类的对象。

这些陈述中的每一个都有三个部分(详细讨论):

声明Declaration:粗体代码是将变量名称与对象类型关联的变量声明。

实例化Instantiating :new关键字是一个java运算符,它用来创建对象。

初始化Initialization:new运算符,随后调用构造函数,初始化新创建的对象。

声明一个变量来指向一个对象,即引用

在此之前,你知道,要声明一个变量,你需要写:

type name;

这将告诉编译器你将使用name引用一个type类型的对象。用一个原始变量,这个声明也保留了适当的内存量的变量。

你也可以在自己的行上声明一个引用变量。例如:

Point  originone;

如果你只是声明一个像originone这样的引用变量,其价值将待定,直到有一个对象真正被创造和分配给它。只是简单地声明一个引用变量而并没有创建一个对象。对于这样,你需要使用new运算符。在你的代码中使用它之前,你必须指定一个对象给originone。否则,你会得到一个编译器错误-----空指针异常。

处于这种状态的变量,目前没有引用任何的对象,可以说明如下(变量名,originone,一个引用没指向任何对象)。

实例化一个类对象

new运算符实例化一个类对象,通过给这个对象分配内存并返回一个指向该内存的引用。new运算符也调用了对象的构造函数。

注意:“实例化一个类的对象”的意思就是“创建对象”。创建对象时,你正在创造一个类的“实例”,因而“实例化”一个类的对象。

new运算符需要一个单一的,后缀参数,需要调用构造函数。构造函数的名称提供了需要实例化类的名称。

new运算符返回它所创建的对象的引用。此引用通常被分配给一个合适的类型的变量,如:Point  originone =new Point(23,94);

由new运算符返回的引用可以不需要被赋值给变量。它也可以直接使用在一个表达式中。例如:  int height = new Rectangle().height;

初始化一个类对象

这是Point类的代码

public class Point {

public int x = 0;

public int y = 0;

//constructor

public Point(int a, int b) {

x = a;

y = b;

}

}

这个类包含一个单一的构造函数。你可以识别一个构造函数,因为它的声明使用与类具有相同的名称,它没有返回类型。在Point类构造函数的参数是两个整数参数,如代码声明(int  a,int  b)。下面的语句提供了94和23作为这些参数的值:

Point originOne = new Point(23, 94);    //结果可描述为下图

这是Rectangle类,包含4个版本的构造方法

public class Rectangle {

public int width = 0;

public int height = 0;

public Point origin;

// four constructors

public Rectangle() {

origin = new Point(0, 0);

}

public Rectangle(Point p) {

origin = p;

}

public Rectangle(int w, int h) {

origin = new Point(0, 0);

width = w;

height = h;

}

public Rectangle(Point p, int w, int h) {

origin = p;

width = w;

height = h;

}

// a method for moving the rectangle

public void move(int x, int y) {

origin.x = x;

origin.y = y;

}

// a method for computing the area of the rectangle

public int getArea() {

return width * height;

}

}

每个构造函数都允许你为矩形的起始值、宽度和高度提供初始值,同时使用原始类型和引用类型。如果一个类有多个构造函数,它们必须有不同的签名。java编译器区分构造函数基于参数的数量和类型。当java编译器遇到下面的代码,它知道在矩形类,需要一点争论,后面跟着两个整数参数调用构造函数:

Rectangle rectOne = new Rectangle(originOne, 100, 200);

结果可描述为下图:

总结:

1.Java关键字new是一个运算符。与+、-、*、/等运算符具有相同或类似的优先级。

2.创建一个Java对象需要三部:声明引用变量、实例化、初始化对象实例。

3.实例化:就是“创建一个Java对象”-----分配内存并返回指向该内存的引用。

4.初始化:就是调用构造方法,对类的实例数据赋初值。

5.Java对象内存布局:包括对象头和实例数据。如下图:

对象头:它主要包括对象自身的运行行元数据,比如哈希码、GC分代年龄、锁状态标志等;同时还包含一个类型指针,指向类元数据,表明该对象所属的类型。

实例数据:它是对象真正存储的有效信息,包括程序代码中定义的各种类型的字段(包括从父类继承下来的和本身拥有的字段)。

在hotSpot虚拟机中,对象在内存中的布局可以分成对象头、实例数据、对齐填充三部分。对齐填充:它不是必要存在的,仅仅起着占位符的作用。

6.Object obj = new Object();

那“Object obj”这部分的语义将会反映到Java栈的本地变量表中,作为一个reference类型数据出现。而“new Object()”这部分的语义将会反映到Java堆中,形成一块存储了Object类型所有实例数据值(Instance Data,对象中各个实例字段的数据)的结构化内存,根据具体类型以及虚拟机实现的对象内存布局(Object Memory Layout)的不同,这块内存的长度是不固定的。另外,在Java堆中还必须包含能查找到此对象类型数据(如对象类型、父类、实现的接口、方法等)的地址信息,这些类型数据则存储在方法区中。

二、内存分配原理

内存分配,在哪分配?-------尽管Java对象的内存分配可以使用逃逸分析技术和栈外分配,但不可否认这仅仅是为了降低GC回收频率以及提升GC回收效率的一种辅助手段,所以Java堆区仍然是分配/存储对象实例的主要区域,这一点毋庸置疑。参阅http://blog.csdn.net/ljheee/article/details/52226368。

参考《Java虚拟机规范(第7版)》的描述,JVM包含三种引用类型,分别是类型 (class type),数组类型(array type)和接口类型(interface type),这些引用类型的值则分别 由类实例、数组实例以及实现了某个接口的派生类实例负责动态创建,那么JVM中究 竟是如何为这些类型创建对应的对象实例呢?-------------如果是在Java语法层面上创建 一个对象,无非就是使用一个简单的new关键字即可,但是在JVM中就没有那么简 单了,其实牵扯到细节的实现相当复杂,而且过程繁多。简单地说,当Java语法层面 使用new关键字创建一个Java对象时,JVM首先会检查这个new指令的参数能否在常 量池中定位到一个类的符号引用,然后检查与这个符号引用相对应的类是否已经成功经 历加载、解析和初始化等步骤,当类完成装载步骤之后,就已经完全确定出创建对象实 例时所需的内存空间大小,接下来JVM将会对其进行内存分配,以存储所生成的对象 实例。如下图所示:

为新对象分配内存是一件非常严谨和复杂的任务,JVM的设计者们不仅需要考虑内存如何分配、在哪分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还要考虑GC执行完内存回收后是否会在内存空间中产生内部碎片。如果内存空间以规整和有序的的方式分布,当为新对象分配内存时,只需要修改指针的偏移量将新对象分配在第一个空闲内存位置上,这种分配方式就叫做指针碰撞(Bump the Pointer),反之则只能使用空闲列表(Free List)执行内存分配。

基于分代的概念,Java堆区如果进一步细分的话,还可分为:新生代 ( Young )和老年代 ( Old );这也就是JVM采用的“分代思想”,简单说,就是针对不同特征的java对象采用不同的策略实施存放和回收,所用分配机制和回收算法就不一样。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。(《Java虚拟机精讲》(高翔龙...))

分代收集算法:采用不同算法处理[存放和回收]Java瞬时对象和长久对象。大部分Java对象都是瞬时对象,朝生夕灭,存活很短暂,通常存放在Young新生代,采用复制算法对新生代进行垃圾回收。老年代对象的生命周期一般都比较长,极端情况下会和JVM生命周期保持一致;通常采用标记-压缩算法对老年代进行垃圾回收。

这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。那么Java堆区被细分成这么多区域,对象实例究竟是存储在堆区中的那一个区域下呢?在JVM运行数据区中,堆区和方法区是线程共享的数据区,任何线程都可以访问到这两个区域中的共享数据,由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆中划分内存空间是非线程安全的,所以务必需要保证数据操作的原子性。基于线程安全的考虑,如果一个类在分配内存之前成功完成的类加载,JVM会优先选择在TLAB(Thread Local Allocation Buffer,本地线程分配缓存区)中为对象实例分配内存空间,TLAB在Java堆中是一块线程私有数据区,它包含在Eden空间内,除了可以避免一系列的非线程安全问题外,同时还能提高内存分配的吞吐量,因此我们可以将这种内存分配方式称之为快速分配策略。

当为对象成功分配好所需的内存空间(实例化)后,JVM接下来要做的任务就是-------初始化对象实例。JVM首先会对分配好的内存空间进行零值初始化,这一步操作确保了对象的实例字段在Java代码中可以不用赋初值就能够直接使用,程序能够访问到这些字段的数据类型所对应的零值。

对分配后的内存空间进行零值初始化后,JVM就会初始化对象头和实例数据。最后将对象引入栈后,再更新PC寄存器中的字节码指令地址。经过这一系列的操作步骤之后每一个Java对象实例才算是真正的创建成功。

总结:

1.在Java语法层面上创建一个对象,使用一个简单的new关键字即可,但是在JVM中细节的实现相当复杂,而且过程繁多。

2.当Java语法层面使用new关键字创建一个Java对象时,JVM首先会检查相对应的类是否已经成功经历加载、解析和初始化等步骤;当类完成装载步骤之后,就已经完全确定出创建对象实例时所需的内存空间大小,才能对其进行内存分配,以存储所生成的对象实例。

3.实例化之后,进行初始化(初始化对象头和实例数据)。

4.内存分配方式有:指针碰撞(Bump the Pointer)、快速分配策略、空闲列表(Free List)。

5.在并发环境下从堆中划分内存空间是非线程安全的,new运算符具有-------数据操作的原子性;也就是说创建一个Java对象分配内存,要么所有步骤都成功,返回对象的引用,要么回归到创建之前的内存状态,返回为NULL。

6.通过new创建一个Java对象,如果成功则返回这个对象的引用,开发者不可直接操作对象实例,需要通过这个引用“牵引”。

看完这篇文章,相信你对Java关键字new及Java对象的完整创建过程有了更深的认识,就不会只停留在new一个对象就完了。(参阅《Java虚拟机精讲》(高翔龙...))

java new 关键字到底做了什么?相关推荐

  1. volatile关键字到底做了什么?

    话不多说,直接贴代码 class Singleton {private static volatile Singleton instance;private Singleton(){}//双重判空pu ...

  2. java webservice报文过长_工作1-5年的Java程序猿到底需要怎样的一个技术栈?

    工作1-5年的Java程序猿到底需要怎样的一个技术栈? 前言: 具有1-5年开发经验的程序员 需要学习的内容其实还有很多很多. 今天跟大家交流一下希望分享出来的对大家能够有帮助,这是我这些年总结出的一 ...

  3. MySQL实战 | 01 当执行一条 select 语句时,MySQL 到底做了啥?

    原文链接:当执行一条 select 语句时,MySQL 到底做了啥? 也许,你也跟我一样,在遇到数据库问题时,总时茫然失措,想重启解决问题,又怕导致数据丢失,更怕重启失败,影响业务. 就算重启成功了, ...

  4. 又被黑!百度到底做错了什么?

    前几天中国互联网巨头的市值又"更新"了一下,没想到百度现在"沦落"成了其它互联网公司市值的衡量单位. 图片来源自腾讯新闻 这一次,怕百度被"黑&quo ...

  5. Java—— this 关键字(实例是什么?)

    目录 Java面向对象设计 - Java this关键字 一.什么是 this? 注意: 二.正式学习this关键字 1.this.调用本类属性,也就是类中的成员变量:用来区分成员变量和局部变量(重名 ...

  6. 灵魂拷问:java的String到底可不可变?

    从认识java的那天起,就被告知String是不可变的,因为源码上是这样写的 public final class Stringimplements java.io.Serializable, Com ...

  7. java控制关键字continue,break,return

    java控制关键字continue,break,return /*** break 可以停止循环继续执行 只能停止最内层的循环* 如果想停止外层for循环 需要给外层循环做标记* continue 停 ...

  8. 面试官系统精讲Java源码及大厂真题 - 03 Java 常用关键字理解

    03 Java 常用关键字理解 引导语 Java 中的关键字很多,大约有 50+,在命名上我们不能和这些关键字冲突的,编译会报错,每个关键字都代表着不同场景下的不同含义,接下来我们挑选 6 个比较重要 ...

  9. 你能分清Java的关键字和标识符吗?

    文章目录 Java标识符和关键字 概念 标识符 关键字 区别 Java标识符和关键字 概念 标识符 Java 中标识符是为方法.变量或其他用户定义项所定义的名称.标识符可以有一个或多个字符. 在 Ja ...

  10. java的关键字和保留字_「Java」详解常见的53个关键字

    1.在Java中目前一共有53个关键字: 其中由51+2个保留字=53个关键字 [友情提示 : Java的关键字都是小写哟] 2.Java的保留字有多少个?分别是什么? 答:Java的保留字有2个,J ...

最新文章

  1. freeImage图像旋转滤波
  2. day17 - 动态内存的分配初步理解
  3. awvs 13使用_如何解密AWVS?15行代码就够了!
  4. python(matplotlib2)——legend图例,Annotation图片注解
  5. linux 命令行 ctrl z,Linux操作系统下运行命令时CTRL+Z的作用
  6. 为什么对开发者很重要?
  7. NC代码调试 - 持续更新
  8. 数据库中的数据类型和c#的数据类型的映射表(转)
  9. [软件工程]在线教程
  10. 耍小聪明的人,永远不可能取得自己事业的成功
  11. CommandArgument 与 CommandName 属性
  12. OFD文件免费转PDF
  13. 人人商城小程序消息服务器配置,人人商城小程序订阅消息设置方法绝对好使
  14. 单片机矩阵键盘扫描程序c语言,51单片机矩阵键盘扫描程序
  15. WPS文档批量转换成PDF
  16. 被破解毁掉的国产游戏之光
  17. Java文件的上传和下载
  18. JS 日历插件 实现农历、节气 可自定义加班和休假
  19. 一文读懂机器学习需要哪些数学知识
  20. linux环境变量、交叉编译工具链gcc

热门文章

  1. 什么是deployment 声明式升级应用
  2. oracle判断字符串以什么开头_sql语句判断字符串以什么什么开头
  3. 利用matlab编程实现主成分分析,利用Matlab编程进行主成分分析
  4. calabash android教程,Calabash Android 使用教程 (二)
  5. android合入第三方库,Android中inflate和merge结合使用
  6. ukf实测信号的预测 matlab,无迹卡尔曼滤波UKF无线传感器网络定位跟踪matlab源码实现.pdf...
  7. MySQL(20)-----数据库设计的常规步骤
  8. 【Codeforces 1114C】Trailing Loves (or L'oeufs?)
  9. 数据库 chapter 10 数据库恢复技术
  10. testng自定义注解