Java面向对象编程中final关键字的使用方法详解_java

在Java中通过final关键字来声明对象具有不变性(immutable),这里的对象包括变量,方法,类,与C++中的const关键字效果类似。
immutable指对象在创建之后,状态无法被改变
可以从三个角度考虑使用final关键字:

  • 代码本身:不希望final描述的对象所表现的含义被改变
  • 安全:final对象具有只读属性,是线程安全的
  • 效率:无法修改final对象本身,对其引用的操作更为高效

final 变量
定义final Object a,则a只能被初始化一次,一旦初始化,a的数据无法修改,若a为引用类型,则不能重新绑定其他对象。
未被初始化的final变量被称为blank final,若为成员变量,则必须被初始化或在构造器中赋值。
例子:

class Circle {
 static final double PI = 3.1415926;
 final int radius = 5;
 final int xPos;
 final int yPos;
 public Circle(int x, int y) {
 xPos = x;
 yPos = y;
 }
}

final 方法
定义final method,则该方法无法被重载,方法设计者不希望由于对方法的重载导致其他相关功能出现异常。
例子:

class BaseClass {
 public final void method() {}
}

class DerivedClass extends BaseClass {
 public final void method() {} // 编译出错
}

需要注意的是,final方法的定义不一定能够产生inline的效果,因为方法是否inline取决于JVM的策略,而非final关键字,通过final的设计提高方法效率是不准确的。

final 类
final class X定义的类X无法被继承。
在Java中,String类被设计成final,其定义如下

复制代码 代码如下:

public class final String extends Object  implements Serializable, Comparable<String>, CharSequence

为什么String被设计成final?

  • 一个String类的实例被初始化后,其在堆上的内容无法被改变,String类提供的任何修改String对象的方法都只能够产生一个新的String对象,大大简化了对String的操作,是代码更易于阅读和理解;
  • String final是实现String interning(在内存中不同的string值只有一份)的必要条件,因为通常代码中存在大量的String对象,不同的引用会指向相同的字符串空间,若String不为final,则当一个字符串空间的内容改变时,所有的引用都需要知道这一情况,这一机制的实现是十分复杂的,无疑会影响效率。String interning能够节省内存空间,同时也节省时间花销;
  • String只读,则不必担心非常重要的内容被篡改。

内部类与final
在一个方法内定义匿名内部类时,内部类只能访问方法内的final类型变量,使得Java编译器能够提前捕获变量的值,并在内部类保存一份副本,当方法销毁时,内部类的内存空间依然完整。
例子:

public class Wrapper {

  public static void main(String[] args) {

    // Object obj = null; //编译出错
    final Object obj = null;
    new Thread(new Runnable() {
     public void run() {
     obj = "hello";
     }
    }).start();
  }
}

PS:内部匿名类无法访问外面的非 final 的变量的问题
这个听起来有点拗口,其实我更多的是想说 Java 内部类的一些特性。

之所以会想起这个题目只要是最近在阅读 JDK 源码中关于 HTTP keepalive 的代码时,其中一个源文件 sun.net.www.protocol.http.HttpURLConnection.java 无意中看到下面这段代码。

final boolean result[] = {false};
java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
  public Object run() {
    try {
      InetAddress a1 = InetAddress.getByName(h1);
      InetAddress a2 = InetAddress.getByName(h2);
      result[0] = a1.equals(a2);
    } catch (UnknownHostException e) {
    } catch (SecurityException e) {
    }
    return null;
  }
});

return result[0];

Java 的匿名内部类无法访问对应的函数的非 final 变量。要想访问外部的 local variable, 这个variable 又必须要先定义成 fianl, 但是一定义成 final 就不能在匿名内部类中修改这个变量的值,所以要想匿名内部类返回一些有用的值时不是那么的容易。这段代码使用了一个非常巧妙的方法,这里使用数组的方式绕过这个限制,虽然我们无法修改 result 这个变量的引用,但是我们可以修改 result 指向的那个数组的内容。

只是想记录一下内部匿名类修改外部变量的一个小技巧。不过既然已经到了这里,不妨继续的看看内部类都有哪些特性或者限制吧。

在继续本文前,我觉得非常有必要的明确下本文中涉及的一些 Java 术语,这些术语不太好翻译成中文,所以我们还是用英文来描述。

// This is class
public class JavaTerm {

  // field or member variable
  private int field;

  // constructor
  public JavaTerm() {
  }

  // method
  public void method() {

    // local variable
    int localVariable = 0;

    // local class
    class LocalClass {
      public LocalClass() {
      }
    }
    // anonymous class
    new Runnable() {
      public void run() {
      }
    };
  }
}

我们今天更多的将关注于 local class 和 anonymous class,它们都属于 inner class。

Java 允许我们在一个 class 里面再定义一个 class, 称为嵌套类(nested class), nested class 又可以分为两类,一类是 static nested class, 另外一个是 non-static nested class, 又称为 inner class。inner class 又可以分为 local class 和 anonymous class。

anonymous class 的一些限制

  • 一个 anonymous class 可以访问包含它的类的类变量(field/member variable)
  • 一个 anonymous class 不能访问包含它的作用于中的不是 final 的本地变量(local variable)
  • 和 nested class 一样,anonymous class 中定义的 variable 会覆盖包含这个内部类的作用域中的同名的 variable
  • 你不能定义静态的初始化方法
  • 一个 anonymous class 可以有静态的成员变量。这个成员变量必须是常量(用 final 修饰)。
  • 一个 anonymous class 不可以有构造函数

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
, 面向对象
final
java面向接口编程详解、面向接口编程详解、ios 面向接口编程详解、java final关键字、android final关键字,以便于您获取更多的相关知识。

时间: 2024-12-09 15:07:31

Java面向对象编程中final关键字的使用方法详解_java的相关文章

Java线程编程中isAlive()和join()的使用详解_java

一个线程如何知道另一线程已经结束?Thread类提供了回答此问题的方法. 有两种方法可以判定一个线程是否结束.第一,可以在线程中调用isAlive().这种方法由Thread定义,它的通常形式如下: final boolean isAlive( ) 如果所调用线程仍在运行,isAlive()方法返回true,如果不是则返回false.但isAlive()很少用到,等待线程结束的更常用的方法是调用join(),描述如下: final void join( ) throws InterruptedE

Struts中使用validate()输入校验方法详解_java

1.在ActionSupport中有一个validate()方法,这个方法是验证方法,它会在execute()方法执行之前执行,所以能够起到很好的验证的作用. @Override //重写Action中的validate()方法 public void validate() { if(null==this.username||this.username.length()<4||this.username.length()>6){ this.addActionError("userna

java集合——Java中的equals和hashCode方法详解_java

Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需要实现特定需求,可能要重写这两个方法,今天就来介绍一些这两个方法的作用. equals()和hashCode()方法是用来在同一类中做比较用的,尤其是在容器里如set存放同一类对象时用来判断放入的对象是否重复. 这里我们首先要明白一个问题: equals()相等的两个对象,hashcode()一定相等,equals()不相等的两个对象,却并不能证明他们的hashcode()不相等.换

java实现单链表中是否有环的方法详解_java

这是一道微软经典笔试题,就是两个指针h1,h2都从头开始遍历单链表,h1每次向前走1步,h2每次向前走2步,如果h2碰到了NULL,说明环不存在:如果h2碰到本应在身后的h1说明环存在(也就是发生了套圈).     如果环不存在,一定是h2先碰到NULL:     如果环存在,h2与h1一定会相遇,而且相遇的点在环内:h2比h1遍历的速度快,一定不会在开始的那段非环的链表部分相遇,所以当h1,h2都进入环后,h2每次移动都会使h2与h1之间在前进方向上的差距缩小1,最后,会使得h1和h2差距减少

基于获取JAVA路径,包括CLASSPATH外的路径的方法详解_java

归纳一些网上取JAVA路径的方法: 注明:如果从ANT启动程序,this.getClass().getResource("")取出来的比较怪,直接用JAVA命令行调试就可成功.得到classpath和当前类的绝对路径的一些方法获得CLASSPATH之外路径的方法:URL base = this.getClass().getResource(""): //先获得本类的所在位置,如/home/popeye/testjava/build/classes/net/ Stri

深入Java Robot实现控制鼠标和键盘的方法详解_java

Java.awt.Robot 类用于控制鼠标和键盘.一旦你得到这种控制,你能够通过你的Java代码做与鼠标和键盘任何类型的操作.这个类通常用于自动化测试.下面的代码样例将向您展示Robot类如何处理键盘事件.如果你运行此代码,并打开notepad,您将在notepad中看到HI CAOER.赶快试一试吧. 复制代码 代码如下: import java.awt.AWTException;import java.awt.Robot;import java.awt.event.KeyEvent;pub

Lua中break语句的使用方法详解

  这篇文章主要介绍了Lua中break语句的使用方法详解,是Lua入门学习中的基础知识,需要的朋友可以参考下 当循环中遇到break语句,循环立即终止,程序控制继续下一个循环语句后面. 如果您正在使用嵌套循环(即一个循环里面另一个循环),break 语句将停止最内层循环的执行并开始执行的下一行代码的程序后段. 语法 Lua break语句语法如下: 代码如下: break 例子: 代码如下: --[ local variable definition --] a = 10--[ while l

js基础之DOM中元素对象的属性方法详解_javascript技巧

在 HTML DOM (文档对象模型)中,每个部分都是节点. 节点是DOM结构中最基本的组成单元,每一个HTML标签都是DOM结构的节点. 文档是一个    文档节点 . 所有的HTML元素都是    元素节点 所有 HTML 属性都是    属性节点 文本插入到 HTML 元素是    文本节点 注释是    注释节点. 最基本的节点类型是Node类型,其他所有类型都继承自Node,DOM操作往往是js中开销最大的部分,因而NodeList导致的问题最多.要注意:NodeList是'动态的',

Javascript中的迭代、归并方法详解_基础知识

迭代方法 在Javascript中迭代方法个人觉得尤为重要,在很多时候都会有实际上的需求,javascript提供了5个迭代方法来供我们操作,它们分别为: every() 对数组中的每一个项运用给定的函数,如果每项都返回true,那么就会返回true filter() 对数组中的每一个项运用给定的函数,把返回true的项组成一个新数组并返回 forEach() 对数组中的每一项运用给定的函数,但是没有任何的返回值 map() 对数组中的每一个项运用给定的函数并返回每次函数调用的结果组成新的数组