第二节 变量
在这一节将讲述JavaFX中变量的定义,如脚本变量、实例变量、本地变量、感应变量等内容。
一、变量的定义
如果大家从前面看过来的话应该有印象,我在前面介绍过定义变量使用def和var两个关键词。def定义的变量就好像定义常量一样(在某种意义上来理解),在定义的地方声明并赋予初始值,并且在其它地方都不能改变此初始值,但有一种情况例外,后面会讲到。var定义的变量更像我们日常理解中的变量,其可以在一个地方定义,在多个地方赋值。因此当我们试图定义的变量希望它的值是不能在其它地方进行改变的,我们就使用def来定义,否则我们就要使用var来定义变量。
这里有一个特例需要注意,那就是通过def定义的变量有时也是会动态改变值的,这种情况就是使用bind关键词。如下:
def x = bind y + z;
这里假设y、z都是已经定义的变量。这段代码中就使用到了JavaFX中特有的数据绑定概念,其中关键词bind就是表明x的值和y的值进行了绑定。当y的值改变时,x的值也会自动跟随改变,特别注意是自动跟随改变。这是JavaFX的内部机制来保证的。
总结来说,通过def定义的变量除非进行了数据绑定,否则在定义完成后,其值是不能再发生改变的。
二、变量的种类
上面只是讲了我们可以通过不同的关键词来定义变量,并且介绍了这两个关键词定义的变量的一些差别。但站在编码的角度,我们在程序中定义的变量可以分为脚本变量、实例变量、局部变量。
1、脚本变量
脚本变量是被声明于顶级脚本当中,即不是声明于类的内部或者声明于块的内部。脚本变量在当前整个脚本全域内都能被访问。如果没有加访问域控制,则脚本变量不能被外部脚本访问。只有被访问域控制标识为可以被外部脚本访问的变量,才能被外部脚本访问。访问域控制的关键词有public、protected、package、public-read和public-init。
举例:
在Foo.fx文件中定义有脚本变量noon。
public var noon=123;
现在就可以在Food.fx文件中访问Foo.fx文件中的noon变量。
println(Foo.noon);
注意:脚本变量的生存周期为从脚本被成功装载开始直到程序运行退出为止。
2、实例变量
实例变量就是在类中定义的成员变量。在声明变量的类的内部的方法,包括类的子类都可以直接使用变量名访问本类的实例变量(我更希望称其为类的成员变量,毕竟我们OO的思想灌输了这么多年)。
同脚本变量一样,实例变量也可以通过访问域控制关键词来限制对实例变量的访问,这点和Java中对类成员的访问修饰有异曲同工之效,只是在JavaFX中访问域控制关键词(或者按Java中概念就是访问修饰符)为public、protected、package、public-read和public-init。如果访问修饰符没有被特别指定,则此实例变量只在本脚本范围内可以被访问。
举例说明:
在Foo.fx文件中定义有如下代码:
public class aa{
var a1=123;
}
在Food.fx文件中访问Foo.fx文件中的aa类中的a1实例变量就会报告错误,例如:
def kk=aa{a1:234};
因为a1实例变量没有指定访问修饰符,因此它只能被在Foo.fx文件中访问,超出定义的脚本范围是不能访问的。
但如果对a1实例变量指定访问修饰符为public,则在Food.fx文件中就可以访问a1实例变量了。
注意:实例变量的生存周期为其和类实例的生存周期相同。
3、局部变量
局部变量是被声明在块中。什么是块?在JavaFX中块就是通过一对花括号括起的部分。例如:
……
var aa=123;
……
{ //块的起始
……
var kk=123;
……
} //块的结束
……
以上代码就展示了程序代码中的块,其中变量kk就是存在于块中的局部变量,而变量aa就是属于脚本变量。程序中可以定义若干个块(0个、1个或者更多)。作为在代码中声明的函数体部分,我们也说它是一个块,只是这个块比较特殊。因此在函数体中定义的变量我们也成为局部变量。
局部变量只能被定义它的块内代码访问,对于局部变量来说可以访问的区域就是从块的起始位置到块的结束位置。不同块之间的局部变量是不能相互访问的。同时也不能为局部变量指定访问修饰符。
注意:局部变量的生存周期就是从局部变量定义处开始,一直到当前块结束为止。
4、函数参数、感应变量和其它表达式参数
函数参数、感应变量和其它表达式参数都一类较特殊的变量。
函数参数其生存周期只在函数体内,函数体结束其生存周期结束。
感应变量专指For表达式中的变量,其生存周期也只是在For表达式的循环体内有效。例如:
for(a in [1..10]){println(a);}
如上代码,变量a就是感应变量,其生存周期在循环结束后就消失了。
在JavaFX还有一下表达式可以在其中定义一些变量,这些变量的生存周期就是在表达式执行完成后就结束。
三、变量定义的语法图
上面介绍了变量的定义细节,现在总结变量定义的方法,并参考官方网站的模式给大家提供一个语法图。
访问修饰符包括如下:
package |
用此访问修饰符定义的变量可供同一个软件包中的其他代码访问。 |
protected |
用此访问修饰符定义的变量可供同一个软件包中的其他代码以及任何软件包中的子类访问。 |
public |
用此访问修饰符定义的变量可见性最高,可以从任何软件包中的任何类或脚本对其进行访问。 |
public-read |
用此访问修饰符定义的变量可公开读取但(在默认情况下)只能从当前脚本内部写入的变量。为了扩大该变量的写入访问级别,可以在该修饰符前面添加 package 或 protected 修饰符(即 package public-read 或 protected public-read)。这会将该变量的写入访问级别设置为 package 或 protected。 |
public-init |
用此访问修饰符定义的变量可以由任何软件包中的对象字面值公开初始化的变量。但是,后续的写入访问将按照与 public-read 相同的方式进行控制(默认情况下写入访问级别是脚本级别,但是该修饰符前面的 package 或 protected 将相应地扩大访问级别)。始终可以从任何软件包读取此变量的值。 |
注意:如果使用def定义变量,就必需立即对其进行初始化。当使用bind关键词绑定表达式时,当绑定变量的值发生改变时,该变量的值将自动获得更新;当使用with inverse关键词时,值表达式必需是一个简单的变量并且改变量的值会随着值表达式的变化而自动获得更新。
四、on replace的语法图
替换触发器是附加到变量的任意代码块,一旦变量的值发生变化,它们就会执行。
以上通过几个摘自官方网站上的示例来说明替换触发器用法和运行机制。
var password = "foo" on replace oldValue {
println("\nALERT! Password has changed!");
println("Old Value: {oldValue}");
println("New Value: {password}");
};
password = "bar";
执行以上代码得到如下输出结果:
ALERT! Password has changed!
Old Value:
New Value: foo
ALERT! Password has changed!
Old Value: foo
New Value: bar
从这个输出结果可以看到,替换触发器不管是初始化时还是当值被改变是都会触发执行。通过触发器可以获得变量的旧值和新值。
再来看个和序列有关的例子:
var seq = ['A', 'B', 'C', 'D', 'E', 'F'] on replace oldValue[firstIdx .. lastIdx] = newElements {
println("replaced {oldValue}[{firstIdx}..{lastIdx}] by {newElements} yielding {seq}")
}
seq[3] = '$';
insert '#' into seq;
delete '$' from seq;
delete seq[2];
seq[1..4] = ['X', 'Y'];
delete seq;
执行以上代码的结果如下:
replaced [0..-1] by ABCDEF yielding ABCDEF
replaced ABCDEF[3..3] by $ yielding ABC$EF
replaced ABC$EF[6..5] by # yielding ABC$EF#
replaced ABC$EF#[3..3] by yielding ABCEF#
replaced ABCEF#[2..2] by yielding ABEF#
replaced ABEF#[1..4] by XY yielding AXY
replaced AXY[0..2] by yielding
通过此代码执行结果我们不难发现替换触发器中各变量值的作用和含义。
五、预定义变量
JavaFX预定义了三个变量:__PRO
__PRO
__FILE__变量中存放的是当前脚本文件的文件路径。
__DIR__变量中存放的是当前脚本所在的目录。