Java中关于this、super、static关键字的理解

文章目录

之所以将static与this和super放一起,是想从对象和类的角度去理解,同时也加深对类和对象的理解。

this

如果有两个同类型的对象,分别叫作a 和b,那么您也许不知道如何为这两个对象同时调用一个 f()方法:

public class Banana {
	void f(int i) {
	}
}

Banana a = new Banana(), b = new Banana();
a.f(1);
b.f(2);

若只有一个名叫f()的方法,它怎样才能知道自己是为 a 还是为 b 调用的呢?
为了能用简便的、面向对象的语法来书写代码——亦即“将消息发给对象”,编译器为我们完成了一些幕后工作。其中的秘密就是第一个自变量传递给方法f(),而且那个自变量是准备操作的那个对象的句柄。所以前述的两个方法调用就变成了下面这样的形式:

Banana.f(a,1);
Banana.f(b,2);

这是内部的表达形式,我们并不能这样书写表达式,并试图让编译器接受它。但是,通过它可理解幕后到底发生了什么事情。

this关键字是当前对象的一个引用,也就是说,能用this调用的方法或属性,肯定不能有static修饰符的,因为static修饰的肯定是属于类属性或方法,两者的存储位置都不在同一内存中。
假定我们在一个方法的内部,并希望获得当前对象的句柄。由于那个句柄是由编译器“秘密”传递的,所以没有标识符可用。这个时候就可以用this关键字,this 关键字(注意只能在非静态方法内部使用)可为已调用了其方法的那个对象生成相应的句柄。可象对待其他任何对象句柄一样对待这个句柄。很多时候,你不用this编译器也能明白,但也有特殊情况,比如需要返回当前对象的时候。
有以下可能会用到this:

  1. 当在一个非静态的方法中想要使用该方法所处类的非静态属性或方法时,可以使用this调用,也可以不写,编译器会默认调用当前对象。
  2. 当在某个非静态方法中想要使用当前对象或者返回当前对象时,使用this。
  3. 在构造函数内部使用this调用另一个构造函数,以此来减少代码量,注意调用的代码应放在构造函数的第一行。这里注意既然只能放在第一行,那就说明在一个构造函数内部,通过this调用另一个构造函数时,只能调用一个,反应过来了吗?如果调用两个,怎么保证this调用的构造函数放在第一行。
  4. 当构造函数的形参变量和类属性变量名相同时,要在类属性变量名前加个this,加以区分,否则编译器会发出警告。

super

super关键字是父类对象的一个引用,只有在继承关系中,才会使用这个关键字,下面对这种使用做个总结:

  1. 子类继承父类的属性和方法,并重写父类的方法,子类修饰符不能比父类方法的修饰符的范围狭窄。 如果你用子类引用,子类实例,范围小不会影响。如果你用父类引用,子类实例,范围小的话会和多态的冲突,所以是行不通的。

  2. 子类继承父类,子类必须在构造函数中的第一行使用super()来调用父类的构造函数,虽然你有时候没有写也能编译通过,那是系统默认调用了super。
    关于super的调用,有以下说明:

    • 父类没有写构造函数(系统默认有一个无参构造函数),或者父类只有一个无参构造函数,则:子类可以不写构造函数(可以理解为:子类的系统默认构造函数,默认调用了super();),也可以写有无参或者有参构造函数,而且不用调用super,系统会默认调用。
    • 如果父类有有参构造函数,没有无参构造函数,则:子类不能有无参构造函数,可以有有参构造函数且必须在有参构造函数中显示的调用父类的有参构造函数,即super(参数名)。
    • 如果父类既有有参构造函数,又有无参构造函数,则:子类可以有有参也可以有无参,且可以使用super调用父类的有参无参构函数,也可以不调用super,使用this.属性 = 形参,的方式去实现有参构造函数。
    • 如果父类的构造函数只有一个,且修饰符是private,则:不可以被继承。

static

理解了 this 关键字后,我们可更完整地理解 static(静态)方法的含义。

通常,我们创建类时会指出那个类的对象的外观与行为。除非用new 创建那个类的一个对象,否则实际上并未得到任何东西。只有执行了 new 后,才会正式生成数据存储空间,并可使用相应的方法。
但在两种特殊的情形下,上述方法并不堪用:

  • 一种情形是只想用一个存储区域来保存一个特定的数据——无论要创建多少个对象,甚至根本不创建对象。这个有点像C语言中的全局变量,任何类都可以引用,而且引用的是同一内存下面的东西。
  • 另一种情形是我们需要一个特殊的方法,它没有与这个类的任何对象关联。也就是说,即使没有创建对象,也需要一个能调用的方法。

为满足这两方面的要求,可使用static(静态)关键字。一旦将什么东西设为static,数据或方法就不会同那个类的任何对象实例联系到一起。所以尽管从未创建那个类的一个对象,但仍能调用一个 static 方法,或访问一些 static 数据。

而在这之前,对于非 static 数据和方法,我们必须创建一个对象,并用那个对象访问数据或方法。这是由于非static 数据和方法必须知道它们操作的具体对象。当然,在正式使用前,由于static 方法不需要创建任何对象,所以它们(静态)不可简单地调用其他(非静态)那些成员,同时不引用一个已命名的对象,即不能直接访问非 static 成员或方法(因为非static 成员和方法必须同一个特定的对象关联到一起)。

例如,下述代码能生成一个 static数据成员,并对其初始化:

class StaticTest {
	Static int i = 47;
}

现在,尽管我们制作了两个StaticTest 对象,但它们仍然只占据StaticTest.i 的一个存储空间。这两个对象都共享同样的i。请考察下述代码:

StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();

此时,无论 st1.i 还是 st2.i 都有同样的值 47,因为它们引用的是同样的内存区域。

有两个办法可引用一个 static 变量。

  • 正如上面展示的那样,可通过一个对象命名它,如st2.i
  • 亦可直接用它的类名引用,如StaticTest .i,而这在非静态成员里是行不通的(最好用这个办法引用static 变量,因为它强调了那个变量的“静态”本质)。

类似的逻辑也适用于静态方法。既可象对其他任何方法那样通过一个对象引用静态方法,亦可用特殊的语法
格式“类名.方法()”加以引用。静态方法的定义是类似的:

class StaticFun {
	static void incr() {
		StaticTest.i++; //此时,无论 st1.i 还是st2.i 的值都是48。
	}
}

从中可看出,StaticFun 的方法 incr()使静态数据 i 增值。通过对象,可用典型的方法调用incr()

StaticFun sf = new StaticFun();
sf.incr();

或者,由于 incr()是一种静态方法,所以可通过它的类直接调用:

StaticFun.incr();

尽管是“静态”的,但只要应用于一个数据成员,就会明确改变数据的创建方式(一个类一个成员,以及每个对象一个非静态成员)。若应用于一个方法,就没有那么戏剧化了。

对方法来说,static 一项重要的用途就是帮助我们在不必创建对象的前提下调用那个方法。这一点是至关重要的——特别是在定义程序运行入口方法 main()的时候。和其他任何方法一样,static 方法也能创建自己类型的命名对象。所以经常把 static 方法作为一个“领头羊”使用,用它生成一系列自己类型的“实例”。

有些人抱怨 static 方法并不是“面向对象”的,因为它们具有全局函数的某些特点;利用 static 方法,我
们不必向对象发送一条消息,因为不存在 this。这可能是一个清楚的自变量,若您发现自己使用了大量静态
方法,就应重新思考自己的策略。然而,static 的概念是非常实用的,许多时候都需要用到它。

【注】:上面的很多内容都是出自《Java编程思想》第四版,大家都说这一本书写的很好,我是第一次看这本书,也是刚看,所以感受不是很深,但把《Java核心技术 卷一》看了好几遍了,感觉有些东西还是没有讲到我想要的深度。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yelvens

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值