《编程导论(Java)·2.2.3 变量的声明模型》

PL4 声明模型(绑定、可见性、作用域和生命期)


1. 变量的含义

在编程语言的教学中,yqj2065经常困惑:是否应该使用比喻——例如将变量比喻成数学中的变量概念。不知道从哪位老先生开始,有这样的说法:“不同于常量,变量的值是可以改变的”。如果你是我的学生,也这样说,嗯,你过来,我保证不打死你。

在汇编语言中,程序员就开始使用变量例如AX、BX以表示操作数的存放地址,而该地址中保存实际的值。变量(variable)表示一个存储场所,是源代码中保存值的符号化地址

请记住变量和值的区别:变量是存储场所,是一个杯子或者盒子,age、dog1这些变量(名)是杯子上面印的字,而值是杯子中装的东西(水、点心或者遥控器)。

从其值是否可变,我宁可说:变量是(其值)可变的变量,而final variable是其值不可变的变量。

所以,恳请老师们、作者们在介绍变量时,不要将它和常量(编程导论中使用“文字”、字面值或常量值这样的术语——见1.2.3)联系在一起。

引用类型的变量称为引用变量(reference variable) ,如Dog dog1或String name中声明的dog1和name,它们被分配同样大小的内存空间,一般是32位(与JVM的实现相关)。引用类型的变量保存的值即引用值,简称引用(reference)。【一些简单的概念问题解决后,就不会出现这样的困惑:Java中方法的参数是否能够按引用传递。】

2. 变量的绑定

如:privateString name ;

    publicstatic final int X = 123,Y=456;

在前面的2.2.1中,介绍了 1. 变量与类型的绑定。这里就简单地介绍变量的 声明语句(declaration statements )格式。

【变量的命名,除了遵照标识符的通用语法外(如不能够以数字、*&^标点开头),通常采用某种命名风格。过去曾流行过匈牙利命名法,也就是在变量名前加上其数据类型的缩写,如iAge、fNum等;也有人建议对成员变量全部加m前缀、局部变量加_前缀。】(我在书中喜欢添加这方面的内容)

3. 变量的作用域

变量的作用域(scope)指可以访问该变量的源代码区域,或变量在程序中起作用的范围或区间。这种词法作用域(lexical scoping)最早在Lisp1.5中引入,后来加入到了Algol语言中。受它们的影响,其他语言都采用了作用域技术。在源代码中,程序员们都喜欢缩排各个作用域以增强代码的可读性。

使用作用域的最基本原因,是因为简单好用的变量名没有想象中的那么多,

有了作用域后,一个程序中可以有很多相同的变量名,它们和平共处——前提是拥有自己的作用域。变量的作用域由变量的声明语句在源代码中的位置决定。(许多知识点先列出,后面再详解)

Java中按照作用域将变量划分为4种,它们的声明位置和作用域如下:

³       成员变量/域(member variable):在类体中声明,其作用域是整个类。成员变量包括静态域和实例域。对于实例域而言,静态方法或静态块是其禁区,它不能够在静态环境中被使用。在声明域时,还可以界定该变量的其他特征。如访问修饰符界定其他对象能否访问该变量;static标识该变量为静态域;final表示该变量为不可变量;transient变量,在对象序列化时被排除在外;volatile用于指出变量的值可以被不同的线程所修改。实例域作为对象的一部分,在创建对象的时候,至少被初始化为默认值,不需要特意地初始化它也可以使用。在类载入时,静态域被初始化。

³       形参(formal arguments):在方法或构造器的形参表中声明,其作用域是方法或构造器的整个方法体。形参是占位符,无法使用赋值语句对它进行初始化而是依靠实参来赋值。

³       局部变量(local variable):在方法体或者方法体的某一代码块中声明,其作用域是从赋值开始到随后遇到的第一个右花括号即“}”。也可以简单地说,局部变量作用域从被定义的语句到包围其定义的最小块(block)的结束处。程序员必须显式地使用赋值语句将局部变量初始化,才能够在后面的代码中使用它。

³       异常处理参数(exception handler parameters):它在catch子句中声明,一般形式为try{}catch(异常类型 异常处理参数){}。当try中出现异常时,JVM将捕获的异常对象与一系列catch子句的参数类型进行匹配,以找到需要执行的catch块中的代码。其他方面与形参相同。

变量与它的值绑定,包括变量初始化、赋值语句和++、--操作符。赋值操作要求类型兼容,兼容性由编译器的类型检查保证。

4. 可见性与变量隐藏

不同变量的作用域可以嵌套,外面的作用域包含内部的作用域。与作用域直接相关的问题有两个:1.哪些位置的代码可访问该变量,2.能否用简单地变量名访问它。

变量的可见性即可访问性。通用规则是:变量在其作用域内可访问,而对于作用域外的程序,尽量使其不可访问。类的成员变量按照其访问修饰符的不同具有不同的可见性,但是通常将实例变量修饰为private,使得它们在类之外不可见。局部变量(形参、异常处理参数)仅在其作用域中可见,在作用域外不能够被使用。

总之,变量的可访问性很直观。

如果存在两个同名的变量,使得一个变量暂时不能通过变量名直接访问,称为变量隐藏(hiding)。Java中关于变量隐藏有几条规则:

³       父类中声明的域,不管其访问权限如何或是否被继承,不管是静态的还是实例的,子类将隐藏父类中同名的域,而且两者的数据类型是否相同也无关紧要。例子见[6.3.2域的继承和隐藏]。

³       一个类中的域不得同名。

³       不同作用域的局部变量互不影响。因为它们彼此没有任何关系。

³       一个局部变量(包括形参)的作用域中不允许存在同名的变量,因为Java语言的设计者认为,那样会使程序产生了混淆。例如整个方法体中,不允许出现与形参同名的局部变量,否则编译器会报错。

³       不鼓励局部变量隐藏域。域的作用域是整个类,在局部变量所处的独立王国中,局部变量能够隐藏域。例如Dog有域int age,在Dog中定义一个方法foo(),其中声明了局部变量age。

    voidfoo(){

        int age=80;

       System.out.println(age);

       System.out.println(this.age);

    }

使用变量名age直接访问的是局部变量,域被隐藏。但是域仍然存在,可以用this关键字访问它。局部变量隐藏域,通常被视为不好的编程风格。

³       鼓励set方法中形参隐藏域。

set方法都带有参数,一般用于设置域的值。set方法的形参隐藏域而且被视为良好的习惯,这样省去了为等价的变量取不同名字的麻烦。

5. 生命期

变量的生命期(lifetime)指在程序执行时可以访问该变量的时间。因为源代码决定了程序的运行,故变量的作用域决定了变量生命期。

Java中按照生命期将变量划分为3种:

³       实例域:实例成员变量是(该类)对象的一部分,每一个对象都拥有自己的、保存该实例域的内存空间。只要对象存在,则对象的实例域就存在。new表达式创建对象为对象诞生的时间,并在Java堆中分配内存。对象的销毁时间由垃圾回收器管理,程序员不需要知道其精确的销毁时间。逻辑上,对象不被任何引用所指向,就可以认为它被销毁。

³       静态域:静态域属于类,它在载入类时被创建,只要类存在,则类的静态域就存在。它由所有对象共享,对象不拥有自己的副本。

³       局部变量(及形参、异常处理参数):它在某一个局部(方法体或者块)中声明,因此每一次执行该声明(第一次赋值)语句时,就创建局部变量,局部执行完毕则销毁。局部变量在栈中分配内存。

注意,在讨论变量的生命期时,不考虑变量是基本类型变量还是引用变量。有人问,基本类型变量和引用变量在哪里分配空间?不是一个有效的问题。

对变量的内存分配不看类型看位置,变量的生命期不看数据类型看位置。总之,对象的空间在堆上,局部变量的空间在栈上。基本类型变量和引用变量都可能分配在stack或heap中,关键是看它们声明的位置——到底是域还是局部变量。

例如class A{inti;String s ="hi";},i和s作为对象的域,在堆中拥有自己的空间。

而foo(){int i;String s="hi";}中的i和s作为局部变量,在栈上分配空间。


声明模型界定了语言中声明标识符需要遵循的准则,包括绑定、作用域/范围、生命期、可见性。本节以变量声明为例介绍声明模型。包和类名的声明参考[第6章封装]、方法的定义参考[2.3.2 方法同名问题]。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值