Java基础(三)——流程控制和数组

三目运算符:(expression)? x1 :x2 //表达式为真则执行x1,否则执行x2
else本来就是对if的取反。为避免出现错误,总把范围较小的条件放在前面处理。
switch中case标签的代码块的开始和结束节点非常清晰,可以不加方括号。当所有case都不符合时,执行default代码块。
如果将case代码块中的break语句都注释掉,则只要找到符合的case后面的语句都会执行,这是由switch语句的运行流程决定的。switch语句会先求出expression表达式的值,然后与case标签后面的值相比较,一旦遇到相等的值,程序就开始执行case后面的代码,不再判断与后面的case、default标签后的条件是否匹配,除非遇到break;才结束。
循环结构:在反复执行循环体时,需要在合适的时候把循环条件变为假,结束循环,否则会形成死循环。

while

先判断后循环

1
2
3
4
5
6
[init_statement]
while(test_expression)
{
statement;
[iteration_statement]
}

do-while

先循环后判断是否为真,为真则进入下一次循环

1
2
3
4
5
6
[init_statemnt]
do
{
statement;
[iteration_statement]
}while(expression);

for循环

每次执行循环前,先判断test_expression条件是否为真,为真则执行循环体,循环体结束后执行iteration_statement循环迭代语句。值得注意的是,for循环的循环迭代语句并没有与循环体放在一起,所以即使在执行循环体遇到continue语句,结束本次循环,循环迭代语句依然会得到执行。

1
2
3
4
for([init_statement]; [test_expression]; [iteration_statement])
{
statement;
}

将for循环的初始语句放在循环之前定义的作用:可扩大初始化语句中定义的变量的作用域。在for循环里定义的变量,作用域只能在循环内有效,for循环终止后,这些变量将不可访问。若要在循环体之外使用这个变量i的值,可以在循环体内定义一个额外变量保存这个循环变量的值。

1
2
3
4
5
6
7
int tmp = 0;
for(int i = 0; i < 10; i ++)
{
System.out.println(i);
tmp = i; //使用tmp保存循环变量i的值
}
...//此处(循环体之外)还可通过tmp变量来访问i变量的值。

控制循环结构:continue和break来控制循环结构,return可以结束整个方法,即结束一次循环。
break语句可以结束其所在的循环,注意只是结束离它最近的循环,外面的循环一般情况下不会结束:

1
2
3
4
5
6
7
8
for(i = 0; i < 5; i++)
{
System.out.println(“i的值是”+ i);
if(i==2)
{
break;
}
}

break语句也可以结束其外层循环,需要用标签标识外层循环:注意标签要用在break所在循环的外层循环前才有意义。

1
2
3
4
5
6
7
8
9
10
11
12
13
outer:
for(i = 0; i<5; i++)
{
System.out.println(“i的值是”+ i);
for(j=0; j<4; j++)
{
{System.out.println(“i的值为”+i+”j的值为+”j”);
if(i==2)
{
break outer;
}
}
}

continue语句忽略本次循环后面的语句,然后进行下一次循环:注意continue放在单次循环的最后一行是没有意义的,因为它没有忽略任何语句。

1
2
3
4
5
6
7
8
9
forint i=0; i<5; i++)
{
System.out.println(“i的值是” + i);
if(i == 1)
{
continue;
}
System.out.println(“continue后面的语句”);
}

continue语句也可以结束其外层循环,需要用标签标识外层循环。
return语句不是专门用于结束循环的,return的功能是结束一个方法。Java中大部分循环都在方法中执行,一旦循环体执行到return语句,return语句就会结束该方法,循环自然也结束。

一个数组只能存储一种类型的数据。Java是面向对象的语言,类与类之间可以支持继承关系,这样可能产生一个数组里可以存放多种数据类型的假象。如有一个水果数组,要求数组中每个元素都是水果,实际上数组元素可以是苹果,香蕉等。
一旦数组的初始化完成,数组在内存中所占的空间将被固定下来,因此数组的长度将不可改变。
Java的数组既可以存储基本数据类型,也可以存储引用类型的数据。
数组也是一种数据类型,他本是是一种引用类型。如int是一个基本类型,int[]也是数据类型,但它是一种引用类型。创建int[]类型的对象就是创建数组,需要使用创建数组的语法。
推荐使用的定义数组的语法:type[] arrayName; //定义一个变量,变量类型是type[],变量名是arrayName。
数组是一种引用类型的变量,因此使用它定义一个变量时,仅仅表示定义了一个引用变量(即定义了一个指针),这个引用变量还未分配内存。需初始化才能分配内存。注意定义数组不能指定数组的长度。
数组的初始化new:java语言中数组先初始化,才能使用。

(1) 静态初始化:显式地指定每个数组元素的初始值

1
2
type[] arrayName; //定义type[]数组类型的变量arrayName
arrayName = new type[]{element1, element2,…}; //静态初始化变量arrayName

实际开发过程中更习惯将数组定义和初始化同时完成,以下是简化形式:=>

1
type[] arrayName = {element1, element2,…};

(2) 动态初始化:只指定数组的长度

1
2
type[] arrayName;
arrayName = new type[length];

简化形式:=>

1
2
//数组的定义和初始化同时完成,初始化时元素的类型可以是定义的数组元素类型的子类
type[] arrayName = new type[length];

又如

1
Object[] books = new String[4];

其中可分别写成定义+初始化:

1
2
Object[] books; //定义一个Objectt[]数组类型的变量books
books = new String[4];//初始化的元素类型String是Object的子类

动态初始化时,系统将按如下规则分配初始值:

  • 整数类型:0
  • 浮点类型:0.0
  • 字符类型:’\u0000’,Java中,字符型并不是用ASCII码来存储的,而是用16位的Unicode字符集来存储
  • 布尔类型:false
  • 引用类型(类、接口和数组):null

访问数组元素:数组引用变量后跟方括号,括号里是索引值,这样就可以访问数组元素了。访问到数组元素后就可以把数组元素当成普通变量来使用。Java数组索引是从0开始,最后一个数组元素的索引值 = 数组长度-1。
超过索引范围编译不会出现问题,但运行时会出现异常:
java.lang.ArrayIndexOutOfBoundsException:N(数组索引越界异常,N为导致越界的索引)
所有数组都提供了length属性,通过这个属性可以访问到数组长度。

1
2
for(int i = 0; i < books.length; i++)

foreach循环

无需获得数组或集合的长度,无需根据索引来访问数组元素和集合元素,foreach循环自动遍历数组或集合的每个元素。它无需循环条件,无需迭代条件,这些部分将由系统自动完成。语法如下:
//type是数组元素或集合元素的类型,variableName是形参名,为循环变量,foreach循环将自动将数组元素、集合元素依次赋给该变量。(注意:不要对循环变量赋值,容易有歧义,这只是临时变量,给它赋值并不能改变数组元素的值)

1
2
3
4
foreach(type variableNme: array/collection)
{
// variableName自动迭代访问每个元素
}

数组类型是一种引用类型,数组变量其实是一个引用,这个引用指向真实的内存。通常无法直接访问堆内存中的数组元素,只能通过引用变量访问数组对象本身。数组变量(栈区)和数组元素(堆区)在内存中是分开存放的。实际数组对象被存储在堆(heap)内存中;如果引用该数组对象的数组引用变量是一个局部变量,那么它存储在栈(stack)内存中。
为什么有栈内存和堆内存之分:
当一个方法执行时,这个方法会建立自己的内存栈,在这个方法中定义的变量将会逐个放入这块内存栈中,随着方法的执行结束,这个内存栈也将销毁。即所有在方法中定义的局部变量都会放入这个栈内存中;在程序中创建一个(方法的)对象时,这个对象将被保存到堆内存(运行时的数据区)中,以便反复使用(因为对象的创建成本较大)。堆内存中的元素不会随方法的结束而销毁,即使方法结束,堆内存中的对象还可能被另一个引用变量引用(在方法的参数传递中很常见),则这个对象依然不会被销毁。只有当一个对象没有任何引用变量引用它时,系统的垃圾回收器才会在适当的时候回收它。

如果堆内存中数组不再有任何变量指向自己,则这个数组将成为垃圾,该数组所占的内存将会被系统的垃圾回收机制回收。因此,为了让垃圾回收机制回收一个数组所占的内存空间,可以将该数组变量赋值为null,这样解切断了数组引用变量和实际数组之间的引用关系,实际数组也成了垃圾。

以上讨论的是基本类型数组的初始化,还用一种是引用类型数组,引用类型数组的数组元素都是引用,它指向另一块内存,该内存中存储了有效数据。
定义一个Person类(所有类都是引用类型):new是实例化的意思,表示建立变量与对象之间的关系。

定义一个Person[]数组(Person[]类是引用类型数组),动态初始化这个数组,并为这个数组的每个元素赋值。

没有多维数组,java语言里提供了支持多维数组的语法,但从数组的底层机制上来看,没有多维数组。Java语言里数组类型是引用类型,因此数组变量其实是一个引用,这个引用指向真实的内存。而数组元素也可以是引用,如果数组元素的引用再次指向真实的数组内存,这种形式看上去就像多维数组。
定义并初始化“多维数组”:

1
2
//动态初始化二维数组。定义一个数组变量b,这个数组变量指向一个长度为3的数组,默认值是null。这个数组的每个元素又是一个数组类型,它们各自指向长度为4的type[]数组类型,每个数组元素的值为0。
int[][] b = new int[3][4];
1
2
3
String[][] str1 = new String[][]{new String[3], new String[]{“hello”}};//静态初始化二维数组
//简化的静态初始化二维数组。String[3]表示定义了一个String[]类型引用,长度为3,String[]{“hello”}表示定义了一个String[]类型引用,指向Hello。
String[][] str2 = {new String[3], new String[]{“hello”}};

静态初始化二维数组时,二维数组的每个数组元素都是一维数组,因此必须指定多个一维数组作为二维数组的初始化值。

Java8增强的工具类:Arrays
Arrays类中包含一些static修饰的方法,可以直接操作数组(static修饰的方法可以直接通过类名调用):导入类使用java.util.Arrays类,调用时使用Arrays. binarySearch( type[] a, type key)
int binarySearch(type[] a, type key):使用二分法查找key元素值在a中出现的索引,用二分法查找之前必须保证a是升序排列。如果a中不包含key元素值,则返回负数。Arrary. binarySearch(a, 2, 4, 1)
Int binarySearch(type[] a, int fromIndex, int toIndex, type key):与上面类似,但只搜索索引从fromIndex到toIndex的元素。
type[] copyOf(type[] original, int length):将original数组复制成新的数组,新数组长度为length。
type[] copyOfRange(type[] original, int from, int to)
boolean equals(type[] a, type[] b)
void fill(type[] a, type val):对数组a赋值为val。
void fill(type[] a, int fromIndex, int toIndex, type val)
void sort(type[] a):对数组排序
void sort(type[] a, int fromIndex, int toIndex)
String toString(type[] a):将数组转换成字符串
Java8为Arrays类增加了一些工具方法,如并发支持利用CPU的并行处理能力来提高性能。
.split()分成两部分
Integer.parseInt()将字符串转换成十进制数字
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//从键盘获取字符
br.readLine()读入一串字符
System.out.print();和Syestem.out.println();用来输出,
System.in.read()用来从键盘输入。
字符以整数形式返回,因此如果要给其赋char型,必须使用强制类型转换。默认控制台的输入是行缓冲的(line buffered),缓冲区存储一整行文本,因此在输入字符后敲击回车键才能将文本送入程序
有时System.in.read()的行缓冲会带来一些烦恼,当按ENTER键时,会往输入流输入回车符和转化符,在一些系统中,只会输入转换符,另外,在读取字符前,后面的字符不断追加到前面的缓冲中,对于一些应用,需要在下次输入前通过读取字符来移除他们。
因为使用了System.in.read(),所以程序必须指定抛出异常的语句throws java.io.IOException。
这行代码对处理输入错误是必要的。
嵌套的if(nested if)

switch

switch表达式可以使char、byte、short、int或枚举类型,从JDK7起,可以用String类型。statement squence
switch没有break时,会继续执行下一个case。还有空的case省略多个case子句在共享代码时很常见。
快捷方式,自动补全:输入main后,Alt+/,输入syso后,Alt+/

1
2
3
4
5
6
7
switch(i){
case 0:
case 1:
case 2:System.out.println("i is 0,1 or 2");
break;
default:System.out.println(i is 3 or more);
}

for(x = 100; x > 0;x -= 5) //每次递减5
无限循环:for(; ;) {…}可以通过break终止循环。
for(i = 1;i < 5;sum += i++)其中的求和运算等于sum+=i;i++;
与for一样,while也在循环开始时检验条件表达式,如果条件为假则不执行,消除了在循环开始前执行单独测试的需要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Compute integer powers of 2(power幂)
public class Power {
public static void main(String[] args){
int i,e,result;
for(i=0;i<10;i++){
result=1;
e=i;
while(e>0){ //while只在e大于0时执行,因此在for循环的第一次迭代中,e为0不执行
result*=2;
e--;
}
System.out.println("2 to the " + i + " power is " + result);
}
}
}

与for、while循环不同,do-while循环在每次循环结束时测试条件,这意味着do-while循环总是至少执行一次。循环不断重复,直到用户做出有效反应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package cha_three;
//guess the letter game
public class Guess2 {
public static void main(String[] args)
throws java.io.IOException{
char ch,ignore,answer='k';
do{
System.out.println("Thinking a letter between a to z");
System.out.println("Can you guess it: ");
ch=(char)System.in.read();
do{
ignore=(char)System.in.read();
}while(ignore!='\n');//这个循环通过不断读取输入来丢弃多余的字符,直到行尾 */
if(ch==answer) System.out.println("Right");
else {
System.out.print("Sorry,you are ");
if(ch<answer) System.out.println("too low");
else System.out.println("too high");
}
}while(ch!=answer);
}
}
/*控制条输入是行缓冲的,必须在字符发送前按回车键,按回车键会生成一个回车符和换行符序列。这些字符会加在缓冲区中,
* 另外,如果在按回车键前输入的字符多余一个,它们也会存储在输入缓冲区中。
do{
ignore=(char)System.in.read();
}while(ignore!='\n');//这个循环通过不断读取输入来丢弃多余的字符,直到行尾
如果不丢弃这些字符,它们也会送至程序,这是不希望看到的。*/

Java中没有goto语句,可以使用break构造成类似goto的用法:break label;
continue有选择地跳过剩下的任何代码和对循环控制条件的判断强迫开始下次循环。
break是跳出整个层的循环,continue是结束整个层的这一次循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//The finished Java statement Help system that processes multiple requests.
//使用无限循环,允许用户多次请求获得帮助
public class Help3 {
public static void main(String[] args)
throws java.io.IOException{
char choice,ignore;
for(; ;){
do{
System.out.println("Help on:");
System.out.println("1.if");
System.out.println("2.swicth");
System.out.println("3.for");
System.out.println("4.while");
System.out.println("5.do-while");
System.out.println("6.break");
System.out.println("7.continue");
System.out.println("Choose one(q to quit):"); //quit离开;停止
choice=(char)System.in.read();
do{
ignore=(char)System.in.read();
}while(ignore!='\n');
}while(choice<'1'|choice>'7'& choice!='q');//外层循环一直进行,直到用户输入q作为用户菜单选择。
if(choice=='q') break; //结束整个无限循环
System.out.println();
switch(choice){
case '1':System.out.println("1.The if:");
System.out.println("if(condition) statement;");
System.out.print("else statement;");
break;
case '2':System.out.println("2.The switch");
System.out.println("switch(expression){ ");
System.out.println("case contant: statement squence; \n break; \n case ... ;\n }");
break;
case '3':System.out.println("3.The for");
System.out.println("for(init;condition;iteration) \n statement;");
break;
case '4':System.out.println("4.The while");
System.out.println("while(condition){ \n statment; \n }");
break;
case '5':System.out.println("5.The do-while");
System.out.println("do{ \n statement; \n}while(condition);");
break;
case '6':System.out.println("6.The break");
System.out.println("break; or break label;");
break;
case '7':System.out.println("7.The continue");
System.out.println("continue;or continue label;");
break;
}
}System.out.println("You have quitted the Help.");
}
}