知識要點-面向對象
1.抽象方法和抽象類
- 定義抽象方法只需在普通方法上增加abstract修飾符,并把普通方法的方法體(方法后花括號括起來的那部分)全部去掉,在最后增加分號即可
//定義一個Shape抽象類
public abstract class Shape{
{
System.out.println("執行Shape的初始化塊");
}
private String color;
//定義一個計算周長的抽象方法
public abstract double calPerimeter();
//定義一個返回形狀的抽象方法
public abstract String getType();
//定義Shape的構造器,該構造器并不是用于創建Shape對象
//而是用于被子類調用
public Shape(){}
public Shape(String color){
System.out.println("執行Shape的構造器");
this.color = color;
}
}
- Shape類里包含了兩個方法,因為有抽象方法所以只能被定義為抽象類,不能創建實例,只能當做父類被其他子類繼承
//下面定義一個三角形類,三角形類被定義成普通類
public class Triangle extends Shape{
//定義三角形的三邊
private double a;
private double b;
private double c;
public Triangle(String color, double a,double b,double c){
super(color);
this.setSides(a,b,c);
}
public void setSides(double a,double b,double c){
if (a >= b + c || b >= a + c || c>= a + b){
System.out.println("三角形兩邊之和必須大于第三邊");
return;
}
this.a = a;
this.b = b;
this.c = c;
}
//重寫Shape類的計算周長的抽象方法
public double calPerimeter(){
return a+b+c;
}
//重寫Shape類的返回形狀的抽象方法
public String getType(){
return "三角形";
}
}
//下面定義一個Circle類,也是Shape的一個子類
public class Circle extends Shape{
private double radius;
public Circle(String color, double radius){
super(color);
this.radius = radius;
}
public void setRadius(double radius){
this.radius = radius;
}
//重寫Shape類的計算周長的抽象方法
public double calPerimeter(){
return 2 * Math.PI * radius;
}
//重寫Shape類的返回形狀的抽象方法
public String getType(){
return getColor() + "圓形";
}
public static void main(String[] args){
Shape s1 = new Triangle("黑色",3,4,5);
Shape s2 = new Circle("黃色",3);
System.out.println(s1.getType());
System.out.println(s1.calPerimeter());
System.out.println(s2.getType());
System.out.println(s2.calPerimeter());
}
}
- 上面代碼main方法中定義了兩個Shape類型的引用變量,它們分別指向Triangle對象和Circle對象,由于在Shape類中定義了calPerimeter()方法和getType()方法,所以程序可以直接調用s1變量和s2變量的兩個方法,無需強制類型轉換為其子類類型
- 利用抽象類和抽象方法的優勢,可以更好地發揮多態的優勢
- final和abstract永遠不能同時使用,因為final定義的類不能被繼承,定義的方法也不能被重寫,這與abstract相悖
- static和abstract也不能同時修飾,因為沒有所謂的類抽象方法;但是可以同時修飾內部類
- private和abstract不能同時使用,abstract修飾的方法必須被子類重寫,private訪問權限會使子類無法重寫
抽象類體現的是一種模板模式的設計,作為多個子類的通用模板
2.接口
- 一種更加特殊的“抽象類”,接口里不能包含任何普通方法,接口里的所有方法都是抽象方法
- Java 8 對接口進行了改進,允許在接口中定義默認方法
- Java 8 中接口的定義
[修飾符] interface 接口名 extends 父接口1,父接口2...{
零到多個常量定義...
零到多個抽象方法定義...
零到多個內部類,接口,枚舉定義...
零到多個默認方法或類方法定義...
}
- 修飾符使用public或者省略,省略默認采用包權限
- 接口只能繼承接口,不能繼承類
- 接口不能包含構造器和初始化塊定義,接口里可以包含成員變量(只能是靜態常量),方法(只能是抽象實例方法,類方法或者默認方法),內部類(包括內部接口,枚舉)定義
- 接口中的靜態常量無論是否使用修飾符,總是使用public static final來修飾
- 接口中定義默認方法,用default修飾
- 接口的用途:1.定義變量,也可以用于進行強制類型轉換;2.調用接口中定義的常量;3.被其他類實現
- 一個類可以實現一個或多個接口,繼承使用extends關鍵字,實現使用implements關鍵字,類實現接口的語法如下
[修飾符] class 類名 extends 父類 implements 接口1,接口2....{
類體部分
//implements必須放在extends之后
}
- 一個類實現了一個或多個接口后,必須完全實現接口里所定義的全部抽象方法,否則該類也必須定義成抽象類
//一個實現接口的類
//定義一個Product接口
interface Output{
//接口里定義的成員變量只能是常量
int MAX_CACHE_LINE = 50;
//接口里定義的普通方法只能是public的抽象方法
void out();
void getData(String msg);
//在接口中定義默認方法,需要使用default修飾
default void print(String... msgs){
for(String msg : msgs){
System.out.println(msg);
}
}
//在接口中定義默認方法,需要使用default修飾
default void test(){
System.out.println("默認的test()方法");
}
//加接口中定義類方法,需要使用static修飾
static String staticTest(){
return "接口里的類方法";
}
}
interface Product{
int getProduceTime();
}
//讓Printer類實現Output和Product接口
public class Printer implements Output, Product{
private String[] printData = new String[MAX_CACHE_LINE];
//用來記錄當前需打印的作業數
private int dataNum = 0;
public void out(){
//只要還有作業,就繼續打印
while (dataNum > 0){
System.out.println("打印機打?。?+ printData[0]);
//把作業隊列整體前移一位,并將剩下的作業數減1
System.arraycopy(printData,1,printData,0,--dataNum);
}
}
public void getData(String msg){
if(dataNum >= MAX_CACHE_LINE){
System.out.println("輸出隊列已滿,添加失敗");
}else {
//把打印數據添加到隊列里,已保存的數量加1
printData[dataNum++] = msg;
}
}
public int getProduceTime(){
return 45;
}
public static void main(String[] args){
//創建一個Printer對象,當成Output使用
Output o = new Printer();
o.getData("輕量級Java學習");
o.out();
//調用Output接口中定義的默認方法
o.print("孫悟空","豬八戒");
o.test();
//擦行間一個Printer對象,當成Product使用
Product p = new Printer();
System.out.println(p.getProduceTime());
//所有接口類型的引用變量都可以直接賦給Object類型的變量
Object obj = p;
}
}
3.接口和抽象類比較
它們都有如下特征:
- 都不能被實例化,都位于繼承樹的頂端,用于被其他類實現和繼承
- 都可以包含抽象方法,實現接口或繼承抽象類的普通子類都必須實現這些抽象方法
區別:
- 接口提現的是一種規范,對于接口的實現者而言,接口規定了實現者必須向外提供哪些服務;對于接口的調用者而言,接口規定了調用者可以調用哪里服務,以及如何調用這些服務;接口是多個模塊間的耦合標準;當在多個應用程序之間使用接口時,接口是多個程序之間的通信標準
- 抽象類作為系統中多個子類的共同父類,它所體現的時一種模板式設計,抽象類作為多個子類的抽象父類,可以被當成是系統實現過程中的中間產品,這個中間產品已經實現了系統的部分功能,但這個產品依然不能當成最終產品,必須由更進一步的完善
除此之外,還有如下差別
差異點
4.面向接口編程
5.內部類(嵌套類)
public class Cow{
private double weight;
//外部類的兩個重載的構造器
public Cow(){}
public Cow(double weight){
this.weight = weight;
}
//定義一個非靜態內部類
private class CowLeg{
//非靜態內部類的兩個實例變量
private double length;
private String color;
//非靜態內部類的兩個重載的構造器
public CowLeg(){}
public CowLeg(double length,String color){
this.length = length;
this.color = color;
}
public double getLength() {
return length;
}
public String getColor() {
return color;
}
public void setLength(double length) {
this.length = length;
}
public void setColor(String color) {
this.color = color;
}
//非靜態內部類的實例方法
public void info(){
System.out.println("當前牛腿顏色是:" + color + ",高:" + length);
//直接訪問外部類的private修飾的成員變量
System.out.println("本牛腿所在奶牛重:" + weight);
}
}
public void test(){
CowLeg cl = new CowLeg(1.12,"黑白相間");
cl.info();
}
public static void main(String[] args){
Cow cow = new Cow(378.9);
cow.test();
}
}
6.Java 8改進的匿名內部類
- 適合創建只需要使用一次的類
new 實現接口() | 父類構造器(實參列表){
//匿名內部類的類體成分
}
- 必須繼承一個父類,或者實現一個接口,但是最多只能繼承一個父類或實現一個接口
- 匿名內部類不能是抽象類,系統在創建匿名內部類時,會立即創建匿名內部類的對象
- 匿名內部類不能定義構造器,可以定義初始化塊
7.Java 8 新增的Lambda表達式
- Lambda表達式支持將代碼塊作為方法參數(類似js中的箭頭函數?)
(形參)->{方法}
- 只有一條語句可以省略花括號,只有return可以省略return
8.Lambda表達式與函數式接口
- Lambda表達式的目標類型必須是“函數式接口”,即只包含一個抽象方法的接口,但是可以包含多個默認方法,類方法
//Runnable接口中只包含一個無參數的方法
//Lambda表達式代表的匿名方法實現了Runnable接口中唯一的,無參數的方法
//因此下面的Lambda表達式創建了一個Runnable對象
Runnable r = () -> {
for(int i = 0; i < 100; i ++){
System.out.println();
}
}
9.方法引用和構造器引用
Lambda表達式支持的方法引用和構造器引用
10.枚舉類
- 枚舉類:一個類的對象(實例)有限而且固定
- Java 5新增enum關鍵字用于定義枚舉類,一個java源文件中最多只能定義一個public訪問權限的枚舉類,該java源文件也必須和該枚舉類的類名相同
-
enum枚舉類與普通類區別如下
區別
public enum SeasonEnum{
//在第一行列出四個枚舉實例
SPRING,SUMMER,FALL,WINNER;
}
11.對象與垃圾回收
- 垃圾回收機制只負責回收堆內存中的對象
- 程序無法精確控制垃圾回收的運行
- 在垃圾回收機制回收任何對象之前,總會先調用它的finalize()方法,該方法可能使該對象重新復活(讓一個引用變量重新引用該對象),從而取消回收
- 對象在堆內存中運行時,可以把它所處的狀態分為三種:
1.可達狀態:當一個對象被創建后,若有一個以上的引用變量引用它,則這個對象在程序中處于可達狀態
2.可恢復狀態:如果程序中某個對象不再有任何引用變量引用它,它就進入了可恢復狀態。在這種狀態下,系統的垃圾回收機制準備回收該對象所占的內存,在回收該對象之前,系統會調用所有可恢復對象的finalize()方法進行資源清理。如果系統在調用finalize()方法時重新讓一個引用變量引用該對象則這個對象會再次變為可達狀態,否則該對象進入不可達狀態
3.不可達狀態:當對象與所有引用變量之間的關系都被切斷,系統已調用過finalize()方法,這個對象會永久失去引用,最后變成不可達狀態,這時系統才會真正回收該對象所占的資源
12.強制垃圾回收
- 程序可以強制系統進行垃圾回收--只是通知系統進行垃圾回收,并不確定是否進行
1.System.gc();
2.Runtime.getRuntime().gc();
13.finalize方法
特點:
- 永遠不要主動調用某個對象的finalize方法,該方法應交給垃圾回收機制調用
- finalize()方法何時被調用,是否被調用具有不確定性
- 當JVM執行課恢復對象的finalize()方法時,可能使該對象或系統中其他對象重新變成可達狀態
- 當JVM執行finalize()方法時出現異常時,垃圾回收機制不會報告異常,程序繼續執行
14.對象的軟,弱,虛引用
- 強引用:最常見,把對象賦給一個引用變量就是強引用
- 軟引用:需要通過SoftReference類來實現,內存足夠時不會被系統回收,內存不足時系統可能會回收,通常用于對象內存敏感的程序中
- 弱引用:需要通過WeakReference類實現,不管內存是否足夠,都會被回收
- 虛引用:需要通過PhantomReference類實現,類似于沒有引用,不能單獨使用,必須和引用隊列聯合使用
15.修飾符的適用范圍
修飾符修飾范圍
16.使用JAR文件
- Java Archive File,壓縮文件,與ZIP兼容,也被稱作JAR包,jar中默認一個META-INF/MANIFEST.MF的清單文件
- 使用JAR文件好處:1.安全;2.加快下載速度,只需要簡歷一次HTTP鏈接;3.壓縮大??;4.包封裝,能夠讓JAR包里面的文件依賴于同一版本的類文件;5.可移植性,作為內嵌在Java平臺內部處理的標準,能夠在各種平臺上直接使用