Java的 final 关键字

本文主要探讨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被赋了值(或者说实例化,以下都用赋值二字代替)。
新建一个类,模拟下:

1.png

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

  • String类的所有构造方法都给valuecoder赋了值(直接或者间接
    对于直接或者间接的解释如下:

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

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.# 颜文字(ฅ´ω`ฅ)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • 如果觉得文章对你有帮助,请点喜欢并关注,这将是我最大的动力,谢谢 final属于修饰符,他可以修饰类,方法和变量 ...
    光哥很霸气阅读 671评论 1 9
  • Advanced Language Features 知识点:一. static修饰符 static修饰符可以用来...
    风景凉阅读 438评论 0 0
  • Win7下如何打开DOS控制台? a:开始--所有程序--附件--命令提示符 b:开始--搜索程序和文件--cmd...
    逍遥叹6阅读 1,590评论 4 12
  • 今天主动去找了老师申请做实验。还记得老师的惊讶,也能感受到他的期许,希望自己不要让她失望吧。 师兄说,有机化学很重...
    沙砾_Grit阅读 206评论 0 0
  • Jpa是非常简单好用的ORM框架,基于Hibernate。参考:Spring Boot中的事务管理Spring D...
    小程有话说阅读 1,925评论 0 0