点击进入我的博客

复用代码是Java众多引人注目的功能之一,但要成为极具革命性的语言,仅仅能够复制代码并对之加以改变是不够的,它还必须能够做更多的事情。

7.1 组合

组合语法

就是在当前类中产生现有类的对象。

toString()方法

每一个非基本类型的对象都有该方法,当编译器需要一个String但你只有一个对象时,该方法会自动调用。

初始化引用

编译器并不是简单的为每一个引用都创建对象,因为这回增加不必要的负担

  1. 在定义对象的时候初始化,这意味着在构造方法之前被调用。
  2. 在类的构造方法中。
  3. 就在正常使用这些对象之前——惰性初始化
  4. 使用实例初始化

7.2 继承

  • 使用extends关键字实现继承
  • 当创建一个类时总是在继承,除非明确指定从其他类继承,否则就是隐式从Object继承
  • 继承时会自动得到基类中所有的域和方法
  • 使用super.func()调用父类中的方法。

7.2.1 初始化基类

  • 当创建类一个子类的对象时,该对象包含了一个基类的自对象。这个子对象与你用基类直接创建的对象是一样的。二者区别在于,后者来自于外部,而基类的子对象被包装在子类对象内部。
  • 对基类子对象对正确初始化:在子类构造器中调用基类构造器。Java会自动在子类构造器中插入对基类构造器对调用。
  • 编译器强制你去初始化基类,但并不代表会初始化成员对象。
带参数的构造器
  • 如果基类没有默认的无参构造器,或者想调用一个带参数的基类构造器,就必须用关键字super显式地编写基类构造器的语句。
  • 对基类构造器的调用必须放到子类构造器的第一行。

7.3 代理

  • Java并没有提供对代理对直接支持,这是继承和组合的中庸之道。
  • 代理:将一个成员对象放置于所需要的构造的类中(像组合),但与此同时在新类中暴露该成员对象的相关方法(像继承)。
  • 使用代理可以有更多的控制力,因为我们可以选择只暴露一个子集。
public class Test {
    TestDemo demo = new TestDemo();
    // 代理
    public void func() {
        demo.func();
    }
}

class TestDemo {
    void func() {}
}

7.4 结合使用组合和继承

7.4.1 确保正确清理

  • 一般情况下,清理并不是问题,仅需让垃圾回收器完成该动作就行。但当必须亲自处理清理(如文件的关闭)时,就要多加小心。
  • 因为,垃圾回收器可能永远也无法被调用,即时被调用,它是按照任何它想要的顺序来回收对象。
  • 最好的办法是除了内存以外,不能依赖垃圾回收器去做任何事。
  • 如果需要清理,就自己写一个清理方法,但不要使用finalize()。
  • 清理语句尽量写在finally代码块中。

7.4.2 名称屏蔽

  • 如果Java的基类已经拥有某个已被多次重载的方法,那么在子类中重新定义该方法名称并不会屏蔽其在基类中的任何版本。因此,无论是在该层或者它的基类中队方法进行定义,重载机制都可以正常工作。
  • 可以使用@Override来表示要重写父类方法。

7.5 在组合和继承间选择

组合和继承都允许在新的类中放置子对象,组合是显式地这样做,而继承是隐式地做。

继承很重要但是并不意味着我们需要常常用他,如何判断是否应该使用继承请参照两个标准:

  1. 如果是“is-a”的关系,那就使用继承。如果是“has-a”的关系,那就使用组合。
  2. 是否需要向上转型,如果必须向上转型则必须使用继承,否则应该慎重考虑

7.6 protected关键字

  • 在实际项目中,经常会想要将某些事物尽可能对这个世界隐藏起来,但仍允许导出类的成员访问它们。
  • 尽管可以创建protected域成员,但最好的方式还是private,只有在真正需要的时候才使用protected关键字。

7.7 向上转型

  • “为新类提供方法”并不是继承技术最重要的方面,其最重要的方面是用来表现子类和基类之间的关系。
  • 子类引用可以向上转型成为基类引用,因为派生类一定具有基类的方法。类接口唯一可能发生的事情是丢失方法,而不是获取它们。

7.8 final关键字

7.8.1 final数据

final变量
  • final基本数据类型:表示数据是不可变的常量
  • final对象引用:引用与对象的关联关系不可变,但可以对对象进行操作。
  • final static约定用大写字母+下划线命名规范
空白final
  • 空白final指被声明为final但又未给定初值的域,但可以在构造方法
  • 必须在域的定义处、static代码块或构造器中对final进行赋值。
final参数
  • Java允许在参数列表中将参数指明为final
  • 修饰对象参数:意味着无法在方法中修改参数引用的对象
  • 修饰基本类型:可以使用但无法修改此参数

7.8.2 final方法

  1. 把方法锁定,禁止继承类修改它(即不会被重写)
  2. 在JavaSE5之前,使用final可以提高效率,但目前已过时
final和private关键字
  • 类中所有private方法都隐式的指定是final的,在private方法前添加final是没有额外意义的。
  • private修饰的方法,不属于基类接口一部分,他仅仅是隐藏在类中的一部分代码。因此如果你在导出类中“覆盖”了基类的private方法,其实并没有覆盖private方法,而产生了一个新方法。

7.8.3 final类

  • final放在class前做修饰,表明该类无法进行继承
  • final类中的域和普通类的域并无差别
  • final类隐式的将该类中所有的方法指定为final,所以在final类中给方法添加final关键词没有意义。

7.8.4 final的忠告

要考虑清楚!!!

7.9 初始化及类的加载

每个类的编译代码.class都存在于独立的文件中,该文件只在需要的使用程序代码时才会被加载。

  1. 按照父类到子类的顺序加载static变量
  2. 构造子类对象时,先构造父类对象。
public class Test extends TestParents {
    // (a) static属性
    static String staticProperty;

    // (b) 构造方法
    public Test() {
        System.out.println("Test constructor");
    }

    // (c) static代码块
    {
        staticProperty = print("Test static property");
        System.out.println("Test static");
    }
    
    // (d) 非static代码属性
    String property = print("Test property");

    public static void main(String[] args) {
        // (1):System.out.println(Test.staticProperty);
        // TestParents static property
        // null(因为static代码块中的代码要创建对象才执行)
        // (2):Test test = new Test();
        // TestParents static property
        // TestParents property
        // TestParents static
        // TestParents constructor
        // Test static property
        // Test static
        // Test property
        // Test constructor
    }
}

class TestParents {
    static String staticProperty = print("TestParents static property");

    String property = print("TestParents property");

    {
        System.out.println("TestParents static");
    }

    public TestParents() {
        System.out.println("TestParents constructor");
    }

    static String print(String str) {
        System.out.println(str);
        return str;
    }
}
静态代码块和静态变量
  • 静态变量 >> 静态代码块
代码块(注意不是静态代码块)、构造方法、非static属性
  • 三者顺序:代码块 == 非static属性 >> 构造方法;即代码块和非static属性按照代码中顺序排序,构造函数在最后面
  • 上述Test类的代码,反编译后的结果如下所示:
public Test()
    {
        staticProperty = print("Test static property");
        System.out.println("Test static");
        property = print("Test property");
        System.out.println("Test constructor");
    }

7.10 总结

优先选择组合和代理,必要时才使用继承。


卢卡斯哔哔哔
46 声望6 粉丝