1 final修饰变量
1.1 final概述
final单词直译为“最终的“,在Java中可以用来修饰变量、方法和类:
- final修饰的变量:可以初始化,不能再更改
- final修饰的方法:不能在子类中重写
- final修饰的类:不能再被继承派生出子类了
1.2 final修饰局部变量
在方法中声明的变量称为局部变量。
局部变量加final修饰以后,只能初始化一次,不能再次更改。因此 final 修饰的变量可以在声明的同时初始化或者在构造函数中初始化。
注意:当引用类型变量被设置为final时候,表示其值就是引用对象的地址值不能再次修改了,但是此时被引用对象的属性或者元素是可以被修改的。
使用final的目的是保护局部变量的值不变,避免在方法运算期间被意外篡改。如果一个需要反复更改的局部变量就不要使用final修饰。
查看如下所示代码:
int 类型的变量a添加了 final 修饰符,则被初始化之后将不能被再次赋值。
另外, final 修饰的变量 arr 是int类型的数组,也是只能被初始化一次不能再次被修改。但是因为变量 arr 是引用类型,其值是数组对象的首地址(初始化以后不能再次被修改),即变量arr与数组之间的引用关系不能发生改变了;但是数组的内容是可以改变的。因此可以给数组的元素赋值,但是不能给 arr 重新定义。
编写代码,测试 final 修饰的基本类型变量和引用类型变量。
案例示意代码如下:
import java.util.Arrays;
public class FinalDemo1 {
public static void main(String[] args) {
final int a;
a = 8; //第一次为变量赋值,称为初始化
System.out.println(a); //
//a = 9; //编译错误,a不能再次被修改
final int b = 9; //声明变量直接初始化
System.out.println(b);
//b = 10; //编译错误,不能再次修改final变量
//final修饰的引用类型变量:
//引用类型变量初始为一个地址值后不能再次修改
final int[] arr = {5, 6};//arr中存储的数组地址不能再次修改类
//arr引用不能修改,但是被引用对象的内容可以修改
arr[0] = 9;
System.out.println(Arrays.toString(arr)); //[9, 6]
//不可以更改arr变量的值,因为arr是final类型的!
//arr = new int[8];
final Ball ball = new Ball();
ball.d = 10;
System.out.println(ball.d);
//不能更换ball的值,也就是地址值
//ball = null;
}
}
class Ball{
int d = 5;
}
1.3 final修饰方法的参数
Java中方法参数也是一种局部变量,只是其声明位置是方法参数,在接收到传递参数的时候初始化。
在方法参数上可以使用final修饰。final修饰以后也是初始化以后不能再次修改,由于方法参数是在调用方法传递参数值时候初始化的,所以在方法运行期间方法参数变量的值不能修改了。
使用final修饰方法参数的好处也是保护变量的值,避免在方法运行期间参数变量的值被意外篡改。
查看如下代码示例:
方法 test 的第二个参数被声明为 final,在方法内部不能再修改其数值了。
定义带两个参数的方法(其中一个参数用 final 修饰),编写代码,测试 final 修饰的参数的使用。
案例示意代码如下:
public class FinalDemo2 {
public static void main(String[] args) {
test(5,6);
test(7,8);
}
public static void test(int a, final int b) {
a = 9;
//b = 8; //编译错误,不能再次更改变量b
System.out.println("a:"+a+",b:"+b);
}
}
1.4 final修饰实例变量
在类中声明的对象属性,由于是属于每个对象实例的变量所以也称为“实例变量”。
final可以修饰实例变量,在final修饰实例变量时候必须直接初始化或者在构造器中初始化,并且实例变量也是在初始化以后不能再次改变了。
使用final修饰实例变量的目的也是保护实例变量,使其值在初始化以后不能改变,避免程序的意外篡改。比如如果希望一个对象的唯一ID编号,在初始化以后不能改了,就可以利用final修饰。
在实际开发中很少使用final修饰的实例变量!主要原因是不方便对象的复用。很多Java底层框架都会利用对象池重复使用对象,避免反复创建销毁对象的性能开销,如果对象属性是final的,就无法再次进行赋值重用对象了!
查看如下代码示例:
类 Eoo 的实例变量 a 和 b,都声明为 final,在 main 方法中测试发现,创建 Eoo 对象后,可以访问该属性,但是不能修改它。
为类定义 final 修饰的实例变量并编写代码测试其特点。
案例示意代码如下:
public class FinalDemo3 {
public static void main(String[] args) {
Eoo eoo = new Eoo(8);
System.out.println(eoo.a); //5
System.out.println(eoo.b); //8
//不能再次更改final的实例变量
//eoo.a = 9;
//eoo.b = 10;
Eoo e2 = new Eoo(10);
System.out.println(e2.a);
System.out.println(e2.b);
}
}
class Eoo {
//final的属性必须初始化
final int a = 5;
final int b;
public Eoo(int b) {
this.b = b;
}
}
2 final修饰方法
方法上可以使用final修饰,final的方法在子类中不能被重写修改了。简单理解:final方法不能被重写。final修饰的方法不会影响方法在当前类中的使用,但是如果派生了子类,则在子类中不能重写修改父类中定义的final方法。
final方法的好处是避免被子类使用重写语法修改方法的功能,保护方法的功能是“最终”版本。如果需要保护方法的功能,避免在子类中重写修改,就可以使用final进行声明。
但是在实际工程项目中很少使用final方法,原因是很多框架工具都会采用“动态代理”技术代理(重写)对象的功能,实现灵活的软件功能,如果使用final的方法将直接影响这些框架功能!很多些软件开发企业在编程规范中明确规定:不能声明final方法!
查看如下示例:
类 Foo 中的方法 test() 添加了 final 修饰符,可以被子类继承但是不能被子类重写。
为类定义 final 修饰的方法并编写代码测试其特点。
案例示意代码如下:
public class FinalDemo4 {
public static void main(String[] args) {
Foo foo = new Foo();
foo.test();
SubFoo sf = new SubFoo();
sf.test();
}
}
class Foo{
public final void test() {
System.out.println("Foo.test()");
}
}
class SubFoo extends Foo{
//public void test() { //编译错误,不能重写Foo中的final方法
// System.out.println("SubFoo.test()");
//}
}
3 final修饰类
类名也可以使用final修饰,被final修饰的类将不能再派生子类了,也就是终结了类的继承。简单理解:final类不能被继承。
final类的好处和final方法类似,也是可以避免被继承和重写,避免被子类修改功能。Java的很多核心API,都是final类型,这样就保护了这些非常重要的API功能。这些API包括:String、Math、Integer、Double、Long等。这些API都不能派生子类。
在开发中也不允许使用final声明类,原因也是因为声明final类以后,造成很多框架无法采用“动态代理”技术代理扩展对象的功能,很多些软件开发企业在编程规范中明确规定:不能声明final类!
查看如下示例:
类Goo添加了 final 修饰符,则不能被继承。
定义 final 修饰的类,并编写代码测试其特点。
案例示意代码如下:
public class FinalDemo5 {
public static void main(String[] args) {
Goo goo = new Goo();
goo.test();
}
}
final class Goo{
public void test() {
System.out.println("test()");
}
}
//class SubGoo extends Goo{ //编译错误,不能继承final类
//}