接口

试想下,如果想要在一个类型中能够兼容多种类型特征,代码该如何编写呢?如果多个不同的类型在不具有相同父类的情况下,仍然要具有相同的特征,那代码又该如何编写呢?那么,在Java中,就可以通过接口实现这些行为的关联。
接口,在JAVA编程语言中是一个抽象类型,是抽象方法的集合。
接口是一种规范。

  1. public interface IPhoto {
  2. public void photo();
  3. }

注:

  1. 接口不是类,但是具有类的某些特征,它也是引用数据类型。
  2. 接口称之为最彻底的抽象类,这是因为在jdk1.8之前是几乎没有带方法体的方法的。
  3. 接口中的方法没有方法体。
  4. 类使用implements关键字来实现接口。
  5. 如果一个类实现了某个接口,那么必须重写此接口中的所有方法(如果不实现则此类必须设置为抽象类)。
  6. 接口无法直接实例化出对象,用接口的引用指向实现类的实例对象。

接口成员

接口,就如同抽象类是利用继承给子类指定规范一样,接口也是给一些没有关系的类制定了需要遵守的规范。接口不关心这些类的内部数据,也不关心这些类里面的方法的实现细节,它只规定这些类里面必须提供某些细节。

抽象方法和常量

  1. //访问修饰符 只能是public 和 默认default
  2. public interface INet {
  3. //接口中抽象方法可以不写abstract关键字
  4. public void network();
  5. //接口中抽象方法可以不写public,但是依然会按照public的限定范围使用
  6. void connection();
  7. //接口中,可以包含常量
  8. public static final int TEMP = 20;
  9. //关于常量的修饰符可以省略,默认public static final
  10. int TEMP2 = 15;
  11. }

测试类

  1. INet it = new Computer();//接口引用指向具体实现类
  2. System.out.println(it.TEMP);

如果在实现类中存在和接口同名的常量,在接口引用指向实现类时,调用的是接口中定义的常量信息
**
注:

  1. 接口中的方法如果没有访问修饰符,默认为public,接口中的抽象方法只能使用public修饰
  2. 接口中可以存放常量
  3. 接口中定义的属性一定是常量,在定义常量的时候,public static final可以省略不写

**

默认方法和静态方法

  1. /**
  2. * jdk 1.8 以后提供了默认方法
  3. * 可以带方法体,默认方法不一定要被实现
  4. */
  5. default void Connection() {
  6. System.out.println("我是接口中的默认链接");
  7. }
  8. /**
  9. * jdk 1.8 以后提供了静态方法
  10. * 可以带方法体
  11. */
  12. static void stop() {
  13. System.out.println("我是接口中的静态方法");
  14. }

默认方法可以在实现类中重写,也可以不重写,并可以通过接口的引用调用。静态方法不可以在实现类中重写,只能通过接口名调用

重写默认方法必须用public修饰。

多接口重名处理

在Java中,只能单继承,但是可以实现多个接口。

同名方法

如果在接口中存在同名的默认方法,要么删除其中一个接口的方法,要么在实现类中,自己定义一个同名的方法。在接口引用指向实现类的时候,调用的是实现类中的方法。

一个类可以继承一个父类,同时实现若干接口

  1. public class FourthPhone extends ThirdPhone implements IPhoto,INet {
  2. @Override
  3. public void photo() {
  4. System.out.println("手机可以拍照");
  5. }
  6. @Override
  7. public void network() {
  8. System.out.println("手机可以上网");
  9. }
  10. public void game() {
  11. System.out.println("手机可以玩游戏");
  12. }
  13. }

如果在父类和接口中都存在同名方法,在FourthPhone 没有重写该方法不会报错,但最终指向的是父类中的同名方法。如果重写了该方法,最终指向的是FourthPhone中重写的方法。

同名常量

类实现了多接口时,如果多接口中出现了重名常量,在此类中通过接口名.变量的方式访问。

类继承父类又实现了多接口时,如果父类、多接口中出现了重名常量,只能在该实现类中自己再定义这个重名的变量,才能消除歧义。

接口的继承

接口之间同样也是存在继承关系的。在Java中,接口之间可以实现多继

  1. public interface IFather1 {
  2. void say();
  3. default void connection() {
  4. System.out.println("我是IFather1中的连接方法");
  5. }
  6. }
  1. public interface IFather2 {
  2. void fly();
  3. default void connection() {
  4. System.out.println("我是IFather2中的连接方法");
  5. }
  6. }
  1. public interface ISon extends IFather1,IFather2{
  2. void run();
  3. default void connection() {
  4. System.out.println("我是儿子的连接方法");
  5. }
  6. }
  1. public class Demo implements ISon{
  2. @Override
  3. public void say() {
  4. // TODO Auto-generated method stub
  5. }
  6. @Override
  7. public void fly() {
  8. // TODO Auto-generated method stub
  9. }
  10. @Override
  11. public void run() {
  12. // TODO Auto-generated method stub
  13. }
  14. }

1.子接口允许有多个父接口
2.子接口继承多个父接口,子接口的实现类需重写父接口中所有的抽象方法
3.多个父接口默认方法重名时子接口会报错,解决方案:子接口重写重名的默认方法

内部类

内部类(内部定义普通类,抽象类,接口的统称)是指一种嵌套的结构关系,即在一个类的内部除了定义属性和方法外还可以定义一个类结构,这样的形式使得程序的结构定义更加灵活。

为什么使用内部类?

准确来说,内部类会使一个类的内部充斥着其他的类结构,所以内部类在整体设计中最大的缺点就是破坏了良好的程序结构,造成代码结构的混乱。但他最大的优点在于可以方便的访问外部类的私有成员。所以我们使用内部类,更多的时候是希望某一个类只为单独一个类服务。

内部类注意点:

  • 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,前面冠以外部类的类名和$符号。
  • 内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否为 private 的。
  • 内部类声明成静态的,就不能随便访问外部类的成员变量,仍然是只能访问外部类的静态成员变量。

成员内部类

成员内部类(实例内部类)是指没有用 static 修饰的内部类,有的地方也称为非静态内部类。

  1. public class Out {
  2. class Inner {}
  3. }

在外部类的静态方法和外部类以外的其他类中,必须通过外部类的实例创建内部类的实例。

  1. public class Outer {
  2. class Inner {}
  3. Inner inner = new Inner(); // 不需要创建外部类实例
  4. public void method1() {
  5. Inner i = new Inner(); // 不需要创建外部类实例
  6. }
  7. public static void method2() {
  8. Inner i = new Outer().new Inner(); // 需要创建外部类实例
  9. }
  10. }
  11. //注意是否在同一个包中

在实例内部类中,可以访问外部类的所有成员。

  1. public class Outer {
  2. public int a = 100;
  3. static int b = 100;
  4. final int c = 100;
  5. private int d = 100;
  6. public String method3() {
  7. return "实例方法";
  8. }
  9. public static String method4() {
  10. return "静态方法";
  11. }
  12. class Inner {
  13. int a2 = a + 1; // 访问 public 的 a
  14. int b2 = b + 1; // 访问 static 的 b
  15. int c2 = c + 1; // 访问 final 的 c
  16. int d2 = d + 1; // 访问 private 的 d
  17. String str1 = method1(); // 访问实例方法method1
  18. String str2 = method2(); // 访问静态方法method2
  19. }
  20. public static void main(String[] args) {
  21. Inner in = new Outer().new Inner();
  22. System.out.println(in.a2); // 输出 101
  23. System.out.println(in.b2); // 输出 101
  24. System.out.println(in.c2); // 输出 101
  25. System.out.println(in.d2); // 输出 101
  26. System.out.println(in.str1); // 输出实例方法
  27. System.out.println(in.str2); // 输出静态方法
  28. }
  29. }

在外部类中不能直接访问内部类的成员,而必须通过内部类的实例去访问。如果类 A 包含内部类 B,类 B 中包含内部类 C,则在类 A 中不能直接访问类 C,而应该通过类 B 的实例去访问类 C。

外部类实例与内部类实例是一对多的关系,也就是说一个内部类实例只对应一个外部类实例,而一个外部类实例则可以对应多个内部类实例。

  1. public class Outer {
  2. int a = 10;
  3. class Inner {
  4. int a = 20;
  5. int a1 = this.a;
  6. int b3 = Outer.this.a;
  7. }
  8. }

在实例内部类中不能定义 static 成员,除非同时使用 final 和 static 修饰。

静态内部类

静态内部类是指使用 static 修饰的内部类。

  1. public class Outer {
  2. static class Inner {} // 静态内部类
  3. }

在创建静态内部类的实例时,不需要创建外部类的实例。

  1. public class Outer {
  2. static class Inner {} // 静态内部类
  3. }

静态内部类中可以定义静态成员和实例成员。外部类以外的其他类需要通过完整的类名访问静态内部类中的静态成员,如果要访问静态内部类中的实例成员,则需要通过静态内部类的实例。

  1. public class Outer {
  2. static class Inner {
  3. int a = 0;
  4. static int b = 0;
  5. }
  6. }
  7. class OtherClass {
  8. Outer.Inner oi = new Outer.Inner();
  9. int a2 = oi.a; // 访问实例成员
  10. int b2 = Outer.Inner.b; // 访问静态成员
  11. }

静态内部类可以直接访问外部类的静态成员,如果要访问外部类的实例成员,则需要通过外部类的实例去访问。

  1. public class Outer {
  2. int a = 0;
  3. static int b = 0;
  4. static class Inner {
  5. Outer o = new Outer();
  6. int a2 = o.b; // 访问实例变量
  7. int b2 = b; // 访问静态变量
  8. }
  9. }

方法内部类

局部内部类(方法内部类)是指在一个方法中定义的内部类。

  1. public class Test {
  2. public void method() {
  3. class Inner {} // 局部内部类
  4. }
  5. }

局部内部类与局部变量一样,不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰。

局部内部类只在当前方法中有效。

  1. public class Test {
  2. Inner i = new Inner(); // 编译出错
  3. Test.Inner ti = new Test.Inner(); // 编译出错
  4. Test.Inner ti2 = new Test().new Inner(); // 编译出错
  5. public void method() {
  6. class Inner {}
  7. Inner i = new Inner();
  8. }
  9. }

局部内部类中不能定义 static 成员。
局部内部类中还可以包含内部类,但是这些内部类也不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰。
在局部内部类中可以访问外部类的所有成员。
在局部内部类中可以直接访问当前方法中的参数与变量。如果方法中的成员与外部类中的成员同名,则可以使用外部类.this.变量的形式访问外部类中的成员。

  1. public class Test {
  2. int a = 0;
  3. int d = 0;
  4. public void method() {
  5. int b = 0;
  6. final int c = 0;
  7. final int d = 10;
  8. class Inner {
  9. int a2 = a; // 访问外部类中的成员
  10. int b2 = b; // 访问外部类中的成员
  11. int c2 = c; // 访问方法中的成员
  12. int d2 = d; // 访问方法中的成员
  13. int d3 = Test.this.d; //访问外部类中的成员
  14. }
  15. Inner i = new Inner();
  16. System.out.println(i.d2); // 输出 10
  17. System.out.println(i.d3); // 输出 0
  18. }
  19. }

匿名内部类

匿名类是指没有类名的内部类,必须在创建时使用 new 语句来声明类。

  1. new <类或接口>() {
  2. // ....
  3. }

这种形式的 new 语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。使用匿名类可使代码更加简洁、紧凑,模块化程度更高。

匿名类有两种实现方式:

  • 继承一个类,重写其方法。
  • 实现一个接口(可以是多个),实现其方法
  1. public class Out {
  2. void show() {
  3. System.out.println("调用 Out 类的 show() 方法");
  4. }
  5. }
  6. public class Test {
  7. public static void main(String[] args) {
  8. Out o = new Out() {
  9. void show() {
  10. System.out.println("调用匿名类中的 show() 方法");
  11. }
  12. }
  13. o.show(); // 调用匿名类中的 show() 方法
  14. }
  15. }

匿名类和局部内部类一样,可以访问外部类的所有成员。

  1. public static void main(String[] args) {
  2. int a = 10;
  3. final b = 10;
  4. Out o = new Out() {
  5. void show() {
  6. System.out.println(a); // 编译通过
  7. System.out.println(b); // 编译通过
  8. }
  9. }
  10. o.show();
  11. }

匿名类中允许使用非静态代码块进行成员初始化操作。

  1. Out o = new Out() {
  2. int i;
  3. {
  4. i = 10;
  5. }
  6. public void show() {
  7. System.out.println("i");
  8. }
  9. }

匿名类的非静态代码块会在父类的构造方法之后被执行。