Java学习笔记 - Chapter 5

本文深入探讨了Java中的构造函数,包括构造函数的定义、重载、权限及在继承中的应用。讲解了类的生存空间、生存期和作用域,以及静态变量和静态方法的概念和特性。此外,还介绍了静态final变量作为常量的使用以及静态方法的调用规则。
摘要由CSDN通过智能技术生成

写在前面:

该学习笔记基于《Head First Java》一书,仅供个人参考备忘使用,可能会存在诸多问题,也会随着学习的推进不断修改,因此请酌情将其当作参考。

目录

类的构造函数

构造函数的重载:

private的构造函数?

父类与子类间的构造函数链

调用毫无痕迹?

父类的构造函数要传参?

想要一劳永逸?

生存空间、生存期和作用域

生存空间

生存期

作用域

静态

静态实例变量(以下简称静态变量)

每个类共用一个?

特殊的静态变量——静态final变量

静态方法


类的构造函数

之前我们看到的类大多是这个样子的:

class Animal {
    private String name;
    
    public void setName(String newName) {
        name = newName;
    }
}

其中有实例变量,也有方法,但并没有提到“构造函数”这一概念。

再看下面这条创建对象的语句:

Animal creature = new Animal();

在该行语句中出现了Animal()这样一种表述,会让人理所当然地认为是在调用一个方法。实际上,这种“类名()”形式即为类的构造函数

类的构造函数形式框架如下:

class className {
    public className(paramters) {
        statements;    
    }
}

构造函数有两个特征:

1.构造函数必须与方法同名

2.构造函数一定不能有返回值。

 构造函数的主要用途是为某些实例变量赋上初值。通过指定构造函数的参数列表,可以使得用户在使用构造函数创建对象时,就可以为对象的某些属性指定想要的值。

构造函数的重载:

之前在谈函数重载时,曾经说过重载的一个主要用途即是对构造函数的重载。为类设置多个具有不同参数列表的构造函数,可以确保用户在不同的初始条件下都能够通过构造函数来创建对象(例如当创建一个代表Car的对象时,可能并不知道车辆的行驶里程等详细信息,但由于Car类拥有形式丰富的构造函数,也许可以仅提供车辆的品牌即可创建一个Car对象)。

因此,在加入了重载之后,一个类的构造函数便有三个特征:

1.构造函数必须与方法同名;

2.构造函数一定不能有返回值;

3.不同的构造函数,参数列表必须不同

private的构造函数?

构造函数并不一定要是public的,有时根据一些特殊的需求,可以将一部分构造函数设置成private,这样的目的是组织该类的对象实例被创建(在之后的静态方法部分会详细说明)。

父类与子类间的构造函数链

在使用关键字new创建对象时,若类之间存在继承关系,则构造函数的执行顺序会像下面这张图一样:

 当创建子类(class3)时,会首先调用其构造函数class3(),但在class3的构造函数本身正式开始之前,首先会调用一次父类(class2)的构造函数,以继承class2中所有的内容。class2的构造函数又会调用上一级父类(class1)的构造函数,构造函数的逐级调用就形成了父类与子类之间的“构造函数链”

从图上也可以看出,最先执行的是最原始、级别最高的父类的构造函数。由于所有类的源头都是Object类,因此最终会先执行Object类的构造函数。

构造函数链的原则:

有父类才会有子类!

调用毫无痕迹?

也可以让调用变得有痕迹可循。在构造函数的最开头添加上super()方法

super();

就可以执行父类的构造函数。千万记得在开头!不然会编译报错!

当然,如果懒得写的话,Java也会自动在每个构造函数的开头添加一句super();语句。

父类的构造函数要传参?

如果父类的构造函数需要参数,就直接在super()方法的参数列表中填写即可:

super(parameters);

super()就可以直接看作是父类构造函数在子类中的另一种表示形式。

想要一劳永逸?

如果不想在每个构造函数中都添加super()方法来体现对父类构造函数的调用(尤其是当所需要调用的父类构造函数参数很复杂时),可以仅用一个比较完善的子类构造函数来调用super(),而其他的构造函数只需要在最开头使用this()来调用这个子类构造函数:

this(parameters);

this()中的参数列表和对应的子类构造函数定义的参数列表保持一致。

生存空间、生存期和作用域

生存空间

Java中内存有两大区域——堆和栈。不同的东西会有不同的生存空间,对象的生存空间,而则是局部变量方法调用的生存空间。

还记得之前区分局部变量和对象的实例变量的方法吗?

变量类型声明位置生存空间
局部变量声明在类的方法中
实例变量生命在类中、方法外(类的属性)

在两类生存空间中,行为也是不同的。

栈的行为比较特殊,方法的调用主要在栈中执行,而栈中的所有方法都以堆栈块的形式存在:

被调用的方法会从栈顶进入,随着方法的逐级调用,栈会越来越高;最终结束调用返回也是从栈顶开始,因此后被调用的方法会先返回

堆中的对象存放则是随机(?可能)的,并没有先后顺序关系。堆区虽然不像栈一样,当方法调用完,堆栈块便被从栈中移除,但堆也存在“垃圾回收机制”,即不再被程序引用的对象会被自动清理

一个原则:

堆区的内存必须通过new关键字进行分配。

生存期

不管是变量还是对象,都是有一定生存期的,超过了生存期,变量和对象便不复存在。由于Java中对象的生存期直接取决于引用它的引用变量的生存期,因此讨论生存期,主要是讨论变量的生存期。

变量生存期的判断规则——只要声明变量的堆栈块还存在于栈上,该变量即仍存活。(这里的变量可以理解为局部变量)

对于对象内部的实例变量,生存期自然与对象保持一致。因此,只要当前栈上还存在着引用该对象的引用变量,那么该对象及其所包含的实例变量即是存活的。

作用域

局部变量的作用域仅限于声明该变量的代码块内,反映到程序运行过程中,也就是该局部变量所属的堆栈块。当方法调用完毕返回之后,当前有效的作用域也随之改变,在已结束调用的方法内声明的所有局部变量都不再有效。

而对象则难以定义作用域一说。因为对象是通过引用变量发挥作用的,当引用变量所对应的作用域还在工作时,就可以认为对象是在“起作用”的。当然,当所有引用变量都不存在时,对象自然也就失去了作用,不过此时对象也要面临垃圾回收了。

静态

静态对于面向过程的语言来说,可能与全局的意思有些相似。但在面向对象的语言中,“静态”意味着与实例无关

静态实例变量(以下简称静态变量)

类中定义静态变量的格式如下:

public static type name = xxx;

在权限范围声明public/private和类型名type之间加上关键字static,即定义了一个静态变量。

静态变量的特点:

1.每个类共用一个静态变量

2.静态变量会在类的任何对象被创建之前、任何静态方法被执行之前就被初始化,可以理解为在程序正式开始执行之前就被初始化

 另外,由于静态变量仍属于实例变量,因此实例变量具有的默认初始值的特点,静态变量同样具有。

每个类共用一个?

类的静态变量对于以该类为模板创建的所有对象都是同步的,也就是说,当任何一个对象通过某种方法改变了静态变量的值时,所有对象的这一变量均会发生改变。而普通的实例变量,则是每个对象自己单独拥有一个,相互之间一般影响。

实例变量静态变量
每个实例(对象)拥有一个每个类拥有一个

这种设定很大程度上方便了某些工作,比如统计从程序开始运行到现在某个类一共创建过多少个对象。

特殊的静态变量——静态final变量

之前在讲“常量”的时候提到过,Java中最接近于常量的,就是静态final变量。静态final变量的声明格式如下:

public static final type NAME = xxx;

一般静态final变量的名称会采用全部大写的方式,这种方式在其他语言中一般就是常量的表示方式,所以说Java中的静态final变量有时也可以看作就是常量。

让我们再次回顾一下final关键字的用法:

final type 变量  →  变量的值在之后不可以修改

final type 方法()  →  类的方法在子类中不可以被覆写

final class  →  类不可以被继承

正因为不可被修改, 静态final变量必须要赋上初始值,否则声明这样的变量就毫无意义了。编译器也会检查所有的静态final变量是否都进行了初始化。

不过,在初始化的时候有一个小技巧,可以采用声明与初始化相分离的方式来对静态final变量进行定义:

/*声明与初始化同时进行*/
public static final NAME = xxx;

/*声明与初始化分开进行*/
public static final NAME;
static {
    NAME = XXX;
}

下面的那种方式,将声明语句和初始化语句放在了不同位置,初始化语句全部用一个以static开头的花括号装起来了

静态方法

与静态变量相似,静态方法的调用也不涉及类的具体实例。静态方法的声明如下:

public static type method() {

}

由于静态方法不涉及具体实例,因此调用静态方法的方式也比较特殊:

class className {
    public static type method() {

    }
}

className.method()

可以看到,静态方法的调用,是通过类名.方法名来实现的。而普通的方法,则是以对象实例.方法名的方式来实现的。

如果某个类只包含静态方法,就意味着这个类的实例没有必要被创建。因此可以将构造函数设置为private,以防止在程序中创建该类的任何实例。

静态方法使用的注意事项:

静态方法中不能调用非静态变量和非静态方法!

 因为静态的本质是在类的任何对象实例被创建之前即可执行,但非静态的变量或方法,则需要有具体的对象实例之后才能够产生和调用。因此如果在静态方法中调用了这两类非静态的东西,则会引发错误。


To be continued……

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值