五. 复用和final关键字的使用

五. 复用和final关键字的使用

对于像 C 语言等面向过程语言来说,“复用”通常指的就是“复制代码”。任何语言都可通过简单复制来达到代码复用的目的,但是这样做的效果并不好。Java 围绕“类”(Class)来解决问题。我们可以直接使用别人构建或调试过的代码,而非创建新类、重新开始。以下介绍两种方式:组合和继承。

1. 组合语法

简单来说,就是把对象的引用放置在一个新的类里,便是使用了组合。

编译器不会为每个引用创建一个默认对象,因为在许多情况下,这会导致不必要的开销。

初始化引用有四种方法:

  1. 当对象被定义时。这意味着它们总是在调用构造函数之前初始化。
  2. 在该类的构造函数中。
  3. 在实际使用对象之前。这通常称为延迟初始化。在对象创建开销大且不需要每次都创建对象的情况下,它可以减少开销。
  4. 使用实例初始化。

2. 继承语法

在创建类时一定有继承,因为除了显式地继承其他类,就肯定隐式地继承 Java 的标准根类对象(Object)。

使用关键字 extends 后跟基类的名称,将自动获得基类中的所有字段和方法。(基类中被private修饰的只能在本类中使用,子类中无法使用)

3. 组合与继承的选择

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

当你想在新类中包含一个已有类的功能时,使用组合,而非继承。也就是说,在新类中嵌入一个对象(通常是私有的),以实现其功能。

有时让类的用户直接访问到新类中的组合成分是有意义的,只需将成员对象声明为 public 即可。成员对象隐藏了具体实现,所以这是安全的。

继承一般是“is a”的关系表达的,而“has a”的关系可以用组合来表达。

一个比较好的选择方法:在考虑一个类该用组合还是继承时,可以问一问自己是否未来可能要将其向上转型,如果需要就必须使用继承。

4. final关键字

根据上下文环境,Java 的关键字 final 的含义可能有一点的不同,但一般都是指“这是不能被改变的”。

final 可以用在三个地方:数据、方法和类

final数据

许多编程语言都有某种方法告诉编译器有一块数据是恒定不变的。恒定是有用的,如:

  1. 一个永不改变的编译时常量。
  2. 一个在运行时初始化就不会改变的值。

一个被 staticfinal 同时修饰的属性只会占用一段不能改变的存储空间。

而当用 final 修饰对象引用而非基本类型时,final 使引用恒定不变。一旦引用被初始化指向了某个对象,它就不能改为指向其他对象。但是,对象本身是可以修改的,Java 没有提供将任意对象设为常量的方法。这对于数组具有同样的意义,数组只不过是另一种引用。一般来说,声明引用为 final 没有声明基本类型为 final 有用。

注意:编译器确保空白 final 在使用前必须被初始化,这样既能使一个类的每个对象的 final 属性值不同,也能保持它的不变性。所以必须在定义时或在每个构造器中执行 final 变量的赋值操作。

final参数

在参数列表中,将参数声明为 final 意味着在方法中不能改变参数指向的对象或基本变量。如下例代码:

class Test {
    
    //void func1(final int i) { i++; } // 无法改变i
    // 只能读取
    int func2(final int i) {
        return i + 1;
    }
}

只能读取而不能修改参数,这个特性主要用于传递数据给匿名内部类。

final方法

一般使用 final 方法是为了防止子类通过覆写改变方法的行为。这是出于继承的考虑,确保方法的行为不会因继承而改变。

final和private

类中所有的 private 方法都被隐式地指定为 final。因为不能访问 private 方法,所以不能覆写它。可以给 private 方法添加 final 修饰,但是并不能给方法带来额外的含义。

当你试图覆写一个 private 方法(隐式是 final 的)时,看上去奏效,而且编译器不会给出错误信息。代码如下:

public class S {

    public static void main(String[] args) {
        Test2 t2 = new Test2();
        t2.func();
        // ((Test)t2).func(); 此处无法调用
    }
}

class Test {

    private void func(){
        System.out.println("This is Test.");
    }
}

class Test2 extends Test {

    public void func(){
        System.out.println("This is Test2.");
    }
}

如果一个方法是 private 的,它就不是基类“接口(可以被其他类使用)”的一部分。它只是隐藏在类内部的代码,且恰好有相同的命名而已。如果你在派生类中以相同的命名创建了 publicprotected 或包访问权限的方法,这些方法与基类中的方法没有联系,你没有覆写方法,只是在创建新的方法而已。

final类

一个类为 final 类时,就意味着它不能被继承。当一个类的设计就是永远不需要改动,或者是出于安全考虑不希望它有子类,可将其定义为 final 类。

由于 final 类禁止继承,类中所有的方法都被隐式地指定为 final,所以没有办法覆写它们。可以在 final 类中的方法加上 final 修饰符,但不会增加任何意义。

final的使用忠告

在设计类时将一个方法指明为 final 看上去是明智的,可能会觉得没人会覆写那个方法,有时这是对的。

但是通常来说,预见一个类如何被复用是很困难的,特别是通用类。如果将一个方法指定为 final,可能会防止其他程序员的项目中通过继承来复用你的类,而这仅仅是因为你没有想到它被以那种方式使用。

参考资料:On Java 8

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值