运算符
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html
现在你已经学会了如何声明和初始化变量,你可能想知道如何对其进行操作。学习 Java 编程语言的运算符是一个很好的开始。运算符是执行特定操作的特殊符号,作用于一个、两个或三个操作数,然后返回一个结果。
当我们探索 Java 编程语言的运算符时,提前了解哪些运算符具有最高优先级可能会对您有所帮助。下表中的运算符按照优先级顺序列出。出现在表格顶部的运算符优先级较高。优先级较高的运算符在相对较低优先级的运算符之前进行评估。同一行上的运算符具有相同的优先级。当具有相同优先级的运算符出现在同一表达式中时,必须有规则来决定哪个首先进行评估。除了赋值运算符之外的所有二元运算符都是从左到右进行评估;赋值运算符是从右到左进行评估。
运算符优先级
运算符 | 优先级 |
---|---|
后缀 | *expr* *expr*-- |
一元 | *expr* --*expr* *expr* -*expr* ~ ! |
乘法 | * / % |
加法 | - |
移位 | << >> >>> |
关系 | < > <= >= instanceof |
相等 | == != |
按位与 | & |
按位异或 | ^ |
按位或 | | |
逻辑与 | && |
逻辑或 | || |
三元 | ? : |
赋值 | = = -= *= /= %= &= ^= |= <<= >>= >>>= |
在通用编程中,某些运算符比其他运算符更频繁地出现;例如,赋值运算符"=
“比无符号右移运算符”>>>
"更常见。因此,以下讨论首先关注您最有可能经常使用的运算符,最后关注那些不太常见的运算符。每个讨论都附有您可以编译和运行的示例代码。研究其输出将有助于巩固您刚刚学到的知识。
赋值、算术和一元运算符
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/op1.html
简单赋值运算符
您将遇到的最常见运算符之一是简单赋值运算符"=
"。您在自行车类中看到了这个运算符;它将右侧的值分配给左侧的操作数:
int cadence = 0;
int speed = 0;
int gear = 1;
这个运算符也可以用于对象上,分配对象引用,如创建对象中所讨论的。
算术运算符
Java 编程语言提供了执行加法、减法、乘法和除法的运算符。您很可能会通过基本数学中的对应物来认识它们。唯一可能看起来新的符号是"%
",它将一个操作数除以另一个操作数,并返回余数作为其结果。
运算符 | 描述 |
---|---|
| 加法运算符(也用于字符串连接) |
- | 减法运算符 |
* | 乘法运算符 |
/ | 除法运算符 |
% | 取余运算符 |
下面的程序,ArithmeticDemo
,测试了算术运算符。
class ArithmeticDemo {
public static void main (String[] args) {
int result = 1 2;
// result is now 3
System.out.println("1 2 = " result);
int original_result = result;
result = result - 1;
// result is now 2
System.out.println(original_result " - 1 = " result);
original_result = result;
result = result * 2;
// result is now 4
System.out.println(original_result " * 2 = " result);
original_result = result;
result = result / 2;
// result is now 2
System.out.println(original_result " / 2 = " result);
original_result = result;
result = result 8;
// result is now 10
System.out.println(original_result " 8 = " result);
original_result = result;
result = result % 7;
// result is now 3
System.out.println(original_result " % 7 = " result);
}
}
该程序打印如下内容:
代码语言:javascript复制1 2 = 3
3 - 1 = 2
2 * 2 = 4
4 / 2 = 2
2 8 = 10
10 % 7 = 3
您还可以将算术运算符与简单赋值运算符结合使用,创建复合赋值。例如,x =1;
和 x=x 1;
都会将 x
的值增加 1。
运算符也可以用于连接(拼接)两个字符串,如下所示的ConcatDemo
程序:
class ConcatDemo {
public static void main(String[] args){
String firstString = "This is";
String secondString = " a concatenated string.";
String thirdString = firstString secondString;
System.out.println(thirdString);
}
}
到程序结束时,变量 thirdString
包含"这是一个连接的字符串。",并将其打印到标准输出。
一元运算符
一元运算符只需要一个操作数;它们执行各种操作,如将值增加/减少一,否定表达式,或反转布尔值的值。
运算符 | 描述 |
---|---|
| 一元加号运算符;表示正值(数字没有这个也是正的) |
- | 一元减号运算符;否定表达式 |
| 递增运算符;将值增加 1 |
-- | 递减运算符;将值减少 1 |
! | 逻辑补运算符;反转布尔值的值 |
下面的程序,UnaryDemo
,测试了一元运算符:
class UnaryDemo {
public static void main(String[] args) {
int result = 1;
// result is now 1
System.out.println(result);
result--;
// result is now 0
System.out.println(result);
result ;
// result is now 1
System.out.println(result);
result = -result;
// result is now -1
System.out.println(result);
boolean success = false;
// false
System.out.println(success);
// true
System.out.println(!success);
}
}
递增/递减运算符可以在操作数之前(前缀)或之后(后缀)应用。代码 result ;
和 result;
都会使 result
增加一。唯一的区别在于前缀版本( result
)会计算为递增后的值,而后缀版本(result
)会计算为原始值。如果只是进行简单的递增/递减,选择哪个版本并不重要。但如果在较大表达式的一部分中使用此运算符,则您选择的版本可能会产生重大差异。
以下程序,PrePostDemo
,演示了前缀/后缀一元递增运算符:
class PrePostDemo {
public static void main(String[] args){
int i = 3;
i ;
// prints 4
System.out.println(i);
i;
// prints 5
System.out.println(i);
// prints 6
System.out.println( i);
// prints 6
System.out.println(i );
// prints 7
System.out.println(i);
}
}
相等、关系和条件运算符
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/op2.html
相等和关系运算符
相等和关系运算符确定一个操作数是否大于、小于、等于或不等于另一个操作数。这些运算符中的大多数可能对您来说看起来很熟悉。请记住,在测试两个原始值是否相等时,必须使用"==
“,而不是”=
"。
== equal to
!= not equal to
> greater than
>= greater than or equal to
< less than
<= less than or equal to
以下程序,ComparisonDemo
,测试比较运算符:
class ComparisonDemo {
public static void main(String[] args){
int value1 = 1;
int value2 = 2;
if(value1 == value2)
System.out.println("value1 == value2");
if(value1 != value2)
System.out.println("value1 != value2");
if(value1 > value2)
System.out.println("value1 > value2");
if(value1 < value2)
System.out.println("value1 < value2");
if(value1 <= value2)
System.out.println("value1 <= value2");
}
}
输出:
代码语言:javascript复制value1 != value2
value1 < value2
value1 <= value2
条件运算符
&&
和||
运算符对两个布尔表达式执行条件-AND和条件-OR操作。这些运算符表现出“短路”行为,这意味着只有在需要时才会评估第二个操作数。
&& Conditional-AND
|| Conditional-OR
以下程序,ConditionalDemo1
,测试了这些运算符:
class ConditionalDemo1 {
public static void main(String[] args){
int value1 = 1;
int value2 = 2;
if((value1 == 1) && (value2 == 2))
System.out.println("value1 is 1 AND value2 is 2");
if((value1 == 1) || (value2 == 1))
System.out.println("value1 is 1 OR value2 is 1");
}
}
另一个条件运算符是?:
,可以被视为if-then-else
语句的简写(在本课程的控制流语句部分讨论)。这个运算符也被称为三元运算符,因为它使用三个操作数。在下面的例子中,这个运算符应该被理解为:“如果someCondition
为true
,则将value1
的值赋给result
。否则,将value2
的值赋给result
。”
以下程序,ConditionalDemo2
,测试了?:
运算符:
class ConditionalDemo2 {
public static void main(String[] args){
int value1 = 1;
int value2 = 2;
int result;
boolean someCondition = true;
result = someCondition ? value1 : value2;
System.out.println(result);
}
}
因为someCondition
为真,这个程序将"1"打印到屏幕上。如果使用?:
运算符而不是if-then-else
语句可以使您的代码更易读;例如,当表达式紧凑且没有副作用(如赋值)时。
类型比较运算符 instanceof
instanceof
运算符将对象与指定类型进行比较。您可以使用它来测试对象是否是类的实例,子类的实例,或者实现特定接口的类的实例。
以下程序,InstanceofDemo
,定义了一个父类(名为Parent
),一个简单接口(名为MyInterface
),以及一个继承自父类并实现接口的子类(名为Child
)。
class InstanceofDemo {
public static void main(String[] args) {
Parent obj1 = new Parent();
Parent obj2 = new Child();
System.out.println("obj1 instanceof Parent: "
(obj1 instanceof Parent));
System.out.println("obj1 instanceof Child: "
(obj1 instanceof Child));
System.out.println("obj1 instanceof MyInterface: "
(obj1 instanceof MyInterface));
System.out.println("obj2 instanceof Parent: "
(obj2 instanceof Parent));
System.out.println("obj2 instanceof Child: "
(obj2 instanceof Child));
System.out.println("obj2 instanceof MyInterface: "
(obj2 instanceof MyInterface));
}
}
class Parent {}
class Child extends Parent implements MyInterface {}
interface MyInterface {}
输出:
代码语言:javascript复制obj1 instanceof Parent: true
obj1 instanceof Child: false
obj1 instanceof MyInterface: false
obj2 instanceof Parent: true
obj2 instanceof Child: true
obj2 instanceof MyInterface: true
当使用instanceof
运算符时,请记住null
不是任何东西的实例。
位与位移操作符
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
Java 编程语言还提供对整数类型执行位和位移操作的运算符。本节讨论的运算符很少使用。因此,它们的覆盖范围很简短;目的只是让您知道这些运算符存在。
一元位取反运算符"~
“反转位模式;它可以应用于任何整数类型,使每个"0"变为"1”,每个"1"变为"0"。例如,一个byte
包含 8 位;将此运算符应用于位模式为"00000000"的值将使其模式变为"11111111"。
有符号左移位运算符"<<
“将位模式向左移动,有符号右移位运算符”>>
“将位模式向右移动。位模式由左操作数给出,要移动的位置数由右操作数给出。无符号右移位运算符”>>>
"将零移入最左侧位置,而">>"
后的最左侧位置取决于符号扩展。
位运算符&
执行位按位与操作。
位运算符^
执行位按位异或操作。
位运算符|
执行位按位包含或操作。
以下程序,BitDemo
,使用位与运算符将数字"2"打印到标准输出。
class BitDemo {
public static void main(String[] args) {
int bitmask = 0x000F;
int val = 0x2222;
// prints "2"
System.out.println(val & bitmask);
}
}
运算符概要
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/opsummary.html
以下快速参考总结了 Java 编程语言支持的运算符。
简单赋值运算符
代码语言:javascript复制= Simple assignment operator
算术运算符
代码语言:javascript复制 Additive operator (also used
for String concatenation)
- Subtraction operator
* Multiplication operator
/ Division operator
% Remainder operator
一元运算符
代码语言:javascript复制 Unary plus operator; indicates
positive value (numbers are
positive without this, however)
- Unary minus operator; negates
an expression
Increment operator; increments
a value by 1
-- Decrement operator; decrements
a value by 1
! Logical complement operator;
inverts the value of a boolean
相等性和关系运算符
代码语言:javascript复制== Equal to
!= Not equal to
> Greater than
>= Greater than or equal to
< Less than
<= Less than or equal to
条件运算符
代码语言:javascript复制&& Conditional-AND
|| Conditional-OR
?: Ternary (shorthand for
if-then-else statement)
类型比较运算符
代码语言:javascript复制instanceof Compares an object to
a specified type
位运算符和位移运算符
代码语言:javascript复制~ Unary bitwise complement
<< Signed left shift
>> Signed right shift
>>> Unsigned right shift
& Bitwise AND
^ Bitwise exclusive OR
| Bitwise inclusive OR
问题和练习:运算符
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/QandE/questions_operators.html
问题
考虑以下代码片段。
代码语言:javascript复制arrayOfInts[j] > arrayOfInts[j 1]
代码包含哪些运算符?
考虑以下代码片段。
代码语言:javascript复制int i = 10;
int n = i %5;
- 在代码执行后,
i
和n
的值是多少? - 如果不使用后缀递增运算符(
i
),而是使用前缀版本(i
),i
和n
的最终值是多少?
要反转boolean
的值,你会使用哪个运算符?
用于比较两个值的运算符是=
还是==
?
解释以下代码示例:result = someCondition ? value1 : value2;
练习
修改以下程序以使用复合赋值:
代码语言:javascript复制class ArithmeticDemo {
public static void main (String[] args){
int result = 1 2; // result is now 3
System.out.println(result);
result = result - 1; // result is now 2
System.out.println(result);
result = result * 2; // result is now 4
System.out.println(result);
result = result / 2; // result is now 2
System.out.println(result);
result = result 8; // result is now 10
result = result % 7; // result is now 3
System.out.println(result);
}
}
在以下程序中,解释为什么值“6”连续打印两次:
代码语言:javascript复制class PrePostDemo {
public static void main(String[] args){
int i = 3;
i ;
System.out.println(i); // "4"
i;
System.out.println(i); // "5"
System.out.println( i); // "6"
System.out.println(i ); // "6"
System.out.println(i); // "7"
}
}
检查你的答案
表达式、语句和块
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/expressions.html
现在你已经了解了变量和运算符,是时候学习 表达式、语句 和 块 了。运算符可以用于构建计算值的表达式;表达式是语句的核心组件;语句可以分组成块。
表达式
一个 表达式 是由变量、运算符和方法调用构成的构造,根据语言的语法构造,计算为单个值。你已经看到了表达式的示例,如下所示:
代码语言:javascript复制int **cadence = 0**;
**anArray[0] = 100**;
System.out.println(**"Element 1 at index 0: " anArray[0]**);
int **result = 1 2**; // result is now 3
if (**value1 == value2**)
System.out.println(**"value1 == value2"**);
表达式返回的值的数据类型取决于表达式中使用的元素。表达式 cadence = 0
返回一个 int
,因为赋值运算符返回与其左操作数相同数据类型的值;在这种情况下,cadence
是一个 int
。从其他表达式中可以看到,表达式也可以返回其他类型的值,比如 boolean
或 String
。
Java 编程语言允许你从各种较小的表达式构建复合表达式,只要表达式的一部分所需的数据类型与另一部分的数据类型匹配。以下是一个复合表达式的示例:
代码语言:javascript复制1 * 2 * 3
在这个特定的例子中,表达式的计算顺序并不重要,因为乘法的结果与顺序无关;无论你如何应用乘法,结果始终相同。然而,并非所有表达式都是如此。例如,下面的表达式根据你是先执行加法还是除法操作而得出不同的结果:
代码语言:javascript复制x y / 100 // ambiguous
你可以使用平衡的括号(( 和 ))来明确指定表达式的计算方式。例如,为了使前面的表达式不含糊,你可以写成以下形式:
代码语言:javascript复制(x y) / 100 // unambiguous, recommended
如果你不明确指定操作的执行顺序,操作的顺序将由表达式中使用的运算符分配的优先级决定。具有更高优先级的运算符首先计算。例如,除法运算符的优先级高于加法运算符。因此,以下两个语句是等价的:
代码语言:javascript复制x y / 100
x (y / 100) // unambiguous, recommended
在编写复合表达式时,要明确指出哪些运算符应该首先计算,并用括号表示。这种做法使代码更易于阅读和维护。
语句
语句大致相当于自然语言中的句子。语句 形成一个完整的执行单元。以下类型的表达式可以通过在表达式末尾加上分号 (;
) 来转换为语句。
- 赋值表达式
- 任何使用
--
的情况 - 方法调用
- 对象创建表达式
这些语句被称为 表达语句。以下是一些表达语句的例子。
代码语言:javascript复制// assignment statement
aValue = 8933.234;
// increment statement
aValue ;
// method invocation statement
System.out.println("Hello World!");
// object creation statement
Bicycle myBike = new Bicycle();
除了表达语句外,还有另外两种语句:声明语句 和 控制流语句。声明语句 用于声明一个变量。你已经看过很多声明语句的例子了:
代码语言:javascript复制// declaration statement
double aValue = 8933.234;
最后,控制流语句 调节语句执行的顺序。你将在下一节学习有关控制流语句的内容,控制流语句。
块
一个 块 是在平衡大括号之间的零个或多个语句组成的组,可以在允许单个语句的任何地方使用。下面的例子,BlockDemo
,演示了块的使用:
class BlockDemo {
public static void main(String[] args) {
boolean condition = true;
if (condition) { // begin block 1
System.out.println("Condition is true.");
} // end block one
else { // begin block 2
System.out.println("Condition is false.");
} // end block 2
}
}
问题和练习:表达式、语句和块
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/QandE/questions_expressions.html
问题
运算符可用于构建 ___,计算值。
表达式是 ___ 的核心组件。
语句可以分组为 ___。
以下代码片段是 ___ 表达式的示例。
代码语言:javascript复制 1 * 2 * 3
语句在自然语言中大致相当于句子,但语句以 ___ 结尾,而不是句号。
块是在平衡 ___ 之间的零个或多个语句组成的组,可以在允许单个语句的任何地方使用。
练习
识别以下种类的表达语句:
-
aValue = 8933.234;
-
aValue ;
-
System.out.println("Hello World!");
-
Bicycle myBike = new Bicycle();
检查你的答案
控制流语句
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/flow.html
源文件中的语句通常按照它们出现的顺序从上到下执行。然而,控制流语句通过使用决策、循环和分支打破执行流程,使您的程序能够有条件地执行特定的代码块。本节描述了 Java 编程语言支持的决策语句(if-then
、if-then-else
、switch
)、循环语句(for
、while
、do-while
)和分支语句(break
、continue
、return
)。
if-then
和 if-then-else
语句
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/if.html
if-then
语句
if-then
语句是所有控制流语句中最基本的。它告诉你的程序仅当特定测试评估为 true
时才执行某个代码段。例如,Bicycle
类可以允许刹车减少自行车的速度,仅当自行车已经在运动中时。applyBrakes
方法的一个可能的实现如下:
void applyBrakes() {
// the "if" clause: bicycle must be moving
if (isMoving){
// the "then" clause: decrease current speed
currentSpeed--;
}
}
如果这个测试评估为 false
(意味着自行车没有在运动中),控制跳转到 if-then
语句的末尾。
此外,如果“then”子句只包含一个语句,则开头和结尾的大括号是可选的:
代码语言:javascript复制void applyBrakes() {
// same as above, but without braces
if (isMoving)
currentSpeed--;
}
决定何时省略大括号是个人品味的问题。省略它们可能会使代码更脆弱。如果稍后向“then”子句添加第二个语句,一个常见的错误是忘记添加新需要的大括号。编译器无法捕捉到这种错误;你只会得到错误的结果。
if-then-else
语句
if-then-else
语句在“if”子句评估为 false
时提供了执行的第二路径。你可以在 applyBrakes
方法中使用 if-then-else
语句,如果自行车在静止状态下刹车,就采取一些行动。在这种情况下,行动就是简单地打印一个错误消息,说明自行车已经停止了。
void applyBrakes() {
if (isMoving) {
currentSpeed--;
} else {
System.err.println("The bicycle has already stopped!");
}
}
以下程序,IfElseDemo
,根据测试分数的值分配等级:90% 或以上为 A,80% 或以上为 B,依此类推。
class IfElseDemo {
public static void main(String[] args) {
int testscore = 76;
char grade;
if (testscore >= 90) {
grade = 'A';
} else if (testscore >= 80) {
grade = 'B';
} else if (testscore >= 70) {
grade = 'C';
} else if (testscore >= 60) {
grade = 'D';
} else {
grade = 'F';
}
System.out.println("Grade = " grade);
}
}
程序的输出是:
代码语言:javascript复制 Grade = C
你可能已经注意到 testscore
的值可以满足复合语句中的多个表达式:76 >= 70
和 76 >= 60
。然而,一旦条件满足,适当的语句就会被执行(grade = 'C';
),剩余的条件就不会被评估。
switch 语句
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html
与 if-then
和 if-then-else
语句不同,switch
语句可以有多个可能的执行路径。switch
适用于 byte
、short
、char
和 int
原始数据类型。它还适用于枚举类型(在 枚举类型 中讨论)、String
类以及包装某些原始类型的几个特殊类:Character
、Byte
、Short
和 Integer
(在 数字和字符串 中讨论)。
以下代码示例,SwitchDemo
,声明了一个名为 month
的 int
,其值表示一个月份。该代码根据 month
的值使用 switch
语句显示月份的名称。
public class SwitchDemo {
public static void main(String[] args) {
int month = 8;
String monthString;
switch (month) {
case 1: monthString = "January";
break;
case 2: monthString = "February";
break;
case 3: monthString = "March";
break;
case 4: monthString = "April";
break;
case 5: monthString = "May";
break;
case 6: monthString = "June";
break;
case 7: monthString = "July";
break;
case 8: monthString = "August";
break;
case 9: monthString = "September";
break;
case 10: monthString = "October";
break;
case 11: monthString = "November";
break;
case 12: monthString = "December";
break;
default: monthString = "Invalid month";
break;
}
System.out.println(monthString);
}
}
在这种情况下,August
被打印到标准输出。
switch
语句的主体称为switch 块。switch
块中的语句可以带有一个或多个 case
或 default
标签。switch
语句评估其表达式,然后执行所有匹配 case
标签后的语句。
你也可以使用 if-then-else
语句显示月份的名称:
int month = 8;
if (month == 1) {
System.out.println("January");
} else if (month == 2) {
System.out.println("February");
}
... // and so on
决定是使用 if-then-else
语句还是 switch
语句取决于可读性和语句测试的表达式。if-then-else
语句可以测试基于值范围或条件的表达式,而 switch
语句仅基于单个整数、枚举值或 String
对象测试表达式。
break
语句是另一个有趣的点。每个 break
语句终止包含的 switch
语句。控制流继续执行 switch
块后的第一条语句。break
语句是必要的,因为没有它们,switch
块中的语句会穿透:匹配的 case
标签后的所有语句会按顺序执行,而不管后续 case
标签的表达式如何,直到遇到 break
语句。程序 SwitchDemoFallThrough
展示了在 switch
块中穿透的语句。该程序显示了对应整数 month
的月份以及该年后续的月份:
public class SwitchDemoFallThrough {
public static void main(String[] args) {
java.util.ArrayList<String> futureMonths =
new java.util.ArrayList<String>();
int month = 8;
switch (month) {
case 1: futureMonths.add("January");
case 2: futureMonths.add("February");
case 3: futureMonths.add("March");
case 4: futureMonths.add("April");
case 5: futureMonths.add("May");
case 6: futureMonths.add("June");
case 7: futureMonths.add("July");
case 8: futureMonths.add("August");
case 9: futureMonths.add("September");
case 10: futureMonths.add("October");
case 11: futureMonths.add("November");
case 12: futureMonths.add("December");
break;
default: break;
}
if (futureMonths.isEmpty()) {
System.out.println("Invalid month number");
} else {
for (String monthName : futureMonths) {
System.out.println(monthName);
}
}
}
}
这是代码的输出:
代码语言:javascript复制August
September
October
November
December
从技术上讲,最后的break
并不是必需的,因为流程会跳出switch
语句。建议使用break
,这样修改代码会更容易,也更少出错。default
部分处理了所有未被case
部分显式处理的值。
以下代码示例,SwitchDemo2
,展示了一个语句可以有多个case
标签。该代码示例计算了特定月份的天数:
class SwitchDemo2 {
public static void main(String[] args) {
int month = 2;
int year = 2000;
int numDays = 0;
switch (month) {
case 1: case 3: case 5:
case 7: case 8: case 10:
case 12:
numDays = 31;
break;
case 4: case 6:
case 9: case 11:
numDays = 30;
break;
case 2:
if (((year % 4 == 0) &&
!(year % 100 == 0))
|| (year % 400 == 0))
numDays = 29;
else
numDays = 28;
break;
default:
System.out.println("Invalid month.");
break;
}
System.out.println("Number of Days = "
numDays);
}
}
这是代码的输出:
代码语言:javascript复制Number of Days = 29
在switch
语句中使用字符串。
在 Java SE 7 及更高版本中,可以在switch
语句的表达式中使用String
对象。以下代码示例,StringSwitchDemo
,根据名为month
的String
的值显示月份的数字:
public class StringSwitchDemo {
public static int getMonthNumber(String month) {
int monthNumber = 0;
if (month == null) {
return monthNumber;
}
switch (month.toLowerCase()) {
case "january":
monthNumber = 1;
break;
case "february":
monthNumber = 2;
break;
case "march":
monthNumber = 3;
break;
case "april":
monthNumber = 4;
break;
case "may":
monthNumber = 5;
break;
case "june":
monthNumber = 6;
break;
case "july":
monthNumber = 7;
break;
case "august":
monthNumber = 8;
break;
case "september":
monthNumber = 9;
break;
case "october":
monthNumber = 10;
break;
case "november":
monthNumber = 11;
break;
case "december":
monthNumber = 12;
break;
default:
monthNumber = 0;
break;
}
return monthNumber;
}
public static void main(String[] args) {
String month = "August";
int returnedMonthNumber =
StringSwitchDemo.getMonthNumber(month);
if (returnedMonthNumber == 0) {
System.out.println("Invalid month");
} else {
System.out.println(returnedMonthNumber);
}
}
}
这段代码的输出是8
。
switch
表达式中的String
与与每个case
标签关联的表达式进行比较,就好像使用了String.equals
方法一样。为了使StringSwitchDemo
示例接受任何大小写的月份,month
被转换为小写(使用toLowerCase
方法),并且所有与case
标签关联的字符串都是小写的。
注意:此示例检查switch
语句中的表达式是否为null
。确保任何switch
语句中的表达式不为 null,以防止抛出NullPointerException
。
while 和 do-while 语句
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/while.html
while
语句在特定条件为true
时持续执行一组语句。其语法可以表示为:
while (expression) {
statement(s)
}
while
语句评估表达式,该表达式必须返回一个boolean
值。如果表达式评估为true
,while
语句执行while
块中的语句。while
语句继续测试表达式并执行其块,直到表达式评估为false
。使用while
语句打印从 1 到 10 的值可以通过以下WhileDemo
程序实现:
class WhileDemo {
public static void main(String[] args){
int count = 1;
while (count < 11) {
System.out.println("Count is: " count);
count ;
}
}
}
您可以使用while
语句实现无限循环,如下所示:
while (true){
// your code goes here
}
Java 编程语言还提供了do-while
语句,可以表示如下:
do {
statement(s)
} while (expression);
do-while
和while
之间的区别在于,do-while
在循环底部评估其表达式,而不是在顶部。因此,do
块内的语句始终至少执行一次,如下所示的DoWhileDemo
程序中所示:
class DoWhileDemo {
public static void main(String[] args){
int count = 1;
do {
System.out.println("Count is: " count);
count ;
} while (count < 11);
}
}
for 语句
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html
for
语句提供了一种紧凑的方式来遍历一系列值。程序员通常将其称为"for 循环",因为它会重复循环,直到满足特定条件为止。for
语句的一般形式可以表示如下:
for (*initialization*; *termination*;
*increment*) {
*statement(s)*
}
使用这个版本的for
语句时,请记住:
- 初始化表达式初始化循环;它在循环开始时执行一次。
- 当终止表达式评估为
false
时,循环终止。 - 增量表达式在每次循环迭代之后被调用;这个表达式完全可以递增或递减一个值。
以下程序ForDemo
使用for
语句的一般形式将数字 1 到 10 打印到标准输出:
class ForDemo {
public static void main(String[] args){
for(int i=1; i<11; i ){
System.out.println("Count is: " i);
}
}
}
该程序的输出是:
代码语言:javascript复制Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5
Count is: 6
Count is: 7
Count is: 8
Count is: 9
Count is: 10
请注意代码如何在初始化表达式中声明一个变量。该变量的作用域从其声明延伸到由for
语句控制的块的末尾,因此它也可以在终止和增量表达式中使用。如果控制for
语句的变量在循环外不需要,则最好在初始化表达式中声明该变量。通常使用i
、j
和k
这些名称来控制for
循环;在初始化表达式中声明它们会限制它们的生命周期并减少错误。
for
循环的三个表达式是可选的;可以创建一个无限循环,如下所示:
// infinite loop
for ( ; ; ) {
// your code goes here
}
for
语句还有另一种形式,专为遍历集合和数组设计。这种形式有时被称为增强型 for语句,可使您的循环更加简洁和易读。为了演示,考虑以下数组,其中包含 1 到 10 的数字:
int[] numbers = {1,2,3,4,5,6,7,8,9,10};
以下程序EnhancedForDemo
使用增强型for
循环遍历数组:
class EnhancedForDemo {
public static void main(String[] args){
int[] numbers =
{1,2,3,4,5,6,7,8,9,10};
for (int item : numbers) {
System.out.println("Count is: " item);
}
}
}
在这个例子中,变量item
保存来自数字数组的当前值。该程序的输出与之前相同:
Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5
Count is: 6
Count is: 7
Count is: 8
Count is: 9
Count is: 10
我们建议尽可能使用这种形式的for
语句,而不是一般形式。
分支语句
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
break
语句
break
语句有两种形式:带标签和未标记。在前面讨论 switch
语句时看到了未标记形式。您还可以使用未标记的 break
终止 for
、while
或 do-while
循环,如下所示的 BreakDemo
程序:
class BreakDemo {
public static void main(String[] args) {
int[] arrayOfInts =
{ 32, 87, 3, 589,
12, 1076, 2000,
8, 622, 127 };
int searchfor = 12;
int i;
boolean foundIt = false;
for (i = 0; i < arrayOfInts.length; i ) {
if (arrayOfInts[i] == searchfor) {
foundIt = true;
break;
}
}
if (foundIt) {
System.out.println("Found " searchfor " at index " i);
} else {
System.out.println(searchfor " not in the array");
}
}
}
该程序在数组中搜索数字 12。粗体显示的 break
语句在找到该值时终止 for
循环。然后控制流转移到 for
循环后的语句。该程序的输出为:
Found 12 at index 4
一个未标记的 break
语句终止最内层的 switch
、for
、while
或 do-while
语句,但带标签的 break
终止外部语句。下面的程序,BreakWithLabelDemo
,类似于前一个程序,但使用嵌套的 for
循环在二维数组中搜索一个值。当找到该值时,带标签的 break
终止外部的 for
循环(标记为 “search”):
class BreakWithLabelDemo {
public static void main(String[] args) {
int[][] arrayOfInts = {
{ 32, 87, 3, 589 },
{ 12, 1076, 2000, 8 },
{ 622, 127, 77, 955 }
};
int searchfor = 12;
int i;
int j = 0;
boolean foundIt = false;
search:
for (i = 0; i < arrayOfInts.length; i ) {
for (j = 0; j < arrayOfInts[i].length;
j ) {
if (arrayOfInts[i][j] == searchfor) {
foundIt = true;
break search;
}
}
}
if (foundIt) {
System.out.println("Found " searchfor " at " i ", " j);
} else {
System.out.println(searchfor " not in the array");
}
}
}
这是程序的输出。
代码语言:javascript复制Found 12 at 1, 0
break
语句终止带标签的语句;它不会将控制流转移到标签处。控制流会转移到带标签的(终止的)语句之后的语句。
continue
语句
continue
语句跳过 for
、while
或 do-while
循环的当前迭代。未标记形式跳到最内层循环体的末尾并评估控制循环的 boolean
表达式。下面的程序,ContinueDemo
,遍历一个 String
,计算字母 “p” 的出现次数。如果当前字符不是 p,则 continue
语句跳过循环的其余部分并继续下一个字符。如果是 “p”,程序会增加字母计数。
class ContinueDemo {
public static void main(String[] args) {
String searchMe = "peter piper picked a " "peck of pickled peppers";
int max = searchMe.length();
int numPs = 0;
for (int i = 0; i < max; i ) {
// interested only in p's
if (searchMe.charAt(i) != 'p')
continue;
// process p's
numPs ;
}
System.out.println("Found " numPs " p's in the string.");
}
}
这是该程序的输出:
代码语言:javascript复制Found 9 p's in the string.
要更清楚地看到这种效果,请尝试删除 continue
语句并重新编译。再次运行程序时,计数将出错,显示找到了 35 个 p,而不是 9 个。
带标签的 continue
语句跳过带有给定标签的外部循环的当前迭代。下面的示例程序,ContinueWithLabelDemo
,使用嵌套循环在另一个字符串中搜索子字符串。需要两个嵌套循环:一个用于迭代子字符串,一个用于迭代被搜索的字符串。下面的程序,ContinueWithLabelDemo
,使用带标签的 continue 形式跳过外部循环的一个迭代。
class ContinueWithLabelDemo {
public static void main(String[] args) {
String searchMe = "Look for a substring in me";
String substring = "sub";
boolean foundIt = false;
int max = searchMe.length() -
substring.length();
test:
for (int i = 0; i <= max; i ) {
int n = substring.length();
int j = i;
int k = 0;
while (n-- != 0) {
if (searchMe.charAt(j ) != substring.charAt(k )) {
continue test;
}
}
foundIt = true;
break test;
}
System.out.println(foundIt ? "Found it" : "Didn't find it");
}
}
这是该程序的输出。
代码语言:javascript复制Found it
return
语句
分支语句中的最后一个是return
语句。return
语句退出当前方法,控制流返回到调用方法的位置。return
语句有两种形式:一种返回一个值,另一种不返回。要返回一个值,只需在return
关键字后面放置该值(或计算该值的表达式)。
return count;
返回值的数据类型必须与方法声明的返回值类型匹配。当方法声明为void
时,使用不返回值的return
形式。
return;
类和对象课程将涵盖你需要了解的关于编写方法的一切。
控制流语句总结
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/flowsummary.html
if-then
语句是所有控制流语句中最基本的。它告诉你的程序只有在特定测试评估为true
时才执行某个代码段。if-then-else
语句在“if”子句评估为false
时提供了一个备用执行路径。与if-then
和if-then-else
不同,switch
语句允许任意数量的执行路径。while
和do-while
语句在特定条件为true
时持续执行一系列语句。do-while
和while
之间的区别在于,do-while
在循环底部评估其表达式而不是顶部。因此,do
块内的语句至少会执行一次。for
语句提供了一种紧凑的方式来迭代一系列值。它有两种形式,其中一种设计用于循环遍历集合和数组。
问题和练习:控制流语句
原文:
docs.oracle.com/javase/tutorial/java/nutsandbolts/QandE/questions_flow.html
问题
- Java 编程语言支持的最基本的控制流语句是 ___ 语句。
- ___ 语句允许任意数量的可能执行路径。
- ___ 语句类似于
while
语句,但在循环的 ___ 处评估其表达式。 - 如何使用
for
语句编写一个无限循环? - 如何使用
while
语句编写一个无限循环?
练习
考虑以下代码片段。
代码语言:javascript复制if (aNumber >= 0)
if (aNumber == 0)
System.out.println("first string");
else System.out.println("second string");
System.out.println("third string");
- 如果
aNumber
为 3,你认为代码会产生什么输出? - 编写一个包含上述代码片段的测试程序;将
aNumber
设为 3。程序的输出是什么?是否符合你的预期?解释输出为何是这样的;换句话说,代码片段的控制流是什么? - 仅使用空格和换行符,重新格式化代码片段,使控制流更易于理解。
- 使用大括号,{ 和 },进一步澄清代码。
检查你的答案
课程:类和对象
原文:
docs.oracle.com/javase/tutorial/java/javaOO/index.html
有了你现在对 Java 编程语言基础知识的了解,你可以学会编写自己的类。在本课程中,您将找到有关定义自己的类的信息,包括声明成员变量、方法和构造函数。
您将学会使用您的类来创建对象,以及如何使用您创建的对象。
本课程还涵盖了将类嵌套在其他类中以及枚举的内容
类
本节展示了类的结构,以及如何声明字段、方法和构造函数。
对象
本节介绍了创建和使用对象。您将学习如何实例化对象,以及一旦实例化,如何使用点
运算符访问对象的实例变量和方法。
更多关于类
本节涵盖了更多依赖于在前一节中学到的对象引用和点
运算符的类的方面:从方法返回值,this
关键字,类与实例成员,以及访问控制。
嵌套类
静态嵌套类、内部类、匿名内部类、局部类和 lambda 表达式都有涵盖。还讨论了何时使用哪种方法。
枚举类型
本节介绍了枚举,这是一种特殊的类,允许您定义和使用一组常量。
类
原文:
docs.oracle.com/javase/tutorial/java/javaOO/classes.html
在标题为面向对象编程概念的课程中,介绍了面向对象概念,以自行车类为例,其中赛车、山地车和双人车为子类。以下是一个可能实现Bicycle
类的示例代码,让你了解类声明的概述。本课程的后续部分将逐步支持和解释类声明。暂时不要担心细节。
public class Bicycle {
// the Bicycle class has
// three *fields*
public int cadence;
public int gear;
public int speed;
// the Bicycle class has
// one *constructor*
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
// the Bicycle class has
// four *methods*
public void setCadence(int newValue) {
cadence = newValue;
}
public void setGear(int newValue) {
gear = newValue;
}
public void applyBrake(int decrement) {
speed -= decrement;
}
public void speedUp(int increment) {
speed = increment;
}
}
MountainBike
类的类声明,作为Bicycle
的子类,可能如下所示:
public class MountainBike extends Bicycle {
// the MountainBike subclass has
// one *field*
public int seatHeight;
// the MountainBike subclass has
// one *constructor*
public MountainBike(int startHeight, int startCadence,
int startSpeed, int startGear) {
super(startCadence, startSpeed, startGear);
seatHeight = startHeight;
}
// the MountainBike subclass has
// one *method*
public void setHeight(int newValue) {
seatHeight = newValue;
}
}
MountainBike
继承了Bicycle
的所有字段和方法,并添加了seatHeight
字段以及一个设置它的方法(山地车的座位可以根据地形要求上下移动)。
声明类
原文:
docs.oracle.com/javase/tutorial/java/javaOO/classdecl.html
你已经看到以下方式定义的类:
代码语言:javascript复制class *MyClass* {
// field, constructor, and
// method declarations
}
这是一个类声明。类体(大括号之间的区域)包含了为从类创建的对象的生命周期提供支持的所有代码:用于初始化新对象的构造函数,提供类及其对象状态的字段声明,以及实现类及其对象行为的方法。
前面的类声明是一个最小的类声明。它只包含了类声明中所需的组件。你可以在类声明的开头提供关于类的更多信息,比如其超类的名称,是否实现了任何接口等。例如,
代码语言:javascript复制class *MyClass extends MySuperClass implements YourInterface* {
// field, constructor, and
// method declarations
}
意味着MyClass
是MySuperClass
的子类,并且实现了YourInterface
接口。
你也可以在最开始添加像public或private这样的修饰符—所以你可以看到类声明的开头行可能会变得相当复杂。决定其他类能否访问MyClass
的修饰符public和private将在本课程的后面讨论。关于接口和继承的课程将解释在类声明中为什么以及如何使用extends和implements关键字。目前你不需要担心这些额外的复杂性。
一般来说,类声明可以按顺序包括这些组件:
- 修饰符,比如public、private以及你以后会遇到的其他一些修饰符。(但是,请注意,private修饰符只能应用于嵌套类。)
- 类名,按照约定首字母大写。
- 类的父类(超类)的名称,如果有的话,前面带有关键字extends。一个类只能扩展(子类化)一个父类。
- 一个逗号分隔的接口列表,如果有的话,前面带有关键字implements。一个类可以实现多个接口。
- 类体,用大括号{}括起来。
声明成员变量
原文:
docs.oracle.com/javase/tutorial/java/javaOO/variables.html
有几种类型的变量:
- 类中的成员变量—这些被称为字段。
- 方法或代码块中的变量—这些被称为局部变量。
- 方法声明中的变量—这些被称为参数。
Bicycle
类使用以下代码行来定义其字段:
public int cadence;
public int gear;
public int speed;
字段声明由三个组件组成,顺序为:
- 零个或多个修饰符,比如
public
或private
。 - 字段的类型。
- 字段的名称。
Bicycle
的字段名为cadence
、gear
和speed
,都是整数类型(int
)。public
关键字将这些字段标识为公共成员,可被任何可以访问该类的对象访问。
访问修饰符
使用的第一个(最左边的)修饰符让你控制其他类对成员字段的访问权限。目前,只考虑public
和private
。其他访问修饰符将在后面讨论。
-
public
修饰符—该字段可从所有类访问。 -
private
修饰符—该字段只能在其自身类中访问。
符合封装原则,通常会将字段设为私有。这意味着它们只能从Bicycle
类直接访问。然而,我们仍然需要访问这些值。这可以通过添加获取字段值的公共方法间接完成:
public class Bicycle {
private int cadence;
private int gear;
private int speed;
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
public int getCadence() {
return cadence;
}
public void setCadence(int newValue) {
cadence = newValue;
}
public int getGear() {
return gear;
}
public void setGear(int newValue) {
gear = newValue;
}
public int getSpeed() {
return speed;
}
public void applyBrake(int decrement) {
speed -= decrement;
}
public void speedUp(int increment) {
speed = increment;
}
}
类型
所有变量必须有一个类型。你可以使用原始类型如int
、float
、boolean
等。或者你可以使用引用类型,比如字符串、数组或对象。
变量名
所有变量,无论是字段、局部变量还是参数,都遵循在语言基础课程中介绍的相同的命名规则和约定,变量—命名。
在这节课中,请注意方法和类名使用相同的命名规则和约定,除了
- 类名的第一个字母应大写,而
- 方法名的第一个(或唯一)单词应该是动词。
定义方法
原文:
docs.oracle.com/javase/tutorial/java/javaOO/methods.html
这是一个典型方法声明的示例:
代码语言:javascript复制public double calculateAnswer(double wingSpan, int numberOfEngines,
double length, double grossTons) {
//do the calculation here
}
方法声明的唯一必需元素是方法的返回类型、名称、一对括号()
,以及大括号{}
之间的方法体。
更一般地,方法声明有六个组成部分,顺序如下:
- 修饰符—例如
public
、private
等,以及其他你将在后面学习的内容。 - 返回类型—方法返回的值的数据类型,或者如果方法不返回值,则为
void
。 - 方法名称—字段名称的规则也适用于方法名称,但约定略有不同。
- 括号中的参数列表—以逗号分隔的输入参数列表,前面是它们的数据类型,用括号
()
括起来。如果没有参数,必须使用空括号。 - 一个异常列表—稍后讨论。
- 方法体,用大括号括起来—方法的代码,包括局部变量的声明,在这里。
修饰符、返回类型和参数将在本课程的后续部分讨论。异常将在后续课程中讨论。
定义: 方法声明的两个组成部分构成了方法签名—方法的名称和参数类型。
上面声明的方法的签名是:
代码语言:javascript复制calculateAnswer(double, int, double, double)
命名方法
尽管方法名称可以是任何合法标识符,但代码约定限制了方法名称。按照约定,方法名称应该是小写的动词或以小写动词开头的多词名称,后面跟着形容词、名词等。在多词名称中,第二个及后续单词的第一个字母应大写。以下是一些示例:
代码语言:javascript复制run
runFast
getBackground
getFinalData
compareTo
setX
isEmpty
通常,一个方法在其类中具有唯一的名称。但是,由于方法重载,一个方法可能与其他方法具有相同的名称。
方法重载
Java 编程语言支持方法重载,并且 Java 可以区分具有不同方法签名的方法。这意味着类中的方法如果具有不同的参数列表,则可以具有相同的名称(对此有一些限制,将在标题为“接口和继承”的课程中讨论)。
假设你有一个可以使用书法来绘制各种类型数据(字符串、整数等)的类,并且包含一个用于绘制每种数据类型的方法。为每个方法使用新名称很麻烦—例如,drawString
、drawInteger
、drawFloat
等。在 Java 编程语言中,你可以为所有绘制方法使用相同的名称,但对每个方法传递不同的参数列表。因此,数据绘制类可能声明四个名为draw
的方法,每个方法都有不同的参数列表。
public class DataArtist {
...
public void draw(String s) {
...
}
public void draw(int i) {
...
}
public void draw(double f) {
...
}
public void draw(int i, double f) {
...
}
}
过载方法通过传入方法的参数的数量和类型来区分。在代码示例中,draw(String s)
和 draw(int i)
是不同且独特的方法,因为它们需要不同的参数类型。
你不能声明多个具有相同名称和相同数量及类型参数的方法,因为编译器无法区分它们。
编译器在区分方法时不考虑返回类型,因此即使它们具有不同的返回类型,也不能声明具有相同签名的两个方法。
注意: 过载方法应该谨慎使用,因为它们会使代码变得难以阅读。
为你的类提供构造函数
原文:
docs.oracle.com/javase/tutorial/java/javaOO/constructors.html
一个类包含用于从类蓝图创建对象的构造函数。构造函数声明看起来像方法声明,只是它们使用类的名称并且没有返回类型。例如,Bicycle
有一个构造函数:
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
要创建一个名为myBike
的新Bicycle
对象,需要通过new
运算符调用构造函数:
Bicycle myBike = new Bicycle(30, 0, 8);
new Bicycle(30, 0, 8)
在内存中为对象创建空间并初始化其字段。
尽管Bicycle
只有一个构造函数,但它可以有其他构造函数,包括无参数构造函数:
public Bicycle() {
gear = 1;
cadence = 10;
speed = 0;
}
Bicycle yourBike = new Bicycle();
调用无参数构造函数以创建一个名为yourBike
的新Bicycle
对象。
由于它们具有不同的参数列表,两个构造函数都可以在Bicycle
中声明。与方法一样,Java 平台根据参数列表中参数的数量和类型区分构造函数。你不能为同一个类编写具有相同数量和类型参数的两个构造函数,因为平台无法区分它们。这样做会导致编译时错误。
你不必为你的类提供任何构造函数,但在这样做时必须小心。编译器会自动为没有构造函数的任何类提供一个无参数默认构造函数。这个默认构造函数将调用超类的无参数构造函数。在这种情况下,如果超类没有无参数构造函数,编译器会报错,因此你必须验证它是否有。如果你的类没有显式的超类,那么它有一个隐式的超类Object
,它有一个无参数构造函数。
你可以自己使用超类构造函数。本课程开头的MountainBike
类就是这样做的。这将在后面关于接口和继承的课程中讨论。
你可以在构造函数的声明中使用访问修饰符来控制哪些其他类可以调用该构造函数。
**注意:**如果另一个类无法调用MyClass
构造函数,则无法直接创建MyClass
对象。
向方法或构造函数传递信息
原文:
docs.oracle.com/javase/tutorial/java/javaOO/arguments.html
方法或构造函数的声明声明了该方法或构造函数的参数的数量和类型。例如,以下是一个计算房屋贷款月供的方法,基于贷款金额、利率、贷款期限(期数)和贷款的未来价值:
代码语言:javascript复制public double computePayment(
double loanAmt,
double rate,
double futureValue,
int numPeriods) {
double interest = rate / 100.0;
double partial1 = Math.pow((1 interest),
- numPeriods);
double denominator = (1 - partial1) / interest;
double answer = (-loanAmt / denominator)
- ((futureValue * partial1) / denominator);
return answer;
}
该方法有四个参数:贷款金额、利率、未来价值和期数。前三个是双精度浮点数,第四个是整数。这些参数在方法体中被使用,并在运行时将采用传递的参数的值。
注意: 参数指的是方法声明中的变量列表。参数是在调用方法时传递的实际值。当您调用方法时,使用的参数必须与声明的参数在类型和顺序上匹配。
参数类型
您可以为方法或构造函数的参数使用任何数据类型。这包括原始数据类型,如双精度、浮点数和整数,就像您在computePayment
方法中看到的那样,以及引用数据类型,如对象和数组。
这是一个接受数组作为参数的方法的示例。在这个示例中,该方法创建一个新的Polygon
对象,并从一个Point
对象数组中初始化它(假设Point
是表示 x、y 坐标的类):
public Polygon polygonFrom(Point[] corners) {
// method body goes here
}
**注意:**如果您想将一个方法传递给另一个方法,那么请使用 lambda 表达式或方法引用。
任意数量的参数
您可以使用称为varargs的构造来传递任意数量的值给方法。当您不知道将传递给方法的特定类型的参数有多少时,可以使用 varargs。这是一种快捷方式,可以手动创建数组(前一个方法可以使用 varargs 而不是数组)。
要使用 varargs,您需要在最后一个参数的类型后面加上省略号(三个点,…),然后是一个空格和参数名。然后该方法可以使用任意数量的该参数调用,包括零个。
代码语言:javascript复制public Polygon polygonFrom(Point... corners) {
int numberOfSides = corners.length;
double squareOfSide1, lengthOfSide1;
squareOfSide1 = (corners[1].x - corners[0].x)
* (corners[1].x - corners[0].x)
(corners[1].y - corners[0].y)
* (corners[1].y - corners[0].y);
lengthOfSide1 = Math.sqrt(squareOfSide1);
// more method body code follows that creates and returns a
// polygon connecting the Points
}
您可以看到,在方法内部,corners
被视为数组。该方法可以使用数组或一系列参数调用。方法体中的代码将在任何情况下将参数视为数组。
您最常见地会在打印方法中看到 varargs;例如,这个printf
方法:
public PrintStream printf(String format, Object... args)
允许您打印任意数量的对象。可以这样调用:
代码语言:javascript复制System.out.printf("%s: %d, %s%n", name, idnum, address);
或者像这样
代码语言:javascript复制System.out.printf("%s: %d, %s, %s, %s%n", name, idnum, address, phone, email);
或者使用不同数量的参数。
参数名称
当您向方法或构造函数声明参数时,为该参数提供一个名称。此名称在方法体内用于引用传入的参数。
参数的名称在其作用域内必须是唯一的。它不能与同一方法或构造函数的另一个参数的名称相同,也不能与方法或构造函数内的局部变量的名称相同。
参数可以与类的字段之一具有相同的名称。如果是这种情况,则说参数遮蔽了字段。字段遮蔽可能会使您的代码难以阅读,并且通常仅在设置特定字段的构造函数和方法中使用。例如,考虑以下 Circle
类及其 setOrigin
方法:
public class Circle {
private int x, y, radius;
public void setOrigin(int x, int y) {
...
}
}
Circle
类有三个字段:x
、y
和 radius
。setOrigin
方法有两个参数,每个参数的名称与一个字段的名称相同。每个方法参数都会遮蔽与其名称相同的字段。因此,在方法体内使用简单名称 x
或 y
指的是参数,而不是字段。要访问字段,必须使用限定名称。这将在本课程的后面部分“使用 this
关键字”中讨论。
传递原始数据类型参数
原始参数,如 int
或 double
,是通过值传递给方法的。这意味着对参数值的任何更改仅存在于方法的范围内。当方法返回时,参数消失,对它们的任何更改都将丢失。以下是一个例子:
public class PassPrimitiveByValue {
public static void main(String[] args) {
int x = 3;
// invoke passMethod() with
// x as argument
passMethod(x);
// print x to see if its
// value has changed
System.out.println("After invoking passMethod, x = " x);
}
// change parameter in passMethod()
public static void passMethod(int p) {
p = 10;
}
}
运行此程序时,输出为:
代码语言:javascript复制After invoking passMethod, x = 3
传递引用数据类型参数
引用数据类型参数,如对象,也是通过值传递给方法的。这意味着当方法返回时,传入的引用仍然引用与之前相同的对象。但是,如果对象的字段值具有适当的访问级别,则可以在方法中更改对象的字段值。
例如,考虑一个在任意类中移动 Circle
对象的方法:
public void moveCircle(Circle circle, int deltaX, int deltaY) {
// code to move origin of circle to x deltaX, y deltaY
circle.setX(circle.getX() deltaX);
circle.setY(circle.getY() deltaY);
// code to assign a new reference to circle
circle = new Circle(0, 0);
}
让方法使用这些参数被调用:
代码语言:javascript复制moveCircle(myCircle, 23, 56)
在方法内部,circle
最初指向 myCircle
。该方法更改了 circle
引用的对象(即 myCircle
)的 x 和 y 坐标分别为 23 和 56。这些更改将在方法返回时保留。然后 circle
被赋予一个新的 Circle
对象的引用,其中 x = y = 0
。然而,这种重新赋值并不具有永久性,因为引用是通过值传递的,不能更改。在方法内部,circle
指向的对象已经改变,但是,当方法返回时,myCircle
仍然引用与调用方法之前相同的 Circle
对象。