李晓晗的博客 Java Coder

headfirstjava第九章笔记

2019-08-13
lxh

head first Java第九章

栈与堆

对象的生存空间堆(heap)
方法调用及局部变量的生存空间栈(stack)
实例变量

实例变量是被声明在类而不是方法里面。它们代表每个独立对象的“字段”(每个实例都能有不同的值)。实例变量存在于所属的对象中。在堆上!

public class Duck{
    int size;//每个Duck对象都会有独立的size
}
局部变量

局部变量又被成为栈变量,局部变量和方法的参数都是被声明在方法中。它们是暂时的,且生命周期只限于方法被放在栈上的这段期间(也就是方法调用至执行完毕为止)。在栈上!

public void foo(int x){
    int i = x + 3;
    boolean b = true;
    //参数x和变量i,b都是局部变量
}

当你调用一个方法时,该方法会放在调用栈的栈顶。实际被堆上栈的是栈堆块,它带有方法的状态,包括执行到哪一行程序以及所有的局部变量的值。

举个例子,有三个方法ABC,其中方法A中调用了方法B,方法B中调用了方法C。当你调用方法A时,方法A就会放在栈顶,最上面,开始执行,执行中又调用了方法B,方法B就会骑在方法A的头上,放到栈顶,根据栈先进后出的原则,会开始执行方法B,执行中又调用了方法C,方法C就会骑到方法B头上,放到栈顶,开始执行,当方法C执行完后,方法C的堆栈块就会被释放,栈顶又成了方法B,继续执行方法B的剩余内容,执行完后方法

栈示例

如果局部变量是个对该对象的引用,只有变量本身会放在栈上。对象本身会放在堆上。
实例变量在堆上

创建一个新的对象需要在堆上分配空间,空间的大小是足以存放该对象的所有实例变量的空间。如果实例变量是primitive主数据类型,就会根据primitive主数据类型的大小分配空间,int就分配32位,long就分配64位,不管值有多大。如果实例变量是对象的话,只会保存改对象的引用所用到的空间,而不会保存一整个对象空间,这个对象具体有没有被分配堆的空间,要看它有没有被new出来。


构造函数

Duck myDuck = new Duck();//看起来很像调用了Duck方法,其实这不是个方法
这是Duck的构造函数

这个东西看起来很像方法,但不是方法,它是带有new的时候就会执行的程序代码。换句话说,这段程序代码会在你初始化一个对象的时候执行。就选你没有写构造函数,编译器也会帮你写一个。

唯一能够调用构造函数的办法就是新建一个对象(严格的说是在构造函数之外能够调用构造函数的办法。)

public class Duck{
    public Duck(){//自定义的构造函数,名字必须和类名一样。
        System.out.println("Quack");
    }
}
public class UseADuck{
    public static void main(String[] args){
        Duck d = new Duck();//这样就会启动Duck的构造函数,控制台输出Quack
    }
}

类可以有和类名重名的方法,它和构造函数的区别在于有没有返回类型。构造方法也可以传参

当你写了带有参数的构造方法时,编译器不会帮你再写一个没有参数的构造方法,编译器只会在你没有写构造方法时帮你写构造方法!
如果有一个以上的构造函数,则参数一定要不一样。
重载构造函数的意思代表你有一个以上的构造函数且参数都不相同

参数不相同在于参数的个数、类型、顺序的不同,不能写出相同参数的构造函数。

在创建新对象时,所有继承下来的构造函数都会执行。
抽象类也有构造函数,虽然你不能对抽象类执行new操作,它的构造函数会在具体子类创建实例时执行。

在构造函数中用super调用父类的构造函数。要记得子类可能会根据父类的状态来继承方法(也就是父类的实例变量)。完整的对象需要也是完整的父类核心,所以这就是为什么父类构造函数必须执行的原因。构造函数在执行的时候,第一件事是去执行它的父类的构造函数。调用的顺序类似于方法的调用,先执行栈顶。

在子类的构造函数中要用super()来调用父类的构造方法。

如果你没有写构造函数,编译器会自动给你写上构造函数并顺便调用父类的构造函数,如果你写了但是没有调用父类的构造函数,编译器也会自动给你加上调用父类的构造函数,如果你写了很多重载的构造函数,编译器会自动把你的每个构造函数都加上调用父类的构造函数。

子类的构造函数会先调用,但是父类的构造函数会先完成!当你想要孙子时,必须先把儿子生了,你自己也得被生了。

构造函数的第一个语句必须是super(),你必须先有了你爹,在考虑构造你自己的事。

通过super()有参的父类构造函数可以初始化父类的私有实例变量。

如果你有几个除了参数不同方法都一样的构造函数,你想有更多重用代码,可以使用this()在构造函数中调用自己的构造函数,但是调用this()了就不能再调用super()了,因为this()作为本身的构造函数已经包括调用super()了。

生命周期

  • 对象的生命周期要看引用变量的生命周期而定
  • 局部变量只会存活在声明该变量的方法中
  • 实例变量的寿命与对象相同

只要声明变量的堆栈块还在栈里,该变量就存活着,但是只能在范围内使用,范围就是该堆栈块在栈顶的时候。

只要有活着的引用,对象也就会活着。如果某个对象的引用已经不在它的范围中,但此引用还是活着的,则此对象就会继续活在堆上。如果对对象的最后一个引用死了,对象就会从堆中被踢开。引用变量会跟堆栈块一起解散,因此被踢开的对象也就正式的声明出局。你无法取得对象的引用,则此对象只是浪费空间罢了,这就是满足回收条件的对象。如果程序内存不足,垃圾收集器(GC)就会去歼灭部分或者全部可回收的对象。

有三种方法可以释放对象的引用
  1. 永久性的离开它的范围

在某个方法中引用的对象,方法执行完毕后,该引用就去世了,对象没有引用了就等着被回收。

  1. 引用赋值到其他对象上
  2. 直接将引用设定为null

null

当你把引用设定为null,等于抹除了这个引用作为遥控器的功能。null是代表“空”的字节组合。不能使用遥控器按钮(用.来调用方法),否则或产生空指针异常。

李晓晗

更新于2019-8-13 下午


Similar Posts

Comments