Java入門系列04 -- 繼承,注解,訪問控制,static,單例模式,final,常量,嵌套類,內(nèi)部類,局部類,抽象類

繼承

  • 繼承使用extends關(guān)鍵字

Object

  • 任何類最終都繼承自java.lang.Object,一般稱之為基類;

同名的成員變量

  • 子類可以定義與父類同名的成員變量,但不推薦這么做;

方法的重寫

  • 子類的方法簽名與父類的一樣,叫做方法的覆蓋,重寫;
  • 子類重寫的方法返回值類型必須 <= 父類的方法返回值類型;
  • 子類重寫的方法權(quán)限 >= 父類的方法權(quán)限;

super

  • 訪問父類的成員變量;
  • 調(diào)用父類中定義的方法,包括構(gòu)造方法;

構(gòu)造方法的細節(jié)

  • 子類的構(gòu)造方法,必須先調(diào)用父類的構(gòu)造方法,然后再執(zhí)行后面的代碼;
  • 案例代碼:定義兩個類Student與Person且Student繼承自Student
public class Person {
    
    public Person () {
        System.out.println("Person()");
    }
}
public class Student extends Person{
    
    public Student () {
        System.out.println("Student()");
    }
}
public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Student student = new Student();
    }
}
  • 發(fā)現(xiàn)new Student(),依然會首先調(diào)用父類Person的構(gòu)造方法;
  • 如果子類的構(gòu)造方法沒有顯示調(diào)用父類的構(gòu)造方法,編譯器會自動調(diào)用父類無參的構(gòu)造方法,若此時父類沒有無參的構(gòu)造方法,編譯器將報錯;
public class Person {
    
    public Person () {
        System.out.println("Person()");
    }
    
    public Person (int age) {
        
    }
}
  • new Student(),會首先調(diào)用父類Person的構(gòu)造方法public Person (),Person現(xiàn)還有一個構(gòu)造方法public Person (int age),若此時將
    public Person ()刪除,程序?qū)箦e,因為new Student(),會首先調(diào)用父類Person的構(gòu)造方法,發(fā)現(xiàn)找不到目標方法;還可以改成Student構(gòu)造方法,調(diào)用父類的構(gòu)造方法;
public class Student extends Person{
    
    public Student () {
        super(0);
        System.out.println("Student()");
        
    }
}

注解Annotation

  • 兩個常見的注解:
    • @override:告訴編譯器這是一個重寫后的方法;
    • @SuppressWarnings("警告類別"):讓編譯器不生成警告信息;
image.png
  • student變量沒有被使用,會被警告,消除警告,可選中student,點擊command + 1,出現(xiàn)如下所示:
    image.png
  • 最后成如下所示,消除了警告:
image.png

訪問控制

  • Java中有4個級別的訪問權(quán)限,從高到低如下所示:
    • public:在任何地方都是可見的;
    • protected:僅在自己的包中,自己的子類中可見;
    • 無修飾符(package-private):僅在自己的包中可見;
    • private:僅在自己的類中可見;
  • 使用注意點:
    • 上述4個訪問權(quán)限都可以修飾類的成員,比如成員變量,方法,嵌套類等;
    • 只有public,無修飾符(package-private)可以修飾頂級類(Top-level Class)
    • 上述4個權(quán)限不可以修飾局部類,局部變量;
    • 一個Java源文件中可以定義多個頂級類,public頂級類的名字必須和文件名一樣;
image.png
  • 在Java中,可以類嵌套,即A類中可以定義一個B類,那么最外層的類A,就可被稱之為頂級類;

封裝

  • 在Java中封裝:是指類的成員變量私有化,只能在本類中訪問,提供setter,getter方法讓外界進行訪問,如下所示:
package com.lyy.model;

public class Person {
    private String name;
    private int age;
    
    public void setAge(int age) {
        this.age = age;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
}
  • 生成getter,setter方法的快捷鍵 shift + command + S,或者
    編輯區(qū)右擊 -> source -> 選擇生成getter,setter方法

Static

  • static:常用來修飾類的成員,包括成員變量,方法,嵌套類;

  • 成員變量被static修飾時,

    • 稱之為類變量,靜態(tài)變量,其在程序運行過程中只占用一份固定的內(nèi)存(存儲在方法區(qū)),可達到數(shù)據(jù)共享;
    • 可以通過類名實例對象進行訪問;
  • 成員變量沒有被static修飾時,

    • 稱之為實例變量,在每個實例對象內(nèi)部都有一份;
    • 只能通過實例對象進行訪問;
  • 方法被Static修飾時,稱之為類方法,靜態(tài)方法,通過類名進行調(diào)用;

    • 可以通過類名實例對象進行訪問;
    • 內(nèi)部不可以使用this關(guān)鍵字;
    • 可以直接訪問類方法,類變量;
    • 不可以直接訪問實例方法,實例變量;
    • 上述的規(guī)則都可以從this的本質(zhì)進行解釋;
  • 方法沒有被static修飾時,稱之為實例方法;

    • 只能通過實例對象進行訪問,不可以通過類進行訪問;
    • 內(nèi)部可以使用this;
    • 可以直接訪問實例變量,實例方法;
    • 可以直接訪問類變量,類方法;
  • 雖然可以通過實例對象去訪問類變量與類方法,但一般情況下不推薦這么使用,還是使用類名去訪問類變量與類方法;

靜態(tài)導(dǎo)入

  • 使用靜態(tài)導(dǎo)入后,就可以省略類名來訪問靜態(tài)成員(成員變量,方法,嵌套類)
  • 案例代碼:
package com.sf;
import static com.sf.Person.*;

public class Main {

    public static void main(String[] args) {
        
        Person.age = 20;
        System.out.println(Person.age);
        Person.run();
        
        //使用靜態(tài)導(dǎo)入后 可省略類名直接訪問
        age = 10;
        System.out.println(age);
        run();
    }
}
  • import static com.sf.Person.*; 屬于靜態(tài)導(dǎo)入;
  • 靜態(tài)導(dǎo)入的經(jīng)典使用場景:
package com.sf;
import static java.lang.Math.PI;

public class Main {
    public static void main(String[] args) {
        System.out.println(2 * PI * 10);
    }
}
  • 省略類名,可直接使用PI

成員變量的初始化

  • 編譯器會自動為未初始化的成員變量設(shè)置初始值;
  • 手動給實例變量進行初始化:
    • 在聲明中;
    • 在構(gòu)造方法中;
    • 在初始化塊中;
  • 在初始化塊中,編譯器會將初始化塊復(fù)制到每個構(gòu)造方法的頭部,每創(chuàng)建一個實例對象,就會執(zhí)行一次初始化塊;
  • 案例代碼:
package com.sf;

public class Person {
    //聲明
    public int age = 10;
    public String name = "yanzi";
    
    //構(gòu)造方法
    public Person () {
        age = 10;
        name = "yanzi";
    }
    
    //初始化塊
    {
        age = 100;
        name = "yanzi33";
    }
}
  • 手動給類變量進行初始化:
    • 在聲明中;
    • 在靜態(tài)初始化塊中;
  • 當(dāng)一個類被初始化時 執(zhí)行靜態(tài)初始化塊,只會執(zhí)行一次
  • 當(dāng)一個類第一次被主動使用時,JVM會對類進行初始化;
  • 案例代碼:
package com.sf;

public class Person {
    //聲明
    public static int age = 10;
    
    //靜態(tài)初始化塊 
    static {
        age = 100;
    }
}
  • 可以有多個(靜態(tài))初始化塊,按照在源碼中出現(xiàn)的順序被執(zhí)行;
  • 案例代碼:
package com.sf;

public class Person {
    //靜態(tài)初始化塊 
    static {
        System.out.println("Person -- static");
    }
    
    //初始化塊
    {
        System.out.println("Person -- init");
    }
    
    //構(gòu)造方法
    public Person() {
        System.out.println("Person -- 構(gòu)造");
    }
}
package com.sf;

public class Student extends Person{
    //靜態(tài)初始化塊 
    static {
        System.out.println("Student -- static");
    }
        
    //初始化塊
    {
        System.out.println("Student -- init");
    }
        
    //構(gòu)造方法
    public Student() {
        System.out.println("Student -- 構(gòu)造");
    }
}
package com.sf;

public class Main {

    public static void main(String[] args) {
        Student student = new Student();
    }
}
  • 執(zhí)行順序為:
image.png
  • 類的初始化,實例的初始化,構(gòu)造方法的調(diào)用都是首先完成父類的,然后再執(zhí)行子類的;

單例模式

  • 如果一個類設(shè)計成單例模式,那么在程序運行過程中,這個類只能創(chuàng)建一個實例對象;
  • 餓漢式單例模式:
/**
 * 餓漢式單例模式
 */
public class YYRocket {
    //私有的靜態(tài)的 實例變量
    private  static  YYRocket instance = new YYRocket();

    //構(gòu)造方法私有化
    private  YYRocket() {}

    //公共的靜態(tài)方法,返回唯一的實例對象
    public  static  YYRocket getInstance() {
        return  instance;
    }
}
  • 懶漢式單例模式
public class YYRocket {
    //私有的靜態(tài)的 實例變量
    private  static  YYRocket instance = null;

    //構(gòu)造方法私有化
    private  YYRocket() {}

    //公共的靜態(tài)方法,返回唯一的實例對象
    //存在線程安全問題
    public  static  YYRocket getInstance() {
        if (instance == null) {
            instance = new YYRocket();
        }
        return instance;
    }
}
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        YYRocket rocket1 = YYRocket.getInstance();
        YYRocket rocket2 = YYRocket.getInstance();
        YYRocket rocket3 = YYRocket.getInstance();

        Log.v("YYRocket",String.valueOf(rocket1 == rocket2));
        Log.v("YYRocket",String.valueOf(rocket1 == rocket3));
        Log.v("YYRocket",String.valueOf(rocket2 == rocket3));
    }
}
  • 打印結(jié)果為true,推薦使用餓漢式單例模式,效率高;

final

  • final可以修飾類,被final修飾的類,不能被繼承;
  • final可以修飾方法,被final修飾的方法,不能被子類重寫;
  • final可以修飾變量,被final修飾的變量,只能進行一次賦值,可視作常量;

常量

  • 常量的寫法:
public class YYPerson {
    public static final double HEIGHT = 172.5;
}
  • static final 一起修飾常量,常量通常大寫;
  • 如果將基本類型或字符串定義為常量,并且在編譯時就能確定值,那么編譯器會使用常量值替代各處的常量名,類似于C語言的宏替換,通常稱為編譯時常量;
public class MainActivity extends AppCompatActivity {

    static final int AGE = 100;
    static final String name = "yanzi";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.v("MainActivity",String.valueOf(AGE));
        Log.v("MainActivity",String.valueOf(100));
        
        Log.v("MainActivity",name);
        Log.v("MainActivity","yanzi");
    }
}

嵌套類

  • 嵌套類:定義在另一個類中的類;
public class OuterClass {

    //非靜態(tài)嵌套類 (內(nèi)部類)
    class InnerClass {

    }

    //靜態(tài)嵌套類
    static class StaticInnerClass {

    }
}
  • InnerClassy與StaticInnerClass都可稱之為嵌套類;
  • 非靜態(tài)嵌套類,可稱之為內(nèi)部類InnerClass;
  • 在嵌套類外層的類,稱之為外部類OuterClass;
  • 最外層的外部類,稱之為頂級類;

內(nèi)部類

  • 沒有被static修飾的嵌套類,即非靜態(tài)嵌套類;
  • 內(nèi)部類與實例變量,實例方法一樣,其與外部類的實例對象相關(guān)聯(lián),也就是說要想使用內(nèi)部類,首先必須外部類實例化對象,
  • 內(nèi)部類不能定義除編譯時常量以外的任何static成員;
  • 內(nèi)部類可以直接訪問外部類中所有成員,即使被聲明為private;
  • 外部類可以直接訪問內(nèi)部類的成員,即使被聲明為private;
  • 案例代碼一:
package com.example.java_test.java;
import android.util.Log;

public class Person {

    private int age;

    public int getAge() {
        return age;
    }

    //非靜態(tài)嵌套類 (內(nèi)部類)
    public class Hand {
        private double height;
    }
}
import com.example.java_test.java.Person;
import com.example.java_test.java.Person.Hand;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Person person = new Person();
        //通過外部類實例 創(chuàng)建內(nèi)部類實例對象
        Hand hand1 = person.new Hand();
        Hand hand2 = person.new Hand();
    }
}
  • 內(nèi)存布局如下:
image.png
  • 案例代碼一:
package com.example.java_test.java;
import android.util.Log;

public class Person {

    private int age;

    public int getAge() {
        return age;
    }
    //訪問內(nèi)部類private成員
    public void run(Hand hand) {
        Log.v("Hand",String.valueOf(hand.height));
    }

    //非靜態(tài)嵌套類 (內(nèi)部類)
    public class Hand {
        private double height;
        //編譯時常量
        private static final int width = 10;
        //訪問外部類private成員
        public void show() {
            Log.v("Hand",String.valueOf(age));
        }
    }
}
  • 內(nèi)部類的細節(jié):
import android.util.Log;

public class OuterClass {
    private int x = 1;
    public class InnterClass {
        private int x = 2;
        public void show() {
            Log.v("InnterClass",String.valueOf(x)); //2
            Log.v("InnterClass",String.valueOf(this.x)); //2
            Log.v("InnterClass",String.valueOf(OuterClass.this.x)); //1
        }
    }
}

靜態(tài)嵌套類

  • 靜態(tài)嵌套類:被static修飾的嵌套類;
  • 靜態(tài)嵌套類在行為上就是一個頂級類,只是定義的代碼寫在了另一個類中;
public class YYPerson {

    public static class YYCar {

    }
}
import com.example.java_test.java.YYPerson.YYCar;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        YYPerson person = new YYPerson();
        //兩種創(chuàng)建方式等價
        YYPerson.YYCar car = new YYPerson.YYCar();
        YYCar car = new YYCar();
    }
}
  • YYPerson與YYCar兩者之間沒有必然聯(lián)系,所以說靜態(tài)嵌套類在行為上就是一個頂級類;
  • 對比一般的頂級類,靜態(tài)嵌套類多了一些特殊的權(quán)限;
    • 可以直接訪問外部類中成員,即使被聲明為private;
  • 嵌套類的使用場景:
    • 如果類A只用在類C內(nèi)部,可以考慮將類A嵌套到類C中;
      • 封裝性更好;
      • 程序包更加簡化;
      • 增強可讀性,維護性;
    • 如果類A需要經(jīng)常要訪問類C的非公共成員,可以考慮將類A嵌套到類C中;
      • 另外也可以根據(jù)需要將類A隱藏起來,不對外暴露;
  • 如果需要經(jīng)常訪問非公共的實例成員,設(shè)計成內(nèi)部類,否則設(shè)計成靜態(tài)嵌套類;
    • 如果必須先有C實例,才能創(chuàng)建A實例,那么可以考慮將類A嵌套到類C中;

局部類

  • 局部類:定義在代碼塊中的類(可以定義在方法中,for循環(huán)中,if語句中等等);
  • 局部類不能定義除編譯時常量以外的任何static成員;
  • 局部類只能訪問final 或者 有效final的局部變量;
    • 從Java8開始,如果局部變量沒有被第二次賦值,就可認定為有效final;
  • 局部類可以直接訪問外部類中的所有成員,即使被聲明為private;
    • 局部類只有定義在實例相關(guān)的代碼塊,才能直接訪問外部類中的實例成員;
import android.util.Log;

public class YYPerson {

    private int width;

    //靜態(tài)初始化塊 與 類相關(guān)
    static {
        class A {

        }
    }

    //初始化塊 與實例相關(guān)
    {
        class A {

        }
    }

    public void test() {
        //有效final
        int a = 10; //賦值會報錯
        //final
        final int b = 11;

        for (int i = 0; i < 10; i++) {
            class A {

            }
        }

        if (true) {
            //局部類
            class A {
                void test() {
                    Log.v("test",String.valueOf(a));
                    Log.v("test",String.valueOf(b));

                    Log.v("test",String.valueOf(width));
                }
            }
        }
    }
}

抽象方法

  • 抽象方法:被abstract修飾的方法;
    • 只有方法聲明,沒有方法實現(xiàn);
    • 不能時private權(quán)限(因為定義抽象方法的目的是讓子類去實現(xiàn))
    • 只能是實例方法,不能是類方法;
    • 只能定義在抽象類,接口中;

抽象類

  • 抽象類:被abstract修飾的類;
  • 定義抽象類的目的是給其他類繼承的,所以不能用final修飾;
  • 可以定義抽象方法;
  • 不能實例化,但可以自定義構(gòu)造方法;
  • 子類必須實現(xiàn)抽象父類中的所有抽象方法(除非子類也是一個抽象類);
  • 可以像非抽象類一樣定義成員變量,常量,嵌套類型,初始化塊,非抽象方法等;
  • 使用場景:
    • 抽取子類的公共實現(xiàn)到抽象父類中,要求子類必須要單獨實現(xiàn)的定義成抽象方法;
public abstract class Shap {

    protected double area;
    protected double girth;

    public double getArea() {
        return area;
    }

    public double getGirth() {
        return girth;
    }
    //抽象方法
    abstract public void show();
}
package com.example.java_test.java;

import android.util.Log;

public class Rectangle extends Shap{

    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public void show() {
        area = width * height;
        girth = (width + height) * 2;
        Log.v("Circle","area:" + String.valueOf(area)+ " girth:" + String.valueOf(girth));
    }
}
import android.util.Log;

public class Circle extends Shap{

    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public void show() {
        area = Math.PI * radius * radius;
        girth = Math.PI * radius * 2;
        Log.v("Circle","area:" + String.valueOf(area)+ " girth:" + String.valueOf(girth));
    }
}
import com.example.java_test.java.Circle;
import com.example.java_test.java.Rectangle;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Circle circle = new Circle(10);
        circle.show();

        Rectangle rectangle = new Rectangle(10,20);
        rectangle.show();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容