封裝的目的是簡化編程和加強安全性。java
簡化編程是指,封裝可讓使用者沒必要了解具體類的內部實現細節,而只是要經過提供給外部訪問的方法來訪問類中的屬性和方法編程
加強安全性是指,封裝可使某個屬性只能被當前類使用,從而避免被其餘類或對象進行誤操做。安全
如何加強安全性:使用訪問修飾符private修飾屬性網絡
構造方法不一樣於普通方法,普通方法表明對象的行爲,而構造方法是提供給系統用於建立對象的方法。函數
構造方法(也稱爲構造函數)是一種特殊的方法,它具備如下特色。工具
構造函數的語法形式:[訪問修飾符] 類名([參數列表]) ;this
類中沒有構造方法時JVM會默認產生一個無參構造方法。可是若是類中已經有任意的構造方法,JVM不會再建立。spa
當使用反射建立對象時,必需要使用無參構造方法。操作系統
this指向當前對象的引用.net
this常常用於:
private int age ; ... public void setAge(int age) { ... this.age = age ; }
public Student() { //調用有一個String參數的構造方法 this(" WangYun" ); } public Student(String name) { //調用有兩個參數的構造方法 this(name,23); } public Student(String name, int age) { ... }
//如下代碼 Student s1 = new Student("zs",18); Student s2 = new Student("zs",18); System.out.print(s1 == s2); System.out.print(s1.getName() == s2.getName()); //執行後,控制檯輸出爲? false true
由於setName()都是雙引號字符串對象,是對常量池中同一個變量的引用。
TestStudent
,學生劉靜濤也定義了一個叫 TestStudent
的類。若是在同一個文件夾下,就會產生命名衝突的問題。而使用了包的機制,就能夠把王雲定義的類存放在 wangyun
包下,把劉靜濤定義的類存放在 liujingtao
包下,以後就能夠先經過 wangyun
和 liujingtao
這樣的包名,區分不一樣的目錄,而後再使用 TestStudent
訪問兩個包中各自的類,從而解決了命名衝突的問題。包的聲明語法格式:package pkg1[.pkg2[.pkg3…]];
命名規則:一般包名所有用小寫字母,如今使用最多的規則是使用翻轉的 internet 域名(不含 www、ftp 等訪問協議)。
java經常使用包:
java.lang
:lang 是 language 的簡寫,這個包提供 Java 語言的基礎類,例如 String、Math、Integer、System 和 Thread 等。java.util
:util 是 utility 的簡寫,組織了 Java 的工具類,包含集合、事件模型、日期和時間設置、國際化和各類實用工具類。java.io
:io 是 input 和 output 的合併簡寫,指輸入和輸出,組織了數據流、序列化和文件系統相關的類。java.net
:net 即網絡,這個包組織了爲實現網絡應用程序而提供的類。java.awt
:抽象窗口工具集(Abstract Window Toolkit),包含用於建立用戶界面和繪製圖形圖像的類。引用包的兩種方法:import 包名.類名;和import 包名.* ; 第二種形式的包只能引用當前包下的類,不能導入其子包中的類;同時只會導入用到的類。
import語句要寫在package以後,在類定義以前。
java.lang中的類不須要import
Java 語言中的訪問權限修飾符有 4 種,但卻只有 3 個關鍵字。由於不寫訪問權限修飾符時,在 Java 中被稱爲默認權限(包權限),本課程中以 default
代替。其餘 3 個訪問權限修飾符分別爲 private
、protected
和 public
。
修飾類:public和default。public表明類能在各類地方使用,default表明類只能在本包內使用。
修飾屬性、構造方法、普通方法:private default protected public。private修飾的成員只能在本類中使用。default修飾的成員只能在本類和本包內使用。protected修飾的成員只能在本類、本包內和子類中使用。public能夠在全部類中使用。
能夠看到,只有成員變量/方法和類/接口才可使用訪問權限修飾符,局部變量不可使用。
對象成員變量和類成員變量,對象成員變量能夠在每個對象中建立,不一樣對象的對象成員變量不能共享;類成員變量只建立一次,能夠被全部對象共享。經過在變量前面加上static使成員變量成爲靜態的(類成員變量)。
能夠直接經過類名引用靜態變量,也能夠經過實例名來引用靜態變量,但推薦採用前者,由於採用後者容易混淆靜態變量和實例變量。
被static修飾的方法稱爲靜態方法/類方法,能夠經過類名.方法名直接調用。
靜態方法中沒法使用實例變量。
Java 類首次裝入 JVM 時,會對靜態成員或靜態塊進行一次初始化,注意此時尚未產生對象。
所以,靜態成員和靜態塊都是和類綁定的,會在類加載時就進行初始化操做。
在使用 new 關鍵字建立並初始化對象的過程當中,具體的初始化分爲如下 4 步。
單例模式指的是不管建立了多少個引用,在堆中僅僅只有一個實例對象。
單例模式經過private修飾構造方法實現。也稱爲構造方法私有化。
public class Singleton { private static Singleton instance; private Singleton() { } }
同時建立一個堆內存對象供使用。這個方法在沒有對象時建立,存在對象時直接返回這個對象的引用。
public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
繼承可使得子類沿用父類的成員(屬性和方法)。當多個子類具備相同的成員時,就能夠考慮將這些成員提取出來,放到父類中,而後再用子類去繼承這個父類,也就是將一些相同的成員提取到了更高的層次中。
繼承能夠大大減小冗餘代碼,提升代碼的複用性。
繼承的關鍵字是extends。繼承的語法形式是class A extends B{}。類A稱爲父類、超類、基類,類B稱爲子類、衍生類和導出類。
子類沒法繼承父類的構造方法。
構造方法是一種特殊的方法,子類沒法繼承父類的構造方法。
子類不能繼承父類中不符合訪問權限的成員。
子類不能繼承private修飾或者不在同包內的default修飾的父類變量。
final 關鍵字用法以下:
若用於修飾屬性,則該屬性不容許被修改。
若用於修飾父類中的方法,則該方法不容許被子類重寫。
若用於修飾類,則該類不容許被其餘類繼承。
final修飾的方法能夠被重載,但不能被重寫。
super關鍵字用於引用父類的對象。
super()用於調用父類的構造方法來建立子類對象,和this()同樣,必須寫在構造方法中的第一行。
須要注意的是,子類的構造方法中若是不寫 super()
,編譯器會幫助你在子類構造方法的第一行加上super()
,由於在子類中調用父類構造器是「必須的」。但若是父類中只存在有參構造方法,並無提供無參構造方法,則須要在子類構造方法中顯式地調用父類存在的構造器,不然可能由於父類中沒有無參構造器而獲得一個編譯錯誤。
使用super調用重寫方法在父類的方法:super.重寫方法()。其做用是對父類方法進行一些補充。
多態能夠優雅的解決程序中的擴展性問題。
在形式上,父類引用能夠指向子類對象。在 Vehicle vehicle = new Car();
中,子類的 Car
對象賦值給了父類 Vehicle
引用,這稱爲向上轉型;
在引用 vehicle
上調用方法,在運行時刻究竟調用的是父類 Vehicle
中的方法仍是子類 Car
中的方法呢?實際須要經過運行時的對象類型來判斷,這稱爲動態綁定。
向上轉型和動態綁定就是多態的具體實現機制。
向上轉型的過程當中涉及了繼承、重寫和父類引用子類對象的概念。
向上轉型的對象調用重寫方法,執行的是子類的重寫方法。
向上轉型的好處,是不須要針對父類的多個子類再設計不一樣的方法,減小了代碼量而且增長了可拓展性。
動態綁定是指在編譯期間方法並不會和「引用」的類型綁定在一塊兒,而是在程序運行的過程當中,JVM 須要根據具體的實例對象才能肯定此時要調用的是哪一個方法。重寫方法遵循的就是動態綁定。
例如,子類和父類中都有info()方法。動態綁定就是指,編譯過程當中info()不會和具體的類綁定到一塊兒,而是在運行期間列舉出子類和父類的info()方法,根據當前的實例對象,調用該實例對象的info()方法。
靜態綁定是指程序編譯期的綁定。如下的類信息,使用的就是靜態綁定機制:
final
、static
或 private
修飾的方法,以及重載方法和構造方法。一種推薦的編程思想是「面向基類」編程。也就是建議將面向的「對象」抽象爲更高層次的基類。
向上轉型的缺點:轉型後的對象沒法調用導出類中的方法。
向下轉型:強制從基類轉向衍生類。
確保向下轉型的正確性:使用instance of運算符。
不管子類構造函數中有沒有super();實際運行時都會調用父類的構造器。
當子類重寫了父類的方法時,調用主體(即對象)和方法是運行時綁定的;
當子類和父類的屬性重名時,調用主體(即對象)和屬性是編譯時綁定的。
這兩種說法,前一種是方法的動態綁定,後一種是成員變量的靜態綁定。
java在編譯時肯定對象類型,同時對這個對象的成員變量進行靜態編譯。也就是說,sup對象的i屬性是在Super類中加載的。
此時sub對象的i屬性是在sub類中加載的。
首先,定義了A類型的變量ab,在編譯過程當中會加載A類,此時A中的靜態塊會被執行,打印「1」。而後ab對象被賦值爲實例化對象的引用,此時須要執行B類的構造函數,首先加載B類到內存中,此時B類中的靜態塊會被執行,打印"a"。而後,執行B類的構造函數。由於B類是A類的子類,因此必定會首先執行A的構造函數,所以打印「2」,而後再執行B中的構造函數,打印「b」。第二句語句,由於A類和B類都不是首次加載,因此只會調用B類的構造函數,打印「2」再打印"b"。所以打印出來結果是"1a2b2b"。
//如下代碼 class Student{ int age = 1; public Student(int age) { this.age = age; } } public class Test extends Student{ public static void main(String[] args) { Test t = new Test(); System.out.println(t.age); } } //執行後,控制檯輸出爲? 編譯時錯誤(可見錯誤)
使用Test類的構造函數,會首先調用父類的構造函數,因爲當前僅定義了父類的有參構造方法,因此JVM不會再建立父類的無參構造函數,所以程序出現編譯時錯誤。
//如下代碼 class Student{ public static void eat() { System.out.print(1); } } public class Test extends Student{ public static void eat() { System.out.print(2); } public static void main(String[] args) { eat(); } } //執行後,控制檯輸出爲?2
此時,沒有指明調用的是哪一個類中的重寫方法,由於入口函數是子類中,因此「就近」調用子類的重寫方法。
//如下代碼 class Student{ public void eat() { System.out.print(1); } } public class Test extends Student{ private void eat() { System.out.print(2); } public static void main(String[] args) { Test t = new Test(); t.eat(); } } //執行後,控制檯輸出爲?編譯錯誤
重寫方法中,權限修飾符不能比父類方法的範圍小
若是範圍相等,或擴大則不會報錯。
//如下代碼 class Student{ int i; public Student(int i) { this.i = i; } } public class Test extends Student{ int i = 1; public Test(int i) { super(10); this.i = i; } public static void main(String[] args) { Student t = new Test(100); System.out.print(t.i); } } //執行後,控制檯輸出爲?10
編譯時,首先根據靜態綁定將父類t.i初始化爲0;而後調用Test(100)構造函數時,首先顯式調用父類有參構造函數,此時父類t.i=10,繼續執行剩下的Test類構造函數,子類i=100。最後打印的是父類的t.i=10。
因爲靜態綁定,儘管它是子類對象,可是它在編譯時綁定了父類成員變量,也會一直用父類成員變量。
//如下代碼 class Student{ int i; public Student(int i) { this.i = i; } } public class Test extends Student{ int i = 1; public Test(int i) { super(10); this.i = i; } public static void main(String[] args) { Test t = new Test(100); System.out.print(t.i); } } //執行後,控制檯輸出爲?100
同理,這個對象的類型是Test類型,因此因爲靜態綁定,t對象的成員變量是Test類中的成員變量。
首先子類初始化t.i=1,而後調用Test類的有參構造函數,其中先顯式調用父類的有參構造函數 ,父類i成員變量初始化爲0,又賦值爲10,而後繼續執行子類構造函數,t.i=100。最後顯示的結果是子類t.i爲100。
因此這種題型只須要肯定對象靜態綁定的類型是什麼類型(即定義爲何類型),跟蹤這個變量的變化就能夠了。
//如下代碼 class Student{ public Student() { System.out.print(1); } } public class Test extends Student{ String name; public Test(String name) { this.name = name; System.out.print(2); } public static void main(String[] args) { Test t = new Test("zs"); } } //執行後,控制檯輸出爲?12
首先,t對象靜態綁定Test類,因此t.name默認值爲null。實例化調用Test類的有參構造函數時,首先調用父類的無參構造函數,打印1,而後繼續執行子類的構造函數,t.name="zs",而後打印2。
//如下代碼 class Student{ int age; public Student(int age) { this.age = age; } } public class Test extends Student{ public Test(int age) { super(age); } public static void main(String[] args) { Test t = new Test(10); System.out.println(t.age); } } //執行後,控制檯輸出爲?10
子類繼承了父類的age屬性。
class Student1{ int age; public Student1(int age) { this.age = age; } } public class Test extends Student1{ int age; public Test(int age) { super(age); } public static void main(String[] args) { Test t = new Test(10); System.out.println(t.age); } }
若是沒有繼承,那麼子類構造函數中僅調用父類構造函數,修改的是父類對象的屬性,並無修改子類age屬性,所以打印默認值0。
緣由是局部變量一旦定義就確定會被用到,須要使用者賦初值0.0不然會引發編譯錯誤。
super()第一行的緣由:若是super()不在第一行,那麼編譯器會自動在第一行補充上super();
this()第一行的緣由:若是this()不在第一行,那麼編譯器會自動在第一行補充上super();
那麼就會出現一個構造方法內,構造出多個對象的狀況。JVM不容許這樣的狀況出現。