当前位置:   article > 正文

细说final的的四种用法-----修饰类,修饰方法,常量,修饰参数 及内部类与final_final 修饰类

final 修饰类

final关键字用于修饰类

public final class 类名称 {

}
一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写,java中典型的final修饰的类 String类

作用:使当前这个类不能有任何子类

说明:当用final修饰一个类时,表明这个类不能被继承。

final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。

final关键字用于修饰成员方法

修饰符 final 返回值类型 方法名称(参数列表) {
方法体
}
当final关键字用来修饰一个方法的时候,这个方法就是最终方法,不能够被覆盖重写
对于类、方法来说,abstract关键字和final关键字不能同时使用,因为作用相互矛盾

使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。

在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“
  因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。
  注:类的private方法会隐式地被指定为final方法。

需要注意的一点是:因为重写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时访问控制权限为private,将会导致子类中不能直接继承到此方法,因此,此时可以在子类中定义相同的方法名和参数,此时不再产生重写与final的矛盾,而是在子类中重新定义了新的方法。

public class B extends A {
 
    public static void main(String[] args) {
 
    }
 
    public void getName() {
        
    }
}
 
class A {
 
    /**
     * 因为private修饰,子类中不能继承到此方法,因此,子类中的getName方法是重新定义的、
     * 属于子类本身的方法,编译正常
     */
    private final void getName() {
        
    }
 
    /* 因为pblic修饰,子类可以继承到此方法,导致重写了父类的final方法,编译出错
    public final void getName() {
    
    }
    */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

final关键字用于修饰局部变量

// ① 第一种基本数据类型情况下的格式
final 基本数据类型 数据名称 = 值;
// ② 引用数据类型情况下的格式
final 类型 对象名 = new 类名();
//例如:final Student stu = new Student();
当final关键字用于修饰局部变量的时候,这个局部变量就不能更改
对于 基本类型 来说,不可改变指的是变量当中的数据不可改变,但是对于 引用类型 来说,不可改变的指的是变量当中的地址值不可改变

final关键字用来修饰成员变量

对于成员变量来说,如果使用了final关键字修饰,那么这个变量也照样是不可变的
① 由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了
② 对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值,只能二选一
③ 必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值

final修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。

用final关键字修饰对象变量,只是不允许这个对象引用再指向其他的对象,但是这个引用所指向的对象的内容是可以改变的
比如一个经典的例子:

final StringBuffer sb = new StringBuffer(“HelloWorld”);
sb = new StringBuffer(“Hello”); // 编译失败,不能修改sb引用的指向
sb.append(“China”); // sb指向的对象可以被修改。

其实从final的位置就看出来了,他是在修饰引用类型变量sb,而不是在修饰堆中的实例对象(new StringBuffer(“HelloWorld”);)

final与方法内部类

什么是方法内部类

内部类的几种创建方法:
1、成员内部类

class Outer{
private int i = 1;
class Inner{
public void fun() {System.out.println(“Outer I=” + i)}
}
}

2、方法内部类

class Outer{
public void fun() {
final int i = 1; // 被方法内部类访问的局部变量必须被final修饰
class Inner{ // 方法内部类 不能有访问修饰符,比如public
public void print() {System.out.println(“Method I=” + i)}
}
}
}

3、匿名内部类

interface USB {
public abstract void start()
}
class Outer{
public void fun() {
final int i = 1; // 被匿名内部类访问的局部变量必须被final修饰
new USB(){
@Override
public void start(){
System.out.println(“local_var_i=” + i);
}
}.start();
}
}

4、静态内部类

class Outer{
  private int i = 1;
  static class Inner{ // 不能访问外部类的非静态成员
    public void fun() { }
}
}

5、接口内部类

interface USB {
class Inner { // 默认是public static,即可以直接new USB.Inner();
}
}

对于方法内部类(匿名内部类)来说,因为内部类要访问所在方法中的局部变量,这时候用持有的外部类当前对象引用还能调用的到吗?

调不到了,那Java语言的设计者是这么来解决这个问题的:将局部变量复制一份给内部类使用,怎么复制的?在内部类初始化的时候通过构造方法传值的方式。
这样,内部类中会有一份复制的private修饰的成员变量。这样我就能访问了。但问题又来了,比如:

public void fun() {
  int i = 1;
  class Inner{
    // int i = 1; 由编译器生成
    publicvoid print() { i++ }
 }
  System.out.println(“i=” + i);
  // 还是会输出i=1,我们明明在内部类方法中对此变量进行++了啊。抱歉,您++的是被复制的另一份。
}

问题的引出<保持两个不同变量的一致性>:

好,问题就这么无情的出现了,怎么解决?Java又说了,要不然给局部变量加上final吧,这样就会保持值得一致性了。
ok,问题解决,这就是为什么方法内部类访问的局部变量必须被final修饰的终极原因(为了约束两个不同变量的一致性)。设计问题,挺无奈的解决方案。

来看看另一种说法:
在Java中,方法的局部变量位于栈上,对象位于堆上。
因为局部变量的范围被限制在该方法内,当一个方法结束时,栈结构被删除,该变量消失。
但是,定义在这个类中的内部类对象仍然存活在堆上,所以内部类对象不能使用局部变量。除非这些局部变量被标识为最终的。

这种说法是片面的,因为根本原因是内部类对象无法访问局部变量,才会去复制一份。
为了保证两个变量的一致性,才去使用final关键字修饰局部变量。而不是因为栈生命周期与堆生命周期不一致的问题。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/574386
推荐阅读
  

闽ICP备14008679号