`
zhang.jianjun
  • 浏览: 20214 次
  • 性别: Icon_minigender_1
  • 来自: 青岛
最近访客 更多访客>>
社区版块
存档分类
最新评论
  • NGG: 个人建议: 正确的做法应该是把样式放到外部css文件中,使用 ...
    js日期选择器

【JAVA优化编程】表达式、语句与保留字之——(2)Java语言中的保留字

阅读更多

2  Java语言中的保留字

    任何一种语言都有自己的保留字,这些保留字是不能单独出现在程序中的,除非你赋予了其应有的意义。下表是Java语言中全部的保留字。

abstract
boolean
break
byte
case
catch
char
class
const *
continue
default
do
double
else
extends
final
finally
float
for
goto *
if
implements
import
instanceof
int
interface
long
native
new
null
package
private
protected
public
return
short
static
super
switch
synchronized
this
throw
throws
transient
try
void
volatile
while

 

    限于篇幅关系,我们只讲解其中几个较为常用、重要的保留字,而且这些保留字有时候如果运用不当,容易产生错误。

 

2.1  静态的(static)

    关于静态变量的知识在前面已经做过一些讲解,这里再介绍一下有关静态代码块、静态变量引用顺序及引用规则的相关知识。

 

    静态代码块

    有时候为了能在引用一个类的时候做一些必要的初始化工作,经常利用static 关键字加入一些静态的代码块,完成这个超前功能。下面给出一个处理配置信息的类的例子。

public class FileDirectoryConfig {
    static boolean isLoaded = false;
    static String fileDirectory = null;
    static {
        if ( ! isLoaded) {
            Properties props = new Properties();
            try {
                java.io.InputStream is = FileDirectoryConfig.class.getResourceAsStream("file.properties");
                System.out.println(is);
                props.load(is);
            } catch (IOException ex) {
                System.out.println("配置文件读取失败!");
                ex.printStackTrace();
            }
            fileDirectory = props.getProperty("fileDir");
            if (fileDirectory == null) {
                System.out.println("read config file failed");
            } else {
                props = null;
                isLoaded = true;
            }
        }
    }
}
 

    这个类的意图很明显,就是通过配置文件获取文件目录的配置信息,这种技术经常被应用在当你想在不修改应用程序代码的情况下,通过配置文件获取配置信息,并且调整或增加应用程序的功能的情景下。这样在其他类中引用这个类的属性fileDirectory 时就不用实例化这个类,而且也不需要调用其任何方法,只需下面一行代码即可达到预期的目的:

String fileDirectory = FileDirectoryConfig.fileDirectory;
 

    这样书写代码就十分简便,节省编码时间,提高效率。你可能已经注意到了,为了保证该类只初始化一次,又引入了静态变量isLoaded,当其为假时,我们知道该类还没有做过初始化操作即执行。如果其为真,说明该类已经做过初始化操作,就不用再做初始化操作了。

 

    静态变量的引用顺序

    静态代码块对静态变量的应用在声明上存在先后顺序的限制,这与方法中引用变量的情况是不同的,在方法体中引用的变量在类文件前后声明没有限制,例如:

public class A {
    private String userName = null;

    public String getUserName() {
        return userName;
    }
}
 

    或者

public class A {
    public String getUserName() {
        return userName;
    }

    private String userName = null;
}
 

都是没有问题的,程序都会正常运行,但是如果你试图在静态代码块中引用静态变量,就要考虑其声明的先后顺序的限定,例如下面的代码是不对的:

public class A {
    Static {
        userName = "张三";
    }

    private static String userName = null;
}
 

    而下面的代码是正确的:

public class A {
    private static String userName = null;

    Static {
        userName = "张三";
    }
}
 

    这是因为在静态代码块中引用的静态变量必须在静态代码块之前声明。

 

    静态变量引用规则

    对静态变量的引用有下面的规则:

    (1) 可以在非静态方法体中引用静态变量。例如:

public class A {
    private static String userName = null;

    public void getMessage () {
        return userName;
    }
}
 

    (2) 在静态方法体中不可以引用非静态变量。

public class A {
    private String userName = null;

    public static void main(String args[]) {
        // 这是不允许的
        System.out.println("userName = " + userName);
    }
}
 

    (3) 可以在静态方法体中创建非静态变量。

public class A {
    private String userName = null;

    public static void main(String args[]) {
        A a = new A();
    }
}
 

 

2.2  超类(super)

    超类(super)在当类试图引用其父类对象时使用,当在类的构造器中试图引用父类的构造器时必须将其放置在子类构造器中的第一行,这是由类的初始化顺序规则决定的,否则编译器会提示你系统错误信息,你是无法完成对该类的编译工作的。例如下面的代码是不正确的:

public class B extends A {
    public B() {
        otherMethod();
        super(initialName);
    }
}
 

    正确的书写方式为:

public class B extends A {
    public B() {
        super(initialName);
        otherMethod();
    }
}
 

 

2.3  最终的(final)

    在日常的程序开发中final 保留字的运用较为广泛,下面是保留字final 的应用范围:

  •  用来声明类的常量;
  •  用来声明方法的常量参数;
  •  用来声明不可覆盖的方法;
  •  用来声明不可继承的类。

    下面对final 保留字的这四个使用范围分别给予详细的讲解。

 

    用来声明类的常量

    在Java程序设计中常量的声明经常采用下面的方式:

public static final String USER_NAME = "张三";
 

    在上面的语句中加入了static 与 final 两个保留字,这是常量具备的基本特征——静态的、不可变的。

 

    用来声明方法的常量参数

    在对一些方法声明的时候为了防止其参数被方法体中的语句更改,经常将参数声明为final 参数,例如:

class A {
    public void showMessage(final String userName) {
        ...
        System.out.println("userName = " + userName);
    }
}
 

    如果你企图在方法体中改变userName 参数的值,如:

... ...
public void showMessage(final String userName) {
    ...
    userName = "李四";
    System.out.println("userName = " + userName);
}
... ...
 

    该类在编译时,编译器就会提示在加粗代码行处有错误,提示你不能更改userName变量的值,这样做的好处是当你在方法中使用类的全局变量,而且不希望在该方法中对这个变量的值做修改,或者你不想在方法中改变某一个参数的值的时候,都可以采用final 关键字声明这个变量,这样做可以对该变量起到相应的保护作用,防止变量被无意赋值、破坏。

 

    用来声明不可覆盖的方法

    在面向对象程序的设计中,子类可以覆盖父类中的方法,即子类中的方法的名称与参数个数、参数类型及排放顺序完全相同,这时我们就说在子类中覆盖了父类中的方法。但有时我们并不希望一个类的子类覆盖某个方法,因为这些方法只能属于父类,只有父类才有条件,有资格完成这个行为,而这个方法子类又是子类无法完成的行为。此时我们就将这个方法声明为final,如下所示。

类A:
public class A {
    protected void showMessage() {}
}

类B:
public class B extends A {
    protected void showMessage() {}
}
 

    上面的代码中,我们在类B中覆盖了类A中的方法showMessage() ,如果将类A中的showMessage()方法声明为final 方法,在类B中就不能覆盖这个方法了,如下所示。

类A:
public class A {
    protected final void showMessage() {}
}

类B:
public class B extends A {
    protected void showMessage() {}
}
 

    上面的代码在编译时就会产生错误,其实采用final 保留字不但可以防止父类中的方法不被子类覆盖,而且还可以加快应用的运行速度,提高系统性能。在介绍之前我们先引入C++语言中内联函数(inline)的概念。

    内联函数是C++中的概念,指将代码插入到调用者代码处的函数。如同C++语言中通过关键字#define 所定义的宏,内联函数通过避免被调用的开销来提高执行效率,尤其是它能够通过调用(“过程化集成”)被编译器优化。内联函数和宏很类似,其区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样地展开,所以取消了函数的参数压栈,减少了调用的开销。你可以像调用函数一样调用内联函数,而不必担心会产生处理宏的一些问题。

    当然,内联函数也有一定的局限性,就是函数中的执行代码不能太多了,如果内联函数的函数体过大,一般的编译器会放弃内联方式,而采用普通的方式调用函数。这样,内联函数就和普通函数执行效率一样了。

    简单而言,内联函数就是当你编译应用程序时就可以确定该函数的代码,并且可以将函数的代码展开插入到调用者代码处的函数。在Java语言中,你可能也经常用到过类似于内联函数的引用,只是不自知罢了,例如你可以在某个方法中直接书写处理代码,也可以通过调用某个方法来封装处理代码段,然后再应用该方法。而前者的调用形式就是内联的调用方式:

public class A {
    public static int max(int a, int b) {
        return (a > b ? a : b);
    }

    public static void main(String args[]) {
        final int N = 10000000;
        int a = 5;
        int b = 17;
        int c;
        // 调用方法
        long startTime = System.currentTimeMillis();
        for (int i = 1; i <= N; i++) {
            c = max(a, b);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("调用方法:" + (endTime - startTime));

        // 使用与max 方法相同的内联代码
        startTime = System.currentTimeMillis();
        for (int i = 1; i <= N; i++) {
            c = (a > b ? a : b);
        }
        endTime = System.currentTimeMillis();
        System.out.println("使用内联代码:" + (endTime - startTime));
    }
}
 

    编译并运行上面的代码,得到下面的输出:

 

        调用方法:63

        使用内联代码:15

 

    由此可见,在编译时就可以确定展开的内联代码比方法调用将近快4倍,上面程序的运行硬件环境为CPU(奔腾4,2.4GZH),内存(DDR 1GB)的PC电脑上。随着硬件配置的提高,这个差别会更加明显。但是上面的例子中我们只能使用一些简单的表达式来达到内联的目的,但是如果代码量较大,并且在应用程序的多个地方引用相同的代码,这种方式就显得有些力不从心了,因为过多拷贝相同的代码块必将使类变得臃肿,怎样解决这个问题呢?通过将方法声明为final 方法来达到这个效果,例如将上面的代码修改成下面的样子:

public class A {
    public final static int max(int a, int b) {
        return (a > b ? a : b);
    }

    public static void main(String args[]) {
        final int N = 10000000;
        int a = 5;
        int b = 17;
        int c;
        // 调用方法
        long startTime = System.currentTimeMillis();
        for (int i = 1; i <= N; i++) {
            c = max(a, b);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("调用方法:" + (endTime - startTime));

        // 使用与max 方法相同的内联代码
        startTime = System.currentTimeMillis();
        for (int i = 1; i <= N; i++) {
            c = (a > b ? a : b);
        }
        endTime = System.currentTimeMillis();
        System.out.println("使用内联代码:" + (endTime - startTime));
    }
}
 

    编译并运行上面的代码,得到下面的输出:

 

        调用方法:16

        使用内联代码:16

 

    与上面的输出有些出入,但是两种方式的运行时间却是相同的或接近的。这是我们将max()方法声明为final 方法的原因。一个方法被声明成final 方法后,当你编译应用程序时就可以确定该方法的代码,并且编译器可以将该方法的代码展开插入到调用者代码处,因此提高了应用的运行速度,提高了系统效率。

 

    用来声明不可继承的类

    将上面的知识推而广之,我们也可以将一个类声明成final 类,使其不可以被其他类继承,说通俗点就是有点让其“断子绝孙”的味道,如下所示。

类A:
public final class A {
    ...
}

类B:
public class B extends A {
    ...
}
 

    类B是无法继承类A的,上面的代码是不正确的,当你在编译应用时编译器将会报错,提示你类B不能继承类A,因为类A是final 类。同理,将类A声明成final 类也可以起到内联的作用,加快应用速度,提高系统性能,如:

... ...
A ref = new A();
ref.showMessage();
... ...

    上面的这段代码就将整个类A看做内联代码。

 

2.4  同步(synchronized)

    同步技术是在多线程的运行环境下提出的,该技术用来保护多线程环境下的共享资源。使用多线程时,由于线程之间可以共享资源,同时访问共享资源有时就会发生冲突。举一个简单的例子,有两个线程:thread1 负责写共享资源;thread2 负责读共享资源。当它们操作同一个对象时,会发现由于thread1 与thread2 是同时执行的,因此,可能thread1 已经修改了数据,而thread2 读出的仍为旧数据,此时就无法获得预期的结果。问题之所以产生,主要是由于在多线程环境下,资源使用的协调不当(不同步)。

    Java语言提供了同步(synchronized)方法和同步状态来协调资源。在Java程序设计中,被宣布为同步(Synchronized)的方法,对象或类数据。在任何一个时刻只能被一个线程访问。通过这种方式,可以保证多线程环境下的共享资源能够被合理的使用,达到线程同步的目的。

    下面给出一个线程同步的具体的例子:

import java.lang.*;

// 线程同步测试类SynchronizedTest.java
public class SynchronizedTest {
    private int i = 0;

    public static void main(String args[]) {
        SynchronizedTest st = new SynchronizedTest();
        st.startThread();
    }

    // 启动线程方法
    private void startThread() {
        // 同时启动3个线程
        for (int j = 0; j < 3; j++) {
            new Thread(new ThreadWorker()).start();
        }
    }

    // 内部线程类ThreadWorker
    class ThreadWorker implements Runnable {
        public void run() {
            showMessage();
        }
    }

    // 方法showMessage()被宣布为同步的方法,因此每次只有一个线程能调用这个方法
    private synchronized void showMessage() {
        Thread currentThread = Thread.currentThread();
        System.out.println("当前线程为: " + currentThread.getName());
        ++i;
    }
}

    运行结果为:

    当前线程为: Thread-1

    当前线程为: Thread-2

    当前线程为: Thread-3

    另外,利用Synchronized 可以锁定对象。

    例如:

synchronized (某个对象A) {

    // 程序块

}
 

    在此程序块中,对于相同的对象A,在任何时候只可以有一个线程在此代码中执行,但对于不同的对象还是有很多个线程同时执行的。用同样的方法也可以协调类数据,例如:

synchronized (new 欲锁定的类().getClassData()) {

    // 程序块

}

    方法getClassData() 是用来获取类数据的。这样通过利用Synchronized 这一关键字可以自由协调对象实体的各种数据。

    关于多线程的相关知识,这里就不再介绍了,想要了解更多的信息,请参考与之相关的技术资料。

 

 

2.5  实例识别 (instanceof)

    实例识别(instanceof)是用来判断一个对象的引用是否为某一类型。比如:

A a = new A();
System.out.println(a instanceof A);

    返回为true,因为a是一个类A的对象的引用。

A a = new A();
System.out.println(a instanceof B);

则返回为false,因为a不是一个类B的对象的引用。

    但如果

A a = null;
System.out.println(a instanceof A);

则返回值为false。这是因为a的值为null,null不是任何对象的引用。这是需要特别注意的。在识别区分子类实例的场合是非常有用的,例如:

基类
class BasicClass {
    ...
}

子类A
class A extends BasicClass {
    ...
}

子类B
class B extends BasicClass {
    ...
}

    在上面的代码中,类A与类B都继承了共同的基类BasicClass,因此可以将类A或类B的对象引用赋给基类BasicClass的对象引用,但是当无法确定基类引用到底是类A的对象引用还是类B的对象引用时,就可以通过instanceof 保留字进行识别和区分,例如:

... ...
BasicClass basicClass = (BasicClass)obj.getObjectOfAOrB();
if (basicClass instanceof A) {
    ...
} else if (basicClass instanceof B) {
    ...
} else {
    ...
}
... ...

    这样就可以确定在运行时刻,基类BasicClass的引用中所指向的对象实例,这在相关的应用程序设计中是非常有用的。

分享到:
评论

相关推荐

    java保留字、关键字

    在Java编程语言中,保留字(Reserved Words)和关键字(Keywords)是两个非常重要的概念,它们构成了Java语法的基础。保留字是Java语言已经预定义并赋予特定含义的词汇,而关键字则是Java语法结构中不可或缺的部分。...

    Java语言基础教程.ppt

    内容概要:《Java语言基础》资源为读者提供了Java语言编程的基础知识和概念,通过一系列实例教程,引导学习者掌握Java程序的基本结构、特殊语句、变量与数据类型、标识符与关键字、运算符与表达式以及流程控制语句。...

    python学习笔记—pythone 33个保留字及其含义

    pythone 33个保留字及其含义正文可以用代码查询python到底有哪些保留字注意: 正文 序号 保留字 含义 1 and 用于表达式运算,逻辑与操作 2 as 用于类型转换 3 assert 断言,用于判断变量或条件表达式的值...

    Javascript关键字及保留字

    在编程实践中,避免与这些关键字和保留字冲突,可以减少语法错误,提高代码质量,并确保程序的兼容性和可维护性。同时,采用合适的命名约定,如匈牙利类型标记法,也有助于团队成员更好地理解代码的意图和数据类型。

    Java语法基础+一些 JAVA案例,和正则表达式

    Java是一种广泛使用的面向对象的编程语言,其语法严谨且具有丰富的功能。本篇文章将深入探讨Java语法基础,包括词法规则、数据类型、常量与变量、运算符和表达式、语句以及数组和字符串。 首先,我们来看Java的词法...

    Java中for语句特殊使用方法---表达式1、2、3均为空.pdf

    在Java编程语言中,`for`循环是一种常用的控制流程结构,用于执行一系列语句多次,通常根据特定条件来决定何时停止。`for`循环的基本语法包括三个表达式:表达式1(初始化)、表达式2(条件检查)和表达式3(更新)...

    Java数据类型与表达式Java语言程序设计第版.pptx

    Java是一种广泛使用的面向对象的编程语言,其语法严谨且具有丰富的特性。在Java中,数据类型和表达式是编程的基础,它们定义了程序中的数据如何存储和操作。 **数据类型**分为两大类:**基本数据类型**和**引用数据...

    SQL 去除字段中符号,只保留数字和字母

    --去除字段中符号,只保留数字和字母sql-- create FUNCTION DBO.REMOVE_SYMBLE(@S VARCHAR(100)) RETURNS VARCHAR(100) AS BEGIN DECLARE @SQL VARCHAR(100) SET @SQL=''

    国开电大 Java语言程序设计 形考任务1-2答案.pdf

    Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems(现为Oracle Corporation的一部分)于1995年推出。它以其跨平台性、安全性、可移植性和高效性能而闻名。Java的设计目标是“一次编写,到处运行”。 1....

    java——面试题

    在Java编程语言中,面试题往往涵盖了广泛的知识点,从基础语法到高级特性。以下是根据题目给出的部分内容解析的一些Java面试常见问题及其解答: 1. **一个.java源文件中的类数量**: - 一个`.java`源文件可以包含...

    python保留字有哪些 .pdf

    例如,在Python 3中,一些在Python 2中是保留字的标识符,如execfile和print,已经不再是保留字,并且它们的使用方式也发生了变化。 最后,关于文档内容中提到的Windows10、DellG3等信息,看起来似乎与Python保留字...

    第2章-Java数据类型与表达式-Java语言程序设计(第2版).ppt

    Java语言程序设计中的第二章主要聚焦于数据类型与表达式,这是编程的基础概念。首先,我们来了解一下Java中的标识符规则。合法的Java标识符必须遵循一定的命名规范,例如,A. counterl 和 D. _byte 是合法的,因为...

    Java中的保留字和关键字.doc

    对于Java这门广泛使用的面向对象编程语言而言,理解其关键字与保留字的含义及用途至关重要。本文将详细介绍Java中的关键字和保留字,并探讨它们在程序开发中的应用。 #### 二、Java关键字详解 Java关键字是对Java...

    java语言程序设计 java编程基础入门教程资料 (2)Java语言编程基础 共105页.pptx

    Java作为一种广泛使用的编程语言,在软件开发领域占据着重要地位。了解Java的基础组成对于掌握这门语言至关重要。 ##### 2.1.1 Java语言分隔符 Java中的分隔符主要用于区分不同的代码片段,帮助编译器正确地解析...

    第2章计算器——数据类型与表达式.ppt

    在编程领域,尤其是在Java语言中,数据类型与表达式是构建程序的基础。本章主要讲解了如何理解和使用数据类型以及如何构建表达式,这对于初学者掌握编程基础至关重要。 首先,我们要理解什么是数据类型。在Java中,...

    JAVA中的关键字和保留字

    Java是一种广泛使用的面向对象的编程语言,其语法严谨,其中包括一系列的关键字和保留字,这些关键字在编程中具有特殊含义,不能用作变量名或其他标识符。以下是对Java中一些重要关键字的详细解释: 1. **abstract*...

    编程语言基础知识ppt课件.ppt

    本文档主要介绍 Java 编程语言的基础知识,包括标识符、保留字、数据类型、类型转换、运算符和表达式、控制语句等概念。 一、标识符 标识符是 Java 程序中用来命名变量、方法、类等的符号。标识符可以是字母、数字...

    java中“53”个关键字(含2个保留字)

    在Java编程语言中,关键字是具有特殊含义的预定义标识符,它们被Java解析器用来识别语法结构。这些关键字不能用作变量名、类名或方法名。在Java中,一共有53个关键字,包括两个保留字。下面将详细阐述这些关键字的...

    Java完美编程(第三版)

    对于赋值与表达式的处理方式,与其他高级语言相似,但对于字符串操作及控制台输出,Java有着自己独特的方法,因此即使是经验丰富的程序员也应该关注这部分内容。 **1.1.1 Origins of the Java Language** Java语言...

    Jvscipt的保留字

    保留字是编程语言中预定义的、具有特殊意义的词汇,它们在语言规范中被预留,用于特定的语法结构或语义。在JavaScript中,使用保留字可能会导致语法错误或者代码解析异常,因此在编写代码时应避免使用它们。 二、...

Global site tag (gtag.js) - Google Analytics