1. 程式人生 > >JavaSE(二)面向物件程式設計

JavaSE(二)面向物件程式設計

面向物件程式設計

面向物件的概念

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:表示任意精度的浮點數