第九章:构造器与垃圾收集器-对象的前世今生

该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!

第九章:构造器与垃圾收集器-对象的前世今生


对象有生有死:

    必须对对象的生命周期负责;
    何时创建、何时销毁(不是消灭、只是放弃),由垃圾收集器(GC)将它蒸发掉,回收所占空间;
本节述及堆、栈、范围、构造器、超级构造器、空引用等内容;

Java中重要的两种内存区域:

    对象的生存空间堆(heap);
    方法调用及变量的生存空间(stack);
当JVM启动时,他会从底层的操作系统取得一块内存,并以此区段来执行Java程序;
 
所有的对象都存活在可垃圾回收的堆上;
局部变量又叫栈变量;

实例变量和局部变量:

    实例变量是被声明在类而不是方法里面;代表每个独立对象的’字段’;
    实例变量存在于所属的对象中;
    
    局部变量和方法的参数都是被声明在方法中;他们是暂时的,且生命周期只限于方法被放在栈上的这段期间;

调用方法是一个压栈的过程,方法执行完之后,弹出栈;
    实际压栈的是堆栈块,它带有方法的状态,包括执行到哪一行程序以及所有的局部变量;

非primitive的局部变量只是保存对象的引用而已,存在栈上;对象本身存在堆上;
认识堆栈,对理解变量的有效范围(scope)、对象的建立、内存管理、线程(thread)和异常处理很有用;

要点:

1.我们关系栈和堆两种内存空间;
2.实例变量声明在类中方法之外的地方;
3.局部变量声明在方法或方法的参数上;
4.所有的局部变量都存在栈上相对应的堆栈块中;
5.对象引用和primitive主数据类型的局部变量都是放在栈上的;
6.不管是实例变量还是局部变量,对象本身都会在堆上;

实例变量存在于对象所属的堆空间上:

    primitive主数据类型的实例变量,Java会依据其大小留下空间;
    对于引用类型的实例变量,Java会留下引用所需的空间;
    
实例变量所需的空间是在对象上;
若实例变量引用类型只声明未赋值,则存对象的堆空间中只会留下引用变量的空间;直到被赋值,该引用变量所指向的内存中在会被赋值的对象占用;

构造对象的过程:

    new Dog();

其实是在调用Dog的构造函数:
    构造函数像方法,但不是方法;
    它带有new的时候会执行的程序代码;(初始化时会执行)
   
唯一能够调用构造函数的办法就是新建一个类;(严格说来,是唯一在构造函数之外能够调用构造函数的方式)
当没有自己写构造函数时,编译器会帮你写一个;

构造函数:

    没有返回类型;
    函数名与类名同;

构造Dog:
    构造函数的一项关键特征是它会在对象能够被赋值给引用之前就执行,这代表你可以有机会在对象被使用之前介入;

新建Dog状态的初始化:
    大部分人都使用构造函数来初始化对象的状态,即给对象的实例变量赋值;
    使用构造函数来初始化Dog状态,最好方式是:把初始化的程序代码放在构造函数中,并把构造函数设置成需要参数的;

构造函数不会被继承!

有了需要参数的构造函数,还要注意——最好要有不需要参数的构造函数(但也确实存在没有无参数的构造函数的类);
    编译器会在你没有设定构造函数时帮你写出一个没有参数的构造函数;

如果一个类有一个以上的构造函数,这代表他们是重载的,他们的参数也一定不一样(和方法重载是相同的);

要点:

1)实例变量保存在所属的对象中,位于堆上;
2)如果实例变量是一个对对象的引用,则引用和对象都是在堆上的;
3)构造函数会在新建对象的时候执行程序代码;
4)构造函数必须与类名同名且没有返回类型;
5)你可以用构造函数来初始化被创建对象的状态;
6)若没写出构造函数,编译器会帮你创建一个;
7)默认的构造函数是没有参数的;
8)如果你写了构造函数,编译器就不会调用;
9)最好有无参数的构造函数让人可以选择使用默认值;
10)重载的构造函数是指多个构造函数之间的关系;
11)两个构造函数的参数必须不同;

构造函数可以是共有的、不指定的、私有的;
    私有的构造函数不是不能存取,它代表该类以外不能存取;

父类的构造函数:

    在创建新对象时,所有继承下来的构造函数都会执行;
    new的时候,会启动构造函数连锁反应——构造函数链(Construction Chaining);(就算是抽象类也有构造函数,虽然抽象类不能new,但会在子类创建出实例时执行)
    使用super可以调用父类的构造函数;

构造函数执行的时候,第一件事就是去执行父类的构造函数;这回连锁反映到Object这个类为止;
构造函数连反应的过程和方法调用类似,也是一个不断压栈、退栈的过程;

示例:(Code-AnimalDrive.java)
public class Animal{
    private String name;
    private int age;
    
    public void setName(String aName){
        name = aName;
    }
    public String getName(){
        return name;
    }
    public void setAge(int aAge){
        age = aAge;
    }
    public int getAge(){
        return age;
    }
    public Animal(){
        name = "def";
        age = 1;
        System.out.println("initial animal");

    }
    public Animal(String aName,int aAge){
        name = aName;
        age = aAge;
        System.out.println("initial animal with paras");
    }
}
public class Dog extends Animal{
    public Dog(){
        super("haha",5);
        System.out.println("initial dog");
    }
    public void eat(){
        System.out.println("dog eat");
    }
}
public class AnimalDrive{
    public static void main(String[] args){
        Animal ani = new Animal();
        
        Dog dog = new Dog();
        dog.eat();
        
        System.out.println(dog.getName());
        System.out.println(dog.getAge());
    }
}
这个实例也展示了如何调用父类的构造函数;

调用父类构造函数的唯一方法就是调用super();

    在你的构造函数中调用super()会把父类的构造函数放在堆栈的最上方;
    如果我们没有调用super(),编译器会帮我们加上super()的调用;(这个可以修改示例代码验证下)
    这里需要注意的是,编译器帮忙加的一定是没有参数的版本,假如父类有多个重载版本的构造函数,也只有无参数的这个版本会被调用到;
    如果没有这个版本,那么就需要主动调用下其他版本,这也是建议提供无参数构造函数的原因;

每个子类的构造函数会立即调用父类的构造函数;对super()的调用必须是构造函数的第一个语句;
super(para)可以接收参数,对应父类有参数的构造函数的调用;(仔细看先上边的示例,展示了子类如何通过super(name,age)来设置父类的私有变量)

从某个构造函数调用重载版的另一个构造函数:

    需要使用this,它表示对对象本身的引用;
    this()只能用在构造函数中,且必须是第一行语句;
    每个构造函数可以选择调用super()或this(),但不能同时使用;
即:
1)使用this()来从某个构造函数调用同一个类的另外一个构造函数;
2)this()只能用在构造函数中,且必须是第一行语句;
3)super()与this()不能兼得;

对象会存活多久——对象生命周期要看引用变量的生命周期而定,那变量到底会活多久?
1)局部变量只会存活在声明该变量的方法中;
2)实例变量的寿命和对象相同;

life和scope:

    lift:只要变量的堆栈块还在堆栈上,局部变量就还活着;
    scope:局部变量的范围只限于声明它的方法之内;(方法中再调用其他方法,变量还活着,但范围会变化到新的方法中,知道新的方法完毕,范围再跟着回来)
       
也就是说:局部变量只能在声明它的方法执行中才能被使用;

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

有三种方式可以释放对象的引用:

1)引用永久性的离开范围;
    void go(){ Dog dog = new Dog(); }
2)引用被赋值到其他的对象上;
3)直接将引用设定为null;

null的真相:

    null是代表“空”的字节组合(只有JVM才知道是什么);
    不能对null引用进行操作(方法调用等);对null引用使用圆点运算符会在执行器遇到NullPointerException这样的错误;
    当变量被置为null时,就只好等着被垃圾回收,原来的地方也会让出来给别人用;


    

猜你喜欢

转载自blog.csdn.net/baby_hua/article/details/79272540
今日推荐