內(nèi)存圖

成員變量和局部變量的區(qū)別
- 在類中的位置不同
- 成員變量在類中, 在方法外
- 局部變量在方法內(nèi)或方法聲明中(形參)
public class Phone {
String x; //成員變量
public void showPhone() {
int y; //局部變量
}
}
- 內(nèi)存中位置不同
- 成員變量在堆內(nèi)存
- 局部變量在棧內(nèi)存
- 生命周期不同
- 成員變量隨著對(duì)象的消失而消失
- 局部變量隨著方法的消失而消失
- 初始化值不同
- 成員變量有默認(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;
}
- 由于靜態(tài)方法和靜態(tài)屬性都是隨著類的加載而加載的, 所以靜態(tài)方法中可以調(diào)用靜態(tài)屬性和靜態(tài)方法
- 靜態(tài)方法中不能調(diào)用普通成員變量和成員方法
- 靜態(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)用順序是:- 先進(jìn)入this調(diào)用的子類構(gòu)造函數(shù)
- 判斷第一行是手動(dòng)調(diào)用還是默認(rèn)調(diào)用父類的構(gòu)造函數(shù), 執(zhí)行父類構(gòu)造函數(shù)
- 執(zhí)行this調(diào)用的子類構(gòu)造函數(shù)
- 執(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("吃魚");
}
}
- 我們的Cat類繼承了Animal, 并且在Cat類中重寫了Animal的eat成員方法
- 我們?cè)趍ain方法中聲明了一個(gè)Animal類型的變量a, 并且將它指向一個(gè)Cat實(shí)例
- 我們調(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í)做多件事情, 安全性低, 效率高