关于java中的final

深入理解Java中的final
本文详细解析了Java中final关键字的真正含义及其应用场景,纠正了一些常见的误解,并通过实例解释了final在方法参数和内部类中的作用。

我以前认为final定义变量那么这个变量就不能改变了,定义一个类时代表这个类不能有子类,定义方法时说明这个方法已经足够满足了需求,不需要被重写。。。


前几天在写Android程序的时候发现,在一个方法中的一个匿名类中如果使用了方法中的参数,那么这个参数只能定义为final。具体怎么回事也没去深究,今天看了一篇文章,基本能够理解了为什么会这样。

 

 

 

从jdk1.0到今天,JAVA技术经过十余年的发展,技术上已经发生了巨大的变化.但final变量的定义从它
诞生那天起,就没有发生任何变化,也就是这十多年它就一直表示它原来的意思.

但遗憾的是,经过十多年仍然有90%的人没有理解它的真实含义,也没有一篇文章,包括我所见到的所有介绍
JAVA的书籍(包括TKJ)都没有说清楚,我相信肯定有些作者是理解的,但没有一个作者向读者说清楚.而中国网友
大多数人被一篇胡说八道的<<浅谈Java中final,finalized,finally>>的文章跑过马.(脸红一下:当初我也是).

final变量的定义本身并不复杂,就是变量一经初始化就不能再指向其它对象.在c++中它是一个const指针,而
不是指向const变量的指针,const指针的意思是说它只能一直指向初始化时的那个地址.但那个地址中对象本身
是可以修改的.而指向const变量的指针是说所指对象本身是不能修改的.

如:final StringBuffer sb = new StringBuffer("Axman");
sb = new StringBuffer("Sager");//错误,sb不能再指向其它对象.
sb.append(" was changed!"); //sb指向的地象本身可以修改.

先说final变量初始化:

很多文章都这么说:其初始化可以在两个地方,一是其定义处,二是在构造函数中,两者只能选其一。
胡说八道!
final变量可以在任何可以被始化的地方被始化,但只能被初始化一次.一旦被初始化后就不能再次赋
值(重新指向其它对象),作为成员变量一定要显式初始化,而作为临时变量则可以只定义不初始化(当然也不能引用)
即使是作为一个类中的成员变量,也还可以在初始化块中初始化,所以"其初始化可以在两个地方,一是其定义处,
二是在构造函数中,两者只能选其一"是错误的.


作为成员变量时,final字段可以设计不变类,是不变类的一个必要条件但不是一个充要条件.至少可以保证字段不
会以setXXX()这样的方式来改变.但无法保证字段本身不被修改(除非字段本身也是不变类);

对于方法参数的final变量:
对于方法参数的变量定义为final,90%以上的文章都说"当你在方法中不需要改变作为参数的对象变量时,明确使
用final进行声明,会防止你无意的修改而影响到调用方法外的变量。"
胡说八道!

我不知道这个修改是说重新赋值还是修改对象本身,但无论是哪种情况,上面的说法都是错误的.
如果是说重新赋值,那么:
 public static void test(int[] x){
  x = new int[]{1,2,3};
 }

    int[] out = new int[]{4,5,6};
    test(out);
    System.out.println(out[0]);
    System.out.println(out[1]);
    System.out.println(out[2]);
    调用test(out);无论如何也不会影响到外面变量out.你加不加final根本没有意义.final只会强迫方法内
多声明一个变量名而已,即把x = new int[]{1,2,3};改成int y = new int[]{1,2,3}; 其它没有任何实际意义.
    如果说是修改对象本身:
 public static void test(final int[] x){
  x[0] = 100;
 }
    int[] out = new int[]{4,5,6};
    test(out);
    System.out.println(out[0]);
难道你用final修饰就不可以修改了?所以说对于方法参数中final是为了不影响调用方法外的变量那是胡说八道的.

那我们到底为什么要对参数加上final?其实对方法参数加final和方法内变量加上final的作用是相同的,即为了将它们
传给内部类时保证调用的一致性:

abstract class ABSClass{
 public abstract void m();
}

现在我们来看,如果我要实现一个在一个方法中匿名调用ABSClass.应该:
 public static void test(final String s){
     //或final String s = "axman";
  ABSClass c = new ABSClass(){
   public void m(){
      int x = s.hashCode();

      System.out.println(x);

   }
  };
  //其它代码.
 }

 从代码上看,在一个方法内部定义的内部类的方法访问外部方法内局部变量或方法参数,是非常自然的事,但内部类编译的时候如何获取这个变量,因为内部类除了它的生命周期是在方法内部,其它的方面它就是一个普通类。那么它外面的那个局部变量或方法参数怎么被内部类访问?编译器在实现时实际上是这样的:

 


  public static void test(final String s){
     //或final String s = "axman";

 

  class OuterClass$1 extends ABSClass{

   private final String s;
   public OuterClass$1(String s){
      this.s = s;   
   }
   public void m(){
      int x = s.hashCode();

      System.out.println(x);

   }
  };

  
  ABSClass c = new OuterClass$1(s);
  //其它代码.
 }
即外部类的变量被作为构造方法的参数传给了内部类的私有成员.
假如没有final,那么:
 public static void test(String s){
     //或String s = "axman";
  ABSClass c = new ABSClass(){
   public void m(){
     s = "other";
   }
  };
  System.out.println(s);
 }
 就会编译成:
  public static void test(String s){
     //或String s = "axman";

 

  class OuterClass$1 extends ABSClass{

   private String s;
   public OuterClass$1(String s){
      this.s = s;   
   }
   public void m(){
     s = "other";

   }
  };

   ABSClass c = new OuterClass$1 (s);

  }

 

 内部类的s重新指向"other"并不影响test的参数或外部定义的那个s.同理如果外部的s重新赋值内部类的s也不会跟着改变。
 而你看到的
  public static void test(String s){
     //或String s = "axman";
  ABSClass c = new ABSClass(){
   public void m(){
     s = "other";
   }
  };
  System.out.println(s);
 }

 

 在语法上是一个s,在内部类中被改变了,但结果打印的出来的你认为是同一的s却还是原来的"axman",
 你能接收这样的结果吗?
 所以final从语法上约束了实际上两个不同变量的一致性(表现为同一变量).

<think>好的,我现在需要回答用户关于Javafinal关键字的使用方法和规则的问题。首先,我需要仔细阅读用户提供的五个引用内容,确保理解每个引用的重点,然后综合这些信息来组织一个结构清晰的中文回答。 首先,用户的问题分为两部分:使用场景和规则。根据引用,final可以修饰类、方法、变量,每个部分有不同的作用。我需要将这些内容分门别类,逐一解释。同时,要注意引用对应的标注,比如引用[1]、[2]等,确保在回答中正确标注来源。 接下来,我需要检查是否有遗漏的信息。例如,引用[5]提到final引用数据类型时,地址不能被修改,但对象内容可以修改。这一点很重要,应该包含在变量修饰的部分。同时,引用[4]提到final方法不能被重写,这也需要强调。 然后,我需要考虑回答的结构。可能的结构是先总述final的作用,然后分点讨论类、方法、变量,每个部分详细说明规则和使用场景。在每个部分的末尾添加对应的引用标注。例如,在final类的部分,引用[1]和[2]都提到阻止继承,所以标注[^1][^2]。 同时,要注意用户提供的示例中的格式要求:行内数学表达式用$...$,独立公式用$$...$$,但在这个问题中可能涉及代码示例,所以需要正确使用代码块。例如,在讨论final变量时,可以举一个代码例子,如final int x = 10;,并说明其不可变。 另外,用户要求生成相关问题,我需要根据回答内容提出三个相关的问题,例如关于性能影响、设计模式中的应用,以及与不可变类的关系等。 最后,确保整个回答符合用户的要求:使用中文,结构清晰,引用正确,没有使用Markdown格式,段落之间用空行分隔,代码块用三个反引号包裹,并且相关问题部分明确标出。 现在,我需要将以上思考整理成一个连贯的回答,确保各部分信息准确,引用正确,并且语言流畅自然。</think>Java中的`final`关键字用于声明不可变的实体,具体规则和使用场景如下: ### 一、final修饰类 1. **规则** - final不能被继承,即不允许有子类。 - 类的所有方法隐式声明为`final`(不可覆盖)[^2]。 2. **使用场景** - 设计工具类或核心组件时,防止继承导致的安全风险(如`String`类)[^5]。 ```java final class UtilityClass { // 工具类方法 } ``` --- ### 二、final修饰方法 1. **规则** - final方法不能被子类覆盖(重写)[^4]。 - 父类方法若声明为`final`,子类调用时需保持原逻辑。 2. **使用场景** - 保护关键算法或业务逻辑的完整性(如支付验证逻辑)[^5]。 ```java class Parent { public final void validate() { // 核心验证逻辑 } } ``` --- ### 三、final修饰变量 1. **规则** - **基本类型变量**:值不可修改(如`final int x = 10;`)[^3][^5]。 - **引用类型变量**:引用地址不可修改,但对象内部状态可修改(如`final List<String> list = new ArrayList<>();`允许`list.add("data")`)[^5]。 - **类成员变量**:必须在声明时或构造方法中初始化。 2. **使用场景** - 定义常量(如`MAX_SIZE`); - 多线程环境下共享不可变引用。 ```java final class Constants { public static final double PI = 3.14159; // 常量 } ``` --- ### 四、final参数的规则 - 方法参数声明为`final`时,方法内不能修改其值(适用于Lambda表达式或匿名内部类)。 ```java void process(final int input) { // input = 5; // 编译错误 } ``` --- ### 总结对比 | 修饰对象 | 规则特性 | 典型用途 | |---------|---------|---------| | 类 | 禁止继承 | 安全工具类 | | 方法 | 禁止重写 | 保护核心逻辑 | | 变量 | 禁止重新赋值 | 常量和线程安全 |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值