一、final

(一)、final的使用

final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)

1. 当用final修饰一个类时,表明这个类不能被继承。
2. 当用final修饰一个方法时,表明这个方法不能被重写。
3. 当用final修饰一个变量时,表明这个变量初始化后就不能再被修改。

final可以理解为"最后的、最终的"。与类而言,是不能被继承;与方法而言,是不能被覆盖;与变量而言,是不能再修改。

参考:浅谈Java中的final关键字

使用final修饰类的原因:
1. 设计原因(Design)
从设计的角度来考虑为final类,此时final 的语义表明为:这个类不想在关系结构上做出任何的改变,也不希望有任何人可以继承自这个类,除此之外,就没有更多的限制了。
2. 效率原因(Efficiency)
这里涉及到内联机制。一个类被final修饰后,它的方法默认被修饰为final ,这时方法的内联起到作用了。会将所有对方法的调用转化为inline调用的机制,大大提高执行效率。

final的实现原理:

1. final修饰域(基本数据类型、引用类型)
在一个类中被final修饰的域会在编译时放入常量池。
在编译后得到的.class文件中,有这么一块内容,叫常量池。常量池中的确包含了常量,当然还有其他的内容。一个类中被final修饰的域在这个时候就会被放入这个大池子中。
至于为什么这么做?原因很简单,为了效率。 其实将一个基本数据类型修饰为final的目的最单纯最美好,就是希望它不要变。这样系统有就可以做一些优化操作,将这些常量值装在需要计算的过程中,让它们充当类似于宏的身份,换句话说,编译器可以在编译期间提前完成一些计算工作,省去了在运行时对于变量的相对复杂的操作。那么到这里就完成了么?其实不是的,这里要补充的一点就是一个编译期间的类文件中,常量池中的基本数据类型的常量是不知道具体的值是什么,换句话说,在文件编译过后,虽然知道一个域是常量,但是至于这个常量的具体内容是什么,此时是无从知晓的。
只有当运行时,常量才会真正的被赋值,对于static和没有static修饰的基本数据类型来说,是有差异的,差异就在于static修饰的域是在类载入的时候进行初始化的,所有实例共享同一个常量,同时Java虚拟机没有把它当作类变量,在使用它的任何类的常量池或者字节码流中直接存放的是它表示的常量值。

2. final修饰类和方法
Java的沙箱为了保证装载的类文件的安全性,会在验证阶段对字节码流做多次的验证,那么其中就包括对各个类之间的二进制兼容的检查,其中就包括,
1. 检查final的类不能拥有子类
2. 检查final的方法不能被覆盖

参考:Final of Java,这一篇差不多了

(二)、final域的内存语义

final域是基础数据类型时的重排序规则:
写final域的重排序规则:在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用对象,这个两个操作之间不能重排序。(禁止把final域的写重排序到构造函数之外)
写final域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了,而普通域不具有这个保障。
读final域的重排序规则:初次读一个包含final域的对象的引用,与随后初次读这个对象包含的final域,这两个操作之间不能重排序。(在一个线程中,初次读对象引用,与初次读该对象包含的final域,JMM禁止处理器重排序这两个操作)
读final域的重排序规则可以确保:在读一个对象的final域之前,一定会先读包含这个final域的对象的引用。

final域是引用类型时的重排序规则:(相比final域是基础数据类型的情况,增加了以下约束)
写final域的重排序规则:在构造函数内对一个final引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。

为什么final引用不能冲构造函数内“逸出”(可以看出写final域的重排序规则保证了final域的写入被限定在构造函数内执行)
在构造函数返回前,被构造对象的引用还不能为其他线程所见,因为此时的final域可能还没有被初始化。

参考:《Java并发编程的艺术》

final类型的变量可以保证在多线程发布某个对象时,这个对象的final域变量能够被正常的初始化(在写final变量后加了storestore屏障,在读final变量前加了loadload屏障),而普通类型的变量可能不会被正确的初始化,这样导致该对象在多个线程之间出现不一致的情况,这也就是我们所说的引用溢出。

参考:说说final关键字(好像有干货)

二、static

(一)、static的使用(可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法)

static可以用来修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。

1. static方法,static方法一般称为静态方法,静态方法不依赖于任何对象就可以进行访问
2. static变量,static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
static成员变量的初始化顺序按照定义的顺序进行初始化。
3. static代码块,static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。(因为只在类加载的时候执行一次,因此可以用static代码块来优化程序性能)

参考:Java中的static关键字解析

另外这里还有一个例子

class Parent {
static String name = "hello"; //非静态代码块
{
System.out.println("parent block");
} static {
System.out.println("parent static block");
} public Parent(String name) {
System.out.println("name");
System.out.println("parent constructor");
}
} class Child extends Parent {
static String childName = "hello";
{
System.out.println("child block");
}
static {
System.out.println("child static block");
} public Child() {
super("name");
System.out.println("child constructor");
}
} public class TestStatic { public static void main(String[] args) {
new Child();// 语句(*)
}
}

运行结果:

parent static block
child static block
parent block
parent constructor
child block
child constructor

分析:当执行new Child()时,它首先去看父类里面有没有静态代码块,如果有,它先去执行父类里面静态代码块里面的内容,当父类的静态代码块里面的内容执行完毕之后,接着去执行子类(自己这个类)里面的静态代码块,当子类的静态代码块执行完毕之后,它接着又去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,父类的非静态代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。子类的非静态代码块执行完毕再去执行子类的构造方法,这个就是一个对象的初始化顺序。

总结:
对象的初始化顺序:首先执行父类静态的内容,父类静态的内容执行完毕后,接着去执行子类的静态的内容,当子类的静态内容执行完毕之后,再去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,父类的非静态代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。子类的非静态代码块执行完毕再去执行子类的构造方法。总之一句话,静态代码块内容先执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。

注意:子类的构造方法,不管这个构造方法带不带参数,默认的它都会先去寻找父类的不带参数的构造方法。如果父类没有不带参数的构造方法,那么子类必须用supper关键子来调用父类带参数的构造方法,否则编译不能通过。

三、final和static在一起使用(他们同时使用时既可修饰成员变量,也可修饰成员方法。)

1. 对于成员变量,该变量一旦赋值就不能改变,该变量被类的所有实例共享,我们称它为“全局常量”。可以通过类名直接访问。
2. 对于成员方法,表示该方法不可继承和改变。可以通过类名直接访问。

对final和static的理解的更多相关文章

  1. 理解Java中的final和static关键字

    回顾这两个关键字前,先考虑一个问题: Static变量存储在JVM中的位置,或者说static变量是如何被加载的? JVM会把类的静态方法和静态变量在类加载的过程中读入方法区(Method Area) ...

  2. 深入理解final和static关键字

    深入理解final和static关键字 参考:http://blog.csdn.net/qq1028951741/article/details/53418852 final关键字 final关键字可 ...

  3. 【转】Java关键字final、static使用总结

    转自:http://lavasoft.blog.51cto.com/62575/18771/   Java关键字final.static使用总结   一.final        根据程序上下文环境, ...

  4. Java关键字final、static使用总结

    Java关键字final.static使用总结   一.final        根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类.非抽象类成员方 ...

  5. 转!Java关键字final、static使用总结

    Java关键字final.static使用总结   一.final 根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类.非抽象类成员方法和变量.你可 ...

  6. Java关键字final、static使用总结(转)

    Java关键字final.static使用总结   一.final        根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类.非抽象类成员方 ...

  7. Java:final、static关键字 详解+两者结合使用

    一  final关键字 1) 关于final的重要知识点 final关键字可以用于成员变量.本地变量.方法以及类. final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误. ...

  8. Java中static、final、static final的区别(转)

    说明:不一定准确,但是最快理解. final: final可以修饰:属性,方法,类,局部变量(方法中的变量) final修饰的属性的初始化可以在编译期,也可以在运行期,初始化后不能被改变. final ...

  9. 每天写两个的java常见面试题—final 和static 的用法

    第一次写随笔,可能写的比较乱,更多的是作为自己记忆一些知识的方式.所有记录的东西都是自己的一些理解,很多语言可能还是从其他大牛的博客里面搬过来的. 一.static的作用: static的的作用从三个 ...

随机推荐

  1. [算法总结] 20 道题搞定 BAT 面试——二叉树

    本文首发于我的个人博客:尾尾部落 0. 几个概念 完全二叉树:若二叉树的高度是h,除第h层之外,其他(1~h-1)层的节点数都达到了最大个数,并且第h层的节点都连续的集中在最左边.想到点什么没?实际上 ...

  2. kubeadm 线上集群部署(二) K8S Master集群安装以及工作节点的部署

    PS:所有机器主机名请提前设置好 在上一篇,ETCD集群我们已经搭建成功了,下面我们需要搭建master相关组件,apiverser需要与etcd通信并操作 1.配置证书 将etcd证书上传到mast ...

  3. Linux 做网关

    首先创建两张路由表,只需要添加到相应的文件中即可,Linux一共支持255个路由表,rt_tables文件中默认已经存在了三张路由表,分别是:   255  local   254  main   2 ...

  4. Python中collections模块的使用

    本文将详细讲解collections模块中的所有类,和每个类中的方法,从源码和性能的角度剖析. 一个模块主要用来干嘛,有哪些类可以使用,看__init__.py就知道 '''This module i ...

  5. iframe子页面position的fixed

    前言: 首先说一说我昨天天的苦逼经历.中午吃饭时一同事跟我说,他做的项目嵌套iframe后,子页面的position设置fixed失效了. 经过反复询问,得知他用了两层iframe,再加上最外的父页面 ...

  6. 机器装多个版本php,并安装redis插件报错【已解决】

    机器原版本php5.5.3 适应新的框架安装了7.1.12 期间遇到的小问题就是安装 redis插件的时候,总报错,报错如下: Starting php-fpm [02-Jan-2019 10:15: ...

  7. Laravel路由除了根目录全报404错误

    Route::get('hello',function(){ return 'Hello World!'; }); 在laravel/app/Http/routes.php下添加上面的语句,然后再浏览 ...

  8. 如何使用g++编译调用dll的c++代码

    本文将有以下4个部分来讲如何使用g++编译调用dll的c++代码. 1.如何调用dll 2.动态链接和静态链接的区别 3.g++的编译参数以及如何编译调用dll的c++代码 4.总结 1.如何调用dl ...

  9. mysql/mybatis之合并两个表的查询结果

    下面这段sql是把两个表中各自符合条件的count值相加,返回结果是两个之和 SELECT sum(result) FROM ( SELECT COUNT(*) result FROM TEST_A ...

  10. Notes of Daily Scrum Meeting(11.13)

    Notes of Daily Scrum Meeting(11.13) 今天邹欣老师给我们讲课大家还是很有收获的,大家课堂的参与度确实有了很大的提升,而且邹欣老师关于项目Scrum Meeting报告 ...