1. 程式人生 > 其它 >Java基礎知識複習2--面向物件篇

Java基礎知識複習2--面向物件篇

所以內容均來自於b站“遇見狂神說”

構造器

一旦定義了一個有參構造,則必須顯示的定義一個無參構造!!

package com.objectOriented.test1;
public class Student {
    String name;
    int age;
    public Student() {//有了有參構造必須定義一個無參的
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

mac中的構造快捷鍵是Command+n


小解物件記憶體分析

推薦原講解視訊:建立物件記憶體分析


三大特徵之“封裝”


三大特徵之“繼承”

mac的idea中檢視繼承關係的快捷鍵是ctrl+h

super

私有的東西無法被”直接繼承“,既不能被直接訪問但是可以間接的訪問,比如上面通過上面的封裝操作:一個方法在父類中是public修飾的,則可以在子類中直接呼叫;倘若是private修飾的,則不可以在子類中使用super關鍵字直接呼叫,應當通過父類中的getset方法既封裝來操作!

呼叫父類的構造方法,必須放在子類構造器的第一行(既首先呼叫父類的構造方法),否則報錯

public Class Person{
  public Person(){
    System.out.printLn("Person的無參構造執行了!")
  }
}
public Class Student extends Person{
  public Student(){
     //super(); //這個是隱藏的,預設的可以不寫,倘若要寫就一定要放在第一行!!!!
     System.out.printLn("Student的無參構造執行了!")
  }
}

強烈建議子類一定要把無參和有參的構造寫一個出來


方法重寫

注意,方法重寫和方法過載是不一樣的概念!!重寫都是方法的重寫,和屬性無關!

重寫關鍵字:Override

測試一:

public class A {
    public static void test(){//靜態方法
        System.out.println("A->test");
    }
}
public class B extends A{
    public static void test(){//靜態方法
        System.out.println("B->test");
    }
}
public class Application {
    public static void main(String[] args) {
        B b=new B();
        b.test();
        A a=new B();//父類的引用指向了子類
        a.test();
    }
}

執行結果是:

測試二:

public class A{
    public void test(){ //沒有static修飾
        System.out.println("A->test");
    }
}
public class B extends A{
    @Override//重寫 必須要有
    public void test() {//同上
        super.test();
    }
}
public class Application {
    public static void main(String[] args) {
        B b=new B();
        b.test();
        A a=new B();//父類的引用指向了子類
        a.test();
    }
}

執行結果:

為什麼上面的差距這麼大,借用彈幕的話:

因為靜態方法是類的方法,而非靜態類是物件的方法,有static時,a呼叫了A類的方法,因為a是用A類定義的;沒有static時,A呼叫的是物件的方法,而a是用B類new的,既a是B類new出來的物件,因此呼叫了B的方法!

至於為什麼可以這樣寫,這是多型的內容,重寫就是為了多型而生!的!

 A a=new B();

另外,靜態方法只能被繼承,不能被重寫

說到底,其本質是:a(地址)引用,指向了B類物件,所以呼叫的就是B類的方法


三大特徵之多型(*******)

舉個簡單的例子,上面繼承那一欄裡面:

A a=new B();

既多型,父類引用子類物件!因為子類繼承了父類的全部!!這樣寫是沒有問題的!

測試一:

public class Person {
    public void run(){
        System.out.println("父類的run");
    }
}
public class Student extends Person{}
public class Application {
    public static void main(String[] args) {
        Student s1=new Student();
        Person p1=new Student();
        s1.run();
        p1.run();
    }
}

執行:

很好理解,父類和子類都有run方法,父類呼叫子類物件的方法,由於子類的run方法繼承的是父類的沒有改變,所以還是父類的run方法!

測試二:子類重寫父類方法

public class Person {
    public void run(){
        System.out.println("父類的run");
    }
}
public class Student extends Person{
    @Override
    public void run() {
        System.out.println("子類的run");
    }
}
public class Application {
    public static void main(String[] args) {
        Student s1=new Student();
        Person p1=new Student();
        s1.run();
        p1.run();
    }
}

執行結果:

同上,父類呼叫子類的run方法,但是由於子類重寫了run方法,所以執行的是子類重寫後的run方法!

測試三:父類呼叫子類新增的方法

public class Person {
    public void run(){
        System.out.println("父類的run");
    }
}
public class Student extends Person{
    @Override
    public void run() {
        System.out.println("子類的run");
    }
    public void eat(){
        System.out.println("子類的eat");
    }    
}
public class Application {
    public static void main(String[] args) {
        Student s1=new Student();
        Person p1=new Student();
        s1.run();
        p1.eat();//一定會報錯
    }
}

這個很好理解,因為父類沒有該方法!能否執行看引用型別(左),執行內容看實際型別(右)

所以,假如是Person p1=new Student()這種,在與static無關的情況下,父類要執行的方法必須要是父子兩個都有的!假如子類沒有重寫,則父類呼叫的還是父類本身的方法(子類繼承的是父類的沒變),子類重寫了則呼叫的是子類重寫的方法!

//Student能呼叫的方法都是自己的或者繼承父類的
Student s1=new Student();
//父型別,可以指向之類,但是不能呼叫子類獨有的方法
Person p1=new Student();

instanceof關鍵字

判斷兩個類之間是否存在父子關係

public class Person {
    public void run(){
        System.out.println("父類的run");
    }
}
public class Student extends Person{
    @Override
    public void run() {
        System.out.println("子類的run");
    }
}
public class Teacher extends Person{
    @Override
    public void run() {
        super.run();
    }
}
public class Application {
    public static void main(String[] args) {
        //Object->Person->Student
        //Object->Person->Teacher
        //Object->String
        Object object=new Student();
        System.out.println(object instanceof Student);
        System.out.println(object instanceof Person);
        System.out.println(object instanceof Object);
        System.out.println(object instanceof Teacher);
        System.out.println(object instanceof String);
    }
}

執行:

同理:

Person person=new Student();
System.out.println(person instanceof Student);
System.out.println(person instanceof Person);
System.out.println(person instanceof Object);
System.out.println(person instanceof Teacher);
//System.out.println(person instanceof String);

執行:

首先要明白一個規矩,既java遵循”編譯看左(引用型別),執行看右(實際指向型別)“的規矩,所以最後一行程式碼報錯,既首先編譯就通不過,因為Person類和String是平級,沒有父子關係!第4個是false是因為編譯的時候看左(Person類)和Teacher有父子關係,可行!實際執行的時候看右(Student類)和 Teacher屬於同級沒有關係!

型別之間的轉換:父與子

同基本資料型別一樣,低轉高自動轉換,但是高轉低就需要強制了!

public class Person {
    public void run(){
        System.out.println("父類的run");
    }
}
public class Student extends Person{
    @Override
    public void run() {
        System.out.println("子類的run");
    }
    public void go(){
        System.out.println("子類的獨有的go方法");
    }
}

子轉父:低轉高,向上轉型,直接轉,丟失子類中原本可以直接呼叫的特有方法

Student s1=new Student();
s1.go();
Person p1=s1;
//p1.go();程式碼報錯,因為沒有go方法(既會丟失子類特有的方法)

父轉子:高轉低,向下轉型,強制轉,丟失父類被子類所重寫掉的方法!

Person p2=new Student();
//p2不能使用Student獨有的方法,如若使用強制轉換
Student s1=(Student)p2;//高轉低(右轉左)需要強制轉換
s1.go();
//或者
((Student)p2).go();

static關鍵字總結

用在方法上叫靜態方法(也可以叫類方法,通過類名訪問),用在屬性上叫靜態屬性(如靜態變數,也叫類變數)!

加了static關鍵字的,是從屬於這個類的,別人用不了,只有本類能用!

public class Test1 {
    {
        //匿名程式碼塊,賦初始值
        System.out.println("匿名程式碼塊!");
    }
    static {
        //靜態程式碼塊,跟類一載入就執行,永久只執行一次!
        System.out.println("靜態程式碼塊!");
    }
    public Test1(){
        //構造器
        System.out.println("建構函式!");
    }
    public static void main(String[] args) {
        Test1 t1=new Test1();
        System.out.println("=============");
        Test1 t2=new Test1();
    }
}

執行:

同樣的,一個類被final修飾了就不能被繼承了,稱之為斷子絕孫修飾符


抽象類


介面(*********)

//interface 定義的關鍵字 介面都需要有實現類
public interface UserService {
    //介面中的屬性預設都是靜態常量,既public static final
    int AGE=10;
    //介面中的所有定義預設都是抽象的public,既定義的時候public abstract可以不用寫
    void add(String name);
    void delete(String name);
    void update(String name);
    void query(String name);
}
public interface TimeService {
    void timer();
}
//類只能單繼承,而介面可以多繼承,且必須重新接口裡面的方法
public class UserServiceImpl implements UserService,TimeService{
    @Override
    public void add(String name) {}
    @Override
    public void timer() {}
    @Override
    public void delete(String name) {}
    @Override
    public void update(String name) {}
    @Override
    public void query(String name) {}
}

總結:


內部類(面試愛問)

所謂內部類就是在一個類的內部再定義一個類,比如A類中定義一個B類,則B類稱之為A類的內部類,A類稱之為B類的外部類!

成員內部類

public class Outer {
    private int id;
    public void out(){
        System.out.println("這是外部類的方法");
    }
    public class Inner{
        public void in(){
            System.out.println("這是內部類的方法");
        }
    }
}
public class Application {
    public static void main(String[] args) {
        Outer outer=new Outer();
        //內部類的建立格式
        Outer.Inner inner=outer.new Inner();
        inner.in();
    }
}

執行:

那麼內部類能幹嘛呢,我們通過內部類可以獲得外部類的私有屬性和方法

靜態內部類

public class Outer {
    private int id;
    public void out(){
        System.out.println("這是外部類的方法");
    }
    public static class Inner{
        public void in(){
            System.out.println("這是內部類的方法");
        }
        public void getId(){
            //System.out.println(id);會報錯
        }
    }
}

上面會報錯的原因是因為:按照我們之前在static總結的來分析,類的執行(載入)順序是靜態->匿名->建構函式!所以,假如上面的的程式碼要想通過,得把id也用static修飾才可以!

區域性內部類

public class Outer{
  //區域性內部類
    public void method(){
        class Inner2{
            public void in(){}
        }
    }
}

需要說明的是,一個java檔案裡面只能有一個public class,但是卻可以有多個class,既:

public class A{}
//public class A{} 錯誤
class C{}
class D{}
......

匿名內部類

public class test {
    public static void main(String[] args) {
        new Apple().eat();
    }
}
class Apple{
    public void eat(){
        System.out.println("吃了蘋果");
    }
}

執行:

你甚至還可以在下面寫上一個介面,當然這個就不做描述了!


異常

三種異常:

捕獲和丟擲異常

public class Exception1 {
    public static void main(String[] args) {
        int a=10;
        int b= 0;
        System.out.println(a/b);
    }
}

如何捕獲這異常呢?-----------使用try/catch語句

public class Exception1 {
    public static void main(String[] args) {
        int a=10;
        int b= 0;
        try {//try表示監控區域(有問題轉到catch)
            System.out.println(a/b);
        }catch (ArithmeticException e){//catch為捕獲異常
            System.out.println("算術執行異常!");
        }finally {//處理善後工作
            System.out.println("over");
        }
        //finally可以不要,但是像涉及到IO,資源的關閉可以放到finally裡!
    }
}

另外,try/catch也可以像if/else一樣可以有多個catch:

public class Exception2 {
    public static void main(String[] args) {
        try {
            new Exception2().a();
        }catch (Error e){
            System.out.println("Error");
        }catch (Exception e){
            System.out.println("Exception");
        }catch (Throwable e){
            System.out.println("Throwable");
        }finally {
            System.out.println("over");
        }
    }
    public void a(){
         b();
    }
    public void b(){
        a();
    }
}

注意,異常等級越高的一定要放在後面!根據先後執行的順序,滿足了就不會執行後面的語句了!

不知道是什麼異常可以這樣寫:

try{
  .....
}catch(Exception e){
  e.printStackTrace();//打印出具體的異常資訊!
}

異常的丟擲

public class Exception3 {
    public static void main(String[] args) {
        try{
            new Exception3().test(3,0);
        }catch (ArithmeticException e) {
            e.printStackTrace();
        }
    }
    //假設這個方法中處理不了這個異常,則可以主動丟擲異常,讓更高級別的去處理
    public void test(int a,int b){
        if(b==0){
            throw new ArithmeticException();
        } else{
            System.out.println(a/b);
        }
    }
}

自定義異常

//自定義一個異常類,要繼承Exception
public class MyException extends Exception{
    private int detail;
    public MyException(int a){
        this.detail=a;
    }
    //toString:異常的列印資訊
    @Override
    public String toString() {
        return "MyException{"+detail+"}";
    }
}
public class Test {
    //可能會存在異常的方法
    static void test(int a) throws MyException {//注意,這裡是throws不是throw
        System.out.println("傳遞的引數為:"+a);
        if(a>10){
            throw new MyException(a);//處理為要麼這裡try/catch,要麼丟擲去
        }
        System.out.println("OK");
    }
    public static void main(String[] args) {
        try {
            test(11);
        }catch (MyException e){
            //e.printStackTrace();
            System.out.println("MyException>>"+e);//e即為執行toString()
        }
    }
}

異常在實際應用中的經驗總結