final修饰符

这个也是个基础鸭😝😝😝

Posted by 华仔 on February 20, 2019
final 接口 方法 变量 形参
使用范围 ×

final放在Interface上时

final的作用范围介绍

在Java中,final可以修饰类、方法、变量(成员变量以及局部变量)、还有方法的形参。

修饰类

当用final修饰一个类,表示这个类不可以被其他类继承,也称之为:终态类。

继承一个被final修饰的类

final修饰在类上一般出于安全用于不需要被继承的类中,例如String类

修饰方法

使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。 –《Java编程思想》第四版

修饰方法

报错信息

被final修饰后的方法的差异:

  • 被final修饰后的方法,子类不能重写此方法
  • 被final修饰后的方法,子类可以重载此方法
  • 被final修饰后的private方法,子类可以声明相同的方法(同名、同参),与父类无关

注:类中被private修饰的方法会隐式指定为final方法

修饰变量

当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。

被final修饰的成员变量表示常量,只能被赋值一次,赋值后不能再改变。

final修饰成员变量时的初始方式

  1. 在变量声明时赋值
  2. 在构造方法中赋值
public class A {
    
    //在变量声明时赋值
    final String str = "Hello World";

    public void test() {
        //被final修饰的成员变量不能在方法中赋值
        //str = "Other"; //Cannot assign a value to final variable "str"
    }
}
public class A {
    final String str;

    public A() {
        //在构造方法中赋值
        str = "Hello World";
    }
}
public class A {
    final String str;

    public A() {
        str = "Hello World";
    }

    public A(String str2) {
        //不能存在未赋值的构造函数,构造函数中必须为final常量修饰
        str = "Hello World2"; //必须
    }
}

修饰方法的形参

public class A {
    void test(final int i) {
        //i = 10; //当形参被final修饰时,表示此形参为只读的 只能使用,不能修改
        System.out.println(i);
    }

    void invoke() {
        int a = 10;
        test(a); //形参被final修饰 实参可以传入普通变量
    }
}

被final修饰的变量和普通变量的区别

public class A {
    public static void main(String[] args) {
        String a = "123";
        String b = "12";
        final String c = "12";

        String x = b + "3";
        String y = c + "3";
        System.out.println(a == y); //true
        System.out.println(a == x); //false
        //由于变量c被final修饰,因此会被当做编译器常量,所以在使用到c的地方会直接将变量c替换为它的值。而对于变量b的访问却需要在运行时通过链接来进行。
    }
}

只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化。

public class A {
    public static void main(String[] args) {
        String a = "123";
        String b = "12";
        final String c = getC();

        String x = b + "3";
        String y = c + "3";
        System.out.println(a == y); //false
        System.out.println(a == x); //false
        //不要以为某些数据是final就可以在编译期知道其值,通过变量c我们就知道了,在这里是使用getC()方法对其进行初始化,他要在运行期才能知道其值。
    }

    static String getC() {
        return "12";
    }
}

example:

public class A {
    public static void main(String[] args) {
        StringBuilder str = new StringBuilder("hello");
        test(str);
        str.append("xixi");
        System.out.println(str.toString());
    }

    static void test(final StringBuilder str) {
		//str = new StringBuilder("123"); //final修饰引用类型的参数,不能再让其指向其他对象,但是对其所指向的内容是可以更改的。
        str.append("456");
        System.out.println(str.toString());
    }
}

结果:
	hello456
	hello456xixi

example2:

public class A {
    public static void main(String[] args) {
        StringBuilder str = new StringBuilder("hello");
        test(str);
        System.out.println(str.toString());
    }

    static void test(StringBuilder str) {
        str = new StringBuilder("123");
        str.append("456");
        System.out.println(str.toString());
    }
}

结果:
	123456
	hello

java采用的是值传递,对于引用变量,传递的是引用的值,也就是说让实参和形参同时指向了同一个对象,因此让形参重新指向另一个对象对实参并没有任何影响。