JAVA中new关键字及对象(object)与引用(reference)介绍

JAVA中new关键字及对象(object)与引用(reference)介绍

Java使用关键字 new 来创建对象,这是常用的创建对象的方法,有人给出语法格式如下:

类名 对象 = new 类名([参数]);

特别提示,实际上应是或应理解为:

类名 对象名 = new 构造方法名([参数列表]);

上句和如下两句:

类名 对象名;

对象名 = new 构造方法名([参数列表]);

等价。

赋值号右边的new是为新建对象开辟内存空间的运算符,用new运算符开辟新建对象的内存之后,系统自动调用构造方法初始化该对象。

New后是构造方法名,只不过类的构造方法名和类名一样。Java每个类都有构造方法,如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认的无参数构造方法。构造方法定义了当创建一个对象时要进行的操作。关于构造方法请参见其它资料。

其中的对象名,也称为对象引用名;[参数列表]是可选项,多个参数用英文逗号分隔。

【官网对new 关键字介绍见 Creating Objects https://docs.oracle.com/javase/tutorial/java/javaOO/objectcreation.html

下面详细解析之。

Thinking in Java》一书中提到,引用和对象就像瑶控器和电视机。用瑶控器(引用)来操作电视机(对象),想换频道什么的直接操作瑶控器就可以了,瑶控器再来调控电视机。这个比喻非常好说明了对象与引用的关系。

创建的对象可以通过引用来操作。

通常用new关键字来创建一个对象,就可以通过引用来操作。

那么对象是怎样存储的,内存又是怎样分配的呢?

栈(stack):位于通用RAM中,通过栈指针的移动来分配和释放内存,指针向下移动分配新的内存;指针向上移动则释放内存。速度仅次于寄存器。创建程序时,Java编译器必须知道存储在栈内所有数据的确切大小和生命周期,因为它必须生成相应的代码,以便上下移动栈指针,这就限制了程序的灵活性。所以java中的对象并不存放在栈当中,但对象的引用存放在栈中。

堆(heap):也是位于RAM中的内存池,用于存放所有的JAVA对象。编译器不需要知道要从堆里分配多少存储区域,也不需要知道存储的数据在堆里面存活多长时间,因此堆要比栈灵活很多。当你new创建一个对象时,编译器会自动在堆里进行存储分配。当然,为这种灵活性必须要付出相应的代码。用堆进行存储分配比用栈进行存储存储需要更多的时间。

静态存储(static storage):这里的静态是指在固定的位置(也在RAM里)。静态存储里存放程序运行时一直存在的数据。你可用关键字static来标识一个对象的特定元素是静态的,即存放类中的静态成员,但JAVA对象本身从来不会存放在静态存储空间里。

常量存储(constant storage):存放字符串常量和基本类型常量(public static final)。常量值通常直接存放在程序代码内部,它们永远不会被改变。有时,在嵌入式系统中,常量本身会和其他部分分割离开,所以在这种情况下,可以选择将其放在ROM中。

寄存器(register):由于寄存器是在CPU内部的,所以它的速度最快,但是数量有限,所以由编译器根据需求进行分配。

 

Java垃圾回收机制

垃圾回收回收的是无任何引用的对象占据的内存空间(堆)而不是对象本身。

1)引用计数器方式:一种简单但是速度很慢的垃圾回收策略。即每个对象都有一个引用计数器,当有引用连接至对象时计数器加1;当引用离开时计数器减1。垃圾回收器会在含有全部对象的列表中遍历,发现某个对象的引用计数器为0时,就释放其占用的内存。

优点:引用计数收集器可以很快的执行,交织在程序运行中。对程序不被长时间打断的实时环境比较有利。

缺点:无法检测出循环引用。如父对象有一个对子对象的引用,子对象反过来引用父对象。这样,他们的引用计数永远不可能为0

2)自适应、分代的、停止——复制、标记——清扫 垃圾回收方式:

停止——复制:先暂停程序的运行,然后将所有活的对象从当前堆复制到另一个堆,没有被复制的都是垃圾。当对象从一个堆复制到另一个堆,它们的排列是一个挨着一个的,所以新堆保持紧凑排列。

标记——清扫:遍历所有的引用,找出所有活的对象,然后对它们进行标记,这个过程不会回收任何对象,只有全部标记工作完成时才开始清除工作。没有被标记的对象将会被释放,不发生任何复制动作,所以剩下的堆空间不是连续的。

 

对象:要理解什么是对象,需要跟类一起结合起来理解。下面这段话引自《Java编程思想》中的一段原话:

 “按照通俗的说法,每个对象都是某个类(class)的一个实例(instance),这里,‘类'就是‘类型'的同义词。”

从这一句话就可以理解到对象的本质,简而言之,它就是类的实例,比如所有的人统称为人类,这里的人类就是一个类(物种的一种类型),而具体到每个人,比如张三这个人,它就是对象,就是人类的实例。

对象引用:Java编程思想》一段话:每种编程语言都有自己的数据处理方式。有些时候,程序员必须注意将要处理的数据是什么类型。你是直接操纵元素,还是用某种基于特殊语法的间接表示(例如C/C++里的指针)来操作对象。所有这些在 Java 里都得到了简化,一切都被视为对象。因此,我们可采用一种统一的语法。尽管将一切都看作对象,但操纵的标识符实际是指向一个对象的引用reference)。  

基本数据类型和引用数据类型:Java的其中基本类型变量有四类8种:byte、 short 、int 、long 、float 、double、 char、 boolean,除了8种基本数据类型变量,其他变量都是引用数据类型,如类、接口、数组等。

基本数据类型,只有一块存储空间, 在栈中,存放的是具体数据值。

引用数据类型,有两块存储空间一个在栈(Stack)中,一个在堆(heap)中。堆中存放对象实体(使用new关键字,即表示在堆中开辟一块新的存储空间),栈中存放对象在堆中所在位置的首地址。new 操作符的返回值是一个对象的引用。

一个引用类型变量(栈中的一块内存空间)保存了一个该类型对象在堆中所在位置的首地址,也称作一个引用类型变量指向了一个该类型的对象,通过这个变量就可以操作对象中的数据。

下面借助一个简单的例子解释之

// Person类部分

class Person {

    String name;

    int age;

}

 

//PersonTest 类部分

public class PersonTest {

    public static void main(String[] args) {

        Person p1 = new Person();

        p1.name = "张伟";

        p1.age = 17;

        System.out.println("姓名:" + p1.name + ",年龄:" + p1.age);

 

        Person p2 = new Person();

        p2.name = "李丽";

        p2.age = 12;

        System.out.println("姓名:" + p2.name + ",年龄:" + p2.age);

 

        p1 = p2;

        p1.age = 13;

        System.out.println("姓名:" + p1.name + ",年龄:" + p1.age);

        System.out.println("姓名:" + p2.name + ",年龄:" + p2.age);

    }

}

 

下面使用bluej(由澳大利亚墨尔本Monash大学BlueJ小组设计并开发的Java开发环境)编译运行情况

 

运行结果如下:

解释如下

内存分配示意图如下:

当多个对象的引用指向堆同一块内存空间(p1 = p2; 即将一个对象的引用赋值给另一个变量,两个变量所记录的地址值是一样的,此时p1指向 p2所指向的对象,不再指向 p1原本指向的对象,如上图所示),只要通过任何一个对象引用修改了对象中的数据,随后,无论使用哪一个对象引用获取数据,得到的都是修改后的值,因此,最后两行打印语句的结果是一样的。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习&实践爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值