(最终还是决定重新写一份Java基础相关的内容,原来因为在写这一个章节的时候没有考虑到会坚持往后边写,这次应该是更新该内容。而且很讨厌写基础的东西,内容比较琐碎,而且整理起来总会很多,有可能会打散成两个章节,但是我不保证,有可能一个章节就写完了,所以有时候希望基础的很多内容还是读者自己去看看,我基本保证把基础的内容全部都写出来,见谅。这一个章节写了过后我会把前边那个关于基础类型的章节从目录里面删除掉,以保证教材的完整性和唯一性,防止有人收藏过链接,我会继续保留在BLOG地址上边不删除,所以请读者见谅!初学者可以从本章的第三小节开始看,因为前两个章节内容也是比较概念性的,刚开始可以不去理解。这里直接从Java语法开始,不提及Java的历史以及Java的前序发展,如果有什么笔误,就发我Email:silentbalanceyh@126.com)
本章目录
1.概念以及提纲
2.语言基础
3.数据类型[一部分]
4.操作符
5.控制流程
6.关键字清单
5)Java的浮点精度:
[1]精确的浮点运算:
在Java里面,有时候为了保证数值的准确性需要精确的数据,先提供一个例子就可以发现问题了:
package org.susan.java.basic;
public class FloatNumberTester {
public static void main(String args[]){
System.out.println(0.05+0.01);
System.out.println(1.0 - 0.42);
System.out.println(4.015 * 100);
System.out.println(123.3 / 100);
}
}
按照我们的期待,上边应该是什么结果呢,但是看输出我们就会发现问题了:
0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999
这样的话这个问题就相对严重了,如果我们使用123.3元交易,计算机却因为1.2329999999999999而拒绝了交易,岂不是和实际情况大相径庭
[2]四舍五入:
另外的一个计算问题,就是四舍五入。但是Java的计算本身是不能够支持四舍五入的,比如:
package org.susan.java.basic;
public class GetThrowTester {
public static void main(String args[]){
System.out.println(4.015 * 100.0);
}
}
这个输出为:
401.49999999999994
所以就会发现这种情况并不能保证四舍五入,如果要四舍五入,只有一种方法java.text.DecimalFormat:
package org.susan.java.basic;
import java.text.DecimalFormat;
public class NumberFormatMain {
public static void main(String args[]){
System.out.println(new DecimalFormat("0.00").format(4.025));
System.out.println(new DecimalFormat("0.00").format(4.024));
}
}
上边代码输出为:
4.02
4.02
发现问题了么?因为DecimalFormat使用的舍入模式,关于DecimalFormat的舍入模式在格式化一章会讲到
[3]浮点输出:
Java浮点类型数值在大于9999999.0就自动转化成为科学计数法,看看下边的例子:
package org.susan.java.basic;
public class FloatCounter {
public static void main(String args[]){
System.out.println(9969999999.04);
System.out.println(199999999.04);
System.out.println(1000000011.01);
System.out.println(9999999.04);
}
}
输出结果为:
9.96999999904E9
1.9999999904E8
1.00000001101E9
9999999.04
但是有时候我们不需要科学计数法,而是转换成为字符串,所以这样可能会有点麻烦。
[4]BigInteger类和BigDecimal介绍:
BigInteger用法:
package org.susan.java.basic;
import java.math.BigInteger;
public class BigIntTester {
public static void main(String args[]){
BigInteger leftInteger = new BigInteger("1234567890123400");
BigInteger rightInteger = BigInteger.valueOf(123L);
//大数加法
BigInteger resultInteger = leftInteger.add(rightInteger);
System.out.println("Add:" + resultInteger);
//大数除法
resultInteger = leftInteger.divide(rightInteger);
System.out.println("Divide:" + resultInteger);
//大数减法
resultInteger = leftInteger.subtract(rightInteger);
System.out.println("Substract:" + resultInteger);
//大数乘法
resultInteger = leftInteger.multiply(rightInteger);
System.out.println("Multiply:" + resultInteger);
}
}
上边的输出为:
Add:1234567890123523
Divide:10037137318076
Substract:1234567890123277
Multiply:151851850485178200
在BigInteger里面有很多运算,这里只是提供了普通的+、-、*、/的运算,可以参考以下BigInteger的方法列表:
BigInteger abs():返回BigInteger的绝对值
BigInteger add(BigInteger val):返回为(this + val)的BigInteger,做加法
BigInteger and(BigInteger val):返回为(this & val)的BigInteger,进行位与运算
BigInteger andNot(BigInteger val):返回为(this & ~val)的BigInteger
int bitCount():返回此BigInteger的二进制补码表示形式中与符号不同的位的常量
int bitLength():返回此BigInteger的最小的二进制补码表示形式的位数,不包括符号位
BigInteger clearBit(int n):返回其值与清除了指定位的此BigInteger等效的BigInteger
int compareTo(BigInteger val):将此BigInteger与指定的BigInteger进行比较
BigInteger divide(BigInteger val):返回值为(this/val)的BigInteger,做除法
BigInteger[] divideAndRemainder(BigInteger val):包含返回(this/val)后跟(this & val)的两个BigInteger的数组
double doubleValue():将此BigInteger转换为double
boolean equals(Object s):比较此BigInteger与指定的Object的相等性
BigInteger flipBit(int n):返回其值与对此BigInteger进行指定位翻转过后的值等效的BigInteger
float floatValue():将此BigInteger转换为float
BigInteger gcd(BigInteger val):返回一个BigInteger,值为abs(this)和abs(val)的最大公约数
int getLowestSetBit():返回此 BigInteger 最右端(最低位)1 比特的索引(即从此字节的右端开始到本字节中最右端 1 比特之间的 0 比特的位数)
int hashCode():返回此BigInteger的哈希码
int intValue():将此BigInteger转换为int
boolean isProbablePrime(int certaintry):如果此 BigInteger 可能为素数,则返回 true,如果它一定为合数,则返回 false
long longValue():将此BigInteger转换为long
BigInteger max(BigInteger val):返回this和val的最大值
BigInteger min(BigInteger val):返回this和val的最小值
BigInteger mod(BigInteger val):返回this mod val的结果,取模运算
BigInteger modInverse(BigInteger val):返回其值为 (this-1 mod val) 的 BigInteger
BigInteger modPow(BigInteger exponent,BigInteger val):返回其值为 (thisexponent mod val) 的 BigInteger
BigInteger multiply(BigInteger val):返回值为(this*val)的BigInteger
BigInteger negate():返回值是(-this)的BigInteger
BigInteger nextProbablePrime():返回大于此BigInteger的可能为素数的第一个整数
BigInteger not():返回其值为(~this)的BigInteger
BigInteger or(BitInteger val):返回值为(this|val)的BigInteger
BigInteger pow(int exponent):返回其值为 (thisexponent) 的 BigInteger
static BigInteger probablePrime(int bitLength,Random rnd):返回有可能是素数的、具有指定长度的正 BigInteger
BigInteger remainder(BigInteger val):返回其值为 (this % val) 的 BigInteger
BigInteger setBit(int n):返回其值与设置了指定位的此 BigInteger 等效的 BigInteger
BigInteger shiftLeft(int n):返回其值为 (this << n) 的 BigInteger
BigInteger shiftRight(int n):返回其值为 (this >> n) 的 BigInteger
int signum():返回此 BigInteger 的正负号函数
BigInteger substract(BigInteger val):返回其值为 (this - val) 的 BigInteger
BigInteger xor(BigInteger val):返回其值为 (this ^ val) 的 BigInteger
BigDecimal用法:
BigDecimal是Java提供的一个不变的、任意精度的有符号十进制数对象。它提供了四个构造器,有两个是用BigInteger构造,在这里我们不关心,我们重点看用double和String构造的两个构造器:
BigDecimal(double val):把一个double类型十进制数构造为一个BigDecimal对象实例
BigDecimal(String val):把一个以String表示的BigDecimal对象构造为BigDecimal对象实例
用一段代码来说明两个构造函数的区别:
package org.susan.java.basic;
import java.math.BigDecimal;
public class BigDecimalMain {
public static void main(String args[]){
System.out.println(new BigDecimal(123456789.01).toString());
System.out.println(new BigDecimal("123456789.01").toString());
}
}
看了输出结果过后,两种构造函数的区别一目了然:
123456789.01000000536441802978515625
123456789.01
BigDecimal舍入模式介绍:
舍入模式在java.math.RoundingMode里面:
RoundingMode.CEILING:向正无限大方向舍入的舍入模式。如果结果为正,则舍入行为类似于 RoundingMode.UP;如果结果为负,则舍入行为类似于 RoundingMode.DOWN。注意,此舍入模式始终不会减少计算值
输入数字 | 使用CEILING舍入模式将数字舍入为一位数 |
5.5 | 6 |
2.5 | 3 |
1.1 | 2 |
1.0 | 1 |
-1.0 | -1 |
-1.1 | -1 |
-1.6 | -1 |
-2.5 | -2 |
-5.5 | -5 |
RoundingMode.DOWN:向零方向舍入的舍入模式。从不对舍弃部分前面的数字加 1(即截尾)。注意,此舍入模式始终不会增加计算值的绝对值
输入数字 | 使用DOWN舍入模式将数字舍入为一位数 |
5.5 | 5 |
2.5 | 2 |
1.1 | 1 |
-1.0 | -1 |
-1.6 | -1 |
-2.5 | -2 |
-5.5 | -5 |
RoundingMode.FLOOR:向负无限大方向舍入的舍入模式。如果结果为正,则舍入行为类似于 RoundingMode.DOWN;如果结果为负,则舍入行为类似于 RoundingMode.UP。注意,此舍入模式始终不会增加计算值
输入数字 | 使用FLOOR舍入模式将输入数字舍入为一位 |
5.5 | 5 |
2.3 | 2 |
1.6 | 1 |
1.0 | 1 |
-1.1 | -2 |
-2.5 | -3 |
-5.5 | -6 |
RoundingMode.HALF_DOWN:向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向下舍入。如果被舍弃部分 > 0.5,则舍入行为同 RoundingMode.UP;否则舍入行为同 RoundingMode.DOWN
输入数字 | 使用HALF_DOWN输入模式舍入为一位 |
5.5 | 5 |
2.5 | 2 |
1.6 | 2 |
1.0 | 1 |
-1.1 | -1 |
-1.6 | -2 |
-2.5 | -2 |
-5.5 | -5 |
RoundingMode.HALF_EVEN:向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。如果舍弃部分左边的数字为奇数,则舍入行为同 RoundingMode.HALF_UP;如果为偶数,则舍入行为同 RoundingMode.HALF_DOWN。注意,在重复进行一系列计算时,此舍入模式可以在统计上将累加错误减到最小。此舍入模式也称为“银行家舍入法”,主要在美国使用。此舍入模式类似于 Java 中对 float 和 double 算法使用的舍入策略
输入数字 | 使用HALF_EVEN舍入模式将输入舍为一位 |
5.5 | 6 |
2.5 | 2 |
1.6 | 2 |
1.1 | 1 |
-1.0 | -1 |
-1.6 | -2 |
-2.5 | -2 |
-5.5 | -6 |
RoundingMode.HALF_UP:向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向上舍入。如果被舍弃部分 >= 0.5,则舍入行为同 RoundingMode.UP;否则舍入行为同 RoundingMode.DOWN。注意,此舍入模式就是通常学校里讲的四舍五入
输入数字 | 使用HALF_UP舍入模式舍入为一位数 |
5.5 | 6 |
2.5 | 3 |
1.6 | 2 |
1.0 | 1 |
-1.1 | -1 |
-1.6 | -2 |
-2.5 | -3 |
-5.5 | -6 |
RoundingMode.UNNECESSARY:用于断言请求的操作具有精确结果的舍入模式,因此不需要舍入。如果对生成精确结果的操作指定此舍入模式,则抛出 ArithmeticException
输入数字 | 使用UNNECESSARY模式 |
5.5 | 抛出 ArithmeticException |
2.5 | 抛出 ArithmeticException |
1.6 | 抛出 ArithmeticException |
1.0 | 1 |
-1.0 | -1.0 |
-1.1 | 抛出 ArithmeticException |
-1.6 | 抛出 ArithmeticException |
-2.5 | 抛出 ArithmeticException |
-5.5 | 抛出 ArithmeticException |
RoundingMode.UP:远离零方向舍入的舍入模式。始终对非零舍弃部分前面的数字加 1。注意,此舍入模式始终不会减少计算值的绝对值
输入数字 | 使用UP舍入模式将输入数字舍入为一位数 |
5.5 | 6 |
1.6 | 2 |
1.1 | 2 |
1.0 | 1 |
-1.1 | -2 |
-1.6 | -2 |
-2.5 | -3 |
-5.4 | -6 |
——[$]示例代码:——
package org.susan.java.basic;
import java.math.BigDecimal;
import java.text.DecimalFormat;
/**
*使用舍入模式的格式化操作
**/
public class DoubleFormat {
public static void main(String args[]){
DoubleFormat format = new DoubleFormat();
System.out.println(format.doubleOutPut(12.345, 2));
System.out.println(format.roundNumber(12.335, 2));
}
public String doubleOutPut(double v,Integer num){
if( v == Double.valueOf(v).intValue()){
return Double.valueOf(v).intValue() + "";
}else{
BigDecimal b = new BigDecimal(Double.toString(v));
return b.setScale(num,BigDecimal.ROUND_HALF_UP).toString();
}
}
public String roundNumber(double v,int num){
String fmtString = "0000000000000000"; //16bit
fmtString = num>0 ? "0." + fmtString.substring(0,num):"0";
DecimalFormat dFormat = new DecimalFormat(fmtString);
return dFormat.format(v);
}
}
这段代码的输出为:
12.35
12.34
ii.数组、复杂类型
1)数组的定义、初始化:
在Java语言里面使用下边的方式定义数组:
Type[] arrayone;
Type arrayone[];
这里的Type可以是基本数据类型,也可以是Object类的子类的引用,arrayone在这里为一个Java合法的标识符
——[$]这里提供一个参考代码进行数组的定义和初始化——
package org.susan.java.basic;
import java.util.Arrays;
/**
*数组的定义和初始化过程
**/
public class ArraysMain {
public static void main(String args[]){
String[] arrays1 = new String[3];
String[] arrays2 = new String[]{"A","B","C"};
String[] arrays3 = {"A","B","C","D"};
System.out.println("Array 1:" + Arrays.asList(arrays1));
System.out.println("Array 2:" + Arrays.asList(arrays2));
System.out.println("Array 3:" + Arrays.asList(arrays3));
}
}
先看上边代码段的输出:
Array 1:[null, null, null]
Array 2:[A, B, C]
Array 3:[A, B, C, D]
这里简单总结一下Java里面定义、创建、初始化数组的一些细节问题:
- 定义数组一般用Type[] arrays或者Type arrays[]两种格式,Java里面推荐使用Type[] arrays这种方式定义数组
- 创建数组的时候可以使用三种方式创建,上边的arrays1创建了一个长度为3的字符串数组,但是未被初始化
- 初始化过程有两种方式,上边arrays2和arrays3是直接赋值,这种情况不仅仅创建了数组,而且进行了初始化的操作,还有一种方式像下边这样针对每个元素初始化
——[$]单项初始化——
package org.susan.java.basic;
import java.util.Arrays;
public class ArrayItemMain {
public static void main(String args[]){
String[] arrayStrings = new String[3];
for(int i = 0; i < arrayStrings.length; i++){
arrayStrings[i] = new String("String" + i);
}
System.out.println(Arrays.asList(arrayStrings));
}
}
上边的输出为:
[String0, String1, String2]
由此可以知道,在这样的循环里面已经将定义的长度为3的字符串数组的每一项都初始化了,而且值分别为String0,String1,String2,而且从上边可以知道,数组的元素访问使用索引进行访问,格式为:
arrays[index],这里需要特殊说明的是索引的范围:0 <= index < arrays.length;关于索引的范围需要特别注意,最小的index是0,最大的index值是length-1
2)多维数组:
Java里面不仅仅可以定义一纬数组,还可以定义多维数组,多维数组的定义如下:
int[][] arrays1 = new int[3][2];
int[][] arrays2 = new int[3][];
不合法的定义方式为:
int[][] arrays3 = new int[][2];
【*:这里定义的多维数组为两维数组,不仅仅如此,根据需要可以定义三维数组或者多维数组,其实在Java里面,二维数组就是数组的数组,也就是说在一纬数组的基础之上每一项又保存了一个数组的引用。】
——[$]提供一个多维数组的例子——
package org.susan.java.basic;
import java.util.Arrays;
import java.util.Random;
public class ArrayMultiMain {
public static void main(String args[]){
// 二维规则数组
int[][] arrays = new int[3][3];
Random random = new Random();
for( int i = 0; i < arrays.length; i++){
for( int j = 0; j < arrays[i].length; j++){
arrays[i][j] = random.nextInt(40);
}
}
printArray(arrays);
System.out.println("------------------------");
// 二维不规则数组
int[][] arrays1 = new int[3][];
for( int i = 0; i < arrays1.length; i++){
int innerLength = random.nextInt(6) + 1;
arrays1[i] = new int[innerLength];
for( int j = 0; j < arrays1[i].length; j++){
arrays1[i][j] = random.nextInt(40);
}
}
//System.out.println(Arrays.asList(arrays1))
printArray(arrays1);
System.out.println("------------------------");
int[][] arrays2 = {{12,33},{45,44,46,47},{56,12,9}};
printArray(arrays2);
}
private static void printArray(int [][] arrays){
for(int[] array:arrays){
for(int item:array){
System.out.print("[" + item + "],");
}
System.out.println();
}
}
}
这里运行的输出为:
[8],[16],[4],
[36],[2],[34],
[20],[35],[37],
------------------------
[4],
[8],[38],[37],[7],
[18],[17],[36],
------------------------
[12],[33],
[45],[44],[46],[47],
[56],[12],[9],
针对输出内容需要说明的有几点:
- 第一个数组的行列是定死的,但是里面的数据是随机的,所以每次运行可能里面盛放的项不一样
- 第二个数组为不规则的二维数组,行市定死的,但是每行的列数十不定的,每次运行可能都不一样
- 第三个数组在创建和初始化的时候因为是直接赋值,是一个死数组,所以第三那个数组每次输出都一样
3)Java中的动态数组和静态数组:【摘录自:http://developer.51cto.com/art/200905/122774.htm】
我们学习的数组都是静态数组,其实在很多的时候,静态数组根本不能满足我们编程的实际需要,比方说我需要在程序运行过程中动态的向数组中添加数据,这时我们的静态数组大小是固定的,显然就不能添加数据,要动态添加数据必须要用到动态数组,动态数组中的各个元素类型也是一致的,不过这种类型已经是用一个非常大的类型来揽括—Object类型。Object类是java.lang包中的顶层超类。所有的类型都可以与Object类型兼容,所以我们可以将任何Object类型添加至属于Object类型的数组中,能添加Object类型的的集合有ArrayList、Vector及LinkedList,它们对数据的存放形式仿造于数组,属于集合类,下面是他们的特点:
特点一、容量扩充性
从内部实现机制来讲ArrayList和Vector都是使用Objec的数组形式来存储的。当你向这两种类型中增加元素的时候,如果元素的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度,Vector缺省情况下自动增长原来一倍的数组长度,ArrayList是原来的50%,所以最后你获得的这个集合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一些优势,因为你可以通过设置集合的初始化大小来避免不必要的资源开销。
特点二、同步性
ArrayList,LinkedList是不同步的,而Vestor是的。所以如果要求线程安全的话,可以使用ArrayList或LinkedList,可以节省为同步而耗费开销。但在多线程的情况下,有时候就不得不使用Vector了。当然,也可以通过一些办法包装ArrayList,LinkedList,使他们也达到同步,但效率可能会有所降低。
特点三、数据操作效率
ArrayList和Vector中,从指定的位置(用index)检索一个对象,或在集合的末尾插入、删除一个对象的时间是一样的,可表示为O(1)。但是,如果在集合的其他位置增加或移除元素那么花费的时间会呈线形增长:O(n-i),其中n代表集合中元素的个数,i代表元素增加或移除元素的索引位置。为什么会这样呢?以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素都要执行(n-i)个对象的位移操作。LinkedList中,在插入、删除集合中任何位置的元素所花费的时间都是一样的—O(1),但它在索引一个元素的时候比较慢,为O(i),其中i是索引的位置。所以,如果只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都可以。如果是对其它指定位置的插入、删除操作,最好选择LinkedList、ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要设计到数组元素移动等内存操作,所以索引数据快插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快。
4.操作符
操作 | 优先级 | 结合性 |
后缀运算符 | [] . () 函数调用 | 从左到右 |
单目运算符 | ! ~ ++ -- +(单操作符) -(单操作符) | 从右到左 |
创建 | new | 从左到右 |
乘除 | * / % | 从左到右 |
加减 | + - | 从左到右 |
移位 | << >> >>> | 从左到右 |
关系 | < <= > >= instanceof | 从左到右 |
相等 | == != | 从左到右 |
按位与 | & | 从左到右 |
按位异或 | ^ | 从左到右 |
按位或 | | | 从左到右 |
逻辑与 | && | 从左到右 |
逻辑或 | || | 从左到右 |
条件 | ? : | 从右到左 |
赋值 | = += -= *= /= %= ^= <<= >>= >>>= | 从右到左 |
几乎所有运算符都只能操作“基本类型”(Primitives),只是=、==、!=可以操作所有对象,除此String类型可以支持+、+=操作符:
i.赋值操作符:
一般情况下基本类型可以直接使用=进行赋值操作,看简单的一份代码
package org.susan.java.basic;
/**
*赋值操作符的使用
**/
public class AssignMain {
public static void main(String args[]){
int i = 5;
int j = 10;
i += 5;
j -= 2;
System.out.println("i = " + i);
System.out.println("j = " + j);
}
}
这段代码的输出为:
i = 10
j = 8
赋值操作符一般使用=或者带操作的=进行,带操作的等号比如上边使用的+=、-=等。针对数值操作符而言:
i += b; 等价于 i = i + b;
所以可以理解上边的两句话:
i += 5;
j -= 2;
这种操作符就是带操作的赋值,这里需要说明的主要是赋值操作符=
[1]针对原始类型的=操作符而言,比如a=b【a和b为两个变量】,在赋值的过程里面,这句话进行了下边的操作:JVM在内存里面为b保存一个栈地址用来存储真实的b的值,然后在赋值过程中,JVM创建一个b的副本,然后把b的副本的值赋值到a里面,也就是说在这种情况下如果a的值发生了变化的话,b本身的值是不会受到影响的,而b在赋值过程的副本也是瞬间的,当a一旦接受到b的值过后,b的副本就从内存里面清除掉,就使得a和b各自有了自己的栈地址
[2]如果是非原始类型:比如有两个对象的引用a和b,如果进行了a=b的操作,这句话就进行了下边的操作:JVM会拷贝一个b的引用,然后把引用副本赋值到a,使得a引用和b引用指向同一个对象,当a引用的对象发生改变的时候,b引用指向的对象也会发生同样的改变
——[$]一个赋值的概念说明例子——
package org.susan.java.basic;
/**
*针对赋值符号的概念说明代码,主要是区分上边的不同类型进行赋值操作
**/
public class AssignObject {
public static void main(String args[]){
// Primitives类型
System.out.println("--------------------");
int bNumber = 1;
int aNumber = bNumber;
System.out.println("Before assign aNumber is " + aNumber);
System.out.println("Before assign bNumber is " + bNumber);
aNumber = 12;
System.out.println("After assign aNumber is " +aNumber);
System.out.println("After assign bNumber is " + bNumber);
// Reference类型
System.out.println("--------------------");
StringBuffer bBuffer = new StringBuffer("b");
StringBuffer aBuffer = bBuffer;
System.out.println("Before assign aBuffer is " + aBuffer);
System.out.println("Before assgin bBuffer is " + bBuffer);
aBuffer.append(" assign a");
System.out.println("Before assign aBuffer is " + aBuffer);
System.out.println("Before assgin bBuffer is " + bBuffer);
}
}
上边程序的输出为:
--------------------
Before assign aNumber is 1
Before assign bNumber is 1
After assign aNumber is 12
After assign bNumber is 1
--------------------
Before assign aBuffer is b
Before assgin bBuffer is b
Before assign aBuffer is b assign a
Before assgin bBuffer is b assign a
【*:上边的程序输出刚好说明了上边的两点内容,这里有一点需要注意的是,在使用引用赋值的时候,所说的修改引用指代对象的内容的时候,这里不能使用String类型,因为使用了String类型就会出现很特殊的情况,这一点在String的说明里面我会特别讲述,需要重视的一点是:修改对象内容。这里针对StringBuffer而言,append方法就修改了StringBuffer对象的内容,所以这个程序这样说明是没有问题的,这里再次提醒一下:String类型的对象属于不可变对象,在针对不可变对象的时候,修改对象内容的原理和可变对象修改对象内容的原理不一样的,所以这里不能使用String,演示的代码使用的是StringBuffer。】
简单总结:
- leftVarl op= rightVal格式的表达式等同于leftVal = leftVal op rightVal
- 原始类型赋值操作和引用类型的赋值操作有本质的区别
- 可变对象和不可变对象对内容的更改本质也不一样的
ii.算术运算符:
Java的算数运算符:+、-、/、*、%,优先级【仅针对算术运算符】:
[1]/、*、%是第一优先级的,这三个操作符的运算是从左到右
[2]+、-操作符是第二优先级的,其运算也是从左到右
——[$]浮点取模、整数取模——
package org.susan.java.basic;
/**
*普通运算的优先级,整数取模和浮点取模
**/
public class NumberOpTester {
public static void main(String args[]){
int a = 2;
int b = 4;
int c = 3;
int result = c + b / a;
System.out.println("c + b / a = " + result);
result = c * b / a;
System.out.println("c * b / a = " + result);
double b1 = 4.5f;
double a1 = 2;
double result1 = b1 % a1;
System.out.println(" b % c = " + (b % c));
System.out.println(" b1 % a1 = " + result1);
}
}
上边的输出为:
c + b / a = 5
c * b / a = 6
b % c = 1
b1 % a1 = 0.5
根据上边的运算符,需要说明一点:
在Java运算符里面,不仅仅像C++一样支持整数取模,而且还支持浮点类型的取模,就像上边的最后一个表达式result1 = b1 % a1,就是进行的浮点取模操作;而运算符的优先级,上边代码已经一目了然了这里就不重复了
iii.自动递增、递减运算:
Java和C++一样存在前递增和前递减(++A和--A),这种情况先进行运算,再生成值;后递增和后递减(A++和A--),会先生成值,再执行运算;因为++和--操作一样,所以提供一段说明代码:
package org.susan.java.basic;
/**
*自动递增递减的代码示例
**/
public class AutoIncreasement {
public static void main(String args[]){
int i = 3;
int j = 3;
System.out.println("++i:" + (++i));
System.out.println("j++:" + (j++));
System.out.println("i:" + i);
System.out.println("j:" + j);
System.out.println(i==(i++));
System.out.println(i==(++i));
float floatValue = 0.3F;
System.out.println(floatValue++);
System.out.println(floatValue);
}
}
上边代码的输出为:
++i:4
j++:3
i:4
j:4
true
false
0.3
1.3
需要说明的是:A++和A = A + 1是等效的,++A的最终执行效果和 A = A + 1也是等效的,二者只有一个最简单的区别,上边代码可以看出来
[1]前递增(递减)和后递增(递减)就是上边表达的那种情况,但是有一点特殊就是前递增(递减)是先改变变量的值,而后递增(递减)是在进行运算过后修改变量的值
[2]而且在Java里面,自动递增递减是支持浮点类型的,最后那一些代码可以说明这一点
注意:可以这样来理解,i,i++,++i代表三个不同的变量,如果i的值是a,那么i++本身的值是在递增之前返回的,i++的判断是和i一样的,而++i本身的值是在递增之后返回的,++i和i的判断却是不一样的,所以这里应该理解的是变量是什么时候进行更改的。主要参考上边的代码:
System.out.println(i==(i++));
System.out.println(i==(++i));
根据这段代码的输出就可以知道(更改变量的时间)在自增自减的运算中的重要性了,提供一个更加迷惑的例子:
package org.susan.java.basic;
/**
*一个比较迷惑的自增自减的例子
**/
public class AutoIncreaseMain {
public static void main(String args[]){
int i = 4;
int j = 6;
int result = i+++ ++j + i+++ ++i + i + ++i;
System.out.println("result:" + result);
int a = 5;
int result1 = a+++a+++a+++a++;
System.out.println("result1:" + result1);
}
}
先看输出:
result:38
result1:26
仔细分析一下上边的输出:
在分析过程模拟一个堆栈,把每次的结果都压入堆栈进行运算:
第一个表达式result的结果:
[1]最先压入栈的是i++,这个地方i的值为4,那么i++返回值就是4,但是运算过后i的值变成了5,结果为:4 +
[2]然后压入栈的是++j,这个地方j的值为6,那么++j返回值就是7,但是运算过后同样使得j变成了7,结果为:4 + 7 +
[3]然后压入栈的同样是i++,但是这个时候i的值为5,那么i++也是5,运算过后i的值变成了6,结果为:4 + 7 + 5 +
[4]按照这种方式运算下去,最终的结果是:4 + 7 + 5 + 7 + 7 + 8,所以result的值为38
按照同样的方式可以运算result1的结果:result1的最终结果为26;至于这种操作的表达式写法有一定的规则,下边的是合法和不合法的例子:
合法的写法:
i+++ ++i;
i+++i++;
i+ ++i;
i+++i;
i++ +(++i);
不合法的写法:
i+++++i;
i++ +++i;
至于其他的写法对与不对可以直接在Eclipse里面进行编译来判断表达式的正确与否
iv.关系运算符:
关系运算符包括<、>、<=、>=、==、!=:
关于关系运算符需要说明的是:
- ==和!=是适合所有的基本类型(Primitives)的,但是其他关系运算符不能比较boolean
- 如果是针对两个引用指向的对象内容进行比较,必须用特殊的操作方法equals()
- ==和!=在比较两个引用的时候,比较的是引用的指向,而不是对象的内容信息
——[$]仅提供一个例子说明——
package org.susan.java.basic;
public class CompareMain {
public static void main(String args[]){
int i = 4;
System.out.println((i==4));
System.out.println((i!=4));
System.out.println((i >= 3));
System.out.println((i < 4));
System.out.println((i > 6));
System.out.println((i <= 4));
}
}
上边的输出为:
true
false
true
false
false
true
v.逻辑运算符和按位运算符:
逻辑运算符有&&、||、!能够结合表达式生成一个boolean返回值
按位运算符AND(&)、OR(|)、XOR(^)、NOT(~)运算
下边是所有的逻辑关系表:
非关系表:
A | !A |
true | false |
false | true |
逻辑与关系:
A | B | A && B |
false | false | false |
true | false | false |
false | true | false |
true | true | true |
逻辑或关系:
A | B | A || B |
false | false | false |
true | false | true |
false | true | true |
true | true | true |
下边是按位运算关系表:
非位运算符:
A | ~A |
1 | 0 |
0 | 1 |
与位运算符:
A | B | A & B |
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
或位运算符:
A | B | A | B |
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
异或运算符:
A | B | A ^ B |
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
关于逻辑运算符一定要理解的是“短路”现象,比如下边的表达式:
if( 1==1 && 1==2 && 1==3)
代码从左到右开始执行,指定第一个表达式1==1是为true,因为是为true,然后就开始运算1==2,运算了第二个表达式过后结果如下:
true && false && 1==3
到了这个时候就不再运算下去了,因为在这种运算里面第二个表达式为false,这种情况就短路了,因为不论最后一个表达式为什么,最终返回就已经是false,短路现象在&&和||中都是常见的,只有逻辑关系运算符存在短路现象,位逻辑运算符中是不存在短路现象的
——[$]提供一个运算的例子——
package org.susan.java.basic;
public class ShortTruFalseTester {
public static void main(String args[]){
boolean a = true;
boolean b = false;
System.out.println(!a);
System.out.println(a && b);
System.out.println(a || b);
int aNumber = 15;
int bNumber = 5;
System.out.println(aNumber & bNumber);
System.out.println(aNumber | bNumber);
System.out.println(aNumber ^ bNumber);
System.out.println(~aNumber);
}
}
输出为:
false
false
true
5
15
10
-16
逻辑运算符这里不做任何解释,但是另外三个计算机底层的位运算符是需要简单解释一下:
aNumber=15中,aNumber转换成为二进制位为:1111
bNumber=5中,bNumber转换成为二进制位为:0101
1111&0101的结果每一位进行&运算可以得到1111&0101=0101,运算规则就为每一位1&1=1,1&0和0&1为0,0&0=0,最后结果转换为0101=5
1111|0101的结果是每一位进行|运算1111|0101=1111,运算规则为1|1=1,1|0=1和0|1=1,0|0=0,最后结果为1111=15
1111^0101的结果是每一位进行^运算1111^0101=1010,运算规则为1^1和0^0为0,1^0和0^1为1,最后结果为1010=10
~aNumber在这个地方相对复杂:
其实在32bit系统中,aNumber的真实数值为:
0000 0000 0000 0000 0000 0000 0000 1111
按位运算结果为:
1111 1111 1111 1111 1111 1111 1111 0000
转化成为数值就为:-16
vi.三元操作符:
三目运算符是一个特殊的运算符,它的语法形式如下:
布尔表达式?表达式1:表达式2
运算过程,如果布尔表达式的值为true就返回表达式1的值,如果为false返回表达式2的值,简单用一个例子说明:
package org.susan.java.basic;
/**
*三元操作符
**/
public class ThirdMain {
public static void main(String args[]){
int sum = 90;
String resultValue = (sum > 90)?"Bigger than 90":"Smaller than 90";
String resultValue1 = (sum < 100)?"Smaller than 100":"Bigger than 100";
System.out.println(resultValue);
System.out.println(resultValue1);
}
}
输出结果为:
Smaller than 90
Smaller than 100
vii.移位运算符:
移位运算符操作的对象是二进制位,可以单独用移位运算符来处理int类型的整数,下边是概念表:
运算符 | 含义 | 例子 |
<< | 左移运算符,将运算符左边的对象向左异动运算符右边指定的位(在低位补0) | x << 3 |
>> | "有符号"右移运算 符,将运算符左边的对象向右移动运算符右边指定的位数。使用符号扩展机制,也就是说,如果值为正,则在高位补0,如果值为负,则在高位补1。 | x >> 3 |
>>> | "无符号"右移运算 符,将运算符左边的对象向右移动运算符右边指定的位数。采用0扩展机制,也就是说,无论值的正负,都在高位补0。 | x >>> 3 |
1)计算机里面的原码、反码、补码
在计算机里面所有的数据都是按照字节来存储的,1个字节等于8位(1Byte=8bit),而计算机只能识别0和1,所以根据排列,1个字节可以存储256种不同的信息,就是28(0和1两种可能,8位排列),比如定义一个字节大小的无符号整数,那么表示的就是0~255(0~28-1),这些数一共是256个数。这里只是标识了一个无符号的整数,那么需要进一步剖析这个数,0是这些数字中最小的一个:00000000,在计算机里面,无符号整数就是这样的方式来进行整数的存储,也就是说如果知道了一个无符号整数的二进制码就可以直接计算该整数的值了。而计算机里面有两外一种存储整数的方式,这种方式是针对有符号的整数而言的。
只有有符号的整数才有原码、反码和补码,其他的类型一概都没有,而且不需要。虽然我们也可以用二进制中最小的数去对应最小的负数,最大的也对应最大的,但是那样不科学,计算机本身却不使用这样的方式,二是有另外的存储方式。如果1个字节不管怎样还是只能标识256位,那么带符号的整数就应该是:-128~127,这种情况在计算机里面怎么存储呢?其实该字节的最高位是符号位,比如:
0 1111111
在上边的8位里面,最高位是0,代表的是符号位,在计算机里面0代表+,1代表-,这样的存储就可以知道实际只有255个数。那么计算机的存储原理是什么呢?
可以这样理解,用最高位表示符号位,如果是0表示正数,如果是1表示负数,剩下的7位用来储存数的绝对值的话,能表示27个数的绝对值,再考虑正负两种情况,27*2还是256个数。首先定义0在计算机中储存为00000000,对于正数我们依然可以像无符号数那样换算,从00000001到01111111依次表示1到127。那么这些数对应的二进制码就是这些数的原码。到这里很多人就会想,那负数是不是从10000001到11111111依次表示-1到-127,那你发现没有,如果这样的话那么一共就只有255个数了,因为10000000的情况没有考虑在内。实际上,10000000在计算机中表示最小的负整数,就是这里的-128,而且实际上并不是从10000001到11111111依次表示-1到-127,而是刚好相反的,从10000001到11111111依次表示-127到-1。负整数在计算机中是以补码形式储存的,补码是怎么样表示的呢,这里还要引入另一个概念——反码,所谓反码就是把负数的原码除符号位(负数的原码除符号位和它的绝对值所对应的原码相同,简单的说就是绝对值相同的数原码相同)各个位按位取反,是1就换成0,是0就换成1,如-1的原码是0000001(注意这里只有7位,不看符号位,我这里所说的负数符号位都是1),和1的原码相同,那么-1的反码就是1111110(这也是7位,后面加上了符号位都是8位了),而补码就是在反码的基础上加1,即-1的补码是11111110+1=11111111,因此我们可以算出-1在计算机中是按11111111储存的。总结一下,计算机储存有符号的整数时,是用该整数的补码进行储存的,0的原码、补码都是0,正数的原码、补码可以特殊理解为相同,负数的补码是它的反码加1。
举个简单的例子:
有符号的整数 | 原码 | 反码 | 补码 |
47 | 00101111 | 00101111 | 00101111(正数补码和原码、反码相同,不能从字面的值进行理解) |
-47 | 10101111 | 11010000 | 11010001(负数补码是在反码上加1) |
简单讲:
整数的补码和原码相同
负数的补码则是符号位为“1”,数值部分按位取反过后在末位加1,也就是“反码+1”
【*:这里只是简单介绍一下计算机存储的码,如果读者需要更加详细的资料,可以去查阅,这里讲这些是为了方便初学者能够理解Java里面的移位运算符】
2)移位运算例子
这里提供简单的例子:
-5>>3=-1
1111 1111 1111 1111 1111 1111 1111 1011
1111 1111 1111 1111 1111 1111 1111 1111
其结果与 Math.floor((double)-5/(2*2*2)) 完全相同。
-5<<3=-40
1111 1111 1111 1111 1111 1111 1111 1011
1111 1111 1111 1111 1111 1111 1101 1000
其结果与 -5*2*2*2 完全相同。
5>>3=0
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0000 0000
其结果与 5/(2*2*2) 完全相同。
5<<3=40
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0010 1000
其结果与 5*2*2*2 完全相同。
-5>>>3=536870911
1111 1111 1111 1111 1111 1111 1111 1011
0001 1111 1111 1111 1111 1111 1111 1111
无论正数、负数,它们的右移、左移、无符号右移 32 位都是其本身,比如 -5<<32=-5、-5>>32=-5、-5>>>32=-5。
一个有趣的现象是,把 1 左移 31 位再右移 31 位,其结果为 -1。
0000 0000 0000 0000 0000 0000 0000 0001
1000 0000 0000 0000 0000 0000 0000 0000
1111 1111 1111 1111 1111 1111 1111 1111
小结:到这里基本的Java运算符就已经讲完了,唯一没有讲到的就是instanceof操作符以及关于String的一些操作符,instanceof已经在《类和对象》章节里面讲到了,而String的操作会在String的专程章节里面讲述。
5.控制流程
Java程序和普通程序运行流程一样,有三种运行顺序:顺序、循环和选择,接下来介绍以下Java里面的这三种程序运行流程【本章节的概念代码可能更加基础,而且这一小节不讲保留字goto】:
1)顺序运行:
顺序运行是最简单的Java程序,基本不需要任何关键字就可以操作,提供一段简单的代码读者就可以明白了:
package org.susan.java.basic;
public class CommonFlow {
public static void main(String args[]){
System.out.println("Step One");
System.out.println("Step Two");
System.out.println("Step Three");
}
}
上边的输出这里就不列出了,它会按照顺序打印Step One——>Step Two——>Step Three
2)选择运行:
选择运行需要使用到Java里面的条件语句,条件语句主要包括:
[1]if语句;
[2]if...else语句;
[3]switch语句:
if语句:
if语句的语法有两种:
if(boolean表达式) 语句1;
if(boolean表达式){ 语句1;语句2;}
它所意味着的含义为如果boolean表达式的返回为true就执行语句1或者执行语句1所在的语句块{}内的内容:
package org.susan.java.basic;
import java.util.Random;
public class IfSingleMain {
public static void main(String args[]){
Random random = new Random();
int result = random.nextInt(3);
if( result == 2){
System.out.print("This is if block,");
System.out.println("Test block flow.");
}
if( result == 1)
System.out.println("This is if single flow.");
System.out.println("This is Inner or Outer."); //这句的写法是故意的
System.out.println("Main flow.");
}
}
上边这段代码的输出是不固定的,主要是看result返回的值是多少,它会随机生成三个整数值:1、2、3
result值为2的时候直接是块语句,所以不需要讲解什么,但是需要知道if还支持直接的不带块的语句,所以不论任何值生成,都会有下边这两句输出:
This is Inner or Outer.
Main flow.
这里提醒读者的只有一个关键点:在使用if语句的时候,如果后边的语句块不带{},那么它能产生的效果只有紧跟着if的后边一句话(单条语句),这种情况下,其他的语句都会视为和if无关的语句
if-else语句
该语句的语法为:
if(布尔表达式1)
{语句执行块1}
else if(布尔表达式2)
{语句执行块2}
else
{语句执行块3}
针对该语句,简单修改一下上边的程序:
package org.susan.java.basic;
import java.util.Random;
public class IfSingleMain {
public static void main(String args[]){
Random random = new Random();
int result = random.nextInt(3);
if( result == 2){
System.out.print("This is if block,");
System.out.println("Test block flow.");
}else if( result == 1){
System.out.println("This is if single flow.");
System.out.println("This is Inner or Outer.");
}else {
System.out.print("This is other flow.");
System.out.println("This is single else flow.");
}
System.out.println("Main flow.");
}
}
这是一个简单的if-else语句,这里只用了else的块语句,没有使用else的单条语句,如果使用else的单条语句,和if是一样的规则:
如果result随机生成的值是0【*:这个地方result只可能有三个值就是0,1,2】,就会得到下边的结果:
This is other flow.This is single else flow.
Main flow.
如果去掉最后一个else后边的{}后会有什么效果呢,去掉了最后一个括号过后,下边的语句不论result为任何值的时候都会输出:
This is single else flow.
Main flow.
原因和if语句一样,如果紧随其后的不是{}的语句块,只能影响一行完整的语句,也就是当去掉上边代码最后一个else后边的{}过后,else只能影响语句:
System.out.print("This is other flow.");
switch语句:
该语句的语法为:
switch(输入因子){
case 匹配因子1:{执行语句块1;}break;
caes 匹配因子2:{执行语句块2;}break;
default:{执行语句块3;}break;
}
当编程过程需要多个分支语句的时候,就需要使用到switch语句,也就是进行多项选择的语句,这里同样提供一个概念说明例子:
package org.susan.java.basic;
import java.util.Random;
public class SwitchTester {
public static void main(String args[]){
Random random = new Random();
int result = random.nextInt(4);
switch (result) {
case 1:
System.out.println("Result is 1");
break;
case 2:
System.out.println("Result is 2");
case 3:
System.out.println("Result is 3");
break;
default:
System.out.println("Result is 4");
break;
}
}
}
当result的值为各种值的时候输出为:
Result is 1【*:result的值为1的输出】
Result is 2
Result is 3【*:result的值为2的输出】
Result is 3【*:result的值为3的输出】
Result is 4【*:result的值为0的输出】
针对switch语句有几点需要说明:
- switch后边的括号里面的输入因子有类型限制的,只能为:
[1]int类型的变量
[2]short类型的变量
[3]char类型的变量
[4]byte类型的变量
[5]enum的枚举类型的变量【JDK1.5过后支持】
其他的变量都是不能传入switch的输入因子的 - 关于case语句的一点点限制:
[1]case语句后边必须是一个整型的常量或者常量表达式
[2]如果使用常量表达式,表达式中的每个变量,必须是final的
[3]case后边不能是非final的变量,比如使用一个case val这种情况将会直接通不过编译,因为val不是final类型的变量,只是一个普通变量
[4]case后边的常量和常量表达式不能使用相同的值 - 关于语句的执行顺序:
当接收到合法的输入因子过后,JVM会去寻找和系统输入因子匹配的case语句,如果没有任何一个case语句匹配的话就直接执行default语句;当匹配到case语句过后,会执行该case到紧接着代码里面写的该case的下一个case之间的语句块,一旦执行完该语句块过后,如果没有遇到return或者break就继续执行下一个case,执行的流程和该case的执行流程是一样的,知道碰到最终的case为止。所以就可以理解为什么上边的代码当result的值为2的时候会输出:
Result is 2
Result is 3
【*:这里思考一个问题,如果把default语句写在case语句的前边会发生什么情况呢?稍稍改动一下上边的代码,就会发现其实default的位置不重要,关键是最终满足的case是否能够匹配,可以这样认为:当所有case条件都不满足的时候选择执行default里面的语句块,当然前提条件是所有的case语句都跟随了一个break语句。一般编程过程中都是直接使case和break配对出现的,因为一旦case执行完了过后,遇到break语句就直接跳出switch语句块了,不会再执行其他的case,如果没有break,还会继续往下一个case执行。而且需要注意一点,执行完匹配的case而没有遇到break或者return的时候,再往下执行就不需要再匹配case条件了,这个时候不论接下来指定的case是否匹配都会执行下去,也就是说,所有的输入因子在进入switch语句过后只匹配一次。还有一点需要提醒一下,当default放在前面的时候,如果default没有匹配的break语句,同样执行完default对应的语句块过后需要执行紧跟着default下边的case语句块,直到遇到break语句。】
3)循环运行
[1]while和do-while循环
[2]for循环
[3]中断循环
while和do-while:
while循环和do-while循环称为条件循环,也就是循环的终止条件为当判断语句为false的时候就终止循环,while和do-while的区别在于不论条件判断返回false还是true,do-while语句至少执行一次,而while语句必须是当条件返回true的时候才会一次次执行
语法规则为:
while(布尔表达式){执行语句块}
do{执行语句块}while(布尔表达式);
下边是一个while和do-while循环的例子:
package org.susan.java.basic;
public class WhileLoopMain {
public static void main(String args[]){
int i = 4;
System.out.println("While loop:");
while(i < 4){
System.out.println(i);
i++;
}
i = 4;
System.out.println("Do While loop:");
do{
System.out.println(i);
i++;
}while(i < 4);
}
}
上边是一个很极端的例子,先看输出,然后再分析结果:
While loop:
Do While loop:
4
这里可以知道的是在while语句里面,先判断i<4,因为i的初始值是4,所以该条件不成立,所以while语句里面直接跳过语句执行块,不打印任何内容;但是针对do-while语句而言,虽然i的初始值也是4,但是i<4是在进行了一次运行过后才比较的,实际上细心的读者会发现,两个循环返回false的条件不一样。第一个while语句是因为4<4返回false的,而do-while语句却是因为5<4返回false的,这里可以看出while和do-while的细微差异
for循环:
语法规则为:
for(初始条件;判断条件;变化条件){执行语句块;}
for(每一项:包含项的列表)【*:等效于C#里面的foreach语句,而且是JDK1.5里面才支持的功能】
同样用一段代码来说明用法:
package org.susan.java.basic;
public class ForLoopMain {
public static void main(String args[]){
String[] arrayStrings = {"A","B","C"};
//进行arrayStrings的遍历
for( int i= 0; i < arrayStrings.length; i++){
System.out.println(arrayStrings[i]);
}
System.out.println("---------------");
for(String item:arrayStrings){
System.out.println(item);
}
}
}
上边代码的输出为:
A
B
C
---------------
A
B
C
这两种都可以进行循环遍历操作,简单总结以下前两种循环:
- 一般情况下,while和do-while循环主要用于无限循环,就是不知道循环次数,但是当终止条件满足的时候就退出循环
- for循环一般是用于有限循环,就是已经知道了循环次数,当到达某种条件的时候退出
这两种循环没有本质的区别,针对哪种循环的选择主要是在于用户在写程序的时候如何设计的问题
中断循环:
循环中断一般有三种方式:break、continue、标签中断
break的特征为:当循环遇到了break语句的时候,直接跳出本循环;
continue的特征为:当循环遇到了continue语句的时候,直接跳出本轮循环,进入下一次循环;
标签中断:可以在循环里面使用标签让break或者continue的时候直接从标签位置继续,这种情况有时候用于嵌套循环的一些内容;
——[$]break和continue——
package org.susan.java.basic;
public class BreakContinueMain {
public static void main(String args[]){
System.out.println("Break Loop");
for( int i = 0; i < 4; i++){
if( i == 2)
break;
System.out.println("Loop " + i);
}
System.out.println("Continue Loop");
for( int i = 0; i < 4; i++){
if( i == 2)
continue;
System.out.println("Loop " + i);
}
}
}
上边这段代码的输出为:
Break Loop
Loop 0
Loop 1
Continue Loop
Loop 0
Loop 1
Loop 3
【*:读者仔细思考一下,如果没有break和continue语句,应该依次打印i的值为0,1,2,3的每一句话,第一个循环是Break循环,当i=2的时候直接跳出了该循环,按照break的语法是直接跳出循环,所以只打印了i=0和1的时候的情况。而第二个循环是continue循环,当i=2的时候使用了continue,一旦使用了continue过后,该次循环就不执行了,直接进入下一轮循环,所以在continue循环的语句里面只有i=2的语句没有打印出来。】
——[$]使用标签——
package org.susan.java.basic;
public class LabelLoopMain {
public static void main(String args[]){
outer:for(int i=1; i < 4; i++){
inner:for(int j =0; j < 5; j++){
if( j == 2 )
continue inner;
System.out.println("i + j = " + (i+j));
if( j == 4)
break outer;
}
}
other:for(int i = 0; i < 4; i++){
if( i == 3){
break other;
}
System.out.println("i = " + i);
}
}
}
上边这段代码定义了三个循环标签分别为outer,inner,other,输出为:
i + j = 1
i + j = 2
i + j = 4
i + j = 5
i = 0
i = 1
i = 2
这里仅仅讲解一下简单的标签的语意,这段程序的详细逻辑留给读者自行去分析
- break labelname:跳出该标签指代的循环,标签指代的循环就是标签的:后边的内容
- continue labelname:继续标签指代的循环,标签指代的循环就是标签的:后边的内容
【*:这里需要说明的是,没有特殊情况可以不用考虑标签循环,标签中断循环主要是设计来为嵌套循环的相互之间的跳转用的,比如有三层循环,在某种条件满足的情况下仅仅想从内层循环跳到中层循环,这种情况下就可以使用标签,直接使用break labelmiddle的方式,labelmiddle就为中间那一层的循环前边定义的标签的名称,其他正常的编程情况能不使用标签的情况可以避免使用标签。】
6.关键字清单
对Java初学者而言,记忆对应的关键字也是一个不错的学习方式,这里提供一个与Java关键字相关的查询文档:
i.关键字、字面量、保留字
在Java里,按照使用的方式把所有可以被IDE着色的Java字分为三种类型:
- 关键字:关键字就是目前正在使用的Java语法
- 字面量:在Sun公司的官方规范里面有明确的说明,false、true和null这三个虽然IDE提供了语法着色,但是这三个不能称为关键字,而是使用的时候提供的字面量
- 保留字:保留字在Java里面称为以后可能会用到的语法,就是定义标识符的时候不能使用,主要有const和goto
ii.关键字清单以及说明
- [3]访问控制:private、protected、public
- [13]类、方法和变量修饰符:abstract、class、extends、final、implements、interface、native、new、static、strictfp、synchronized、transient、volatile
- [12]程序控制语句:break、continue、return、do、while、if、else、for、instanceof、switch、case、default
- [5]错误处理:catch、finally、throw、throws、try
- [2]包相关:import、package
- [8]基本类型:boolean、byte、char、double、float、int、long、short
- [3]变量引用:super、this、void
- [1]JDK1.5新关键字:enum
- [2]其他:new、assert
【*:需要说明的是const和goto从概念上来将属于Java里面的保留字,不应该列入关键字之列;true、false和null三个属于字面量,虽然属于Java里面的关键字,但是在规范里面不列入关键字之列。这里解释一下,很多资料都说Java里面有51个关键字,数数上边就会发现只有49个,51个关键字的来历是什么呢?51个关键在是在上边关键字的基础上计算了false、true和null,而且是JDK1.4的说法,还去掉了新关键字enum,所以很多资料都记载了51个关键字。】
7.小节
到这里,整个Java语言最基础的语法、流程、关键字、标识符等各种前边的基础知识就讲完了,没有设计到的内容就是JDK的工具以及JDK环境的配置,基本上用这一个章节直接替代掉原来写的Java基础类型的章节应该方便初学者进行学习,至于目前还没有涉及的IO、格式化、序列化、多线程以及JavaSE的基础部分的内容我会在后边的章节慢慢写上来,只是完成这样一篇BLOG可能花费的时间很长,所以产量比较慢希望读者能够见谅。而且每一次写完了我自己要阅读几遍才能帮着读者标注整个这篇文章的重点以及代码着色。