依据上下文环境,java的keywordfinal也存在着细微的差别,但通常指的是“这是无法改变的。”不想改变的理由由两种:一种是效率,还有一种是设计。因为两个原因相差非常远,所以关键子final可能被吴用。

接下来介绍一下使用到fianl的三中情况:数据,方法,类。

final数据

   很多编程语言都有某种方法,来向编译器告知一块数据是恒定不变的。有时数据的恒定不变是非常实用的,比如:

1,一个编译时恒定不变的常量

2,一个在执行时初始化,而你不希望它被改变。

对于编译期常量的这样的情况,编译器能够将该常量值代入不论什么可能用到它的计算式中,也就是说,能够在编译期就执行计算式,这减轻了一些执行时的负担。在java中,这类常量必须是基本类型,而且以final表示。在对这个常量定义时,必须进行赋值。

一个即是static又是fianl的域仅仅占一段不能改变的存储空间。

当final应用于对象引用时,而不是基本类型时,其含义有些让人疑惑。对基本类型使用fianl不能改变的是他的数值。而对于对象引用,不能改变的是他的引用,而对象本身是能够改动的。一旦一个final引用被初始化指向一个对象,这个引用将不能在指向其它对象。java并未提供对不论什么对象恒定不变的支持。这一限制也通用适用于数组,它也是对象。

以下的事例示范fianl域的情况。注意,依据惯例,即是static又是fianl的域(即编译器常量)将用大写表示,并用下划切割个单词:

package reusing;

//: reusing/FinalData.java
// The effect of final on fields.
import java.util.*;
import static net.mindview.util.Print.*;

class Value {
int i; // Package access
public Value(int i) { this.i = i; }
}

public class FinalData {
private static Random rand = new Random(47);
private String id;
public FinalData(String id) { this.id = id; }
// Can be compile-time constants:
private final int valueOne = 9;
private static final int VALUE_TWO = 99;
// Typical public constant:
public static final int VALUE_THREE = 39;
// Cannot be compile-time constants:
private final int i4 = rand.nextInt(20);
static final int INT_5 = rand.nextInt(20);
private Value v1 = new Value(11);
private final Value v2 = new Value(22);
private static final Value VAL_3 = new Value(33);
// Arrays:
private final int[] a = { 1, 2, 3, 4, 5, 6 };
public String toString() {
return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;
}

public static void main(String[] args) {
FinalData fd1 = new FinalData("fd1");
//! fd1.valueOne++; // Error: can't change value
fd1.v2.i++; // Object isn't constant!
fd1.v1 = new Value(9); // OK -- not final
for(int i = 0; i < fd1.a.length; i++)
fd1.a[i]++; // Object isn't constant!
//! fd1.v2 = new Value(0); // Error: Can't
//! fd1.VAL_3 = new Value(1); // change reference
//! fd1.a = new int[3];
print(fd1);
print("Creating new FinalData");
FinalData fd2 = new FinalData("fd2");
print(fd1);
print(fd2);
}
}

/* Output:
fd1: i4 = 15, INT_5 = 18
Creating new FinalData
fd1: i4 = 15, INT_5 = 18
fd2: i4 = 13, INT_5 = 18
*/

因为valueOne和VALUE_TWO都是带有编译时数值的fianl基本类型,所以它们二者均能够用作编译期常量,而且没有重大差别。VALUE_THREE是一种更加典型的对常量进行定义的方式:定义为public,能够被不论什么人訪问;定义为static,则强调仅仅有一份;定义为fianl,这说明它是个常量。请注意带有恒定初始值(即,编译期常量)的final static基本类型全用大写字母命名,而且字母与字母之间用下划线隔开。

我们不能由于某些数据是fianl的就觉得在编译时能够知道它的值。在执行时使用随机数来初始化i4和INT_5的值叫说明了这一点。事例部分也展示了将fianl数据定义为static和非static的差别。此差别仅仅有当数值在执行时内被初始化时才会显现,这是由于在编译器对编译时的数值一视同仁(而且他们可能由于优化而消失)。当执行时会看见这个差别。请注意,在此fd1和fd2中i4的值是唯一的,每次都会被初始化为15,13。INT_5的值是不能够通过创建第二个FinalData对象加以改变的。这是由于他是static的,在装载类时(也就是第一次创建这个类对象时)已经被初始化,而不是每次创建都初始化。

假设看上面的事例来理解我标记颜色的的部分有点困难的话,请看以下的事例:

public class B3 {
static Random r =new Random(12);
final int int1= r.nextInt(100);//产生0-99的随机数
static final int INT_2= r.nextInt(100);

public static void main(String[] args) {
B3 b1=new B3();
System.out.println("int1:"+b1.int1+" INT_2:"+b1.INT_2);
B3 b2=new B3();
//b2.INT_2=100;//错误的赋值
System.out.println("int1:"+b2.int1+" INT_2:"+b2.INT_2);

}

}

启动main()先运行的是B3 b1=new B3();,创建B3的第一个对象,这将会先初始化static final int INT_2= r.nextInt(100);,然后是初始化final int int1= r.nextInt(100);,所以第一条输出语句的结果是int1:12    INT_2:66。接下来创建B3的第二个对象,这也会导致B3类中成员的初始化,但static final int INT_2= r.nextInt(100);不会在被初始化,为什么前面已经提过。输出的结果是int1:56    INT_2:66。两次的输出INT_2的值都是一样的。

在说回我们的第一个事例,V1到VAL_3说明final引用的意义。正如在main()方法中看见的,能够改变对象数组a的值,但不能将a的引用指向还有一个对象。看起来使基本类型成为fianl比引用类型成为final的用处大。

java或许生成"空白final",所谓空白final是指被声明为final但又未给初值的域。不管什么情况下编译器都会保证final域在使用前初始化。但空白final在fianl的使用上提供了非常大的灵活性,为此,一个fianl域能够依据某些对象有所不同,却又保持恒定不变的特性。以下的事例说明了一点。

class Poppet {
private int i;
Poppet(int ii) { i = ii; }
}

public class BlankFinal {
private final int i = 0; // Initialized final
private final int j; // Blank final
private final Poppet p; // Blank final reference
// Blank finals MUST be initialized in the constructor:
public BlankFinal() {
j = 1; // Initialize blank final
p = new Poppet(1); // Initialize blank final reference
}
public BlankFinal(int x) {
j = x; // Initialize blank final
p = new Poppet(x); // Initialize blank final reference
}
public static void main(String[] args) {
new BlankFinal();
new BlankFinal(47);
}
} //

final 參数

      java中或许将參数列表中的參数以声明的方式声指明为final。这意味着你无发改变參数所指向的对象。

class Gizmo {
public void spin() {}
}

public class FinalArguments {
void with(final Gizmo g) {
//! g = new Gizmo(); // Illegal -- g is final
}
void without(Gizmo g) {
g = new Gizmo(); // OK -- g not final
g.spin();
}
// void f(final int i) { i++; } // Can't change
// You can only read from a final primitive:
int g(final int i) { return i + 1; }
public static void main(String[] args) {
FinalArguments bf = new FinalArguments();
bf.without(null);
bf.with(null);
}
} //

方法f()g()展示了基本类型的參数被指定为final是所出现的结果:你能够读參数,但不能改动參数。这一特性仅仅要用来向匿名内部类传递数据。

final 方法

使用final方法有两个原因。第一个原因是把方法锁定,以防止不论什么继承它的类改动它的含义。这是出于设计的考虑:想要确保在继承中使用的方法保持不变,而且不会被覆盖。

过去建议使用final方法的第二个原因是效率。在java的早期实现中,假设将一个方法指明为fianl,就是允许编译器将针对该方法的全部调用都转为内嵌调用。当编译器发现一个final方法调用命令时,它会依据自己的慎重推断,跳过插入程序代码这样的正常的调用方式而运行方法调用机制(将參数压入栈,跳至方法代码处运行,然后跳回并清理栈中的參数,处理返回值),而且以方法体中的实际代码的副本来取代方法调用。这将消除方法调用的开销。当然,假设一个方法非常大,你的程序代码会膨胀,因而可能看不到内嵌所带来的性能上的提高,由于所带来的性能会花费于方法内的时间量而被缩减。

上面标颜色的地方不太懂。不知道那位看过Java编程思想和知道的高人给解释解释。

在最进的java版本号中,虚拟机(特别是hotspot技术)能够探測到这些情况,并优化去掉这些效率反而减少的额外的内嵌调用,因此不再须要使用final方法来进行优化了。其实,这样的做法正逐渐受到劝阻。在使用java se5/6时,应该让编译器和JVM去处理效率问题,仅仅有在想明白禁止覆盖式,才将方法设置为fianl的。

final和privatekeyword

类中的全部private方法都是隐式的制定为final的。因为你无法訪问private方法你也就无法覆盖它。能够对private方法加入final修饰词,但这毫无意义。

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()");
}
}

"覆盖"仅仅有在某方法是基类接口的一部分时才会发生。即,必须将一个对象向上转型为它的基类并条用同样的方法。假设某方法是private的,它就不是基类接口的一部分。它仅是一些隐藏于类中的程序代码,假设一个基类中存在某个private方法,在派生类中以同样的名称创建一个public,protected或包訪问权限方法的话,该方法仅仅只是是与基类中的方法有同样的名称而已,并没有覆盖基类方法。由于private方法无法触及且有非常好的隐藏性,所以把它看成是由于他所属类的组织结的原因而存在外,其它不论什么事物都不用考虑。

final 类

当将类定义为final时,就表明了你不打算继承该类,并且也不或许别人这样做。换句话说,出于某种考虑,你对该类的设计永不须要做不论什么变动,或者出于安全的考虑,你不希望他有子类。

class SmallBrain {}

final class Dinosaur {
int i = 7;
int j = 1;
SmallBrain x = new SmallBrain();
void f() {}
}

//! class Further extends Dinosaur {}
// error: Cannot extend final class 'Dinosaur'

public class Jurassic {
public static void main(String[] args) {
Dinosaur n = new Dinosaur();
n.f();
n.i = 40;
n.j++;
}
}

请注意,final类的域能够依据个人的意愿选择是或不是final。不论类是否被定义为final,相同的规则相同适用于定义为final的域。然而,由于final是无法继承的,所以被final修饰的类中的方法都隐式的制定为fianl,由于你无法覆盖他们。在fianl类中能够给方法加入final,但这不会产生不论什么意义。

java final keyword的更多相关文章

  1. 【笔试题】Java final keyword

    Java 知识测试 Java final keyword Question 1 What is the use of final keyword in Java? A. When a class is ...

  2. Summary: Final Keyword

    In this tutorial we will learn the usage of final keyword. final keyword can be used along with vari ...

  3. Java Final and Immutable

    1. Final keyword Once a variable X is defined final, you can't change the reference of X to another ...

  4. 深入java final关键字

    Java final关键字详解:https://blog.csdn.net/kuangay/article/details/81509164 深入java final关键字 用法注意点和JVM对其进行 ...

  5. 又一次认识java(七) ---- final keyword

    你总以为你会了,事实上你仅仅是一知半解. final 关键字概览 final关键字可用于声明属性.方法.參数和类,分别表示属性不可变.方法不可覆盖.參数不可变和类不能够继承. 我们来分别看看它的使用方 ...

  6. Java final修饰符

    final的定义: 在英文层面上,final的意思是"最后的","最终的"意思,在Java中也同样表示出此种含义. final的运用对象: final适用于修饰 ...

  7. Java final 修饰符知识点总结

    final从字面上理解含义为“最后的,最终的”.在Java中也同样表示出此种含义. final可以用来修饰变量(包括类属性.对象属性.局部变量和形参).方法(包括类方法和对象方法)和类. 1. fin ...

  8. Java final方法

    之所以要使用final方法,可能是出于对两方面理由的考虑.第一个是为方法"上锁",防止任何继承类改变它的本来含义.设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖 ...

  9. Java final自变量

    Java 1.1 允许我们将自变量设成final 属性,方法是在自变量列表中对它们进行适当的声明.这意味着在一个方法的内部,我们不能改变自变量句柄指向的东西.如下所示: /** * Created b ...

随机推荐

  1. 在ajax当中使用url重写来避免url的暴露

    记得一次面试,有这样一道面试题:jsp页面当中需要用到ajax的实现,此时需要调用java的url:此时的问题是如果用户查看页面源码就能看到真是的url,这个问题如何避免.说实话,AJAX我用的只是皮 ...

  2. 递归-快速排序quickSort

    现在对“6  1  2 7  9  3  4  5 10  8”这个10个数进行排序.首先在这个序列中随便找一个数作为基准数.为了方便,就让第一个数6作为基准数吧.接下来,需要将这个序列中所有比基准数 ...

  3. python获取对象信息

    获取对象信息 拿到一个变量,除了用 isinstance() 判断它是否是某种类型的实例外,还有没有别的方法获取到更多的信息呢? 例如,已有定义: class Person(object): def ...

  4. Django 数据库查询优化

    Django数据层提供各种途径优化数据的访问,一个项目大量优化工作一般是放在后期来做,早期的优化是“万恶之源”,这是前人总结的经验,不无道理.如果事先理解Django的优化技巧,开发过程中稍稍留意,后 ...

  5. Servlet高级应用---Servlet与缓存

    一]设置缓存文件的有效日期        重点方法:            HttpServletRequest类:                    1>String getRequest ...

  6. VisualStudio替换所有空行

    [一篮饭特稀原创,转载请注明出自http://www.cnblogs.com/wanghafan/p/3371620.html] Ctrl+Shift+H 查找内容\r\n\r\n   //如果要替换 ...

  7. Best Practices for Using Alpha

    Alpha是图形界面开发中常用的特效,通常我们会使用以下代码来实现Alpha特效: view.setAlpha(0.5f); View.ALPHA.set(view, 0.5f); ObjectAni ...

  8. Android 使用LinearLayout.getChildAt(i)获取一个线性布局的view,并实现content中实现方法

    1.定义接口content的方法,如ok,cancle; 2.在View的处理类myview中实现content的方法. 3.通过contently.getChildAt(i)的方法获得View v; ...

  9. c标准库和运行时库

    c运行时库与c标准库的区别 c标准库包括常用的数学函数.字符串操作函数等等,这些函数都是由编程高手写的,效率高,很少出错,而且是完全符合c语言标准的函数. c运行库可以说是c标准库的扩展集,它是完全包 ...

  10. C++ Prime:范围for语句

    C++11新标准引入了一种更简单的for语句,这种语句可以遍历容器或者其他序列的所有元素.范围for语句的语法形式是: for( declaration : expression) statement ...