JavaSE(二)面向物件程式設計
阿新 • • 發佈:2019-01-09
面向物件程式設計
面向物件的概念
OOP:Object Oriented Programming
class 和 instance 是“模板”和“例項”的關係 ==>
類:class,物件模板
例項:instance,具體物件
class用欄位(field)封裝了資料,不同的instance擁有各自獨立的field
通過 **變數.欄位名** 訪問某個欄位
指向 instance 的變數都是引用變數
資料封裝
方法
方法封裝了訪問例項欄位的邏輯 例項欄位修飾符 public:外部程式碼可以訪問該欄位 private:外部程式碼無法訪問該欄位 方法修飾符 public:外部程式碼可以訪問該方法 private:外部程式碼無法訪問該方法 方法是一組執行語句,遇到return返回 void表示不返回任何值(注意區分null) 方法命名規範 方法內部的隱私變數this
構造方法Constructor
構造方法可以在new操作時初始化例項物件
構造方法名就是類名
構造方法沒有返回值
編譯器會為沒有定義構造方法的類自動生成預設構造方法(無引數、無執行語句)
如果我們自定義了構造方法,編譯器就不再自動建立預設構造方法
初始化順序:
1)初始化欄位(沒有賦值的欄位初始化為預設值,基本型別=0,引用型別=null)
2)執行構造方法程式碼
可以定義多個構造方法,編譯器根據引數數量、位置和型別自動判斷使用哪個
可以在一個構造方法中呼叫其他構造方法,便於程式碼的複用,呼叫方法是this(...)
方法過載 Overload
指方法名相同,單引數不同: 1)型別不同 2)數量不同 3)位置不同 方法過載的目的是方便呼叫 過載方法應該完成相同的功能,參考String的indexOf() 1)indexOf(String str) 2)indexOf(String str1, int FromIndex) 不要去交換引數順序 1)indexOf(String str, int fromIndex) 2)indexOf(int fromIndex, String str) 過載方法返回值型別應該相同
繼承和多型
繼承
繼承是面向物件程式設計的程式碼複用方式
Java使用extend繼承
被繼承類:超類,父類,基類 繼承類:子類,派生類
Java只允許繼承一個類
Object是所有類的根類
protected修飾的欄位和方法可以被子類訪問,protected把欄位和方法的訪問許可權控制在了繼承樹內部
super表示父類
沒有呼叫super()時編譯器會自動生成super()語句,如果父類沒有預設構造方法,子類就必須顯示呼叫super()
向上轉型和向下轉型 Upcasting Downcasting
子類型別可以安全地向上轉型為父類型別
父類型別可以強制向下轉型為子類型別(可能報錯ClassCastException)
我們在做向下轉型的時候,可以先用instanceof判斷型別
繼承和組合
繼承是is關係
組合是has關係
多型Polymorphic
方法覆蓋:
子類覆寫父類的方法,Override
方法簽名如果不同就不是Override,而是建立了一個新方法
加上@Override可以讓編譯器幫助檢查是否進行正確的覆寫
@Override不是必需的
多型:
針對某個型別的方法呼叫,其真正執行的方法取決於執行時實際型別的方法
Java的例項方法呼叫是基於執行時實際型別的動態呼叫 ===> 多型
對某個型別呼叫方法,執行的方法可能是某個子類的覆寫方法
允許新增更多型別的子類來擴充套件功能
Object定義的幾個重要的方法:
1)toString:把instance輸出為String
2)equals:判斷兩個instance是否邏輯相等
3)hashCode:計算一個instance的雜湊值
final
用final修飾的方法不能被Override
用final修飾的類不能被繼承
用final修飾的欄位初始化後不能重新賦值
抽象類和介面
抽象類 Abstract Class
抽象方法用abstract修飾,抽象方法沒有任何執行語句;因為無法執行抽象方法,所以這個類也必須申明為抽象類(abstract class)
無法例項化一個抽象類
含有抽象方法的類稱為抽象類
抽象類的作用:
1)被繼承
2)強迫子類實現抽象方法
3)抽象方法相當於定義“規範”
面向抽象程式設計的本質:
1)上層程式碼只定義規範
2)不需要子類即可編譯
3)具體邏輯由不同子類實現,呼叫者並不關心
介面 Interface
介面定義了純抽象規範,使用interface宣告一個介面,介面只有抽象方法
實現interface使用implements
一個class可以實現多個interface
介面:
1)不能定義例項欄位
2)不能定義普通方法
3)可以定義default方法(JDK>=1.8)
一個介面可以extends另一個介面,相當於擴充套件介面方法
介面層次代表抽象程度
介面也是資料型別,適用於向上轉型和向下轉型
abstract class | interface | |
---|---|---|
繼承 | 只能extends一個class | 可以implements對個interface |
欄位 | 可以定義例項欄位 | 不能定義例項欄位 |
抽象方法 | 可以定義抽象方法 | 可以定義抽象方法 |
非抽象方法 | 可以定義非抽象方法 | 可以定義default方法 |
包和classpath
靜態欄位和方法
用static修飾的欄位:靜態欄位,屬於class,不屬於例項
普通欄位在每個例項中都有自己的一個獨立“空間”
靜態欄位只有一個共享“空間”,所有例項都共享該欄位
訪問靜態欄位:類名.靜態欄位 不推薦使用 例項變數.靜態欄位
用static修飾的方法:靜態方法,屬於class,不屬於例項
呼叫例項方法必須通過例項變數
呼叫靜態方法不需要例項變數
靜態方法類似其他程式語言的函式
訪問靜態方法:類名.靜態方法 不推薦使用 例項變數.靜態方法
靜態方法不能訪問this變數,但可以訪問靜態欄位
靜態方法不能訪問例項欄位
靜態方法常用用於工具類(Arrays.sort()、Math.random())和輔助方法
Java程式的入口main()也是靜態方法
包 Packages
package用於解決類名衝突:
Java完整類名=包名+類名;JVM只看完整類名,編譯器編譯後的class只含完整類名
包可以有多層結構,包沒有父子關係(例如:java.util和java.util.zip是不同的包,兩者沒有任何關係)
包作用域
位於同一個包的類,可以訪問包作用域的欄位和方法
不用public、protected、private修飾的欄位和方法就是包的作用域
引用其他類的方法
使用完整類名;先import,再使用類名;可以使用*(不推薦)
static import可以匯入一個類的靜態欄位和靜態方法,很少使用
查詢class
編譯器查詢class完整類名的步驟:
1)根據完整類名查詢
2)查詢當前package
3)查詢import的class
4)查詢java.lang的class
5)編譯錯誤
預設自動import當前package,預設自動import java.lang.*
最佳實踐
包名使用倒置域名確保唯一,不要和java.lang的類重名,不用和JDK常用類重名
作用域
訪問許可權
訪問許可權指一個類內部,能否引用另一個類以及它的欄位和方法
方法許可權有public、protected、private和package四種
final不是訪問許可權
最佳實踐:最小化暴露對外方法
區域性變數
方法內部定義的變數時區域性變數(包括方法引數名)
區域性變數作用域由所在語句塊{...}決定
classpath和jar
classpath
classpath是環境變數,指示JVM如何搜尋class,路徑和作業系統相關
假設classpath是 .;C:\work\project1\bin;C:\shared
JVM在載入com.feiyangedu.Hello這個類時,依次查詢:
<當前目錄>\com\feiyangedu\Hello.class
C:\work\project1\bin\com\feiyangedu\Hello.class
C:\shared\com\feiyangedu\Hello.class
在某個路徑下找到了,就不再繼續搜尋;如果都沒有找到,就報錯
classpath設定方法:
1)在系統環境變數設定(不推薦)
2)啟動JVM時用-classpath或-cp設定(推薦)
java -classpath C:\work\bin;C\shared com.feiyangedu.Hello
java -cp C:\work\bin;C\shared com.feiyangedu.Hello
Eclipse自動傳入當前工程bin目錄作為classpath
jar
jar包是zip格式的壓縮檔案,包含若干class檔案
jar包相當於目錄
使用jar包來避免大量目錄和class檔案
建立jar包:
1)JDK的jar命令
2)Maven等工具
3)壓縮為zip然後改名為jar
jar包可以有一個特殊的/META-INF/MANIFEST.MF檔案來指定Main-Class
例如
Mainfest-Version: 1.0
Main-Class: com.feiyangedu.sample.Main
X-Copyright: dafjlaf...
X-Build-Version: 1.0
Java核心類
字串和編碼
字串
Java字串的特點:
1)字串物件可以直接使用 雙引號 “...” 表示
2)內容不可變
3)使用equals(Object)判斷是否相等,equalsIgnoreCase(String)忽略大小寫比較
字串常用操作:
1)是否包含子字串:contains、indexOf、lastIndex、startsWith、endsWith
boolean contains(CharSequence)
int indexOf(String)
int lastIndex(String)
boolean startsWith(String)
boolean endsWith(String)
2)去除首尾空白字元:trim
空白字元包括:空格,\t,\r,\n
trim()不改變字串內容,而是返回新字串
3)提取子串:substring
4)大小寫轉換:toUpperCase、toLowerCase
5)替換子串:replace、replaceAll(正則表示式替換)
6)分割:split
String[] split(String)
7)拼接:join
static String join()
String和其他資料型別互相轉換
// 把任意資料轉換為String
static String valueOf(int)
static String valueOf(boolean)
static String valueOf(Object)
// 把String轉換為其他型別
static int Integer.parseInt(String)
static Integer Integer.valueOf(String)
String和char[]互相轉換
// String轉換為char[]
char[] toCharArray()
// char[]轉換為String
new String(char[])
String和byte[]互相轉換(需要指定編碼)
//String轉換為byte[]
byte[] getBytes() // 不推薦,原因是windows上預設是GBK,而在linux上是utf-8
byte[] getBytes(String)
byte[] getBytes(Charset)
// byte[]轉換為String
new String(byte[], String)
new String(byte[], Charset)
編碼
ASCII:最早的字元編碼,一個字元佔一個位元組,最多表示128個字元
GB2312/GBK/GB18030:一箇中文字元佔2個位元組,第一個位元組最高位是1
Unicode:全球統一編碼,全球所有文字都有唯一編碼,一個Unicode字元通常佔用2個位元組,Java使用Unicode編碼
Q:有了Unicode為什麼還需要UTF-8?
A:英文Unicode編碼和ASCII不一致,包含大量英文的文字會浪費空間
UTF-8:是變長編碼,英文UTF-8編碼和ASCII一致,其他Unicode字元需要2~6位元組不等,UTF-8編碼容錯能力強
建議總是使用UTF-8編碼
StringBuilder
String可以用+拼接,但是每次迴圈都會建立新的字串物件,絕大多數都是臨時物件,浪費記憶體,影響GC效率
可以高效拼接String
是可變物件
可以預分配緩衝區
可以進行鏈式操作
不要使用StringBuffer
StringBuffer是StringBuilder的執行緒安全版本,很少使用
包裝型別 Wrapper
基本型別 | 對應的包裝型別 |
---|---|
boolean | Boolean |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
int、Integer和String的相互轉換
int i = 100;
Integer n1 = new Integer(i);
Integer n2 = Integer.valueOf(i);
Integer n3 = Integer.valueOf("100");
int x1 = n1.intValue();
int x2 = Integer.parseInt("100");
String s = n1.toString();
// WARNING
Integer prop = Integer.getInteger("cpus");//從系統環境中讀取系統變數
自動裝箱:auto boxing
int ==> Integer
自動拆箱:auto unboxing
Integer ==> int
自動裝箱和自動拆箱只發生在編譯階段,裝箱和拆箱會影響執行效率
編譯後的class程式碼是嚴格區分基本型別和引用型別的
自動拆箱可能發生 NullPointerException
包裝型別定義了一些有用的靜態變數
整數和浮點數包裝型別繼承自Number
Boolean t = Boolean.TRUE;
Boolean f = Boolean.FALSE;
int max = Integer.MAX_VALUE; // 2147483647
int min = Integer.MIN_VALUE; // -2147483648
int sizeOfLong = Long.SIZE; // 64(bits)
int bytesOfLong = Long.BYTES; // 8(bytes)
JavaBean
Q:什麼是JavaBean?
A:JavaBean是一種Java程式設計規範,例如
// private Type field
// public Type getField()
// public void setField(Type value)
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
目的:
1)方便IDE工具讀寫屬性
2)傳遞資料
3)列舉屬性
JavaBean的特點:
1)一組public getter/setter方法
2)boolean屬性的讀方法通常為isXxx()
3)屬性可以是隻讀或只寫的
4)屬性只需要getter/setter方法,不一定需要例項欄位
5)利用IDE可以快速生成屬性方法
6)使用introspector.getBeanInfo()獲取屬性列表
public class Person {
private String name;
private int age;
private boolean gender;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
public class Main {
public static void main(String[] args) throws Exception{
BeanInfo bInfo = Introspector.getBeanInfo(Person.class);
for(PropertyDescriptor pd : bInfo.getPropertyDescriptors()){
printPropertyDescriptor(pd);
}
}
static void printPropertyDescriptor(PropertyDescriptor pd){
String name = pd.getName();
Class<?> clazz = pd.getPropertyType();
if(clazz == null || name.equals("class")){
return;
}
Method read = pd.getReadMethod();
Method write = pd.getWriteMethod();
System.out.println("===== PropertyDescriptor =====");
System.out.println("Name: " + name);
System.out.println("Type: " + clazz.getName());
System.out.println("Read method: " + (read == null ? "null" : read.getName()));
System.out.println("Write method: " + (write == null ? "null" : write.getName()));
}
}
===== PropertyDescriptor =====
Name: address
Type: java.lang.String
Read method: getAddress
Write method: setAddress
===== PropertyDescriptor =====
Name: age
Type: int
Read method: getAge
Write method: setAge
===== PropertyDescriptor =====
Name: gender
Type: boolean
Read method: isGender
Write method: setGender
===== PropertyDescriptor =====
Name: name
Type: java.lang.String
Read method: getName
Write method: setName
列舉類 Enumeration
Java使用enum定義常量型別,常量本身帶有型別資訊,可以使用==比較
enum定義的型別是class,繼承java.lang.Enum
所有常量都是唯一引用例項
常量可用於switch語句
name()獲取常量定義的字串,注意不要使用toString()
ordinal()返回常量定義的順序(無實質意義)
可以為enum類編寫構造方法、欄位、方法
構造方法必須為private
public static void main(String[] args) {
for(Weekday day : Weekday.values()){
System.out.println(day.name());
}
Weekday fri = Weekday.FRI;
// enum -> String:
System.out.println("FRI.name() = " + fri.name());
// 定義時的序號:
System.out.println("FRI.ordinal() = " + fri.ordinal());
// String -> enum:
System.out.println(Weekday.valueOf("FRI").name());
// 不存在的name:
Weekday.valueOf("ABC");
}
SUN
MON
TUE
WED
THU
FRI
SAT
FRI.name() = FRI
FRI.ordinal() = 5
FRI
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant test06.Weekday.ABC
at java.lang.Enum.valueOf(Enum.java:238)
at test06.Weekday.valueOf(Weekday.java:1)
at test06.Main.main(Main.java:46)
public enum Weekday {
SUN("星期日"),
MON("星期一"),
TUE("星期二"),
WED("星期三"),
THU("星期四"),
FRI("星期五"),
SAT("星期六");
private String chinese;
private Weekday(String chinese){
this.chinese = chinese;
}
public String toChinese(){
return chinese;
}
}
public static void main(String[] args) {
Weekday fri = Weekday.FRI;
System.out.println(fri.toChinese()); // 星期五
}
常用工具類
Math:數學計算
1)abs / min / max
2)pow / sqrt / exp / log / log10
3)sin / cos / tan / asin / acos ...
常量: PI = 3.14159... E = 2.71828
Math.random() 生成一個隨機數:0 <= 隨機數 < 1;可用於生成某個區間的隨機數
Random:生成偽隨機數
nextInt / nextLong / nextFloat
nextInt(N) 生成不大於N的隨機數
什麼是偽隨機數:
給定種子後偽隨機數演算法會生成完全相同的序列
不給定種子時Random使用系統當前時間戳作為種子
SecureRandom:生成安全的隨機數,缺點是比較慢
BigInteger:表示任意大小的整數
BigDecimal:表示任意精度的浮點數