Java學習之旅基礎知識篇:數據類型及流程控制


    經過開篇對Java運行機制及相關環境搭建,本篇主要討論Java程序開發的基礎知識點,我簡單的梳理一下。在講解數據類型之前,我順便提及一下Java注釋:單行注釋、多行注釋以及文檔注釋,這里重點強調文檔注釋。通常單行注釋獨占一行並用"//"來開頭,多行注釋會占據多行並用/*...*/來表示。

/*
    HelloWorld是每一個Java學習者的起點,
    需要好好掌握
*/
public class HelloWorld {
    public static void main(String[] args) {
        // 輸出Hello World!
        System.out.println("Hello World!");
    }
}

那什么叫文檔注釋呢?簡單的說,就是利用javadoc將源代碼的文檔注釋生成一份API文檔,供使用者查詢和參考。JDK API的在線文檔查詢:http://dlc.sun.com.edgesuite.net/jdk/jdk-api-localizations/jdk-api-zh-cn/builds/latest/html/zh_CN/api/,從文檔界面不難看出分成三大區:包列表區(左上方)、類列表區(左下方)、詳細說明區(右側)。點擊左側"類列表區"中的類,右側將顯示該類對應的構造函數、屬性、方法等詳細信息。接下來,我們利用javadoc來生成一份API文檔,需要說明的是,javadoc只處理以public或protected修飾的類、接口、方法、屬性、構造器和內部類之前的文檔注釋而忽略其他地方的文檔注釋,如果開發者希望給使用者提供private成員的文檔,則需要加入-private選項。文檔注釋用/**...*/來表示。javadoc的語法如下:

javadoc -d [API文檔存放目錄] -windowtitle [API文檔瀏覽器標題] -doctitle [概述頁面標題] -header [頁面頁眉] ... Java源文件/包 (可利用javadoc -help查詢全部選項)

如源代碼中包含中文,則需要設置-locale, -encoding, -charset如下:

javadoc -d [API文檔存放目錄] -windowtitle [API文檔瀏覽器標題] -doctitle [概述頁面標題] -header [頁面頁眉] -encoding utf-8 -charset utf-8 -locale zh_CN ... Java源文件/包 (可利用javadoc -help查詢全部選項)

同時,為了更詳細說明類、方法等成員的文檔信息,經常添加以下javadoc標記(如@author、@version、@param、@return等)到這些成員上加以說明。

package miracle;
/**
* Description:
* <br/>此程序主要測試Javadoc指令來生成文檔注釋
* <br/>程序名:TestJavadoc.java
* <br/>編寫日期:2012-08-22
* @author Miracle, He miracle@sina.com
* @version 1.0
*/
public class TestJavadoc {
    /**
    * 測試屬性
    */
    protected String name;
    /**
    * 主方法,程序入口
* @param args 輸入參數列表
*/ public static void main(String[] args) { System.out.println("Hello, Miracle!"); } }
package miracleHe;
/**
* Description:
* <br/>此程序主要測試Javadoc指令來生成文檔注釋
* <br/>程序名:TestJavadocTag.java
* <br/>編寫日期:2012-08-22
* @author Miracle, He miracle@sina.com
* @version 1.0
*/
public class TestJavadocTag {
    /**
    * 此方法用來打招呼
    * @param name 打招呼的名稱
    * @return 返回打招呼的字符串
    */
    private String hello(String name) {
        return name + ",你好!";
    }
}

我們這里添加了兩個包miracle和miracleHe(目的為生成概述),執行以下命令將輸出API文檔。

javadoc -d apidoc -windowtitle "Generate Javadoc" -doctitle "Learning HelloWorld Class by Javadoc" -header "Javadoc Test" -charset "utf-8" -encoding "utf-8" Test*.java

默認情況下不會提取@author、@version等信息,如需提取則需要添加。

javadoc -d apidoc -windowtitle "Generate Javadoc" -doctitle "Learning HelloWorld Class by Javadoc" -header "Javadoc Test"-charset "utf-8" -encoding "utf-8" -author -version Test*.java

Java是強類型語言,也就是變量或表達式在編譯時就已經明確其類型,即先聲明后使用。數據類型分為: 基本類型(Primitive Type)和引用類型(Reference Type)。其組織關系如下:基本類型包含整數類型(byte、short、int、long)、字符類型(char)、浮點類型(float、double)和布爾類型(true、false);引用類型包含字符串(String)、數組、類、接口和空類型(null)。先從整數類型談起,如果一個較小的整數(在byte或short的范圍之內)賦值給byte或short時,系統會自動轉化為對應的類型;如果一個巨大的整數(超出int的范圍),系統則不會自動當做long來進行處理,需要添加后綴L來進行標識,即使這個整數在int的范圍之內聲明的long類型變量不添加L仍然當做是int

public class TestInteger {
    public static void main(String[] args) {
        byte b = 56; //系統會自動轉化為byte
        //long big = 999999999999; //出錯,系統不會當作long來處理
        long big2 = 41433333313243133L; //使用L強制為long類型
    }
}

整數除了十進制來表示外,還可以使用八進制(0開頭)以及十六進制(0x開頭,A~F代表10~15)來進行表示。

int octalValue = 013;
int hexValue = 0x2F;

雖然字符型被單獨處理,但其實它就是一種整數(0~65535之間的無符號整數),字符通常可以使用''(如'A')、轉義字符('\n'、'\r')和Unicode('\uXXXX',前256個字符與ASCII碼一致)來表示。如果將0~65535之間的整數賦給char變量將直接將int轉化為char類型。

public class TestChar {
    public static void main(String[] args) {
        //定義字符型
        char a = 'a';
        char enter = '\r';
        char ch = '\u24af';
        System.out.println(ch);//輸出?
        char yu = '宇';
        int yuValue = yu;
        System.out.println(yuValue);//23431
        char c = 97;
        System.out.println(c);//a
    }
}

但請注意,字符串雖然是由字符組成(可看作字符數組),但字符串是引用類型。接下來,我們討論一下浮點數。浮點數分為單精度浮點數(float)和雙精度浮點數(double)。其中float占4個字節,第一位是符號位,接下來8位是指數位,最后23位是尾數,必須要添加后綴F;double占8個字節,第一位是符號位,接下來11位是指數位,最后52位是尾數,是默認類型,可以不添加后綴D來標識。浮點數可用十進制(如5.12,.512)和科學計數法(5.12E2)來表示。特別需要注意的是:浮點數還包含三個特殊的值,正無窮大(POSITIVE_INFINITY,通過正數除以0得到)、負無窮大(NEGATIVE_INFINITY,通過負數除以0得到)和非數(NaN,通過0.0除以0.0或對負數開方得到)。所有正無窮大值都相等,所有負無窮大值相等,非數不與任何數相等(包含NaN本身也不相等)

public class TestFloat {
    public static void main(String[] args) {
        float f = 5.12F;
        double zero = 0.0;
        float p = Float.POSITIVE_INFINITY;
        double n = Double.NEGATIVE_INFINITY;
        System.out.println(p==n);//false
        System.out.println(f/zero);//Infinity
        System.out.println(f/zero==p);//true
        System.out.println(0.0/zero==Double.NaN);//false
        System.out.println(6.0/0==8.2/0);//true
        System.out.println(1/0);//拋出異常
    }
}

另外提一句,bool類型只能為true或false,不能用0或非0來表示,其他基本類型都不能轉化為bool類型,如果bool類型與字符串相連,將直接轉化為字符串

      接下來,我們討論一下數據類型轉換相關知識。數據類型轉換分為自動轉換和強制轉換,自動轉換的關系如下圖:

箭頭左邊的類型可以自動轉化為右邊的類型。此外,當基本類型與字符串進行連接時,基本類型會自動轉換為字符串。反之,將字符串轉化為基本類型則調用xxx.pareseXxx方法(如Integer.parseInt("12"))。

public class AutoConversion {
    public static void main(String[] args) {
        int a = 6;
        float f = a;
        System.out.println(f);//6.0
        byte b = 9;
        //char c = b;//出錯,byte不能自動轉化為char
        double d = b;
        System.out.println(d);//9.0
        //基本類型與字符串進行轉化
        String s = 5.3F + "";
        System.out.println(s);//5.3
        System.out.println(3 + 4 + "Hello!");//7Hello!
        System.out.println("Hello!" + 3 + 4);//Hello!34
    }
}

反之,如果想把箭頭右邊的類型轉換為左邊的類型,就需要強制類型轉換,這樣做可能導致數據溢出或精度丟失。因此在進行強制類型轉換時需格外小心。

public class ForceConversion {
    public static void main(String[] args) {
        int i = 234;
        byte b = (byte)i;
        System.out.println(b);//-22
        double d = 3.56;
        int n = (int)d;
        System.out.println(n);//3
    }
}

可能大家會問234咋轉換為byte就變成了-22了呢?我們都知道,byte的范圍是-128~127,顯示超出了表示的范圍,234的二進制表示為00..0011101010,轉換后截取后8位之后變成11101010,而第一位是符號位(這里是個負數),而負數在計算機中以補碼形式存在,需要轉換為原碼(補碼減1成反碼再按位取反,符號位不變,因此:11101010-->11101001-->10010110)。

    在進行表達式計算時,數據類型會發生自動提升:如所有byte、short和char將自動提升為int類型,表達式的數據類型將提升為最高等級操作數的數據類型。對於整數相除時,即使不能除盡也要舍棄小數部分,對於字符串與數字或字符相加時,此時應該從左自右進行運算,以判斷是否為字符串連接還是加法運算。

public class AutoPremotion {
    public static void main(String[] args) {
        short s = 5;
        //s = s - 2;//出錯,表達式被提升為int
        byte b = 10;
        char c = 'a';
        double d = .12;
        System.out.println(b + c + d);//表達式被提升為double
        System.out.println(23 / 3);//7
        System.out.println(b + c + "Hello!");//107Hello!
        System.out.println("Hello!" + b + c);//Hello!10a
    }
}

在講解流程控制之前,我在這里補充一下平常容易出錯的知識點。我們來看以下程序:

public class CompareString {
    public static void main(String[] args) {
        String a = new String("Miracle");
        String b = new String("Miracle");
        System.out.println(a == b);//false,因此a與b指向不同的實例(盡管內容一致)
        String c = "Miracle";
        String d = "Miracle";
        System.out.println(c == d);//true,此處由於字符串緩存機制,比較的僅僅是兩者的內容
    }
}

另外,對於短路運算符(如||)與不短路運算符(如|)的區別: ||先計算左邊的操作數,如果為true將不再繼續計算,而|不管左邊結果如何都會計算之后的操作數

public class TestLogicOperator {
    public static void main(String[] args) {
        int a = 5;
        int b = 10;
        if(a > 4 || b++ > 10)
        {
            System.out.println("a=" + a + ",b=" + b);//a=5,b=10
        }
        if(a > 4 | b++ > 10)
        {
            System.out.println("a=" + a + ",b=" + b);//a=5,b=11
        }
    }
}

接下來,進入流程控制的講解,任何編程語言(Java也不例外)的流程控制結構包含:順序結構和分支結構(if、if...else、if...else if...else、switch)和循環結構(while、do...while、for、foreach)。這里不再舉例說明,只是強調一點,對於分支結構if...else,盡量不要省略之后的花括號,即使只有一條語句也不能省略,其中對於非常簡單的if...else結構,可以用三目運算符(if(a>b)?a:b)來替代。為了避免發生邏輯錯誤,應該遵守:總是優先把包含范圍小的的條件放在前面處理

public class TestIf {
    public static void main(String[] args) {
        int age = 45;
        if(age > 20) {
            System.out.println("Young");
        } else if(age > 40) {
            System.out.println("Middle");
        } else if(age > 60) {
            System.out.println("Old");
        }
    }
}

我們發現運行之后輸出Young,明顯與預期不符(應該輸出Middle)。就是因為剛才提到的范圍問題導致(age > 20比age > 60范圍大)。我們改寫一下:

public class TestIf {
    public static void main(String[] args) {
        int age = 45;
        if(age > 60) {
            System.out.println("Old");
        } else if(age > 40) {
            System.out.println("Middle");
        } else if(age > 20) {
            System.out.println("Young");
        }
    }
}

剛才提到if之后的花括號不能省略,對於switch...case來說,case之后的花括號盡量省略,而break不要省略(否則將貫穿多個case執行),盡量加上default,此外對於switch(expression)的表達式只能為整數,不能為字符串(這點跟其他語言不一樣)。
      對於循環結構,也不要省略循環體中的花括號(即使只有一句),還可以組成多層嵌套循環。可以使用break結束本層循環,進入循環之后的代碼,使用continue結束本次循環,進入下一次循環,也可以使用return直接返回。但是有時還有特殊情況,就是從內層循環跳出到外層循環,需要使用Java標簽(用:表示),不過通常此標簽必須位於break所在循環的外層循環之前才起作用。以下程序將輸出: i = 0,j = 0;i = 0,j = 1。

public class TestBreak {
    public static void main(String[] args) {
        outer:
        for(int i = 0; i < 5; i++) {
            for(int j = 0; j < 2; j++) {
                System.out.println("i = " + i + ",j = " + j);
                if(j == 1) {
                    break outer;
                }
            }
        }
    }
}

此外,continue也可添加標簽,表示立即結束continue所在循環,跳到標簽所在位置進入下一次循環。如果將以上的break改成continue的話,將輸出: i = 0, j = 0; i = 0, j = 1; i = 1, j = 0; i = 1, j = 1; i = 2, j = 0; i = 2, j = 1; i = 3, j = 0; i = 3, j = 1; i = 4, j = 0; i = 4, j = 1。最后,return直接返回整個方法,而不管方法中嵌套有多少層循環。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM