JavaSE:抽象类和接口
目錄
1. 抽象類
1.1 抽象類概念?
1.2 抽象類語法?
1.3 抽象類特性
1.4 抽象類的作用?
2. 接口?
2.1 接口的概念?
2.2 語法規則?
2.3 接口使用?
2.4 接口特性?
2.5 實現多個接口?
2.6 接口間的繼承?
2.7,幾個重要接口使用實例?
Comparable接口?
Comparator接口?
2.8Clonable 接口和深拷貝
淺拷貝 VS 深拷貝
2.9 抽象類和接口的區別?
3. Object類
3.1,Object類方法總覽
3.1.1 獲取對象信息 :toString()方法
3.1.2 對象比較:equals方法
3.1.2 hashcode?
1. 抽象類
1.1 抽象類概念?
在面向對象的概念中,所有的對象都是通過類來描繪的,但是反過來,并不是所有的類都是用來描繪對象的,如果一個類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類就是抽象類。 比如:?
//抽象類 abstract class Shape{//抽象方法public abstract void draw(); } public class Test {public static void main(String[] args) {} }在這個Shape中的draw方法沒有具體的實現,draw方法就為抽象方法,Shape就為抽象類
1.2 抽象類語法?
在Java中,一個類如果被 abstract 修飾稱為抽象類,抽象類中被 abstract 修飾的方法稱為抽象方法,抽象方法不用給出具體的實現體。
// 抽象類:被abstract修飾的類 public abstract class Shape {// 抽象方法:被abstract修飾的方法,沒有方法體abstract public void draw();abstract void calcArea();// 抽象類也是類,也可以增加普通方法和屬性public double getArea(){return area;}protected double area; // 面積 }?注意:抽象類也是類,內部可以包含普通方法和屬性,甚至構造方法 ,抽象類最突出和普通類不一樣的地方就是不可以進行實例化!!
1.3 抽象類特性
1.抽象類不能實例化
2.抽象類雖然不可以進行實例化,但是可以被繼承【也就是說抽象類其實就是為了被繼承】
//抽象類 abstract class Shape{public int a;public static int b;public void func(){}//抽象方法public abstract void draw(); } class Ract extends Shape{@Overridepublic void draw() {System.out.println("畫一個圖形");} } public class Test {public static void main(String[] args) {} }3.當一個普通類繼承這個抽象類之后,如果這個抽象類當中包含抽象方法,那么需要重寫這個抽象方法,否則代碼不能通過
4.當一個抽象類同時擁有多個抽象方法時,繼承的這個類需要重寫所有抽象方法
//抽象類 abstract class Shape{public int a;public static int b;public void func(){}//抽象方法public abstract void draw();//1public abstract void draw2();//2 } class Ract extends Shape{@Overridepublic void draw() {System.out.println("畫一個圖形");}@Overridepublic void draw2() {} } public class Test {public static void main(String[] args) {} }5.如果抽象類A繼承了抽象類B。那么抽象類A可以不重寫B中的抽象方法
//抽象類 abstract class Shape{public int a;public static int b;public void func(){}//抽象方法public abstract void draw();//1public abstract void draw2();//2 } abstract class Ract extends Shape{} public class Test {public static void main(String[] args) {} }6.如果抽象類A繼承了抽象類B。那么抽象類A可以不重寫B中的抽象方法,但是當有一個類C繼承了A方法后需要繼承A,B中的所有抽象方法
//抽象類 abstract class B{public int a;public static int b;public void func(){}//抽象方法public abstract void draw();//1public abstract void draw2();//2 } abstract class A extends B {public abstract void draw3();//3 } class C extends A{@Overridepublic void draw() {}@Overridepublic void draw2() {}@Overridepublic void draw3() {} }public class DEmo {public static void main(String[] args) {} }7.抽象方法不能是private的
abstract class Shape {abstract private void draw(); } // 編譯出錯 Error:(4, 27) java: 非法的修飾符組合: abstract和private注意:抽象方法沒有加訪問限定符時,默認是public.
8. 抽象方法不能被final和static修飾,因為抽象方法要被子類重寫
9. 抽象類中不一定包含抽象方法,但是有抽象方法的類一定是抽象類
10. 抽象類中可以有構造方法,供子類創建對象時,初始化父類的成員變量?
1.4 抽象類的作用?
抽象類的作用,就是為了被繼承,一是父類中的抽象方法在定義的時候可以不用寫具體的執行體,二是編譯器多了一層校驗,當我們沒有重寫抽象方法的時候會直接報錯。
2. 接口?
2.1 接口的概念?
接口就是對行為的規范,大家在實現的時候,只要按著規范,那就都可以通用。
在Java中,接口可以看成是:多個類的公共規范,是一種引用數據類型
就拿我們生活中的插座舉例:
2.2 語法規則?
接口的定義格式與定義類的格式基本相同,將class關鍵字換成 interface 關鍵字,就定義了一個接口。?
interface IShape{public abstract void draw(); }提示:
1,創建接口時,接口的名字一般以大寫字母 I 開頭。
2,接口的命名一般用形容詞性的單詞。
3,阿里編程規范中要求,接口中的方法和成員前不要加任何修飾符,保持代碼的簡潔,比如上面的public abstract,當然雖然我們沒寫,但是編譯器時是知道的。
?
2.3 接口使用?
接口是不能直接使用的,必須要有一個類來實現這個接口,并且重寫接口里面所有的抽象方法才可
interface AnimalEat{void eat(); } class Dog implements AnimalEat{@Overridepublic void eat() {System.out.println("狗狗吃狗糧!");} } class Cat implements AnimalEat{@Overridepublic void eat() {System.out.println("貓貓吃貓糧!");} } public class TestDemo {public static void main(String[] args) {AnimalEat animal1 = new Dog();AnimalEat animal2 = new Cat();animal1.eat();animal2.eat();} }?注意:子類和父類之間是extends 繼承關系,類與接口之間是 implements 實現關系
2.4 接口特性?
1.接口當中的成員變量,默認都是 public static final 修飾的
?2.接口當中的成員方法默認都是抽象方法:即接口中的方法會被隱式的指定為public abstract
3.接口當中的普通成員方法,是不能有具體的實現的
4.接口當中的普通成員方法,如果要有具體的實現,必須加上 default? 修飾
?5.接口當中可以有靜態的成員方法,但是不管是靜態的方法還是default修飾的方法,都是public修飾的
interface IShape{int count = 10;void draw();public default void func(){}public static void func2(){System.out.println("靜態成員方法");}}?public 是系統默認的可寫可不寫
6.接口也是不可以實例化的 ,接口類型是一種引用類型,但是不能直接new接口的對象
7.一個接口可以引用具體實現類,相當于是一個向上轉型。
interface IShape{void draw();/*int count = 10;*//*default void func(){}static void func2(){System.out.println("靜態成員方法");}*/} class Ract implements IShape{@Overridepublic void draw() {System.out.println("畫一個矩形");} } class Flower implements IShape{@Overridepublic void draw() {System.out.println("畫一朵畫");} } public class Test {public static void drawMap(IShape shape){shape.draw();}public static void main(String[] args) {drawMap(new Flower());drawMap(new Ract());/* IShape iShape = new IShape();*/} }//畫一朵畫 //畫一個矩形 //8.接口當中不能有靜態,實例代碼塊,構造方法
public interface Shape {// 編譯失敗public Shape(){}{} // 編譯失敗void draw();void show(); }9.接口雖然不是類,但是接口編譯完成后字節碼文件的后綴格式也是.class
2.5 實現多個接口?
abstract class Animal{public String name;public int age;public Animal(String name, int age) {this.name = name;this.age = age;}public abstract void eat(); }interface IFlying {//提供一個飛的接口void fly(); } interface IRunning {//提供一個跑的接口void run(); } interface ISwimming {//提供一個游泳的接口void swim(); }//叫 跑 游泳 class Duck extends Animal implements IRunning,IFlying,ISwimming{public Duck(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println(name+"正在吃鴨糧");}@Overridepublic void fly() {System.out.println(name+"正在飛");}@Overridepublic void run() {System.out.println(name+"正在跑");}@Overridepublic void swim() {System.out.println(name+"正在游泳");} } class Dog extends Animal implements IRunning{public Dog(String name,int age){super(name,age);}@Overridepublic void eat() {System.out.println(name+"正在吃狗糧");}@Overridepublic void run() {System.out.println(name+"正在四條腿跑");} } //叫 跑 飛 //一定是先繼承再實現 class Bird extends Animal implements IRunning,IFlying{public Bird(String name,int age){super(name,age);}@Overridepublic void eat() {System.out.println(name+"正在吃鳥糧");}@Overridepublic void run() {System.out.println(name+"正在用兩條腿跑");}@Overridepublic void fly() {System.out.println(name+"正在用翅膀飛");} } class Robot implements IFlying{@Overridepublic void fly() {System.out.println("機器人正在飛");} }public class Test {public static void walk(IRunning iRunning){iRunning.run();}public static void fly(IFlying iFlying){iFlying.fly();}public static void main(String[] args) {//Dog沒有實現FLY:fly(new Dog("狗子",1));fly(new Bird("鸚鵡",1));//這兩個類都實現了ifly接口fly(new Duck("唐老鴨",1));fly(new Robot());}public static void main2(String[] args) {walk(new Dog("狗子",1));//這三個類都實現了running接口walk(new Bird("鸚鵡",1));walk(new Duck("唐老鴨",1));}public static void func1(Animal animal){animal.eat();}public static void main1(String[] args) {func1(new Dog("狗子",1));func1(new Bird("鸚鵡",1));func1(new Duck("唐老鴨",1));} }?
?注意:一個類實現多個接口時,每個接口中的抽象方法都要實現,否則類必須設置為抽象類
上面的代碼展示了 Java 面向對象編程中最常見的用法: 一個類繼承一個父類, 同時實現多種接口.
繼承表達的含義是 is - a 語義, 而接口表達的含義是 具有 xxx 特性 .
貓是一種動物, 具有會跑的特性.
青蛙也是一種動物, 既能跑, 也能游泳
鴨子也是一種動物, 既能跑, 也能游, 還能飛
在這里就很好的映射了上面所說的 接口可以看成是:多個類的公共規范,是一種引用數據類型
我們在上面的代碼中定義了? IRunning IFlying ISwimming 幾個公共接口,所有的類都可以去調用它們,不用去管是不是Animal類型的,如Robot類可以調用IRunning,但是Robot并不是Animal類的子類。
在這個里面比如說游泳,這個時候肯定不能把游泳的方法定義在父類Animal里面了,因為Dog類也會繼承到這個方法,但是狗不會游泳,這個時候就顯得不是很恰當。那如果我們又多定義一個游泳的類,但是duck已經繼承了Animal類,不可能再繼承游泳類了。所以,接口這個時候就顯得十分的好用,一個類可以實現多個接口,并且也可以實現多態的思想,對于各種各樣不同的行為功能,我們只要在類的后面實現各個接口就好了。
2.6 接口間的繼承?
在Java中,類和類之間是單繼承的,一個類可以實現多個接口,接口與接口之間可以多繼承。即:用接口可以達到
多繼承的目的。
接口可以繼承一個接口, 達到復用的效果. 使用 extends 關鍵字.?
接口間的繼承相當于把多個接口合并在一起
2.7,幾個重要接口使用實例?
Comparable接口?
public interface Comparable<T> {public int compareTo(T o); } //這是Comparable接口里面的方法情況,就一個comparableTo方法,在實現的類里面需要進行重寫?假如這里有一個學生類,我們定義了一個學生數組,需要我們按照學生的年齡進行排序,那么如下實現:
class Student implements Comparable<Student>{public String name;public int age;public double score;public Student(String name, int age, double score) {this.name = name;this.age = age;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}@Overridepublic int compareTo(Student o) {return this.age-o.age;} }public class Test {public static void main(String[] args) {Student[] stu = new Student[3];stu[0] = new Student("xiaowang",18,88.0);stu[1] = new Student("zhangsan",19,66.0);stu[2] = new Student("lisi",12,88.9);System.out.println("排序前" +Arrays.toString( stu));Arrays.sort(stu);//比較兩個對象的大小//error: if (student>student2){}System.out.println("排序后" +Arrays.toString( stu));} }注意事項: 對于 sort 方法來說, 需要傳入的數組的每個對象都是 "可比較" 的, 需要具備 compareTo 這樣的能力. 通
過重寫 compareTo 方法的方式, 就可以定義比較規則?
?這是我們在學生類里面重寫了compareTo方法,按照兩個學生對象的年齡進行比較。其中,this.age代表你調用這個方法的對象的年齡,比如上面的o.age代表你傳參傳入的對象的年齡。當我們對數組進行排序調用sort方法的時候,sort方法的內部會自動調用compareTo方法,在默認的邏輯上,當this.age - o.age < 0 的時候不會進行交換,所以排序出來也就是升序的;但如果我們交換一下變成 o.age - this.age的話,那么原本是升序的,比如3,5,這個時候你用5-3 > 0,那么大于0就需要進行交換,所以最后排序出的結果也就是降序的。
總結:如果以后是自定義類型的數據,牽扯到大小比較,需要進行一些設計的。比如實現接口。
但是在這里面我們已經寫死了,如果我們想按照其他類型比較的話只能在 compareTo方法內區改變this和o,那這么搞的話會影響到其他代碼的運行
Comparator接口?
又稱比較器
class Student implements Comparable<Student>{public String name;public int age;public double score;public Student(String name, int age, double score) {this.name = name;this.age = age;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}@Overridepublic int compareTo(Student o) {return this.age-o.age;} } //根據年紀比較 class AgeComparator implements Comparator<Student> {@Overridepublic int compare(Student o1, Student o2) {return o1.age-o2.age;} } //根據分數比較 class ScoreComparator implements Comparator<Student>{@Overridepublic int compare(Student o1, Student o2) {return (int)(o1.score-o2.score);} }public class Test {public static void main(String[] args) {Student student1 = new Student("bit",10,78.9);Student student2 = new Student("abc",11,88.9);//比較年齡AgeComparator ageComparator = new AgeComparator();int ret = ageComparator.compare(student1,student2);System.out.println(ret);//比較分數ScoreComparator scoreComparator = new ScoreComparator();int ret2 = scoreComparator.compare(student1,student2);System.out.println(ret2);}}可以根據自己的需要去比較大小
我們可以將不同類型的比較方法都分離出來,在需要的時候調用,不會影響到其他的代碼的運行,這樣就可以將我們具體的比較過程與我們的學生類分離開,就可以定義多個不同的比較類,多個比較依據,根據不同的比較類去定義不同的對象,然后將學生對象作為參數進行傳入到compare方法里面,這樣調用不同的比較類的比較方法,根據用戶的需求,自己去調用比較器。
為了進一步加深對接口的理解, 我們可以嘗試自己實現一個 sort 方法來完成剛才的排序過程(使用冒泡排序)
public static void sort(Comparable[] array){//因為學生類實現了Comparable接口的,所以用來接收學生數組for(int i = 0;i < array.length - 1;i++){for(int j = 0;j < array.length - 1 - i;j++){if(array[j].compareTo(array[j+1]) > 0){Comparable tmp = array[j];array[j] = array[j+1];array[j+1] = tmp;}}} } .....//在main函數里面就可以直接用sort(stu)就行,只不過這里的compareTo依舊是你在Student里面重寫的那個,以年齡為依據比較2.8Clonable 接口和深拷貝
Object類里面有一個clone方法,可以用來創建一個對象的拷貝,但是如果想要使用clone方法,必須先實現Clonable接口,不然就會拋出一個CloneNotSupportedException 異常。
可以看到,我們的Clonable接口里面實際上是沒有任何抽象方法的,這叫做空接口或者標記接口,表示該類可以進行克隆?
class Person implements Cloneable{public int age;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();//要克隆必須要重寫一個克隆方法}@Overridepublic String toString() {return "person{" +"age=" + age +'}';} }public class Test {public static void main(String[] args) throws CloneNotSupportedException {Person person = new Person();Person person1 = (Person) person.clone();} }淺拷貝 VS 深拷貝
淺拷貝
class Money{public double money = 19.9; }class Person implements Cloneable{public int age = 10;public Money m = new Money();@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();//要克隆必須要重寫一個克隆方法}@Overridepublic String toString() {return "person{" +"age=" + age +'}';} }public class Test {public static void main(String[] args) throws CloneNotSupportedException {Person person = new Person();Person person2 = (Person) person.clone();System.out.println(person.m.money);System.out.println(person2.m.money);System.out.println("=========================");person2.m.money = 99.9;System.out.println(person.m.money);System.out.println(person2.m.money);} }?
淺拷貝是只拷貝了這個對象,沒有拷貝對象里面的引用所指的對象
如上代碼,我們可以看到,通過clone,我們只是拷貝了Person對象。但是Person對象中的Money對象,并沒有拷貝。通過person2這個引用修改了m的值后,person這個引用訪問m的時候,值也發生了改變。這里就是發生了淺拷貝
所以在這個代碼中我們一旦更改了money的值拷貝的和被拷貝的值都發生了變化
深拷貝
class Money implements Cloneable{public double money = 19.9;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();//重寫克隆方法} }class Person implements Cloneable{public int age = 10;public Money m = new Money();@Overrideprotected Object clone() throws CloneNotSupportedException {Person temp = (Person)super.clone();temp.m = (Money)this.m.clone();return temp;//return super.clone();//要克隆必須要重寫一個克隆方法}@Overridepublic String toString() {return "person{" +"age=" + age +'}';} }public class Test {public static void main(String[] args) throws CloneNotSupportedException {Person person = new Person();Person person2 = (Person) person.clone();System.out.println(person.m.money);System.out.println(person2.m.money);System.out.println("=========================");person2.m.money = 99.9;System.out.println(person.m.money);System.out.println(person2.m.money);} } 我們需要讓Money類也實現Cloneable這個接口,后重寫克隆方法這時我們就可以去實現兩個克隆,對數據有更深層的克隆,我們就可以對Money也進行克隆了,我們可以看到在Person的克隆方法中我們先定義一個臨時的變量temp,將Person和Money都克隆給他,后返回temp給person2,就完成了深拷貝 對于一個拷貝到底是深拷貝還是淺拷貝,不取決于數據的類型,完全是取決于代碼的實現2.9 抽象類和接口的區別?
3. Object類
Object類是Java默認提供的一個類,它是所有類的父類,可與稱之為超類。
Object是Java默認提供的一個類。Java里面除了Object類,所有的類都是存在繼承關系的。默認會繼承Object父
類。即所有類的對象都可以使用Object的引用進行接收。?
可以用Object接收任意類的對象
3.1,Object類方法總覽
?對于整個Object類中的方法需要實現全部掌握。
本小節當中,我們主要來熟悉這幾個方法:toString()方法,equals()方法,hashcode()方法
3.1.1 獲取對象信息 :toString()方法
果要打印對象中的內容,可以直接重寫Object類中的toString()方法
// Object類中的toString()方法實現:public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode()); }3.1.2 對象比較:equals方法
在Java中,==進行比較時有三種情況:
a.如果==左右兩側是基本類型變量,比較的是變量中值是否相同
b.如果==左右兩側是引用類型變量,比較的是引用變量地址是否相同
?c.如果要比較對象中內容,必須重寫Object中的equals方法,因為equals方法默認也是按照地址比較的:?
equals底層代碼:
class Person{public String id;public Person(String id) {this.id = id;}@Overridepublic String toString() {return "Person{" +"name='" + id + '\'' +'}';}@Overridepublic boolean equals(Object obj){//判斷obj參數是否為空if (obj == null){return false;}if(this == obj){return true;//有可能是對象之間的賦值,例如per2 == per1,那兩個肯定是相等的}if(!(obj instanceof Person)){return false;//兩個毫不相關的類的屬性不能進行比較//obj根本就不是Person類的對象,所以肯定不相等}Person tmp = (Person) obj;return this.id.equals(tmp.id);//Sting類型的equals}} class Student{ }public class Test {public static void func(Object obj) {System.out.println(obj);}public static void main(String[] args) {Person person1 = new Person("123");Person person2 = new Person("123");System.out.println(person1==person2);//System.out.println("equals未重寫的 "+person1.equals(person2));System.out.println("equals重寫后的 "+person1.equals(person2));} }?自定義類型都需要重寫equals的!!
equals:判斷對象是否相同
3.1.2 hashcode?
?hashcode方法的作用是幫助我們定位一個對象的具體的位置。
hashcode方法源碼:
public native int hashCode();我們認為兩個id一樣的對象,將存儲在同一個位置,如果不重寫hashcode()方法,我們可以來看示例
代碼:
?
注意事項:兩個對象的hash值是不一樣的
在未重寫hashCode時,id相同時hash值是不一樣的
class Person{public String id;public Person(String id) {this.id = id;}}@Overridepublic int hashCode(){return Objects.hash(id);}} public class Test {public static void func(Object obj) {System.out.println(obj);}public static void main(String[] args) {Person person1 = new Person("123");Person person2 = new Person("123");//將來對于他們我們想放在同一個位置上System.out.println(person1.hashCode());System.out.println(person2.hashCode());} }在重寫hashCode后兩個地址就一樣了
其實一般情況下,對于自定義的類型,上面的equals和hashcode方法最好都是需要重寫下的,因為你不清楚什么時候可能就會用到,而編譯器也是為我們提供了自動生成的快捷方式。?
?然后一輪next就會得到
@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return Objects.equals(id, person.id);}@Overridepublic int hashCode() {return Objects.hash(id);}總結
以上是生活随笔為你收集整理的JavaSE:抽象类和接口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mbps和MBps
- 下一篇: java美元兑换,(Java实现) 美元