類與物件(二)
目錄
程式碼塊的定義與使用:
定義:使用“{ }”定義的一段程式碼。
根據程式碼塊定義的位置以及關鍵字,可以分為以下四種:
1.普通程式碼塊
2.構造塊
3.靜態程式碼塊
4.同步程式碼塊
1.普通程式碼塊
定義在方法中的程式碼塊。
2.構造塊
定義在類中的程式碼塊(不加修飾符)。
舉個栗子:
class A{ public A(){ System.out.println("這是構造方法"); } { System.out.println("這是構造塊"); } } public class Test{ public static void main(String[] args){ A a1 = new A(); A a2 = new A(); } }
執行結果:
通過上述程式碼我們發現:在物件產生時,構造塊優先於構造方法執行,有幾個物件產生,就呼叫幾次構造塊。構造塊可以完成一些屬性的初始化操作(在呼叫構造方法前)。
3.靜態程式碼塊
使用static定義的程式碼塊。
根據靜態塊所在的類不同,可以分為以下兩種型別:
1.在非主類中
2.在主類中
3.1在非主類中的靜態程式碼塊
舉個栗子:
class A{ public A(){ System.out.println("這是構造方法"); } { System.out.println("這是構造塊"); } static{ System.out.println("這是靜態塊"); } } public class Test{ public static void main(String[] args){ System.out.println("-----start-----"); A a1 = new A(); A a2 = new A(); System.out.println("-----end-----"); } }
執行結果:
通過上述程式碼我們發現:靜態程式碼塊在類載入時執行,優先於構造塊執行,無論有多少物件產生,只會呼叫一次。
靜態塊的主要作用是為static屬性進行初始化。
3.2在主類中的靜態程式碼塊
舉個栗子:
public class Test{ { System.out.println("主類的構造塊"); } static{ System.out.println("主類的靜態塊"); } public Test(){ System.out.println("主類的構造方法"); } public static void main(String[] args){ System.out.println("-----start-----"); A a1 = new A(); A a2 = new A(); System.out.println("-----end-----"); } }
執行結果:
通過上述程式碼我們可以發現:在主類中定義的程式碼塊,優先於主方法執行。
4.同步程式碼塊
與執行緒同步問題有關,在此不多加闡述。
程式碼塊的基本知識都已經明白了,接下來我們看一道習題,結合了上述所有知識點。可以自己想想答案是什麼。
class HelloA{
public HelloA(){
System.out.println("這是父類的構造方法");
}
{
System.out.println("這是父類的非靜態程式碼塊");
}
static{
System.out.println("這是父類的靜態程式碼塊");
}
}
class HelloB extends HelloA{
public HelloB(){
System.out.println("這是子類的構造方法");
}
{
System.out.println("這是子類的非靜態程式碼塊");
}
static{
System.out.println("這是子類的靜態程式碼塊");
}
}
public class Test{
public static void main(String[] args){
System.out.println("-----start-----");
new HelloB();
new HelloB();
System.out.println("-----end-----");
}
}
繼承的定義與使用:
1.繼承的實現
在Java中,繼承使用extends關鍵字來實現。定義的語法如下:
class 子類 extends 父類
注:子類又時也被稱為派生類,父類也被稱為超類或基類。
在發生了類的繼承關係後,子類可以直接繼承父類的操作,實現程式碼的重用。
子類最低也維持和父類相同的功能。
子類可以進行功能上的擴充。
2.繼承的限制
子類物件例項化前,首先呼叫父類構造方法產生父類物件後,再呼叫子類構造方法例項化子類物件。
舉個栗子:
class Person{
public Person(){
System.out.println("父類物件產生");
}
}
class Student extends Person{
public Student(){
super();//此語句在無參時寫與不寫都一樣
System.out.println("子類物件產生");
}
}
public class Test{
public static void main(String[] args){
new Student();
}
}
執行結果:
注:實際上在子類的構造方法中,相當於隱含了一條語句,super( );如果父類中沒有提供無參構造,就必須使用super( )明確指明你要呼叫的構造方法。
Java只允許單繼承,不允許多繼承。(Java的單繼承侷限)
要想在Java中實現類似的“多繼承”,要麼多層繼承,要麼使用內部類。
//多層繼承
class A(){}
class B extends A(){}
class C extends B(){}
多層繼承層數不建議太多,最好不要超過3層。
在繼承時,子類會繼承父類的所有結構。(包含私有域與其他屬性,方法)
顯式繼承:所有非私有操作(非private操作)屬於顯式繼承(可以直接呼叫)
隱式繼承:所有私有操作(private操作)屬於隱式繼承(不可以直接呼叫,需要通過其他形式呼叫,例如getter,setter)
覆寫:
定義:子類定義了與父類方法名稱,引數列表,返回值完全相同的方法。被覆寫的方法不能擁有比父類更為嚴格的訪問控制權限。
private < default < protected < public
舉個栗子:
class Person{
public void print(){
System.out.println("父類的print方法");
}
}
class Student extends Person{
public void print(){
System.out.println("子類的print方法");
}
}
public class Test{
public static void main(String[] args){
new Student().print();
}
}
執行結果:
以後在進行覆寫操作的時候,要注意以下兩點:
a.看new在哪(當前使用的物件是通過哪個類new的)
b.呼叫的方法有沒有被子類覆寫,如果被覆寫,呼叫的一定是被覆寫後的方法。
覆寫和過載的區別:
區別 | 過載(overload) | 覆寫(override) |
概念 | 方法名稱相同,引數的型別及個數不同 | 方法名稱,返回值型別,引數的型別及個數完全相同 |
範圍 | 一個類 | 繼承關係 |
限制 | 沒有許可權要求 | 被覆寫的方法不能擁有比父類更為嚴格的訪問控制權限 |
super關鍵字:
1.super用於方法
a.用於構造方法
表示呼叫父類構造方法。
語法:super (引數列表)
I. 當子類呼叫父類無參構造時,super( )可寫可不寫,表示呼叫父類無參構造。
II. 當子類呼叫父類有參構造時,super(引數列表)必須要寫,要告訴編譯器當前呼叫的是哪個有參構造。
注意:
(1)子類構造方法中呼叫父類構造必須是第一行語句
(2)this與super不能同時呼叫
b.用於普通方法
語法:super.方法名(引數)
用於在子類中明確呼叫父類被覆寫的方法。
舉個栗子:
class Person{
private String name;
public Person(String name){
this.name = name;
System.out.println("父類的有參構造");
}
public void print(){
System.out.println("父類的print方法");
}
}
class Student extends Person{
public Student(){
super("li");//super用於構造方法
System.out.println("子類的無參構造");
}
public void print(){
super.print();//super用於普通方法
System.out.println("子類的print方法");
}
}
public class Test{
public static void main(String[] args){
new Student().print();
}
}
執行結果:
2.super用於屬性
super.屬性名
表示呼叫父類中被覆寫的屬性
final關鍵字:
1.final修飾類(String類以及8大基本資料型別的包裝類,Integer)
a.當一個類被final關鍵字修飾,表示該類不能擁有子類(該類不允許被繼承)。
b.一旦一個類被final修飾,該類的所有方法都會預設加上final。
2.final修飾方法
當一個方法被final修飾,明確表示該方法不能被覆寫。
3.final修飾變數
I.final修飾普通資料型別的成員變數
被final修飾的成員變數必須在宣告時初始化(構造塊或構造方法中初始化),並且初始化後值無法被修改。
II.final修飾引用資料型別的變數
其引用不可變,即不能再指向其他的物件。
多型性:
1.方法的多型性
a.方法的過載:同一個方法名稱可以根據引數的型別或個數不同,呼叫不同的方法體。
b.方法的覆寫:同一個父類的方法,可能根據例項化子類的不同也有不同的實現。
2.物件的多型性
a.物件的向上轉型:用於引數統一化
語法:父類 父類引用 = new 子類();
不管是否發生了向上轉型,其核心本質還在於:你使用的是哪一個子類(new在哪裡),而且呼叫的方法是否被子類覆寫了。
b.向下轉型:當父類引用需要呼叫子類擴充方法時,才需要向下轉型
語法:子類 子類引用 = (子類)父類引用;
注:要發生向下轉型,必須先發生向上轉型
舉個栗子:
class Person{
public void print(){
System.out.println("我是父類方法");
}
}
class Student extends Person{
public void print(){
System.out.println("我是子類方法");
}
public void fun(){
System.out.println("只有子類有");
}
}
public class Test{
public static void main(String[] args){
Person per = new Student();//向上轉型
per.print();
System.out.println("-----------------------");
//這個父類能夠呼叫的方法只能是本類定義好的方法
//所以並沒有子類中的dun()方法,那麼只能向下轉型
Student stu = (Student) per;//向下轉型
stu.fun();
}
}
執行結果:
內部類的定義與使用:
內部類:在類內部進行其他類結構巢狀操作。
1.內部類的優缺點:
內部類的優點:
a.內部類與外部類可以方便的訪問彼此的私有域(包括私有方法,私有屬性)
b.內部類是另外一種封裝(保護性),對外部的其他類隱藏
c.內部類可以實現Java單繼承的侷限
缺點:
結構複雜
2.內部類與外部類的關係:
a.對於非靜態內部類,內部類的建立需要依賴外部類物件,在沒有外部類例項之前無法建立非靜態內部類。
b.內部類是一個相對獨立的個體,與外部類沒有is--a關係。
c.內部類可以直接訪問外部類的元素(包含私有域),但是外部類不可以直接訪問內部類元素,需要通過內部類的引用間接訪問。
3.建立內部類語法
a.在外部類外部建立非靜態內部類
外部類.內部類 內部類引用 = new 外部類().new 內部類();
Outter.Inner in = new Outter( ).new Inner( );
b.在外部類外部建立靜態內部類
外部類.內部類 內部類引用 = new 外部類.內部類( );
Outter.Inner in = new Outter.Inner( );
4.內部類的分類
I.成員內部類(類比成員方法)
a.成員內部類內部不能存在任何static變數或方法,可以訪問外部類的靜態域
b.成員內部類是依附外部類的,所有隻有先建立了外部類,才能建立內部類。
II.靜態內部類(類比靜態方法)
a.靜態內部類的建立不需要依賴外部類,可以直接建立
b.靜態內部類不可以使用任何外部類的非static域(包含屬性與方法),但是可以存在自己的成員變數
舉個栗子:
class Outter{
private static String msg = "Hello World";
//定義一個內部類
static class Inner{
public void print(){
//只能使用外部類的靜態屬性
System.out.println(msg);
}
}
//在外部類定義一個方法,負責產生內部類物件並且呼叫print方法
public void fun(){
Inner in = new Inner();//內部類物件
in.print();//內部類提供的print方法
}
}
public class Test{
public static void main(String[] args){
Outter.Inner in = new Outter.Inner();
in.print();
}
}
執行結果:
III.方法內部類(區域性內部類)
a.方法內部類不允許使用訪問許可權修飾符 public、private、protected
b.方法內部類對外部完全隱藏,除了建立這個類的方法可使用以外,其他地方均不能訪問
c.方法內部類如果要想使用方法形參,該形參必須使用final宣告(JDK8將形參變為隱式final宣告)
IV.匿名內部類(lamdba表示式前身)
匿名內部類就是一個沒有名字的方法內部類。因此特點與方法內部類完全一樣,除此之外還有兩個自己的特點:
a.匿名內部類必須繼承一個抽象類或者實現一個介面
b.匿名內部類沒有構造方法,因為它沒有類名
舉個栗子:
interface MyInterface{
void test();
}
class Outter{
private int num;
public void display(int para){
//匿名內部類,實現了MyInterface介面
new MyInterface(){
@Override
public void test() {
System.out.println("匿名內部類"+para);
}
}.test();
}
}
public class Test{
public static void main(String[] args){
Outter outter = new Outter();
outter.display(20);
}
}
執行結果:
5.內部類的特點
a.破壞了程式的結構
b.方便進行私有屬性的訪問。(外部類也可以訪問內部類的私有結構)
c.如果發現類名稱出現了“.”,應當 立即想到內部類的概念