java 面向?qū)ο蠡A(chǔ)

內(nèi)存圖



成員變量和局部變量的區(qū)別


  1. 在類中的位置不同
    • 成員變量在類中, 在方法外
    • 局部變量在方法內(nèi)或方法聲明中(形參)

public class Phone {
  String x; //成員變量

  public void showPhone() {
    int y; //局部變量
  }
  
}
  1. 內(nèi)存中位置不同
    • 成員變量在堆內(nèi)存
    • 局部變量在棧內(nèi)存
  2. 生命周期不同
    • 成員變量隨著對(duì)象的消失而消失
    • 局部變量隨著方法的消失而消失
  3. 初始化值不同
    • 成員變量有默認(rèn)值
    • 局部變量沒有默認(rèn)值, 必須先賦值后使用


常用API


nextLine

獲取鍵盤輸入的字符串

用法
nextLine屬于Scanner類, 所以在使用nextLine時(shí)需要先創(chuàng)建Scanner實(shí)例, 然后調(diào)用Scanner實(shí)例的nextLine方法

 Scanner sc = new Scanner(System.in);
 String s = sc.nextLine();


String構(gòu)造方法

  • String(String original)
    把字符串封裝成字符串對(duì)象
  • String(char[] value)
    把字符數(shù)組封裝成字符串對(duì)象
  • String(char[] value, int offset, int count)
    把字符數(shù)組中從索引offset開始的count個(gè)字符封裝成字符串對(duì)象


直接賦值String和new String()的區(qū)別


String成員方法

判斷

  • boolean equals(Object object)
    比較字符串內(nèi)容是否相同
  • boolean equalsIgnoreCase(String str)
    比較字符串內(nèi)容是否相同, 忽略大小寫
  • boolean startsWith(String str)
    判斷字符串是否以指定str開頭
  • boolean endsWith(String str)
    判斷字符串是否以指定的str結(jié)尾

獲取

  • int length()
    獲取字符串長(zhǎng)度
  • char charAt(int index)
    獲取index位置的字符
  • int indexOf(String str)
    獲取str在字符串中第一次出現(xiàn)的位置
  • String substring(int start, int end)
    從start位置開始截取字符串到end位置(不包括end), 沒有給end值則默認(rèn)截取到最后


StringBuilder類


先看下面的圖



當(dāng)我們對(duì)字符串拼接時(shí), 會(huì)把拼接的字符串放在方法區(qū)常量池的一個(gè)新的地址中, 并且我們的變量指向這個(gè)新的地址, 這就意味著拼接前的字符串在拼接之后就變成了內(nèi)存中的垃圾

StringBuilder是一個(gè)可變的字符串, 可以在其后隨意拼接字符串, 這樣我們?cè)谄唇幼址畷r(shí), 就會(huì)動(dòng)態(tài)的添加到原字符串后面, 這樣就不會(huì)造成內(nèi)存垃圾了


StringBuilder構(gòu)造方法

  • StringBuilder()
    創(chuàng)建一個(gè)StringBuilder類
  • `StringBuilder(String str)
    將字符串str轉(zhuǎn)換為StringBuiler類型


StringBulider成員方法

  • public int capacity()
    返回當(dāng)前容量
  • public int length()
    返回當(dāng)前長(zhǎng)度
  • public StringBuilder append(任意類型)
    拼接并返回StringBuilder本身
  • public StringBuilder reverse()
    反轉(zhuǎn)并返回StringBuilder本身
  • public String toString()
    將StringBuilder轉(zhuǎn)換成一個(gè)新的String類型字符串


將數(shù)組轉(zhuǎn)換成字符串

    public static void main(String[] args) {
          int[] arr = {1,2,3};
          StringBuilder sb = new StringBuilder();
          for(int i = 0; i < arr.length; i++) {
              sb.append(arr[i]);
          }
          String newString = sb.toString();
          System.out.println(newString);
    }


ArrayList


ArrayList數(shù)組列表可以動(dòng)態(tài)的創(chuàng)建數(shù)組, 并且封裝了很多好用的數(shù)組成員方法, 可以讓我們很方便的操作數(shù)組

構(gòu)造函數(shù)

  • ArrayList<T>()
ArrayList<String> arr = new ArrayList<String>(); //創(chuàng)建以字符串為元素的ArrayList實(shí)例


成員方法

  • public boolean add(T t)
    添加元素
  • public void add(int index, T t)
    在index位置添加元素


static和代碼塊


static關(guān)鍵字

static修飾的靜態(tài)方法/屬性屬于類, 所有該類的實(shí)例都可以調(diào)用, 其隨著類而加載, 優(yōu)先于對(duì)象加載

下面的代碼, 我們?cè)赟tudent類中定義了一個(gè)static屬性school, 我們?cè)赟tudent的實(shí)例對(duì)象p1中并沒有給school賦值, 但是我們卻可以直接調(diào)用p1.school

public class MyPhone {
    public static void main(String[] args) {
        Student p1 = new Student();
        p1.name = "小張";
        p1.age = 20;
        System.out.println(p1.name + ' ' + p1.age + ' ' + p1.school);
    }
}


class Student {
    public static String school = "清華";
    public String name;
    public int age;
}
  1. 由于靜態(tài)方法和靜態(tài)屬性都是隨著類的加載而加載的, 所以靜態(tài)方法中可以調(diào)用靜態(tài)屬性和靜態(tài)方法
  2. 靜態(tài)方法中不能調(diào)用普通成員變量和成員方法
  3. 靜態(tài)方法中沒有this, 因?yàn)殪o態(tài)方法是隨類加載的, 所以此時(shí)還沒有對(duì)象


static的應(yīng)用舉例

Math類就是static的典型應(yīng)用, Math類中的所有方法都是靜態(tài)方法, 所以我們直接用Math類調(diào)用方法就可以了, 不需要?jiǎng)?chuàng)建Math實(shí)例, 并且也無法創(chuàng)建Math實(shí)例


繼承與抽象類


繼承就是讓其他類擁有某個(gè)類中的成員變量和方法, 這個(gè)被繼承的類就叫做父類

我們用extends關(guān)鍵字來繼承類

看下面的代碼, 我們讓Man繼承了Person, 所以我們?cè)趧?chuàng)建Man的實(shí)例對(duì)象時(shí), 我們不但有Man的成員變量agent, 同時(shí)還擁有了繼承自Person類的name, age屬性, 以及say方法, 這樣就可以大大減少代碼量

class Person {
    String name;
    int age;

    public void say() {
         System.out.println("hello");
     }
}

class Man extends Person {
    String agent;
}


java是單繼承語言

  • 一個(gè)類只能有一個(gè)父類
  • 一個(gè)父類可以有多個(gè)子類
  • java可以多層繼承, 也就是說一個(gè)類可以繼承其父類的父類的屬性和方法


java繼承中成員變量的特點(diǎn)

  • 子類只能繼承父類非私有成員變量
  • 繼承也符合變量就近原則, 子類中如果有跟父類的同名變量, 則優(yōu)先使用子類中的成員變量
  • 可以用super.成員變量來調(diào)用父類的同名變量


java繼承中成員方法的特點(diǎn)

  • 方法的重寫: 子類中如果有跟父類完全相同的成員方法, 則會(huì)覆蓋父類的成員方法, 在子類的實(shí)例中會(huì)優(yōu)先調(diào)用子類的同名方法, 如果想調(diào)用父類的同名方法,依然使用super關(guān)鍵字
  • 當(dāng)父類的某個(gè)方法不能滿足子類的要求時(shí), 我們可以通過重寫來擴(kuò)展該方法, 此時(shí)只需要在子類的同名方法中用super調(diào)用父類同名方法就可以實(shí)現(xiàn)在子類中對(duì)該父類方法的功能擴(kuò)展
  • 在子類重寫的放上前加入@Override, 這樣在重寫時(shí)如果有錯(cuò)誤ide會(huì)提示錯(cuò)誤
  • 不能重寫父類的私有成員方法
class Person {
    String name;
    int age;

    public void say() {
         System.out.println("hello");
     }
}

@Override
class Man extends Person {
    public void say() {
        super.say();
        System.out.println("world");
    }
}


java繼承構(gòu)造方法的執(zhí)行順序

  • 在有子父類繼承關(guān)系的類中, 創(chuàng)建子類對(duì)象時(shí),調(diào)用子類的構(gòu)造方法, 如果子類構(gòu)造方法的第一行代碼沒有調(diào)用父類的構(gòu)造函數(shù), 則會(huì)默認(rèn)的調(diào)用父類的無參構(gòu)造
  • 如果想手動(dòng)調(diào)用父類的構(gòu)造方法, 直接在子類構(gòu)造方法中的第一行使用super(參數(shù))
class Person {
    String name;
    int age;
    
    public Person() {
        
    }

    public void say() {
         System.out.println("hello");
     }
}

class Man extends Person {
    public Man() {
        super();
    }
    public void say() {
        super.say();
        System.out.println("world");
    }
}
  • 可以在子類的構(gòu)造方法中的第一行使用了this(參數(shù))調(diào)用子類的其他構(gòu)造方法, 調(diào)用順序是:
    1. 先進(jìn)入this調(diào)用的子類構(gòu)造函數(shù)
    2. 判斷第一行是手動(dòng)調(diào)用還是默認(rèn)調(diào)用父類的構(gòu)造函數(shù), 執(zhí)行父類構(gòu)造函數(shù)
    3. 執(zhí)行this調(diào)用的子類構(gòu)造函數(shù)
    4. 執(zhí)行調(diào)用this的子構(gòu)造函數(shù)

其實(shí)這種調(diào)用順序是有原因的, 因?yàn)樽宇惱^承父類, 所以子類很可能要調(diào)用父類中的成員變量或者成員方法, 所以在子類調(diào)用之前預(yù)先調(diào)用父類就是為了如果子類需要用的父類的某些成員變量或者方法前, 先將其初始化


抽象類和抽象方法


abstract修飾的類和成員方法就是抽象類和抽象方法

abstract class Teacher { // 抽象類
    public abstract void say(); // 抽象方法
}
  • 抽象方法只能存在于抽象類中
  • 抽象類中可以有非抽象方法
  • 抽象類不能實(shí)例化
  • 繼承抽象類的普通類必須重寫抽象類中的所有抽象方法
  • 抽象類中可以有成員變量和常量


抽象類舉例

下面我們抽象一個(gè)老師的類

public class MyTeacher {
    public static void main(String[] args) {
        BasicTeacher bt = new BasicTeacher();
        bt.name = "Adam";
        bt.age = 20;
        bt.gender = "male";
        bt.teach();
    }
}

abstract class Teacher {
    String name; // 姓名
    int age; // 年齡
    String gender; // 性別

    public abstract void teach();
}

class BasicTeacher extends Teacher {
    @Override
    public void teach() {
        System.out.println("基礎(chǔ)班課程");
    }
}


接口


接口的出現(xiàn)主要是為了結(jié)局單一繼承的局限性
接口比抽象類更抽象, 它只能有抽象方法
接口可以通過類來實(shí)現(xiàn)

nterface Animal {
    public abstract void eat();

}

class Cat implements Animal {
    public void eat() {
       System.out.println("吃魚");
    }
}

接口的特點(diǎn)

  • 接口中所有的方法都是抽象方法無論你是否寫abstract關(guān)鍵字
  • 實(shí)現(xiàn)接口必須要將其所有抽象方法都具現(xiàn)
  • 接口內(nèi)只能定義公共靜態(tài)常量
  • 一個(gè)類可以實(shí)現(xiàn)多個(gè)接口
  • java 9+可以在接口中使用靜態(tài)方法、默認(rèn)方法、私有方法、私有靜態(tài)方法、常量
  • 默認(rèn)方法、靜態(tài)方法、私有方法、私有靜態(tài)方法都必須在接口中實(shí)現(xiàn)


多態(tài)


多態(tài)的前提條件

  • 子父類繼承關(guān)系
  • 方法的重寫
  • 父類引用指向子類對(duì)象

看上面的條件可能看不懂, 實(shí)際上總的來說就是, 一個(gè)子類重寫了父類的方法, 然后我們定義了一個(gè)父類類型的變量, 然后把這個(gè)變量用子類的實(shí)例賦值, 看下面的例子

public class MyAnimal {
    public static void main(String[] args) {
        Animal a = new Cat(); // 多態(tài)
        a.eat();
    }
}

class Animal {
    public void eat() {

    }

}

class Cat extends Animal {
    public void eat() {
       System.out.println("吃魚");
    }
}
  1. 我們的Cat類繼承了Animal, 并且在Cat類中重寫了Animal的eat成員方法
  2. 我們?cè)趍ain方法中聲明了一個(gè)Animal類型的變量a, 并且將它指向一個(gè)Cat實(shí)例
  3. 我們調(diào)用a的eat方法, 此時(shí)調(diào)用的是Cat的eat方法

這就是典型的多態(tài)例子


多態(tài)的成員變量及方法

  • 由于成員變量沒有重寫和動(dòng)態(tài)綁定的概念, 所以多態(tài)中調(diào)用的成員變量為定義類的成員變量, 而不是實(shí)例對(duì)象的成員變量, 比如上面的例子, 如果我們需要調(diào)用成員變量, 那么這個(gè)變量來自于Animal而不是Cat
  • 對(duì)于重寫過的普通成員方法, 多態(tài)調(diào)用的是重寫后的成員方法, 比如上面的例子普通成員方法則會(huì)調(diào)用Cat實(shí)例的成員方法, 而對(duì)于重寫過的靜態(tài)方法則會(huì)調(diào)用Animal的靜態(tài)方法, 原因是因?yàn)殪o態(tài)方法相當(dāng)于直接調(diào)用類中的方法而非實(shí)例中的方法



多態(tài)的優(yōu)缺點(diǎn)
缺點(diǎn)

  • 無法直接訪問子類特有的成員變量

優(yōu)點(diǎn)

  • 提高代碼的擴(kuò)展性和維護(hù)性


內(nèi)部類


分類

  • 成員內(nèi)部類
  • 局部?jī)?nèi)部類
  • 匿名內(nèi)部類


成員內(nèi)部類

所在位置和成員變量和成員方法一樣, 同樣可以使用修飾符修飾。下面的例子我們?cè)贠uter類中間創(chuàng)建了一個(gè)內(nèi)部類:

class Outer {
  private int num = 10;
  
  public void show() {
    Inner in = new Inner(); //創(chuàng)建內(nèi)部類實(shí)例對(duì)象
    in.methods(); //調(diào)用實(shí)例的methods方法, 此時(shí)num調(diào)用的是Outer的num
    System.out. println("Hello");
  }
  
  // 內(nèi)部類
  public class Inner {    
    public void methods() {
      System.out.println(num);
    }
  }
}
  • 成員內(nèi)部類可以直接訪問外部類的成員包括私有成員, 因?yàn)槲覀兛梢园褍?nèi)部類看成外部類的一個(gè)特殊成員變量
  • 在相同包的其他類中若想創(chuàng)建該外部類的內(nèi)部類實(shí)例, 可以import內(nèi)部類或者按如下方法創(chuàng)建實(shí)例
Outer.Inner in = new Outer().new Inner();
  • 我們也可以用static修飾符修飾內(nèi)部類, 修飾后內(nèi)部類的性質(zhì)跟靜態(tài)成員變量/方法無異


局部?jī)?nèi)部類

局部?jī)?nèi)部類是定義在成員方法中的內(nèi)部類

class Outer {
    private int num = 10;
    public void show() {
         // 局部?jī)?nèi)部類
        class Inner {
            private int num2 = 10;

            public void methods() {
            }
        }
        Inner in = new Inner();
        in.methods();
    }
}
  • 局部?jī)?nèi)部類的作用域跟局部變量是一樣的, 只能是方法內(nèi), 所以不能在方法外創(chuàng)建和使用局部?jī)?nèi)部類的實(shí)例對(duì)象
  • 局部?jī)?nèi)部類應(yīng)用場(chǎng)景有限, 一般用的不太多


匿名內(nèi)部類

定義在方法內(nèi)部的沒有類名的內(nèi)部類, 匿名內(nèi)部類實(shí)際上是對(duì)某個(gè)類的繼承或?qū)δ承┙涌诘膶?shí)現(xiàn), 所以在使用匿名內(nèi)部類時(shí), 必須預(yù)先定義其父類或接口

class Outer {
  private int num = 10;
  
  public void show() {
     // 匿名內(nèi)部類
    new Inner() {
      public void function() {}
    };
  }
}

//匿名內(nèi)部類實(shí)現(xiàn)的接口
interface Inner {
  public void function();
}

我們還可以利用多態(tài)把匿名內(nèi)部類賦值給類型為匿名內(nèi)部類的父類的變量來實(shí)現(xiàn)匿名內(nèi)部類, 如:

Inner i = new Inner(){
    public void function() {}
};


泛型


泛型是Java中一個(gè)非常重要的知識(shí)點(diǎn),在Java集合類框架中泛型被廣泛應(yīng)用。


泛型類

假設(shè)我們創(chuàng)建以下Box類:

class Box{
  private String object;
  public void set(String str) {
    this.object = str;
  }
  public String get(){
    return object;
  }
}

Box有一個(gè)非常明顯的局限, 只能傳String類型的成員變量, 當(dāng)我們要傳int類型的成員變量時(shí), 就必須要重寫B(tài)ox, 這要那個(gè)Box的復(fù)用性就很差了

class Box<T>{
  private T object;
  public void set(T t) {
    this.object = t;
  }
  public T get(){
    return object;
  }
}

這樣我們的Box類便可以得到復(fù)用,我們可以將T替換成任何我們想要的類型:

Box<Integer> integerBox = new Box<Integer>();
Box<Double> doubleBox = new Box<Double>();
Box<String> stringBox = new Box<String>();


泛型方法

明一個(gè)泛型方法很簡(jiǎn)單,只要在返回類型前面加上一個(gè)類似<K, V>的形式就行了:

public class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}
public class Pair<K, V> {
    private K key;
    private V value;
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

我們可以像這樣調(diào)用泛型方法

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);


多線程


  • 進(jìn)程
    一個(gè)應(yīng)用程序在內(nèi)存中的執(zhí)行區(qū)域
  • 線程
    進(jìn)程中的一個(gè)執(zhí)行控制單元/執(zhí)行路徑, 一個(gè)進(jìn)程中有一個(gè)線程我們叫做單線程, 一個(gè)進(jìn)程中有多個(gè)線程我們叫做多線程
  • 單線程
    一個(gè)只做一件事情, 安全性高, 效率低
  • 多線程
    同時(shí)做多件事情, 安全性低, 效率高
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 還不太會(huì)用簡(jiǎn)書……這玩意兒到底該怎么用的= = 又是平淡寡味的一天啊。早上睡到了9點(diǎn)過醒,醒過來之后還是想睡。難道...
    清湯寡水_感覺自己快要死亡閱讀 245評(píng)論 0 0
  • 胡卜胡卜閱讀 390評(píng)論 1 11
  • 踩踏事件還在不斷地升級(jí),不斷有人被腳下地“東西”絆倒。四個(gè)“死神”緊隨其后,收割著落單地人,這讓原本擁擠地走廊更加...
    腹黑腹肌閱讀 147評(píng)論 0 0

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