对象的前世今生

对象如何创建、存在何处以及如何让保存和抛弃更有效率。

会述及堆、栈、范围、构造器、超级构造器、空引用等。

1、内存的两个区域:堆和栈

堆(heap):对象的生存空间,又称为可垃圾回收的堆

栈(stack):方法调用和局部变量。

2、变量的生存空间

  • 实例变量:声明在类中方法之外的地方,存在于所属的对象中,因此保存在堆中。
  • 局部变量:局部变量和方法的参数都声明在方法中,是暂时的,生命周期仅限于方法被放在栈的这段时间(方法调用至执行完毕),保存在栈中。

3、方法在栈中的存放顺序

根据调用顺序依次放在栈顶,最先释放的在最上面。

4、对象的局部变量

非primitive变量只是保存对象的引用。

对象存在堆中,不论对象是否声明或创建,如果局部变量是个对该对象的引用,只有变量本身会放在栈上。

对象本身存在堆上。

5、对象的实例变量:存放于对象所属的堆空间中

需要多大的存放空间:

  • 实例变量是 primitive主数据类型:

java会根据主数据类型的大小,在对象所属的堆空间为该实例变量留下空间。如int需要32位,long需要64位。

变量所需要的空间在对象中。

  • 实例变量是 对象的引用

对象带有对象引用的变量:此时真正的问题是,是否要保留对象带有的所有对象的空间?--不带

举例1:

public class CellPhone{

    // 有声明变量,未赋值;Antenna对象在堆上只会留下变量的空间

    private Antenna ant;

}

举例2:

public class CellPhone{

    // 引用变量被赋值一个新的对象,新的Antenna对象在堆上占有堆空间

    private Antenna ant = new Antenna();

}

结论:引用和对象都在堆中。

6、构造函数

对象创建三部曲:声明引用变量,创建对象,连接对象和引用。

Duck myDuck = new Duck();

其中,创建对象是在调用 Duck的构造函数。

  • 什么是构造函数:构造函数带有你在初始化对象时,会执行的程序代码。新建一个对象时就会被执行。
  • 如果没有写构造函数,编译器会帮你写一个:public Duck(){}
  • 构造函数的特点:无返回类型;与类同名

7、构造Duck

代码:

public class Duck{
    public Duck(){
        System.out.println("Quck!");
    }
}
public class UseADuck {
    public static void main(String[] args) {
        Duck d = new Duck();
    }
}

输出:Quck!

总结:构造函数让你有机会介入new的过程中。

8、新建Duck状态的初始化

代码:

public class Duck {
    int size;     public Duck(){
        System.out.println("Quack!");
    }     public void setSize(int newSize){
        size = newSize;
    }
}

代码:

public class UseADuck {
    public static void main(String[] args) {
        Duck d = new Duck();
        d.setSize(45);
    }
}

执行结果:Quack!

总结:

大部分人都是使用构造函数来初始化对象的状态,即设置和给对象的实例变量赋值。在上面的代码中,可以使用setSize()来设定大小,但这会让Duck暂时处于没有大小数值的状态(实例变量没有默认值),且需要两行搞定。

问题:先构造对象再设置大小会很危险,万一忘记设置会出问题。

9、使用构造函数来初始化Duck的状态

代码:

public class Duck02 {
    int size;     public Duck02(int duckSize){
        System.out.println("Quack!");
        // 把初始化的程序代码放到构造函数中,然后把构造函数设定成需要参数的
        size = duckSize;
        System.out.println("size is "+size);
    }
}
public class UseADuck02 {
    public static void main(String[] args) {
        // 只需要一行就可以创建出新的Duck并且设定好大小
        Duck02 d = new Duck02(43);
    }
}

结果:

Quack!

size is 43

总结:给构造函数加参数,使用参数的值设定size的实例变量。只需要一行就可以创建出新的Duck并且设定好大小。

10、有参和无参构造方法

让用户创建对象的时候有选择。

代码:

/**
 * 重载构造参数
 */
public class Duck03 {
    int size;     // 无参构造方法
    public Duck03(){
        size = 27;
        System.out.println("size is "+ size);
    }     // 有参构造方法
    public Duck03(int duckSize){
        size = duckSize;
        System.out.println("size is "+ size);
    }
}

代码:

public class useADuck03 {
    public static void main(String[] args) {
        // 调用无参构造方法
        Duck03 d1 = new Duck03();
        System.out.println("-------------------");
        // 调用有参构造方法
        Duck03 d2 = new Duck03(45);
    }
}

结果:

size is 27

-------------------

size is 45

11、编译器一定会帮你写出没有参数的构造函数吗?

  • 完全没有设定构造函数:编译器帮你调用一个无参构造函数
  • 写了有参构造函数:自己要写上无参构造函数,编译器不会调用

12、重载构造函数

代码:

/**
 * 重载构造参数:
 * 代表你有一个以上的构造函数且参数都不相同
 * 不能有相同的参数类型和顺序
 */
public class Mushroom {
    //要知道参数多大
    public Mushroom(int size){}
    // 不知道参数多大
    public Mushroom(){}
    // 知道是否有魔力,不知道多大
    public Mushroom(boolean isMagic){}
    // 知道是否有魔力,知道多大
    public Mushroom(boolean isMagic,int size){}
    // 和上面相同,但是参数顺序不同所以过关
    public Mushroom(int size,boolean isMagic){}
}

实例变量的默认值:0/0.0/false;引用变量的默认值:null

13、父类、继承和构造函数的关系

1)  实例变量

继承下来的父类的实例变量也会保存在对象中。

创建某个对象时(new一个对象),对象会取得所有实例变量所需要的空间,包括继承下来的实例变量的空间。

2)  父类的构造函数

创建对象时,所有继承下来的构造函数都会执行。

执行new的指令,会启动构造函数连锁反应。

3)   构造函数链:

Hippo对象IS-A Animal,Animal IS-A Object。如果你要创建出Hippo,也得创建出 Animal 与 Object的部分。

所以构造函数在执行的时候,第一件事情时去执行它的父类的构造函数,这会连锁反应到Object这个类为止。

4)  调用过程举例:

代码:

public class Animal {
    public Animal(){
        System.out.println("Making an Anilmal");
    }
} public class Hippo extends Animal{
    public Hippo(){
        System.out.println("Making a Hippo");
    }
} public class TestHippo {
    public static void main(String[] args) {
        System.out.println("starting...");
        Hippo h = new Hippo();
    }
}

结果:

starting...

Making an Anilmal

Making a Hippo

说明:先调用父类的构造函数,再调用自身的构造函数。

执行过程如下:

5) 如何调用父类的构造函数:super()

唯一方法:super()

含义:调用其父类的无参构造器。

代码举例:

public class Animal {
    public Animal(){
        System.out.println("Making an Anilmal");
    }
} public class Hippo02 extends Animal{
    int size;     public Hippo02(int newSize){
        // 调用父类的构造函数
        super();
        size = newSize;
    }
}

6) 如果没有调用super() 会发生什么?

编译器会帮我们加上super() 的调用

编译器有两种涉入构造函数的方法:

第一种:没有编写构造函数

编译器会加super()及构造函数。

public ClassName(){
    super();
}

第二种:有构造函数但是没有调用super()

编译器会帮你对每个重载版本的构造函数,加上这种调用:super().

编译器帮忙加的一定是没有参数的版本,即使父类有多个重载版本,也只有无参数的版本会被调用到。

7)对super()的调用必须是构造函数的第一个语句。

8)有参数的父类构造函数:怎么传参?

实例变量name私有的,不能被继承。Hippo有getName()方法但是没有name实例变量,所以需要通过Animal维持name实例变量,然后从getName()来返回这个值。

代码:

public abstract class Animal02 {
    // 每个Animal02都有名字
    private String name;     // Hippo03 会继承这个getter
    public String getName(){
        return name;
    }     // 有参数的构造函数,用来设定name
    public Animal02(String theName){
        name = theName;
    }
} public class Hippo03 extends Animal02{
    public Hippo03(String name){
        // 传给Animal的构造函数
        super(name);
    }
} public class makeHippo {
    public static void main(String[] args) {
        Hippo03 h= new Hippo03("Buffy");
        System.out.println(h.getName());
    }
}

结果:

Buffy

总结:

通过super()来引用父类,所以要从这里把name值都传进去,让Animal把它存到私有的name 实例变量中。

13、this() 从构造函数调用另一个重载版的另一个构造函数

  • 使用this() 来从某个构造函数调用同一个类的另外一个构造函数
  • this()只能用在构造函数中,且必须是第一行语句
  • super() 与 this() 不可兼得
  • this() 中的参数,根据需要调用的构造方法决定

举例:

import java.awt.*;

public class Mini extends Car{
    Color color;
    public Mini(){// 无参数的构造函数以默认颜色调用真正的构造函数
        this(Color.RED);
    }     public Mini(Color c){// 真正的构造函数
        super("Mini");
        color = c;
        // 初始化动作
    }     public Mini(int size){// 有问题,不能同时调用super()和this()
        this(Color.RED);
        super(size);
    }
}

14、对象会存活多久?

  • 对象:生命周期看引用到它的“引用”。如果引用还活着,对象也会继续活着;如果引用死了,对象也会跟着“陪葬”
  • 引用变量:

实例变量:寿命和对象相同,对象活着,实例变量也活着。

局部变量:只存活在声明该变量的方法中。

15、局部变量的生命期和作用域

life:只要变量的堆栈块还存在于堆栈中上,局部变量就算活着,活到方法执行完毕。

Scope:局部变量的范围只限于声明它的方法之内。

局部变量在堆栈中,状态保存;

局部变量所在方法在栈顶,才能被使用。

16、引用变量的生命期和作用域

1)变量的生命周期如何影响对象的生命周期

引用活着,对象活着。

当对象的最后一个引用消失,对象就会变成可回收的。

2)释放对象引用的三种方法

Head First Java学习:第九章-构造器和垃圾收集器的更多相关文章

  1. “全栈2019”Java多线程第九章:判断线程是否存活isAlive()详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. “全栈2019”Java异常第九章:throws关键字详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

  3. 从Java虚拟机的内存区域、垃圾收集器及内存分配原则谈Java的内存回收机制

    一.引言: 在Java中我们只需要轻轻地new一下,就可以为实例化一个类,并分配对应的内存空间,而后似乎我们也可以不用去管它,Java自带垃圾回收器,到了对象死亡的时候垃圾回收器就会将死亡对象的内存回 ...

  4. 从.Net到Java学习第九篇——SpringBoot下Thymeleaf

    从.Net到Java学习系列目录 Thymeleaf概述 Thymeleaf 是一个流行的模板引擎,该模板引擎采用java语言开发.模板引擎是一个技术名称,是跨领域平台的概念,在java语言体系下有模 ...

  5. 深入理解Java虚拟机学习笔记(二)-----垃圾收集器与内存分配策略

    写在前面 本节常见面试题: 如何判断对象是否死亡(两种方法). 简单的介绍一下强引用.软引用.弱引用.虚引用(虚引用与软引用和弱引用的区别.使用软引用能带来的好处). 如何判断一个常量是废弃常量 如何 ...

  6. 《深入java虚拟机》读书笔记之垃圾收集器与内存分配策略

    前言 该读书笔记用于记录在学习<深入理解Java虚拟机--JVM高级特性与最佳实践>一书中的一些重要知识点,对其中的部分内容进行归纳,或者是对其中不明白的地方做一些注释.主要是方便之后进行 ...

  7. 《深入理解Java虚拟机》(三)垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 详解 3.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第三章 ,为了整理思路,简单记录一下,方便后期查阅. 3.2 对象已死吗 在垃圾收集器进行回收 ...

  8. 深入理解Java虚拟机之读书笔记二 垃圾收集器

    1.对象已死? a.引用计数算法:缺点是它很难解决对象之间的相互循环引用的问题,Java语言中没有选用它. b.根搜索算法(GC Roots Tracing):通过一系列的名为"GC Roo ...

  9. JVM学习(一)、垃圾收集器简介

    一.垃圾收集算法 (1)标记-清除算法:最基础的收集算法“标记--清除”(Mark-sweep)算法,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的 ...

  10. [JVM-3]Java垃圾回收(GC)机制和垃圾收集器选择

    哪些内存需要回收? 1.引用计数法 这个算法的实现是,给对象中添加一个引用计数器,每当一个地方引用这个对象时,计数器值+1:当引用失效时,计数器值-1.任何时刻计数值为0的对象就是不可能再被使用的.这 ...

随机推荐

  1. 前端Vue仿企查查 天眼查知识产权标准信息列表组件

    ​ 引入Vue仿企查查天眼查知识产权标准信息列表组件 随着技术的不断发展,传统的开发方式使得系统的复杂度越来越高.在传统开发过程中,一个小小的改动或者一个小功能的增加可能会导致整体逻辑的修改,造成牵一 ...

  2. CodeForces 1367F2 Flying Sort (Hard Version)

    题意 给一个长度为\(n\)的数组,你可以有两种操作 将某一个数放置在数组开头 将某一个数放置在数组结尾 问最小操作多少次可以得到一个非递减数列 (比\(F1\)难在\(n\)变大,且数组中元素可以有 ...

  3. RK3568开发笔记(七):在宿主机ubuntu上搭建Qt交叉编译开发环境,编译一个Demo,目标板运行Demo测试

    前言   在之前的博文中已经搭建好了一个比较完善的ubuntu宿主机,都很完善了但是发现没有Qt交叉编译开发环境,所以还需要搭建一套Qt交叉编译开发环境.   补充说明   本篇是基于<RK35 ...

  4. Java实践项目 - 商品分类

    Smiling & Weeping ---- 好想回到那个拉钩许诺的年代 1.1商品分类的思路:一次性查询三级分类 (一级为美味麒麟榜,二级为闭眼入,第三级为商品) 优点:只需要一次查询,根据 ...

  5. LeetCode155:最小栈,最简单的中等难度题,时间击败100%,内存也低于官方

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 最近运气不错,在LeetCode上白捡一道送 ...

  6. 鞭尸没 jj

      提前退役了.现在我想说一点无关紧要的闲话.   与其说是 OI 回忆录,不如说是对这主线明确的六年做的一个梳理,倒不一定 OI 强相关. 壹.零度下的相遇 视线就这样交叠 与你   最初接触到 O ...

  7. selenium库浅析

    selenium库浅析 基于4.3 pip install selenium安装好后,在sitepackages下 2个主要的目录,common和webdriver 1- common 该目录一共就一 ...

  8. 新零售SaaS架构:面向中小连锁的SaaS系统整体规划

    零售企业的发展路径 零售企业的发展路径一般可分为以下几个阶段: 单店经营阶段:企业在一个地区或城市开设单个门店.这时,企业需要把精力放在了解当地市场和顾客需求上,这是积累经验和品牌知名度的重要环节.为 ...

  9. DBConvertStudio使用记录

    DBConvertStudio使用记录 前言 这篇文章是我在学习使用DBConvertStudio过程中的学习记录,以便日后遗忘查阅: 诸君也可跟随我的步伐了解一下DBConvertStudio 声明 ...

  10. MySQL——MySQL面试题

    文章目录 数据库基础知识 为什么要使用数据库 什么是SQL? 什么是MySQL? 数据库三大范式是什么 mysql有关权限的表都有哪几个 MySQL的binlog有有几种录入格式?分别有什么区别? 数 ...