JAVA筆記


基礎

在Java程序之中的包,主要的目的是可以將不同功能的文件進行分割。在之前的代碼開發之中,所有的程序都保存在了同一個目錄之中,這樣一來所帶來的問題:如果出現了同名文件,那麼會發生覆蓋問題,因爲在同一個目錄之中不允許有重名文件,而在不同的目錄下可以有重名文件,所謂的包實際上指的就是文件夾。

包聲明

包聲明語句把java類放到特定的包裏
eg:package com.abc.dollapp
- 在一個java源文件裏面,最多只能有一個package語句,但是package語句不是必要的.(如果沒有提供,表明放在默認包裏,默認包沒有名字。)
- package語句必須放在java源文件的第一行(忽略註釋行)。
* 在同一個java源文件定義的多個java類或接口都在同一個包裏

作用:

  • 區分相同名字的類
    如果類名包含所在的包的信息,這種類名稱爲完整類名,或者完整限定名。如com.abc.bookstore.book就是完整限定名.所以如果位於默認類,則完整限定名就是類名。
  • 有助於實施權限控制。當不同包之間的類相互訪問時,會受到訪問權限約束。
  • 有利於劃分和組織Java應用的各個類

一旦包定義完成,在進行編譯的時候爲javac -d . Hello.java
這個命令之間有空格,每一個操作的含義:
· -d:表示的是生成文件目錄,根據package的定義生成;
· .:表示在當前所在的目錄下生成。
這個時候類的名字必須帶上包的名稱,所以執行類的時候:java cn.mldn.demo.Hello,也就是說完整類的名稱就是“包.類”,而在所有的開發之中,沒有包的類是絕對不存在的,只要是程序一定要有包
`

命名規範

通常使用小寫包含以下信息:
* 類所屬項目的信息
* 類在具體軟件項目所處的位置

com.abc.netstore.common.SysContent類,SysContent類由ABC公司開發,屬於netstore項目,位於netstore項目的common包。

包導入引用語句

  • 如果一個包訪問了來自另外一個包(java.lang 包除外)的類,那麼需要用import語句把這個類引入
    eg:
    import com.abc.dollapp.doll.Doll; //AppMain類通過import語句引入Doll類
    import com.abc.dollapp.doll.*; //AppMain類通過import語句引入com.abc.dollapp.doll包中所有的類

    關於public class和class定義類的區別
    · public class:文件名稱和類名稱保持一致,在一個*.java文件之中只能存在一個public class定義,如果一個類要想被外部的包所訪問必須定義爲public;
    · class:文件名稱可以和類名稱不一致,在一個.java之中可以同時存在多個class定義,並且編譯完成之後會形成多個.class文件,使用class定義的類只能夠在一個包中訪問,不同包無法訪問。

    • 由於某種需要,同時導入兩個包,這個時候要使用Message類的時候必須加上類的全名。

    • import語句不會導致類的初始化

    • Java應用常見目錄結構

      • arc子目錄 存放java源文件
      • classs子目錄 存放編譯生成的java類文件
      • lib子目錄 存放第三方java軟件的jar文件
      • doc子目錄 存放各種幫助文件
      • doc\api子目錄 存放javadoc文檔
      • deploy子目錄 存放java應用的打包文件,jar文件

jar命令

Jar是一種Java給出的壓縮格式文件,即:可以將.class文件以.jar壓縮包的方式給用戶,這樣方便程序的維護。如果要使用jar的話,可以直接利用JDK給出的jar命令完成。
在以上的操作之中,往往只使用三個參數:
- -c:創建一個新的文件;
- -v:生成標準的壓縮信息;
- -f:由用戶自己指定一個*.jar的文件名稱。

編譯java源文件

命令如下:
javac [options] [sourcefiles]

javac命令選項:

-nowarn 不輸出警告信息
-verbose 輸出編譯器運行中的詳細工作信息
-deprecation 輸出源程序使用了不鼓勵使用的API具體位置
-classpath<路徑>覆蓋classpath環境變量,重新設定classpath。
-sourcepath<路徑> 指定java源程序的路徑
-d<路徑> 指定編譯生成的類文件的存放目錄。//Javac的命令不會自動創建指定的目錄,因此需要提前保證該目錄已存在。如果沒有設定此項,編譯生成的類文件在源文件目錄
-help 顯示各命令選項的用法

系統常用包

  • java.lang:包含了一些系統的常用類,例如:String、Object、Integer等,JDK 1.1之後此包默認導入;
  • java.lang.reflect:Java的反射機制操作包;
  • java.util:是一個工具的提供包,裏面提供了各種的工具操作類;
  • java.util.regex:正則開發包;
  • java.io:編寫IO操作;
  • java.sql:數據庫的程序開發包;
  • java.net:網絡程序開發包;
  • java.applet:小應用程序開發包;

面試題:關於Applet和Application的區別?
· Applet是不需要主方法運行的,需要嵌入在HTML代碼之中;
· Application是需要通過主方法執行。
· java.awt.、javax.swing:是進行圖形界面的開發包,其中java.awt是一組重量級的組件包,而javax.swing是屬於輕量級的組件包,並且JDK 1.2的最大特點是加入了javax.swing包;

編譯流程

  1. 以AppMain類爲例子,其中該類引入了com.abc.dollapp.doll.Doll類,所以編譯AppMain類時需要先獲得Doll.class
  2. java編譯器先到classpath目錄下尋找Doll.class文件,然後到-southpath下尋找Doll.java文件
  3. 如果找到了Doll.class和Doll.java文件 ,編譯器判斷Doll.class過期與否。過期了就重新編譯。
  4. 如果只找到了Doll.class文件,java編譯器就直接使用這個class。如果只找到java文件,就進行編譯。
  5. 如果都沒有 就拋出錯誤

運行java程序

java命令用於運行java程序,它會啓動java虛擬機。虛擬機加載相關的類並調用相關的主程序類的main方法
-classpath<路徑> 覆蓋classpath環境變量,重新設定classpath。

java命令的用法
-verbose 輸出運行中的詳細工作信息
-jar 指定運行某個jar文件中的特定java類
-help 顯示各命令選項的用法
-D<屬性名=屬性值> 設置系統屬性,例如java-Duser=”Tom” SampleClass

例如java-Duser="Tom" SampleClass其中user是屬性名 Tom是屬性值 SampleClass是類名,在SampleClass中調用System.getProperty(“user”)方法就會返回Tom值


public class Doll{...} //指明類名字爲DOll public意味着這個類可以被公開訪問

private String name; //表明Doll類有一個name屬性,字符串類型。private意味不能公開訪問


所有的java程序都有一個最爲核心的單元:類,而本次程序使用了一個hello類

  • public class 類名稱{}:文件名稱必須與類名稱保持一致,一個*.java文件裏面只能有一個public class
  • class 類名稱{}:文件名稱可以與類名稱不一致,但是生成的*.class是根據文件中的定義的class一致的,
  • 在一個.java文件中可以定義多個class,但是編譯後會分別形成不同的。class文件;
  • 總結:嚴格來講,每個*.java裏面只會出現一個public class定義,但是在講課會在一個文件出現多個類。
  • 主方法:所有程序都是由主方法開始

    public static void main (String args[])
    {
    要編寫的代碼;
    }
  • 主方法所在的類稱爲主類

屏幕輸出

  • 輸出之後換行:System.out.println()
  • 輸出之後不增加換行:System.out.print()

格式

 public class 類名稱{
    public ststic void main(String args[])
    {
    所編寫的所有代碼語句;
    }
 }

命名規範

命名規範的主要特點就是保證程序之中類名稱或方法等名稱的標記明顯一些,可是對於Java而言,有一些固定的命名規範還是需要遵守的:

- 類名稱:每一個單詞的開頭首字母大寫,例如:TestDemo;
- 變量名稱:第一個單詞的首字母小寫,之後每個單詞的首字母大寫,例如:studetName;
- 方法名稱:第一個單詞的首字母小寫,之後每個單詞的首字母大寫,例如:printInfo();
- 常量名稱:每個字母大寫,例如:FLAG;
- 包名稱:所有字母小寫,例如:cn.mldnjava.util。

訪問權限

No. 範圍 private default protected public
1 在同一包的同一類
2 同一包的不同類
3 不同包的子類
4 不同包的非子類

即:
- private只能在一個類中訪問;
- default:只能在一個包中訪問;
- protected:在不同包子類;
- public:所有都可以。

日後開發原則基本上爲:
- 屬性永遠都是private;
- 正常類的方法永遠都是public;
- protected實際上也屬於一種封裝,但是這種封裝知道特點就行了。
- 封裝:private、default、protected。

CLASSPATH

設置 CLASSPATH

SET CLASSPATH=d:\testjava

  • 結論:每當使用 java 命令解釋一個類的時候,會自動的通過CLASSPATH所設置的路徑加載所需要的類。但是如果在實際的工作之中這樣到處亂設置CLASSPATH並不方便,最好的做法是從當前所在的路徑下去加載所需要的類文件。那麼往往會將LASSPATH設置爲“ .”,表示從當前所在路徑加載。

設置從當前所在路徑加載類
SET CLASSPATH=.

CLASSPATH的主要目的是爲了定義類的加載路徑,不管定義了多少個路徑,一定要定義一個”.”(表示在當前所在目錄進行類的加載)。


PATH和CLASSPATH的區別

PATH:是屬於操作系統屬性,定義所有可執行程序的路徑
CLASSPATH:是java程序的解釋類文件時所使用的加載路徑

註釋

    ·// 單行註釋
    ·/*.....*/ 多行註釋
    ·/**....*/ 文檔註釋
    日後開發代碼儘量使用單行註釋,原因是多行註釋在有些開發工具格式化後效果不好

標識符

包括類名稱 屬性名稱 方法名稱等都稱爲標識符 所有的標識符都有自己嚴格的定義要求,基本要求如下:

  • 由任意順序的大小寫字母、數字、下劃線(_)和美元符號($)組成
  • 但標識符不能以數字開頭,不能是 Java 中的保留字(關鍵字)。
  • Java 中的保留關鍵字:
    abstract boolean break byte case catch
    char class continue default do double
    else extend false final finally float
    for if implement import instanceof int
    interface long native new null package
    private protected public return short static
    synchronized super this throw throws transient
    true try void volatile while


    ·java有兩個未用到的關鍵字:goto、const;
    ·java有三個特殊含義的標記:true、false、null;
    ·JDK 1.4之後增加了assert關鍵字
    ·JDK 1.5之後增加了enum關鍵字

對於程序開發,以上要求實際上就夠了 但是在JDK1.7後開始增加了中文特性,即Java支持中文定義標識符。


數據類型

  • 基本數據類型:不存在內存分配問題
  • 引用數據類型:需要開發者爲其分配空間,而後進行關係的匹配

任何一門語言都是由若干種不同的數據類型所組成,在java之中數據類型一共分爲兩類:

· 基本數據類型(數值操作): 默認值
|- 數值型:
|- 整型:byte、short、int、long; 0
|- 浮點型:float、double; 0.0
|- 字符型:char; ‘\u0000’
|- 布爾型:boolean; false
· 引用數據類型(內存操作):
|- 數組、類、接口; null

其中布爾型只有兩種取值範圍:true 或 false

  • byte的數據長度是8位,-128 ~ 127;
  • int數據的長度爲32位,-2147483648 ~ 2147483647;
  • double可以保存的數據範圍是最大的。

但是對於以上給出的基本數據類型的定義,如果從實際的開發角度上,以下的幾種類型最爲常用:

  • int型:只要是看見了整型的定義,其類型都是int;
  • double型:只要是看見了小數的定義,其類型都是double;
  • byte型:日後進行數據傳輸的時候使用此類型,講解到IO,和編碼轉換的操作上使用;
  • boolean:用於程序的邏輯操作使用;
  • long:表示日期時間、表示文件長度的時候。
  • char:使用中文可以避免亂碼問題

    • 如果按照保存範圍byte

數據類型的轉換問題

一般在進行數據類型轉換的時候都會採用由小到大的方式進行,例如:double 的範圍大於 float,所以可以自動轉型,double 的範圍大於 int 也可以自動轉型,但是如果由大到小的方式進行的話,則必須採用強制的手段.
如果常量進行強制轉換,有兩種:數字L、(long)數字。
所有的程序的操作之中的數據類型轉換:
1. 如果小範圍和大範圍操作,小範圍自動變爲大範圍,例如:int + long = long;
2. 如果現在把表示範圍大的數據變成表示範圍小的數據,則必須強制完成,例如:int型 = (int) long型;

整型

整型就表示一個基本的整數,可以直接使用int定義,而且在java之中默認的一個整數,其類型就是int。

在爲byte類型賦值的時候,如果其給出的數據範圍在byte範圍之中,則自動的將int變爲byte型數據,當然這種操作只是在直接賦值的情況下完成。

浮點型

浮點型指的是小數,在java之中,默認的一個小數實際上對應的類型就是double型數據。
在浮點型數據之中,實際上也分爲兩種:double、float,float的範圍要小於double的範圍,那麼既然默認的小數屬於double型數據,如果要爲float賦值呢?必須強制轉換,而轉換的方式:數字F、(float)數字;

字符操作

在字符的操作中還存在着轉義字符,例如:有如下的轉移字符:

  • ‘\n’:表示換行
  • ‘\t’:表示 TAB
  • ‘\’:表示一個“\”
  • ‘\”’:表示一個”
  • ‘\”:表示一個’

數組

內存機制

· 堆內存:保存對象的真正數據,都是每一個對象的屬性內容;
· 棧內存:保存的是一塊堆內存的空間地址,可以把它想象成一個int型變量(每一個int型變量只能存放一個數值),所以每一塊保留一塊堆內存地址,但是爲了方便理解,可以簡單的將棧內存之中保存的數據理解爲對象的名稱(Person per),就假設保存的是per。
· 如果要想開闢堆內存空間,只能依靠關鍵字new來進行開闢。即:只要看見了關鍵字new不管何種情況下,都表示要開闢新的堆內存空間。

數組的定義格式如下(動態操作格式):

格式一:聲明並開闢(實例化)數組
數據類型 數組名稱 [] = new 數據類型 [長度]
數據類型 [] 數組名稱 = new 數據類型 [長度]

格式二:分步完成
聲明數組: 數據類型 數組名稱 [] = null ;
開闢數組: 數組名稱 = new 數據類型 [長度] ;

數組的靜態初始化

無論是對象還是數組,聲明之後要進行實例化操作(new)

數組的動態初始化在開闢空間之後,裏面的所有內容都是其對應數據類型的默認值,那麼也可以使用靜態初始化的方式,爲數組在開闢空間時,設置好指定的內容,而數組的靜態初始化的語法格式如下:

格式一:簡寫格式
數據類型 數組名稱 [] = {值,值,…} ;
數據類型 [] 數組名稱= {值,值,…} ;
格式二:完整格式(推薦使用)
數據類型 數組名稱 [] = new 數據類型 [] {值,值,…} ;
數據類型 [] 數組名稱 = new 數據類型 [] {值,值,…} ;

由於數組是通過一個名稱統一的進行管理,所以數組在輸出的時候往往可以利用for循環完成,for循環需要知道明確的循環次數,那麼數組數據的輸出循環次數就是數組長度,而數組長度的取得“數組名稱.length”。


把數組給方法之中的參數

>
如果一個方法要想接收參數,則對應的參數類型必須是數組

public class TestDemo {
    public static void main(String args[]) {
        int data [] = new int [] {209,201,2,2,3,6,7} ;
        print(data) ;// 引用傳遞,int temp [] = data ;
    }
    public static void print(int temp []) {
        for (int x = 0 ; x < temp.length ; x ++) {
            System.out.print(temp[x] + "、") ;
        }
        System.out.println() ;
    }
}

>


判斷某一個數據是否在指定的數組之中

基本操作原理:使用數組的靜態初始化,聲明一個數組內容,而後隨便設置一個數據,判斷這個數據是否在指定的數組之中存在,如果存在則提示存在,不存在,則提示不存在。
定義一個查找方法,那麼這個查找方法應該返回boolean型數據。這個方法要接收兩個參數:要查找的數據、數組.

public class TestDemo {
    public static void main(String args[]) {
        int data [] = new int [] {209,201,2,2,3,6,7} ;
        int searchData = 3 ;    // 要查找的內容
        if (isExists(data,searchData)) {    // 爲true
            System.out.println("已經查找到了內容。") ;
        } else {
            System.out.println("沒有找到內容。") ;
        }
    }
    public static boolean isExists(int temp [],int search) {    // 存在
        for (int x = 0 ; x < temp.length ; x ++) {
            if (temp[x] == search) {
                return true ;   // 查找到了,後面的循環不做了
            }
        }
        return false ;  // 沒有查找到
    }
}

數組排序的操作

冒泡法

public class TestDemo {
    public static void main(String args[]) {
        int data [] = new int [] {1,3,2,6,10,0,5,8} ;
        sort(data) ;
        print(data) ;
    }
    public static void sort(int temp []) {
        for (int x = 0 ; x < temp.length ; x ++) {
            for (int y = 0 ; y < temp.length - 1 ; y ++) {
                if (temp[y] > temp[y + 1]) {    // 後面的小
                    int t = temp[y] ;   // 第三方接收
                    temp[y] = temp[y + 1] ;
                    temp[y + 1] = t ;
                }
            }
        }
    }
    public static void print(int temp []) {
        for (int x = 0 ; x < temp.length ; x ++) {
            System.out.print(temp[x] + "、") ;
        }

與數組有關的操作方法

1、 數組排序:java.util.Arrays.sort(數組名稱)

public class TestDemo {
    public static void main(String args[]) {
        int data [] = new int [] {1,3,2,6,10,0,5,8} ;
        java.util.Arrays.sort(data) ;   // 排序
        print(data) ;
    }
    public static void print(int temp []) {
        for (int x = 0 ; x < temp.length ; x ++) {
            System.out.print(temp[x] + "、") ;
        }
        System.out.println() ;
    }
}

2、 數組拷貝,從一個數組之中拷貝部分內容到另外一個數組之中
· 方法:System.arraycopy(源數組名稱,源數組開始點,目標數組名稱,目標數組開始點,拷貝長度) ;

“`java
/*
現在給定兩個數組:
· 數組A:1、2、3、4、5、6、7、8、9;
· 數組B:11、22、33、44、55、66、77、88、99。
將數組B的部分內容替換到數組A中,數組A的最終結果:1、2、66、77、88、6、7、8、9。
*/
public class TestDemo {
public static void main(String args[]) {
int dataA [] = new int [] {1,2,3,4,5,6,7,8,9} ;
int dataB [] = new int [] {11,22,33,44,55,66,77,88,99} ;
System.arraycopy(dataB,5,dataA,2,3) ;
print(dataA) ;
}
public static void print(int temp []) {
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + “、”) ;
}
System.out.println() ;
}
}

“`
簡單Java類:
一個類之中只包含基本的屬性、setter、getter方法,這種類稱爲簡單java類,對於簡單Java類的開發原則有如下幾點,必須嚴格遵守:

· 類名稱必須可以明確的表示出一類的定義,例如:Person、Emp、Dept;
· 類之中的所有屬性必須使用private進行封裝;
· 類之中的所有屬性都必須定義相應的setter、getter;
· 類之中可以提供構造方法,爲屬性初始化,但是不管提供了多少個構造方法,一定要保留有一個無參構造;
· 類之中不允許直接使用System.out.println()輸出,所有的內容要返回給被調用處輸出。
·類之中需要有提供一個取得對象完整信息的方法,暫定爲:getinfo(),而且返回string數據
class Emp { // 僱員類
    private int empno ;
    private String ename ;
    private String job ;
    private double sal ;
    private double comm ;
    public Emp(){}
    public Emp(int eno,String ena,String j,double s,double c){
        empno = eno ;
        ename = ena ;
        job = j ;
        sal = s ;
        comm = c ;
    }
    public void setEmpno(int eno) {
        empno = eno ;
    }
    public void setEname(String ena) {
        ename = ena ;
    }
    public void setJob(String j) {
        job = j ;
    }
    public void setSal(double s) {
        sal = s ;
    }
    public void setComm(double c) {
        comm = c ;
    }
    public int getEmpno() {
        return empno ;
    }
    public String getEname() {
        return ename ;
    }
    public String getJob() {
        return job ;
    }
    public double getSal() {
        return sal ;
    }
    public double getComm() {
        return comm ;
    }
    public double salary() {
        return sal + comm ;
    }
    public double income() {
        return this.salary() * 12 ;
    }
    public String getInfo() {
        return  "僱員信息:" + "\n" +
                "\t|- 編號:" + this.getEmpno() + "\n" +
                "\t|- 姓名:" + this.getEname() + "\n" +
                "\t|- 職位:" + this.getJob() + "\n" +
                "\t|- 工資:" + this.getSal() + "\n" +
                "\t|- 佣金:" + this.getComm() + "\n" +
                "\t|- 月薪:" + this.salary() + "\n" +
                "\t|- 年薪:" + this.income() ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Emp emp = new Emp(7369,"SMITH","CLERK",800.0,0.0) ;
        System.out.println(emp.getInfo()) ;
    }
}

STRING類

string類對象相等判斷使用equals()方法完成,”==”實現的是地址的比較

基本數據類型內不包含字符串,而是將字符串作爲String類的匿名對象

如果日後開發要判斷輸入的內容是否爲某一字符串,要將字符串寫在最前面。

錯誤例子

public class StringDemo {
    public static void main(String args[]) {
        String str = null ; // 假設是輸入的
        if (str.equals("Hello")) {
            System.out.println("條件滿足。") ;
        }
    }
}

正確方法

 public class StringDemo {
    public static void main(String args[]) {
        String str = null ; // 假設是輸入的
        if ("Hello".equals(str)) {
            System.out.println("條件滿足。") ;
        }
    }}

實例化方法

直接賦值:

public class StringDemo {
    public static void main(String args[]) {
        String str = "Hello" ;  // 定義字符串(匿名對象)
        System.out.println(str) ;
    }
}

此時內存會開闢一塊堆內存,並且由一塊棧內存指向此堆內存

public class StringDemo {
    public static void main(String args[]) {
        String stra = "hello";              // 直接賦值實例化
        String strb = "hello";              // 直接賦值實例化
        String strc = "hello";              // 直接賦值實例化
        String strd = "yootk" ;             // 直接賦值實例化,內容不相同
        System.out.println(stra == strb);       // 判斷結果:true
        System.out.println(stra == strc);       // 判斷結果:true
        System.out.println(strb == strc);       // 判斷結果:true
        System.out.println(stra == strd);       // 判斷結果:false
    }

以上所有采用直接賦值的String類對象的內存地址完全相同,stra strb strc指向同一個堆內存

在String類進行設計的時候採用了一種稱爲共享設計模式的概念,在每一個運行的JVM底層存在一個字符串的對象池(Object Pool,不一定只保存String對象),如果用戶採用了直接賦值的方式,會將字符串的內容放入到池之中,以供其他繼續使用直接賦值方式的String對象使用,如果新聲明的字符串內容不再池之中,則會開闢一個新的,繼續放到池,以供下次使用。


String類的構造:public String(String str);

構造方法:

ublic class StringDemo {
    public static void main(String args[]) {
        String str = new String("Hello") ;  // 定義字符串
        System.out.println(str) ;
    }
}

用構造方法實例化的String對象,不會入池,所以,只能自己使用。可是在String類之中爲了方便操作提供了一種稱爲手工入池的方法:public String intern()。

手工入池(intern() )

public class StringDemo {
    public static void main(String args[]) {
        String str1 = new String("Hello").intern() ;
        String str2 = "Hello" ; // 入池
        String str3 = "Hello" ; // 使用池對象
        System.out.println(str1 == str2) ;  // true
        System.out.println(str1 == str3) ;  // true
        System.out.println(str2 == str3) ;  // true
    }
}

面試題:請解釋String類的兩種對象實例化方式的區別?
· 直接賦值(String str=”字符串”;):只開闢一塊堆內存空間,字符串的內容可以自動入池,以供下次使用;
· 構造方法(String str=new String(“字符串”);):開闢兩塊堆內存空間,有一塊將成爲垃圾,並且不能自動入池,使用intern()手工入池。

在日後的所有開發之中,String對象的實例化永遠都採用直接賦值的方式完成。


字符串的內容一旦聲明則不可改變,String類對象內容的改變的是依靠引用關係的變更實現的。

String類除了之前所介紹的兩個方法之外,還存在着大量的其他操作,考慮到String類在實際的工作之中使用非常的廣泛,那麼就建議大家都背下來,以下所講解的每一個方法:記住方法的名稱、返回值類型、參數的類型及個數、方法的作用。
以後所有的開發都要和文檔掛勾,而且文檔不許使中文,因爲在所有的Java學習之中,有十幾份文檔呢。文檔之中會對每一個類進行詳細的解釋,而一般的解釋類的文檔的結構:


string類的常用方法

>
以後所有的開發都要和文檔掛勾,而且文檔不許使中文,因爲在所有的Java學習之中,有十幾份文檔呢。文檔之中會對每一個類進行詳細的解釋,而一般的解釋類的文檔的結構:
- 類的基本定義及相關繼承機構;
- 類的一些簡短的說明;
- 類的主體部分:
- 類之中的成員(Field Summary);
- 類中的構造方法(Constructor Summary);
- 類中的普通方法(Method Summary)。
- 對每一個成員、構造方法、普通方法的作用進行詳細說明,包括參數的作用。

Deprecated 表明此方法不再建議使用


字符串與字符

No 方法名稱 類型 描述
1 public String(char[] value) 構造 將全部的字符數組內容變爲String類對象
2 public String(char[] value, int offset, int count) 構造 將部分字符數組變爲String類對象,offset表示開始點,count表示要操作的長度
3 public char charAt(int index) 普通 取得指定索引位置上的字符
4 public char[] toCharArray() 普通 將字符串轉換爲字符數組返回

取得指定索引位置上的字符(charAt(int index))

public class StringDemo {
    public static void main(String args[]) {
        String str = "helloworld" ;
        char c = str.charAt(7) ;
        System.out.println(c) ;
    }
}

程序之中字符串下標都是從0開始的

字符串和字符數組轉換,完成一個小寫字符串變爲大寫字符串的操作,小寫字母和大寫字母差了32

public class StringDemo {
    public static void main(String args[]) {
        String str = "helloworld" ;
        char data [] = str.toCharArray() ;  // 字符串變爲字符數組
        for (int x = 0 ; x < data.length ; x ++) {
            System.out.print(data[x] + "、") ;
            data [x] -= 32 ;    // 變大寫
        }
        System.out.println() ;
        System.out.println("全部字符數組變爲字符串:" + new String(data)) ; //構造方法
        System.out.println("部分字符數組變爲字符串:" + new String(data,0,5)) ; //構造方法
    }
}

將一個字符串首先變爲字符數組,而後依次判斷字符數組之中的每一個字符是否是數字,如果全是,則返回true,否則返回false。

public class StringDemo {
    public static void main(String args[]) {
        String str = " 1a 23" ;
        System.out.println(isNumber(str)) ;
    }
    public static boolean isNumber(String temp) {
        char data [] = temp.toCharArray() ; // 變爲字符數組
        for (int x = 0 ; x < data.length ; x ++) {
            if (data[x] < '0' || data[x] > '9') {
                return false ;  // 不是數字
            }
        }
        return true ;
    }
}

如果寫的方法的返回值是布爾值類型,那麼這個方法的命名一般以is開頭命名


字節與字符串

在String類裏面將字符串轉變爲字節數組的操作,目的就是爲了傳輸以及編碼轉換。

No 方法名稱 類型 描述
1 public String(byte[] bytes) 構造 將全部的字節數組變爲字符串
2 public String(byte[] bytes, int offset, int length) 構造 將部分的字節數組變爲字符串
3 public byte[] getBytes() 普通 將字符串變爲字節數組
4 public byte[] getBytes(String charsetName) throws UnsupportedEncodingException 普通 字符串轉碼操作
public class StringDemo {
    public static void main(String args[]) {
        String str = "helloworld" ;
        byte data [] = str.getBytes() ; // 字符串變爲字節數組
        for (int x = 0 ; x < data.length ; x ++) {
            System.out.print(data[x] + "、") ;
            data [x] -= 32 ;    // 變大寫
        }
        System.out.println() ;
        System.out.println("全部字節數組變爲字符串:" + new String(data)) ;
        System.out.println("部分字節數組變爲字符串:" + new String(data,0,5)) ;
    }
}
一般情況下,在程序之中如果要想操作字節數組只有兩種情況:
    · 情況一:需要進行編碼的轉換時;
    · 情況二:數據要進行傳輸的時候。

字符串比較

之前使用equals()用於比較兩個字符串的內容是否相同。

No 方法名稱 類型 描述
1 public boolean equals(String anObject) 普通 區分大小寫的相等判斷
2 public boolean equalsIgnoreCase(String anotherString) 普通 不區分大小寫比較是否相等
3 public int compareTo(String anotherString) 普通 比較兩個字符串的大小(按照字符編碼比較),此方法的返回值有如下3種結果:
=0表示要比較的字符串內容相等
>0表示大於的結果
<0表示小於的結果

現在只有String類的對象才具有大小的判斷。

字符串查找

No 方法名稱 類型 描述
1 public boolean contains(String s) 普通 查找指定的子字符串是否存在,JDK 1.5之後有
2 public int indexOf(String str) 普通 從頭查找指定字符串的位置,如果找到了則返回第一個字母的位置索引,找不到返回-1
3 public int indexOf(String str, int fromIndex) 普通 由指定位置向後查找字符串的位置,找不到返回-1
4 public int lastIndexOf(String str) 普通 由後向前查找字符串的位置,找不到返回-1
5 public int lastIndexOf(String str, int fromIndex) 普通 從指定位置由後向前查找
6 public boolean startsWith(String prefix) 普通 判斷是否以指定的字符串開頭
7 public boolean startsWith(String prefix, int toffset) 普通 從指定位置判斷是否以指定字符串開頭,JDK 1.7
8 public boolean endsWith(String suffix) 普通 判斷是否以指定的字符串結尾
範例:判斷開頭和結尾操作
public class StringDemo {
    public static void main(String args[]) {
        String str = "**@@hello##" ;
        System.out.println(str.startsWith("**")) ;
        System.out.println(str.startsWith("@@",2)) ;
        System.out.println(str.endsWith("##")) ;
    }
}
範例:使用contains()方法查找字符串是否存在,直接返回boolean,用於各種的執行判斷
public class StringDemo {
    public static void main(String args[]) {
        String str = "helloworld" ;
        System.out.println(str.contains("hello")) ;
        System.out.println(str.contains("xx")) ;
    }
}
範例:古老的操作,尋找字符串
public class StringDemo {
    public static void main(String args[]) {
        String str = "helloworld" ;
        if (str.indexOf("hello") != -1) {
            System.out.println("可以查詢到數據") ;
        }
    }
}

字符串替換操作

替換是指用一個新的字符串替換掉舊的字符串數據,Oracle中的替換函數是replace(),那麼String類之中的替換操作有如下幾個方法:

No 方法名稱 類型 描述
1 public String replaceAll(String regex, String replacement) 普通 用新的內容替換掉舊的內容(全部)
2 public String replaceFirst(String regex, String replacement) 普通 替換首個滿足條件的內容
範例:驗證兩個替換操作
public class StringDemo {
    public static void main(String args[]) {
        String str = "Hello World ." ;
        System.out.println(str.replaceAll("l","_")) ;   // He__o Wor_d .
        System.out.println(str.replaceFirst("l","_")) ; // He_lo World .
    }
}
regex 正則

字符串截取

從一個完整的字符串裏面截取部分的子字符串數據

No 方法名稱 類型 描述
1 public String substring(int beginIndex) 普通 從指定位置截取到結尾
2 public String substring(int beginIndex, int endIndex) 普通 截取部分子字符串的內容
範例:字符串截取
public class StringDemo {
    public static void main(String args[]) {
        String str = "Hello World ." ;
        System.out.println(str.substring(6)) ;
        System.out.println(str.substring(0,5)) ;
    }
}

字符串拆分

No 方法名稱 類型 描述
1 public String[] split(String regex) 普通 按照指定的字符串全拆分
2 public String[] split(String regex, int limit) 普通 按照指定的字符串進行部分拆分,最後的數組的長度就是由limit決定(如果能拆分的結果很多,數組長度纔會由limit來決定)
範例:完成全拆分
public class StringDemo {
    public static void main(String args[]) {
        String str = "Hello World !!!" ;
        String result [] = str.split(" ") ;
        for (int x = 0 ; x < result.length ; x ++) {
            System.out.println(result[x]) ;
        }
    }
}
範例:拆分爲指定的個數
public class StringDemo {
    public static void main(String args[]) {
        String str = "Hello World !!!" ;
        String result [] = str.split(" ",2) ;
        for (int x = 0 ; x < result.length ; x ++) {
            System.out.println(result[x]) ;
        }
    }
}
範例:拆分爲指定的個數
public class StringDemo {
    public static void main(String args[]) {
        String str = "Hello World !!!" ;
        String result [] = str.split(" ",2) ;
        for (int x = 0 ; x < result.length ; x ++) {
            System.out.println(result[x]) ;
        }
    }
}
範例:現在要求拆分IP地址
public class StringDemo {
    public static void main(String args[]) {
        String str = "192.168.1.1" ;
        String result []= str.split("\\.") ;
        for (int x = 0 ; x < result.length ; x ++) {
            System.out.println(result[x]) ;
        }
    }
}

// 提示:以後在進行字符串拆分的時候,如果遇見拆不開的問題,就使用“\\”(由於敏感字符,即正則標記,有些字符拆不開。需要\\進行轉義後拆分)

在實際開發之中,拆分的操作很常見。因爲很多時候會傳遞一組數據到程序中進行處理。

public class TestDemo
{
    public static void main(String arg[])
{
    String str="張三:20|李四:21|王五:22";
    String result [] =str.split("\\|");
    for(int x = 0;x<result.length;x++)
{
    String temp[]=result[x].split(":");
    System.out.println("姓名 "+temp[0]+", 年齡 "+temp[1]);
        }
    }

}

其他方法

No 方法名稱 類型 描述
1 public boolean isEmpty() 普通 判斷是否爲空字符串(是”“,不是null)
2 public int length() 普通 取得字符串長度
3 public String trim() 普通 去掉字符串中左右兩邊的空格
4 public String toLowerCase() 普通 將全部字符串轉小寫
5 public String toUpperCase() 普通 將全部字符串轉大寫
6 public String intern() 普通 入池
7 public String concat(String str) 普通 字符串連接,與”+“類似,其結果並沒有在原始字符串上添加內容
範例:轉大小寫
public class StringDemo {
    public static void main(String args[]) {
        String str = "(*& Hello World !!! &*)" ;
        System.out.println(str.toUpperCase());
        System.out.println(str.toLowerCase());
    }
}
public class StringDemo {
    public static void main(String args[]) {
        String str = " hello " ;
        System.out.println("字符串內容:〖" + str + "〗") ; //一般用戶在輸入數據時會攜帶一些無用的空格內容,接收到這些內容後需要去空格
        System.out.println("字符串內容:〖" + str.trim() + "〗") ;
        // 對象.方法().方法().方法()...代碼鏈,看每個方法的返回值
        System.out.println("字符串長度:" + str.trim().length()) ;
    }
}
public class StringDemo {
    public static void main(String args[]) {
        String s = str.trim () ;
        int len = s.length() ;
        System.out.println("字符串長度:" + len) ;
    }
}

在Oracle的學習過程之中,發現裏面有一個initcap()的方法,可以讓首字母大寫,可是這個方法其實很重要,但是String類沒有提供。下面簡單實現一下,給一個基本的原理。

public class StringDemo {
    public static void main(String args[]) {
        String str = "hello" ;
        System.out.println(initcap(str)) ;
    }
    public static String initcap(String s) {
        return s.substring(0,1).toUpperCase().concat(s.substring(1)) ;
    }
}

this關鍵字

在整個Java之中,this是最麻煩一個關鍵字,只要是代碼開發,幾乎都離不開this。在Java中this可以完成三件事情:表示本類屬性、表示本類方法、當前對象(只是先介紹概念)。

“this.屬性”表示本類屬性

class Person {
    private String name ;
    private int age ;
    public Person(String name,int age) {
        this.name = name ;
        this.age = age ;
    }   // setter、getter略
    public String getInfo() {
        return "姓名:" + this.name + ",年齡:" + this.age ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Person per = new Person("張三",20) ;
        System.out.println(per.getInfo()) ;
    }
}

在日後的所有開發之中,只要是調用類中屬性的情況,都要使用“this.屬性”的方式來進行表示。

調用本類方法

對於一個類之中的方法現在是分爲兩種:
· 普通方法:在之前強調過,如果現在要調用的是本類方法,則可以使用“this.方法()”調用;
· 構造方法:調用其他構造使用“this()”調用。

有缺陷的代碼(重複太多)
class Person {
    private String name ;
    private int age ;
    public Person() {
        System.out.println("*** 一個新的Person類對象被實例化。") ;
    }
    public Person(String name) {
        System.out.println("*** 一個新的Person類對象被實例化。") ;
        this.name = name ;
    }
    public Person(String name,int age) {
        System.out.println("*** 一個新的Person類對象被實例化。") ;
        this.name = name ;
        this.age = age ;
    }   // setter、getter略
    public String getInfo() {
        return "姓名:" + this.name + ",年齡:" + this.age ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Person per = new Person("張三",20) ;
        System.out.println(per.getInfo()) ;
    }
}
修改後的,使用了this關鍵詞調用本類的構造方法
class Person {
    private String name ;
    private int age ;
    public Person() {
        System.out.println("*** 一個新的Person類對象被實例化。") ;
    }
    public Person(String name) {
        this() ;    // 調用無參構造
        this.name = name ;
    }
    public Person(String name,int age) {
        this(name) ;    // 調用有一個參數的構造
        this.age = age ;
    }   // setter、getter略
    public String getInfo() {
        return "姓名:" + this.name + ",年齡:" + this.age ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Person per = new Person("張三",20) ;
        System.out.println(per.getInfo()) ;
    }
}

使用this()就完成了構造方法之間的互相調用操作。

注意:在使用this調用構造方法的時候有以下一些問題:
    · 所以的構造方法是在對象實例化的時候被默認調用,而且是在調用普通方法之前調用,所以使用“this()”調用構造方法的操作,一定要放在構造方法的首行;
    · 如果一個類之中存在了多個構造方法的話,並且這些構造方法都使用了this()互相調用,那麼至少要保留一個構造方法沒有調用其他構造,以作爲程序的出口。
/* 例子:
寫一個公司員工類
數據成員:
        員工號、姓名、薪水、部門
方法:
        1、  利用構造方法完成設置信息:
            A、單參,只傳遞員工號,則員工姓名:無名氏,薪水:0,部門:未定
            B、雙參,傳遞員工號,姓名,則員工薪水爲1000,部門:後勤
            C、四參,傳遞員工號,姓名,部門,薪水
            D、無參,則均爲空值
        2、  顯示信息
*/

class Emp {
    private int empno ;
    private String ename ;
    private double salary ;
    private String dept ;
    public Emp(){}
    public Emp(int empno){
        this.empno = empno ;
        this.ename = "無名氏" ;
        this.salary = 0.0 ;
        this.dept = "未定" ;
    }
    public Emp(int empno,String ename){
        this.empno = empno ;
        this.ename = ename ;
        this.salary = 1000.0 ;
        this.dept = "後勤" ;
    }
    public Emp(int empno,String ename,double salary,String dept){
        this.empno = empno ;
        this.ename = ename ;
        this.salary = salary ;
        this.dept = dept ;
    }
    public String getInfo() {
        return "僱員編號:" + this.empno + ",姓名:" + this.ename + ",工資:" + this.salary + ",部門:" + this.dept ;
    }
}
如果按照以上的方式開發,是可以完成功能的實現,但是代碼之中卻會存在重複的代碼,很明顯,這樣不合適。

class Emp {
    private int empno ;
    private String ename ;
    private double salary ;
    private String dept ;
    public Emp(){}
    public Emp(int empno){
        this(empno,"無名氏",0.0,"未定") ;
    }
    public Emp(int empno,String ename){
        this(empno,ename,1000.0,"後勤") ;
    }
    public Emp(int empno,String ename,double salary,String dept){
        this.empno = empno ;
        this.ename = ename ;
        this.salary = salary ;
        this.dept = dept ;
    }
    public String getInfo() {
        return "僱員編號:" + this.empno + ",姓名:" + this.ename + ",工資:" + this.salary + ",部門:" + this.dept ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Emp emp = new Emp(7369,"SMITH") ;
        System.out.println(emp.getInfo()) ;
    }}

總結:這種構造方法的互相調用是在對象實例化的時候,不同的構造有一些相同操作的情況下去使用。

this表示當前對象

當前對象指的是當前正在調用本類方法的操作對象,前面的“this.屬性”就表示當前對象的屬性

class A
{
    private B b;
    public A()  {//2.執行A類構造、
        //3.爲B類對象b實例化
        this.b= new B(this);    //4.this就是temp
        this.b.get();//7.調用B類的get()方法
    }
    public void print(){
        System.out.println("Hello World");
    }
}
class B
{
    private A a;
    public B(A a) {//5.參數a = temp
        this.a=a;   //6.保存a對象(保存Temp)
    }
    public void get()//8.調用此方法
    {
        this.a.print(); //this.temp=a
    }

}



public class TestDemo{
    public static void main(String arg[])
    {
        //1.實例化A類對象,調用A類對象的無參構造
        A temp =new A();
    }
}

面向對象

引用傳遞

第一個例子

class Demo {
    private int data = 10 ;
    public Demo(int data) {
        this.data = data ;
    }
    public void setData(int data) {
        this.data = data ;
    }
    public int getData() {
        return this.data ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Demo demo = new Demo(100) ;
        fun(demo) ; // Demo temp = demo ;
        System.out.println(demo.getData()) ;
    }
    public static void fun(Demo temp) {
        temp.setData(30) ;
    }
}

第二個例子

public class TestDemo {
    public static void main(String args[]) {
        String str = "Hello" ;
        fun(str) ;  // String temp = str ;
        System.out.println(str) ;
    }
    public static void fun(String temp) {
        temp = "World" ;
    }
}

/*
結果是hello不變
*/

第三個例子

class Demo {
    private String data ;
    public Demo(String data) {
        this.data = data ;
    }
    public void setData(String data) {
        this.data = data ;
    }
    public String getData() {
        return this.data ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Demo demo = new Demo("Hello") ;
        fun(demo) ; // Demo temp = demo ;
        System.out.println(demo.getData()) ;
    }
    public static void fun(Demo temp) {
        temp.setData("World") ;
    }
}

/*
Demo對象(棧)之中包含了String的引用(data是String的名字在棧,而字符串的內容是在堆)不過在Demo對象的堆內存裏面保存了一個data(棧內存)的引用關係。
*/

引用傳遞具體使用

· 一個人有一輛汽車,問,如何設計這個要求?
如果按照以上的思路去設計類的話,現在應該由兩個類完成關係,那麼這兩個類和數據表的對應關係:
· 類中的屬性 = 表的字段;
· 類的名稱 = 表的名稱;
· 類的一個實例化對象 = 數據表的一行記錄;
· 類的對象數組 = 數據表的多行記錄。

    - 實現代碼 —— 一個人有一輛車,一輛車屬於一個人
    · 開發步驟一:先完成各自簡單java類的定義;
    Member類         Car類
    mid             tittle
    name            color

    · 開發步驟二:設置關聯關係。
    private Car car//一個人有一輛車
    private Member member //一輛車屬於一個人

class Member {
    private int mid ;
    private String name ;
    private Car car ;   // car == null表示沒有車
    public Member(int mid,String name) {
        this.mid = mid ;
        this.name = name ;
    }
    public void setCar(Car car) {
        this.car = car ;
    }
    public Car getCar() {
        return this.car ;
    }
    public String getMemberInfo() {
        return "人員編號:" + this.mid + ",姓名:" + this.name ;
    }
}
class Car {
    private String title ;
    private String color ;
    private Member member ;
    public Car(String title,String color) {
        this.title = title ;
        this.color = color ;
    }
    public void setMember(Member member) {
        this.member = member ;
    }
    public Member getMember() {
        return this.member ;
    }
    public String getCarInfo() {
        return "汽車名字:" + this.title + ",顏色:" + this.color ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Member mem = new Member(1,"付雲鬆") ;
        Car car = new Car("BMW,別摸我","紅色") ;
        mem.setCar(car) ;   // 一個人有一輛車
        car.setMember(mem) ;    // 一輛車屬於一個人
        System.out.println(mem.getMemberInfo()) ;
        System.out.println(mem.getCar().getCarInfo()) ;
        System.out.println("=====================") ;
        System.out.println(car.getMember().getMemberInfo()) ;
    }
}

每一個人都有一個自己的孩子,每個孩子都有自己的車,那麼又該如何設計呢?
現在有兩種做法:
· 做法一:定義一個孩子的類,於是在Member表中設置這個類的關係,這樣的設計過於重複;
· 做法二:孩子本身依然屬於一個人,那麼依然具備了人所具備的所有屬性,那麼直接做一個內部的關聯。


class Member {
    private int mid ;
    private String name ;
    private Car car ;   // car == null表示沒有車
    private Member child ;  // 一個人有一個孩子
    public Member(int mid,String name) {
        this.mid = mid ;
        this.name = name ;
    }
    public void setCar(Car car) {
        this.car = car ;
    }
    public Car getCar() {
        return this.car ;
    }
    public void setChild(Member child) {
        this.child = child ;
    }
    public Member getChild() {
        return this.child ;
    }
    public String getMemberInfo() {
        return "人員編號:" + this.mid + ",姓名:" + this.name ;
    }
}
class Car {
    private String title ;
    private String color ;
    private Member member ;
    public Car(String title,String color) {
        this.title = title ;
        this.color = color ;
    }
    public void setMember(Member member) {
        this.member = member ;
    }
    public Member getMember() {
        return this.member ;
    }
    public String getCarInfo() {
        return "汽車名字:" + this.title + ",顏色:" + this.color ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Member mem = new Member(1,"付雲鬆") ;
        Member chd = new Member(100,"付三") ;
        Car car = new Car("BMW,別摸我","紅色") ;
        Car c = new Car("AUTO","黑色") ;
        mem.setCar(car) ;   // 一個人有一輛車
        car.setMember(mem) ;    // 一輛車屬於一個人
        mem.setChild(chd) ; // 一個人有一個孩子
        chd.setCar(c) ; // 孩子有一輛車
        c.setMember(chd) ;  // 車屬於孩子
        System.out.println("找到孩子的車:" + mem.getChild().getCar().getCarInfo()) ;
    }
}

簡單java類和數據表的映射

思考題
利用此關係模型,表示出emp和dept的關係,使用字段:
· emp表:empno、ename、job、sal、comm、mgr、deptno;
· dept表:deptno、dname、loc。
關係:
· 一個部門有多個僱員;
· 一個僱員有一個領導;
· 一個僱員屬於一個部門。

第一步:實現基本字段的轉換(簡單java類)

class Emp {
    private int empno ;
    private String ename ;
    private String job ;
    private double sal ;
    private double comm ;
    //setter、getter省略
    public Emp() {}
    public Emp(int empno,String ename,String job,double sal,double comm) {
        this.empno = empno ;
        this.ename = ename ;
        this.job = job ;
        this.sal = sal ;
        this.comm = comm ;
    }
    public String getEmpInfo() {
        return "僱員編號:" + this.empno + ",姓名:" + this.ename + ",職位:" + this.job + ",工資:" + this.sal + ",佣金:" + this.comm ;
    }
}
class Dept {
    private int deptno ;
    private String dname ;
    private String loc ;
    public Dept() {}
    public Dept(int deptno,String dname,String loc) {
        this.deptno = deptno ;
        this.dname = dname ;
        this.loc = loc ;
    }
    public String getDeptInfo() {
        return "部門編號:" + this.deptno + ",部門名稱:" + this.dname + ",位置:" + this.loc ;
    }
}

第二步、解決外鍵問題
- 一個僱員屬於一個部門,應該在僱員裏面保存部門信息。

    private Dept dept ;
    public void setDept(Dept dept) {
        this.dept = dept ;
    }
    public Dept getDept() {
        return this.dept ;
    }
  • 一個部門裏面有多個僱員,在描述這個問題時可以使用對象數組完成
    private Emp emps [] ;   // 多個僱員
    public void setEmps(Emp emps[]) {
        this.emps = emps ;
    }
    public Emp[] getEmps() {
        return this.emps ;
    }
  • 一個僱員有一個領導
    private Emp mgr ;
    public void setMgr(Emp mgr) {
        this.mgr = mgr ;
    }
    public Emp getMgr() {
        return this.mgr ;
    }

第三步.設置並取得數據


public class TestDemo {
    public static void main(String args[]) {
        // 第一步設置數據
        //1.產生各自的獨立對象
        Dept dept = new Dept(10,"ACCOUNTING","New Yrok") ; //部門信息
        Emp empa = new Emp(7369,"SMITH","CLERK",800.0,0.0) ;
        Emp empb = new Emp(7566,"ALLEN","MANAGER",2450.0,0.0) ;
        Emp empc = new Emp(7839,"KING","PRESIDENT",5000.0,0.0) ;
        //2.設置僱員和領導的關係
        empa.setMgr(empb) ;
        empb.setMgr(empc) ;
        //3.設置僱員和部門的關係
        //一個僱員屬於一個部門
        empa.setDept(dept) ;
        empb.setDept(dept) ;
        empc.setDept(dept) ;
        // 每一個部門有多個僱員
        dept.setEmps(new Emp[]{empa,empb,empc}) ; //用new關鍵詞 靜態初始化
        // 第二部取出數據
        //1.通過僱員找到領導信息和部門信息
        System.out.println(empa.getInfo());
        System.out.println("\t|-"empa.getMgr().getInfo());
        System.out.println("\t|-"empa.getDept().getInfo());
        System.out.println("\t|-"empa.getDept().getInfo());

        System.out.println("===================") ;
        //2.根據部門找到僱員以及其領導
        System.out.println(dept.getDeptInfo()) ;
        for (int x = 0 ; x < dept.getEmps().length ; x ++) {
            System.out.println("\t  |-"dept.getEmps()[x].getEmpInfo()) ;
            if (dept.getEmps()[x].getMgr() != null) {   // 有領導時輸出領導
                System.out.println("\t\t |-" + dept.getEmps()[x].getMgr().getEmpInfo()) ;
            }
        }
    }
}

class Emp {
    private int empno ;
    private String ename ;
    private String job ;
    private double sal ;
    private double comm ;
    private Emp mgr ;
    private Dept dept ;
    public Emp() {}
    public Emp(int empno,String ename,String job,double sal,double comm) {
        this.empno = empno ;
        this.ename = ename ;
        this.job = job ;
        this.sal = sal ;
        this.comm = comm ;
    }
    public void setDept(Dept dept) {
        this.dept = dept ;
    }
    public Dept getDept() {
        return this.dept ;
    }
    public void setMgr(Emp mgr) {
        this.mgr = mgr ;
    }
    public Emp getMgr() {
        return this.mgr ;
    }
    public String getEmpInfo() {
        return "僱員編號:" + this.empno + ",姓名:" + this.ename + ",職位:" + this.job + ",工資:" + this.sal + ",佣金:" + this.comm ;
    }
}
class Dept {
    private int deptno ;
    private String dname ;
    private String loc ;
    private Emp emps [] ;   // 多個僱員
    public Dept() {}
    public Dept(int deptno,String dname,String loc) {
        this.deptno = deptno ;
        this.dname = dname ;
        this.loc = loc ;
    }
    public void setEmps(Emp emps[]) {
        this.emps = emps ;
    }
    public Emp[] getEmps() {
        return this.emps ;
    }
    public String getDeptInfo() {
        return "部門編號:" + this.deptno + ",部門名稱:" + this.dname + ",位置:" + this.loc ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        // 第一步設置數據
        //1.產生各自的獨立對象
        Dept dept = new Dept(10,"ACCOUNTING","New Yrok") ; //部門信息
        Emp empa = new Emp(7369,"SMITH","CLERK",800.0,0.0) ;
        Emp empb = new Emp(7566,"ALLEN","MANAGER",2450.0,0.0) ;
        Emp empc = new Emp(7839,"KING","PRESIDENT",5000.0,0.0) ;
        //2.設置僱員和領導的關係
        empa.setMgr(empb) ;
        empb.setMgr(empc) ;
        //3.設置僱員和部門的關係
        //一個僱員屬於一個部門
        empa.setDept(dept) ;
        empb.setDept(dept) ;
        empc.setDept(dept) ;
        // 每一個部門有多個僱員
        dept.setEmps(new Emp[]{empa,empb,empc}) ; //用new關鍵詞 靜態初始化
        // 第二部取出數據
        //1.通過僱員找到領導信息和部門信息
        System.out.println(empa.getInfo());
        System.out.println("\t|-"empa.getMgr().getInfo());
        System.out.println("\t|-"empa.getDept().getInfo());
        System.out.println("\t|-"empa.getDept().getInfo());

        System.out.println("===================") ;
        //2.根據部門找到僱員以及其領導
        System.out.println(dept.getDeptInfo()) ;
        for (int x = 0 ; x < dept.getEmps().length ; x ++) {
            System.out.println("\t  |-"dept.getEmps()[x].getEmpInfo()) ;
            if (dept.getEmps()[x].getMgr() != null) {   // 有領導時輸出領導
                System.out.println("\t\t |-" + dept.getEmps()[x].getMgr().getEmpInfo()) ;
            }
        }
    }
}

static關鍵字

和C++的static差不多

使用static定義屬性

類似與C++的類的static數據,在對象實例化之前即存在,即公共屬性

  • 使用static定義的屬性不在堆內存之中保存,保存在全局數據區;
  • 使用static定義的屬性表示類屬性,類屬性可以由類名稱直接進行調用;
  • static屬性雖然定義在了類之中,但是其可以在沒有實例化對象的時候進行調用(普通屬性保存在堆內存裏,而static屬性保存在全局數據區之中);

    使用static定義方法

  • 使用static定義的方法也可以在沒有實例化對象產生的情況下由類名稱直接進行調用。
  • static定義的方法不能調用非static的方法或屬性;
  • 非static定義的方法可以調用static的屬性或方法。

>
爲什麼要有這樣的限制呢?
· 使用static定義的屬性和方法,可以在沒有實例化對象的時候使用;
· 非static定義的屬性和方法,必須實例化對象之後纔可以進行調用。
>

主方法

如果一個方法在主類之中定義,並且由主方法直接調用的時候,那麼前面必須有public static。

public static 返回值類型 方法名稱 (參數列表) {
    [return [返回值] ;]
}

所有的非static方法幾乎都有一個特點:方法要由實例化對象調用。
主方法的組成,可以發現裏面有很多的關鍵字:
· public:是一種訪問權限,表示公共;
· static:此方法由類名稱直接調用,執行類:java 類名稱;
· void:主方法是一切的開始,開弓沒有回頭箭;
· main:系統規定的一個方法名稱,執行類的時候默認找到此名稱;
· String args[]:表示的是一些運行時參數,通過字符串接收。

\\範例:接收參數
public class TestDemo {
    public static void main(String args[]) {
        for (int x = 0 ; x < args.length ; x ++) {
            System.out.println(args[x]) ;
        }
    }
}

所有的屬性在執行類的時候使用空格分隔,例如:java TestDemo 參數 參數 參數 …
但是,由於在默認情況下空格作爲參數的分隔符,如果說現在要輸入的參數本身就存在空格,這些參數上可以使用“””聲明:java TestDemo “hello world” “hello mldn”。

static關鍵字的應用

使用static的原因有二:
· 希望可以在沒有實例化對象的時候可以輕鬆的執行類的某些操作;
· 現在希望表示出數據共享的概念。

範例:統計一個類產生的實例化對象個數
class Person {
    private static int count = 0 ;
    public Person() {   // 構造方法一定會調用
        System.out.println("對象個數:" + ++ count) ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        new Person() ;new Person() ;new Person() ;
    }
}

範例:爲類的屬性自動命名
class Book {
    private static int count = 0 ;
    private String title ;
    public Book() { // 自動命名
        this("NOTITLE - " + count ++) ; //調用另外一個構造函數
    }
    public Book(String title) {
        this.title = title ;
    }
    public String getTitle() {
        return this.title ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        System.out.println(new Book().getTitle()) ;
        System.out.println(new Book("Java SE").getTitle()) ;
        System.out.println(new Book().getTitle()) ;
    }}

代碼塊

代碼塊一共分爲四種:普通代碼塊、構造塊、靜態塊、同步塊 。
普通代碼塊:定義在方法之中的代碼塊.


構造塊:定義在類之中的代碼塊。

class Person {
    public Person() {
        System.out.println("構造方法。") ;
    }
    {   // 構造塊
        System.out.println("構造塊。") ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        new Person() ;  new Person() ;  new Person() ;
    }
}

構造塊優先於構造方法執行,而且每當有一個新的實例化對象產生的時候,就會出現構造塊的執行。


靜態塊:定義在類之中的,如果一個構造塊上使用了static關鍵字進行定義的話,那麼就表示靜態塊。

情況一:在非主類之中定義的靜態塊
class Person {
    public Person() {
        System.out.println("構造方法。") ;
    }
    {   // 構造塊
        System.out.println("構造塊。") ;
    }
    static {
        System.out.println("靜態塊。") ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        new Person() ;  new Person() ;  new Person() ;
    }
}

靜態塊優先於構造塊執行,而且不管有多少個實例化對象產生,靜態塊只調用一次

情況二:在主類中定義的靜態塊
public class TestDemo {
    static {
        System.out.println("靜態塊。") ;
    }
    public static void main(String args[]) {
        System.out.println("Hello World .") ;
    }
}

在主類之中的靜態塊優先於主方法執行

內部類

內部類指的是在一個類的內部定義了其他類的情況

class Outer {   // 外部類
    private String msg = "Hello World " ;   // 普通屬性
    class Inner {   // 內部類
        public void print() {
            System.out.println(msg) ;
        }
    }
    public void fun() {
        Inner in = new Inner() ;
        in.print() ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Outer out = new Outer() ;
        out.fun() ;
    }
}

內部類屬於在一個類內部定義的新的結構,不過按照類的基本組成來講,一個類之中應該由屬性和方法所組成,但是這個時候又多了一個類,這樣的結構並不好,所以內部類本身最大的缺點破壞了程序的結構。
內部類的最大優點:可以方便的訪問外部類的私有操作,或者是由外部類方便的訪問內部類的私有操作。(即解決了C++的友元問題,取消友元的不安全性,用內部類取代)

>
但是這個時候一個嚴重的問題來了,在之前一直強調:只要是訪問類中的屬性前面都要有“this.”,即:以上的程序之中,內部類訪問外部類私有屬性的時候,應該是“this.msg”纔對,但是在內部類使用this表示的只是內部類的當前對象,如果要想表示出外部類的當前對象,使用“外部類.this”來表示。
>

class Outer {   // 外部類
    private String msg = "Hello World " ;   // 普通屬性
    class Inner {   // 內部類
        public void print() {
            System.out.println(Outer.this.msg) ;// 內部類訪問外部類,Android開發之中,此代碼常見
        }
    }
    public void fun() {
        Inner in = new Inner() ;
        in.print() ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Outer out = new Outer() ;
        out.fun() ;
    }
}

如果說現在內部類不希望被外面看見呢?那麼可以繼續進行封裝操作

class Outer {   // 外部類
    private String msg = "Hello World " ;   // 普通屬性
    private class Inner {   // 內部類
        public void print() {
            System.out.println(Outer.this.msg) ;// 內部類訪問外部類
        }
    }
}

使用static定義內部類

使用了static定義的內部類,則就表示爲“外部類”,並且只能訪問外部類之中static類型的操作。

class Outer {   // 外部類
    private static String msg = "Hello World " ;    // 普通屬性
    static class Inner {    // 內部類 = ”外部類“
        public void print() {
            System.out.println(Outer.msg) ;// 內部類訪問外部類
        }
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Outer.Inner in = new Outer.Inner() ;
        in.print() ;
    }
}

在方法中定義內部類

內部類理論上可以在類的任意位置上進行定義,這就包括代碼塊之中,或者是普通方法之中,而在以後的開發過程之中,在普通方法裏面定義內部類的情況是最多的。

範例:在方法之中定義內部類
class Outer {   // 外部類
    private String msg = "Hello World " ;   // 普通屬性
    public void fun() {
        class Inner {   // 方法中定義的內部類
            public void print() {
                System.out.println(Outer.this.msg) ;// 內部類訪問外部類
            }
        }
        Inner in = new Inner() ;    // 產生內部類對象
        in.print() ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        new Outer().fun() ;
    }
}

但是一個內部類如果要定義在方法之中,並且要訪問方法的參數或者是方法中定義變量的時候,這些參數或變量前一定要增加一個“final”關鍵字。

class Outer {   // 外部類
    private String msg = "Hello World " ;   // 普通屬性
    public void fun(final int x) {  // 參數
        final String info = "Hello MLDN" ;  // 變量
        class Inner {   // 方法中定義的內部類
            public void print() {
                System.out.println(Outer.this.msg) ;
                System.out.println(x) ;
                System.out.println(info) ;
            }
        }
        Inner in = new Inner() ;    // 產生內部類對象
        in.print() ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        new Outer().fun(30) ;
    }

鏈表

鏈表實際上可以理解爲一串數據,或者按照專業性的說法,可以理解爲動態的對象數組。
但是鏈表需要人爲進行關係的設置,而且每個操作設置的時候,除了要保存“對象”之外,還要再多保留一個引用。
Link類處理節點的操作,而Node類負責節點的具體順序的操作,但是客戶端,不關心節點,只關心Link類即可。
鏈表的配置上用的最多的是內部類還有遞歸的使用

開發可用鏈表

1.增加數據:public String add(數據 對象)

class Link {    // 用戶唯一關注的是此類
    // 使用內部類的最大好處是可以和外部類進行私有操作的互相訪問
    private class Node {    // 處理節點關係
        private String data ;   // 要保存的數據
        private Node next ; // 下一個節點
        public Node(String data){
            this.data = data ;
        }
        public void addNode(Node newNode) { // 增加節點
            if (this.next == null) {    // 當前節點之後沒有節點
                this.next = newNode ;   // 保存新節點
            } else {    // 當前節點之後有節點了
                this.next.addNode(newNode) ;    // 向下繼續判斷
            }
        }
    }
    private Node root ; // 根節點,第一個保存元素
    public boolean add(String data) {   // 增加數據
        if (data == null) { // 如果保存的是一個空數據
            return false ;  // 增加失敗
        }
        // 將數據封裝爲節點,目的:節點有next可以處理關係
        Node newNode = new Node(data) ;
        // 鏈表的關鍵就在於根節點
        if (this.root == null) {    // 現在沒有根節點
            this.root = newNode ;   // 第一個作爲根節點
        } else {    // 根節點有了,新的節點要保留在合適的位置
            this.root.addNode(newNode) ;    // Node類負責處理
        }
        return true ;   // 增加成功
    }
}

2.增加多個數據:public boolean addAll(數據 對象 [] )

//採用循環數組
    public boolean addAll(String data[]) {  // 一組數據
        for (int x = 0 ; x < data.length ; x ++) {
            if (!this.add(data[x])) {   // 保存不成功
                return false ;
            }
        }
        return true ;
    }

3.統計數據個數:public int size()

    private int count ; // 在Link類之中增加一個統計的屬性:count,統計個數
    public boolean add(String data) {   // 增加數據
        if (data == null) { // 如果保存的是一個空數據
            return false ;  // 增加失敗
        }
        // 將數據封裝爲節點,目的:節點有next可以處理關係
        Node newNode = new Node(data) ;
        // 鏈表的關鍵就在於根節點
        if (this.root == null) {    // 現在沒有根節點
            this.root = newNode ;   // 第一個作爲根節點
        } else {    // 根節點有了,新的節點要保留在合適的位置
            this.root.addNode(newNode) ;    // Node類負責處理
        }
        this.count ++ ; // 保存數據量增加
        return true ;   // 增加成功
    }

4.判斷是否是空鏈表:public boolean isEmpty()
空鏈表是指的是鏈表之中不保存任何的數據,實際上這個null可以通過兩種方式判斷:一種判斷鏈表的根節點是否爲null,另外一個是判斷保存元素的個數是否爲0。

    public boolean isEmpty() {
        return this.count == 0 ;
    }

5.根據內容查詢:public boolean contains(數據 對象)
操作原理:逐個盤查,盤查的具體實現還是應該交給Node類去處理,但是在盤查之前必須有一個前提:有數據存在。

link類
    public boolean contains(String data) {  // 查找數據
        // 根節點沒有數據,查找的也沒有數據
        if (this.root == null || data == null) {
            return false ;  // 不需要進行查找了
        }
        return this.root.containsNode(data) ;   // 交給Node類處理
    }


Node類
//  · 判斷當前節點的內容是否滿足於查詢內容,如果滿足返回true;
//  · 如果當前節點的內容不滿足,則向後繼續查,如果已經沒有後續節點了,則返回false。

        public boolean containsNode(String data) {  // 查找數據
            if (data.equals(this.data)) {   // 與當前節點數據吻合
                return true ;
            } else {    // 與當前節點數據不吻合
                if (this.next != null) {    // 還有下一個節點
                    return this.next.containsNode(data) ;
                } else {    // 沒有後續節點
                    return false ;  // 查找不到
                }
            }
        }

6.根據索引位置取得數據:public 數據 get(int index)
在一個鏈表之中會有多個節點保存數據,現在要求可以取得指定節點位置上的數據。但是在進行這一操作的過程之中,有一個小問題:如果要取得數據的索引超過了數據的保存個數,那麼是無法取得的。
此時需要定義腳標foot,且foot每次操作時需要置爲0.

Link類
    public String get(int index) {
        if (index > this.count) {   // 超過個數
            return null ;   // 返回null
        }
        this.foot = 0 ; // 操作foot來定義腳標
        return this.root.getNode(index) ;
    }

Node類
    public String getNode(int index) {
        if (Link.this.foot ++ == index) {   // 當前索引爲查找數值
            return this.data ;
        } else {
            return this.next.getNode(index) ;
        }
    }

7.刪除數據:public void remove(數據 對象)

刪除數據的操作需要分兩種情況討論:
· 情況一:刪除的數據不是根節點,使用:要刪除節點的上一個節點.next = 要刪除節點.next;
· 情況二:刪除的數據是根節點,根節點 = 根節點.next。

Link 類·
    public void remove(String data) {   // 要刪除的節點
        if (! this.contains(data)) {    // 要刪除的數據不存在
            return ;    // 直接返回被調用處,下面代碼不執行了
        }
        if (data.equals(this.root.data)) {  // 要刪除的是根節點
            this.root = this.root.next ;    // 根節點的下一個
        } else {    // 要刪除的不是根節點
            this.root.next.removeNode(this.root,data) ;
        }
        this.count -- ; // 修改個數
    }

Node類
        // 傳入兩個參數:上一個節點,另外一個表示要刪除的數據
        public void removeNode(Node previous,String data) {
            if (this.data.equals(data)) {   // 當前節點的數據吻合刪除條件
                previous.next = this.next ; // 空出當前節點
            } else {
                this.next.removeNode(this,data) ;   // 向後繼續刪除
            }
        }

8.將鏈表變爲對象數組:public 數據類型 [] toArray()

對於鏈表的這種數據結構,在實際的開發之中,最爲關鍵的是兩個操作:增加數據、取得全部數據,而鏈表本身屬於一種動態的對象數組,所以在鏈表輸出時,最好的做法是將鏈表中所保存的數據以對象數組的方式返回,而這個返回的對象數組長度也應該是根據保存數據的個數來決定的

Link類

private String [] retArray ;    // 增加一個返回的數組屬性內容,之所以將其定義爲屬性,是因爲內部類和外部類都可以訪問。
/**
     將鏈表中的數據轉換爲對象數組輸出
     如果鏈表沒有數據返回null,如果有數據則將數據變爲對象數組後返回
**/
    public String[] toArray() {
        if (this.root == null) {    // 判斷鏈表是否有數據
            return null;    // 沒有數據返回null
        }
        this.foot = 0; // 腳標清零操作
        this.retArray = new String[this.count]; // 根據保存內容開闢數組
        this.root.toArrayNode(); // 交給Node類處理
        return this.retArray;   // 返回數組對象
    }

Node類
/**
* 將節點中保存的內容轉化爲對象數組
* 第一次調用(Link):this = Link.root;
 * 第二次調用(Node):this = Link.root.next;
 */
    public void toArrayNode() {
        Link.this.retArray[Link.this.foot++] = this.data;   // 取出數據並保存在數組之中
        if (this.next != null) {    // 有後續元素
            this.next.toArrayNode();    // 繼續下一個數據的取得
        }
    }

9.清空鏈表:public void clear()

    public void clear() {
        this.root = null;       // 清空鏈表
        this.count = 0;     // 元素個數爲0
    }

可用鏈表

class Link {                                // 鏈表類,外部能夠看見的只有這一個類
    private class Node {                    // 定義的內部節點類
        private Object data;                // 要保存的數據
        private Node next;              // 下一個節點引用
        public Node(Object data) {          // 每一個Node類對象都必須保存相應的數據
            this.data = data;
        }
        /**
         * 設置新節點的保存,所有的新節點保存在最後一個節點之後
         * @param newNode 新節點對象
         */
        public void addNode(Node newNode) {
            if (this.next == null) {            // 當前的下一個節點爲null
                this.next = newNode ;       // 保存節點
            } else {                            // 向後繼續保存
                this.next.addNode(newNode) ;
            }
        }
        /**
         * 數據檢索操作,判斷指定數據是否存在
         * 第一次調用(Link):this = Link.root
         * 第二次調用(Node):this = Link.root.next
         * @param data 要查詢的數據
         * @return 如果數據存在返回true,否則返回false
         */
        public boolean containsNode(Object data) {
            if (data.equals(this.data)) {           // 當前節點數據爲要查詢的數據
                return true;                    // 後面不再查詢了
            } else {                            // 當前節點數據不滿足查詢要求
                if (this.next != null) {            // 有後續節點
                    return this.next.containsNode(data);    // 遞歸調用繼續查詢
                } else {                        // 沒有後續節點
                    return false;               // 沒有查詢到,返回false
                }
            }
        }
        /**
         * 根據索引取出數據,此時該索引一定是存在的
         * @param index 要取得數據的索引編號
         * @return 返回指定索引節點包含的數據
         */
        public Object getNode(int index) {
            // 使用當前的foot內容與要查詢的索引進行比較,隨後將foot的內容自增,目的是下次查詢方便
            if (Link.this.foot++ == index) {            // 當前爲要查詢的索引
                return this.data;                   // 返回當前節點數據
            } else {                                // 繼續向後查詢
                return this.next.getNode(index);    // 進行下一個節點的判斷
            }
        }
        /**
         * 修改指定索引節點包含的數據
         * @param index 要修改的索引編號
         * @param data 新數據
         */
        public void setNode(int index, Object data) {
            // 使用當前的foot內容與要查詢的索引進行比較,隨後將foot的內容自增,目的是下次查詢方便
            if (Link.this.foot++ == index) {        // 當前爲要修改的索引
                this.data = data;               // 進行內容的修改
            } else {
                this.next.setNode(index, data); // 繼續下一個節點的索引判斷
            }
        }
        /**
         * 節點的刪除操作,匹配每一個節點的數據,如果當前節點數據符合刪除數據
         * 則使用“當前節點上一節點.next = 當前節點.next”方式空出當前節點
         * 第一次調用(Link),previous = Link.root、this = Link.root.next
         * 第二次調用(Node),previous = Link.root.next、this = Link.root.next.next
         * @param previous 當前節點的上一個節點
         * @param data 要刪除的數據
         */
        public void removeNode(Node previous, Object data) {
            if (data.equals(this.data)) {               // 當前節點爲要刪除節點
                previous.next = this.next;          // 空出當前節點
            } else {                                    // 應該向後繼續查詢
                this.next.removeNode(this, data);   // 繼續下一個判斷
            }
        }
        /**
         * 將節點中保存的內容轉化爲對象數組
         * 第一次調用(Link):this = Link.root;
         * 第二次調用(Node):this = Link.root.next;
         */
        public void toArrayNode() {
            Link.this.retArray[Link.this.foot++] = this.data;   // 取出數據並保存在數組中
            if (this.next != null) {                            // 有後續元素
                this.next.toArrayNode();                        // 繼續下一個數據的取得
            }
        }
    }
    // ===================== 以上爲內部類 ===================
    private Node root;                          // 根節點定義
    private int count = 0 ;                     // 保存元素的個數
    private int foot = 0 ;                          // 節點索引
    private Object [] retArray ;                    // 返回的數組
    /**
     * 用戶向鏈表增加新的數據,在增加時要將數據封裝爲Node類,這樣纔可以匹配節點順序
     * @param data 要保存的數據
     */
    public void add(Object data) {              // 假設不允許有null
        if (data == null) {                     // 判斷數據是否爲空
            return;                             // 結束方法調用
        }
        Node newNode = new Node(data);      // 要保存的數據
        if (this.root == null) {                    // 當前沒有根節點
            this.root = newNode;                // 保存根節點
        } else {                                // 根節點存在
            this.root.addNode(newNode);     // 交給Node類處理節點的保存
        }
        this.count ++ ;                         // 數據保存成功後保存個數加一
    }
    /**
     * 取得鏈表中保存的數據個數
     * @return 保存的個數,通過count屬性取得
     */
    public int size() {                             // 取得保存的數據量
        return this.count;
    }
    /**
     * 判斷是否是空鏈表,表示長度爲0,不是null
     * @return 如果鏈表中沒有保存任何數據則返回true,否則返回false
     */
    public boolean isEmpty() {
        return this.count == 0;
    }
    /**
     * 數據查詢操作,判斷指定數據是否存在,如果鏈表沒有數據直接返回false
     * @param data 要判斷的數據
     * @return 數據存在返回true,否則返回false
     */
    public boolean contains(Object data) {
        if (data == null || this.root == null) {    // 現在沒有要查詢的數據,根節點也不保存數據
            return false ;                      // 沒有查詢結果
        }
        return this.root.containsNode(data) ;   // 交由Node類查詢
    }
    /**
     * 根據索引取得保存的節點數據
     * @param index 索引數據
     * @return 如果要取得的索引內容不存在或者大於保存個數,返回null,反之返回數據
     */
    public Object get(int index) {
        if (index > this.count) {                   // 超過了查詢範圍
            return null ;                       // 沒有數據
        }
        this.foot = 0 ;                         // 表示從前向後查詢
        return this.root.getNode(index) ;       // 查詢過程交給Node類
    }
    /**
     * 根據索引修改數據
     * @param index 要修改數據的索引編號
     * @param data 新的數據內容
     */
    public void set(int index, Object data) {
        if (index > this.count) {                   // 判斷是否超過了保存範圍
            return;                             // 結束方法調用
        }
        this.foot = 0;                          // 重新設置foot屬性的內容,作爲索引出現
        this.root.setNode(index, data);         // 交給Node類設置數據內容
    }
    /**
     * 鏈表數據的刪除操作,在刪除前要先使用contains()判斷鏈表中是否存在指定數據
     * 如果要刪除的數據存在,則首先判斷根節點的數據是否爲要刪除數據
     * 如果是,則將根節點的下一個節點作爲新的根節點
     * 如果要刪除的數據不是根節點數據,則將刪除操作交由Node類的removeNode()方法完成
     * @param data 要刪除的數據
     */
    public void remove(Object data) {
        if (this.contains(data)) {                  // 主要功能是判斷數據是否存在
            // 要刪除數據是否是根節點數據,root是Node類的對象,此處直接訪問內部類的私有操作
            if (data.equals(this.root.data)) {      // 根節點數據爲要刪除數據
                this.root = this.root.next;             // 空出當前根節點
            } else {                                // 根節點數據不是要刪除數據
                // 此時根元素已經判斷過了,從第二個元素開始判斷,即第二個元素的上一個元素爲根節點
                this.root.next.removeNode(this.root, data);
            }
            this.count--;                           // 刪除成功後個數要減少
        }
    }
    /**
     * 將鏈表中的數據轉換爲對象數組輸出
     * @return 如果鏈表沒有數據,返回null,如果有數據,則將數據變爲對象數組後返回
     */
    public Object[] toArray() {
        if (this.root == null) {                        // 判斷鏈表是否有數據
            return null;                            // 沒有數據,返回null
        }
        this.foot = 0;                              // 腳標清零操作
        this.retArray = new Object[this.count];     // 根據保存內容開闢數組
        this.root.toArrayNode();                    // 交給Node類處理
        return this.retArray;                       // 返回數組對象
    }
    /**
     * 清空鏈表數據
     */
    public void clear() {
        this.root = null;                           // 清空鏈表
        this.count = 0;                             // 元素個數爲0
    }
}

模型

class Book {
    private String title ;
    private double price ;
    public Book(String title,double price) {
        this.title = title ;
        this.price = price ;
    }
    /**
     * 進行本類對象的比較操作,在比較過程中首先會判斷傳入的對象是否爲null,而後判斷地址是否相同
     * 如果都不相同則進行對象內容的判斷,由於compare()方法接收了本類引用,所以可以直接訪問私有屬性
     *  book 要進行判斷的數據
     *  內存地址相同或屬性完全相同返回true,否則返回false
     */
    public boolean compare(Book book) {
        if (book == null) {                     // 傳入數據爲null
            return false ;                      // 沒有必要進行具體的判斷
        }
        // 執行“b1.compare(b2)”代碼時會有兩個對象
        // 當前對象this(調用方法對象,就是b1引用)
        // 傳遞的對象book(引用傳遞,就是b2引用)
        if (this == book) {                     // 內存地址相同
            return true ;                       // 避免進行具體細節的比較,節約時間
        }
        if (this.title.equals(book.title)
            && this.price == book.price) {          // 屬性判斷
            return true ;
        } else {
            return false ;
        }
    }
    // setter、getter略
    public String getInfo() {
        return "圖書名稱:" + this.title + ",圖書價格:" + this.price ;
    }
}

class Link {                                    // 鏈表類,外部能夠看見的只有這一個類
    private class Node {                            // 定義的內部節點類
        private Book data;                      // 要保存的數據
        private Node next;                      // 下一個節點引用
        public Node(Book data) {                    // 每一個Node類對象都必須保存相應的數據
            this.data = data;
        }
        /**
         * 設置新節點的保存,所有的新節點保存在最後一個節點之後
         * @param newNode 新節點對象
         */
        public void addNode(Node newNode) {
            if (this.next == null) {                    // 當前的下一個節點爲null
                this.next = newNode ;                   // 保存節點
            } else {                                // 向後繼續保存
                this.next.addNode(newNode) ;
            }
        }
        /**
             * 數據檢索操作,判斷指定數據是否存在
         * 第一次調用(Link):this = Link.root
         * 第二次調用(Node):this = Link.root.next
         * @param data 要查詢的數據
         * @return 如果數據存在返回true,否則返回false
         */
        public boolean containsNode(Book data) {
            if (data.compare(this.data)) {              // 當前節點數據爲要查詢的數據
                return true;                        // 後面不再查詢了
            } else {                                // 當前節點數據不滿足查詢要求
                if (this.next != null) {                    // 有後續節點
                    return this.next.containsNode(data);    // 遞歸調用繼續查詢
                } else {                            // 沒有後續節點
                 return false;                      // 沒有查詢到返回false
                }
            }
        }
        /**
         * 根據索引取出數據,此時該索引一定是存在的
         * @param index 要取得數據的索引編號
         * @return 返回指定索引節點包含的數據
         */
        public Book getNode(int index) {
            // 使用當前的foot內容與要查詢的索引進行比較,隨後將foot的內容自增,目的是下次查詢方便
            if (Link.this.foot++ == index) {                // 當前爲要查詢的索引
                return this.data;                   // 返回當前節點數據
            } else {                                // 繼續向後查詢
                return this.next.getNode(index);            // 進行下一個節點的判斷
            }
        }
        /**
         * 修改指定索引節點包含的數據
         * @param index 要修改的索引編號
         * @param data 新數據
         */
        public void setNode(int index, Book data) {
        // 使用當前的foot內容與要查詢的索引進行比較,隨後將foot的內容自增,目的是下次查詢方便
            if (Link.this.foot++ == index) {                    // 當前爲要修改的索引
                this.data = data;                       // 進行內容的修改
            } else {
                this.next.setNode(index, data);             // 繼續下一個節點的索引判斷
            }
        }
        /**
         * 節點的刪除操作,匹配每一個節點的數據,如果當前節點數據符合刪除數據,
         * 則使用“當前節點上一節點.next = 當前節點.next”方式空出當前節點
         * 第一次調用(Link):previous = Link.root、this = Link.root.next
         * 第二次調用(Node):previous = Link.root.next、this = Link.root.next.next
         * @param previous 當前節點的上一個節點
         * @param data 要刪除的數據
         */
        public void removeNode(Node previous, Book data) {
            if (data.compare(this.data)) {                  // 當前節點爲要刪除節點
                previous.next = this.next;                  // 空出當前節點
            } else {                                    // 應該向後繼續查詢
                this.next.removeNode(this, data);           // 繼續下一個判斷
            }
        }
        /**
         * 將節點中保存的內容轉化爲對象數組
         * 第一次調用(Link):this = Link.root;
         * 第二次調用(Node):this = Link.root.next;
         */
        public void toArrayNode() {
            Link.this.retArray[Link.this.foot++] = this.data;   // 取出數據並保存在數組中
            if (this.next != null) {                        // 有後續元素
                this.next.toArrayNode();                    // 繼續下一個數據的取得
            }
        }
    }
    // ===================== 以上爲內部類 ===================
    private Node root;                              // 根節點定義
    private int count = 0 ;                             // 保存元素的個數
    private int foot = 0 ;                              // 節點索引
    private Book [] retArray ;                          // 返回的數組
    /**
     * 用戶向鏈表增加新的數據,在增加時要將數據封裝爲Node類,這樣纔可以匹配節點順序
     * @param data 要保存的數據
     */
    public void add(Book data) {                        // 假設不允許有null
        if (data == null) {                     // 判斷數據是否爲空
            return;                         // 結束方法調用
        }
        Node newNode = new Node(data);          // 要保存的數據
        if (this.root == null) {                    // 當前沒有根節點
            this.root = newNode;                // 保存根節點
        } else {                            // 根節點存在
            this.root.addNode(newNode);         // 交給Node類處理節點的保存
        }
        this.count ++ ;                     // 數據保存成功後保存個數加一
    }
    /**
     * 取得鏈表中保存的數據個數
     * @return 保存的個數,通過count屬性取得
     */
    public int size() {                         // 取得保存的數據量
        return this.count;
    }
    /**
     * 判斷是否是空鏈表,表示長度爲0,不是null
     * @return 如果鏈表中沒有保存任何數據則返回true,否則返回false
     */
    public boolean isEmpty() {
        return this.count == 0;
    }
    /**
     * 數據查詢操作,判斷指定數據是否存在,如果鏈表沒有數據直接返回false
     * @param data 要判斷的數據
     * @return 數據存在返回true,否則返回false
     */
    public boolean contains(Book data) {
        if (data == null || this.root == null) {        // 現在沒有要查詢的數據,根節點也不保存數據
            return false ;                      // 沒有查詢結果
        }
        return this.root.containsNode(data) ;       // 交由Node類查詢
    }
    /**
     * 根據索引取得保存的節點數據
     * @param index 索引數據
     * @return 如果要取得的索引內容不存在或者大於保存個數返回null,反之返回數據
     */
    public Book get(int index) {
        if (index > this.count) {                   // 超過了查詢範圍
            return null ;                       // 沒有數據
        }
        this.foot = 0 ;                     // 表示從前向後查詢
        return this.root.getNode(index) ;           // 查詢過程交給Node類
    }
    /**
     * 根據索引修改數據
     * @param index 要修改數據的索引編號
     * @param data 新的數據內容
     */
    public void set(int index, Book data) {
        if (index > this.count) {                       // 判斷是否超過了保存範圍
            return;                                 // 結束方法調用
        }
        this.foot = 0;                          // 重新設置foot屬性的內容,作爲索引出現
        this.root.setNode(index, data);                 // 交給Node類設置數據內容
    }
    /**
     * 鏈表數據的刪除操作,在刪除前要先使用contains()判斷鏈表中是否存在指定數據
     * 如果要刪除的數據存在,則首先判斷根節點的數據是否爲要刪除數據
     * 如果是,則將根節點的下一個節點作爲新的根節點
     * 如果要刪除的數據不是根節點數據,則將刪除操作交由Node類的removeNode()方法完成
     * @param data 要刪除的數據
     */
    public void remove(Book data) {
        if (this.contains(data)) {                  // 主要功能是判斷數據是否存在
            // 要刪除數據是否是根節點數據,root是Node類的對象,此處直接訪問內部類的私有操作
            if (data.equals(this.root.data)) {          // 根節點數據爲要刪除數據
                this.root = this.root.next;                 // 空出當前根節點
            } else {                                // 根節點數據不是要刪除數據
                // 此時根元素已經判斷過了,從第二個元素開始判斷,即第二個元素的上一個元素爲根節點
                this.root.next.removeNode(this.root, data);
            }
            this.count--;                       // 刪除成功後個數要減少
        }
    }
    /**
     * 將鏈表中的數據轉換爲對象數組輸出
     * @return 如果鏈表沒有數據返回null,如果有數據則將數據變爲對象數組後返回
     */
    public Book[] toArray() {
        if (this.root == null) {                        // 判斷鏈表是否有數據
            return null;                            // 沒有數據返回null
        }
        this.foot = 0;                          // 腳標清零操作
        this.retArray = new Book[this.count];           // 根據保存內容開闢數組
        this.root.toArrayNode();                    // 交給Node類處理
        return this.retArray;                       // 返回數組對象
    }
    public void clear() {
        this.root = null;                           // 清空鏈表
        this.count = 0;                         // 元素個數爲0
    }
}
public class LinkDemo {
    public static void main(String args[]) {
        Link all = new Link();                      // 創建鏈表對象
        all.add(new Book("Java開發實戰經典",79.8));       // 保存數據
        all.add(new Book("Oracle開發實戰經典",89.8)); // 保存數據
        all.add(new Book("Android開發實戰經典",99.8));    // 保存數據
        System.out.println("保存書的個數:" + all.size()) ;
        System.out.println(all.contains(new Book("Java開發實戰經典",79.8))) ;
        all.remove(new Book("Android開發實戰經典",99.8)) ;
        Book [] books = all.toArray() ;
        for (int x = 0 ; x < books.length ; x ++) {
             System.out.println(books[x].getInfo()) ;
        }
    }
}
class Province{
    private int pid;
    private String name;
    private Link cities=new Link();
    public Province(int pid,String name){
        this.pid=pid;
        this.name=name;
    }
    public boolean compare(Province province){
        if(this==province){
        return true;
    }
        if(province==null){
        return false;
    }
    if(this.pid==province.pid&&this.name.equals(province.name)){
        return true;
    }
        return false;
    }
    publicLink getCities(){
    return this.cities;
    }
    public String getInfo(){
        return "省份編號:"+this.pid+",名稱:"+this.name;
    }
}
class City{
    private intcid;
    private String name;
    private Province province;
    public City(intcid,String name){
        this.cid=cid;
        this.name=name;
    }
    public boolean compare(Citycity){
        if(this==city){
            return true;
        }
        if(city==null){
            return false;
        }
        if(this.cid==city.cid&&this.name.equals(city.name)&&this.province.compare(city.province)){
            return true;
        }
        return false;
    }
    publicvoid setProvince(Province province){
        this.province=province;
    }
    public Province getProvince(){
        return this.province;
    }
    public String getInfo(){
        return "城市編號:"+this.cid+",名稱"+this.name;
    }
}
classLink{
    private classNode{
    private City data;
    private Node next;
    public Node(City data){
        this.data=data;
    }
    publicvoid addNode(Node newNode){
        if(this.next==null){
            this.next=newNode;
        }else{
            this.next.addNode(newNode);
        }
    }
    public boolean containsNode(City data){
    if(data.compare(this.data)){
        return true;
    }else{
        if(this.next!=null){
            return this.next.containsNode(data);
        }else{
        return false;
    }
}
}
    public City getNode(int index){
    if(Link.this.foot++==index){
    return this.data;
    }else{
        return this.next.getNode(index);
           }
           }
publicvoid setNode(int index,City data){
if(Link.this.foot++==index){
this.data=data;
}else{
this.next.setNode(index,data);
}
}
publicvoid removeNode(Node previous,City data){
if(data.compare(this.data)){
previous.next=this.next;
}else{
this.next.removeNode(this,data);
}
}
publicvoid toArrayNode(){
Link.this.retArray[Link.this.foot++]=this.data;
if(this.next!=null){//有後續元素
this.next.toArrayNode();
}
}
}
private Node root;
private intcount=0;
private int foot=0;
private City[] retArray;//返回的數組
publicvoid add(City data){
Node newNode=new Node(data);
if(this.root==null){
this.root=newNode;
}else{
this.root.addNode(newNode);
}
count++;
}
publicintsize(){
return this.count;
}
public boolean isEmpty(){
return this.count==0;
}
public boolean contains(City data){
if(data==null||this.root==null){
return false;
}else{
return this.root.containsNode(data);
}
}
public City get(int index){
if(this.count<=index){
return null;
}
this.foot=0;
return this.root.getNode(index);
}
publicvoid set(int index,City data){
if(this.count<=index){
return;
}
this.foot=0;
this.root.setNode(index,data);
}
public City[] toArray(){
if(this.root==null){
return null;
}
this.foot=0;//需要腳標控制
this.retArray=new City[this.count];//根據保存內容開闢數組
this.root.toArrayNode();//交給Node類處理
return this.retArray;
}
publicvoid remove(City data){
if(this.contains(data)){
if(data.compare(this.root.data)){
this.root=this.root.next;
}else{
this.root.next.removeNode(this.root,data);
}
this.count--;
}
}
}
publicclassLinkDemo{
publicstaticvoid main(String args[]){
Link all=new Link();
//設置關係數據
//1、先準備好各自獨立的對象;
Province pro=new Province(1,"河北省");
Cityc1=new City(1001,"唐山");
Cityc2=new City(1002,"秦皇島");
Cityc3=new City(1003,"石家莊");
c1.setProvince(pro);
c2.setProvince(pro);
c3.setProvince(pro);
pro.getCities().add(c1);
pro.getCities().add(c2);
pro.getCities().add(c3);
//取出關係
System.out.println(pro.getInfo());
System.out.println("擁有的城市數量:"+pro.getCities().size());
pro.getCities().remove(c1);
Cityc[]=pro.getCities().toArray();
for(intx=0;x<c.length;x++){
System.out.println(c[x].getInfo());
}
}
}

繼承性

JAVA的繼承和C++基本相似
格式爲: class 子類 extends 父類 {}
其中 · 子類又被稱爲派生類;
· 父類又被稱爲超類(Super Class)。
不同之處和限制有
限制一:一個子類只能夠繼承一個父類,存在單繼承侷限。但可以使用多層繼承來彌補這個問題
結論:Java之中只允許多層繼承,不允許多重繼承,Java存在單繼承侷限。
限制二:在一個子類繼承的時候,實際上會繼承父類之中的所有操作(屬性、方法),但是需要注意的是,對於所有的非私有(no private)操作屬於顯式繼承(可以直接利用對象操作),而所有的私有操作屬於隱式繼承(間接完成)。

class A {
    private String msg ;
    public void setMsg(String msg) {
        this.msg = msg ;
    }
    public String getMsg() {
        return this.msg ;
    }
}
class B extends A {
    public void print() {
        System.out.println(msg) ;   // 錯誤: msg可以在A中訪問private
    }
}
public class Test {
    public static void main(String args[]) {
        B b = new B() ;
        b.setMsg("張三") ;
        System.out.println(b.getMsg()) ;
    }
}
//此時對於A類之中的msg這個私有屬性發現無法直接進行訪問,但是卻發現可以通過setter、getter方法間接的進行操作。

限制三:在繼承關係之中,如果要實例化子類對象,會默認先調用父類構造,爲父類之中的屬性初始化,之後再調用子類構造,爲子類之中的屬性初始化,即:默認情況下,子類會找到父類之中的無參構造方法。
如果父類沒有無參構造的話,則子類必須通過super()調用指定參數的構造方法。(所以要求一個簡單Java類一定要保留有一個無參構造方法。)

覆寫

方法的覆寫

當子類定義了和父類在方法名稱、返回值類型、參數類型及個數完全相同的方法的時候,稱爲方法的覆寫。
當一個類之中的方法被覆寫之後,如果實例化的是這個子類對象,則調用的方法就是被覆寫過的方法。
但是在進行方法覆寫的時候有一個點需要注意:被子類所覆寫的方法不能擁有比父類更嚴格的訪問控制權限,對於訪問控制權限現在已經接觸過三種:private < default(不寫) < public;

如果此時父類之中的方法是default權限,那麼子類覆寫的時候只能是default或public權限,而如果父類的方法是public,那麼子類之中方法的訪問權限只能是public。

別沒事幹自己給自己找事,以後只要是方法都是public,99.9999%可以解決問題。
當一個子類覆寫了一個父類方法的時候,那麼在這種情況下,子類要想調用父類的被覆寫過的方法,則在方法前要加上“super”。

//錯誤的覆寫
class A {
    private void print() {
        System.out.println("Hello World .") ;
    }
    public void fun() {
        this.print() ;
    }
}
class B extends A {
    public void print() {   // 不叫覆寫
        System.out.println("世界,你好!") ;
    }
}
public class Test {
    public static void main(String args[]) {
        B b = new B() ;
        b.fun() ;   // 方法從父類繼承而來
    }
}

從覆寫的概念上來講:現在父類的權限是private,而子類是public,的確是擴大了權限,而且方法的參數名稱及個數、返回值類型都一樣。這種情況在開發之中是絕對不會出現的,即:使用了private定義的操作都無法真正覆寫。

屬性的覆寫

基本上和C++的一樣


No. 區別 this super
1 定義 表示本類對象 表示父類對象
2 使用 本類操作:this.屬性、this.方法()、this() 父類操作:super.屬性、super.方法()、super()
3 調用構造 調用本類構造,要放在首行 子類調用父類構造,放在首行
4 查找範圍 先從本類查找,找不到查找父類 直接由子類查找父類

|No.|區別|重載 |覆寫|
|—|—|—|—|
1 | 英文單詞| Overloading | Override |
2 |定義| 方法名稱相同、參數的類型及個數不同 |方法名稱、參數類型及個數、返回值類型完全相同|
3 | 權限| 沒有權限要求| 被子類所覆寫的方法不能擁有比父類更嚴格的訪問控制權限|
4 |範圍 |發生在一個類之中| 發生在繼承關係類之中|


綜合實戰

定義一個整型數組的操作類,數組的大小由外部決定,用戶可以向數組之中增加數據,以及取得數組中的全部數據,也可以根據外部提供的數組的增長大小,在原本的數組之上擴充指定的容量,另外,在此類上派生兩個子類:
· 排序類:取得的數組內容是經過排序出來的結果;
· 反轉類:取得的數組內容是反轉出來的結果;

基礎父類

/*
定義一個數組操作類,其構造函數定義了數組長度
在這個程序裏面需要一個腳標,腳標用來記錄當前的數組已存的容量,當foot小於length說明還有容量可以存放數據


*/
class Array {   // 數組操作類
    private int [] data ;
    private int foot = 0 ;  // 控制腳標
    public Array(int len) { // 由外部傳遞大小
        if (len > 0) {
            this.data = new int [len] ;
        } else {
            this.data = new int [1] ;   // 維持一個大小
        }
    }
    public boolean add(int num) {
        if (this.foot < this.data.length) { // 有位置
            this.data[this.foot ++] = num ; // 保存數據
            return true ;
        }
        return false ;
    }
    public int [] getData() {
        return this.data ;
    }
    public void increment(int num) {//擴大數組長度操作
        int [] newArr = new int [this.data.length + num] ;
        System.arraycopy(this.data,0,newArr,0,this.data.length) ;
        this.data = newArr ;    // 改變引用
    }
}
public class Test {
    public static void main(String args[]) {
        Array arr = new Array(5) ;
        System.out.println(arr.add(8)) ;
        System.out.println(arr.add(10)) ;
        System.out.println(arr.add(2)) ;
        System.out.println(arr.add(5)) ;
        System.out.println(arr.add(3)) ;
        System.out.println(arr.add(9)) ;
        arr.increment(3) ;
        System.out.println(arr.add(20)) ;
        System.out.println(arr.add(10)) ;
        System.out.println(arr.add(30)) ;
        int result [] = arr.getData() ;
        for (int x = 0 ; x < result.length ; x ++) {
            System.out.println(result[x]) ;
        }
    }
}

排序類

//排序類與父類的不同在於返回數組的時候需要對數組進行一次排序,其餘基本相同
class SortArray extends Array {
    public SortArray (int len) {
        super(len) ;    //因爲父類裏面沒有無參構造,所以需要i手動 調用父類的有參構造
    }
    public int [] getData() {
        java.util.Arrays.sort(super.getData()) ; //系統給的數組的排序方法
        return super.getData() ; //使用父類的方法返回數組
    }
}
public class Test {
    public static void main(String args[]) {
        SortArray arr = new SortArray(5) ;
        System.out.println(arr.add(8)) ;
        System.out.println(arr.add(10)) ;
        System.out.println(arr.add(2)) ;
        System.out.println(arr.add(5)) ;
        System.out.println(arr.add(3)) ;
        System.out.println(arr.add(9)) ;
        arr.increment(3) ;
        System.out.println(arr.add(20)) ;
        System.out.println(arr.add(10)) ;
        System.out.println(arr.add(30)) ;
        int result [] = arr.getData() ;
        for (int x = 0 ; x < result.length ; x ++) {
            System.out.println(result[x]) ;
        }
    }
}

由於所有的操作方法是以父類操作爲主,所以在使用的時候此處只是換了一個類的名稱,其他的地方都沒有改變。這樣做可以保持客戶端操作的一致性。這也是編寫的原則之一

反轉類

class ReverseArray extends Array {
    public ReverseArray(int len) {
        super(len) ;
    }
    public int [] getData() {   // 覆寫
        int head = 0 ; //數組頭的腳標
        int tail = super.getData().length - 1 ; //數組尾的腳標
        int center = super.getData().length / 2 ;
        for (int x = 0 ; x < center ; x ++) {
            int temp = super.getData()[head] ;
            super.getData()[head] = super.getData()[tail] ;
            super.getData()[tail] = temp ;
            head ++ ;
            tail -- ;
        }
        return super.getData() ;
    }
}
public class Test {
    public static void main(String args[]) {
        ReverseArray arr = new ReverseArray(5) ;
        System.out.println(arr.add(8)) ;
        System.out.println(arr.add(10)) ;
        System.out.println(arr.add(2)) ;
        System.out.println(arr.add(5)) ;
        System.out.println(arr.add(3)) ;
        System.out.println(arr.add(9)) ;
        arr.increment(3) ;
        System.out.println(arr.add(20)) ;
        System.out.println(arr.add(10)) ;
        System.out.println(arr.add(30)) ;
        int result [] = arr.getData() ;
        for (int x = 0 ; x < result.length ; x ++) {
            System.out.println(result[x]) ;
        }
    }
}

在整個程序的開發之中,可以明顯感覺到,所有的操作都是圍繞着父類功能的擴充進行的,但是方法並沒有改變,所以在開發之中,父類的設計是最重要的,子類最好的繼承或者說是覆寫操作,都應該以父類的方法爲主。

final關鍵字

在Java中,final關鍵字表示的是一個終結器的概念,使用final可以定義類、方法、變量。
- 使用final定義的類不能有子類
- 使用final定義的方法不能被子類所覆寫
- 使用final定義的變量,就表示常量,常量在定義的時候必須設置默認值,並且無法修改
重點:public static final String INFO = “hello world” ; 此時爲全局常量
定義final常量的時候每個單詞的字母都要大寫。

多態

多態是面向對象的最後一個主要特徵,它本身主要分爲兩個方面:
- 方法的多態性:重載與覆寫
|- 重載:同一個方法名稱,根據不同的參數類型及個數可以完成不同的功能;
|- 覆寫:同一個方法,根據操作的子類不同,所完成的功能也不同。
- 對象的多態性:父子類對象的轉換。
|- 向上轉型:子類對象變爲父類對象,格式:父類 父類對象 = 子類實例,自動;(用的比較多)
|- 向下轉型:父類對象變爲子類對象,格式:子類 子類對象 = (子類) 父類實例,強制;

轉型

class A {
    public void print() {
        System.out.println("A、public void print(){}") ;
    }
}
class B extends A {
    public void print() {   // 方法覆寫
        System.out.println("B、public void print(){}") ;
    }
}
//向上轉型
public class Test {
    public static void main(String args[]) {
        A a = new B() ; // 向上轉型
        a.print() ;
    }
}
//此時輸出B、public void print(){}
//向下轉型
public class Test {
    public static void main(String args[]) {
        A a = new B() ; // 向上轉型
        B b = (B) a ;   // 向下轉型
        b.print() ;
    }
}

錯誤例子:

public class Test {
    public static void main(String args[]) {
        A a = new A() ; // 沒有轉型
        B b = (B) a ;   // 向下轉型,java.lang.ClassCastException: A cannot be cast to B
        b.print() ;
    }
}

以上類轉換異常,即:兩個沒有關係的類互相發生了對象的強制轉型。
在現實中:
- 如果兩個人真的有關係,可以分配遺產;
- 如果兩個人真的沒有關係,不能分配遺產。
- 所以在程序中,如果要實現轉型。需要具有關係(即進行向上轉型)
轉型因素:
- 在實際的工作之中,對象的向上轉型爲主要使用,80%,向上轉型之後,所有的方法以父類的方法爲主,但是具體的實現,還是要看子類是否覆寫了此方法;
- 向下轉型,10%,因爲在進行向下轉型操作之前,一定要首先發生向上轉型,以建立兩個對象之間的聯繫,如果沒有這種聯繫,是不可能發生向下轉型的,一旦發生了運行中就會出現“ClassCastException”,當需要調用子類自己特殊定義方法的時候,才需要向下轉型;
- 不轉型,10%,在一些資源較少的時候,例如:移動開發。

判斷某一個對象是否是某一個類的實例,只需要使用instanceof關鍵字即可,此操作的語法如下:
對象 instanceof 類 返回boolean型

//驗證instanceof關鍵字
public class Test {
    public static void main(String args[]) {
        A a = new A() ;
        System.out.println(a instanceof A) ;
        System.out.println(a instanceof B) ;
        if (a instanceof B) {
            B b = (B) a ;
            b.getB() ;
        }
    }
}

爲了日後的操作方便,在編寫代碼的時候,儘量不要去執行向下轉型操作。子類儘量不要去擴充新的方法名稱(父類沒有的方法名稱),依據父類定義的操作完善方法。

class Person {
    private String name ;
    private int age ;
    public Person(String name,int age) {
        this.name = name ;
        this.age = age ;
    }
    public String getInfo() {
        return "姓名:" + this.name + ",年齡:" + this.age ;
    }
}
class Student extends Person {  // Student類繼承了Person類
    private String school ; // 子類的屬性
    public Student(String name,int age,String school) {
        super(name,age) ;
        this.school = school ;
    }
    public String getInfo() {
        return super.getInfo() + ",學校:" + this.school ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Person per = new Student("張三",20,"清華大學") ;
        System.out.println(per.getInfo()) ;
    }
}

使用對象轉型的好處

  • 參數的類型就得到了統一
//不使用轉型
class A {
    public void print() {
        System.out.println("A、public void print(){}") ;
    }
}
class B extends A {
    public void print() {   // 方法覆寫
        System.out.println("B、public void print(){}") ;
    }
}
class C extends A {
    public void print() {   // 方法覆寫
        System.out.println("C、public void print(){}") ;
    }
}
public class Test {
    public static void main(String args[]) {
        fun(new B()) ;
        fun(new C()) ;
    }
    public static void fun(B b) {
        b.print() ;
    }
    public static void fun(C c) {
        c.print() ;
    }
}
//此程序在於如果有無數的子類則fun方法需要無數次定義
//使用向上轉型
public class Test {
    public static void main(String args[]) {
        fun(new B()) ;
        fun(new C()) ;
    }
    public static void fun(A a) {
        a.print() ;
    }
}
//此時參數的類型就得到了統一,就算有再多的子類出現,方法或者是類也不需要進行修改了

一個類不能去繼承一個已經實現好的類,只能繼承抽象類或實現接口。
以後所有的代碼之中,都會存在對象的轉型問題,並且向上轉型居多。

抽象類

基本概念

普通類就是一個完善的功能類,可以直接產生對象並且可以使用,裏面的方法都是帶有方法體的,而抽象類之中最大的特點是包含了抽象方法,而抽象方法是隻聲明而未實現(沒有方法體)的方法,而抽象方法定義的時候要使用abstract關鍵字完成,而抽象方法一定要在抽象類之中,抽象類要使用abstract關鍵字聲明。
定位很像C++裏的虛基類,基本差不多

abstract class A {
    private String info = "Hello World ." ;
    public void print() {
        System.out.println(info) ;
    }
    public abstract void get() ;    // 只聲明沒有方法體
}

抽象類的使用原則:

  • 抽象類必須有子類,使用extends繼承,一個子類只能繼承一個抽象類;
  • 子類(如果不是抽象類)則必須覆寫抽象類之中的全部抽象方法;
  • 抽象類對象可以使用對象的向上轉型方式,通過子類來進行實例化操作。

抽象類疑問:

  • 抽象類能否使用final定義?
    不能,因爲抽象類必須有子類,final定義的類太監類,不能有子類;
  • 抽象類之中能否包含構造方法?
    可以,因爲抽象類之中除了包含抽象方法之外,還包含了普通方法和屬性,而屬性一定要在構造方法執行完畢之後纔可以進行初始化操作;
  • 抽象類之中能否不包含抽象方法?
    可以,抽象類之中可以沒有抽象方法,但是反過來講,如果有抽象方法,則一定是抽象類,即使抽象類之中沒有抽象方法,也不能夠被直接實例化;
  • 抽象類能否使用static聲明?
    如果定義的是外部抽象類,則不能夠使用static聲明,可是如果定義的是內部抽象類,那麼這個內部的抽象類使用了static聲明之後,就表示是一個外部的抽象類。
abstract class A {
    private String info = "Hello World ." ;
    static abstract class B {// 外部類
        public abstract void print() ;
    }
}
class Impl extends A.B {
    public void print() {
        System.out.println("Hello MLDN .") ;
    }
}
public class Test {
    public static void main(String args[]) {
        A.B b = new Impl() ;
        b.print() ;
    }
}

應用:模板設計模式

現在有三種類型:狗、機器人、人;
- 狗具備三種功能:吃、睡、跑;
- 機器人具備兩個功能:吃、工作;
- 人具備四個功能:吃、睡、跑、工作。

要求設計一個程序,可以讓這三類不同的類型,進行工作。

abstract class Action {
    public static final int EAT = 1 ;
    public static final int SLEEP = 3 ;
    public static final int WORK = 5 ;
    public static final int RUN = 7 ; //利用全局常量來實現輸入的合理,數字使用1,3,5,7是因爲兩兩相加不會混淆
    public void order(int flag) {
        switch (flag) {
            case EAT :
                this.eat() ;
                break ;
            case SLEEP:
                this.sleep() ;
                break ;
            case WORK :
                this.work() ;
                break ;
            case RUN :
                this.run() ;
                break ;
            case EAT + SLEEP + RUN :
                this.eat() ;
                this.sleep() ;
                this.run() ;
                break ;
            case EAT + WORK :
                this.eat() ;
                this.work() ;
                break ;
            case EAT + SLEEP + RUN + WORK :
                this.eat() ;
                this.sleep() ;
                this.run() ;
                this.work() ;
                break ;
            }
    }
    public abstract void eat() ;
    public abstract void sleep() ;
    public abstract void run() ;
    public abstract void work() ;
}
class Dog extends Action {
    public void eat() {
        System.out.println("小狗在吃。") ;
    }
    public void sleep() {
        System.out.println("小狗在睡。") ;
    }
    public void run() {
        System.out.println("小狗在跑步。") ;
    }
    public void work() {}
}
class Robot extends Action {
    public void eat() {
        System.out.println("機器人喝油。") ;
    }
    public void sleep() {}
    public void run() {}
    public void work() {
        System.out.println("機器人在工作。") ;
    }
}
class Person extends Action {
    public void eat() {
        System.out.println("人在吃飯。") ;
    }
    public void sleep() {
        System.out.println("人在睡覺。") ;
    }
    public void run() {
        System.out.println("人在跑步。") ;
    }
    public void work() {
        System.out.println("人在工作。") ;
    }
}
public class Test {
    public static void main(String args[]) {
        Action act1 = new Dog() ;
        act1.order(Action.EAT + Action.SLEEP + Action.RUN) ;
        Action act2 = new Robot() ;
        act2.order(Action.EAT + Action.WORK) ;
    }
}
//實現了參數統一性

接口

接口屬於一種特殊的類,如果一個類定義的時候全部由抽象方法和全局常量所組成的話,那麼這種類就稱爲接口,但是接口是使用interface關鍵字進行定義的。
相當於C++裏的抽象類

interface A {   // 定義接口
    public static final String INFO = "Hello World ." ;
    public abstract void print() ;
}
interface B {
    public abstract void get() ;
}

接口之中,也同樣存在了抽象方法,很明顯,接口對象無法直接進行對象的實例化操作,那麼接口的使用原則如下:
- 每一個接口必須定義子類,子類使用implements關鍵字實現接口;
- 接口的子類(如果不是抽象類)則必須覆寫接口之中所定義的全部抽象方法;
- 利用接口的子類,採用對象的向上轉型方式,進行接口對象的實例化操作。
- 一個子類可以同時實現多個接口,但是隻能繼承一個父類。即解決了抽象類的單繼承問題

interface A {   // 定義接口
    public static final String INFO = "Hello World ." ;
    public abstract void print() ;
}
interface B {
    public abstract void get() ;
}
class X implements A,B {    // 同時實現了兩個接口
    public void print() {   // 方法覆寫
        System.out.println("Hello World .") ;
    }
    public void get() {
        System.out.println(INFO) ;
    }
}
public class Test {
    public static void main(String args[]) {
        A a = new X() ;
        B b = new X() ;
        a.print() ;
        b.get() ;
    }
//一個類現在即要實現接口又要繼承抽象類的話,則應該採用先繼承後實現的方式完成

interface A {   // 定義接口
    public static final String INFO = "Hello World ." ;
    public abstract void print() ;
}
interface B {
    public abstract void get() ;
}
abstract class C {
    public abstract void fun() ;
}
class X extends C implements A,B {  // 同時實現了兩個接口
    public void print() {   // 方法覆寫
        System.out.println("Hello World .") ;
    }
    public void get() {
        System.out.println(INFO) ;
    }
    public void fun() {
        System.out.println("世界,你好!") ;
    }
}
public class Test {
    public static void main(String args[]) {
        A a = new X() ;
        B b = new X() ;
        C c = new X() ;
        a.print() ;
        b.get() ;
        c.fun() ;
    }
}

接口之中的全部組成就是抽象方法和全局常量,那麼在開發之中以下的兩種定義接口的最終效果是完全一樣的:(省略static final 和abstract)

完整定義:

interface A {   // 定義接口
    public static final String INFO = "Hello World ." ;
    public abstract void print() ;
}

簡化定義:

interface A {   // 定義接口
    public String INFO = "Hello World ." ;
    public void print() ;
}
  • 接口之中的訪問權限只有一種:public,即:定義接口方法的時候就算沒有寫上public,那麼最終也是public。
  • 每一個抽象類都可以實現多個接口,但是反過來講,一個接口卻不能繼承抽象類
  • 一個接口卻可以同時繼承多個接口,以實現接口的多繼承操作。
  • 在開發之中,內部類是永遠不會受到概念限制的,在一個類中可以定義內部類,在一個抽象類之中也可以定義抽象內部類,在一個接口裏面也可以定義內部抽象類或內部接口,但是從實際的開發來講,用戶自己去定義內部抽象類或內部接口的時候是比較少見的(Android開發中見過內部接口),而且在定義內部接口的時候如果使用了static,表示是一個外部接口。
interface A {
    public void printA() ;
    static interface B {    // 外部接口
        public void printB() ;
    }
}
class X implements A.B {
    public void printB() {
        System.out.println("Hello World .") ;
    }
}
public class Test {
    public static void main(String args[]) {
        A.B temp = new X() ;
        temp.printB() ;
    }
}

以上對於接口的概念並不是很難理解,但是需要強調的是,在實際的開發之中,接口有三大主要功能:
· 制訂操作標準;
· 表示一種能力;
· 將服務器端的遠程方法視圖暴露給客戶端。

使用接口 定義標準

利用接口將繼承於同一個接口的對象集合起來

interface USB {                     // 定義標準一定就是接口,開發中先定義接口
    public void start();                // USB設備開始工作
    public void stop();             // USB設備停止工作
}
class Computer {
    public void plugin(USB usb) {       // 插入USB接口設備(子類對象)
        usb.start();                    // 開始工作
        usb.stop();                     // 停止工作
    }
}
class Flash implements USB {        // 實現USB接口
    public void start() {
        System.out.println("U盤開始使用");
    }
    public void stop() {
        System.out.println("U盤停止使用");
    }
}
class Print implements USB {        // 定義打印機
    public void start() {
        System.out.println("打印機開始工作");
    }
    public void stop() {
        System.out.println("打印機停止工作");
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Computer com = new Computer();      // 實例化計算機類
        com.plugin(new Flash());                // 插入USB接口設備
        com.plugin(new Print());                // 插入USB接口設備
    }
}

工廠設計模式

interface Fruit {
    public void eat() ;
}
class Apple implements Fruit {
    public void eat() {
        System.out.println("吃蘋果。") ;
    }
}
class Orange implements Fruit {
    public void eat() {
        System.out.println("吃橘子。") ;
    }
}
public class Test {
    public static void main(String args[]) {
        Fruit f = new Apple() ;
        f.eat() ;
    }
}

主方法或者是主類是一個客戶端,客戶端的操作應該越簡單越好。但是現在的程序之中,有一個最大的問題:客戶端之中,一個接口和一個固定的子類綁在一起了。
在本程序之中,最大的問題在於耦合上(耦合太過緊密),發現在主方法之中,一個接口和一個子類緊密耦合在一起,這種方式比較直接,可以簡單的理解爲由:A===> B,但是這種緊密的方式不方便於維護,所以後來使用了A====> C====> B,中間經歷了一個過渡,這樣一來B去改變,C去改變,但是A不需要改變,就好比JAVA的JVM一樣:程序===>JVM===>操作系統。

  interface Fruit {                 // 定義接口
    public void eat();              // 定義抽象方法
  }
  class Apple implements Fruit {        // 定義接口子類
    public void eat() {             // 覆寫抽象方法
        System.out.println("*** 吃蘋果。");
    }
  }
  class Orange implements Fruit {       // 定義接口子類
    public void eat() {             // 覆寫抽象方法
        System.out.println("*** 吃橘子。");
    }
  }
  class Factory {                                   // 定義工廠類,此類不提供屬性
    /**
     * 取得指定類型的接口對象
     * @param className 要取得的類實例化對象標記
     * @return 如果指定標記存在,則Fruit接口的實例化對象,否則返回null
     */
    public static Fruit getInstance(String className) {
        if ("apple".equals(className)) {            // 是否是蘋果類
            return new Apple();
        } else if ("orange".equals(className)) {    // 是否是橘子類
            return new Orange();
        } else {
            return null;
        }
    }
  }
  public class TestDemo {
    public static void main(String args[]) {
        Fruit f = Factory.getInstance("orange");    // 通過工廠類取得指定標記的對象
        f.eat();                                    // 調用接口方法
    }
  }

代理設計模式

代理負責完成與真實業務有關的所有輔助性操作

interface Network{                          // 定義Network接口
    public void browse() ;                  // 定義瀏覽的抽象方法
}
class Real implements Network{          // 真實的上網操作
    public void browse(){                   // 覆寫抽象方法
        System.out.println("上網瀏覽信息") ;
    }
}
class Proxy implements Network{         // 代理上網
    private Network network ;
    public Proxy(Network network){      // 設置代理的真實操作
        this.network = network ;            // 設置代理的子類
    }
    public void check(){                        // 與具體上網相關的操作
        System.out.println("檢查用戶是否合法");
    }
    public void browse(){
        this.check() ;                      // 可以同時調用多個與具體業務相關的操作
        this.network.browse() ;             // 調用真實上網操作
    }
}
public class TestDemo {
    public static void main(String args[]){
        Network net = null ;                // 定義接口對象
        net = new Proxy(new Real()) ;       // 實例化代理,同時傳入代理的真實操作
        net.browse() ;                      // 客戶只關心上網瀏覽一個功能
    }
}

Object類

定義了一個類,如果沒有默認繼承任何一個父類的話,則默認將繼承Object類。
所以如果在日後的開發之中,一個操作可能接收所有類的對象,那麼使用Object作爲參數最合適。
除此之外,對於任意的一個簡單Java類而言,理論上講應該覆寫Object類之中的三個方法:
- 取得對象信息:public String toString();
- 對象比較:public boolean equals(Object obj);
- 取得哈希碼:public int hashCode()。

取得對象信息:toString()

對象直接輸出默認調用了Object類之中的toString()方法,但是默認的toString()方法有一個特點:爲了適用於所有的子類,那麼在toString()默認情況下就是輸出了對象地址.所以每一個子類可以進行相應的修改進行適應。(其實也就是簡單java類裏的GetInfo方法)

class Person {
    private String name ;
    private int age ;
    public Person(String name,int age) {
        this.name = name ;
        this.age = age ;
    }
    public String toString() {  // 方法覆寫
        return "姓名:" + this.name + ",年齡:" + this.age ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Person per = new Person("張三",20) ;
        System.out.println(per) ;   // Person@ 1f 6226
    }
}

對象比較:equals()

在Object類之中,默認的equals()方法實現比較的是兩個對象的內存地址數值,但是並不符合於真正的對象比較需要。對象比較之前也寫過,但是之前是自己定義了一個新的方法名稱,今天可以給出標準的方法名稱:equals()。

class Person {
    private String name ;
    private int age ;
    public Person(String name,int age) {
        this.name = name ;
        this.age = age ;
    }
    public String toString() {  // 方法覆寫
        return "姓名:" + this.name + ",年齡:" + this.age ;
    }
    public boolean equals(Object obj) {
        if (this == obj) {
            return true ;
        }
        if (obj == null) {
            return false ;
        }
        if (! (obj instanceof Person)) {    // 不是本類對象
            return false ;
        }
        // 因爲name和age屬性是在Person類中定義,而Object類沒有
        Person per = (Person) obj ;
        if (this.name.equals(per.name) && this.age == per.age) {
            return true ;
        }
        return false ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Person per1 = new Person("張三",20) ;
        Person per2 = new Person("張三",20) ;
        System.out.println(per1.equals("Hello")) ;
        System.out.println(per1.equals(per2)) ;
    }
}

使用Object接收所有的引用數據類型

Object是所有類的父類,那麼Object類可以接收所有類的對象,但是在Java設計的時候,考慮到引用數據類型的特殊性,所以Object類實際上是可以接收所有引用數據類型的數據,這就包括了數組、接口、類。

//使用Object類接收數組,數組和Object沒有任何明確的定義關係
public class TestDemo {
    public static void main(String args[]) {
        Object obj = new int [] {1,2,3} ;   // 接收數組
        if (obj instanceof int []) {
            int [] data = (int []) obj ;    // 向下轉型
            for (int x = 0 ; x < data.length ; x ++) {
                System.out.println(data[x]) ;
            }
        }
    }
}

interface Message {
}
class MessageImpl implements Message {  // 定義接口子類
    public String toString() {
        return "New Message : Hello World ." ;
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Message msg = new MessageImpl() ;   // 向上轉型
        Object obj = msg ;  // 使用Object接收接口對象,向上轉型
        Message temp = (Message) obj ;  // 向下轉型
        System.out.println(temp) ;  // toString()
    }
}

寵物醫院

“實現一個寵物商店的模型,一個寵物商店可以保存多個寵物的信息(主要屬性爲:名字、年齡),可以實現寵物的上架、下架、模糊查詢的功能”。
1.定義寵物的標準,即定義一個寵物接口

interface Pet {  // 定義一個寵物的標準
    /**
     * 取得寵物的名字
     * @return 寵物名字信息
     */
    public String getName();
    /**
     * 取得寵物的年齡
     * @return 寵物年齡信息
     */
    public int getAge(); 
}

2.定義一個寵物醫院

class PetShop {                         // 一個寵物商店要保存有多個寵物信息
    private Link pets = new Link();     // 保存的寵物信息,使用了鏈表
    /**
     * 新的寵物類型上架操作,向鏈表中保存寵物信息
     * @param pet 要上架的寵物信息
     */
    public void add(Pet pet) { 
        this.pets.add(pet);                 // 向鏈表中保存數據
    }
    /**
     * 寵物下架操作,通過鏈表刪除保存的信息,需要對象比較equals()方法的支持
     * @param pet 要刪除的寵物信息
     */
    public void delete(Pet pet) {
        this.pets.remove(pet);          // 從鏈表中刪除寵物信息
    }
    // 模糊查詢一定是返回多個內容,不知道多少個,返回Link即可
    /**
     * 寵物數據的模糊查詢,首先要取得全部保存的寵物信息
     * 然後採用循環的方式依次取出每一種寵物信息,並且對名稱進行判斷
     * @param keyWord 模糊查詢關鍵字
     * @return 寵物信息通過Link類型返回,如果有指定查詢關鍵字的寵物信息則通過Link集合返回,否則返回null
     */
    public Link search(String keyWord) {
        Link result = new Link();       // 保存結果
        // 將集合變爲對象數組的形式返回,因爲集合保存的是Object
        // 但是真正要查詢的數據在Pet接口對象的getName()方法的返回值
        Object obj[] = this.pets.toArray();
        for (int x = 0; x < obj.length; x++) {
            Pet p = (Pet) obj[x];       // 向下轉型找到具體的寵物對象
            if (p.getName().contains(keyWord)) {    // 查詢到了
                result.add(p);          // 保存滿足條件的結果
            }
        }
        return result;
    }
}

3.定義寵物貓子類

class Cat implements Pet {              // 如果不實現接口無法保存寵物信息
    private String name;
    private int age;
    public Cat(String name, int age) {
          this.name = name;
          this.age = age;
    }
    public boolean equals(Object obj) {
          if (this == obj) {
            return true;
          }
          if (obj == null) {
            return false;
        }
          if (!(obj instanceof Cat)) {
            return false;
          }
          Cat c = (Cat) obj;
          if (this.name.equals(c.name) && this.age == c.age) {
            return true;
        }
          return false;
    }
    public String getName() {           // 覆寫接口中的方法
          return this.name;
    }
    public int getAge() {               // 覆寫接口中的方法
          return this.age;
    }
    public String toString() {
          return "貓的名字:" + this.name + ",年齡:" + this.age;
    }
    // 其餘的setter、getter、無參構造略

4.定義寵物狗子類

class Dog implements Pet {           // 如果不實現接口無法保存寵物信息
    private String name;
    private int age;
    public Dog(String name, int age) {
          this.name = name;
          this.age = age;
    }
    public boolean equals(Object obj) {
          if (this == obj) {
           return true;
          }
          if (obj == null) {
           return false;
          }
          if (!(obj instanceof Cat)) {
           return false;
          }
          Dog c = (Dog) obj;
          if (this.name.equals(c.name) && this.age == c.age) {
           return true;
          }
          return false;
    }
    public String getName() {           // 覆寫接口中的方法
          return this.name;
    }
    public int getAge() {               // 覆寫接口中的方法
          return this.age;
    }
    public String toString() {
          return "狗的名字:" + this.name + ",年齡:" + this.age;
    }
    // 其餘的setter、getter、無參構造略
}

5.測試類

public class TestDemo {
    public static void main(String args[]) {
          PetShop shop = new PetShop() ;        // 實例化寵物商店
          shop.add(new Cat("波斯貓",1)) ;      // 增加寵物
          shop.add(new Cat("暹羅貓",2)) ;      // 增加寵物
          shop.add(new Cat("波米拉貓",1)) ;     // 增加寵物
          shop.add(new Dog("鬆獅",1)) ;           // 增加寵物
          shop.add(new Dog("波爾多",2)) ;      // 增加寵物
          shop.delete(new Cat("波米拉貓",9)) ;  // 刪除寵物信息
          Link all = shop.search("波") ;         // 關鍵字檢索
          Object obj [] = all.toArray() ;           // 將結果轉換爲對象數組輸出
          for (int x = 0 ; x < obj.length ; x ++) {
            System.out.println(obj[x]) ;
          }
    }
}

匿名內部類

interface Message {                         // 定義接口
    public void print();
}
public class TestDemo {
    public static void main(String args[]) {
        fun(new Message() {                 // 直接實例化接口對象
            public void print() {               // 匿名內部類中覆寫print()方法
                System.out.println("Hello World !");
            }
        });                                     // 傳遞匿名內部類實例化
    }
    public static void fun(Message msg) {   // 接收接口對象
        msg.print();
    }
}

包裝類

java 一共提供了8種包裝類:byte(Byte)、short(Short)、int(Integer)、long(Long)、float(Float)、double(Double)、boolean(Boolean)、char(Character)
而這八種包裝類又分爲兩大陣營:
- 數值型(Number子類):Byte、Short、Integer、Float、Double、Long;
- 對象型(Object子類):Boolean、Character。
對於Number的子類,就必須觀察出Number類之中定義的方法:byteValue()、intValue()、doubleValue()、shortValue()、floatValue()、doubleValue(),就是從包裝的類之中取得所包裝的數值。

裝箱與拆箱

在基本數據類型和包裝類之間的轉換操作之中分爲兩個重要概念:
- 裝箱操作:將基本數據類型變爲包裝類,稱爲裝箱,包裝類的構造方法;
- 拆箱操作:將包裝類變爲基本數據類型,稱爲拆箱,各個類的xxValue()方法。

//int和Integer
public class TestDemo { 
    public static void main(String args[]) { 
        Integer var = new Integer(15) ; // 裝箱
        int result = var.intValue() ;   // 拆箱
        System.out.println(result * result) ; 
    } 
} 
//boolean和Boolean
public class TestDemo { 
    public static void main(String args[]) { 
        Boolean var = new Boolean(true) ;   // 裝箱
        boolean result = var.booleanValue() ;   // 拆箱
        if (result) { 
            System.out.println("Hello World .") ; 
        } 
    } 
} 

JDK 1.5之後,Java提供了自動的裝箱和拆箱機制,並且包裝類的對象可以自動的進行數學計算

public class TestDemo { 
    public static void main(String args[]) { 
        Integer var = 15 ;  // 自動裝箱
        int result = var ;  // 自動拆箱
        System.out.println(++ var * result) ; 
    } 
} 
****************************
public class TestDemo { 
    public static void main(String args[]) { 
        Boolean var = true ;    // 自動裝箱
        if (var) {  // 自動拆箱後操作
            System.out.println("Hello World .") ; 
        } 
    } 
} 

此時Object類可以接收基本數據類型的數據

public class TestDemo { 
    public static void main(String args[]) { 
        Object obj = 15 ;   // int --> 自動裝箱 --> Object 
        int result = (Integer) obj ; // Object --> 包裝類 --> 自動拆箱
        System.out.println(result * result) ; 
    } 
} 

此時包裝類類似於Sring類一樣,有入池和開闢空間(Integer y = 10 )兩張方法(Integer x = new Integer(10) ),需要考慮到equal()和==的區別

數據轉型

包裝類種提供了可以將字符串變爲指定的基本數據類型的方法
- Integer類:public static int parseInt(String s);
- Double類:public static double parseDouble(String s);
- Boolean類:public static boolean parseBoolean(String s);
- Character這個包裝類之中,並沒有提供一個類似的parseCharacter(),因爲字符串String類之中提供了一個charAt()方法,可以取得指定索引的字符,而且一個字符的長度就是一位。

public class TestDemo { 
    public static void main(String args[]) { 
        String str = "16" ; 
        int result = Integer.parseInt(str) ;    // String --> int 
        System.out.println(result * result) ; 
    } 
} 

進行該項操作時,字符串中的全部內容需要都爲數字
使用boolean型包裝類時,需要注意的是隻要不是ture統一按照false處理

基本類型變爲字符串

利用String類的方法,public static String valueOf(數據類型 b)

public class TestDemo { 
    public static void main(String args[]) { 
        int num = 100 ; 
        String str = String.valueOf(num) ;  // int --> String 
        System.out.println(str.length()) ; 
    } 
} 

異常處理

如果希望程序出現了異常之後,程序依然可以正常的完成的話,那麼就可以使用異常處理的代碼

try { 
    可能出現異常的語句 ; 
} [ catch (異常類型 異常對象) { 
    處理異常 ; 
} catch (異常類型 異常對象) { 
    處理異常 ; 
} ... ] [finally { 
    不管是否出現異常,都執行此代碼 ; 
}] 

例子:

public class TestDemo { 
    public static void main(String args[]) { 
        System.out.println("1、除法計算開始。") ; 
        try { 
            int result = 10 / 0 ; // 異常
            System.out.println("2、除法計算結果:" + result) ; // 之前語句有異常,此語句不再執行
        } catch (ArithmeticException e) { 
            System.out.println(e) ; // 異常處理:輸出錯誤信息,java.lang.ArithmeticException: / by zero 
        } 
        System.out.println("3、除法計算結束。") ; 
    } 
} 
/*
結果:
1、除法計算開始。
java.lang.ArithmeticException: / by zero 
3、除法計算結束。
*/

一般來說,爲了使錯誤信息更加具體,會調用printStackTrace()方法進行異常信息的打印.
finally關鍵字的代碼使之無論是否出現錯誤都會執行。

當程序可能出現多種異常,此時的代碼只能處理一種,又不能所有的異常都一條條地判斷
所有的異常類型最高的繼承類是Throwable,通過doc文檔可以發現在Throwable下有兩個子類: Error和Exception
面試題:
請解釋Error和Exception的區別?
- Error:指的是JVM錯誤,這個時候的程序並沒有執行,無法處理;
- Exception:指的是程序之中出現的錯誤信息,可以進行異常處理,主要關心Exception。
請解釋java之中的異常處理流程。
1、 如果程序之中產生了異常,那麼會自動的由JVM根據異常的類型,實例化一個指定異常類的對象;
2、 如果這個時候程序之中沒有任何的異常處理操作,則這個異常類的實例化對象將交給JVM進行處理,而JVM的默認處理方式就是進行異常信息的輸出,而後中斷程序執行;
3、 如果程序之中存在了異常處理,則會由try語句捕獲產生的異常類對象;
4、 與try之後的每一個catch進行匹配,如果匹配成功,則使用指定的catch進行處理,如果沒有匹配成功,則向後面的catch繼續匹配,如果沒有任何的catch匹配成功,則這個時候將交給JVM執行默認處理;
5、 不管是否有異常都會執行finally程序,如果此時沒有異常,執行完finally,則會繼續執行程序之中的其他代碼,如果此時有異常沒有能夠處理(沒有一個catch可以滿足),那麼也會執行finally,但是執行完finally之後,將默認交給JVM進行異常的信息輸出,並且程序中斷;

catch捕獲異常類型的操作,就和方法接收參數是一樣的,那麼按照對象多態性來講,所有的異常類都是Exception的子類,那麼這個時候,實際上所有的異常都可以使用Exception進行接收。

public class TestDemo { 
    public static void main(String args[]) { 
        System.out.println("1、除法計算開始。") ; 
        try { 
            int x = Integer.parseInt(args[0]) ; 
            int y = Integer.parseInt(args[1]) ; 
            int result = x / y ; 
            System.out.println("2、除法計算結果:" + result) ; 
        } catch (Exception e) { 
            e.printStackTrace() ; 
        } finally { 
            System.out.println("不管是否出現異常都執行") ; 
        } 
        System.out.println("3、除法計算結束。") ; 
    } 
} 

throws關鍵字

thrwos關鍵字主要是在方法定義上使用的,表示的是此方法之中不進行異常的處理,而交給被調用處處理。

class MyMath { 
    public int div(int x,int y) throws Exception { 
        return x / y ; 
    } 
} 
//此時地div()方法拋出了一個異常,表示此時所有的異常都交由調用處處理
class MyMath { 
    public int div(int x,int y) throws Exception { 
        return x / y ; 
    } 
} 
public class TestDemo { 
    public static void main(String args[]) { 
        try { //在調用throws聲明方法的時候,一定要使用異常處理操作進行異常的處理,這個是屬於強制性的處理
            System.out.println(new MyMath().div(10,0)) ; 
        } catch (Exception e) { 
            e.printStackTrace() ; 
        } 
    } 
} 
//主方法本身也屬於方法,那麼實際上在主方法上也可以繼續使用throws進行異常的拋出
class MyMath { 
    public int div(int x,int y) throws Exception { 
        return x / y ; 
    } 
} 
public class TestDemo { 
    public static void main(String args[]) throws Exception { //這個時候表示的是將異常繼續向上拋,交給JVM進行異常的處理。
        System.out.println(new MyMath().div(10,0)) ; 
    } 
} 

throw關鍵字

之前的所有異常類對象都是由JVM自動進行實例化操作的,而現在用戶也可以自己手工的拋出一個實例化對象,就通過throw完成了。

public class TestDemo { 
    public static void main(String args[]){ 
        try { 
            throw new Exception("拋着玩的,你管不着。") ; 
        } catch (Exception e) { 
            e.printStackTrace() ; 
        } 
    } 
} 

異常處理的標準格式

現在覺得有兩個內容實在是沒有用處:finally、throw。
現在要求定義一個div()方法,而這個方法有如下的一些要求:
    · 在進行除法操作之前,輸出一行提示信息,可以理解爲大爺開門;
    · 在除法操作執行完畢後,輸出一行提示信息,可以理解爲大爺關門;
    · 如果中間產生了異常,則應該交給被調用處來進行處理,譬如:xxOver了。
class MyMath { 
    // 出現異常要交給被調用處出,使用throws 
    public int div(int x,int y) throws Exception { 
        System.out.println("===== 計算開始 =====") ;    // 資源打開
        int result = 0 ; 
        try { 
            result = x / y ;    // 除法計算
        } catch (Exception e) { 
            throw e ;   // 向上拋
        } finally { 
            System.out.println("===== 計算結束 =====") ;    // 資源關閉
        } 
        return result ; 
    } 
} 
public class TestDemo { 
    public static void main(String args[]){ 
        try { 
            System.out.println(new MyMath().div(10,0)) ; 
        } catch (Exception e) { 
            e.printStackTrace() ; 
        } 
    } 
} 

簡化以上代碼

class MyMath { 
    // 出現異常要交給被調用處出,使用throws 
    public int div(int x,int y) throws Exception { 
        System.out.println("===== 計算開始 =====") ;    // 資源打開
        int result = 0 ; 
        try { 
            result = x / y ;    // 除法計算
        } finally { 
            System.out.println("===== 計算結束 =====") ;    // 資源關閉
        } 
        return result ; 
    } 
} 
public class TestDemo { 
    public static void main(String args[]){ 
        try { 
            System.out.println(new MyMath().div(10,0)) ; 
        } catch (Exception e) { 
            e.printStackTrace() ; 
        } 
    } 
} 
//此時這段代碼不catch錯誤,而是留給到主方法再進行處理,以上不帶有catch的操作在開發之中並不建議使用。永遠的標準格式:try…catch、finally、throws、throw一起使用。

RuntimeException

Java之中明確規定了,對於RuntimeException的異常類型可以有選擇性的來進行處理,在開發之中,如果沒有處理,那麼出現異常之後將交給JVM默認進行處理。
面試題:請解釋一下RuntimeException和Exception的區別?請列舉出幾個常見的RuntimeException;
- RuntimeException是Exception的子類;
- Exception定義了必須處理的異常,而RuntimeException定義的異常可以選擇性的進行處理;
- 常見的RuntimeException:NumberFormatException、ClassCastException、NullPointerException、ArithmeticException、ArrayIndexOutOfBoundsException。

斷言:assert

斷言指的是程序執行到某行之後,其結果一定是預期的結果,而在JDK 1.4之後增加了一個assert關鍵字。

public class TestDemo { 
    public static void main(String args[]){ 
        int x = 10 ; 
        // 假設經過了若干操作
        assert x == 30 : "x的內容不是30" ; 
        System.out.println(x) ; 
    } 
} 

默認情況下,Java之中的斷言,不會在正常執行的代碼中出現,如果要想啓用斷言,則應該增加一些選項:
java -ea TestDemo

自定義異常類

在Java之中本身已經提供了大量的異常類型,但是在開發之中,這些異常類型根本就不能滿足於開發的需要。所以在一些系統架構之中往往會提供一些新的異常類型,來表示一些特殊的錯誤,而這種操作就稱爲自定義異常類,而要想實現這種自定義異常類,那麼可以讓一個類繼承Exception或RuntimeException。

範例:自定義異常類
class MyException extends Exception {   // 自定義異常類
    public MyException(String msg) { 
        super(msg) ; 
    } 
} 
public class TestDemo { 
    public static void main(String args[]) throws Exception { 
        throw new MyException("自己的異常類") ; 
    } 
} 

Eclipse

  • 輸入main,Alt + / + 回車 : 自動輸入主方法
  • 輸入sysout Alt + / :自動輸入System.out.println();
  • ALT + /:代碼自動補充提示;
  • CTRL + 1:進行錯誤代碼糾正提示;
  • CTRL + D:刪除當前行代碼;
  • CTRL + SHIFT + O:組織導入所需要的包.類;
  • CTRL + SHIFT + F:格式化代碼顯示;
  • CTRL + /:註釋或者取消註釋當前行代碼;
  • CTRL + SHIFT + L:所有快捷鍵列表;
  • CTRL + H:強搜索代碼;

JDK多線程

可變參數

替代了原本用數組的形式來封裝傳遞數據的結構

package cn.mldn.demo;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        System.out.println(add(new int[] { 1, 2, 3 }));
    }
    public static int add(int[] data) {
        int sum = 0;
        for (int x = 0; x < data.length; x++) {
            sum += data[x];
        }
        return sum;
    }
}

格式:

public [static] [final] 返回值類型 方法名稱 (參數類型 ... 變量) { // 雖然定義方式改變了,但本質上還是個數組
    [return [返回值] ;] 
} 
package cn.mldn.demo;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        System.out.println(add(new int[] { 1, 2, 3 }));
        System.out.println(add(1, 2, 3));
        System.out.println(add());
    }
    public static int add(int ... data) {   // 可變參數,其功能代替了數組
        int sum = 0;
        for (int x = 0; x < data.length; x++) {//操作其實仍是一樣的 都還是按照數組來做
            sum += data[x];
        }
        return sum;
    }
}

以上的操作在開發之中不建議使用

foreach輸出

foreach並不是新的概念,最早是在.NET中提出來的,所謂的foreach可以理解爲增強性的for循環


//最早的for循環。
package cn.mldn.demo;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        int data[] = new int[] { 1, 2, 3, 4, 5, 6 };
        int sum = 0;
        for (int x = 0; x < data.length; x++) {
            sum += data[x];
        }
        System.out.println(sum);
    }
}

格式:

for (數據類型 變量 : 數組 | 集合) { 
    // 操作代碼
} 
//新的for循環
package cn.mldn.demo;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        int data[] = new int[] { 1, 2, 3, 4, 5, 6 };
        int sum = 0;
        for (int x : data) { // 自動循環,將每一個元素賦給x
            sum += x;
        }
        System.out.println(sum);
    }
}

能看懂就行 不建議使用

靜態導入

//原本方法
package cn.mldn.util;
public class MyMath {
    public static int add(int x, int y) {
        return x + y;
    }
    public static int sub(int x, int y) {
        return x - y;
    }
    public static int mul(int x, int y) {
        return x * y;
    }
    public static int div(int x, int y) {
        return x / y; 
    }
}
//以上爲MyMath類

package cn.mldn.demo;
import cn.mldn.util.MyMath;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("加法操作:" + MyMath.add(10, 20));
        System.out.println("減法操作:" + MyMath.sub(20, 10));
    }
}

//使用MyMath.add的形式對add進行調用

在JDK1.5之後可以使用靜態導入,即如果一個類之中的全部方法都是static型的,則可以使用如下的語法進行靜態導入:
import static 包.類.* ;
表示的是將這個指定類之中的全部方法導入進來,最後就好象這些方法全部是在主類之中定義的方法一樣。

package cn.mldn.demo;
import static cn.mldn.util.MyMath.*;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("加法操作:" + add(10, 20));
        System.out.println("減法操作:" + sub(20, 10));
    }
}

依舊不建議使用

泛型

題目要求:
定義一個表示座標的操作類(Point),這個類可以表示三種類型的座標:
· 整數座標:x = 10、y = 20;
· 小數座標:x = 10.1、y = 20.3;
· 字符串數據:x = “東經100度”、y = “北緯20度”。

按照之前的思路:
保存以上的數據,一定需要定義x和y兩個屬性,而這兩個屬性可以接收三種數據類型,那麼只能使用Object類來定義會比較合適,這樣會發生如下的幾種轉換關係:
· 整數:int –>自動裝箱爲Integer –>向上轉型爲Object;
· 小數:double –> 自動裝箱爲Double –> 向上轉型爲Object;
· 字符串:字符串 –> 向上轉型爲Object。

class Point {
    private Object x ;
    private Object y ;
    public void setX(Object x) {
        this.x = x;
    }
    public void setY(Object y) {
        this.y = y;
    }
    public Object getX() {
        return x;
    }
    public Object getY() {
        return y;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        // 一層設置
        Point point = new Point() ;
        point.setX(10) ;
        point.setY(20) ;
        // 一層取出
        int x = (Integer) point.getX() ;
        int y = (Integer) point.getY() ;
        System.out.println("X的座標是:" + x + ",Y的座標是:" + y);
    }
}
//錯誤例子
public class TestDemo {
    public static void main(String[] args) throws Exception {
        // 一層設置
        Point point = new Point() ;
        point.setX(10) ;    // 此處設置成int型(Integer型)
        point.setY("北緯20度") ;
        // 一層取出
        String x = (String) point.getX() ;
        String y = (String) point.getY() ;
        System.out.println("X的座標是:" + x + ",Y的座標是:" + y);
    }
}

/*
此時數字10被裝箱成了Integer,可以使用Object接收,從技術上而言,本操作沒有問題,但是從實際來講,數據是有錯誤的,因爲沒有統一,所以在取得數據並且執行向下轉型的過程之中就會出現如下的錯誤提示信息:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

*/

此時可以使用泛型
泛型:類之中操作的屬性或方法的參數的類型不在定義的時候聲明,而是在使用的時候動態設置。
這項功能其實就是c++中的類模板和函數模板

class Point<T> {    // T:Type
    private T x ;
    private T y ;
    public void setX(T x) {
        this.x = x;
    }
    public void setY(T y) {
        this.y = y;
    }
    public T getX() {
        return x;
    }
    public T getY() {
        return y;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        // 一層設置
        Point<String> point = new Point<String>() ;
        point.setX("東經100度") ;
        point.setY("北緯20度") ;
        // 一層取出
        String x = point.getX() ;
        String y = point.getY() ;
        System.out.println("X的座標是:" + x + ",Y的座標是:" + y);
    }
}

此時沒有了向下轉型的這些操作關係,那麼程序就避免了安全性的問題,而且如果設置的類型不統一,在程序編譯的過程之中也是可以很好的解決了,直接會報出語法錯誤。
而當用戶在使用Point類聲明對象的時候沒有設置泛型,那麼程序在編譯的過程之中會出現警告信息,而且爲了保證程序不出現錯誤,所有的類型都將使用Object進行處理。使用泛型可以很好的解決數據類型的統一問題。
但是在此處需要提醒的是,JDK 1.5和JDK 1.7在定義泛型的時候是稍微有些區別的。

//範例:JDK 1.5的時候聲明泛型對象操作
    Point<String> point = new Point<String>() ;
//以上是JDK 1.5的語法,在聲明對象和實例化對象的時候必須都同時設置好泛型類型。
//範例:JDK 1.7的時候簡化了
    Point<String> point = new Point<>() ;
//這個時候實例化對象時的泛型類型就通過聲明時的泛型類型來定義了。

通配符

class Message<T> {
    private T info;
    public void setInfo(T info) {
        this.info = info;
    }
    public T getInfo() {
        return info;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Message<String> msg = new Message<String>();
        msg.setInfo("Hello World .");
        print(msg); // 引用傳遞
    }
    public static void print(Message<String> temp) {
        System.out.println(temp.getInfo()); // 只是輸出
    }
}

若此時定義的泛型類型不是String了呢?例如:換成了int

public class TestDemo {
    public static void main(String[] args) throws Exception {
        Message<Integer> msg = new Message<Integer>();
        msg.setInfo(100);
        print(msg); // 無法進行引用傳遞
    }
    public static void print(Message<String> temp) {
        System.out.println(temp.getInfo()); // 只是輸出
    }
}
/*此時print()方法無法再接收Message<Integer>對象的引用,因爲這個方法只能夠接收Message<String>對象的引用
而這個方法又不能換成Message<Integer>進行重載,因爲方法重載的時候觀察的不是泛型類型,而是類的名稱,或者是說數據類型的,那麼現在就可以發現,這個給出了泛型類之後,就相當於將一個類又劃分成了若干個不同的小類型。
而如果在定義print()如果不寫上泛型類型,雖然現在的程序可算是正常了,但是現在在print()方法的參數上出現了警告,問題就在於方法操作中,沒有類型限制了.
*/

以上方法 可以使用通配符進行解決

    public static void print(Message<?> temp) {
        System.out.println(temp.getInfo()); // 只是輸出
    }

由於“?”出現的情況較多,尤其是在學習一些類庫的時候,所以對於?就記住一點,表示任意類型,如果有參數返回的時候也是這個“?”,就當成Object進行理解。

既然現在談到了Object,那麼現在實際上又有了另外一個問題:對於所有的子類,都是Object子類,那麼如果對於之前的程序使用Message能不能接收所有的泛型類型呢?

        Message<String> msg = new Message<String>();
        Message<Object> temp = msg ;    // 無法接收
//因爲Object要比String的範圍大,把我去超市買的東西理解爲“new Message<String>”,把整個超市的商品理解爲“Message<Object>”,那麼如果真的可以直接轉換,就變成了,我買了整個超市的商品。

在通配符“?”上又衍生出了兩個子符號:

  • 設置泛型的上限:? extends 類;
    • 例如:? extends Number,表示只能是Number或者是Number的子類Integer等;
  • 設置泛型的下限:? super 類;
    • 例如:? super String,表示只能是String或者是String的父類(Object)。
//設置泛型上限
package cn.mldn.demo;
class Message<T extends Number> {
    private T info;
    public void setInfo(T info) {
        this.info = info;
    }
    public T getInfo() {
        return info;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Message<Integer> msg = new Message<Integer>();  // Integer是Number的子類
        msg.setInfo(100);
        print(msg); // 引用傳遞
    }
    public static void print(Message<?> temp) {
        System.out.println(temp.getInfo()); // 只是輸出
    }
}
//設置泛型下限,在方法的參數上使用
package cn.mldn.demo;
class Message<T> {
    private T info;
    public void setInfo(T info) {
        this.info = info;
    }
    public T getInfo() {
        return info;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Message<String> msg = new Message<String>();    // Integer是Number的子類
        msg.setInfo("Hello World .");
        print(msg); // 引用傳遞
    }
    // 只能是String的泛型或者是Object的泛型使用
    public static void print(Message<? super String> temp) {
        System.out.println(temp.getInfo()); // 只是輸出
    }
}

泛型接口

接口也是可以進行泛型定義的,而使用泛型定義的接口可以稱爲泛型接口。

interface Message<T> {  // 泛型接口
    public String echo(T msg) ; 
}

泛型接口的實現,在Java之中有兩種方式:
方法一:在子類上繼續定義泛型,同時此泛型在接口上繼續使用

package cn.mldn.demo;
interface Message<T> {  // 泛型接口
    public String echo(T msg) ; 
}
class MessageImpl<T> implements Message<T> {//子類繼承泛型
    public String echo(T msg) {
        return "ECHO : " + msg;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Message<String> msg = new MessageImpl<String>() ;
        System.out.println(msg.echo("張三"));
    }
}

方法二:在子類上設置具體類型

package cn.mldn.demo;
interface Message<T> {  // 泛型接口
    public String echo(T msg) ; 
}
class MessageImpl implements Message<String> {//在繼承句這裏註明設置類型
    public String echo(String msg) {
        return "ECHO : " + msg;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Message<String> msg = new MessageImpl() ;
        System.out.println(msg.echo("張三"));
    }
}

泛型方法

package cn.mldn.demo;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Integer result[] = get(1, 2, 3);
        for (int temp : result) { // 連自動拆箱都包含了
            System.out.println(temp);
        }
    }
    public static <T> T[] get(T... args) { // T的類型由方法調用的時候來決定
        return args;
    }
}

枚舉

//多例設計模式

package cn.mldn.demo;
class Color {
    private static final Color RED = new Color("紅色") ;
    private static final Color GREEN = new Color("綠色") ;
    private static final Color BLUE = new Color("藍色") ;
    private String title ;
    private Color(String title) {
        this.title = title ;
    }
    public String toString() {
        return this.title ;
    }
    public static Color getInstance(int ch) {
        switch(ch) {
        case 0 :
            return RED ;
        case 1 :
            return GREEN ;
        case 2 :
            return BLUE ;
        default :
            return null ;
        }
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Color c = Color.getInstance(0) ;
        System.out.println(c);
    }
}

JDK 1.5之後對於多例設計模式有了一些新的改進,使用了一個新的關鍵字來進行定義 —— 枚舉(就是一個簡化了的多例設計模式),枚舉使用enum關鍵字來進行定義。

package cn.mldn.demo;
enum Color {
    RED,GREEN,BLUE ;
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Color c = Color.RED ;
        System.out.println(c);
    }
}

利用枚舉實現的多例設計模式會更加的簡單直白一些,但是在Java之中,枚舉並不是一個新的類型,嚴格來講,每一個使用enum定義的類實際上都屬於一個類繼承了Enum父類而已.
java.lang.Enum類定義:

public abstract class Enum<E extends Enum<E>>
extends Object 
implements Comparable<E>, Serializable 

而在Enum類之中定義了兩個方法:
- 取得枚舉的序號:public final int ordinal();
- 取得枚舉的名稱:public final String name()。

package cn.mldn.demo;
enum Color {
    RED,GREEN,BLUE ;
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Color c = Color.RED ;
        switch(c) {
        case RED:
            System.out.println("紅色");
            break ;
        case GREEN :
            System.out.println("綠色");
            break ;
        case BLUE :
            System.out.println("藍色");
            break ;
        }
    }
}

總結:關於switch允許的操作類型
- 在JDK 1.5之前,switch只能夠操作int或char型數據;
- 在JDK 1.5之後,switch可以操作enum型;
- 在JDK 1.7之後,switch可以操作String型。
enum和Enum的關係?
enum是JDK 1.5之後定義的新關鍵字,主要用於定義枚舉類型,在Java之中每一個使用enum定義的枚舉類型實際上都表示一個類默認繼承了Enum類而已。

附:必背代碼

鏈表代碼

  class Link {                              // 鏈表類,外部能夠看見的只有這一個類
    private class Node {                    // 定義的內部節點類
        private Object data;                // 要保存的數據
        private Node next;              // 下一個節點引用
        public Node(Object data) {          // 每一個Node類對象都必須保存相應的數據
            this.data = data;
        }
        /**
         * 設置新節點的保存,所有的新節點保存在最後一個節點之後
         * @param newNode 新節點對象
         */
        public void addNode(Node newNode) {
            if (this.next == null) {            // 當前的下一個節點爲null
                this.next = newNode ;       // 保存節點
            } else {                            // 向後繼續保存
                this.next.addNode(newNode) ;
            }
        }
        /**
         * 數據檢索操作,判斷指定數據是否存在
         * 第一次調用(Link):this = Link.root
         * 第二次調用(Node):this = Link.root.next
         * @param data 要查詢的數據
         * @return 如果數據存在返回true,否則返回false
         */
        public boolean containsNode(Object data) {
            if (data.equals(this.data)) {           // 當前節點數據爲要查詢的數據
                return true;                    // 後面不再查詢了
            } else {                            // 當前節點數據不滿足查詢要求
                if (this.next != null) {            // 有後續節點
                    return this.next.containsNode(data);    // 遞歸調用繼續查詢
                } else {                        // 沒有後續節點
                    return false;               // 沒有查詢到,返回false
                }
            }
        }
        /**
         * 根據索引取出數據,此時該索引一定是存在的
         * @param index 要取得數據的索引編號
         * @return 返回指定索引節點包含的數據
         */
        public Object getNode(int index) {
            // 使用當前的foot內容與要查詢的索引進行比較,隨後將foot的內容自增,目的是下次查詢方便
            if (Link.this.foot++ == index) {            // 當前爲要查詢的索引
                return this.data;                   // 返回當前節點數據
            } else {                                // 繼續向後查詢
                return this.next.getNode(index);    // 進行下一個節點的判斷
            }
        }
        /**
         * 修改指定索引節點包含的數據
         * @param index 要修改的索引編號
         * @param data 新數據
         */
        public void setNode(int index, Object data) {
            // 使用當前的foot內容與要查詢的索引進行比較,隨後將foot的內容自增,目的是下次查詢方便
            if (Link.this.foot++ == index) {        // 當前爲要修改的索引
                this.data = data;               // 進行內容的修改
            } else {
                this.next.setNode(index, data); // 繼續下一個節點的索引判斷
            }
        }
        /**
         * 節點的刪除操作,匹配每一個節點的數據,如果當前節點數據符合刪除數據
         * 則使用“當前節點上一節點.next = 當前節點.next”方式空出當前節點
         * 第一次調用(Link),previous = Link.root、this = Link.root.next
         * 第二次調用(Node),previous = Link.root.next、this = Link.root.next.next
         * @param previous 當前節點的上一個節點
         * @param data 要刪除的數據
         */
        public void removeNode(Node previous, Object data) {
            if (data.equals(this.data)) {               // 當前節點爲要刪除節點
                previous.next = this.next;          // 空出當前節點
            } else {                                    // 應該向後繼續查詢
                this.next.removeNode(this, data);   // 繼續下一個判斷
            }
        }
        /**
         * 將節點中保存的內容轉化爲對象數組
         * 第一次調用(Link):this = Link.root;
         * 第二次調用(Node):this = Link.root.next;
         */
        public void toArrayNode() {
            Link.this.retArray[Link.this.foot++] = this.data;   // 取出數據並保存在數組中
            if (this.next != null) {                            // 有後續元素
                this.next.toArrayNode();                        // 繼續下一個數據的取得
            }
        }
    }
    // ===================== 以上爲內部類 ===================
    private Node root;                          // 根節點定義
    private int count = 0 ;                     // 保存元素的個數
    private int foot = 0 ;                          // 節點索引
    private Object [] retArray ;                    // 返回的數組
    /**
     * 用戶向鏈表增加新的數據,在增加時要將數據封裝爲Node類,這樣纔可以匹配節點順序
     * @param data 要保存的數據
     */
    public void add(Object data) {              // 假設不允許有null
        if (data == null) {                     // 判斷數據是否爲空
            return;                             // 結束方法調用
        }
        Node newNode = new Node(data);      // 要保存的數據
        if (this.root == null) {                    // 當前沒有根節點
            this.root = newNode;                // 保存根節點
        } else {                                // 根節點存在
            this.root.addNode(newNode);     // 交給Node類處理節點的保存
        }
        this.count ++ ;                         // 數據保存成功後保存個數加一
    }
    /**
     * 取得鏈表中保存的數據個數
     * @return 保存的個數,通過count屬性取得
     */
    public int size() {                             // 取得保存的數據量
        return this.count;
    }
    /**
     * 判斷是否是空鏈表,表示長度爲0,不是null
     * @return 如果鏈表中沒有保存任何數據則返回true,否則返回false
     */
    public boolean isEmpty() {
        return this.count == 0;
    }
    /**
     * 數據查詢操作,判斷指定數據是否存在,如果鏈表沒有數據直接返回false
     * @param data 要判斷的數據
     * @return 數據存在返回true,否則返回false
     */
    public boolean contains(Object data) {
        if (data == null || this.root == null) {    // 現在沒有要查詢的數據,根節點也不保存數據
            return false ;                      // 沒有查詢結果
        }
        return this.root.containsNode(data) ;   // 交由Node類查詢
    }
    /**
     * 根據索引取得保存的節點數據
     * @param index 索引數據
     * @return 如果要取得的索引內容不存在或者大於保存個數,返回null,反之返回數據
     */
    public Object get(int index) {
        if (index > this.count) {                   // 超過了查詢範圍
            return null ;                       // 沒有數據
        }
        this.foot = 0 ;                         // 表示從前向後查詢
        return this.root.getNode(index) ;       // 查詢過程交給Node類
    }
    /**
     * 根據索引修改數據
     * @param index 要修改數據的索引編號
     * @param data 新的數據內容
     */
    public void set(int index, Object data) {
        if (index > this.count) {                   // 判斷是否超過了保存範圍
            return;                             // 結束方法調用
        }
        this.foot = 0;                          // 重新設置foot屬性的內容,作爲索引出現
        this.root.setNode(index, data);         // 交給Node類設置數據內容
    }
    /**
     * 鏈表數據的刪除操作,在刪除前要先使用contains()判斷鏈表中是否存在指定數據
     * 如果要刪除的數據存在,則首先判斷根節點的數據是否爲要刪除數據
     * 如果是,則將根節點的下一個節點作爲新的根節點
     * 如果要刪除的數據不是根節點數據,則將刪除操作交由Node類的removeNode()方法完成
     * @param data 要刪除的數據
     */
    public void remove(Object data) {
        if (this.contains(data)) {                  // 主要功能是判斷數據是否存在
            // 要刪除數據是否是根節點數據,root是Node類的對象,此處直接訪問內部類的私有操作
            if (data.equals(this.root.data)) {      // 根節點數據爲要刪除數據
                this.root = this.root.next;             // 空出當前根節點
            } else {                                // 根節點數據不是要刪除數據
                // 此時根元素已經判斷過了,從第二個元素開始判斷,即第二個元素的上一個元素爲根節點
                this.root.next.removeNode(this.root, data);
            }
            this.count--;                           // 刪除成功後個數要減少
        }
    }
    /**
     * 將鏈表中的數據轉換爲對象數組輸出
     * @return 如果鏈表沒有數據,返回null,如果有數據,則將數據變爲對象數組後返回
     */
    public Object[] toArray() {
        if (this.root == null) {                        // 判斷鏈表是否有數據
            return null;                            // 沒有數據,返回null
        }
        this.foot = 0;                              // 腳標清零操作
        this.retArray = new Object[this.count];     // 根據保存內容開闢數組
        this.root.toArrayNode();                    // 交給Node類處理
        return this.retArray;                       // 返回數組對象
    }
    /**
     * 清空鏈表數據
     */
    public void clear() {
        this.root = null;                           // 清空鏈表
        this.count = 0;                             // 元素個數爲0
    }
  }

  ```
### 對象向上轉型
```java
class A {
    public void print() {
        System.out.println("A、public void print(){}");
    }
}
class B extends A {     // 定義A的子類
    public void print() {   // 此時子類覆寫了父類中的print()方法
        System.out.println("B、public void print(){}");
    }
}
class C extends A {     // 定義A的子類
    public void print() {   // 此時子類覆寫了父類中的print()方法
        System.out.println("C、public void print(){}");
    }
}
public class TestDemo {
    public static void main(String args[]) {
        fun(new B()) ;  // 對象向上轉型,等價於:A a = new B() ;
        fun(new C()) ;  // 對象向上轉型,等價於:A a = new C() ;
    }
    /**
     * 接收A類或其子類對象,不管傳遞哪個對象都要求調用print()方法
     * @param a A類實例化對象
     */
    public static void fun(A a) {
        a.print();
    }
}




<div class="se-preview-section-delimiter"></div>

向下轉型,調用子類中的特殊功能

class A {
    public void print() {
        System.out.println("A、public void print(){}");
    }
}
class B extends A {             // 定義A的子類
    public void print() {           // 此時子類覆寫了父類中的print()方法
        System.out.println("B、public void print(){}");
    }
    public void funB() {
        System.out.println("B、擴充的funB()方法");
    }
}
public class TestDemo {
    public static void main(String args[]) {
        fun(new B()) ;          // 對象向上轉型
    }
    public static void fun(A a) {
        a.print() ;
        if (a instanceof B) {   // 如果a對象是B類的實例
            B b = (B) a;        // 向下轉型
            b.funB();           // 調用子類擴充的方法
        }
    }
}




<div class="se-preview-section-delimiter"></div>

### 實現對象的比較
“`java
//以貓類爲例子
public boolean equals(Object obj) {
if (this == obj) {//是否是同一個對象
return true;
}
if (obj == null) {//是否爲空
return false;
}
if (!(obj instanceof Cat)) {//輸入的內容是否同一個類
return false;
}
Cat c = (Cat) obj;
if (this.name.equals(c.name) && this.age == c.age) {//對象的各個屬性進行對比
return true;
}
return false;
}

“`

工廠設計模式(Factory)

interface Fruit {                   // 定義接口
    public void eat();              // 定義抽象方法
}
class Apple implements Fruit {      // 定義接口子類
    public void eat() {             // 覆寫抽象方法
        System.out.println("*** 吃蘋果。");
    }
}
class Orange implements Fruit {     // 定義接口子類
    public void eat() {             // 覆寫抽象方法
        System.out.println("*** 吃橘子。");
    }
}
class Factory {                                 // 定義工廠類,此類不提供屬性
    /**
     * 取得指定類型的接口對象
     * @param className 要取得的類實例化對象標記
     * @return 如果指定標記存在,則Fruit接口的實例化對象,否則返回null
     */
    public static Fruit getInstance(String className) {
        if ("apple".equals(className)) {            // 是否是蘋果類
            return new Apple();
        } else if ("orange".equals(className)) {    // 是否是橘子類
            return new Orange();
        } else {
            return null;
        }
    }
}
public class TestDemo {
    public static void main(String args[]) {
        Fruit f = Factory.getInstance("orange");    // 通過工廠類取得指定標記的對象
        f.eat();                                    // 調用接口方法
    }
}




<div class="se-preview-section-delimiter"></div>

代理設計模式(Proxy)

interface Network{                          // 定義Network接口
    public void browse() ;                  // 定義瀏覽的抽象方法
}
class Real implements Network{          // 真實的上網操作
    public void browse(){                   // 覆寫抽象方法
        System.out.println("上網瀏覽信息") ;
    }
}
class Proxy implements Network{         // 代理上網
    private Network network ;
    public Proxy(Network network){      // 設置代理的真實操作
        this.network = network ;            // 設置代理的子類
    }
    public void check(){                        // 與具體上網相關的操作
        System.out.println("檢查用戶是否合法");
    }
    public void browse(){
        this.check() ;                      // 可以同時調用多個與具體業務相關的操作
        this.network.browse() ;             // 調用真實上網操作
    }
}
public class TestDemo {
    public static void main(String args[]){
        Network net = null ;                // 定義接口對象
        net = new Proxy(new Real()) ;       // 實例化代理,同時傳入代理的真實操作
        net.browse() ;                      // 客戶只關心上網瀏覽一個功能
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章