网站建设与管理案例教程教学大纲,网站续费公司,网站的空间是服务器吗,万网有网站建设吗一、前言 在java的关键字中#xff0c;static和final是两个我们必须掌握的关键字。不同于其他关键字#xff0c;他们都有多种用法#xff0c;而且在一定环境下使用#xff0c;可以提高程序的运行性能#xff0c;优化程序的结构。下面我们来了解一下final关键字及其用法。 …一、前言 在java的关键字中static和final是两个我们必须掌握的关键字。不同于其他关键字他们都有多种用法而且在一定环境下使用可以提高程序的运行性能优化程序的结构。下面我们来了解一下final关键字及其用法。 二、final关键字 在java中final的含义在不同的场景下有细微的差别但总体上来说它指的是“这是不可变的”。不想被改变的原因有两个效率、设计。使用到final的有三种情况数据、方法、类。下面我们来讲final的四种主要用法。 1、修饰变量 有时候数据的恒定不变是很有用的它能够减轻系统运行时的负担。对于这些恒定不变的数据我可以叫做“常量”。在java中用final关键字修饰的变量只能进行一次赋值操作并且在生存期内不可以改变它的值。“常量”主要应用与以下两个地方 1、编译期常量永远不可改变。 2、运行期初始化时我们希望它不会被改变。 对于编译期常量它在类加载的过程就已经完成了初始化所以当类加载完成后是不可更改的编译期可以将它代入到任何用到它的计算式中也就是说可以在编译期执行计算式。当然对于编译期常量只能使用基本类型而且必须要在定义时进行初始化。 有些变量我们希望它可以根据对象的不同而表现不同但同时又不希望它被改变这个时候我们就可以使用运行期常量。对于运行期常量它既可是基本数据类型也可是引用数据类型。基本数据类型不可变的是其内容而引用数据类型不可变的是其引用引用所指定的对象内容是可变的。不过在针对基本类型和引用类型时final关键字的效果存在细微差别。我们来看下面的例子
class Value {int v;public Value(int v) {this.v v;}
}public class FinalTest {final int f1 1;final int f2;public FinalTest() {f2 2;}public static void main(String[] args) {final int value1 1;// value1 4;final double value2;value2 2.0;final Value value3 new Value(1);value3.v 4;}
} 上面的例子中我们先来看一下main方法中的几个final修饰的数据在给value1赋初始值之后我们无法再对value1的值进行修改final关键字起到了常量的作用。从value2我们可以看到final修饰的变量可以不在声明时赋值即可以先声明后赋值。value3时一个引用变量这里我们可以看到final修饰引用变量时只是限定了引用变量的引用不可改变即不能将value3再次引用另一个Value对象但是引用的对象的值是可以改变的从内存模型中我们看的更加清晰 上图中final修饰的值用粗线条的边框表示它的值是不可改变的我们知道引用变量的值实际上是它所引用的对象的地址也就是说该地址的值是不可改变的从而说明了为什么引用变量不可以改变引用对象。而实际引用的对象实际上是不受final关键字的影响的所以它的值是可以改变的。
另一方面我们看到了用final修饰成员变量时的细微差别因为final修饰的数据的值是不可改变的所以我们必须确保在使用前就已经对成员变量赋值了。因此对于final修饰的成员变量我们有且只有两个地方可以给它赋值一个是声明该成员时赋值另一个是在构造方法中赋值在这两个地方我们必须给它们赋初始值。
最后我们需要注意的一点是同时使用static和final修饰的成员在内存中只占据一段不能改变的存储空间。 2、修饰方法参数 前面我们可以看到如果变量是我们自己创建的那么使用final修饰表示我们只会给它赋值一次且不会改变变量的值。那么如果变量是作为参数传入的我们怎么保证它的值不会改变呢这就用到了final的第二种用法即在我们编写方法时可以在参数前面添加final关键字它表示在整个方法中我们不会实际上是不能改变参数的值
public class FinalTest {/* ... */public void finalFunc(final int i, final Value value) {// i 5; 不能改变i的值// v new Value(); 不能改变v的值value.v 5; // 可以改变引用对象的值}
} 3、修饰方法 第三种方式即用final关键字修饰方法它表示该方法不能被覆盖。这种使用方式主要是从设计的角度考虑即明确告诉其他可能会继承该类的程序员不希望他们去覆盖这个方法。这种方式我们很容易理解。
下面这段话摘自《Java编程思想》第四版第143页 “使用final方法的原因有两个。第一个原因是把方法锁定以防任何继承类修改它的含义第二个原因是效率。在java的早期实现中如果将一个方法指明为final就是同意编译器将针对该方法的所有调用都转为内嵌调用。当编译器发现一个final方法调用命令时它会根据自己的谨慎判断跳过插入程序代码这种正常的调用方式而执行方法调用机制将参数压入栈跳至方法代码处执行然后跳回并清理栈中的参数处理返回值并且以方法体中的实际代码的副本来代替方法调用。这将消除方法调用的开销。当然如果一个方法很大你的程序代码会膨胀因而可能看不到内嵌所带来的性能上的提高因为所带来的性能会花费于方法内的时间量而被缩减。在最近的Java版本中不需要使用final方法进行这些优化了。“ 因此如果只有在想明确禁止该方法在子类中被覆盖的情况下才将方法设置为final的。 注类的private方法会隐式地被指定为final方法。 4、修饰类 当用final修饰一个类时表明这个类不能被继承。也就是说如果一个类你永远不会让他被继承就可以用final进行修饰。final类中的成员变量可以根据需要设为final但是要注意final类中的所有成员方法都会被隐式地指定为final方法。 注在使用final修饰类的时候要注意谨慎选择除非这个类真的在以后不会用来继承或者出于安全的考虑尽量不要将类设计为final类。 上面我们讲解了final的四种用法然而对于第三种和第四种用法我们却甚少使用。这不是没有道理的从final的设计来讲这两种用法甚至可以说是鸡肋因为对于开发人员来讲如果我们写的类被继承的越多就说明我们写的类越有价值越成功。即使是从设计的角度来讲也没有必要将一个类设计为不可继承的。Java标准库就是一个很好的反例特别是Java 1.0/1.1中Vector类被如此广泛的运用如果所有的方法均未被指定为final的话它可能会更加有用。如此有用的类我们很容易想到去继承和重写他们然而由于final的作用导致我们对Vector类的扩展受到了一些阻碍导致了Vector并没有完全发挥它应有的全部价值。 三、深入理解final关键字 在了解了final关键字的基本用法之后这一节我们来看一下final关键字容易混淆的地方。 1、类的final变量和普通变量有什么区别 当用final作用于类的成员变量时成员变量注意是类的成员变量局部变量只需要保证在使用之前被初始化赋值即可必须在定义时或者构造器中进行初始化赋值而且final变量一旦被初始化赋值之后就不能再被赋值了。 那么final变量和普通变量到底有何区别呢下面请看一个例子
public class Test {public static void main(String[] args) {String a hello2; final String b hello;String d hello;String c b 2; String e d 2;System.out.println((a c));System.out.println((a e));}/*** Output:* true* false*/
} 大家可以先想一下这道题的输出结果。为什么第一个比较结果为true而第二个比较结果为fasle。这里面就是final变量和普通变量的区别了当final变量是基本数据类型以及String类型时如果在编译期间能知道它的确切值则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方相当于直接访问的这个常量不需要在运行时确定。这种和C语言中的宏替换有点像。因此在上面的一段代码中由于变量b被final修饰因此会被当做编译器常量所以在使用到b的地方会直接将变量b 替换为它的 值。而对于变量d的访问却需要在运行时通过链接来进行。想必其中的区别大家应该明白了不过要注意只有在编译期间能确切知道final变量值的情况下编译器才会进行这样的优化比如下面的这段代码就不会进行优化
public class Test {public static void main(String[] args) {String a hello2; final String b getHello();String c b 2; System.out.println((a c));}public static String getHello() {return hello;}/*** Output:* false*/
} 这段代码的输出结果为false。 2、被final修饰的引用变量指向的对象内容可变吗 在上面提到被final修饰的引用变量一旦初始化赋值之后就不能再指向其他的对象那么该引用变量指向的对象的内容可变吗看下面这个例子
public class Test {public static void main(String[] args) {final MyClass myClass new MyClass();System.out.println(myClass.i);}
}class MyClass {public int i 0;
} 这段代码可以顺利编译通过并且有输出结果输出结果为1。这说明引用变量被final修饰之后虽然不能再指向其他对象但是它指向的对象的内容是可变的。 3、final和static 很多时候会容易把static和final关键字混淆static作用于成员变量用来表示只保存一份副本而final的作用是用来保证变量不可变。看下面这个例子
public class Test {public static void main(String[] args) {MyClass myClass1 new MyClass();MyClass myClass2 new MyClass();System.out.println(myClass1.i);System.out.println(myClass1.j);System.out.println(myClass2.i);System.out.println(myClass2.j);}
}class MyClass {public final double i Math.random();public static double j Math.random();
} 运行这段代码就会发现每次打印的两个i值都是一样的而j的值却是不同的。从这里就可以知道final和static变量的区别了。