Sebelum menerangkan antara muka, kelas abstrak ialah konsep yang tidak boleh dipintaskan sebagai kelas yang lebih abstrak daripada kelas abstrak.
Apakah itu kelas abstrak? "Kelas yang mengandungi satu atau lebih kaedah abstrak ialah kelas abstrak, dan kaedah abstrak ialah kaedah tanpa badan kaedah" Kedua-dua kaedah abstrak dan kelas abstrak mesti diisytiharkan sebagai abstract
. Contohnya:
<code>// 抽象类<br>public abstract class Person {<br> // 抽象方法<br> public abstract String getDescription();<br>}</code>
Ingat! "Selain kaedah abstrak, kelas abstrak juga boleh mengandungi data konkrit dan kaedah konkrit" . Contohnya, kelas abstrak Person
juga menyimpan nama dan kaedah khusus yang mengembalikan nama:
<code>public abstract class Person{<br> private String name;<br> public Person(String name){<br> this.name = name ;<br> }<br> public abstract String getDescription();<br> public String getName(){<br> return name;<br> }<br>}</code>
❝Ramai pengaturcara akan "salah" berfikir bahawa dalam abstraksi Kelas tidak boleh mengandungi kaedah konkrit. Malah, ini juga perbezaan antara antara muka dan kelas abstrak tidak boleh mengandungi kaedah tertentu.
❞
"Kelas abstrak tidak boleh dibuat seketika". Dalam erti kata lain, jika kelas diisytiharkan sebagai abstract
, objek kelas ini tidak boleh dibuat.
<code>new Person("Jack"); // Error</code>
boleh mentakrifkan pembolehubah objek bagi kelas abstrak, tetapi ia hanya boleh merujuk kepada objek subkelas bukan abstrak. Andaikan bahawa kelas Student
ialah subkelas bukan abstrak Person
:
<code>Person p = new Student("Jack"); // Right</code>
Subkelas bukan abstrak yang dipanggil bermakna jika anda mencipta subkelas yang mewarisi kelas abstrak dan mencipta membantahnya, kemudian "Takrifan kaedah mesti disediakan untuk semua kaedah abstrak kelas induk" . Jika anda tidak melakukan ini (anda boleh memilih untuk tidak melakukannya), subkelas masih akan menjadi kelas abstrak dan pengkompil akan memaksa kami menambah kata kunci abstract
pada kelas baharu.
Yang berikut mentakrifkan subkelas khusus Person
kelas abstrak lanjutan Student
:
<code>public class Student extends Person { <br> private String major; <br> public Student(String name, String major) { <br> super(name); <br> this.major = major; <br> } <br> @Override<br> public String getDescription(){ // 实现父类抽象方法<br> return "a student majoring in " + major; <br> } <br>} </code>
melaksanakan kaedah abstrak Student
dalam kelas induk dalam kelas getDescription
. Oleh itu, "Semua kaedah dalam kelas Student
bukan abstrak dan kelas ini bukan lagi kelas abstrak" .
???? dipanggil seperti berikut:
<code>Person p = new Student("Jack","Computer Science");<br>p.getDescription();<br></code>
Oleh kerana objek kelas abstrak Person
tidak boleh dibina, pembolehubah p
tidak akan merujuk kepada Person
objek, tetapi kepada objek subkelas Spesifik seperti Student
, dan kaedah getDescription
ditindih dalam objek ini.
Intipati antara muka sebenarnya ialah kelas, dan ia adalah kelas yang lebih abstrak daripada kelas abstrak. Bagaimana untuk mengatakannya? Kelas abstrak boleh mengandungi kaedah konkrit, tetapi antara muka menghapuskan kemungkinan ini "Sebelum Java 8, antara muka adalah sangat tulen dan hanya boleh mengandungi kaedah abstrak, iaitu kaedah tanpa badan kaedah" . Terdapat beberapa perubahan dalam antara muka dalam Java 8, dan antara muka telah mula dibenarkan untuk mengandungi kaedah lalai dan kaedah statik, yang akan diterangkan di bawah.
Java menggunakan kata kunci interface
dan bukannya class
untuk mencipta antara muka. Seperti kelas, kami biasanya menambah kata kunci interface
sebelum kata kunci public
, jika tidak, antara muka hanya mempunyai kebenaran akses pakej dan hanya boleh digunakan di bawah pakej dengan antara muka yang sama.
<code>public interface Concept {<br> void idea1();<br> void idea2();<br>}</code>
Begitu juga, kerana terdapat kaedah abstrak dalam antara muka, ia perlu dilanjutkan (diwarisi). Gunakan kata kunci implements
untuk membuat kelas memanjangkan antara muka tertentu (atau sekumpulan antara muka Dalam istilah orang awam: antara muka hanyalah rupa Sekarang subkelas lanjutan ini perlu menerangkan cara ia berfungsi.
<code>class Implementation implements Concept {<br> @Override<br> public void idea1() {<br> System.out.println("idea1");<br> }<br> <br> @Override<br> public void idea2() {<br> System.out.println("idea2");<br> }<br>}<br></code>
Perlu diingatkan di sini bahawa anda boleh memilih untuk mengisytiharkan kaedah dalam antara muka secara eksplisit sebagai public
, tetapi "Walaupun anda tidak melakukan ini, mereka masih public
". Jadi apabila melaksanakan antara muka, kaedah daripada antara muka mesti ditakrifkan sebagai public
. Jika tidak, mereka hanya mempunyai akses pakej, jadi apabila diwarisi, kebolehcapaian mereka dikurangkan, yang tidak dibenarkan oleh pengkompil Java.
Selain itu, pemalar dibenarkan dalam antara muka Sama seperti kaedah dalam antara muka ditetapkan secara automatik kepada public
, medan dalam antara muka akan ditetapkan secara automatik kepada public static final
Taip" <.>, sebagai contoh:
<code>public interface Concept {<br> void idea1(); // public void idea1();<br> // 静态属性<br> double item = 95; // a public static final constant<br>}</code>❝
boleh menandakan kaedah antara muka sebagai3. Ciri-ciri antara muka Perbezaan antara antara muka dan kelas ialah kitadan medan sebagai
public
Sesetengah pengaturcara di luar kebiasaan atau untuk meningkatkan kejelasan Pertimbangkan, bersedia berbuat demikian, tetapi Spesifikasi Bahasa Javapublic static final
"Adalah disyorkan untuk tidak menulis kata kunci berlebihan ini" . ❞
tidak boleh menggunakan operator untuk membuat seketika antara muka seperti kelas "new
:
<code>x = new Concept(. . .); // ERROR</code>Alasannya juga sangat mudah. Antara muka tidak mempunyai kaedah pembinaan tertentu, jadi ia tidak boleh dibuat instantiated. Sudah tentu, walaupun objek antara muka tidak boleh dibina, masih mungkin untuk mengisytiharkan pembolehubah antara muka:
<code>Concept x; // OK</code>
接口变量必须引用实现了接口的类对象:
<code>x = new Implementation(. . .); // OK provided Implementation implements Concept</code>
接下来, 如同使用 instanceof
检查一个对象是否属于某个特定类一样, 也可以使用 instanceof
检查一个对象是否实现了某个特定的接口:
<code>if(x instanceof Concept){<br> ...<br>}</code>
另外,与可以建立类的继承关系一样,「接口也可以被继承」:
<code>public interface Concept1 {<br> void idea1();<br> void idea2();<br>}<br><br>-------------------------------------------<br> <br>public interface Concept2 extends Concept1{<br> double idea3();<br>}</code>
当然,读到这里大家可能依然无法理解,既然有了抽象类,为什么 Java 程序设计语言还要不辞辛苦地引入接口这个概念?
很重磅!因为「一个类可以实现多个接口,但是一个类只能继承一个父类」。正是接口的出现打破了 Java 这种单继承的局限,为定义类的行为提供了极大的灵活性。
<code>class Implementation implements Concept1, Concept2 // OK</code>
有一条实际经验:在合理的范围内尽可能地抽象。显然,接口比抽象类还要抽象。因此,一般更倾向使用接口而不是抽象类。
上文提过一嘴,「在 Java 8 中,允许在接口中增加静态方法和默认方法」。理论上讲,没有任何理由认为这是不合法的,只是这有违于将接口作为抽象规范的初衷。举个例子:
<code>public interface Concept {<br> // 静态方法<br> public static void get(String name){<br> System.out.println("hello " + name);<br> }<br> // 默认方法<br> default void idea1(){<br> System.out.println("this is idea1");<br> };<br>}</code>
用 default
修饰符标记的方法就是默认方法,这样子类就不需要去实现这个方法了。
不过,引入默认方法后,就出现了一个「默认方法冲突」的问题。如果先在一个接口 A 中将一个方法 idea
定义为默认方法, 然后又在另一个接口 B 或者超类 C 中定义了同样的方法 idea
,然后类 D 实现了这两个接口 A 和 B(或超类 C)。于是类 D 中就有了方法 idea
的两个默认实现,出现了冲突,为此,Java 制定了一套规则来解决这个二义性问题:
1 ) 「超类优先」。如果超类提供了一个具体方法,接口中的同名且有相同参数类型的默认方法会被忽略。
2 ) 「接口冲突」。如果一个父类接口提供了一个默认方法,另一个父类接口也提供了一个同名而且参数类型相同的方法,子类必须覆盖这个方法来解决冲突。例如:
<code>interface A {<br> default void idea(){<br> System.out.println("this is A");<br> }<br>}<br><br>interface B {<br> default void idea(){<br> System.out.println("this is B");<br> }<br>}<br><br>// 需要在 D 类中覆盖 idea 方法<br>class D implements A, B{<br> public void getName(){<br> System.out.println("this is D");<br> }<br>}</code>
现在假设 B
接口没有为 idea
提供默认实现:
<code>interface B {<br> void idea();<br>}</code>
那么 D 类会直接从 A 接口继承默认方法吗?这好像挺有道理, 不过,Java 设计者更强调一致性。两个接口如何冲突并不重要,「只要有一个接口提供了一个默认实现,编译器就会报告错误, 我们就必须解决这个二义性」。
当然,如果两个接口都没有为共享方法提供默认实现, 那么就与 Java 8 之前的情况一样,这里不存在冲突。
在我自己早期学习编程的时候,对接口存在的意义实在困惑,我自己乱写代码的时候基本上不可能意识到需要去写接口,不知道接口到底有什么用,为什么要定义接口,感觉定义接口只是提前做了个多余的工作。
其实不是,定义接口并非多余,「接口是用来提供公用的方法,规定子类的行为的」。举个例子,让大家直观的感受下接口的作用:
比如有个网站, 需要保存不同客户的信息, 有些客户从 Web 网站来, 有些客户从手机客户端来, 有些客户直接从后台管理系统录入。假设不同来源的客户有不同的处理业务流程, 这个时候我们定义接口来提供一个保存客户信息的方法,然后不同的平台实现我们这个保存客户信息的接口,以后保存客户信息的话, 我们只需要知道这个接口就可以了,具体调用的方法被封装成了黑盒子,这也就是 Java 的多态的体现,「接口帮助我们对这些有相同功能的方法做了统一管理」。
Atas ialah kandungan terperinci Apakah peranan antara muka dalam Java?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!