美文网首页java技术基础
java面试 final、static关键字

java面试 final、static关键字

作者: 步积 | 来源:发表于2017-03-28 13:50 被阅读34次

    一、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/

    相关文章

      网友评论

        本文标题:java面试 final、static关键字

        本文链接:https://www.haomeiwen.com/subject/pwadottx.html