JAVA面向對象基礎
一、類與實例
對象:萬物皆對象,所有的東西都是對象,對象是一個自包含的實體,用一組可識別的特性和行為來標識。
類:是具有相同屬性和功能的對象的抽象集合
實例:就是一個真實的對象
實例化:創建對象的過程,使用new關鍵字來創建
public void Test()
{
Cat cat = new Cat();//將Cat類實例化
}
'Cat cat = new Cat();'做了兩件事
Cat cat;//聲明一個Cat的對象,對象名為cat
cat = new Cat();//將此cat對象實例化
二、構造方法
構造方法又叫構造函數,就是對類進行初始化。與類同名,無返回值,不需要void,在new的時候調用。
Cat cat = new Cat()
其中,Cat()就是構造方法。
所有類都有構造方法,不定義構造方法系統會默認生產空的構造方法。定義構造方法,則默認的構造就方法會消失。
public class Cat
{
//聲明Cat類的私有字符串變量name
private String name = "";
//定義Cat類的構造方法,參數是輸入一個字符串。
public Cat(String name)
{
this.name = name;//將參數賦值給私有變量name
}
}
三、方法重載
方法重載提供創建同名的多個方法的能力,但是這些方法需使用不同的參數類型,不只構造方法可以重載,普通方法也可以重載。
方法名相同,參數類型或個數必須要有所不同。
方法重載可在不改變原方法的基礎上,新增功能。
public class Cat
{
private String name = "";
public Cat(String name)
{
this.name = name;
}
//將構造方法重載
public Cat()
{
this.name = "無名";
}
}
四、屬性與修飾符
屬性
屬性:是一個方法或一對方法,在調用它的代碼看來,它是一個字段,即屬性適合於以字段的方式使用方法調用的場合。
字段:是存儲類要滿足其設計所需要的數據,字段都是與類相關的變量。
//聲明一個內部字段,private,默認為3
private int shoutNum = 3;
//ShoutNum屬性,注意是public
public int ShoutNum
{
//get表示外界調用時可以得到shoutNum的值
public int get(){
return shoutNum;
}
//set表示外界可以給內部的shoutNum賦值
public void set(int value){
shoutNum = value;
}
}
屬性有兩個方法,get和set。
get:訪問器返回與聲明的屬性相同的數據類型,調用時可以得到內部的字段的值或引用。
set:調用屬性時可以給內部的字段或引用賦值。
修飾符
public:表示它所修飾的類成員可以允許其它任何類來訪問,俗稱公有的。
private:表示只允許同一個類中的成員訪問,其它類包括它的子類無法訪問,俗稱私有的。
通常字段都是private,即私有變量(一般是首字母小寫或前加‘_’),而屬性都是public,即公有變量(首字母大寫)。
final
- 修飾變量:修飾變量時必須賦初值且不呢改變,修飾引用變量不能再指向其他對象。
- 修飾方法:方法前面加上final關鍵字,代表這個方法不可被子類重寫。
- 修飾類:表示這個類不能被繼承,類中的成員可以根據需要設為final,final類中的所有成員方法都會被隱飾地指定為final方法。
注:類的private方法會被隱飾地指定為final
protected:繼承的子類可以對基類(父類)有完全訪問權。對子類公開,不對其它類公開。
五、封裝
每個對象都包含它能進行操作所需的所有信息,因此對象不必依賴其它對象來完成自己的操作,能夠避免對象屬性賦值的隨意性。
封裝的好處:
- 良好的封裝能減少耦合(相互作用相互影響的關系)。
- 類內部的實現可以自由地修改。
- 具有清晰的對外接口。(對外的屬性和方法)set和get方法。
this關鍵字是對當前內的簡化。
提供專門對外的set和get方法對屬性進行設置,而不能用類.屬性的方法修改值。
public class Student{
private int age;//age屬性私有化
public Student(int age){
this.age = age;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
}
六、繼承
java只支持單繼承,不允許多繼承。
子類繼承父類:
- 子類擁有父類所有的屬性和方法(包括私有屬性和私有方法),但是父類中的私有屬性和方法子類無法訪問,只是擁有。
- 子類擁有自己的屬性和方法,即子類可以在父類的基礎上做擴展。
- 子類可以用自己的方式重寫父類的方法。
package Test;
class Students {
int age;//id屬性私有化
String name;//name屬性私有化
Students(int age,String name){
this.age = age;
this.name = name;
}
public void printInfo() {
System.out.println("父類方法 年齡:"+age+" 姓名:"+name);
}
}
class Student extends Students{
Student(int age, String name) {
super(age, name);
}
//重寫父類同名方法
@Override
public void printInfo() {
System.out.println("子類方法 年齡:"+age+" 姓名:"+name);
}
//使用super調用父類的方法
public void FatherprintInfo() {
super.printInfo();
}
}
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s2 = new Student(19,"張三");
s2.printInfo();
s2.FatherprintInfo();
}
}
//子類方法 年齡:19 姓名:張三
//父類方法 年齡:19 姓名:張三
super關鍵字
- super用於在子類調用父類的重名方法(不重名也能引用)。
- super在子類中引用重名的變量(不重名也能引用)。
- super()可以用於直接調用父類的構造方法。
繼承的優點
繼承使得所有子類公共的的部分都放在了父類,使得代碼得到了共享,就可以避免重復,繼承使得修改和擴展繼承而來的實現都較為容易。
繼承的缺點
繼承也是有缺點的,那就是父類做出修改,子類就不得不進行改變。另外,繼承也會破壞包裝,父類實現的細節暴露給子類。
總結:只有合理的應用繼承才能發揮好的作用。例如貓繼承動物,而動物不能繼承貓。
七、多態的淺理解
多態表示不同的對象執行相同的操作,但是要通過自己的實現代碼來執行。
注意點:
- 子類以父類的身份出現。
- 子類在工作時以自己的方式來實現。
- 子類以父類身份出現時,子類特有的屬性和方法不可以使用,只能用父類存在的方法。
多態有兩種情形
編譯時多態:
- 重載(overload)多個同名的不同方法。
運行時多態:
- 覆蓋(override),子類對父類方法進行覆蓋(重寫)。
- 動態綁定--也稱為虛方法調用,正真的方法在運行時才確定。
- 在調用方法時,程序會正確地調用子類對象的方法。
多態中成員的特點
1.多態成員變量:編譯運行看左邊。
People p = new Student();
System.out.println(p.name)//p是People中的值,只能取到父類中的值。
2.多態成員方法:編譯看左邊,運行看右邊。
People p1 = new Student();
System.out.println(p1.printInfo())//p1的表面上類型是People,但實際上是Student,所以調用的是重寫后的方法。
instancof關鍵字
用於判斷某個對象是否屬性某種數據類型。
返回類型為布爾類型。
People p1 = new Student();
System.out.println(p1 instancof Student);
//true
多態的轉型
向上轉型
將子類對象賦值給父類變量,稱之為向上轉型。
//People[] people = {new Student(19,"張三"), new Teacher(30,"老師A")};
People people[];//聲明一個人類數組
people = new People[1];//實例化最多兩個人類對象
//向上轉型
people[0] = new Student(19,"張三");
people[1] = new Teacher(30,"老師A");
//foreach遍歷對象
for(People item : people)
{
item.printInfo();
}
//年齡:19 姓名:張三
//年齡:30 姓名:老師A
向下轉型
將父類變量轉換為子類變量,稱之為向下轉型。向下轉型格式如下:
子類類型 變量名 = (子類類型) 父類類型的變量
for(int i=0;i<people.length;i++){
if(people[i] instanceof Student){
//向下轉型
Student student = (Student) people[i];
}else if(people[i] instanceof Teacher){
Teacher teacher = (Teacher) people[i];
}else{
System.out.println("程序錯誤。");
}
}
八、抽象類
仔細觀察,你會發現,People類其實根本不能實例化,一個學生長什么樣子,可以想象。new People;即實例化一個人,一個人長什么樣?
People是一個抽象的名詞,沒有其具體對象與之對應。
Java運行把類和方法聲明為abstract,即抽象類和抽象方法。
abstract class People{//加abstract關鍵字,表面抽象類。
......
//在方法返回值前加abstract表明此方法是抽象方法,抽象方法沒有方法體,直接在括號后面加上";"
protected abstract void printInfo();
}
抽象類注意點:
- 抽象類不能實例化。
- 抽象方法必須被子類重寫。
- 如果類中有抽象方法,那么類就必須定義為抽象類,不論是否還包含其它的一般方法。
讓抽象類擁有盡可能多的共同代碼,擁有盡可能少的數據。
抽象類通常代表一個抽象的概念,它提供一個繼承的出發點,當設計一個新的抽象類時,一定是用來繼承的,所有在一個繼承關系形成的等級結構里,樹葉節點應當是具體類。而樹葉節點均應當是抽象類。
九、接口(interface)
接口就是把隱式公共方法和屬性組合起來,以封裝特定功能的一個集合,一旦類實現了接口,類就可以職稱接口所指定的所有屬性和成員,聲明接口的語法和抽象類完全相同,但不允許提供接口中任何成員的執行方式。所以接口不能實例化,不能有構造方法和字段;不能有修飾符(如public,private等);不能聲明虛擬的或靜態的等。還有實現接口的類就必須要屬性接口中的所有方法和屬性。
一個類可以支持多個接口,多個類也可以支持相同的接口。接口的命名,前面要加一個大寫的字母'I',這是規范。
接口用interface聲明,而不是class,接口名稱前要加‘I’,接口中的方法或屬性前面不能有修飾符、方法沒有方法體。
//聲明一個IPeople接口,此接口有一個printInfo方法
interface IPeople{
void printInfo();
}
使用接口實現學生類
class Student implements IPeople{
public void printInfo(){
System.out.println("運行正確。");
}
}
區分抽象類和接口
抽象類可以給出一些成員的實現,接口卻不包含成員的實現,抽象類的抽象成員可被子類部分實現,接口的成員需要實現類完全實現,一個類只能繼承一個抽象類,但可實現多個接口等。
區分:
- 類是對對象的抽象;抽象類是對類的抽象;接口是對行為的抽象。
- 如果行為跨越不同類的對象,可使用接口;對於一些相似的類對象,用繼承抽象類。(實現接口和繼承抽象類並不沖突)。
- 從設計者角度來講,抽象類是從子類對象中發現了公共的東西,泛化出父類,然后子類繼承父類。而接口是根本不知子類的存在,方法如何實現還不確認,提前定義。
十、泛型
泛型是具有占位符(類型參數)的類、結構、接口和方法,這些占位符是類、結構、接口和方法所儲存或使用的一個或多個類型的占位符。泛型集合類可以將類型參數用作它所存儲的對像的類型的占位符;類型參數作為其字段和其方法的參數類型出現。
IList arrayPeople;//聲明一個集合變量,可以用接口IList,也可以直接聲明“ArrayList arrayPeople;”
用法就是在IList和List后面加'
//聲明一泛型集合變量,用接口IList,注意:IList表示此集合變量只能接受People類型,其它類型不接受,也可以直接聲明“List ”
IList<People> arrayPeople;
//實例化List對象,需要指定List<T>的'T'是People
arrayPeople = new List<People>();
泛型的使用
泛型有三種使用的方式,分別為:泛型類、泛型接口、泛型方法。
public class People<T>{
//T stands for "Type"
private T t;
public void set(T t){this.t = t;}
public T get(return t);
}
十一、注解
Java注解是附加在代碼中的一些元信息,用於一些工具在編譯、運行時進行解析和使用,起到說明,配置的功能。注解不會也不呢影響代碼的實際邏輯,僅僅起到輔助性的作用。
注解 Annotation 實現原理與自定義注解例子(opens new window)
十二、反射
什么是反射
可以獲取任意一個類的所有屬性和方法,還可以調用這些屬性和方法。
Java反射主要提供以下功能:
- 在運行時判斷任意一個對象所屬的類;
- 在運行時構造任意一個類的對象;
- 在運行時判斷任意一個類所具有的成員變量和方法(通過反射甚至可以調用private方法);
- 在運行時調用任意一個對象的方法;
重點:是運行時而不是編譯時。
反射機制的優缺點
- 優點:可以讓代碼更加靈活,為各種框架提供開箱即用的功能提供了便利。
- 缺點:讓我們在運行時有了分析操作類的能力,增加了安全問題。比如可以無視泛型參數的安全檢查(泛型參數的安全檢查發生在編譯時)。另外,反射的性能也會稍差點。不過,對框架來說影響不大。Java Reflection: Why is it so slow?
反射的應用場景
我們平時大部分時候都是在寫業務代碼,很少會接觸到直接使用反射機制的場景。
但是,這並不代表反射沒有用。相反,正是因為反射,你才能這么輕松地使用各種框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射機制。
這些框架中也大量使用了動態代理,而動態代理的實現也依賴反射。
十三、異常
在Java中,所有的異常都有一個共同的祖先java.lang包中的Throwable類,Throwable類有兩個重要的子類Error(錯誤)和Exception(異常)。其中Error用來表示JVM(Java虛擬機)無法處理的錯誤,Exception又分為兩種:
- 受檢查異常:需要用try-catch語句捕獲並進行處理,並且可以從異常中恢復。
- 不受檢查異常:java代碼在編譯過程中,即使不處理不受檢查異常也可以正常通過編譯。是運行時的錯誤,例如除0會引發ArithmeticException(算術錯誤異常),此時程序崩潰並且無法恢復。RuntimeException及其子類都統稱為非受檢查異常。
Throwable類常用方法
public string getMessage()//返回異常發生時的簡要描述。
public string toString()//返回異常發生時的詳細信息。
public string getLocalizedMessage()//返回異常對象的本地化信息。使用Throwable的子類覆蓋這個方法,可以生成本地化信息。如果子類沒有覆蓋該方法,則該方法返回的信息與getMessage()返回結果相同。
public void printStackTrace()//在控制台上打印Throwable對象封裝的異常信息。
try-catch-finally
try塊:用於捕獲異常,后面可以接零個或多個catch塊,如果沒有catch塊,則必須跟一個finally塊。
catch塊:用於處理try捕獲到的異常。
finally塊:無論是否捕獲或處理異常,finally塊里的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回之前被執行。
在下面三種特殊情況下,finally塊不會被執行:
- 在try或finally塊中用了System.exit(int)退出程序。如果System.exit(int)在異常語句之后,finally還是會被執行。
- 程序所在的線程死亡
- 關閉CPU
注意:當try語句和finally語句中都有return語句時,在方法返回之前,finally語句的內容將被執行,並且finally與的返回值會覆蓋原始的返回值,如下:
public class Test{
public static int f(int value){
try{
return value*value;
}finally{
if(value == 2)
return 0;
}
}
}
如果調用f(2),返回值將是0,因為try語句的返回值被finally語句的返回值覆蓋。
使用try-with-resources來代替try-catch-finally
- 適用范圍(資源的定義):任何實現java.lang.AutoCloseable或者java.io.Closeable的對象。
- 關閉資源和finally塊的執行順序:在try-with-resources語句中,任何catch或finally塊在聲明的資源關閉后運行。
《Effecitve Java》中明確指出:
面對必須要關閉的資源,我們總是應該優先使用try-with-resources而不是try-finally。隨之產生的代碼更簡短,更清晰,產生的異常對我們也更有用。try-with-resources語句讓我們更簡短清晰,產生的異常對我們更有用。try-with-resources語句讓我們更容易編寫必須要關閉的資源的代碼,若采用try-finally則幾乎做不到這點。
Java中類似於InputStream、OutputStream、Scanner、PrintWriter等的資源都需要我們調用close()方法來手動的關閉。
使用try-catch-finally語句實現:
//讀取文本文件的內容
Scanner scanner = null;
try{
scanner = new Scanner(new File("D://read.txt"));
while(scanner.hasNext())
System.out.println(scanner.nextLine());
}catch (FileNotFoundException e){
e.printStackTrace();
}finally{
if(scanner != null)
scanner.close();
}
使用Java 7之后的try-with-resources語句改造上面的代碼:
try (Scanner scanner = new Scanner(new File("D:\\read.txt"))){
while(scanner.hasNext())
System.out.println(scanner.nextLine());
}catch (FileNotFoundException e){
e.printStackTrace();
}
通過使用分號分隔符,可以在try-with-resources塊中聲明多個資源。
try(BufferredInputStream bin = new BufferredInputStream(new FileInputStream(new File("test.txt"))); BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))){
int b;
while((b = bin.read()) != -1)
bout.write(b);
}catch (IOException e)
{
e.printStackTrace();
}