专栏首页汤圆学JavaJava中对象的生与灭- 核心篇
原创

Java中对象的生与灭- 核心篇

前言

大家好啊,我是汤圆,今天给大家带来的是《Java中对象的生与灭- 核心篇》,希望对大家有帮助,谢谢

文章纯属原创,个人总结难免有差错,如果有,麻烦在评论区回复或后台私信,谢啦

简介

前面我们了解了Java的三大特性,其中介绍了类的继承、重载等,这里我们就基于这些知识点,把对象的创建和回收进行一个简单的介绍

这篇不是很长,只是介绍核心的几个知识点,相信大家很快就可以看完,真的

目录

  • 堆和栈
  • 构造函数(生)
  • 对象的回收(灭)

正文

堆(heap)和栈(stack)

堆是一块内存,用来存放对象

栈是另一块内存,用来执行方法并存储局部变量,遵循后进先出的原则;

PS:栈并不存储方法,只是执行方法,执行完方法后,会将方法弹出栈(方法存在方法区)

下面我们用实际代码,来看下堆和栈的区别

代码如下:

​
public class LiveAndDeathDemo {
    // 基本类型属性
    private int a;
​
    public static void main(String[] args) {
        LiveAndDeathDemo live = new LiveAndDeathDemo(1);
        live.fun();
    }
    public void fun(){
        int temp = 10;
        System.out.println(temp);
    }
​
    public LiveAndDeathDemo(int a) {
        this.a = a;
    }
    public int getA() {
        return a;
    }
​
    public void setA(int a) {
        this.a = a;
    }
}
​

可以看到,有一个实例变量a(堆), 两个方法main和fun,其中fun有一个局部变量temp(栈)

它们的区别如下所示:

这里简单介绍一下上面的流程

  1. main方法压入栈中,创建局部变量live(对象的引用)
  2. 创建对象live,在堆中开辟内存,将live放入堆中
  3. live调用fun方法,将fun压入栈中(此时fun在栈顶)
  4. fun执行完成,出栈,继续执行main方法
  5. 最后main方法执行完成,也出栈,程序结束

这里可能有朋友要问了,那如果属性是一个引用呢?它要存放在哪里?

引用存放在堆里,引用指向的对象也存放在堆里,只不过是堆的另一个地方

如下图所示:堆中 live对象的属性 liveRef 指向了另一个对象(live对象2)

为啥要先介绍堆和栈呢?

因为堆和栈跟对象的生活息息相关

如果用人来比作对象的话,那堆就是人的家,栈就是外面的世界

我们出生在家里,跟外面的世界打交道,最后在家里。。。

对象的创建(生)

生存还是毁灭,这是一个问题。 -- 莎士比亚《哈姆莱特》

在Java的花花世界中,这也是个问题,不过是个有答案的问题;

答案就在下面。。。

这里我们先把问题简化

因为我们最常见的创建对象是通过new创建,而new对象的核心就是通过构造函数来实现,所以我们这里简单起见,着重介绍构造函数,其他的后面等到虚拟机部分再介绍

构造函数的分类:

  • 无参构造函数
  • 有参构造函数

构造函数和普通方法的区别

  1. 构造函数没有返回类型
  2. 构造函数名与类名一致

关于编译器的默认操作

  1. 如果没有定义构造函数,编译器会默认创建一个无参构造函数
  2. 如果子类定义了有参构造函数,且没有显示调用父类的构造函数,则编译器默认调用父类的无参构造函数

当你自己有创建构造函数(无参或有参)时,编译器都不会再去创建构造函数

构造函数的重载:

很常用,一般用来设置属性的默认值

具体做法就是多个构造函数层层调用(又来套娃了)

下面举个例子:

​
public class LiveAndDeathDemo {
    private int a;
    private String name;
    
    public LiveAndDeathDemo(){
        this(1);
    }
    public LiveAndDeathDemo(int a) {
        this(a, "JavaLover");
    }
    public LiveAndDeathDemo(int a, String name) {
        this.a = a;
        this.name = name;
    }
  // 省略getter,setter
}

用图表示的话,就是下面这个样子

构造函数的私有化

如果构造函数私有化,那么它要怎么用呢?

私有化说明只有类本身可以调用,这种主要用在工厂方法中

比如Java中的LocalDate,源码如下:

public final class LocalDate
        implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable {
  // 构造函数私有化 
  private LocalDate(int year, int month, int dayOfMonth) {
        this.year = year;
        this.month = (short) month;
        this.day = (short) dayOfMonth;
    }
  // 对外提供一个静态方法,用来创建对象实例
   public static LocalDate of(int year, int month, int dayOfMonth) {
        YEAR.checkValidValue(year);
        MONTH_OF_YEAR.checkValidValue(month);
        DAY_OF_MONTH.checkValidValue(dayOfMonth);
        return create(year, month, dayOfMonth);
    }
}

这种用法在LocalDate这种工具类中用的比较多,还有就是单例模式(后面设计模式时再介绍)

上面介绍的构造函数没有介绍到父类,下面开始介绍

如果有父类,构造函数的有哪些不一样的地方

this和super:

在介绍父类的构造函数之前,有必要介绍下这个super

  • this指向当前类
  • super指向父类

super用来显式调用父类相关属性和方法(包括构造函数)

比如super.filedA,super.fun()

这里有个特例,如果是在子类的构造函数中或者覆写方法中,则直接调用super()即可调用父类对应的构造函数或方法(下面代码有演示)

构造函数的执行顺序

  1. 如果子类Dog继承父类Animal,那么会先调用父类的构造函数,再调用子类的构造函数
  2. 如果父类Animal上面还有父类,会继续往上调用
  3. 上面这个过程就叫做“构造函数链

这个关系有点像是:子女和父母的关系,子女要想出生,必须先让爷爷奶奶把父母生出来,然后父母才能生子女

所以这里如果我们要构造子类,必须先构造父类;如果父类还有父类,则继续延伸,一直到Object超类为止

下面用代码演示下这个层层调用的过程:

​
public class SuperDemo extends Father{
    public SuperDemo() {
        // 1.1 这里显示调用父类的构造函数
        // 1.2 Super必须放在构造函数的第一行
        super();
        System.out.println("sub construct");
    }
​
    public static void main(String[] args) {
        SuperDemo demo = new SuperDemo();
    }
}
class Father{
    public Father() {
        // 2. 这里没有显示调用父类(Object)的构造函数,编译器会自己去调用
        System.out.println("father construct");
    }
}
/** 假设下面这个Object就是我们的超类
class Object{
  public Object(){
      // 3. 最终的构造函数,会调到这里为止
  } 
}
**/
​

输出如下:

father construct
sub construct

可以看到,先调用父类Father的构造函数,再调用子类的构造函数

他们之间的继承关系如下:

图示说明:

左边的虚线表示层层往上调用,直到超类Object

右边的实现表示上面的构造完成会回到下面那一层,继续构造,直到当前类

好了,构造的过程大致就是这个样子了,还有很多其他方面的细节(比如类的初始化等)这里先不介绍了,太多了,放到后面介绍

对象的回收(灭)

对象的回收是在程序内存不够用时,将没用的对象(可回收)进行释放的一种操作,这个操作是由垃圾收集器GC来完成的

什么是没用的对象?

没用的对象就是可回收的对象

说人话:当指向对象A的最后一个引用ref消失时,这个对象A就会变成没用的对象,等待着垃圾收集器的回收

那怎么才算引用消失呢?

基本分为两种情况:

  • 如果引用是局部变量,那当引用所在的方法执行完毕时,引用就会被释放,那么该对象随即也就会被标记为没用的对象,等待回收
  • 当引用指向其他对象或者null时,该对象会被标记为没用的对象,等待回收

上面都是假设引用是指向对象的最后一个引用的情况,如果有多个引用指向同一个对象,那么要等到引用都消失,对象才会被标记为可回收,即没用的东西

总结

  1. 堆和栈

堆存放对象,栈用来执行方法并存放局部变量

  1. 对象的创建

主要通过构造函数来创建,比如new对象

如果是反序列化来创建对象,则不会构造,因为构造后,对象的属性会被重新初始化,那么序列化的属性值就被抹掉了(前面的Java中的IO流有涉及)

如果子类有父类,则先调用父类的构造函数,如果父类还有父类,则依次类推,直到Object超类

  1. 对象的回收

当指向对象的最后一个引用消失时,这个对象就会变成没用的对象,等待着垃圾收集器的回收

后记

最后,感谢大家的观看,谢谢

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java核心技术 对象与类

    在Java中,使用构造器构造新实例。构造器是一种特殊的方法,用来构造并初始化对象。

    菜的黑人牙膏
  • Java中的类与对象(基础篇七)

    Java是一门面向对象的编程语言,面向对象是一种程序设计思想,我们都是在面向对象思想的指引下去设计、开发计算机程序。Java中有“万物皆对象”的说法,这里的对象...

    故里
  • 回顾15年我从嵌入式转至Java后端阅读的一些书籍,让我变成了自己想要的样子

    很早就想整理下自己读过的一些书了,想把感觉还不错的分享和推荐给大家。然而,写这篇文前面的一个月一直在忙着公司的项目和另一本技术书的阅读。感觉需要做一点事情来定下...

    养码场
  • Android粒子篇之文字的粒子化运动

    张风捷特烈
  • 浏览器中的JavaScript核心BOM(浏览器对象模型)重点掌握对象之Window对象的属性与方法

    浏览器为我们提供了JavaScript运行的环境,同时也给我们提供了很多的对象,我们从这一篇开始逐个讲解浏览器上的内置对象的属性和方法。常见的浏览器内置对象有:...

    @零一
  • 浏览器中的JavaScript核心BOM(浏览器对象模型)重点掌握对象之Location对象的属性与方法

    在学过JavaScript之后,我们都知道对象分为内置对象 、宿主对象 、自定义对象,我们经常用到的浏览器中的内置对象就是宿主对象的一种,浏览器的内置对象有很多...

    @零一
  • Java对象的结构与对象在内存中的结构

    当我们在Java中使用new这个指令创建一个对象的时候,对象的创建到底经过了什么样的一个过程呢?

    星如月勿忘初心
  • java中类与对象的关系与区别

    参考博客:https://blog.csdn.net/scbiaosdo/article/details/79912037

    用户7886150
  • 浏览器中的JavaScript核心BOM(浏览器对象模型)

    在学过JavaScript之后,我们都知道对象分为内置对象 、宿主对象 、自定义对象,我们经常用到的浏览器中的内置对象就是宿主对象的一种,浏览器的内置对象有很多...

    @零一
  • 在游戏中,爆出神装是真随机还是假随机?

    周末,陪女朋友去电影院看了《复仇者联盟4:终局之战》,作为一个漫威粉三个小时看的是意犹未尽。出来之后,准备和女朋友聊一聊漫威这十年。

    Java3y
  • 对象共享:Java并发环境中的烦心事

    并发的意义在于多线程协作完成某项任务,而线程的协作就不可避免地需要共享数据。今天我们就来讨论下如何发布和共享类对象,使其可以被多个线程安全地访问。

    lyb-geek
  • Java核心基础知识:面向对象的三大特性讲义

    上述的每一个步骤, 我们都是参与者, 并且需要面对具体的每一个步骤和过程, 这就是面向过程最直接的体现.

    用户8870853
  • Java核心基础知识:面向对象的三大特性讲义

    上述的每一个步骤, 我们都是参与者, 并且需要面对具体的每一个步骤和过程, 这就是面向过程最直接的体现.

    用户1880875
  • Java虚拟机--对象回收

    SuperHeroes
  • Java核心(三)并发中的线程同步与锁

    乐观锁、悲观锁、公平锁、自旋锁、偏向锁、轻量级锁、重量级锁、锁膨胀...难理解?不存的!来,话不多说,带你飙车。

    Java中文社群-磊哥
  • java之hibernate之session中对象的生命周期

    1. session是用来执行对象的crud操作,并且session是对象事务工厂。session是线程级别的,所以生命周期比较短。

    Vincent-yuan
  • HarmonyOS与Android的全面对比

    第二是我个人非常看好鸿蒙系统的未来,清楚明白华为和一些民族企业担负的责任和国人的期待,虽然带着一些民族感情;鸿蒙刚发布的时候自己是非常激动的,但是后来项目太忙一...

    肉眼品世界
  • 为什么真正的代码高手少之又少?

    本文来自一名资深Android程序员,我们且看他的观点,是否更能坚定你从事App开发的信心。   做开发快七年了,对于程序员,外行人总有着数不完的...

    程序员互动联盟
  • IllegalMonitorStateException 异常 与 Java中的"对象监视器Monitor"和"对象锁"详解

    在线程中调用wait方法的时候要用synchronized锁住对象,确保代码段不会被多个线程调用。

    一个会写诗的程序员

扫码关注云+社区

领取腾讯云代金券