一、java基本知識點
java是由SUN公司在1995年推出的,在2009年SUN公司又被甲骨文公司收購,所以甲骨文公司獲得java版權。其底層是由C語言寫的,java分為三個體系:
- JavaSE,即Java Platform Standard Edition,java平台標准版。
- JavaEE,即Java Platform Enterprise Edition,java平台企業版。
- JavaME,即Java Platform Micro Edition,java平台微型版。
其中JavaSE標准版更多的是基礎語法,而JavaEE是用於企業的,擴展了其他的一些功能,可以說JavaSE是JavaEE的子集。而JavaME用的很少,不做討論。
另外,java有下面的特性比較重要:
- 簡單。 其語法和C++接近,但是沒有用操作符重載、多繼承、指針等比較復雜的概念,並且提供了自動的垃圾收集機制,和JavaScript類似。
- 面向對象。 java最大的特點就是面向對象。提供了類、接口、繼承等概念。
- 分布式語言。支持Internet應用的開發,在基本的Java應用編程接口中有一個應用編程接口(java net)。
- 中立的體系結構。java程序(后綴為java的文件)在Java平台上被編譯為體系結構中立的字節碼格式(后綴為class的文件),然后可以在實現這個java平台的任何系統中運行。
- 支持多線程。和JavaScript的單線程不同,java語言支持多線程,通過Thread類來創建。
二、java開發工具以及安裝配置
(1)jvm、jre、jdk、sdk、eclipse區別和聯系。
JDK(Java Development Kit)是Java語言的開發工具包(SDK)。在JDK的安裝目錄下有一個jre目錄,里面有兩個文件夾bin和lib,在這里可以認為bin里的就是jvm,lib中則是jvm工作所需要的類庫,而jvm和lib合起來就成為jre。
JRE(Java Runtime Environment)即java運行環境,包含JVM標准實現以及Java核心類庫。JRE是Java的運行環境,並不是一個開發環境,所以沒有包含任何開發工具(如編譯器和調試器)。
JVM(Java VIrtual Machine)即java虛擬機,JVM是一種用於計算設備的規范,是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。
(參考自文章:https://blog.csdn.net/songkai320/article/details/51819046)
如上,所以JDK是整個Java的核心,而JRE是Java程序必須的環境的集合,包含了JVM標准實現和Java核心類庫。JVM是整個java實現跨平台的最核心的部分,能夠運行以Java語言寫的程序。
而eclipse是方便我們寫java程序的開發工具,它不是必須的,就像我們寫前端html頁面,你可以使用記事本來寫,當然也可以使用sublime這種開發工具來寫。
(2)java開發環境配置
我們可以在oracle公司網站上下載jdk,進入之后選擇與本機操作系統對應的jdk文件即可,且在安裝jdk的同時會提示安裝jre,直接安裝即可。
在cmd中運行 java -version、java、javac幾個命令,出現了相關信息,沒有報錯,就說明環境變量配置成功。
我在安裝java環境的時候,首先安裝到了D盤新建的Java中,然后中間又在安裝什么沒有看清楚,安裝到了c盤program files下面的java中。安裝完成之后cmd中輸入java --version是可以成功的,輸入java也顯示了很多內容,但是輸入javac時不能正確識別,然后,我將 D:\java\bin 也加入了環境變量,就都可以識別了。如下所示:
如上,如果javac和java兩者都成功運行,那么基本的java環境就搭好了,如果想要運行文件,只需要用一個記事本寫java程序然后通過命令行運行即可。
工具下載可以在網站 https://down.eeyes.net 上下載,更加正規,不存在有病毒的可能。
(3)JAVA開發工具
Eclipse是免費開源的java IDE,是絕大多數開發者的選擇,選擇Eclipse IDE for Java Developers即可。其他的編輯器也很多,比如notepad++以及netbeans等。這里我們還是推薦使用Eclipse。
而如果要開發安卓app,一般使用的是 Android Studio,因為相對來說Eclipse更加臃腫,所以開發安卓推薦 Android Studio。
三、第一個例子
我們隨意打開一個編輯器,創建FOO.java文件,文件中的代碼如下所示:
public class FOO { public static void main(String[] args) { System.out.println("hello world!"); } }
然后因為我們這里還沒有使用IDE(如eclipse),所以可以使用命令行編譯運行,如下:
即我們首先使用javac命令(即java compile)進行編譯文件,然后使用java命令來執行文件。
注意:類名必須和文件名保持一致,即該文件名必須為FOO.java。
接下來,我們使用eclipse創建一個java程序,如下:
先創建一個java project:
然后我們創建一個class:
然后在創建class時,需要右鍵src,package就自動創建好了,然后結果如下:
即首先創建一個foo工程,在src文件並列的有JRE System Library,然后在src下創建一個foo包,這個包中創建一個Foo.java類即可,類下代碼如下:
package foo; public class FOO { public static void main (String[] args) { System.out.println("wayne"); } }
然后點擊運行,如下:
最后結果如下:
即在console控制台中打印出了最終結果。
四、java基礎語法
(1)基礎部分
- 大小寫敏感。
- 類名首字母要大寫,且類名使用的是大駝峰式寫法。
- 方法名必須要以小寫字母開頭。
- 源文件名必須要和類名相同,后綴是java,如果文件名和類名不同則會導致編譯錯誤。
- 主文件入口:所有的Java程序都必須由 public static void main(String []args)方法開始執行。
(2)變量
java中的變量有局部變量、類變量(靜態變量)以及成員變量(非靜態變量)。其中局部變量就是一般函數中的變量,而靜態變量是類中的變量,但是靜態變量的特點是用static修飾了,且不需要通過實例來引用這個變量,而非靜態變量是類中沒有使用static修飾的變量,並且非靜態變量必須要通過實例來引用。
(3)關鍵字
java中的關鍵詞和其他語言關鍵字也都是大同小異,比如private、protected、public、class、extends、interface、static、break、for、case、do、try、void、goto等等等等。
(4)注釋
注釋和JavaScript一樣,使用 // 作為單行注釋,使用 / * */作為多行注釋。當然,這些注釋行和所有的空白行都會被java編譯器在編譯的過程中直接忽略。
(5)接口
在java中,接口可以理解為對象之間相互通信的協議。接口在繼承中扮演着非常重要的角色,接口只定義派生要用到的方法,但是方法的具體實現完全取決於派生類。
(6)java源程序與編譯型運行區別
- 編譯型語言。如c/c++,他們通過編譯器直接編譯為exe可執行文件,然后在運行的時候就可以直接運行,而不需要解釋,所以速度很快。
- 解釋型語言。如java、JavaScript,java寫完之后編譯為class文件,然后這個文件是不能直接運行的,還需要再運行的時候邊解釋邊運行,所以速度較慢。
五、java對象和類
面向對象的語言不只是java,還有c++、python之類的,所以在理解起來都是沒有問題的,大同小異。比如對象是類的一個實例,它有狀態(屬性)和行為(方法),而類是一個模板,這個模板它描述了一類對象的行為和狀態。在我們創建第一個java程序時,我們就使用到了類,即public class 類名 {} 。
而java中的變量(這里不是類的變量)有多種,即局部變量、成員變量、類變量。如在方法中定義的變量,就是局部變量,因為它會在方法執行完成之后立即被銷毀,這就是局部變量;而成員變量是定義在類中、方法體之外的變量。這種變量在創建對象的時候實例化,它可以被類中方法、構造方法和特定類的語句塊訪問; 而類變量也是生命在類中的,方法體之外,但必須聲明為static類型,且不是被實例訪問,而是類訪問的。
而java中的構造方法和c++中的構造函數是類似的,他們都是為了更加方便的構造實例,並且兩者的構造函數如果開發者沒有定義就會自動提供一個默認的構造函數 ,且構造方法和類的名稱必須要同名,一個類可以有多個構造方法。
如下所示,創建一個Puppy類,這個類中含有兩個構造方法:
public class Puppy{ public Puppy(){ } public Puppy(String name){ // 這個構造器僅有一個參數:name } }
而如下所示的程序就可以創建一個實例:
public class FOO { public FOO(String name) { // 構造方法,創建一個實例時輸出其名稱 System.out.println("create a new object:" + name); } public static void main(String[] args) { FOO foo = new FOO("wayne"); } }
但是在運行時時會發生編譯錯誤,即無法正常解碼,我們使用 -encoding UTF-8 的編碼方式即可,如下所示:
PS C:\Users\Administrator\Desktop> javac -encoding UTF-8 FOO.java PS C:\Users\Administrator\Desktop> java FOO create a new object:wayne
這樣,我們就可以得到爭取的結果了。
另外,如果我們希望訪問屬性和方法也可以通過下面的方式進行訪問:
public class FOO { public FOO(String name) { // 構造方法,創建一個實例時輸出其名稱 System.out.println("create a new object:" + name); } int fooAge; public void setAge(int age) { fooAge = age; } public int getAge() { System.out.println("the age is:" + fooAge); return fooAge; } public static void main(String[] args) { FOO foo = new FOO("wayne"); foo.setAge(22); foo.getAge(); } }
如上所示,通過int Age就可以創建一個實例變量,方法也是類似的,最后我們在public static void main函數中執行主要的邏輯,通過foo.setAge和foo.getAge來調用實例方法。最后結果如下:
PS C:\Users\Administrator\Desktop> javac -encoding UTF-8 FOO.java PS C:\Users\Administrator\Desktop> java FOO create a new object:wayne the age is:22
ok,到這里,我們就對java中基本的類、實例、屬性、方法等有一個基本的了解了。
六、其他相關規則(源文件、包)
即我們在一個.java這樣的源文件中是要遵循相應的規則的,比如說一個源文件只能有一個public類、但是一個源文件可以有多個非public類、源文件的名稱應該和publick類的類名保持一致、如果一個類定義在了某個包中那么package語句應該在源文件的首行、而如果源文件包含import語句,那么應該放在package語句和類定義之間;如果沒有package語句,那么import語句應該放在源文件中最前面、import語句和package語句對源文件中定義的所有類都有效。
java包的主要作用是對類和接口進行分類,當開發java程序時,可能編寫成百上千的類,因此很有必要對類和接口進行分類。
在java中,如果給出一個完整的限定名,包括包名、類名,那么java編譯器就可以很容易地定位到源代碼或者類了,而import語句就是用來提供一個合理的路徑,使得編譯器可以找到某個類。
package和import的區別:
兩者是互逆的過程,如package freedom.bean語句的作用是將當前的類(class文件)打包到這個freedom.bean包中; 而 import freedom.bean.* 就是把freedom.bean這個包中的所有類都引入,這樣就可以使用了,比如我們是可以引用到之前package freedom.bean的類的。
又如,我們通過import java.io.*就可以導入輸入輸出相關的所有類。
下面,我們通過一個例子來進行進一步的講解,這個例子中,我們創建Employee和Employee Test兩個類。
首先,我們創建Employee.java文件,如下:
import java.io.*; public class Employee{ String name; int age; String designation; double salary; // Employee 類的構造器 public Employee(String name){ this.name = name; } // 設置age的值 public void empAge(int empAge){ age = empAge; } /* 設置designation的值*/ public void empDesignation(String empDesig){ designation = empDesig; } /* 設置salary的值*/ public void empSalary(double empSalary){ salary = empSalary; } /* 打印信息 */ public void printEmployee(){ System.out.println("名字:"+ name ); System.out.println("年齡:" + age ); System.out.println("職位:" + designation ); System.out.println("薪水:" + salary); } }
然后,創建EmployeeTest.java如下:
import java.io.*; public class EmployeeTest{ public static void main(String args[]){ /* 使用構造器創建兩個對象 */ Employee empOne = new Employee("RUNOOB1"); Employee empTwo = new Employee("RUNOOB2"); // 調用這兩個對象的成員方法 empOne.empAge(26); empOne.empDesignation("高級程序員"); empOne.empSalary(1000); empOne.printEmployee(); empTwo.empAge(21); empTwo.empDesignation("菜鳥程序員"); empTwo.empSalary(500); empTwo.printEmployee(); } }
注意:前兩個文件要放在同一個文件夾下。
最后,我們直接運行即可,最終結果如下:
PS C:\Users\Administrator\Desktop> javac -encoding UTF-8 EmployeeTest.java PS C:\Users\Administrator\Desktop> java EmployeeTest 名字:RUNOOB1 年齡:26 職位:高級程序員 薪水:1000.0 名字:RUNOOB2 年齡:21 職位:菜鳥程序員 薪水:500.0
所以,我們可以發現,創建了Employee.java這個類之后,我們並不需要package將之打包,也不需要在EmployeeTest.java中使用import引用,而是如果兩者在同一個文件夾下,那么就可以直接在一個文件中調用另外一個類。並且,要通過上述的運行,我們即使不使用 import java.io.* 也是可以正常執行的。但是,如果我們沒有將兩者放在同一個文件夾下,就會出現問題,而不能正常執行。
七、java基本數據類型
java中也分為了兩種數據類型,一種是內置數據類型,另外一種是引用數據類型,這個和JavaScript是一樣的。
但js中的基本數據類型有5種,而java中的基本數據類型卻有8種(四個整數型、兩個浮點型、一種字符類型、一種布爾型)。
- byte數據類型。它是8位、有符號的、以二進制補碼表示的整數,最小值為-128,最大值為127,默認值為0. byte類型占用的空間只有int類型的四分之一。
- short數據類型。它是16位、有符號的、以二進制補碼表示的整數,最小值是-32768(-2^15),最大值是32767(2^15 - 1),short類型和byte類型一樣都很節省空間,因為short變量是int變量所占空間的二分之一。它的默認值也是0.
- int數據類型。它是32位、有符號的以二進制補碼表示的整數,最小值為-2^31,最大值是2^31 - 1,一般整型變量默認就是int類型,默認值為0。
- long數據類型。它是64位、有符號的以二進制補碼表示的整數,最小值為-2^63,最大值為2^63 - 1,這種類型主要使用在需要比較大整數的系統上。默認值為0L。比如 long a = 100000L。
- float數據類型。它是單精度、32位、符合IEEE 754標准的浮點數。它在儲存大型浮點數組的時候可以節省內存空間,默認值為0.0f,它不能用來表示精確的值,如貨幣。 如float f1 = 234.5f。
- double數據類型。它是雙精度、64位、符合IEEE 754標准的浮點數。浮點數的默認類型就是double類型。它同樣不能表示精確的值,如貨幣。默認值是0.0d。如double d1 = 123.4。
- boolean數據類型。它表示一位的信息,只有兩個取值:true和false。 這種類型只作為記錄true/false情況,默認值是false。比如boolean one = true。
- char類型。char類型是一個單一的16位的Unicode字符,最小值是 \u0000(即為0), 最大值是 \uffff(即為65535),char數據類型可以存儲任何字符,比如char letter = 'A';。
如上,就是四類8種基本數據類型了。在上面的詳細介紹中,有其最大最小值的限制,而實際上,我們根本就不不要記住,而是通過MIN_VALUE和MAX_VALUE就可以獲取到了。如下所示:
public class FOO { public static void main (String[] args) { System.out.println("基本類型: byte二進制位數: " + Byte.SIZE); System.out.println("包裝類: java.lang.Byte"); System.out.println("最大值: Byte.MIN_VALUE=" + Byte.MIN_VALUE); System.out.println("最小值: Byte.MAX_VALUE=" + Byte.MAX_VALUE ); System.out.println(); System.out.println("基本類型: int二進制位數: " + Integer.SIZE); System.out.println("包裝類: java.lang.Integer"); System.out.println("最大值: Integer.MIN_VALUE=" + Integer.MIN_VALUE); System.out.println("最小值: Integer.MAX_VALUE=" + Integer.MAX_VALUE ); System.out.println(); System.out.println("基本類型: char二進制位數: " + Character.SIZE); System.out.println("包裝類: java.lang.Character"); System.out.println("最大值: Character.MIN_VALUE=" + (int)Character.MIN_VALUE); System.out.println("最小值: Character.MAX_VALUE=" + (int)Character.MAX_VALUE ); System.out.println(); } }
即我們可以通過如上的方式獲取到java中基本數據類型的范圍,結果如下:
基本類型: byte二進制位數: 8 包裝類: java.lang.Byte 最大值: Byte.MIN_VALUE=-128 最小值: Byte.MAX_VALUE=127 基本類型: int二進制位數: 32 包裝類: java.lang.Integer 最大值: Integer.MIN_VALUE=-2147483648 最小值: Integer.MAX_VALUE=2147483647 基本類型: char二進制位數: 16 包裝類: java.lang.Character 最大值: Character.MIN_VALUE=0 最小值: Character.MAX_VALUE=65535
上面說了8種都是java中的基本數據類型,下面是java中的引用類型。java中的應用類型非常類似於c/c++中的指針,引用類型指向一個對象,指向對象的變量是引用變量,這些變量聲明時被指定為一個特定的類型,比如Employee、Puppy等。注意:對象、數組都是引用類型,所有引用類型的默認值都是null,一個引用變量可以用來引用任何與之兼容的類型。如Site site = new Site("wayne");
java常量。java常量在程序運行中是不能被修改的,常量的聲明方式和變量類似,使用final關鍵字,如下:
final double PI = 3.1435926;
一般,這個常量的大小寫沒有限制,但是最好使用大寫,便於觀察。
在java中,字符串常量和字符常量都可以包含任何Unicode字符,例如:
char a = '\u0001'; String a = "\u0001";
java語言支持一些特殊的轉義字符序列。
八、java類型轉換
整型、常量、字符型數據可以混合運算,運算中,不同的數據類型先轉化為同一數據類型,然后進行運算。
轉換由低級到高級(而如果從高級到低級轉換,就會導致精度丟失了),如下:
低 ---------------------------------------> 高 byte,short,char—> int —> long—> float —> double
注意,轉換過程中也是要遵循一定的規則的:
- 不能對boolean類型進行類型轉換。
- 不能吧對象類型轉換為不想關類的對象。
- 在把容量大的類型轉換為容量小的類型時,必須使用強制類型轉換。
- 轉換過程中可能導致溢出或者損失精度,如下:
int i =128; byte b = (byte)i;
byte類型最大值為127,所以,int類型強制轉化為byte類型時,值128時就會導致溢出。
- 浮點數到整數的轉化是通過舍棄小數得到的,而不是四舍五入,如(int)23.7 == 23; 。
強制類型轉換的條件是轉換的數據類型必須是兼容的,比如:
int i = 123; byte j = (byte)i;
這里就將int類型的i強制轉化為了byte類型的j了。
九、java中的Number類和Math類
在java中,我們可能常常會用到byte、int、long、double等內置數據類型,但是,在實際開發中,我們可能經常會用到它們的對象,因為對象上封裝了很多方法,可以方便我們調用,而所有的包裝類(Integer、Long、Byte、Double、Float、Short)都是抽象類Number的子類,而Number類是屬於java.lang包的。
如下所示:
public class Test { public static void main(String[] args) { Integer num = 5; System.out.println(num); } }
編譯執行之后就會輸出5。這只是一個簡單的例子,如果真的只能這么用,還不如直接用int,所以啊,其實Interger包括其他繼承自Number類的類都有很多方法可以調用,后面會講到。
另外,Math類也是在數值計算中常常會用到的,java中的Math包含了用於執行基本數學運算的屬性和方法,如初等指數、對數、平方根和三角函數。如下所示:
public class Test { public static void main(String[] args) { System.out.println("90度的正弦值: " + Math.sin(Math.PI/2)); System.out.println("0度的余弦值: " + Math.cos(0)); System.out.println("求2的3次方: " + Math.pow(2, 3)); } }
最終結果如下所示:
90度的正弦值: 1.0 0度的余弦值: 1.0 求2的3次方: 8.0
而下面是Number和Math的一些類方法:
- xxxValue() - 如byteValue()可以將Number類型的值轉化為基本數據類型的byte類型。
- compareTo() - 將number對象與參數進行比較。
- equals() - 判斷number對象與參數是否相等。
- valueOf() - 返回一個Number對象指定的內置數據類型。
- toString() - 以字符串的形式返回。
- parseInt() - 將字符串轉化為int類型。
- abs() - 返回參數的絕對值。
- ceil() - 返回大於等於給定參數的最小整數。
- floor() - 返回小於等於給定參數的最大整數。
- rint() - 返回與參數最接近的整數,返回類型為double。
- min() - 返回兩個參數中的最小值。
- max() - 返回兩個參數中的最大值。
- random() - 返回一個隨機數。
十、java中的String類
字符串廣泛應用在java編程中,在java中字符串屬於對象,java提供了String類來創建和操作字符串。
如下我們可以創建String對象:
public class Test { public static void main(String[] args) { String c = "wayne-zhu"; String b = new String("hedy-he"); System.out.println(c); // wayne-zhu System.out.println(b); // hedy-he } }
上面的例子中演示了兩種創建String實例的方法,都是正確的。
而使用String,是為了使用String類的方法,如下:
public class Test { public static void main(String[] args) { String c = "wayne-zhu"; String b = new String("hedy-he"); int cLen = c.length(); System.out.println("cLen: " + cLen); // clen: 9 String d = c.concat(b); System.out.println("d: " + d); // d: wayne-zhuhedy-he String fs; fs = String.format("浮點型變量的值為:%f" + ", 整型變量的值為: %d" + ", 字符型變量的值為: %s", 0.618, 666, "wayne"); System.out.println(fs); } }
最終結果如下所示:
cLen: 9 d: wayne-zhuhedy-he 浮點型變量的值為:0.618000, 整型變量的值為: 666, 字符型變量的值為: wayne
另外,String還有其他很多方法:
- char charAt(int index)返回指定索引處的char值。
- int compareTo(Object o)把這個字符創和另外一個對象比較,返回的是ASCII差值。
- int compareTo(String anotherString)按照字典順序比較兩個字符串
- int compareToIgnoreCase(String str)按照字典順序比較,不考慮大小寫
- String concat(String str)將指定字符串連接到此字符串的結尾。
- int indexOf(int ch)返回指定字符串中第一次出現處的索引。
十一、java數組
數組在任何編程語言中都是存在的,它用來存儲固定大小的同類型元素,這里主要介紹java數組的聲明、創建、初始化。
double[] myList; // 首選的方法 double myList[];
如上所示,我們聲明數組有兩種方法,第一種是推薦的方法,第二種也可以,但是不推薦。
上面是聲明的過程,但是創建數組要用下面的方法:
double[] myList = new double[10];
這里,我們創建了myList數組,且長度為10。
當然,我們可以直接賦值,如下所示:
double[] myList = {1.0, 131.1, 2.3, 6.3};
另外,數組有for循環可以調用,如果要單獨訪問到某個元素,可以使用下標進行訪問,如下所示:
public class Test { public static void main(String[] args) { double[] myList = {1.1, 1.2, 1.3, 1.4}; System.out.println("第一個元素: " + myList[0]); for (double element: myList) { System.out.println(element); } } }
最終結果如下所示:
第一個元素: 1.1 1.1 1.2 1.3 1.4
另外,數組也可以作為參數被傳遞,也可以是一個函數的返回值。
十二、java方法
在java中,我們之前用到的最多的時System.out.println(),其中:
- println()就是一個方法。
- out是對象。
- System是類。
即println()是System類下的out對象的方法。
方法其實就是函數,在JavaScript、c++等語言中都是一樣的,它可以使得程序變得簡潔而清晰、有利於程序維護、可以提高程序開發的效率、提高了代碼的重用性。
需要注意的是,和JavaScript不同,java對方法的命名有一定的要求:即方法的名字的第一個單詞應以小寫字母開頭,后面的單詞則用大寫字母開頭,不使用連接符,例如addPerson; 下划線可能出現在JUnit測試方法名稱用以分隔名稱的邏輯組件,如test<MethodUnderTest>_<state>,即testPop_emptyStack。
方法的定義如下:
修飾符 返回值類型 方法名(參數類型 參數名) { ... 方法體 ... return 返回值; }
方法中包含了一個方法頭和一個方法體,下面是一個方法的所有部分:
- 修飾符: 這是可選的,即告訴編譯器如何調用該方法,如public static 等。
- 返回值類型: 方法可能會返回值。 如int double等等,如果沒有返回值,那么就用void。
- 方法名: 即方法的實際名稱。
- 參數類型:參數是可選的,方法可以不傳遞任何參數,但如果傳遞,則需要說明其類型、順序以及參數的個數。
- 方法體: 即具體的語句,來定義該方法的功能。
java中的方法根據返回值或者參數的不同可以實現重載,如下所示:
public class Test { public static void main(String[] args) { int x1 = 2; int x2 = 5; System.out.println(add(x1, x2)); double y1 = 1.5; double y2 = 2.2; System.out.println(add(y1, y2)); } public static int add(int a, int b) { System.out.println("返回一個int值"); return a + b; } public static double add(double a, double b) { System.out.println("返回一個double值"); return a + b; } }
最終的結果如下所示:
返回一個int值 7 返回一個double值 3.7
這樣,我們就完成了java方法的重載。 注意,上面函數使用public表示該方法可以被外部訪問,使用static之后,如果這個方法在這個類之外被調用,可以直接Test.add調用,如果沒有static修飾符,就要先創建一個Test的實例,然后通過實例來調用。
既然,這里講到了方法,那么其實我們每次在寫最基本的java程序的時候 public static void main 就是一個方法了,並且每次通過 "java 文件名" 來調用的時候,就是調用的這個main函數,那么如果給這個main函數傳遞參數呢?其實,這時只要通過命令行來傳遞參數就可以了,如下:
public class Test { public static void main(String[] args) { for (int i = 0; i < args.length; i++) { System.out.println("args[" + i + "]: " + args[i]); } } }
即我們在命令行中輸入的就是這里的agrs,其實根據之前學的數組,可以將main函數中的參數寫成(String args[])也是可以的。
最終的結果如下:
十三、Java流(Stream)、文件(File)和IO
Java.io包括幾乎所有的操作輸入、輸出所需要的類,所有這些類代表了輸入源和輸出目標,一個流可以理解為一個數據的序列,輸入流表示從一個源讀取數據,輸出流表示向一個目標寫數據,且Java為I/O提供了強大、靈活的支持,使其廣泛地應用到文件傳輸和網絡編程中。
十四、java面向對象
java是面向對象的,自然有繼承的概念,java中使用extends表示子類繼承父類,繼承之后,子類擁有父類非private的屬性、方法; 子類也可以擁有自己的屬性的方法,即子類可以對父類進行擴展;子類可以用自己的方式實現父類的方法;java可以單繼承也可以多繼承;
java中所有的類都是繼承與java.lang.Object。java中,類的繼承是單一繼承,即一個子類只能有一個父類,即extends只能繼承一個類。
而如果使用implements關鍵字可以變相的使java具有多繼承的特性,使用范圍為類繼承接口的情況,如下:
public interface A { public void eat(); public void sleep(); } public interface B { public void show(); } public class C implements A,B { }
這樣,C就同時繼承了A和B。
在java中,我們還常常見到super和this關鍵字。其中:
- super關鍵字用來實現對父類成員的訪問,用來引用當前對象的父類。
- this關鍵字用來指向自己的引用。
class Animal { void eat() { System.out.println("animal : eat"); } } class Dog extends Animal { void eat() { System.out.println("dog : eat"); } void eatTest() { this.eat(); // this 調用自己的方法 super.eat(); // super 調用父類方法 } } public class Test { public static void main(String[] args) { Animal a = new Animal(); a.eat(); Dog d = new Dog(); d.eatTest(); } }
最終結果如下:
animal : eat
dog : eat
animal : eat
另外,在java中,final關鍵字表示可以把類定義為不能繼承的,即最終類; 或者用於修飾方法,該方法不能被子類重寫。
重寫(Override) - 即子類對父類的允許訪問的方法的實現過程進行重寫,返回值和形參都不能改變,即外殼不變,核心重寫。它的好處是子類可以根據需要,定義屬於自己的行為,也就是說子類能夠根據需要實現父類的方法。 如下:
class Animal{ public void move(){ System.out.println("動物可以移動"); } } class Dog extends Animal{ public void move(){ System.out.println("狗可以跑和走"); } } public class TestDog{ public static void main(String args[]){ Animal a = new Animal(); // Animal 對象 Animal b = new Dog(); // Dog 對象 a.move();// 執行 Animal 類的方法 b.move();//執行 Dog 類的方法 } }
最后結果如下:
動物可以移動
狗可以跑和走
而如果子類希望調用父類的被重寫方法時,就要使用super關鍵字了,這樣,就可以調用父類方法。
剛剛我們說的是重寫(override),即在子類中重寫(覆蓋)父類的方法。
這里我們要說的是重載(overload),即在一個類里面,方法名字相同,而參數不同,返回類型可以相同也可以不同 。但參數必須有所不同!且最常見的其實就是構造器的重載。被重載的方法必須改變參數列表(參數個數或者類型不一樣),但是其返回類型可以修改,並且訪問修飾符可以修改。如下所示:
package jicheng; public class Jicheng { public int test(){ System.out.println("fa"); return 1; } public void test(int a) { System.out.println("test2"); } public static void main(String[] args) { Jicheng foo = new Jicheng(); foo.test(); foo.test(1); } }
多態。 這個概念在生活中可以理解為比如按下up鍵,在word中是移動到上一行;在游戲中,是前進;在網頁中,是向上滑動; 即同一個事件發生在不同的對象上會產生不同的結果。
又比如你打印一個東西,按下打印鍵之后,在彩色打印機上打印出的是彩色、而在黑白打印機上打印出的時黑白的。
public class Test { public static void main(String[] args) { show(new Cat()); // 以 Cat 對象調用 show 方法 show(new Dog()); // 以 Dog 對象調用 show 方法 Animal a = new Cat(); // 向上轉型 a.eat(); // 調用的是 Cat 的 eat Cat c = (Cat)a; // 向下轉型 c.work(); // 調用的是 Cat 的 work } public static void show(Animal a) { a.eat(); // 類型判斷 if (a instanceof Cat) { // 貓做的事情 Cat c = (Cat)a; c.work(); } else if (a instanceof Dog) { // 狗做的事情 Dog c = (Dog)a; c.work(); } } } abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃魚"); } public void work() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨頭"); } public void work() { System.out.println("看家"); } }
最終結果如下:
吃魚
抓老鼠
吃骨頭
看家
吃魚
抓老鼠
於是,我們可以看出多態存在的三個必要條件是繼承、重寫、父類引用指向子類對象。
java抽象類。 即通過abstract class進行類的定義。說是抽象類,是因為我們約定這種類是抽象的,不能直接實例化得到對象,所以,這樣的類就只能被繼承。如果直接實例化抽象類就會編譯錯誤。另外,抽象類中可以有抽象方法,抽象方法中不能有方法體。注意:抽象類不一定有抽象方法,但是有抽象方法的類一定是抽象類。
- 構造方法、類方法(用static修飾的方法)不能聲明為抽象方法。
- 抽象類的子類必須給出抽象類中的抽象方法(如果有的話)的具體實現,除非該子類也是抽象類。
java封裝。我們知道面向對象的三大特征是繼承、多態和封裝,之前已經講了繼承和多態,這里就是將封裝了。
封裝(Encapsulation)是指一種將抽象性函式接口的實現細節部分包裝、隱藏起來的方法。封裝可以認為是一種保護屏障、防止該類的代碼和數據被外部類定義的代碼隨機訪問。要訪問該類的代碼和數據,必須通過嚴格的接口控制。
封裝最主要的功能就是我們能夠修改自己的實現代碼,而不用修改那些調用我們代碼的程序片段。適當的封裝可以讓程式代碼更容易理解和維護,也加強了程式碼的安全性。
封裝的優點如下:
- 良好的封裝能夠減少耦合。
- 類內部的結構可以自由修改。
- 可以對成員變量進行更加精確的控制。
- 隱藏信息,實現細節。
實現java封裝需要兩個基本步驟,第一就是修改屬性的可見性來限制對屬性的訪問(一般限制為private),第二就是對每個值屬性提供對外的公共方法訪問,也就是創建一對賦值、取值方法,用於對私有屬性的訪問。
1、私有屬性,對外隱藏。
public class Person { private String name; private int age; }
這些屬性只有在類的內部可以訪問,而其他的類是訪問不了的,這樣就實現了對細節的隱藏。
2、提供公共方法,對外開放。
public class Person{ private String name; private int age; public int getAge(){ return age; } public String getName(){ return name; } public void setAge(int age){ this.age = age; } public void setName(String name){ this.name = name; } }
如上所示,這些方法對私有屬性進行訪問,然后暴露出去。 其中,采用this關鍵字是為了解決實例變量和局部變量之間發生的同名的沖突 。
java接口(Interface),在java編程語言中是一個抽象類型,是抽象方法的集合,接口通常以interface來聲明。 一個類通過繼承接口的方法,從而來繼承接口的抽象方法。
java中的接口並不是類,接口包含的是類要實現的方法。 除非實現接口的類是抽象類,否則該類要定義接口中的所有方法。
接口無法被實例化,但是可以被實現。一個實現接口的類,必須實現接口內所描述的所有方法,否則就必須被聲明為抽象類。
接口的聲明格式:
[可見度] interface 接口名稱 [extends 其他的類名] { // 聲明變量 // 抽象方法 }
如下所示,就是一個接口:
/* 文件名 : Animal.java */ interface Animal { public void eat(); public void travel(); }
ok,在聲明了接口之后,如何實現接口呢? 即類使用implements關鍵字實現接口,如下所示:
/* 文件名 : MammalInt.java */ public class MammalInt implements Animal{ public void eat(){ System.out.println("Mammal eats"); } public void travel(){ System.out.println("Mammal travels"); } public int noOfLegs(){ return 0; } public static void main(String args[]){ MammalInt m = new MammalInt(); m.eat(); m.travel(); } }
接口的繼承。一個接口能繼承另一個接口,使用extends關鍵字即可,即子接口繼承父接口。如下就是Sports接口被Hockey和Football接口繼承。
// 文件名: Sports.java public interface Sports { public void setHomeTeam(String name); public void setVisitingTeam(String name); } // 文件名: Football.java public interface Football extends Sports { public void homeTeamScored(int points); public void visitingTeamScored(int points); public void endOfQuarter(int quarter); } // 文件名: Hockey.java public interface Hockey extends Sports { public void homeGoalScored(); public void visitingGoalScored(); public void endOfPeriod(int period); public void overtimePeriod(int ot); }
即Hockey接口自己聲明了四個方法,從Sports接口繼承了兩個方法,那么實現Hockey接口的類需要實現六個方法。相似的,實現Football接口的類需要實現五個方法,其中兩個來自Sprots接口。
在java中,類的多繼承是不允許的,但是接口允許多繼承,使用extends關鍵字即可:
public interface Hockey extends Sports, Event
接口這里需要理解的: https://www.zhihu.com/question/20111251
即接口並不是可有可無的,通過接口,我們知道哪些方法必須被實現,可以有一個預知的作用。
比如說你今年放假出去杭州旅游,玩了一上午,你也有點餓了,突然看到前面有個店子,上面掛着KFC,然后你就知道今天中飯有着落了。
KFC就是接口,我們看到了這個接口,就知道這個店會賣炸雞腿(實現接口)。
那么為神馬我們要去定義一個接口涅,這個店可以直接賣炸雞腿啊(直接寫實現方法),是的,這個店可以直接賣炸雞腿,但沒有掛KFC的招牌,我們就不能直接簡單粗暴的沖進去叫服務員給兩個炸雞腿了。
要么,我們就要進去問,你這里賣不賣炸雞腿啊,賣不賣漢堡啊,賣不賣聖代啊(這就是反射)。很顯然,這樣一家家的問實在是非常麻煩(反射性能很差)。
要么,我們就要記住,中山路108號賣炸雞,黃山路45號賣炸雞(硬編碼),很顯然這樣我們要記住的很多很多東西(代碼量劇增),而且,如果有新的店賣炸雞腿,我們也不可能知道(不利於擴展)。
java包(package)
為了更好的組織類,Java提供了包機制,用於區別類名的命名空間。所以,包的作用如下:
- 把功能相似或者相關的類或者接口組織在同一個包中,方便類的查找和使用。
- 就像文件夾一樣,包也采用了樹形目錄的存儲方式。同一個包中的類名字是不同的,不同的包中的類的名字是可以相同的,當同時調用兩個不同包中相同的類名的類時,應該加上包名加以區別。因此,包可以避免命名沖突。
- 包定義了訪問權限,擁有包訪問權限的類才能訪問某個包中的類。
所以,java中使用包(package)這種機制是為了防止命名沖突,訪問控制,提供搜索和定位類(class)、接口、枚舉(enumerations)和注釋(annotation)等。
比如一個Something.java文件的內容:
package net.java.util; public class Something{ ... }
即,我們在定義一個類的時候,需要說明這個類的包,這樣,它的路徑應該是 net/java/util/Something.java 這樣保存的。 package(包)的作用是把不同的java程序分類保存,更方便的被其他java程序調用。下面就是一些java的包:
- java.lang - 打包基礎的類 (即將java中一些基礎的類打包到java.lang這個包中)
- java.io - 包含輸入輸出功能的函數。
開發者可以自己把一組類和接口等打包。並定義自己的包。並且這在實際開發中是值得提倡的,當你自己完成類的實現之后,將相關的類分組,可以讓其他的編程者更容易地確定哪些類、接口、枚舉和注釋等是相關的。
比如,我們可以在一個包中加入一個接口:
/* 文件名: Animal.java */ package animals; interface Animal { public void eat(); public void travel(); }
然后在同一個包中創建一個類來實現這個接口:
package animals; /* 文件名 : MammalInt.java */ public class MammalInt implements Animal{ public void eat(){ System.out.println("Mammal eats"); } public void travel(){ System.out.println("Mammal travels"); } public int noOfLegs(){ return 0; } public static void main(String args[]){ MammalInt m = new MammalInt(); m.eat(); m.travel(); } }
這樣,他們就會都保存在 animals 的子目錄下。
而如果我們希望引入其他的類,可以 import payroll.* 來引入payroll這個包下面所有的類,或者通過import payroll.foo來引入payroll包下面的foo類。
通常,一個公司會使用它互聯網域名的顛倒形式來作為它的包名。例如:互聯網域名是run.com,那么所有的包名都會以 com.run 作為開頭。 比如在com.run開頭的com.run.test這個包,它包含了一個叫做Runoob.java的源文件,那么應該有如下目錄:
com/run/test/Runoob.java
編譯的時候,編譯器會為包中定義的每個類、接口等類型各創建一個不同的輸出文件,輸出文件的名字就是這個類型的名字,並加上.class作為擴展后綴。
編譯之后的.class文件應該和.java源文件一樣,他們放置的目錄應該和包的名字對應起來,但是,並不要求路徑完全一致,我們可以分開來安排源碼和類的目錄。
十五、java數據結構、集合框架
java工具包提供了強大的數據結構,在java中的數據結構主要包括以下幾種接口和類,即枚舉、位集合、向量、棧、字典、哈希表、屬性。
- 枚舉是一種傳統的接口,被迭代取代了,所以枚舉已經很少使用 了。
- 位集合(Bitset)實現了一組可以單獨設置和清除的位或標志。
- 向量(vector)和傳統的數組非常相似,但是Vector的大小能根據需要動態的變化。即Vector類最主要的好處就是在創建對象的時候不必給對象指定大小,它的大小會根據需要動態的變化。即vector是一個動態數組。
- 棧(Stack)實現了一個后進先出(LIFO)的數據結構。當你在棧中添加一個新元素時,就將新元素放在了其他元素的頂部。即最后進棧的元素最先被取出。其實棧是Vector的一個子類,它除了Vector定義的所有方法,自己也定義了一些方法,比如empty()判斷棧是否為空、peek()查看棧頂部的對象、pop()可以移除棧頂部的對象、push()可以把對象壓入棧的頂部、search(Object element)可以返回對象在棧中的位置。
- 字典(Dictionary) 是一個抽象類(即只能被繼承,不能實例化),它定義了鍵映射到值的數據結構。當你想要通過特定的鍵而不是整索引來訪問數據時,這時候就要使用Dictionary了。
- 哈希表(Hashtable) 是提供了一種在用戶定義鍵結構的基礎上來組織數據的手段。
- 屬性(properties) 表示一個持久的屬性集。
java結合框架即java collection framework,簡稱JCF,和c++中的STL(standard Template Library)是類似的。JCF設計參考了STL,但是其定位並不是java版的STL,而是用於實現一個精簡緊湊的容器框架。
java容器就是可以容納其他java對象的對象,JCF是為java開發者挺了通用的容器,其始於JDK1.2,優點是可以降低編程難度、提高程序性能、提高API間的互操作性、降低學習難度。
Java容器里只能放對象,對於基本類型(int, long, float, double等),需要將其包裝成對象類型后(Integer, Long, Float, Double)才能放到容器里。很多時候拆包裝和解包裝能夠自動完成,這雖然會導致額外的性能和空間開銷,但是簡化了設計和編程。
泛型。 java容器能夠容納任何類型的對象,這一點表面上是通過泛型機制完成的。java泛型不是什么神奇的東西,只是編譯器為我們提供的一個“語法糖”。
十六、java泛型
泛型是java中一個非常重要的概念,這個概念在Typescript中也是存在的,所以學好java中的泛型還是非常有必要的。
java泛型是JDK5中引入的一個新特性。泛型提供了編譯時類型安全監測機制,該機制允許程序員在編譯時檢測到非法的類型。
比如我們又這樣的一個需求:寫一個排序方法,能夠對整型數組、字符串數組甚至是其他任何類型的數組進行排序,該如何實現?
答案即使使用java泛型。
比如我們可以使用泛型來定義類/接口:
public class Test<T> { private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } }
泛型中的通配符。
- 無邊界通配符(<?>) - 主要作用是可以讓泛型接受未知類型的數據。
- 固定上邊界通配符(<? extends E>)- 能夠接受指定類及其子類類型的數據。 其中E指的是該泛型的上邊界。
- 固定下邊界通配符(<? super E>)- 能夠接受指定類及其父類類型的數據。