• Java 基础 final vs static


    总结

    共同点,都可以修饰类,方法,属性。而不同点:

    static

    • 含义:表示静态或全局,被修饰的属性和方法属于类,可以用类名.静态属性 / 方法名 访问
    • static 类:只可能是静态内部类!!!否则编译不通过!!Java规定,顶级类不能声明为static!!! 参考-> 静态内部类 vs 非静态内部类 
    • static 方法:只能被static方法覆盖;方法内部的不能使用“外部的non-static变量”,但可以在方法体中,定义non-static局部变量使用。
    • static 属性:也就是类的变量,是在类加载时被创建并进行初始化,只会被创建一次。static修饰的变量可以重新赋值。
    • static 代码块:可以修饰。修饰的代码块表示静态代码块。当JVM加载类时,就会执行该代码块,只会被执行一次
    • 互斥关键字:static方法中不能用this和super关键字

    final

    • 含义:表示一旦创建不可改变
    • final 类:不能被继承,没有子类,final类中的方法默认是final的
    • final 属性:必须在声明的同时赋值,或在该类的构造方法中赋值,不可以重新赋值。但是,可以调用对象方法修改属性值。对基本类型来说是其值不可变;对引用变量来说其引用不可变,即不能再指向其他的对象。
    • final 方法:不能被子类重写;final不能用于修饰构造方法;private类型的方法默认是final类型的

    理解final和static修饰成员变量加载过程例子:

    import java.util.Random;
     
    public class TestStaticFinal {
     
    	public static void main(String[] args) {
    		StaticFinal sf1 = new StaticFinal();
    		StaticFinal sf2 = new StaticFinal();
    		
    		System.out.println(sf1.fValue == sf2.fValue);//打印false
    		System.out.println(sf1.sValue == sf2.sValue);//打印true。因为static字段属于类,和实例对象无关。
    	}
    }
     
    class StaticFinal {
    	
    	final int fValue = new Random().nextInt();
    	static int sValue = new Random().nextInt();
    	
    }
    

    final数据

    ref: https://www.cnblogs.com/fangpengchengbupter/p/7858510.html; https://www.cnblogs.com/xiaoxiaoyihan/p/4974273.html

    final关键字表示的不可变的。下面讨论final关键字使用的三种场合:数据、方法以及类。

    1、final属性

        程序中经常需要用到一些“常数”。常数主要应用于两个方面:

    • 编译期常数,永远不会改变
    • 在运行期初始化一个值,不希望它发生改变。

        对于编译期的常数,计算可以在编译期间提前执行,可以将常数值直接用于程序中。Java中,这种常数必须是基本数据类型。前置关键字final声明。定义时必须提供一个值

    class Person {
        final String name;  // name未初始化,编译出错
    }

        如果对对象句柄使用final,final会将句柄变成一个常数。进行声明时,必须将句柄初始化到一个具体的对象,而且不能将句柄指向另一个对象。

    复制代码
    class Person {
        String name = "张三";
    }
    
    public class FinalDemo {
    
        public static void main(String[] args) {
            final Person p = new Person();
            p = new Person();   // Error:无法为最终变量p分配值
        }
    }
    复制代码

    然而,对象本身是可以修改的。

    复制代码
    class Person {
        String name = "张三";
    }
    
    public class FinalDemo {
        public static void main(String[] args) {
            final Person p = new Person();
            p.name = "萧萧弈寒";
        }
    }
    复制代码

    一个可能的结果:

    name = fd1,i1 = 0, i2 = 6

    name = fd2,i1 = 8, i2 = 6

    i1,i2是在运行期间随机产生的数据。

    2、空白final

        Java1.1允许创建“空白final”,它们属于特殊字段。尽管被声明为final,但是却未得到一个初始值。即便如此,空白final还是必须在使用之前得到初始化。 示例:

    复制代码
    class Person {}
    
    public class FinalDemo {
        final int i;
        final Person p;
    
        FinalDemo() {
            i = 1;
            p = new Person();
        }
    
        FinalDemo(int x) {
            i = x;
            p = new Person();
        }
    
        public static void main(String[] args) {
            FinalDemo fd = new FinalDemo();
        }
    }
    复制代码

    现在强行要求对final进行赋值处理,要么在定义字段时使用一个表达式,要么在每个构造器中

    3、用final修饰参数

    java中也会将参数列表中的参数以声明的方式指明为final。这意味着你无法改变参数所指向的对象。例如:

    复制代码
    package com.bupt.java.test;
    
    class Gizmo{
        public void spin(String temp){
            System.out.println(temp+" Method call Gizmo.spin()");
        }
    }
    public class FinalArguments {
        void with(final Gizmo g){
            //g = new Gizmo(); //不能对终态局部变量 g 赋值。它必须为空白,并且不使用复合赋值
        }
        
        void without(Gizmo g) {
            g = new Gizmo();
            g.spin("without");
        }
        
    //    void f(final int i){
    //        i++;
    //    }不能对终态局部变量 i 赋值。它必须为空白,并且不使用复合赋值
        
        int g(final int i){
            return i + 1;
        }
        public static void main(String[] args){
            FinalArguments bf = new FinalArguments();
            bf.without(null);
            bf.with(null);
            System.out.println("bf.g(10)="+bf.g(10));
        }
    }
    复制代码

    运行结果:

    without Method call Gizmo.spin()
    bf.g(10)=11

    final方法

    final方法主要有两个方面的作用:一种是防止任何继承类覆盖方法。若希望一个方法的行为在继承期间保持不变,不可被覆盖和改写,就可以采取这种做法。另一种是提高程序执行的效率。将一个方法设成final后,编译器就会忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。它会用方法主体内实际代码的一个副本来替换方法调用。这样可以避免方法调用时的系统开销。若方法体太大,可能效率也得不到提升。

    复制代码
    class Human {
        public final void show() {
            //...
        }
    }
    public class Man extends Human{
        public void show() {}   //Cannot override the final method from Human
    }
    复制代码

        类内所有的private方法都自动成为final。由于不能访问一个private方法,所以它绝对不会被覆盖。

    final类

    如果整个类都是final,就表明这个类不允许被继承。或者出于安全方面的理由,不希望进行子类化。除此之外,或许还考虑执行效率的问题,确保涉及这个类各对象的所有行动都要尽可能地有效。

    final class Human {
    }
    
    public class Man extends Human{ // The type Man cannot subclass the final class Human
    }

    注意:数据成员既可以是final,也可以不是。无论类是否被定义成final,应用于final的规则同样适用于数据成员。

    将类定义成final后,结果只是禁止被继承。由于禁止了继承,所以一个final类中的所有方法都默认是final。

  • 相关阅读:
    HTTP协议基础
    MySQL必知必会总结(二)
    MySQL必知必会总结(一)
    微信小程序开发总结
    从零开始搭建物联网平台(8):邮箱通知服务
    使用CDN优化首页加载速度
    Django+Vue前后端分离项目的部署
    Docker命令
    Django中间件执行流程和CSRF验证
    golang 快速排序及二分查找
  • 原文地址:https://www.cnblogs.com/frankcui/p/10808900.html
Copyright © 2020-2023  润新知