java面试 final、static关键字

一、final

根据程序上下文环境,java中的final关键字有无法修改的、最终形态的含义。它可以修饰非抽象类、非抽象成员方法和变量。
final关键字修饰的类不能被继承、没有子类,其类中的方法也默认是final的。
final修饰的方法不能被子类中的方法覆盖,但是可以被继承。
final修饰的成员变量表示常量,只能被赋值一次,且赋值后值就不再改变。
final不能用于修饰构造方法。
值得注意的一点是:父类中的private私有方法是不能被子类方法覆盖的,因此,private类型的方法默认是final类型的。

1. final类

如上说明,final类不能被继承,因此其内的成员方法也不能被覆盖,默认都是final的。我们在设计一个类的时候,如果不需要有子类,类的实现细节不允许改变,且能够确信这个类不会被再次扩展,那么就可以将这个类设计为final类。例如String类就是final类,代码如下:

public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence

我们不能继承或者重写String类,而能直接使用该类。

2. final方法

我们下面使用子类继承的方式来演示final修饰符在实际中修饰方法的应用
testfinal.java

public class testfinal {
    public void method1() {
        System.out.println("This is method1");
    }
    //不能被改变的方法,此方法无法被子类覆盖
    public final void method2() {
        System.out.println("This is final method2");
    }
    public void method3() {
        System.out.println("This is method3");
    }
    //私有方法,不能被子类覆盖,也不能被子类继承
    private void method4() {
        System.out.println("This is private method4");
    }
}

keywordfinal.java

public class keywordfinal extends testfinal {
    //对于父类中的method1方法进行了覆盖
    public void method1() {
        System.out.println("This is keywordfinal's method1");
    }
    
    public static void main(String[] args) {
        keywordfinal keywordfinal = new keywordfinal();
        keywordfinal.method1();
        keywordfinal.method2();
        keywordfinal.method3();
        //keywordfinal.method4();//父类中的private方法,子类无法继承和覆盖
    }
}

执行结果为

This is keywordfinal's method1
This is final method2
This is method3

通过上述演示的结果,我们可以发现,在父类中声明的final方法,无法在子类覆盖
编译器在遇到final方法的时候,会转入内嵌机制,这种方式可以大大提高代码的执行效率。

3. final变量(常量)

使用final修饰符修饰的变量,用于表示常量,因为值一旦给定,就无法改变!
可以使用final修饰的变量有三种:静态变量、实例变量和局部变量,这三种类型分别可以代表三种类型的常量。
我们可以在类中使用PI值的时候,将其声明为常量,这样就可以在整个运行过程中,值都不会改变。
在声明final变量的时候,可以先声明,不给定初始值,这种变量也可以称之为final空白。无论什么情况,编译器都必须确final空白在被使用之前初始化值。但是这种方式也提供了更大的灵活性,我们可以实现,一个类中的final常量依据对象的不同而有所不同,但是又能保持其恒定不变的特征。
下面的代码对于上面的说明进行了具体实现:

public class Finalword {
    private final String finalS = "finalS";
    private final int finalI = 100;
    public final int finalIntB = 90;
    
    public static final int staticfinalC = 80;
    private static final int staticfinalD=70;
    
    public final int finalIntE;//final空白,必须要在初始化对象的时候给定初始值,如果声明为静态变量,必须要给定初始值。
    public Finalword(int e) {
        this.finalIntE = e;
    }
    
    //public Finalword() {}//在类中有未给定值的final常量时,无法声明不给定初始值的构造方法,会提示finalIntE未初始化。
    
    public static void main(String[] args) {
        Finalword finalword = new Finalword(60);
        //finalword.finalI = 101;//提示值已分配错误,final变量的值一旦给定,无法进行改变
        //finalword.finalIntB = 91;//提示值已分配错误,final变量的值一旦给定,无法进行改变
        //finalword.staticfinalC = 81;//提示值已分配错误,final变量的值一旦给定,无法进行改变
        //finalword.staticfinalD = 71;//提示值已分配错误,final变量的值一旦给定,无法进行改变
        
        System.out.println(finalword.finalI);
        System.out.println(finalword.finalIntB);
        System.out.println(finalword.staticfinalC);//不推荐使用实例的方式调用静态常亮,推荐使用类名.常量名的方式,例如Finalword.staticfinalC
        System.out.println(finalword.staticfinalD);//不推荐使用实例的方式调用静态常亮,推荐使用类名.常量名的方式,例如Finalword.staticfinalD
        System.out.println(Finalword.staticfinalC);
        System.out.println(Finalword.staticfinalD);
        
        //System.out.println(Finalword.finalIntE);//无法调用非静态变量
        System.out.println(finalword.finalIntE);
        
        Finalword finalword2 = new Finalword(50);
        System.out.println(finalword2.finalIntE);//final空白变量finalIntE可以根据实例化时给定值的不同而不同
    }

    private void testMethod() {
        final int a;//final空白,在需要的时候才赋值 
        final int b = 4;//局部常量--final用于局部变量的情形 
        final int c;//final空白,一直没有给赋值.
        a = 3; 
        //a=4;//出错,已经给赋过值了. 
        //b=2;//出错,已经给赋过值了. 
    }
}

4. final参数

当函数的参数为final类型时,在方法内部可以读取和使用该参数,但是无法改变值

public class FinalWord2 {
    public void method1(final int i) {
        //i++;//提示值已初始化错误,final修饰的参数的值不允许改变
        System.out.println(i);
    }
    public static void main(String[] args) {
        new FinalWord2().method1(5);
    }
}

2. static

static表示有“全局”或者“静态”的意思,用来修饰成员变量和成员方法,可以形成静态static代码块,但是目前在java语言中并没有全局变量的概念。

被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它并不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,java虚拟机就能根据类名在运行时数据区的方法区内找到静态内容。也是因此,static修饰的对象可以在他的任何对象创建之前访问,无需引用任何对象。

使用public修饰的static成员变量和成员方法本质上就是全局变量和全局方法,当声明其类的对象时,不生成static变量的副本,而是类的所有实例共享同一个static变量。

static变量前的权限修饰,影响static的可调用范围,如果使用private修饰,则表示该变量可以在类的静态代码块中,或者类的其他静态成员方法中调用,也可以用于非静态成员方法,但是不能在其他类中通过类名直接引用。因此static是不需要实例化就可以使用,而权限控制前缀只是限制其使用范围。

调用静态成员或者静态方法的方式也很简单,可以直接使用类名来访问,语法如下:

类名.静态方法名(参数)
类名.静态变量名

使用static修饰的代码块就是静态代码块,当java虚拟机(JVM)加载类的时候,就会执行该代码块。

1. static变量

类中变量根据是否使用static进行修饰,可以分为两种:
一种是使用static修饰的,成为静态变量或者类变量
另一种是没有使用static修饰的,成为实例变量

静态变量在内存中只有一个拷贝(节省内存),JVM中只为静态变量分配一次内存,在类加载的过程中就完成了对于静态变量的内存分配,可以使用类名直接访问,也可以使用实例化后的对象进行访问(这种方式不推荐)。
实例变量是每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中存在多个拷贝,且互不影响,相较于静态变量来讲更为灵活。

2. 静态方法

静态方法使用类名可以直接进行调用,任何实例也可以直接调用,因此静态方法中不能使用this和super关键字,不能直接访问所属类的实例变量和实例方法(指的是不使用static修饰符修饰的方法和变量),只能访问所属类中的静态成员变量和静态成员方法。这个问题主要是因为实例成员对象同特定的对象关联,而静态方法同具体的实例无关。

因为static方法独立于任何对象,所以就要求static方法必须被实现,且不能是抽象abstract的。

3. static代码块

static代码块也成为静态代码块,指的是类中独立于类成员的static语句块,在一个类中可以存在多个静态代码块,位置也可以随便放,它不在任何的方法体内,JVM加载类的时候会首先执行这些静态代码块,如果static代码块有多个,则JVM会根据它们在类中出现的先后顺序依次执行,每个代码块都只会被执行一次,例如:

public class StaticBlock {
    private static int a;
    private int b;
    
    static {
        StaticBlock.a = 5;
        System.out.println(a);
        
        StaticBlock staticBlock = new StaticBlock();
        staticBlock.f();
        staticBlock.b = 1000;
        System.out.println(staticBlock.b);
    }
    
    static {
        StaticBlock.a = 4;
        System.out.println(a);
    }
    
    public static void main(String[] args) {
        StaticBlock staticBlock = new StaticBlock();
        staticBlock.b = 990;
        System.out.println("This is method main()");
        System.out.println(staticBlock.b);
        System.out.println(StaticBlock.a);
    }
    static {
        StaticBlock.a = 6;
        System.out.println(a);
    }
    
    public void f() {
        System.out.println("This is method f()");
    }
}

执行结果

5
This is method f()
1000
4
6
This is method main()
990
6

从上可以看出,我们可以使用静态代码块对于静态变量进行赋值。main方法也是静态的,这样JVM在运行main方法的时候可以直接调用,而不需要创建实例调用。静态变量、静态方法、静态方法块的运行都在main方法执行之前。

三、 static同final一起使用

static final用来修饰成员变量和成员方法,可以理解为“全局常量”。
对于变量,表示一旦给定初始值,就不可以修改,而且可以直接通过类名访问。
对于方法,表示不可覆盖,而且可以通过类名直接访问。

对于被static final修饰过的实例常量,实例本身不能再改变,但是对于一些容器类型,例如(ArrayList、HashMap),不可以改变容器变量本身,但是可以修改容器内存放的对象。这种特性在编程中用到很多。

例子如下:

public class TestStaticFinal {
    private static final String strStaticFinalVar = "aaa";//全局常量
    private static String strStaticVar = null;//静态变量
    private final String strFinalVar = null;//不可变常量
    private static final int intStaticFinalVar = 0;
    private static final Integer integerStaticFinalVar = new Integer(8);
    private static final ArrayList<String> arrStaticFinalVar = new ArrayList<String>();
    
    private void test() {
        System.out.println("-------------值处理前----------");
        System.out.println("strStaticFinalVar = " + strStaticFinalVar);
        System.out.println("strStaticVar = " + strStaticVar);
        System.out.println("strFinalVar = " + strFinalVar);
        System.out.println("intStaticFinalVar = " + intStaticFinalVar);
        System.out.println("integerStaticFinalVar = " + integerStaticFinalVar);
        System.out.println("arrStaticFinalVar = " + arrStaticFinalVar);
        
        //strStaticFinalVar = "新值";//错误,final变量修饰,不可变,所以不能修改
        strStaticVar = "新的静态变量值";//正确,static修饰的变量表示全局变量,可以改变值
        //strFinalVar = "新的不可变值";//错误,final变量修饰,在使用前必须给出初始值,null值也算,且该初始值给定之后就不可修改。
        //intStaticFinalVar = 1;//错误,final变量修饰,不可变,所以不能修改
        //integerStaticFinalVar = new Integer(9);//错误,final变量修饰,不可变,所以不能修改
        arrStaticFinalVar.add("item1");//正确,容器变量本身没有变化,变的是其内部的存放内容。该方法使用范围较为广泛
        arrStaticFinalVar.add("item2");//正确,容器变量本身没有变化,变的是其内部的存放内容。该方法使用范围较为广泛
        
        System.out.println("-------------值处理后----------");
        System.out.println("strStaticFinalVar = " + strStaticFinalVar);
        System.out.println("strStaticVar = " + strStaticVar);
        System.out.println("strFinalVar = " + strFinalVar);
        System.out.println("intStaticFinalVar = " + intStaticFinalVar);
        System.out.println("integerStaticFinalVar = " + integerStaticFinalVar);
        System.out.println("arrStaticFinalVar = " + arrStaticFinalVar);
    }
    public static void main(String[] args) {
        new TestStaticFinal().test();
    }
}

执行结果

-------------值处理前----------
strStaticFinalVar = aaa
strStaticVar = null
strFinalVar = null
intStaticFinalVar = 0
integerStaticFinalVar = 8
arrStaticFinalVar = []
-------------值处理后----------
strStaticFinalVar = aaa
strStaticVar = 新的静态变量值
strFinalVar = null
intStaticFinalVar = 0
integerStaticFinalVar = 8
arrStaticFinalVar = [item1, item2]

通过上面的例子可以总结得出,使用final修饰的变量给定初始值之后就不可以改变了,但是使用final修饰的容器在不改变容器本身的情况下,修改容器内部存储的内容,这个改动是允许的。

参考:http://lavasoft.blog.51cto.com/62575/18771/

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

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,581评论 18 399
  • Advanced Language Features 知识点:一. static修饰符 static修饰符可以用来...
    风景凉阅读 434评论 0 0
  • Java关键字final、static使用总结 一、final 根据程序上下文环境,Java关键字final有“这...
    孑孖阅读 377评论 0 0
  • final final类 final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。...
    吃瓜群众liu阅读 325评论 0 1
  • Java关键字final 在设计程序时,出于效率或者设计的原因,有时候希望某些数据是不可改变的。这时候可以使用fi...
    狮_子歌歌阅读 734评论 1 4