一、接口
接口(interface)使得Java抽象的概念更加清晰,上一章文中,我们可以使用abstract声明一个抽象类,使其和普通的类区分出来,没有提供任何具体实现。而接口interface关键字替代class来创建接口,接口是一个完全抽象的,同样的不提供任何实现,只允许确定方法名、参数列表和返回类型,没有方法体,和抽象类中的抽象方法一样,但是要注意的是
1.抽象类允许非抽象方法的存在,而接口不可以,接口的每个方法都默认是抽象方法。
2.接口中的变量默认地是static和final以及public权限(接口不允许空final的存在),而抽象类可以是各种类型。
3.继承类只能是单继承,而实现接口可以多实现。
4.类的继承只是单继承,而接口的继承可以是多继承。
如下创建一个接口:
public interface Inter{ … }
要让一个类实现某个接口时,使用implements关键字,如下:
public class class_oneimplements Inter{ … }
需要实现多个接口的时候,每个接口之间用“,”隔开,如下:
public interface Inter1{ … }
public interface Inter2{ … }
public class class_oneimplements Inter, Inter1, Inter2{ … }
接口的实现和类的继承可以同时存在,如下:
public classclass_two{ … }
public classclass_one extends class_two implements Inter, Inter1, Inter2{ … }
接口的多继承,如下:
public interfaceInter3 extends Inter, Inter1, Inter2{ … }
我们可以看出,通过接口的单继承和多继承,很容易的在接口添加新的方法声明以及将多个接口组合在一起,也体现Java的特性之一多态。
其次,我们明白Java的设定,类只能单继承,一个类对多个接口的同时实现,就是Java类实现多继承功能的方式之一。
在上面描述中,可所谓是多种多样,要是程序中创建了多个接口和类,并将这些组合起来,多种多样的组合那可将会是五彩斑斓以及眼花缭乱,但这中间一定要注意方法等名字的冲突,个人认为最好的办法就是在接口和类中创建方法等时取名一定要规范、可识别度高、可读性强,简单来说就是看到一个名字,你就能知道它是做什么的(出自什么地方)。
利用接口解耦(耦合:关联性),例如:有两组代码,A组代码和B组代码,现在A组代码需要使用测试乐器的声音,A组代码用到了测试代码V,而B组代码是测试喇叭的声音,也用到了测试代码V,然而,在没有接口的情况下,明明实现代码一样,B组代码却无法使用A组代码中的测试代码V,而A组代码无法使用B组代码的测试代码V,这是因为A组代码和B组代码分别与V耦合太深,这时候就运用了接口去解除之间的耦合性,使得V可以被A和B通用,举例如下(A、B在同一个包下):
未修改之前的A组:
public class V {
public static void v (Instrumentinstrument) {
instrument.test();
}
public static voidmain(String[] args) {
v(new Piano());
}
}
class Instrument { public void test() {…} }
class Piano extends Instrument {
public void test() { System.out.println("Pianotest"); }
}
未修改之前的B组:
public class V{
public static void v (Horn horn) {
horn.test();
}
public static void main(String[] args) {
v(newHorn_A());
}
}
class Horn{ public void test() {…} }
class Horn_A extends Horn {
public voidtest() { System.out.println("Horn_A test"); }
}
修改后(A、B、V在同一个包下):
V组:
public interface VoiceTest {void test(); }
public class V {
public static void v (VoiceTest voiceTest) {
voiceTest.test();
}
}
A组:
public class A_V{
public static void main(String[] args){
V.v(new Piano());
}
}
class Instrument implement VoiceTest { public void test() {…} }
class Piano extends Instrument {
public void test() { System.out.println("Piano test");}
}
B组:
public class B_V {
public static void main(String[] args) {
V.v(new Horn_A());
}
}
class Horn implement VoiceTest { public void test() {…} }
class Horn_A extends Horn {
public void test() { System.out.println("Horn_Atest"); }
}
(例程只限于为了更好的理解,其中类和方法的取名很多不符合规范,请读者注意。)
二、内部类
内部类,字面意思很好理解,定义在类里面的类。
内部类可分为四种:1.成员内部类2.局部内部类3.匿名内部类4.静态内部类
a) 成员内部类
成员内部类,算是内部类中最普通的,其属于外围类的一个成员,所以称作成员内部类,成员内部类可以无限制的调用外围类的成员属性和方法。既然它是外围类一员,所以必须先创建了外围类才能创建内部类,其次,成员内部类中不能任何static的方法和变量。
创建成员内部类:
Public class Outer{
private int outer_i = 5;
Class Inner{
private inner_i =outer_i;
}
Public Inner getInner(){
Return new Inner( );
}
Class Inner1{ Inner1(String s){} }
Public Inner getInner1(String s1){
Return new Inner1(s1);
}
public void ship(){
Inner inner = newInner;
Inner1 inner1 = newInner(“inner1”);
}
}
调用内部类
第一种方式:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
Outer.Inner1 inner1 = outer.new Inner1(“Inner1”);
第二种方式:
Outer.Innerinner = outer.getInner ();
Outer.Inner1 inner1 = outer.getInner1 (“Inner1”);
由上可以看出,想要调用成员内部类,就必须通过外围类来调用。
再者,内部类对象通过外围类对象创建的时候,其内部类对象也会秘密地获取一个指向那个外围类对象的引用,这就是为什么内部类可以引用外围类的成员属性和方法。
b)局部内部类
在方法和作用域内的内部类,称为局部内部类,通常这么做是为了解决两个问题:
1、实现了某类型的接口,于是可以创建并返回对其的引用。
2、要解决一个复杂的问题,创建一个类来辅助你解决方案,同时又不希望这个类是公用的。
第一个问题情况如下:
interface C{
void testB();
}
public class A {
String name;
private class B implementsC{
public void testB(){
}
}
public B getB(){
return new B();
}
}
class test{
public static voidmain(String[] args) {
A a = new A();
C ab = a.getB();
ab.testB();
}
}
第二种问题情况如下:
public class Test{
private void test_tra (Boolean b){
if (b){
class tra{
tra(String a){
String getSlip(){ return a; }
}
}
tra a = new tra(“tra”);
}
}
public void track() { test_tra (true); }
public static coid main(String[] args){
Test p = new Test( ) ;
p. track();
}
}
c) 匿名内部类
顾名思义,没有名字的内部类,这样说,可能理解不了,直接看下面例程和分析:
public interface C{ }
public class Outer{
classMyC implements C {
private int i = 0;
}
public C c(){
return new MyC();
}
public static void main (String[ ] args) {
Outerouter = new Outer( );
C c = outer. c( );
}
}
可以看出上面例程中,内部类是通过c方法new 表达式返回的引用向上转型直接返回一个C对象,这个内部类没有名字,可能上面的例程不是特别好理解这个匿名这个意思,那么把上面的例程简化一下,如下:
public interface C{ }
public class Outer{
public C c(){
return new C (){
private int i = 0;
};
}
public static void main (String[ ] args) {
Outerouter = new Outer( );
C c = outer. c( );
}
}
简化之后,可以明确的看出通过c方法返回了(new)一个继承自C的内部类的对象,而且可以看出在return语句结束之前插入这个类的定义。
当然上面是用接口举了例子,接口可以换成普通类来充当被其导出类的“公共”接口,那么问题来了,普通类中有带参数的构造器时,该怎么办?如下
public class C{
private int I;
public C( int x ){
I = x;
}
}
public class Outer{
public C c(inty){
finalint k = 0;
return new C (y){
private int J = k;
};
}
public static void main (String[ ] args) {
Outerouter = new Outer( );
C c = outer. c( );
}
}
这里将y直接导入就可以了,在例程中还要有一个注意的地方就是在类的定义中使用了k,所以k一定要被声明为final,假如类的定义中使用了y,同样的道理,int y就要改成final int y 。
d) 静态内部类(嵌套内部类)
在前面我们提到,Static可以修饰变量、方法、代码块、同样的它也可以修饰内部类,被修饰的内部类称为静态内部类(嵌套内部类),声明为静态之后,就可以脱离外部类的实例依赖。那么这样会造成两种现象:
1. 要创建静态内部类的对象时,不需要通过其外围类的对象。
2. 不能使用外围类的任何非static成员变量和方法。
Public class Outer{
static class Inner{
private inner_i =outer_i;
}
public static void main (String[ ] args) {
Outer.Inner inner = new outer.Inner();
}
}
内部类的主要意义
在上面介绍接口的时候,我说接口多实现就是Java类实现多继承功能的方式之一,而第二种方法就是通过内部类实现多继承功能,尽管内部类实现多继承也建立在接口上,但是却实现了类的多继承功能,使得Java在实现类似多继承功能上更加完整。(这里需要说明的是,Java类是不能多继承的,要实现类似多继承功能只能通过其他方式,概念不要混淆),上例程:
public interface A{
public int a();
}
public class X implement A{
public int a(){ return1}
}
public class Y {
public int b(){ return2}
}
public class C extends Y implements A {
private class XXextend X {
public int a(){
return super.a();
}
}
public int a(){
return newXX.a();
}
public int b(){
returnsuper.b();
}
}
可以看出,class C已经在功能上继承(拥有)了classA和class B的功能。
------------------------------------------------分割线----------------------------------------------------------
上文如有错误、写得不合理以及有疑惑的地方,希望您可以在评论区提出。
在下十分感谢。
如果只是想在评论区吐吐槽、聊聊心得、卖个萌,同样也十分欢迎。
祝大家生活美好,干杯!~( ゜▽゜)つロ
转载请注明原作者以及附上原文章地址,谢谢。