本文主要探讨Java final 关键字修饰变量时的用法。

!!!!文末有彩蛋!!!!

1.修饰类

  当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

  在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。

2.修饰方法

  下面这段话摘自《Java编程思想》第四版第143页:

  “使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“

  因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。

  注:类的private方法会隐式地被指定为final方法。

注:以上1,2点从浅析Java中的final关键字引用

3.修饰变量

1.思考,参考String类中几个final修饰的全局变量。

private final byte[] value;
private final byte coder;
private static final long serialVersionUID = -6849794470754667710L;
static final boolean COMPACT_STRINGS;

可以明显看到,只有serialVersionUID 被赋了值(或者说实例化,以下都用赋值二字代替)。

新建一个类,模拟下:



对于bc都会提示错误,即"还没有被初始化",然而String类中的变量却没有报错,原因在于:

  • String类的所有构造方法都给valuecoder赋了值(直接或者间接

    对于直接或者间接的解释如下:



    2个参数的构造方法并没有直接给b赋值,但调用了给b赋值的另一个无参的构造方法,也不会报错(也可以在类初始化的过程中给b赋值)
  • 必须有且仅有一个static代码块给COMPACT_STRINGS赋值,多次赋值会出错,如下图:

2.回顾,听说final修饰的变量不可变?

答案为肯定,但不可变却可以再继续讨论。

不可变可以简单的解释为:

  • final修饰的基本类型不能再被赋值;
public class Demo {
private final int b = 1;
public void testInt(String[] args) {
b = 2;//很显然此处会报错
}
}
  • final修饰的对象与数组,不能再指向新的对象(数组),但是属性(索引的值)可以改变;
public class Demo {
public void testObject() {
final User user = new User();
user.setName("111");//本行与下行不会报错,对象指向不能改变,但是属性可以改变
user.setName("222");
user = new User();//user不能 = new xx,也不能 = user02
}
public void testArray() {
final int[] array = new int[]{1,2,3,4,5,6};
array = new int[]{1,2,3,4,5,6};//报错
array[1] = 2;//正常
}
}
class User{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

3.深入,final还能带来什么?

  • 对于编译器,如下栗子:
public class Demo04 {
public static void main(String[] args) { String a = "a";
String b = "b";
String ab_add = a + b;
String ab_new = "ab";
System.out.println(ab_add == ab_new);//输出为 false final String c = "c";
String cd_add = c + "d";
String cd_new = "cd";
System.out.println(cd_add == cd_new);//输出为 true
}
}

为什么上面指向的不是一个对象,下面指向的是一个对象:因为下面的cfinal修饰后,与常量"d"一样,被编译器当成常量,所以

String cd_new = "cd"; 指向的是已经存在的"cd"

  • 对于虚拟机,如下栗子:
public class Demo05 {

    static {
Demo05 demo05 = new Demo05();
}
Demo05() {
System.out.println("a = "+a+" b = "+b);
}
public static void main(String[] args) { } static int a = 123;
static final int b = 456;
}

分析:

1.按照正常的类加载顺序,应该是先加载静态代码与变量(按照前后顺序),然后是成员变量与构造方法。

2.对于上方代码,static加载后,遇到类的构造方法,导致需要去加载构造方法(static相关暂停,在加载完次构造方法后,继续加载),此时a还没有被赋实际值,暂为0。所以输出时,a为0。

3.对于b,由于被final修饰导致先被赋上实际值,所以输出不为0。

对String类的采访!

Q:听说你被final修饰的事情,大家都知道了?(●’◡’●)ノ

A: ( •́ .̫ •̀ )是啊。但这是原因的,你听我解释~这并不丢人!!

Q:哦?( ‘-ωก̀ )。

A:.巴拉巴拉、解释中

Q:你的值真的不能修改吗?

A:当然啦,除非。。。(•ิ_•ิ)

Q:快说,除非什么? (..•˘_˘•..)

A:reflect。。。。溜了溜了~(/゚Д゚)

画风突变------>“代码如下”:

public class Demo04 {
public static void main(String[] args) {
String str = "1234";
Field field = getField("java.lang.String","value");
field.setAccessible(true);//不写报错:Demo04 cannot access a member of class java.lang.String
// (in module java.base) with modifiers "private final"
byte[] value = {};
try {
value = (byte[])field.get(str);
}catch (Exception e) {
e.printStackTrace();
}
value[0] = 'a';
value[1] = 'b';
System.out.println(str);
//输出为:ab34
}
/**
* Description:指定类名,指定属性名,获取属性
* @param className,fieldName
* @return field
*/
public static Field getField(String className, String fieldName) {
try {
//获得类名
Class c = Class.forName(className);
//获得类对象
Object object = c.getConstructor().newInstance();
//获得指定属性
Field field = object.getClass().getDeclaredField(fieldName);
return field;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

至此,本文结束

本文参考:

1.# 浅析Java中的final关键字

2.# Java虚拟机类加载机制——案例分析

3.# 菜鸟学Java(十五)——Java反射机制(二)

4.# 颜文字(ฅ´ω`ฅ)

Java的 final 关键字的更多相关文章

  1. JAVA面向对象-----final关键字

    JAVA面向对象-–final关键字 1:定义静态方法求圆的面积 2:定义静态方法求圆的周长 3:发现方法中有重复的代码,就是PI,圆周率. 1:如果需要提高计算精度,就需要修改每个方法中圆周率. 4 ...

  2. 聊聊Java的final关键字

    Java的final关键字在日常工作中经常会用到,比如定义常量的时候.如果是C++程序员出身的话,可能会类比C++语言中的define或者const关键字,但其实它们在语义上差距还是挺大的. 在Jav ...

  3. java之final关键字

    final关键字(可以读不可以写.只读) 1.final的变量的值不能够被改变 ①.final的成员变量 ②.final的局部变量(形参) //意思是“实参”一旦传进我的方法里面,就不允许改变 2.f ...

  4. Java的final关键字详解

    Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使 ...

  5. java中final 关键字的作用

    final 关键字的作用 java中的final关键字可以用来声明成员变量.本地变量.类.方法,并且经常和static一起使用声明常量. final关键字的含义: final在Java中是一个保留的关 ...

  6. Java基础 -- final关键字

    在java的关键字中,static和final是两个我们必须掌握的关键字.不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优化程序的结构.下面我们来了解一下final ...

  7. Java中final关键字修饰变量、方法、类的含义是什么

    Java中的关键字final修饰变量.方法.类分别表示什么含义? 先看一个简单的介绍 修饰对象 解释说明 备注 类 无子类,不可以被继承,更不可能被重写. final类中的方法默认是final的 方法 ...

  8. java浅析final关键字

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. ...

  9. 关于Java中final关键字的详细介绍

    Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使 ...

  10. java基础---->final关键字的使用

    这里介绍一些java基础关于final的使用,文字说明部分摘自java语言规范.心甘情愿这四个字,透着一股卑微,但也有藏不住的勇敢. Final关键字的说明 一.关于final变量规范说明 .A fi ...

随机推荐

  1. JDK 注解详解

    注解用途 我们在使用spring MVC框架时用到了很多的注解,如:@Controller.@RequestMapping等等,spring框架通过反射获取到标签进行不同的逻辑处理.注解是代码的附属信 ...

  2. 两台Linux系统之间传输文件

    用CRT分别连上两台需要传输文件的linux系统服务器,并检查防火墙是否关闭. 查看防火墙状态: /etc/init.d/iptables status 若防火墙启用,暂时关闭防火墙: /etc/in ...

  3. lnmp1.4环境下thinkphp3.2配置pathinfo模式

    1.打开php.ini 通常该文件在 /usr/local/php/etc/php.ini vi /usr/local/php/etc/php.ini 找到 cgi.fix_pathinfo,默认为0 ...

  4. 【zabbix】自定义监控项key值

    说明: zabbix自带的默认模版里包括了很多监控项,有时候为了满足业务需求,需要根据自己的监控项目自定义监控项,这里介绍一种自定义监控项的方式. 1,首先编写自定义监控脚本,本文以监控httpd进程 ...

  5. macOS Sierra上ssh免密码登录linux服务器

    1.生成私钥文件 在客户端终端下输入以下命令 ssh-keygen -t rsa 每次执行 ssh-keygen -t rsa 产生的私钥文件都会不同 如果文件"~/.ssh/id_rsa& ...

  6. 国内NLP的那些人那些会

    统计学和语言学专家都列在一起了,没有区分.1,黄昌宁,1937年生于广东,1955年考入清华大学电机系,1961年毕业并留校任教至博士生导师, 1983-1984年赴美国耶鲁大学进修,1986-198 ...

  7. 前端调试利器——BrowserSync

    此处记录一下踩过的坑 之前看的这个地址:http://www.browsersync.cn/ 也就是 BrowserSync的官网上面关于代理服务器的例子不管怎么试都不行 请看下例子 browser- ...

  8. 160726 smarty 笔记(1)

    模板里面显示变量:1.变量要写在Smarty标记之间,变量名以$开头<{$test}>2.支持所有类的数据,包括数组(关联数组),对象关联数组在模板显示的时候,除了可以使用php语法之外, ...

  9. 对Java CAS的一些了解(正在整理学习中)

    ①引言 在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题 ...

  10. 多线程中sleep和wait的区别,以及多线程的实现方式及原因,定时器--Timer

    1.  Java中sleep和wait的区别 ① 这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类. sleep是Thread的静态类方法,谁调用的谁去睡觉,即 ...