博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
final关键字总结
阅读量:5034 次
发布时间:2019-06-12

本文共 5879 字,大约阅读时间需要 19 分钟。

1、被final修饰的类不能被继承

经典案例就是java.lang.String类

public final class String implements java.io.Serializable, Comparable
, CharSequence

还有一些常见的类也是被final所修饰的,如下:基本类型对应的包装类型(如java.lang.Integer、java.lang.Long等)、字符相关类(java.lang.StringBuilder、java.lang.StringBuffer)、系统类(java.lang.Class、java.lang.System)等。

是Java语法所规定,如此设计的目的:类不需要被拓展、实现细节不允许改变,估计是为了安全考虑。

2、被final修饰的方法不能被重写

另外,不能被重写或者覆盖的方法还有:构造方法(也是静态方法)、静态方法、私有方法(子类中不可见,所以不允许重写)

同样是Java语法规定,不允许改变方法的实现。

静态方法为何不可以被子类重写?

静态方法的调用是编译器编译时静态绑定的,而实例方法的调用是在运行时动态绑定的;静态方法无法像实例方法那样在运行时动态确定方法的实现,所以静态方法的复写没有意义。

另外,因为二者的调用的方式不同,所以子类中不可以声明和父类中方法同名的静态方法;即:静态方法不能隐藏实例方法,二者只能存在其一,否则会存在歧义(子类调用方法时,不知该调用哪个方法)!

但是 静态方法可以被子类的同名静态方法隐藏(实际这时,这已经是两个不相关的方法)

因为调用方式一致,不会像上面造成歧义,虽然父类和子类都定义了同样的函数,但是编译器会根据对象的静态类型激活对应的静态方法的引用,造成了重写的假象,实则不是重写!

附加Java多态的实现

package com.learn.pra06;public class ClassReference {    static class Person {        @Override        public String toString(){            return "I'm a person.";        }        public void eat(){            System.out.println("Person eat");        }        public void speak(){            System.out.println("Person speak");        }    }    static class Boy extends Person{        @Override        public String toString(){            return "I'm a boy";        }        @Override        public void speak(){            System.out.println("Boy speak");        }        public void fight(){            System.out.println("Boy fight");        }    }    static class Girl extends Person{        @Override        public String toString(){            return "I'm a girl";        }        @Override        public void speak(){            System.out.println("Girl speak");        }        public void sing(){            System.out.println("Girl sing");        }    }    public static void main(String[] args) {        Person boy = new Boy();        Person girl = new Girl();        System.out.println(boy);        boy.eat();        boy.speak();        System.out.println(girl);        girl.eat();        girl.speak();    }}

首先看看方法表在内存的模型: 

 

通过看Girl和Boy方法表可以看出继承的方法从头到尾开始排列,并且方法引用在子类的中都有固定索引,即都有相同的偏移量;若子类重写父类某个方法,就会使子类方法表原先存父类的方法引用变成重写后方法的引用,到这就应该理解为什么可以根据对象类型而调用到正确的方法,关键就在于方法表。 

总体流程就是:编译器将类编译成class文件,其中方法会根据静态类型从而将对应的方法引用写入class中,运行时,JVM会根据INVOKEVIRTUAL 所指向的方法引用在方法区找到该方法的偏移量,再根据this找到引用类型真实指向的对象,访问这个对象类型的方法表,根据偏移量找出存放目标方法引用的位置,取出这个引用,调用这个引用实际指向的方法,完成多态!

 

以上均参考 https://blog.csdn.net/dawn_after_dark/article/details/74357049

3、被final修饰的变量不能被“改变”

1)被final修饰的变量不像static那样,它也可以修饰局部变量。

2)被final修饰的变量一定要被初始化,否则编译不通过。

初始化有两种:直接在变量定义时初始化 和 在构造函数中初始化(每个构造函数都要初始化即每个实例化对象的入口都要进行初始化)。

3)被final修饰的基本类型变量,它的值是不可变的。被final修饰的引用类型变量,它的引用地址是不可变的,对象里的内容是可变的。

4、在匿名内部类中使用外部方法的局部变量(也可能是函数的参数形式)时,该变量必须被final修饰。

1)原因:匿名内部类里面使用外部方法中的局部变量时,其实就是内部类的对象在使用它,内部类对象生命周期中都可能调用它,而内部类对象试图访问外部方法中的局部变量时,外部方法的局部变量很可能已经不存在了(方法执行完,局部变量便从栈中弹出,生命周期结束不复存在了),那么就得延续其生命,拷贝到内部类中,而拷贝会带来不一致性,从而需要使用final声明保证一致性。

==》为了解决内部类实例与外部方法局部变量生命周期不同的问题,匿名内部类备份了变量,为了解决备份变量引起的修改后没有同步修改的问题,外部变量需要被定义成final  ==》 匿名内部类使用final不是怕修改,是怕不能同步修改。

2)匿名内部类使用外部类的成员变量是不需要是final的。 因为内部类本身都会含有一个外围了的引用(外围类.this),所以回调的时候一定可以访问到

private Animator createAnimatorView(final View view, final int position) {    MyAnimator animator = new MyAnimator();    animator.addListener(new AnimatorListener() {        @Override        public void onAnimationEnd(Animator arg0) {            Log.d(TAG, "position=" + position);         }    });    return animator;}

内部类回调里访问position的时候createAnimatorView()早就执行完了,position如果不是final的,回调的时候肯定就无法拿到它的值了,因为局部变量在函数执行完了以后就被回收了。

以上参考 https://www.cnblogs.com/DarrenChan/p/5738957.html

附加 通过反编译查看传递给匿名内部类的参数

public interface MyInterface {    void doSomething();}public class TryUsingAnonymousClass {    public void useMyInterface() {        final Integer number = 123;        System.out.println(number);        MyInterface myInterface = new MyInterface() {            @Override            public void doSomething() {                System.out.println(number);            }        };        myInterface.doSomething();        System.out.println(number);    }}

我们进行反编译,结果是:

class TryUsingAnonymousClass$1 implements MyInterface {    private final TryUsingAnonymousClass this$0;    private final Integer paramInteger;    TryUsingAnonymousClass$1(TryUsingAnonymousClass this$0, Integer paramInteger) {        this.this$0 = this$0;        this.paramInteger = paramInteger;    }    public void doSomething() {        System.out.println(this.paramInteger);    }}

可以看到:外部类的实例引用 this$0 和外部方法局部变量 number 都作为构造方法的参数传入了匿名内部类。

5、final变量与普通变量有什么区别,什么时候可以相等?

public class FinalTest2 {    public static void main(String[] args) {        final String str1 = "test";        final String str2 = getContent();        String str3 = "test";                String str4 = str1 + "";        String str5 = str2 + "";                System.out.println(str3 == str4);        System.out.println(str3 == str5);    }    public static String getContent(){        return "test";    }}

输出后的结果为true和false。这是为什么呢?

如果是final修饰直接定义的字符串或者是基本类型,它在编译期间就会确定其值,则编译器会把它当做常量,放在常量池中。所以当有使用到它的地方会直接用常量替换(str1 str3 str4都是常量池中的同一个常量)。而其他都是运行时才会确定的值,所以依然使用变量去计算。在代码中str2变量,虽然用是final修饰但是它的值要在的运行时才能确定,所以它相当于普通变量。而str5的生成,因为str2是普通变量,所以str5会通过StringBulider去计算整个表达式的值,返回一个新的String,引用地址变了。所以第12行的输出为false;

6、final与finally 和finalize的区别

finally是异常处理语句结构的一部分,表示最终执行。

 

finalize是Object类的一个析构方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。

当垃圾回收器将要释放无用对象的内存时,先调用该对象的finalize()方法。如果在程序终止前垃圾回收器始终没有执行垃圾回收操作,那么垃圾回收器将始终不会调用无用对象的finalize()方法。在Java的Object基类中提供了protected类型的finalize()方法,因此任何Java类都可以覆盖finalize()方法,通常,在析构方法中进行释放对象占用的相关资源的操作。

Java 虚拟机的垃圾回收操作对程序完全是透明的,因此程序无法预料某个无用对象的finalize()方法何时被调用。如果一个程序只占用少量内存,没有造成严重的内存需求,垃圾回收器可能没有释放那些无用对象占用的内存,因此这些对象的finalize()方法还没有被调用,程序就终止了。

程序即使显式调用System.gc()或Runtime.gc()方法,也不能保证垃圾回收操作一定执行,也就不能保证对象的finalize()方法一定被调用

当垃圾回收器在执行finalize()方法的时候,如果出现了异常,垃圾回收器不会报告异常,程序继续正常运行。

参考:https://www.cnblogs.com/yuanfy008/p/8021673.html

转载于:https://www.cnblogs.com/genggeng/p/10034048.html

你可能感兴趣的文章
Codeforces Round #167 (Div. 1 + Div. 2)
查看>>
Oracle 关闭正在执行的job
查看>>
Istio调用链埋点原理剖析—是否真的“零修改”分享实录(下)
查看>>
Question 关于js正则的问题,why????????????????
查看>>
day04 python_selenium
查看>>
关于 hover 时候闪烁的问题
查看>>
加密学教程(Cryptography Tuturials)文件夹
查看>>
90后大学生卖煎饼月收入4k
查看>>
MongoDB的查询索引
查看>>
u-boot简单学习笔记(二)——AR9331 uboot.lds分析
查看>>
文件操作:根据现有类生成所需要的类
查看>>
pdf中内嵌字体问题
查看>>
Debian搭建WordPress
查看>>
Xstream 解析xml文件内容
查看>>
JavaScript正则表达式方法总结
查看>>
pub/sub的实际应用总结
查看>>
多媒体标签
查看>>
TCP的应用编程服务器端重要笔记
查看>>
【题解】 Codeforces 919F A Game With Numbers(拓扑排序+博弈论+哈希)
查看>>
分布式技术追踪 2018年第二十一期
查看>>