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

32次阅读

共计 4017 个字符,预计需要花费 11 分钟才能阅读完成。

前言

大家好啊,我是汤圆,明天给大家带来的是《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(栈)

它们的区别如下所示:

<img src=”https://tva1.sinaimg.cn/large/008eGmZEly1gpfqb6nb8mj30lq0d2wf4.jpg” 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. 如果子类定义了有参构造函数,且没有显示调用父类的构造函数,则编译器默认调用父类的无参构造函数

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

构造函数的重载:

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

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

上面举个例子:

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” alt=” 构造函数 调用层级 ” />

构造函数的私有化

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

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

比方 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 的构造函数,再调用子类的构造函数

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

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

图示阐明:

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

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

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

对象的回收(灭)

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

什么是没用的对象?

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

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

那怎么才算援用隐没呢?

根本分为两种状况:

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

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

总结

  1. 堆和栈

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

  1. 对象的创立

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

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

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

  1. 对象的回收

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

后记

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

正文完
 0