来源:Ivan_Pig点这里返回目录


Lesson 6: Operators

目录:

- 连接操作符
- 运算操作符
- 一元操作符
- 关系操作符
- 条件操作符
- 类型比较操作符

-连接操作符

链接操作符”=”是你最常用的操作符之一。使用”=”,可以将其右边的值赋给左边的变量。

result = num1 + num2;
days = ["Mon","Tue","Wed","Thu","Fri"];

在前面的章节里面,你已经使用了很多次了。

-运算操作符

运算操作符提供加,减,乘,除的功能。”mod”操作符为求余!

+ (additive operator)
- (subtraction operator)
* (multiplication operator)
/ (division operator)
mod (remainder operator)

下面提供一些例子:

var result = 1 + 2; // result is now 3
println(result);  

result = result - 1; // result is now 2
println(result);  

result = result * 2; // result is now 4
println(result);  

result = result / 2; // result is now 2
println(result);  

result = result + 8; // result is now 10
println(result);  

result = result mod 7; // result is now 3
println(result);

你还能同时使用运算操作符和连接运算符构成复合运算符。例如 result += 1; 和 result = result+1;两者的效果都是result加上了1.

var result = 0;
result += 1;
println(result); // result is now 1  

result -= 1;
println(result); // result is now 0  

result = 2;
result *= 5; // result is now 10
println(result);  

result /= 2; // result is now 5
println(result);

只有”mod”操作符不能这么写。如果你想求一个数被2除的余数,你需要这么写:

result = result mod 2;

-一元操作符

大部分的操作符需要两个操作数;而一元操作符只需要一个操作数,对操作数进行加/减操作,取负数,如果操作数为boolean,则可以取逻辑反,等操作。

- Unary minus operator; negates a number
++ Increment operator; increments a value by 1
– Decrement operator; decrements a value by 1
not Logical complement operator; inverts the value of a boolean

例子:

var result = 1; // result is now 1  

result--;  // result is now 0
println(result);  

result++; // result is now 1
println(result);  

result = -result; // result is now -1
println(result);  

var success = false;
println(success); // false
println(not success); // true

++/–操作符既可以在操作数的前面,也可以在操作数的后面。result++和++result运算的最后效果都是result加1。唯一的区别是result++是先取值后加1,++result是先加1再取值。如果你只是做简单的+/-,那么你用哪种方法都没有影响。但是,如果你在一个复杂的表达式里面使用,那么两种方法可能会不同。

下面的代码演示他们的区别:

var result = 3;
result++;
println(result); // result is now 4
++result;
println(result); // result is now 5
println(++result); // result is now 6
println(result++); // this still prints prints 6!
println(result); // but the result is now 7

-关系操作符

关系操作符用来比较一个操作数是否大于,小于,等于或不等于另一个数。

== equal to
!= not equal to
> greater than
>= greater than or equal to
< less than
<= less than or equal to

下面的代码测试这些操作符:

def num1 = 1;
def num2 = 2;  

println(num1 == num2); // prints false
println(num1 != num2); // prints true
println(num1 > num2);  // prints false
println(num1 >= num2); // prints false
println(num1 < num2);  // prints true
println(num1 <= num2); // prints true

-条件操作符

条件and和条件or是用在两个boolean表达式间的操作符。这两个操作符有”short-circuiting”的特性,就是第二个操作数只有在需要的时候才执行。举个例子,对于and操作符,如果第一个表达式的执行结果是false,那么第二个表达式就不会执行了,当第一个表达式为true的时候,第二个表达式才会执行。

and
or

下面是例子:

def username = "foo";
def password = "bar";  

if ((username == "foo") and (password == "bar")) {
     println("Test 1: username AND password are correct");
}  

if ((username == "") and (password == "bar")) {
     println("Test 2: username AND password is correct");
}  

if ((username == "foo") or (password == "bar")) {
     println("Test 3: username OR password is correct");
}  

if ((username == "") or (password == "bar")) {
     println("Test 4: username OR password is correct");
}

输出:

Test 1: username AND password are correct
Test 3: username OR password is correct
Test 4: username OR password is correct

-类型比较操作符

instanceof操作符判断一个对象是否是一个特定的类型。你可以使用此操作符来判断,一个对象是否是一个特定的类。

def str1="Hello";
println(str1 instanceof String);  // prints true  

def num = 1031;
println(num instanceof java.lang.Integer); // prints true

在学了以后的类和继承章节后,你会发现这个操作符很有用。


Lesson 7: Expressions

目录:

- 块表达式
- if表达式
- 范围表达式
- for表达式
- while表达式
- break和continue表达式
- throw, try, catch 和 finally表达式

-块表达式

块表达式包含一系列的声明或表达式,由大括号包围,且以分号隔开。块表达式的值是块里面最后的表达式的值。如果块里面没有表达式,块表达式就是Void类型。注意,var和def是表达式。

下面的块表达式添加了一些成员并把结果存放在total变量里面:

var nums = [5, 7, 3, 9];
var total = {
     var sum = 0;
     for (a in nums) { sum += a };
     sum;
}
println("Total is {total}.");

输出:

Total is 24.

第一行 (var nums = [5, 7, 3, 9];) 声明了一个整型的序列。

接着声明了一个total变量来保存序列里面的值的总和。

块表达式,为大括号里面的所有代码:

{
var sum = 0;
     for (a in nums) { sum += a };
     sum;
}

在括号里面,第一行声明了一个变量sum,来保存序列里面的成员。第二行循环序列,取出序列里的成员与sum相加。最后一行,为返回块表达式的值。

-if表达式

if表达式表达式可以根据特定的条件执行特定的一段代码。

例如,下面的代码根据年龄设置票价。12-65岁票价10元。老人和小孩5元。5岁一下的免费。

def age = 8;
var ticketPrice;  

if (age < 5 ) {
     ticketPrice = 0;
} else if (age < 12 or age > 65) {
     ticketPrice = 5;
} else {
     ticketPrice = 10;
}
println("Age: {age} Ticket Price: {ticketPrice} dollars.");

年龄设为8时,输出如下:

Age: 8 Ticket Price: 5 dollars.

程序流程如下:

<strong>if (age < 5 ) {
     ticketPrice = 0;
} </strong>else if (age > 5 and age < 12) {
     ticketPrice = 5;
} else {
     ticketPrice = 10;
}

如果年龄小于5,票价为0.那么程序就会跳过其他的判断,直接打印出结果。
如果不小于5,那么程序跳转到第二个判断。

if (age < 5 ) {
     ticketPrice = 0;
} else if (age > 5 and age < 12) {
     ticketPrice = 5;
} else {
     ticketPrice = 10;
}

如果年龄在5-12岁,票价为5.
如果大于或等于12岁,就跳到了else语句里面。

if (age < 5 ) {
     ticketPrice = 0;
} else if (age > 5 and age < 12) {
     ticketPrice = 5;
} else {
     ticketPrice = 10;
}

这个块,只有在前面的条件都不符合的情况下才执行。设置12岁及以上的人的票价为12.

Note: 上面的代码可简写为:

ticketPrice = if (age < 5) 0 else if (age > 5 and age < 12) 5 else 10;

你最好掌握,在后面的章节将会用到。

-范围表达式

在序列那一讲,你了解到了如果简单的定义一个有序的数字序列。

var num = [0..5];

理论上,[0..5]就是个范围表达式。默认的,数字间递增1,但是你可以使用step关键字来修改递增的值。例如,定义一个1到10之间的所有奇数的序列。

var nums = [1..10 step 2];
println(nums);

输出

[ 1, 3, 5, 7, 9 ]

要创建一个递减的范围,请保证第二个数要小于第一个数,并且要将step设为一个负数。

var nums = [10..1 step -1];
println(nums);

输出:

[ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]

如果你创建了一个递减的序列,但是没有设定step的值,你将会得到一个空的序列。

下面的代码:

var nums = [10..1 step 1];
println(nums);

将会在编译的时候出现警告:

range.fx:1: warning: empty sequence range literal, probably not what you meant.
var nums = [10..1 step 1];
^
1 warning

如果你忽略了警告,那你得到的是个空的序列。

-for表达式

另一个和序列有关的表达式是for表达式。for表达式提供一个简便的循环结构来遍历序列里的所有项。

下面是个例子:

var days = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];  

for (day in days) {
     println(day);
}

输出:

Mon
Tue
Wed
Thu
Fri
Sat
Sun

让我们来分析这段代码。for表达式以for开头

<strong>for</strong> (day in days) {
     println(day);
}

days变量是序列的名称,for表达式将遍历此序列。

for (day in <strong>days</strong>) {
     println(day);
}

day变量依次持有序列里的项

for (<strong>day</strong> in days) {
     println(day);
}

注意,day变量并不需要声明就可以在for表达式里面使用了。并且,for表达式结束后,day无法被访问。程序员经常给临时变量一个简短的名字以示区别。

在前面的例子里面,for没有显示的返回值。但是for返回一个序列。下面的两个例子来使用for来从一个序列创建另一个序列。

// Resulting sequence squares the values from the original sequence.
var squares = for (i in [1..10]) i*i;   

// Resulting sequence is ["MON", "TUE", "WED", and so on...]
var capitalDays = for (day in days) day.toUpperCase();

注意了,toUpperCase方法由String提供。你能在API里找到完整定义。

-while表达式

另一个循环表达式是while表达式。和for不同,不是遍历序列的,而是根据条件来循环的。while是一个语义(syntactically)上的表达式,为Void类型,没有返回值。

给个例子:

var count = 0;
while (count < 10) {
    println("count == {count}");
    count++;
}

输出:

count == 0
count == 1
count == 2
count == 3
count == 4
count == 5
count == 6
count == 7
count == 8
count == 9

第一行声明了一个变量count并初始化为0

<strong>var count = 0;</strong>
while (count < 10) {
    println("count == {count}");
    count += 1;
}

接着开始while表达式。表达式循环,知道count为10为止。

var count = 0;
<strong>while (count < 10) {</strong>
    println("count == {count}");
    count += 1;
}

循环体中,打印count值,并加1。

var count = 0;
while (count < 10) {
    <strong>println("count == {count}");
    count += 1;</strong>
}

当count为10时,循环结束。如果要创建一个无限循环,条件里改为true关键字,就像这样: while(true){}

-break和continue表达式

break和continue表达式和循环表达式有关。这两个表达式是作用于循环上的:break终止循环,continue跳过当前的循环。

brean和continue是语义(syntactically)表达式。他们为Void类型,没有返回值。

例子:

for (i in [0..10]) {
     if (i > 5) {
          break;
     }  

     if (i mod 2 == 0) {
          continue;
     }  

     println(i);
}

输出:

1
3
5

如果没有if表达式,程序将会简单的输出:0到10。

如果只有第一个if表达式,程序将在i比5大的时候跳出循环。

if (i > 5) {
     break;
}

程序将只打印1到5。

加上第二个if后,程序会间隔的执行循环。

if (i mod 2 == 0) {
     continue;
}

这里,表达式只有在i为偶数的时候才执行continue。当continue执行的时候,println()将不会被执行,也就不会打印了。

-throw, try, catch 和 finally表达式

在现实世界的应用里面,有时可能会出现不正常的程序流程。例如,一段脚本读取文件,但是文件找不到,那么这段脚本就无法执行了。我们将这种情况称为”异常”。

Note:异常是对象。一般可以从字面意思上知道他们表示的是什么异常。(例如,FileNotFoundException表示了无法找到文件。)但是,下面的例子并不是来展现各种各样的异常的。所以,以普通的异常来说明throw, try, catch, 和 finally

下面的脚本定义一个方法并抛出异常:

import java.lang.Exception;  

foo();  

println("The script is now executing as expected... ");  

function foo() {
     var somethingWeird = false;  

     if(somethingWeird){
          throw new Exception("Something weird just happened!");
     } else {
          println("We made it through the function.");
     }
}

运行,输出:

We made it through the function.
The script is now executing as expected…

但是,将变量改为true,异常将被抛出。在运行时,脚本将会中断,并打印如下信息:

Exception in thread “main” java.lang.Exception: Something weird just happened!
at exceptions.foo(exceptions.fx:10)
at exceptions.javafx$run$(exceptions.fx:3)

要阻止程序被异常终端,我们需要用try/catch来捕获foo()抛出的异常。由名字来看,这个表达式的作用是,try执行一些代码,但是发生了问题就catch到了一个异常。

try {
     foo();
} catch (e: Exception) {
     println("{e.getMessage()} (but we caught it)");
}

现在,程序打印:

Something weird just happened! (but we caught it)
The script is now executing as expected…

还有个finally块 (这个不能算得上是个表达式),不论是否有异常,finally块里的代码都会被执行。finally块一般是用来做清理工作的。

try {
     foo();
} catch (e: Exception) {
     println("{e.getMessage()} (but we caught it)");
} finally {
     println("We are now in the finally expression...");
}

程序输出:

Something weird just happened! (but we caught it)
We are now in the finally expression…
The script is now executing as expected…


Lesson 8: Data Binding and Triggers

目录

- 绑定概述
- 绑定和对象
- 绑定和方法
- 绑定序列
- 替换触发器

-绑定的概念

bind关键字将目标变量的值和一个范围表达式联系(bound expression)起来。范围表达式可以是基本类型,一个对象,方法的返回值或者一个表达式的返回值。
下面的章节将一个个的举例。

-绑定和对象

现实中,大部分情况下,你要使用数据绑定,来同步GUI和它的数据
(GUI是 《Building GUI Applications with JavaFX》的主题; 下面我们演示的是简单的非GUI例子)

我们从简单的开始:下面的脚本中,将变量x绑定到了变量y上,改变x的值,然后打印出y的值。由于变量被绑定了,y的值会自动的更新为新值。

var x = 0;
def y = bind x;
x = 1;
println(y); // y now equals 1
x = 47;
println(y); // y now equals 47

注意,我们是用def声明的变量y。这样的话,能够阻止直接修改y的值!(y的值允许以绑定的方式修改)在下面的绑定对象的例子中,你应该以相同的方式来绑定。

var myStreet = "1 Main Street";
var myCity = "Santa Clara";
var myState = "CA";
var myZip = "95050";  

def address = bind Address {
     street: myStreet;
     city: myCity;
     state: myState;
     zip: myZip;
};  

println("address.street == {address.street}");
myStreet = "100 Maple Street";
println("address.street == {address.street}");

当你修改myStreet的值的时候,address对象里的street变量的值也会随之改变。

address.street == 1 Main Street
address.street == 100 Maple Street

注意,myStreet值的改变将导致一个新的Address对象被创建,并且这个对象被赋予了address变量。如果想改变值,但是不要新创建一个新的Address对象,直接在实例变量上进行绑定。

def address = bind Address {
     street: bind myStreet;
     city: bind myCity;
     state: bind myState;
     zip: bind myZip;
};

如果你已明确的对实例变量进行了绑定,那么你可以省略第一个bind(Address前面的那个bind)

def address = Address {
     street: bind myStreet;
     city: bind myCity;
     state: bind myState;
     zip: bind myZip;
};

-绑定和方法

前面已经讨论过方法了,但是你还要学习一下bound functions 和non-bound functions之间的差别。

下面的方法,它创建并返回一个Point对象:

var scale = 1.0;  

bound function makePoint(xPos : Number, yPos : Number) : Point {
     Point {
          x: xPos * scale
          y: yPos * scale
     }
}  

class Point {
     var x : Number;
     var y : Number;
}

这就是所谓的bound function,因为它以bound关键字开头。

Note: bound关键字不是替代bind关键字的。在下面的例子里面,将会同时使用bound和bind.

接着,我们添加一些代码来调用这个方法并测试绑定:

var scale = 1.0;  

bound function makePoint(xPos : Number, yPos : Number) : Point {
     Point {
          x: xPos * scale
          y: yPos * scale
     }
}  

class Point {
     var x : Number;
     var y : Number;
}  

var myX = 3.0;
var myY = 3.0;
def pt = bind makePoint(myX, myY);
println(pt.x);  

myX = 10.0;
println(pt.x);  

scale = 2.0;
println(pt.x);

输出:

3.0
10.0
20.0

我们来分析这段代码:

代码:

var myX = 3.0;
var myY = 3.0;
def pt = bind makePoint(myX, myY);
println(pt.x);

初始化变量myX和myY为3.0。这些值将已参数的方式传给makePoint方法。makePoint方法前面的bind关键字,将新创建的Point对象pt绑定到了makePoint方法的返回值上。(
The bind keyword, placed just before the invocation of makePoint, binds the newly created Point object (pt) to the outcome of the makePoint function.)[这里字面意思是pt绑定到了makePoint的返回值上去了,我理解的是返回值绑定到pt上去。]

下一段代码:

myX = 10.0;
println(pt.x);

修改myX的值为10.0然后打印pt.x。pt.x也将输出10.0

最后一段代码:

scale = 2.0;
println(pt.x);

修改scale的值并打印出pt.x。现在pt.x的值是20.0,如果我们将方法前面的bound关键字去掉(就变成了non-bound function),输出将会是:

3.0
10.0
10.0

这是因为non-bound functions只有在它的参数发生改变的时候才会重新执行。而scale并不是方法的参数,所以改变scale的值不会对方法产生影响。

-绑定序列

你还可以在表达式上使用bind。要解释这一点,让我们先来定义两个序列并打印他们的值。

var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
printSeqs();  

function printSeqs() {
     println("First Sequence:");
     for (i in seq1){println(i);}
     println("Second Sequence:");
     for (i in seq2){println(i);}
}

seq1有10个项(1到10).seq2也有10个项,并且和seq1的值相等,但是我们给每项都乘以了2,所以值将是seq1的两倍。

输出:

第一个序列:
1
2
3
4
5
6
7
8
9
10
第二个序列:
2
4
6
8
10
12
14
16
18
20

只要在for关键字前面加上bind关键字,我们就能将两个序列绑定了。

def seq2 = bind for (item in seq1) item*2;

现在的问题就是“如果seq1里面发生改变了,seq2里面的所有项或部分项会有影响吗?”我们可以在seq1的末尾插入一项(值为11)来测试一下。

var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
insert 11 into seq1;
printSeqs();  

function printSeqs() {
     println("First Sequence:");
     for (i in seq1){println(i);}
     println("Second Sequence:");
     for (i in seq2){println(i);}
}

输出:

第一列:
1
2
3
4
5
6
7
8
9
10
11
第二列:
2
4
6
8
10
12
14
16
18
20
22

输出显示了在seq1中插入11后,对seq2里的前面10项并没有产生影响,而是自动的加到了seq2的后面,并且值是22.

-替换触发器(replace trigger)

替换触发器是一段特殊的代码,它和变量关联,并且只要变量的值发生改变就会执行。下面的例子展示了基本的语法:定义了一个password变量并和一个替换触发器关联,当password改变的时候,触发器打印出password的新值。

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

这里触发器被触发了两次:第一次,当password被初始化为”foo”的时候,以及第二次当值变成”bar”时。
注意,oldValue变量持有触发器执行前变量的值。你能够任意命名此变量,我们使用oldValue只是因为它比较直观。


Lesson 9: Writing Your Own Classes

目录

- 例子:Customer
- 从其他类继承

-例子:Customer

在Writing Scripts章节,你学会了如何使用对象。但是,当时我们是让你去下载了.class文件,以使编译器知道怎么去创建Address和Customer对象。在下面的例子里面,我们重新来看代码,新增缺少的类定义,以保证所有的代码都能通过编译。

def customer = Customer {
     firstName: "John";
     lastName: "Doe";
     phoneNum: "(408) 555-1212"
     address: Address {
          street: "1 Main Street";
          city: "Santa Clara";
          state: "CA";
          zip: "95050";
     }
}  

customer.printName();
customer.printPhoneNum();
customer.printAddress();  

class Address {
     var street: String;
     var city: String;
     var state: String;
     var zip: String;
}  

class Customer {
     var firstName: String;
     var lastName: String;
     var phoneNum: String;
     var address: Address;  

    function printName() {
        println("Name: {firstName} {lastName}");
    }  

    function printPhoneNum(){
        println("Phone: {phoneNum}");
    }  

    function printAddress(){
        println("Street: {address.street}");
        println("City: {address.city}");
        println("State: {address.state}");
        println("Zip: {address.zip}");
    }
}

如果你已经掌握了变量和方法,那么这段代码你应该很容易理解。Address类声明了street, city, state, and zip实例变量,且都是String类型。Customer也声明了一些实例变量还有一些方法来打印他们的值。因为这些变量和方法都是在类里面声明的,所以在你创建的任意的Address和Customer类的实例都能够访问到他们。

-从其他类继承

你还能创建一个类,并从其他类里面继承变量和方法。例如,假设在银行里面保存,验证一个帐户。每个帐户都有帐户号码和金额。你能查询金额,存款或取款。我们能对此建模,抽象出一个基本的帐户类并且给与通用的变量和方法:

abstract class Account {  

     var accountNum: Integer;
     var balance: Number;  

     function getBalance(): Number {
          return balance;
     }  

     function deposit(amount: Number): Void {
          balance += amount;
     }  

     function withdraw(amount: Number): Void {
          balance -= amount;
     }
}

我们将此类标记为抽象的,这样的话,Account对象是不能够直接被创建出来的(继承的类只需要savings accounts or checking accounts)

accountNum和balance 变量持有帐户号码和当前的帐户金额。 而余下的那些方法则提供了基本的取钱,存钱和查询金额的功能。 T

我们能定义一个SavingsAccount,并使用extends关键字来继承得到这些变量和方法。

class SavingsAccount extends Account {  

     var minBalance = 100.00;
     var penalty = 5.00;  

     function checkMinBalance() : Void {
          if(balance < minBalance){
               balance -= penalty;
          }
     }
}

因为SavingsAccount是Account的子类,所以它将自动的包含Account里面所有的实例变量和方法。这可以使我们专注于SavingsAccount所特有的属性和方法(比如,如果帐户金额小于100,则无法取款)

类似的,我们再定义一个CheckingAccount类继承Account

class CheckingAccount extends Account {  

     var hasOverDraftProtection: Boolean;  

     override function withdraw(amount: Number) : Void {
          if(balance-amount<0 and hasOverDraftProtection){  

               // code to borrow money from an overdraft account would go here  

          } else {
               balance -= amount; // may result in negative account balance!
          }
     }
}

这里定义了一个变量,来判断帐户持有者能否透支。(如果取款的时,取款金额超过了帐户现有金额,那么透支功能将起作用,判断此用户是否能透支)注意,在这里我们修改了继承的withdraw方法,这就是方法的覆盖,所以方法前面需要加上override关键字


Lesson 10: Packages

目录

- Step 1: 选择一个包名
- Step 2: 创建目录
- Step 3: 添加包声明
- Step 4: 添加访问权限
- Step 5: 编译源码
- Step 6: 使用类

到这里,你对javaFX的基础应该比较熟悉了。但是对于源文件的存放,你可能还不是很清楚(你现在可能是用的一个文件夹来存放所有的例子代码)我们将把代码放到包中,来改变你对存放代码的认识。(We can improve our overall organization by placing our code into packages.)

包能够让你按功能来将代码分类保存。它还给你的类一个唯一的命名空间。我们将在下面一步步的来将Address类存放到一个特殊的包内。

-Step 1: 选择一个包名

在我们修改代码前,我们需要给包起个名字。由于我们的Address类是假设用在addressbook应用上的,所以我们使用”addressbook”作为包名。

-Step 2: 创建目录

接着,我们必须创建一个addressbook目录。这个目录里面将包含所有我们设计的属于addressbook这个包的.fx文件。你可以在任意的地方创建目录。我们在例子里面使用
/home/demo/addressbook,但是脚本必须在一个和包名相同的目录里面,这里就是
addressbook

-Step 3: 添加包声明

现在,到addressbook目录里面创建Address.fx源代码文件。粘贴下面的代码到源代码文件里面去。第一行提供了一个包声明,这将表示这个类属于addressbook这个包。

package addressbook;  

class Address {
     var street: String;
     var city: String;
     var state: String;
     var zip: String;
}

注意了,如果源代码里面有包声明,它必须在其它代码的前面,即源码文件的第一行。没个源文件只能有一个包声明。

-Step 4: 添加访问权限

接下来,我们需要在Address的类和变量上加上pulic关键字。

package addressbook;  

public class Address {
     public var street: String;
     public var city: String;
     public var state: String;
     public var zip: String;
}

这个关键字是5个访问限制修饰符里面的一个。我们会在下一节介绍访问限制修饰符。现在你只要知道,public关键字使得这段代码能够被其他的类和脚本访问。

-Step 5:编译源码

依然在addressbook目录里面,像平时一样使用javafxc Address.fx命令编译即可。编译完成后,这个文件夹里面将包含编译得到的.class文件。

-Step 6: 使用类

现在我们能够测试修改后的Address类了。但是我们必须先返回到父目录 /home/demo。这里我们创建一个简单的脚本packagetest.fx类测试如何使用addressbook包。

我们有两种方法来访问这个类:

// Approach #1  

addressbook.Address {
     street: "1 Main Street";
     city: "Santa Clara";
     state: "CA";
     zip: "95050";
}

Approach #1使用完整的类名创建了一个对象(addressbook.Address)。对比另一种方法,这种方法比较的笨拙,但是你还是需要知道有这种写法。

// Approach #2
import addressbook.Address;  

Address {
     street: "1 Main Street";
     city: "Santa Clara";
     state: "CA";
     zip: "95050";
}

Approach #2使用import关键字,import关键字允许你以简短的名字在脚本里面使用类。当程序比较大时,推荐使用这种方法,因为它是self-documenting。


Lesson 11: Access Modifiers

目录

- 默认访问权限
- package访问权限修饰符
- protected访问权限修饰符
- public访问权限修饰符
- public-read访问权限修饰符
- public-init访问权限修饰符

-默认访问权限

当你不提供任何访问权限控制符的时候,就是默认的访问权限,也就是”script-only”.这也是我们在教程里面最常使用的权限。

例子:

var x;
var x : String;
var x = z + 22;
var x = bind f(q);

这一级别的访问权限使得变量只能在脚本内被initialized, overridden, read, assigned, 或 bound.其他文件无法访问。

-package访问权限修饰符

为了让变量,方法或类能被包里的其他代码访问到,使用package访问权限修饰符

package var x;

不要把这和前一节包的声明玳瑁搞混淆。

例子:

// Inside file tutorial/one.fx
package tutorial; // places this script in the "tutorial" package
package var message = "Hello from one.fx!"; // this is the "package" access modifier
package function printMessage() {
     println("{message} (in function printMessage)");
}  

// Inside file tutorial/two.fx
package tutorial;
println(one.message);
one.printMessage();

你能使用下面的命令编译和运行这个例子:

javafxc tutorial/one.fx tutorial/two.fx
javafx tutorial/two

输出:

Hello from one.fx!
Hello from one.fx! (in function printMessage)

-protected访问权限修饰符

protected访问权限修饰符使得变量或方法不仅能时包里的其他代码访问到,还能使不在同一个包内的子类访问到。

例子:

// Inside file tutorial/one.fx
package tutorial;
public class one {
     protected var message = "Hello!";
}  

// Inside file two.fx
import tutorial.one;
class two extends one {
     function printMessage() {
          println("Class two says {message}");
     }
};  

var t = two{};
t.printMessage();

编译运行:

javafxc tutorial/one.fx two.fx
javafx two

输出:

Class two says Hello!

Note:这个访问权限修饰符不能用在类上,这就是为什么我们在类one前面写的是public的原因。

-public访问权限修饰符

一个pulibc的类,变量,方法具有最大可见度,即,它可以被任意的类或脚本访问,不管是不是在一个包内。

例子:

// Inside file tutorial/one.fx
package tutorial;
public def someMessage = "This is a public script variable, in one.fx";
public class one {
     public var message = "Hello from class one!";
     public function printMessage() {
          println("{message} (in function printMessage)");
     }
}  

// Inside file two.fx
import tutorial.one;
println(one.someMessage);
var o = one{};
println(o.message);
o.printMessage();

编译运行:

javafxc tutorial/one.fx two.fx
javafx two

输出:

This is a public script variable, in one.fx
Hello from class one!
Hello from class one! (in function printMessage)

-public-read访问权限修饰符

public-read访问权限修饰符修饰的变量是公共的但是对外是只读的,只能被当前的脚本修改。如果想扩大它的可被修改的权限范围,在前面加上package或protected(ackage public-read 或者 protected public-read)这样的话,可以使得package或protected范围的代码能修改此变量。

例子:

// Inside file tutorial/one.fx
package tutorial;
public-read var x = 1;  

// Inside tutorial/two.fx
package tutorial;
println(one.x);

编译运行:

javafxc tutorial/one.fx tutorial/two.fx
javafx tutorial/two

输出是”1″,这证明了x能够被tutorial/one.fx外的其他代码读取到。

现在我们来试着修改它的值:

// Inside tutorial/two.fx
package tutorial;
one.x = 2;
println(one.x);

结果是编译期错误:

tutorial/two.fx:3: x has script only (default) write access in tutorial.one
one.x = 2;
^
1 error

为了让此代码能运行,我们要扩大x的写的访问权限:

// Inside file tutorial/one.fx
package tutorial;
package public-read var x = 1;  

// Inside tutorial/two.fx
package tutorial;
one.x = 2;
println(one.x);

现在将打印”2″。

-public-init访问权限修饰符

public-init访问权限修饰符修饰的变量能被任何包里的对象初始化。初始化后的写操作权限限制,却是和public-read类似的访问控制。(默认是脚本级别的写操作,前面加上package或 protected就扩大了访问权限了)

例子:

// Inside file tutorial/one.fx
package tutorial;
public class one {
     public-init var message;
}  

// Inside file two.fx
import tutorial.one;
var o = one {
     message: "Initialized this variable from a different package!"
}
println(o.message);

编译:

javafxc tutorial/one.fx two.fx
javafx two

打印出”Initialized this variable from a different package!”, 这证明了其他包内的对象能够初始化message变量。但是,接下来的写操作权限却是script-only,我们不能修改它的值。

// Inside file two.fx
import tutorial.one;
var o = one {
     message: "Initialized this variable from a different package!"
}
o.message = "Changing the message..."; // WON'T COMPILE
println(o.message);

编译出错:

two.fx:12: message has script only (default) write access in tutorial.one
o.message = “Changing the message…”; // WON’T COMPILE
^
1 error

这证明了这个很特别的行为:对象能够被任意对象初始化,但是初始化以后却是受不同的访问级别控制的。

ps:呼!花了一周时间,终于翻译完了!还好语法比较简单,翻译难度不大!如果有时间和精力,还会翻译《Building GUI Applications With JavaFX》。