《Java 编程思想》学习笔记 06 | 复用类

1.创建的类总是在继承

如果创建的类没有明确声明继承自哪个类,那么该类就会默认继承自Object类。

2.用super可以调用从基类继承而来的方法

假如子类重写了父类的一个方法,而你又想调用父类的方法,就可以使用super来调用。

3.初始化基类

Java仅有一种方法用来保证正确的初始化基类:在子类构造器中调用基类构造器。

注意:

  1. 子类的默认构造器也会默认调用基类的构造器,前提是基类的构造器是不带参的构造器。
  2. 如果基类的构造器是有参的,没有无参的,那么就必须显式的定义出子类的构造器并调用基类的带参构造。
  3. 基类在子类构造器可以访问它之前就已经完成了初始化,所以初始化的顺序是从基类再到派生类向外扩散。
  4. 因为Java没有多继承,所以在子类构造器中一定只使用到了一次super来调用基类构造器。
  5. 调用基类构造器一定是你在子类构造器中要做的第一件事。
  6. 应该在子类构造器中以super(args)的形式调用基类构造器,而不是以super.的形式使用。
class Game {
    Game(int i) {};
}
class BoardGame extends Game {
    BoardGame(int i) {
        super(i); 
    }
}
public class Chess extends BoardGame {
    Chess(int i) {
        super(11);
    }
}
/*
* 由11行可知,因为Java没有多继承,就算出现了多层继承,也只需要一个super来调用上一层基类。
*/

4.Java在继承中的重载机制

假如基类中存在一个已被多次重载的方法,那么在子类中重新对其进行重载并不会屏蔽掉基类中的任何一个重载版本。

因此,在子类中对基类的同一个方法的任意重载版本进行重写是可行的,Java的重载机制非常自由。

5.@Override注解防止重载

@Override注解表示重写方法,它可以防止你意外进行了重载,然后给出错误信息。

6.在组合与继承直接选择

组合是 has a 有一个的关系;

继承是 is a 是一个的关系。

7.使用继承的时机

其实在实际开发中继承并不常用,但你只要问一下自己:“我需要向上转型嘛?”如果需要,则可以使用继承。

8.final

final使基本类型的值恒定不变。

final使对象的引用恒定不变,但对象本身可以修改。(这一限制也适用于数组,数组也是对象)

class Value {
    int i;
    public Value(int i) {
        this.i = i;
    }
}

public class FinalData {
    
    private static Random rand = new Random(47);
    
    private string name;
    
    public FinalData(String name) {
        this.name = name;
    }
    
    //编译时常量:
    private final int valueOne = 9;
    private static final int VALUE_TWO = 99;
    public static final int VALUE_THREE = 39;
    
    //不是编译时常量:
    //基本类型:
    private final int i4 = rand.nextInt(20);
    private static final int INT_5 = rand.nextInt(20);
    //对象:
    private Value v1 = new Value(11);
    private final Value v2 = new Value(22);
    private static final Value VAL_3 = new Value(33);
    //数组:
    private final int[] a = {1, 2, 3, 4, 5, 6};
    
    public Sting toString() {
        return name + ":" + "i4 = " + i4 + ", INT_5 = " + INT_5;
    }
    
    public static void main(String[] args) {
        FinalData fd1 = new FinalData("fd1");
        FinalData fd2 = new FinalData("fd2");
        
        //fd1.valueOne++; //错误,被final修饰的基本类型数据无法修改
        fd1.v1 = new Value(0); //正确,对象v1没有用final修饰,引用可以改变,指向新new出来的对象。
        fd1.v2.i++; //正确,fonal只让对象v2的引用无法改变,但可以修改v2对象里的属性的值。
        //fd1.v2 = new Value(0); //错误,final使对象v2的引用无法改变,也就是v2无法指向新new出来的对象。
        //fd1.VAL_3 = new Value(0); //错误,final使对象VAL_3的引用无法改变,也就是VAL_3无法指向新new出来的对象。
        
        for(int i =0; fd1.a.length; i++) {
            fd1.a[i]++; //正确,final只让数组的引用不变,但可以修改数组的值。
        }
        fd1.a = new int[3]; //错误,final使数组的引用无法改变,数组a无法指向新new出来的数组。
        
        System.out.println(fd1); //fd1: i4 = 15, INT_5 = 18
        System.out.println(fd2); //fd2: i4 = 13, INT_5 = 18
    }
}

9.final与static final

在上面的代码块中,可以看到:fd1对象与fd2对象的i4的值是不同的,但INT_5的值却是相同的,这就是finalstatic final的区别:

finalstatic final都能使基本类型的值恒定不变,不同的是:

  1. 当被修饰的成员是编译时常量时(如19、20行代码),二者没有本质区别,都是使值恒定不变;
  2. 当被修饰的成员是非编译时常量时(如25、26行代码),i4INT_5通过rand.nextInt()实现了在运行时才得以确定数值。也就是说,当创建不同的对象时,有static修饰的INT_5,不管对象再多,它的值只有一个,是静态的final,是共享的;而没有static修饰的i4,虽然它是final的,但因为它不是编译时常量,所以这种“不变”的性质只存在于在运行时才得以确定的对象之中每创建一个对象,这种非编译时常量就会因为没有static修饰而在不同的对象中有所不同。

总结:

finalstatic final都能使值恒定不变,但如果被修饰的成员不是编译时常量,那么没有static修饰的该成员在不同的对象中的值就会有所不同,但final的这种“不变”的性质在每一个独立的对象内部都是成立的。

10.空白final(未给定初值的final)

空白final成员可以相当于是非编译时常量,可以在创建不同对象的时候给予其不同的值。

public class BlankFinal {
    private final j; //空白final成员
    
    public BlankFinal() {
        j = ;
    }
    
    public BlankFinal(int i) {
        j = i;
    }
    
    public static void main(String[] args) {
        BlankFinal b1 = new BlankFinal(); //j = 1
        BlankFinal b2 = new BlankFinal(2); //j = 2
    }
}

11.final参数

首先记住一点,当final被用来修饰方法的参数时,不管该参数是基本类型也好,还是非基本类型也好,其作用都是使参数引用无法被改变。当传入方法的参数是final的时,你只能“用”这个参数,而不能“修改”这个参数,因为如果你修改了这个参数,就变相地使参数引用去指向不同的参数对象。

void f(int i) {
    i++; //可以
}

void f(final int i) {
    //i++; //不可以
}

void f(final int i) {
    System.out.println(i); //可以
}

12.final方法

使用final方法的原因:使该方法无法被修改。也就是说,子类想要重写该方法,是不可奏效的

13.pivate方法都是final方法

类中所有的private方法都隐式的指定为是final的。

13.你并不能在子类中重写private方法,就算写了,也是新的

由于private方法都隐式的指定为是final,所以你并不能通过任何的途径去修改该方法,就算是在继承关系中,好像你在子类中“重写”了父类的private方法,但事实上并没有:

  1. 被你“重写”的父类的private方法仍旧只有一个版本,只存在于父类。
  2. 而在子类中好像被你“重写”了的父类的private方法对于父类来说却是全新的东西,它只存在于子类中,二者并没有本质的联系。

总结:由于private方法无法触及而且能有效隐藏,所以你只需要把它看成是它所归属的类的组织结构就行。

14.final类

当一个类被修饰为final,就表明该类无法被继承,你不能重写该类的方法,因为final类的方法都默认是final方法。

15.大道理

如果将项目视作是一种有机的、进化着的生命体而去培养,而不是打算像盖摩天大楼一样快速见效,就会获得更多的成功和更迅速的回馈。继承与组合正是在面向对象程序设计中使得你可以执行这种实验的最基本的两个工具。

注:《Java编程思想》p147原话。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值