【Java基础】final的详细用法解读

31 篇文章 4 订阅

一. final修饰变量

1. final修饰基本数据类型变量和引用数据类型变量

  • 基本知识:被final修饰的变量,称为常量。常量的值是不可修改的。一般和static一起使用,称为静态常量。常量命名:字母全部大写

  • 数据在内存中的存储:java中的内存分为:栈内存和堆内存。特点:栈的存取速度比较快,栈的内存要小于堆内存。基本类型存储在栈空间中,引用类型 栈中的引用指向堆空间

  • 基本类型存储在栈空间中的示意图
    基本类型存储在栈空间中

  • 引用类型 栈中的引用指向堆空间示意图
    引用类型  栈中的引用指向堆空间示意图

  • 当final修饰的是一个基本数据类型时,这个数据的值再初始化后不能被改变。当final修饰的是一个引用类型数据时,也就是修饰一个对象时,引用再初始化后将永远指向一个内存地址,不可修改,但是该内存地址中保存的对象信息,是可以进行修改的。

  • final修饰基本数据类型时的内存示意图
    final修饰基本数据类型时的内存示意图

  • 在上图中,变量a在初始化后将永远指向003这块内存,而这块内存在初始化后将永远保存数值100

  • final修饰引用数据类型时的内存示意图
    final修饰引用数据类型时的内存示意图

  • 在上图中,变量p指向了0003这块内存,0003内存中保存的是对象p的句柄(存放对象p数据的内存地址),这个句柄值是不能被修改的,也就是变量p永远指向p对象,但是p对象的数据是可以修改的。

public class Test_final {
    private int n1 = 2020;  //普通变量
    private static final int n2 = 2020; //final修饰的变量
    private int age = 4;    //普通变量
    private static final double PI = 3.1415926;     //final修饰的变量
    private static final String PASSWORD = "123456";//final修饰的变量

    public static void main(String[] args) {
        Test_final t = new Test_final();
        t.test();
    }

    public void test(){
//        PI = 3; //编译错误,出现红色波浪线,不可以修改
        age = 9;
        System.out.println(age);    //9    4的值被9覆盖
        System.out.println(PI);     //3.1415926

        final User u = new User("tom",12,1001); //final修饰的引用数据类型
        u.setName("alice");
        System.out.println(u.getName());    //alice User类对象u里的名字name变量值被修改

        User u2 = new User("jack",13,1002);
//        u = u2;     //编译错误,出现红色波浪线,因为u经final修饰永远指向上面定义的u对象,不能指向u2对象
        System.out.println(u);  //Error:(22, 9) java: 无法为最终变量u分配值  这是系统提示报错的信息

        //不变性
        String a = "tom";   //字符常量,放在常量池
        a = "jack";         //常量池中有两个String对象
        String b = "tom";   //创建流程:先判断常量池是否存在tom,如果有,就直接指向
        System.out.println(a);  //jack  当重新赋值,会在内存中再次分配一块新的空间

        String s = "20200202";  //字符常量,放在常量池
        String s1 = n1 + "0202";
        String s2 = n2 + "0202";
        System.out.println(s1);     //20200202
        System.out.println(s2);     //20200202
        System.out.println(s == s1);//false
        System.out.println(s == s2);//true
        //问题1
        String s3 = "2020";
        String s4 = s3 + "0202";    //不是字符常量
        System.out.println(s == s4);//false
        //问题2
        String s5 = "2020" + "0202";//是字符常量,在编译时已经确定值
        System.out.println(s == s5);//true
        //问题3
        String s6 = "2020";
        String s7 = "0202";
        String s8 = s6 + s7;    //不是字符常量  s8不是通过双引号直接创建,通过运算得到的
        System.out.println(s == s8);//false
    }
}

class Animal{
    public final void show(){

    }
}

final class Dog extends Animal{
    //不能被重写
//    @Override
//    public void show(){
//
//    }
    public void sum(final int a, int b){
//        a = 9;  //出现红色波浪线,编译错误,不能修改
        b = 6;
        int sum = a + b;
    }
}

//Dog类不能有子类
//class YellowDog extends Dog{
//
//}

  • 上述代码中,是下面所有测试的代码,有一个User类,在上一篇博客中有,这里就不粘贴呢,还有一个Test_final类,一共两个类,可以自己粘贴代码测试。
  • 下面的代码是截取上面代码的一段
//        PI = 3; //编译错误,出现红色波浪线,不可以修改
        age = 9;
        System.out.println(age);    //9    4的值被9覆盖
        System.out.println(PI);     //3.1415926

        final User u = new User("tom",12,1001); //final修饰的引用数据类型
        u.setName("alice");
        System.out.println(u.getName());    //alice User类对象u里的名字name变量值被修改

        User u2 = new User("jack",13,1002);
//        u = u2;     //编译错误,出现红色波浪线,因为u经final修饰永远指向上面定义的u对象,不能指向u2对象
        System.out.println(u);  //Error:(22, 9) java: 无法为最终变量u分配值  这是系统提示报错的信息
  • 不难看出final修饰变量的本质:final修饰的变量会指向一块固定的内存,这块内存中的值不能改变
  • 引用类型变量所指向的对象之所以可以修改,是因为引用变量不是直接指向对象的数据,而是指向对象的引用的,所以被final修饰的引用类型变量将永远指向一个固定的对象,不能被修改。对象的数据值可以被修改。

2.被final修饰的常量在编译阶段会被放入常量池中

  • final是用于定义常量的,定义常量的好处是:不需要重复的创建相同的变量。由final修饰的变量会在编译阶段放入到调用类的常量池中。
        //不变性
        String a = "tom";   //字符常量,放在常量池
        a = "jack";         //常量池中有两个String对象
        String b = "tom";   //创建流程:先判断常量池是否存在tom,如果有,就直接指向
        System.out.println(a);  //jack  当重新赋值,会在内存中再次分配一块新的空间
  • 在上述代码中有一个字符串的不变性代码的测试:这里解读一下,字符串的值一旦确定,则不可修改,不可修改指的是指内存中的值不可修改
  • 字符串不变性示意图:
    字符串不变性示意图
  • 什么是常量池:
    String 常量: 使用双引号直接创建的字符串,称为字符常量
    1.字符常量放在内存中的常量池
    2.JDK1.8常量池和堆空间是相对独立
    3.常量池中的值不会被gc回收,垃圾回收机制不会清理该区域的内容
    4.多次出现相同字符常量,只会在常量池中创建一个String对象
		String s = "20200202";  //字符常量,放在常量池
        String s1 = n1 + "0202";
        String s2 = n2 + "0202";
        System.out.println(s1);     //20200202
        System.out.println(s2);     //20200202
        System.out.println(s == s1);//false
        System.out.println(s == s2);//true
  • 上诉代码我是在最上面的代码中截取的一段,这一段的代码运作过程是这样的。
  • 首先根据final修饰的常量会在编译期放到常量池的原则,n2会在编译期间放到常量池中
  • 然后s变量所对应的“20200202”字符串会放入到字符串常量池中,并对外提供一个引用返回给s变量
  • 这时候 拼接字符串s1,由于n1对应的数据没有放入到字符串常量池中,所以s1暂时无法拼接,需要等程序加载运行时才能确定s1对应的值
  • 但在拼接s2的时候,由于n2已经存在于常量池中,所以可以直接与“0202”拼接,拼接出的结果是“20200202”,这时系统会查看字符串常量池,发现已经存在字符串20200202,所以直接返回20200202的引用,所以s2和s指向的是同一个引用,这个引用指向的是字符串常量池中的20200202.
  • 当程序执行时,n1变量才有具体的指向
  • 当拼接s1的时候,会创建一个新的String类型对象,也就是说字符串常量池中的20200202会对外提供一个新的引用
  • 所以当s1与s用“==”判断时,由于对应的引用不同,会返回false。而s2和s指向同一个引用,返回true。

总结:这个例子想说明的是,由于被final修饰的常量会在编译期进入常量池,如果有涉及到该常量的操作,很有可能在编译期就已经完成。

		String s = "20200202";  //字符常量,放在常量池
		//问题1
        String s3 = "2020";
        String s4 = s3 + "0202";    //不是字符常量
        System.out.println(s == s4);//false
        //问题2
        String s5 = "2020" + "0202";//是字符常量,在编译时已经确定值
        System.out.println(s == s5);//true
        //问题3
        String s6 = "2020";
        String s7 = "0202";
        String s8 = s6 + s7;    //不是字符常量  s8不是通过双引号直接创建,通过运算得到的
        System.out.println(s == s8);//false
  • 上述代码,又举了几个例子,加深一下理解

二.final修饰方法

  • 被final修饰的方法,不能被重写,不让任何继承类对其进行修改
class Animal{
    public final void show(){

    }
}

final class Dog extends Animal{
    //不能被重写
//    @Override
//    public void show(){
//
//    }
}

三.final修饰类

  • 被final修饰的类,不能被继承,不能有子类
  • 当程序中有永远不会被继承的类时,或者自己写的类不想被别人继承,可以使用final关键字修饰
  • 被final修饰的类所有成员方法都将被隐式修饰为final方法
final class Dog extends Animal{
    //不能被重写
//    @Override
//    public void show(){
//
//    }
}

//Dog类不能有子类.不能被继承
class YellowDog extends Dog{

}

四.final修饰参数

  • 被final修饰的参数,不能修改
final class Dog extends Animal{
    //不能被重写
//    @Override
//    public void show(){
//
//    }
    public void sum(final int a, int b){
//        a = 9;  //出现红色波浪线,编译错误,不能修改
        b = 6;
        int sum = a + b;
    }
}

如果对你有帮助,点个赞吧0.0
若有不正之处,请多多谅解并欢迎批评指正,不甚感激

参考资料
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Final 关键字用于修饰类、变量和方法,它分别有不同的用法:1. 修饰类:当用 final 修饰一个类时,表明这个类不能被继承;2. 修饰变量:当用 final 修饰一个变量时,表明这个变量的值不能被改变;3. 修饰方法:当用 final 修饰一个方法时,表明这个方法不能被重写。 ### 回答2: 在Java中,final是一个关键字,用于修饰不可更改的类、方法和变量。 当final用于类的时候,表示这个类是不可继承的。也就是说,final修饰的类不能有子类,它是一个最终类,不能被其他类继承。 当final用于方法的时候,表示这个方法是不可重写的。也就是说,final修饰的方法不能在子类中被重写或覆盖。 当final用于变量的时候,表示这个变量是不可改变的。也就是说,final修饰的变量只能被赋值一次,并在后续的运行中不能被重新赋值。final修饰的变量通常被认为是常量,常用的命名风格是使用全部大写的字母。 使用final修饰类、方法和变量的主要目的有以下几个方面: 1. 安全性:final修饰类可以防止被其他类继承修改或重写,确保类的完整性;final修饰方法可以保证方法的逻辑不被修改,防止被子类进行重写。 2. 效率:final修饰变量可以使编译器在编译时对这个变量进行优化,使其在运行时更加高效;final修饰方法可以避免在运行时进行虚拟方法调用,提高程序的性能。 3. 可读性:final修饰变量可以增加代码的可读性,明确表示这个变量是不可更改的,便于理解和维护代码。 总之,finalJava中的用法丰富多样,可以限制类、方法和变量的行为,提高程序的安全性、效率和可读性。 ### 回答3: 在Java中,final是一个关键字,用于声明一个不可变的实体,可以应用于变量、方法和类。 对于变量,final修饰的变量称为常量,一旦被赋值后,其值则不能再次改变。常量一般使用大写字母命名,多个单词间使用下划线分隔。final修饰的变量在声明时或构造函数中被初始化,可以在编译时或运行时确定其值。常量的使用可以提高程序的可读性和可维护性。 对于方法,final修饰的方法称为最终方法,即该方法不能被子类重写。这样确保了方法的行为在继承体系中不会被改变。最终方法在类的设计中起到了一种保护的作用,可以保证类中特定行为的一致性和稳定性。 对于类,final修饰的类称为最终类,即该类不能被继承。最终类通常是具有特定功能或安全性要求的类,禁止其他类对其进行继承或修改。最终类可以避免继承带来的不确定性,保证类的稳定性和安全性。 使用final时需要注意以下几点: 1. final变量必须在声明时或构造函数中初始化,否则会报编译错误。 2. final方法不能被子类重写,但可以被继承并使用。 3. final类不能被其他类继承,但可以被其他类使用。 4. final变量的值不能被改变,但如果变量是引用类型的,其属性或内容可变。 总而言之,final的使用可以提高程序的可靠性和性能,并在设计和实现中起到一定的保护作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值