透析Java之“关键字”和“标识符”

一个非常典型的面试题:在Java中有没有goto?
除了goto,今天我们还要谈到几个特别的标识,分别是const、true、false、null。这些标识在Java语言中,是不是关键字呢?如果是,该如何使用?如果不是,与普通的标识符是否又存在什么区别?

goto:
在C / C++等语言中,使用goto可以实现程序的跳转,从某些方面来说其提供了一定的方便性,例如,在多重嵌套的循环中,可以直接从内层循环中跳出外层循环。然而,这种跳转却没有任何限制,可以随意地进行,从而打破了正常的程序流程。如果程序中多处使用goto,不仅降低程序的可读性,也会对程序的维护与更新造成影响。
因此,为了避免上述情况,Java语言取消了goto的使用,取而代之的是使用循环标签。但是,为了避免程序员自行使用goto带来同样的混乱性(例如将方法或变量的名称声明为goto),Java语言仍将goto定义为一个关键字,用来限制程序员将goto作为一个标识符来使用,由于是一个从不使用的关键字,故也称为“保留字”。

package com.example;

public class MyClass {

    public static void main(String[] args) {
        testGoto();
    }

    private static void testGoto() {
        int[][] array = {
                {1, 2, 3},
                {11, 12},
                {101, 102, 103, 104}
        };
        //int goto=0;//错误,不能使用goto作为标识符
        int index = 0;
        outer:
        for (int i = 0, outerLen = array.length; i < outerLen; i++) {
            for (int j = 0, innerLen = array[i].length; j < innerLen; j++) {
                System.out.println(array[i][j]);
                index++;
                if (index == 5) {
                    //goto here;语法错误
                    break outer;
                }
            }
        }
        here:
        System.out.println("here:" + index);
    }
}

运行结果如下:

1
2
3
11
12
here:5

const:
在C / C++中,const是一个关键字,用来声明一个变量的值是不可改变的(即我们通常所谓的常量),与goto类似,Java语言也将const定义为关键字,但是却没有任何语法应用,也就是保留字。使用const来作为标识符也是不允许的。

true,false和null:
在很多集成开发环境中,true、false、null往往使用与关键字相同的特殊颜色给以标出,这便增加了这3个符号的混淆性,很多人认为这3个符号也是关键字。然而,这是错误的。在Java语言中,这3个符号是3个字面常量(也称直接量)。其中,true与false是布尔类型的字面常量,null是引用类型的字面常量。这些就好比“abc”是String类型的字面常量,而数值“5”是int类型的字面常量一样。
这么说来,这3个符号不是很平常了吗?似乎没有必要将其单独列出。也不是,还是有一些差别的。尽管true、false、null不是关键字,但是,也不能将其作为标识符使用,否则同样会产生编译错误。从这一点来说,这3个字面常量与关键字的表现是相似的。

关键字:
这里写图片描述

要点总结:
· 在Java中,goto与const作为保留的关键字而存在,虽然未在程序中使用,但是也禁止程序员将其作为标识符来使用。
· true、false与null是3个字面常量,并非Java中的关键字,在程序中也禁止作为标识符使用。

标识符:
在大多数人的理解中,Java标识符的定义规则如下。
1.标识符由字母、数字、货币符号(¥、$等)、连接符号(_等)组成。(这里的字母为Unicode字符集,而不再局限于传统的26个英文字母。)
2.标识符的首字符可以是字母、货币符号与连接符号,但不能是数字。
3.标识符不能与Java中的关键字相同。
4.标识符不能和Java中预定义的字面常量名称相同(true、false、null)。
以上的定义正确吗?
恩,基本上是正确的,在使用过程中通常也不会遇到问题。但是,更准确地来说,还是存在一定的错误成分。
首先,Unicode字符集的取值范围为U+0000~U+10FFFF,但是,并非范围内每一个Unicode值(代码点)都与一个字符相对应(部分代码点尚未使用),这也就意味着,并非整个Unicode字符集在Java中都可以作为标识符。其次,不能作为Java标识符首字符的字符集,也不仅仅是简单的0~9这10个数字字符而已。
可以使用Character类中如下两个静态方法来判断标识符的合法性:

public static boolean isJavaIdentifierStart(char ch)
public static boolean isJavaIdentifierPart(char ch)

后来,由于Unicode字符集的扩展,为了能够处理增补字符(U+10000~U+10FFFF),在JDK1.5中,新增了两个重载方法:

public static boolean isJavaIdentifierStart(int codePoint)
public static boolean isJavaIdentifierPart(int codePoint)

其中isJavaIdentifierStart用来判断代码点(codePoint)对应的字符是否可以作为Java标识符的首字符,而isJavaIdentifierPart用来判断代码点对应的字符是否可以作为Java标识符的一部分(首字符或首字符后面的字符)。

package com.example;

public class MyClass {

    public static void main(String[] args) {
        test();
    }

    private static void test() {
        int startCount = 0;
        int partCount = 0;
        for (int i = 0x0000; i <= 0x10ffff; i++) {
            if (Character.isJavaIdentifierStart(i)) {
                startCount++;
            }
            if (Character.isJavaIdentifierPart(i)) {
                partCount++;
            }
        }
        System.out.println("Unicode字符集个数:" + (0x10ffff + 1));
        System.out.println("可作为标识符首字符的字符个数:" + startCount);
        System.out.println("可作为标识符一部分的字符个数:" + partCount);
        System.out.println("二者之差:" + (partCount - startCount));
    }
}

打印结果如下:

Unicode字符集个数:1114112
可作为标识符首字符的字符个数:101296
可作为标识符一部分的字符个数:103584
二者之差:2288

从运行结果可知,可以作为标识符使用的字符只是Unicode集合中的一小部分,而且可以作为标识符一部分的字符总数与可以作为标识符首字符的字符总数相差2102个,绝不是仅仅0~9这10个数字而已。
现在,让我们来重新定义一下标识符的定义规则。
1.标识符的首字符所对应的代码点必须使得Character类的isJavaIdentifierStart方法返回值为true,后续字符所对应的代码点(如果存在后续字符的话)必须使得Character类的isJavaIdentifierPart方法返回值为true。
2.标识符不能与Java中的关键字相同。
3.标识符不能和Java中预定义的字面常量名称相同(true、false、null)。
4.标识符的长度必须在系统所支持的范围内(这点是Java虚拟机要求的)。

“$:”
尽管$可以作为标识符使用,然而我们应该尽量避免对其使用。因为$通常在编译器生成的标识符名称中使用,在源文件(.java文件)编译成字节码(.class文件)后,会成为顶层类型与嵌套类型之间的连接符。例如,如果存在一个顶层类A,在其内声明一个成员类B,那么编译之后就会产生两个class文件,分别为A.class与A$B.class。就本程序来说,会生成3个class文件,分别是User$VIP.class(顶层类)、User.class与User$VIP.class(User类的成员类,也就是类VIP)。由于试图存在两个User$VIP.class,因此才会产生如上的编译错误。
这个问题说明,我们不要图新鲜使用符号“$”,因为Java中的标识符真的有很多,完全没有必要去跟编译器争用这个字符。

标识符的长度:
在Java语言规范中,标识符的长度是任意的。但是,在Java虚拟机规范中,标识符是有长度限制的。在class文件中,代表标识符的常量字符串存储在CONSTANT_Utf8_info表中,而该表使用两个字节(length项)来表示字符串的长度,由于length是无符号类型,因此最大长度为216−1,即65535。这也就是标识符的最大长度。但是,这个最大长度仅限于除了空字符null以外的ASCII字符(‘\u0001’~‘\u007f’),如果标识符中含有这个范围以外的字符,最大长度将会减少。

要点总结
· 在Java语言中,标识符是区分大小写的,仅当两个标识符的Unicode字符序列完全相同时,这两个标识符才是相同的。
· 标识符的首字符所对应的代码点必须使得Character类的isJavaIdentifierStart方法返回值为true,后续字符(如果存在后续字符的话)所对应的代码点必须使得Character类的isJavaIdentifierPart方法返回值为true。并且不能与关键字、布尔字面常量(true、false)和引用字面常量(null)相同。
· 应该避免在标识符中使用“$”,尽管“$”可以在标识符中使用,但可能会与编译器的命名相冲突。
· 当标识符的所有字符都是除了null之外的ASCII字符时,其最大长度可以达到65535,如果超过了这个长度,编译器将产生错误信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值