关于this关键字

    5.4介绍的是this关键字。关于this,在此之前只是一走一过,没有深挖过,这两天忙完公众号支付,翻到这里,觉得这部分的介绍收获不少,现将阅读时的思考和理解记录下来。
    书中用一个有趣的思想引入关键字this,这点是我从没想过的,老样子,只写关键代码:

Banana a = new Banana(), 
       b = new Banana();  
    (多嘴一句,以前经常 int i = 1, j = 2 ; 这样写。虽然无伤大雅,但是现在看来,很多科班的东西,确实值得野路子好好思考)
    a.peel(1);
    b.peel(2);

    那么问题来了:编译器如何知道是哪个对象调用的peel()方法呢? 在此之前我真不知道… 书中给出的解释是:

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

    编译器暗自将“所操作对象的引用”作为方法的第一个参数进行传递,形成了这样一种类似于静态方法调用的内部表现形式。
    由此,引入了指代当前对象引用的this关键字。
    书中主要介绍了this的四种应用场景,现按照书中序,归纳解释如下:
    0.只在必要处使用this。这条不计数,因为讲道理,应该没谁会在一个方法中调用本类另一方法时多此一举使用this来调用,执意如此,必是真爱
    1 . 返回当前对象的引用。这条很实用,借书中例:

Leaf increment(){
    i++;
    return this;
}

new Leaf().increment().increment().increment();

    一句话:链式编程的福音!我个人很喜欢链式编程,也很喜欢用匿名对象,很多时候,明明匿名对象能满足的单次调用,非要费尽心思想个名字,实在浪费时间。(于我,会对每个声明的变量负责。如果你刚入行,希望你也能这样要求自己,真的没有坏处。老鸟应该不会点开这篇文章,当然,就算看到应该也不会改变他们无所谓的态度。噢,可以尝试Alt+Ctrl+L,第一次见到这个重构快捷键的效果,我是被惊艳到的。)性能上,省略一个没有必要的栈中空间,虽然提升微乎其微,但至少是一种态度。同样的,明明一行链式编程能实现的逻辑,非要拆分成一整段?别人会一行一行读?还是不加注释强迫别人一行一行读?一行链式编程,一个阐述这一串代码逻辑的简要注释,足矣。我一激动就容易话多…

    2 . 将当前对象传递给其他方法。对于此种应用我想补充我自己的想法,现将书中例完整引用(一般我不这样做),仅调整代码顺序,这样你可以自上而下只读一遍,不用读到最后再回头找…:

public class PassingThis{
    public static void main(String[] args){
        new Person().eat(new Apple());
    }
}
class Person{
    public void eat(Apple apple){
        Apple peeled = apple.getPeeled();
        System.out.println("Yummy");  //每日一词:yummy 好吃的。讲道理不削皮更有营养,当然,洗干净...
    }
}
class Apple{
    Apple getPeeled(){
        return Peeler.peel(this);
    }
}
class Peeler{
    static Apple peel(Apple apple){
        //... remove peel 削皮
        return apple;
    }
}
/* Output: Yummy */

    书中的写法如上。我个人的想法啊,这个例子有点为了用this而用this之嫌,完全可以这样:

public class PassingThis{
    public static void main(String[] args){
        new Person().eat(new Apple());
    }
}
class Person{
    public void eat(Apple apple){
        Apple peeled = Peeler.peel(apple);
        System.out.println("Yummy"); 
    }
}
class Apple{
}
class Peeler{
    static Apple peel(Apple apple){
        //... remove peel 削皮
        return apple;
    }
}
/* Output: Yummy */

    eat()方法直接调用工具类Peeler的静态方法peel(),传递apple对象即可,干掉Apple类中的getPeeled()方法。
这是我的第一感觉。
    转念一想,如果Apple类中集中了大量的操作苹果的方法,那么,书中的写法应该是更便于管理的。
    此外,书中例子下的解释提到“Apple需要调用外部工具类Peeler的peel()方法,这个方法由于某种原因必须放在Apple类外部,也许是因为该外部方法要应用于许多不同的类,而你不想重复”。可能是我的理解有障碍,也可能是翻译的失了真,这段解释我来来回回琢磨了好一阵。我的理解是:这个Peeler.peel()方法不仅能削苹果,还能削梨,还能“削”你,所以放在Apple类中不合适,所以例中使用this来指代当前调用方法的对象。但是这个“不重复”我没有理解,如果削梨,一样要重复上述代码,我能想到的,多态能去重,如:

public class Fruit {
    public Fruit getPeeled() {
        System.out.println(this);
        return Peller.peel(this);
    }
    public static void main(String[] args) {
        new Apple().getPeeled();
        new Pear().getPeeled();
    }
}
class Apple extends Fruit{
}
class Pear extends Fruit{
}

class Peller {
    public static Fruit peel(Fruit fruit) {
        System.out.println("削皮");
        //... remove peel 削皮
        return fruit;
    }
}

但是说到底,我个人还是倾向于:

class Person{
    public void eat(Fruit fruit){
        Fruit peeled = Peeler.peel(fruit);
    }
}

这是第二个应用,我道行浅,真没这么写过… 另外有个小细节,我会这样写:

class Peeler{
    static Apple peel(Apple apple){
        //... remove peel 削皮
        return peeledApple;
    }
}

    3 . 在构造器中调用构造器。也就是 this(…) 的使用(括号中“…”代表参数列表,下同)。 对此,书中的解释是:在构造器中,如为this添加了参数列表(需指出,无参形式的 this() 亦包含在内),这将产生对符合此参数列表的某个构造器的明确调用(显式调用)。这里我个人有一个疑问,在这个定义之前,也就是引语中,有这样一句:“有时可能想在一个构造器中调用另一个构造器,以避免重复代码”,不知道这里“避免重复代码”是指在一个构造方法中调用另一构造方法时,省略拷贝后者中的代码进前者,还是除了this(…)/super(…)之外还有其他显式调用其他构造器的方法?我印象里是没有。当然,这是题外话了,另外讲道理,这块儿结合类和对象的初始化一起说可能效果更好,等之后腾开时间再说吧,此处就不涉及了。此处只合并this(…)/super(…)二者加以说明。
    关于this(…)/super(…)是什么就不再赘述了。书中例挺繁琐的,我简化一下(额,可能不止一下…),主要解释结论。

Class A{
    public static void main(String[] args){
        A a = new A();
    }

    A(){
        this("A");
        //this("A",1);   //不能调用两次
    }

    A(String s){
    }

    A(String s,int i){
    }

    void 这是一个普通方法(){
        //this("A",1);   //不能在除构造方法之外的任何方法中调用另一构造方法
    }

}

    我省略了输出,由于this(…)/super(…)的必须置于首行的特性,书中例整体上 自下向上执行,自上而下输出,可能从某种程度上费点儿眼神…
    主要解释注释:
    其一,this(…)可以实现在一个构造器中调用另一个构造器,但不能调用两个。
    其二,必须将 构造器调用 置于最起始处(首行),见下例

    A(){
        System.out.println("只是为了占用首行");
        // this("A");  
        // Constructor call must be the first statement in a constructor   构造器调用必须是一个构造器中的首条语句
    }

    这两条结论的解释是:保证对象能够正确进行初始化(关于首行),且这种初始化仅进行一次(关于不能调用两个)。

    有意思的是(或者说让人困惑的),

    A(){
        this("A");
        // this("A",1);  
        // 它的报错信息同样是:
        // Constructor call must be the first statement in a constructor 
    }

    看起来就像:“因为必须置于首行,所以才不能调用两个”。实际上这种因果关系是牵强的。记结论吧,这地方容易花式绕进去(比如“因为不能在首行外的其他行调用第二个,所以第二次调用行报错,只能置于首行”…)。

    其三,不能在除构造方法外的其他任何方法中,使用this(…)/super(…)调用其他构造方法。没什么解释的。

    关于super(…)补充说明如下:
    其一,用数学语言,this(…)和super(…)互斥。
    其二,当没有显式使用this(…)或super(…)调用指定构造,编译器默认补充无参形式super(),调用其基类或公共基类Object的无参构造,以保证初始化正确进行。
    这第三条终于说完了…

    4 . this.参数 代表成员变量。第四条就简单了,局部变量名称和成员变量冲突,使用 this.同名参数 指代成员变量,通常用于将局部变量赋值给同名成员变量,对,就是自动生成的 set() 方法,自己写就不要给自己挖坑找麻烦了。

    最后,书中关于static的补充说明也十分有意思,特别是“在静态方法内部能够调用非静态方法”的“打破真理一般”的表述,面试加分项,我觉得。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值