• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

关于java:Java中对象的生与灭-核心篇

java 搞代码 3年前 (2022-02-19) 167次浏览 已收录 0个评论
文章目录[隐藏]

前言

大家好啊,我是汤圆,明天给大家带来的是《Java中对象的生与灭- 外围篇》,心愿对大家有帮忙,谢谢

文章纯属原创,集体总结不免有过错,如果有,麻烦在评论区回复或后盾私信,谢啦

简介

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

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

目录

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

注释

堆(heap)和栈(stack)

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

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

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

上面咱们用理论代码,来看下堆和栈的区别

代码如下:

<code class="java">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(栈)

它们的区别如下所示:

<img src=”https://tva1.sinaimg.cn/large/008eGmZEly1gpfqb6nb8mj30lq0d2wf4.jpg&#8221; alt=”栈 堆” />

这里简略介绍一下下面的流程

  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. 如果子类定义了有参构造函数,且没有显示调用父类的构造函数,则编译器默认调用父类的无参构造函数

当你本人有创立构造函数(无参或有参)时,编译器都不会再去创立构造函数

构造函数的重载:

很罕用,个别用来设置属性的默认值

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

上面举个例子:

<code class="java">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
}

用图示意的话,就是上面这个样子

<img src=”https://tva1.sinaimg.cn/large/008eGmZEly1gpfrldwz7wj30mm0fkt99.jpg&#8221; alt=”构造函数 调用层级” />

构造函数的私有化

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

私有化阐明只有类自身能够调用,这种次要用在工厂办法中

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

<code class="java">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超类为止

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

<code class="java">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. 最终的构造函数,会调到这里为止
    } 
}
**/

输入如下:

<code class="java">father construct
sub construct

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

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

<img src=”https://tva1.sinaimg.cn/large/008eGmZEly1gpfz2zqrknj30pa0hmabg.jpg&#8221; alt=”SuperDemo的继承关系” style=”zoom:;” />

图示阐明:

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

左边的实现示意下面的结构实现会回到上面那一层,持续结构,直到以后类

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

对象的回收(灭)

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

什么是没用的对象?

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

说人话:当指向对象A的最初一个援用ref隐没时,这个对来源gaodai#ma#com搞*!代#%^码网象A就会变成没用的对象,期待着垃圾收集器的回收

那怎么才算援用隐没呢?

根本分为两种状况:

  • 如果援用是局部变量,那当援用所在的办法执行结束时,援用就会被开释,那么该对象随即也就会被标记为没用的对象,期待回收
  • 当援用指向其余对象或者null时,该对象会被标记为没用的对象,期待回收

下面都是假如援用是指向对象的最初一个援用的状况,如果有多个援用指向同一个对象,那么要等到援用都隐没,对象才会被标记为可回收,即没用的货色

总结

  1. 堆和栈

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

  1. 对象的创立

次要通过构造函数来创立,比方new对象

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

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

  1. 对象的回收

当指向对象的最初一个援用隐没时,这个对象就会变成没用的对象,期待着垃圾收集器的回收

后记

最初,感激大家的观看,谢谢


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:关于java:Java中对象的生与灭-核心篇

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址