Chapter 7 复用类

final关键字

java中的final关键字通常是指它所修饰的元素“是无法改变的”。而根据它所修饰的元素的不同,所起的作用存在着细微的区别。下面就讨论可能用到final的三种情况:数据、方法和类。

 

final数据

final修饰变量本身并不复杂,就是变量一经初始化就不能再改变(如果是基本数据类型,就是其数值不可以改变;如果是引用类型,就是其不可以再重新指向其他对象)。

关于final成员变量的初始化——总之一句话:就是在构造方法调用结束之前完成final成员变量的初始化。

很多文章都这么说——其初始化可以在两个地方,一是其定义处,二是在构造方法中,两者只能选其一。这种说法是不正确的,final变量可以在任何可以被初始化的地方初始化,但只能被初始化一次。一旦被初始化后就不能再次赋值。作为成员变量一定要显式初始化,而作为临时变量则可以只定义不初始化(当然也不能引用)——既空白final。下面是final成员变量初始化的小例子:

package com.ygc;

import java.util.Random;

/**
 * final成员变量初始化的几种情况, 可以在定义处初始化,可以在语句块中初始化,可以在构造方法中初始化。
 * 可以采用随机值进行初始化,甚至可以使用方法进行初始化,只要初始化在构造方法结束前完成,并且只初始化一次就可以。
 * */
public class FinalFieldInitialization {
	Random rand = new Random(47);
	/**
	 * 可以在定义时初始化, 可以使用随机值初始化
	 * */
	final int finalIntegerValue = 1;
	final int randomFinalIntegerValue = rand.nextInt();
	final String finalReference = "final reference";

	/**
	 * 可以在语句块中初始化
	 * */
	final int finalIntegerValue1;
	final int randomFinalIntegerValue1;
	final String finalReference1;
	{
		finalIntegerValue1 = 1;
		randomFinalIntegerValue1 = rand.nextInt();
		finalReference1 = "final reference";
	}

	/**
	 * 可以在构造方法中初始化, 有多少个构造方法就得初始化多少次
	 * */
	final int finalIntegerValue2;
	final String finalReference2;

	public FinalFieldInitialization() {
		finalIntegerValue2 = 1;
		finalReference2 = "final reference";
	}

	public FinalFieldInitialization(int i) {
		finalIntegerValue2 = 1;
		finalReference2 = "final reference";
	}

	/**
	 * 使用方法初始化
	 * */
	public FinalFieldInitialization(String s) {
		finalIntegerValue2 = 1;
		finalReference2 = getString();
	}

	private String getString() {
		return "final refrence";
	}

}


final修饰方法参数

final修饰方法参数的效果也是一样的,如果修饰的是基本数据类型,则不可以修改它的值;而如果修饰的是引用类型,则不能重新指向其它对象,但对象本身是可以改变的。而final修饰方法参数最常用的目的就是供匿名内部类使用,而且如果想要在匿名内部类中使用局部变量或者方法参数,则必须使用final修饰,而使用外部类的成员变量则不需要使用final修饰。因为当你在匿名内部类中使用局部变量或方法参数时,编译器会把它传递给匿名内部类的构造方法,然后隐式地对它做一个拷贝,使它成为匿名内部类的一个成员变量,而如果这个局部变量或方法参数不是final的,那么他们在匿名内部类的内部或外部就可以被修改(如果是基本数据类型 就可以修改它们的数值;而如果是引用类型,可以修改它们所指向的对象),一旦对这个变量进行了修改,就会导致外部变量和内部变量的不一致,从而产生一些不可预测的错误。为了避免这种情况的发生,匿名内部类在引用局部变量或方法参数时必须使用final修饰。而使用成员变量时,编译器会隐式地给匿名内部类传入一个外部类的this引用 ,而这个this引用是final的,所以匿名 内部类可以随意地访问外部类的成员。下面是一个小例子:

	private String enclosingClassField;
	private void updateStatus() {
		final StringBuffer stringBuffer = new StringBuffer();
		Runnable doUpdateStatus = new Runnable() {
			public void run() {
				// 你可以使用FinalFieldInitialization.this, 因为它永远都是final的
				FinalFieldInitialization.this.enclosingClassField = "";
				// 下面这种写法是简便的写法,因为每一个内部类在实例化的时候,编译器都会隐式地帮它们添加一个外部类的this引用
				enclosingClassField = "";
				// 你不可以改变stringBuffer的值,但是你可以修改它指向的对象的内容
				stringBuffer.append("hello final");
			}
		};
	}

上述说法可以通过查看字节码得到 验证,有兴趣的同学可以研究一下。

final方法

final修饰方法的第一个原因是把方法锁定,以防止任何继承类修改它的定义。既,用final修饰的方法不可以重写。

final修饰方法的第二个原因是为了提高效率,在早期的java实现中会起到一定的作用,现在不建议使用,因为使用final修饰方法并不会得到性能的显著提升。所以只有当你确定你的方法不想被重写时,才应该考虑用final修饰。

类中所有private修饰的方法都隐式地被指定为final的。因为在继承类中无法访问private方法,所以也无法覆盖。如果你试图重写一个父类中的private修饰的方法,实际上你只是定义了一个新的方法。

class WithFinals {
    // Identical to "private" alone:
    private final void f() {
        print("WithFinals.f()");
    }

    // Also automatically "final":
    private void g() {
        print("WithFinals.g()");
    }
}
class OverridingPrivate extends WithFinals {
    private final void f() {
        print("OverridingPrivate.f()");
    }

    private void g() {
        print("OverridingPrivate.g()");
    }
} 
class OverridingPrivate2 extends OverridingPrivate {
    public final void f() {
        print("OverridingPrivate2.f()");
    }

    public void g() {
        print("OverridingPrivate2.g()");
    }
}
public class FinalOverridingIllusion {
    public static void main(String[] args) {
        OverridingPrivate2 op2 = new OverridingPrivate2();
        op2.f();
        op2.g();
        // You can upcast:
        OverridingPrivate op = op2;
        // But you can't call the methods:
        // ! op.f();
        // ! op.g();
        // Same here:
        WithFinals wf = op2;
        // ! wf.f();
        // ! wf.g();
    }
}

 

“覆盖”只有在某方法是基类的接口的一部分时才会出现。即,必须能将一个对象向上转型为它的基本类型并调用相同的方法。如果某方法为private,它就不是基类接口的一部分。它仅是一些隐藏于类中的程序代码,只不过具有相同的名称而已。但如果在导出类中以相同的名称生成一个public、protected 或包访问权限(package access)方法的话,此时你并没有覆盖该方法,你仅是生成了一个新的方法。

final 类

final修饰类的目的在于你不想有任何类从此类继承。final类中的所有方法都隐式地是final的,因为类无法被继承,所以其中的方法也不会被重写。但是final类中的域并不是final的,除非 显示地设置。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值