static final 和final的区别

学习java的时候常常会被修饰符搞糊涂,这里总结下static final和final的区别。

static是静态修饰关键字,可以修饰变量和程序块以及类方法:

  当定义一个static的变量的时候jvm会将将其分配在内存堆上,所有程序对它的引用都会指向这一个地址而不会重新分配内存;

  当修饰一个程序块的时候(也就是直接将代码写在static{...}中)时候,虚拟机就会优先加载静态块中代码,这主要用于系统初始化;

  当修饰一个类方法时候你就可以直接通过类来调用而不需要新建对象。

final可以修饰变量、方法及类:

  当定义一个final变量时,jvm会将其分配到常量池中,程序不可改变其值;

  当修饰一个方法时,该方法在子类中将不能被重写;

  当修饰一个类时,该类不能被继承。

static变量

  按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。

  两者的区别是:

    对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。

    对于实例变量,没创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。

static方法

  静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。

  因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。

1、static 强调只有一份,final 说明是一个常量,final定义的基本类型的值是不可改变的,但是fianl定义的引用对象的值是可以改变的,下面举个例子来说明:

package DifStaticFinalAndFinal;

class SelfCounter {
    private static int counter;
    private int id=counter++;
    
    public String toString(){
        return "SelfCounter: "+id;
    }
}
package DifStaticFinalAndFinal;

class WithFinalFields {
    static final SelfCounter wffs=new SelfCounter();
    final SelfCounter wff=new SelfCounter();
    
    public String toString(){
        return "wff= "+wff+",\n wffs= "+wffs;
    }
}

主函数:

package DifStaticFinalAndFinal;

public class StaticFinal {
    public static void main(String[] args) {
        System.out.println("First Object:");
        System.out.println(new WithFinalFields());
        System.out.println("Second Object:");
        System.out.println(new WithFinalFields());
    }
}

运行结果:

First Object:
wff= SelfCounter: 1,
wffs= SelfCounter: 0
Second Object:
wff= SelfCounter: 2,
wffs= SelfCounter: 0

  分析为什么wff两次的运行结果不同,而wffs两次的运行结果相同?

    因为wffs这个容器是用static final来定义的,static 强调只有一份,因此只有一个值,而final修饰的引用是可以变化的,因此wff的值是可以变化的,这也是final修饰基本类型和引用的不同。

2、在方法中将参数指明为final时,在使用该方法时,可以读参数但是无法使用该参数。

package cn.qdu.chapter7_example;

public class FinalArguments {
    void with(final Gizmo g){
        
    }
    
    void without(Gizmo g){
        g=new Gizmo();
        g.spin();
    }
    
    int g(final int i){return i+1;}
    public static void main(String[] args) {
        FinalArguments bf=new FinalArguments();
        bf.with(null);//不管这里的参数换成什么,都是执行void(final Gizmo g)方法,无法更改参数
        bf.without(null);
        
    }
}

3、为什么使用final方法呢?

  原因有两个。

    其一是把方法锁定,确保在继承中使用方法行为不变,并且不会被覆盖;

    其二是效率,如果一个方法指明为final,就是同意编译器将针对该方法的所有调用都转为内嵌调用。 转为内嵌调用的目的是节省开销,因为编译器发现一个final方法调用命令时,会跳过程序代码这种正常方式而执行方法调用机制(将参数压入栈,跳至方法代码处并执行,然后跳回并清理栈中的参数,处理返回值),并且以方法体中的实际代码的副本来替代方法调用。但是如果一个方法很大,程序很膨胀,就会看不到内嵌带来的任何性能的提高。

4、final和private关键字

  类中所有private方法都隐式地指定为是final的,因为private关键字只能被本类调用,其他类中的方法也无法覆盖private修饰的方法,因此和加上final效果是一样的。

5、当某个类的整体定义为final时,表明该类不能被继承,方法不能被覆盖,且final类中的所有方法都隐式指定为是final的,方法声明为final后还可以有效地“关闭”动态绑定。

6、static加载类的动作只发生一次。

     最后,说了这么多,也要对final做一个客观的评价。若方法或类不想让别人来继承和修改,设定为final是明智的,但是在团体中这样会阻碍其他程序员通过你想不到的合理的途径来复用类,这样的话final方法就显得过于严苛了。

原文地址:https://www.cnblogs.com/diandianquanquan/p/10606843.html