Java学习笔记(十一)——接口、四种内部类

接口

基本介绍

接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。语法:

1
2
3
4
5
6
7
8
9
10
interface 接口名{
//属性
//抽象方法、默认实现方法或静态方法。
}

class 类名 implements 接口{
自己属性,
自己方法:
必须实现的接口的抽象方法
}

小结:

  • 接口是更加抽象的抽象的类,抽象类里的方法可以有方法体,接口里的所有方法都没有方法体 【idk7.0之前】。接口体现了程序设计的多态和高内聚低偶合的设计思想.
  • 特别说明:Jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现。
  • 在接口中,抽象方法可以省略abstact关键字。

接口的应用

对初学者讲,理解接口的概念不算太难,难的是不知道什么时候使用接口,考虑以下两个应用场景:

  • 现在要制造战斗机,武装直升机.专家只需把飞机需要的功能/规格定下来即可,然后让别的人具体实现就可。
  • 现在有一个项目经理(段玉),管理三个程序员,功能开发一个软件,为了控制和管理软件,项目经理可以定义
    一些接口,然后由程序员具体实现。(1.项目质量 2.项目进度 3.项目奖)参加工作

接口使用细节

  • 1、接口不能被实例化

  • 2、接口中所有的方法是 public方法,接口中抽象方法,可以不用abstract 修饰

  • 3、一个普通类实现接口,就必须将该接口的所有方法都实现

  • 4、抽象类实现接口,可以不用实现接口的方法。

  • 5、一个类同时可以实现多个接口 [举例]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface IA{
int n1 = 10;//等价 public static final int n1 = 10;
void say();
void hi();
}

interface IB{
void cry();
}
class cat implements IA,IB{
@Override
public void say() {

}

@Override
public void hi() {

}

@Override
public void cry() {

}
}
  • 6、接口中的属性,只能是final的,而且是 public static final 修饰符。比如:
1
int a=1;//实际上是 public static final int a=1;(必须初始化)
  • 7、接口中属性的访问形式:接口名.属性名
  • 8、接口不能继承其它的类,但是可以继承多个别的接口
1
2
3
interface A extends B,C{}
//接口和接口之间,我们谈继承
//接口和类之间,我们谈实现
  • 9、接口的修饰符之能是public和默认,这点和类的修饰符是一样的。

题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.learn.static_.Interface_;

public class interface1 {
public static void main(String[] args) {
B b = new B();
System.out.println(b.a);
System.out.println(A.a);
System.out.println(B.a);
}
}

interface A{
int a = 10;
}

class B implements A{
}

接口 VS 继承

当子类继承了父类,就自动的拥有父类的功能。如果子类需要扩展功能,可以通过实现接口的方式扩展。可以理解:实现接口是对 java 单继承机制的一种补充.

  • 1、接口和继承解決的问题不同
    • 继承的价值主要在于:解决代码的复用性和可维护性。
    • 接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法。即更加的灵活。
  • 2、接口比继承重加灵活:
    • 接口比继承更加灵活,继承是满足 is-a的关系,而接口只需满足 like -a的关系。
  • 3、接口在一定程度上实现代码解耦,即:接口规范性+动态绑定机制

接口的多态特性

  • 1、多态参数:接口引用可以指向实现了接口的类的对象
  • 2、多态数组:和类的多态数组一样
  • 3、接口存在多态传递现象
1
2
3
4
5
6
7
8
9
10
public class interface2 {
IG ig = new Test();
IH ih = new Test();
}

interface IH{}

interface IG extends IH{}

class Test implements IG{}

小结

前面所学到此为止,共学习了类的五大成员:

  • 1、属性
  • 2、方法
  • 3、构造器
  • 4、代码块
  • 5、最后一部分:内部类,就在此后展开。

内部类

基本介绍

一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员【思考:类的五大成员是哪些?[属性、方法、构造器、代码块、内部类]】,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系,注意:内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类。

基本语法

1
2
3
4
5
class Outer{//外部哪
class Inner{//内部类
}
}
class Other{}

内部类的分类

  • 定义在外部类局部位置上(比如方法内):
    • 1、局部内部类(有类名)
    • 2、匿名内部类(没有类名,重点)
  • 定义在外部类的成员位置上:
    • 1、成员内部类(没用static修饰)
    • 2、静态内部类(使用static修饰)

局部内部类

说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名

  • 1、可以直接访问外部类的所有成员,包含私有的
  • 2、不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final。
  • 3、作用域:仅仅在定义它的方法或代码块中。
  • 4、局部内部类—访问—->外部类的成员(访问方式:直接访问)
  • 5、外部类—访问—->局部内部类的成员(访问方式:创建对象,再访问(注意:必须在作用域内)
  • 6、外部其他类—不能访问—->局部内部类(因为局部内部类地位是一个局部变量)
  • 7、如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。

为什么要加一个this?outer.this本质上就是外部类的对象,即哪个对象调用了m1,那么outer.this就是哪个对象。

记住:

  • 1、局部内部类定义在方法中/代码块
  • 2、作用域在方法体或者代码块中
  • 3、本质仍然是一个类

匿名内部类

说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名。

  • (1)本质是类
  • (2)内部类
  • (3)该类没有名字 (表面上看没有,但系统底层会给他分配一个隐藏名字)
  • (4)同时还是一个对象

基本语法

1
2
3
new 类或接口(参数列表){
类体
};

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.learn.static_.Inner_;

public class AnonymousInnerClass {
}

class Outer04{
private int n1 = 10;
public void method(){
//基于接口的匿名内部类
//1.需求: 想使用 IA 接口,并创建对象
//2.传统方式,是写一个类,实现该接口,并创建对象
//3.需求是 Tiger/Dog 类只是使用一次,后面再不使用
//4. 可以使用匿名内部类来简化开发
//5. tiger 的编译类型 ? IA
//6. tiger 的运行类型 ? 就是匿名内部类 Outer04$1(外部类名+$1)
/*
我们看底层 会分配 类名 Outer04$1
class Outer04$1 implements IA {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
}
*/
IA tiger = new IA(){
@Override
public void cry() {
}
};
System.out.printf("tiger的运行类型:"+tiger.getClass());
tiger.cry();
tiger.cry();
}
}
interface IA{
public void cry();
}

匿名内部类注意事项

  • 1、匿名内部类的语法比较奇特,需要注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。
  • 2、可以直接访问外部类的所有成员,包含私有的[案例演示]
  • 3、不能添加访问修饰符,因为它的地位就是一个局部变量。 [过]
  • 4、作用域:仅仅在定义它的方法或代码块中。 [过]
  • 5、匿名内部类—访问—->外部类成员[访问方式:直接访问]
  • 6、外部其他类—不能访问—->匿名内部类(因为匿名内部类地位是一个局部变量)
  • 7、如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问

成员内部类

说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。

  • 1、可以直接访问外部类的所有成员,包括私有的
  • 2、可以添加任意的访问修饰符(public、protected、默认、private),因为他的地位本身就是一个成员
  • 3、作用域和外部类的其他成员- -样,为整个类体比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法.
  • 4、成员内部类—访—- >外部类成员(比如:属性) [访问方式:直接访问] (说明)
  • 5、外部类–访——>成员内部类(说明)访向方式:创建对象, 再访问
  • 6、外部其他类–访向—成员内部类
  • 7、如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员) 去访问

静态内部类

说明:静态内部类是定义在外部类的成员位置,并且有static修饰

  • 1、可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
  • 2、可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员。
  • 3、作用域:同其他的成员,为整个类体
  • 4、静态内部类—访问—->外部类(比如:静态属性) [访问方式:直接访问所有静态成员]
  • 5、外部类–访问—— >静态内部类访问方式:创建对象,再访问
  • 6、外部其他类—访问—->静态内部类
  • 7、如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员) 去访问