码迷,mamicode.com
首页 > 编程语言 > 详细

java 对 final 关键字 深度理解

时间:2019-08-09 01:38:23      阅读:109      评论:0      收藏:0      [点我收藏+]

标签:注意   个人   线程   不能被继承   并发   vat   nal   深度   xtend   

基础理解 :

  1.修饰类

    当用final去修饰一个类的时候,表示这个类不能被继承。处于安全,在JDK中,被设计为final类的有String、System等,这些类不能被继承 。注意:被修饰的类的成员可以是final修饰,可可以不是 。

  2.修饰方法 :

     方法不能被子类重写。常用在类设计时不希望被子类重写而修饰。

  3.修饰方法参数 :

     被修饰 的参数变量,不能在方法体内再次被赋值。这个好像是站在调用者的角度考虑的哈,就好像有个大佬拿了把菜刀给我,叫我去看人 ,大佬说,你一定要用这把菜刀去砍,不要给换了屠龙刀,也不要换了把机关枪,不然你就死定了 。总没感觉这个这个final修饰参数的价值在哪 ,不知道理解到位没有。

  4.修饰成员变量。

      被修饰的成员变量,只能被赋值一次 。一定会在类初始化之前被赋值。

深理解,及使用

  主要运用修饰成员变量的一些特性(个人觉得这方面用的比较多 )在java中的使用.

  1.利用final修饰的成员变量,必须在类初始化赋值,设计类的继承使用 。

    场景 : 在设计类A的时候有个字段field 是必须的。但是这个类可能会有很多的子类,而每个子类的值是不一样的 。那么就可以设计这个fileld 用final 修饰。在子类初始化时候赋值。如一下代码,我假设人的名字名字都是必须的,设计Person类,但是每个人的名字又不同,于是用final修饰字段name,让name在构造方法中赋值 。那么继承Person的子类必须调用 Person 构造方法给name赋值 。这么做有个缺陷就是会失去无参构造方法的使用。

public class Person {
    final String name ;   // 不赋值,就必须在构造方法中赋值。
    public Person(String name){
        this.name = name;
    }
    public void worlk() {

    }
}
public class Student extends Person {

    public Student(String name) {
        super(name); //必须调用父类的构造器
    }
    
    
    @Override
    public String toString() {
        return "Student [name=" + name + "]";
    }
    public static void main(String[] args) {
        Student s = new Student("A");
        Student s1 = new Student("B");
        System.err.println(s); // 输出: Student [name=A]
        System.err.println(s1);// 输出: Student [name=B]
    }
}

  2.final 关键字对并发编程的特殊意义,代码出自《java多线程编程指南》

    

public class FinalFieldExample {
  final int x;
  int y;
  static FinalFieldExample instance;

  public FinalFieldExample() {
    x = 1;
    y = 2;
  }

  public static void writer() {
    instance = new FinalFieldExample();
  }

  public static void reader() {
    final FinalFieldExample theInstance = instance;
    if (theInstance != null) {
      int diff = theInstance.y - theInstance.x;
      // diff的值可能为1(=2-1),也可能为-1(=0-1)。
      print(diff);
    }
  }

  private static void print(int x) {
    // ...
  }
}

  以上并发编程中就会有安全隐患,当然在事件中也不会有这种代码设计 。书中只是用来做列子参考。在编译器初始化对象的时候,其实不是原子操作,(针对有成员对象)。以上代码在初始化 FinalFieldExample 的时候,等效一下的伪代码:

 objRef = allocate (FinalFieldExample.class) ;//子操作1:分配对象所需的存储空间 
  objRef.x=1;//子操作2:对象初始化
  objRef.y=2://子操作3:对象初始化
  instance =objRef;//子操作4:将对象赋值给引用变量

 

 但是有时候编译器,会优化代码,重编译 。对变量y的赋值在对象初始化完成之后,也就是,有可能第三步,会在第四步之后之前。这样可能在第四步执行完成之这一刻,别的线程调用了这个对象,那么这个y的值是默认初始值0,而不是期待值2。但是变量X的值是可以保证的,因为被final修饰 ,必须在FinalFieldExample  初始化之前被赋值 ,可以保证步骤2不会被优化排序到步骤4之后,而保证对其他线程的可见。

  所有以上代码,y也有final修饰之后,可以保证调用线程拿到期望值  y = 2; x = 1;

 

3. 设计安全对象:不可变对象。

 

  待更新...

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

   

java 对 final 关键字 深度理解

标签:注意   个人   线程   不能被继承   并发   vat   nal   深度   xtend   

原文地址:https://www.cnblogs.com/jonrain0625/p/11324676.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!