java关键字-final总结

151 阅读5分钟

Final

  1. Final变量:

    • 使用final修饰的变量表示常量,其值不能被修改。可以在声明时初始化,或在构造函数或初始化块中初始化。
    • 使用final修饰引用数据类型时,值不能修改,但引用对象的状态仍可以修改。
    • final修饰的变量可以在多线程下实现安全共享。

public final class FinalClass {
    final String text = "我是final修饰的变量";

    public final void modeText() {
        //text 是final修饰的,编译不通过,无法修改
        text = "是否能修改";
    }


    //FinalClass 被final修饰的,编译不通过,无法被继承
    class sonClass extends FinalClass {

        @Override
        //modeText是final修饰的,编译不通过,方法无法重载
        public void modeText() {
            super.modeText();
        }
    }

}

public class QuoteValue {
    public static void main(String[] args) {
        final int[] array = {1, 2, 3}; // 使用final修饰数组引用变量
        System.out.println(array[0]); // 输出: 1

        array[0] = 10; // 修改数组的第一个元素 引用类型的内容可以修改
        System.out.println(array[0]); // 输出: 10

        // array = new int[]{4, 5, 6}; // 试图将引用变量指向新的数组,编译错误
    }
}

final修饰的变量可以在多线程下实现安全共享?

是的,将变量声明为final可以在多线程环境下实现安全共享。当一个变量被声明为final时,它的值在初始化后不能被修改,这意味着多个线程可以安全地访问和读取该变量的值,而不需要额外的同步机制。

由于final变量的值不可更改,它在内存中的存储是稳定的。这样,多个线程可以在没有竞争条件的情况下同时访问该变量,因为不会出现数据不一致或意外修改的情况。

但需要注意的是,final关键字只能保证引用变量本身不被修改,而不能保证引用对象的状态不被修改。如果引用的对象是可变的,那么其内部状态仍然可能被修改。在这种情况下,需要额外的同步机制来确保对对象状态的安全访问。

总而言之,将变量声明为final可以在多线程环境下实现安全共享,但对于可变对象,仍然需要采取适当的同步措施来保证其状态的一致性。

为什么项目中很少用fianl变量解决线程安全问题?

尽管final变量可以提供线程安全性,但在实际项目中很少使用它来解决线程安全问题的原因可能有以下几点:

局限性:final关键字只能保证引用变量本身不被修改,而无法保证引用对象的状态不被修改。如果引用的对象是可变的,那么其内部状态仍然可能被修改。因此,在需要保证对象状态的一致性和线程安全性的情况下,仅使用final关键字是不够的。

可读性和可维护性:使用final关键字修饰变量可能会使代码更加冗长和复杂。这可能会降低代码的可读性和可维护性,特别是当需要在多个方法或类之间共享状态时。

同步机制的需求:在实际项目中,通常需要更复杂的同步机制来确保线程安全,例如使用synchronized关键字、使用锁(Lock)或使用并发容器(ConcurrentHashMap,ConcurrentLinkedQueue等)。这些机制提供了更灵活和细粒度的控制,以确保线程安全性。

性能考虑:在某些情况下,使用final关键字可能会带来一些性能开销。因为final变量的值在初始化后不能更改,可能会导致频繁创建新对象的情况,从而增加了垃圾回收的压力。

综上所述,尽管final变量可以提供一些线程安全性,但在实际项目中,通常需要使用更复杂的同步机制来确保线程安全和对象状态的一致性

  1. Final方法:

    • 使用final修饰的方法不能被子类重写,确保方法的实现不会被修改。
  2. Final类:

    • 使用final修饰的类不能被继承,确保类的定义不会被修改。
  3. Final参数:

    • 使用final修饰方法的参数,表示参数的值不能被修改,确保方法内部不会意外修改参数的值。

. 局部变量声明为final才能在匿名内部类中访问:

-   在匿名内部类中,如果需要访问外部方法或代码块中的局部变量,该局部变量必须声明为`final`。这是因为匿名内部类可能在外部方法或代码块执行完毕后仍然存在,并且需要保证访问的局部变量的值不会发生改变。
public class Example {

    public static void main(String[] args) {
        final int finalCount = 10;

        Runnable runnable = () -> {
            //为什么需要将局部变量声明为final才能在匿名内部类中访问? 在匿名内部类中使用count变量
            /**
             * 生命周期不一致
             * 可以试着想想: 当main方法已经执行完后, main方法的栈帧将会弹出, 如果此时Thread对象的生命周期还没有结束,
             * 还没有执行打印语句的话, 将无法访问到外部的finalCount变量.
             *
             * 数据不一致问题
             * 为什么要求这些局部变量是final的呢?这是由于Java内部实现的限制所致
             * 当创建匿名内部类时,它会持有对外部作用域的引用。如果内部类可以修改外部作用域的局部变量,
             * 那么当外部作用域的变量发生改变时,内部类可能会产生不一致的行为(解决数据不一致问题)
             * */
            System.out.println("Count: " + finalCount);
        };

        runnable.run();
    }


}
  1. Final和Static:

    • final保证程序运行期间变量不可变,static保持内存共享。

public class Test {
    public static void main(String[] args) {
        VerifyClass verifyClass1 = new VerifyClass();
        VerifyClass verifyClass2 = new VerifyClass();
        /**finalValue的作用是用来保证变量不可变,并且在整个程序运行过程中保持不变。
         *staticValue变量,它在类加载时被初始化为一个随机生成的字符串,并且在整个程序运行过程中只有一份副本。无论创建多少个VerifyClass对象,
         * 它们都共享相同的静态变量值。
         */
        System.out.println("finalValue is " + verifyClass1.finalValue);
        System.out.println("finalValue is " + verifyClass2.finalValue);

        System.out.println("staticValue is " + verifyClass1.staticValue);
        System.out.println("staticValue is " + verifyClass2.staticValue);
    }
}
class VerifyClass {
    public final String finalValue = UUID.randomUUID().toString();
    public static String staticValue = UUID.randomUUID().toString();
}