5. 内部类

接口变化

JDK 1.8 之后,接口的语法有了新的变化。增加了两类成员。

  1. 静态方法 public static。static 不可以省略,通过 “接口名.方法名” 来进行调用。
  2. 默认方法 public default。default 不可以省略,通过 “实现类对象.方法名” 来进行调用。
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
package com.atguigu.code;

public class NewInterface {
public static void main(String[] args) {
MyInter.print(); // 直接调用接口的静态方法

MyImpl myImpl = new MyImpl();
myImpl.test(); // 调用接口实现的抽象方法
myImpl.method(); // 调用接口的默认方法
}
}

interface MyInter{
String INFO = "info"; // 1.8 之前就有的属性,全局的静态的常量
void test(); // 1.8 之前就有的属性,公共的抽象的方法

public static void print() {
System.out.println("1.8 接口新增静态方法");
}

public default void method() {
System.out.println("1.8 接口新增默认方法");
}
}

class MyImpl implements MyInter{

@Override
public void test() {
System.out.println("实现接口的抽象方法");
}

}

接口中默认方法冲突

  1. 在实现多个接口时候,接口中都有签名相同的方法。
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
package com.atguigu.code;


public class InterfaceSameMethod {
public static void main(String[] args) {
C c = new C();
c.method();
}
}

interface A{
public default void method() {
System.out.println("A 默认方法");
}
}

interface B{
public default void method() {
System.out.println("B 默认方法");
}
}

class C implements A, B{
@Override
public void method() {
A.super.method(); // 可以使用 A.super.method() 调用 A 中的 method
B.super.method(); // 可以使用 B.super.method() 调用 B 中的 method
}
}
  1. 在继承和接口的实现中,父类和接口中都有签名相同的方法。
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
38
39
40
41
42
43
44
package com.atguigu.code;

import org.omg.CORBA.PUBLIC_MEMBER;

public class InterfaceSameMethod {
public static void main(String[] args) {
D d = new D();
d.method(); // 默认调用的是父类中的,也就是说当父类和接口的有签名相同的方法时,先使用父类中的

E e = new E();
e.method(); // 指定调用

F f = new F();
f.method(); // 自己重新实现
}
}

interface A{
public default void method() {
System.out.println("A 默认方法");
}
}

class C{
public void method() {
System.out.println("C 中的 method");
}
}

class D extends C implements A{

}

class E extends C implements A{
public void method () {
A.super.method(); // 指定调用接口中的
}
}

class F extends C implements A{
public void method () {
System.out.println("自己实现"); // 自己重新实现
}
}

内部类

含义:当一个类的内部,仍然有一个完整的结构。这个完整的结构仍然需要一个类进行描述,因为有自己的特征(属性,方法),并且这个内部类是为外部类服务的。

分类

  1. 成员内部类:和成员变量一样,在类中,方法外。成员内部类又可以分为两种,分别是静态成员内部类(简称静态内部类)和非静态成员内部类(简称成员内部类
  2. 局部内部类:在类中,方法内。局部内部类又可以分为两种,分别是由名字的局部内部类(简称为局部内部类)和没有名字的局部内部类(简称为匿名内部类

常见程度:静态内部类 = 成员内部类 > 匿名内部类 > 局部内部类

静态内部类

静态内部类中,类的5大成员它都可以拥有。

  1. 属性:静态和非静态属性
  2. 方法:静态方法和非静态方法。在抽象的静态内部类中,还可以有抽象方法
  3. 代码块:静态的和非静态的
  4. 构造器:有参的和无参的
  5. 内部类:语法上可以,但是太复杂了,一般不这么写

静态内部类的修饰符

  1. 权限修饰符:4个都可以有
  2. static必须有
  3. abstract 可以有
  4. final 可以有

使用

  1. 在静态内部类中不允许使用外部类的非静态的成员(static 中不能使用非 static)

  2. 在外部类中,使用静态内部类和使用其他类一样

  3. 在外部类的外面使用静态内部类,不需要外部内的对象。例如下面的 Outer.Inner

  4. 在外部类的外面要调用静态内部类的非静态方法时,则需要静态内部类的对象。例如:

    Outer.Inner obj = new Outer.Inner();
    obj.test();

  5. 在外部类的外面要调用静态内部类的静态方法时,则不需要静态内部类的对象。例如:Outer.Inner.test2();

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
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.atguigu.code;

import com.atguigu.code.Outer.Inner;

public class TestStaticInner {
public static void main(String[] args) {
// 调用 Inner 的 test 方法
Outer.Inner obj = new Outer.Inner();
obj.test();
// 调用 Inner 的 test2 方法
Outer.Inner.test2();
}
}


class Outer{
private static int i;
private int j;

/**
* 静态内部类
* @author rex
*
*/
static class Inner{
/**
*静态内部类中的非静态方法
*/
public void test() {
System.out.println(i); // 正确,静态内部类中可以直接使用外部类的静态私有成员。
// System.out.println(j); 错误的,static 中不能使用非 static
}

/**
* 静态内部类中的静态方法
*/
public static void test2() {
System.out.println(i);
}
}

/**
* 在外部类中使用静态内部类和使用其他类一样
* @return
*/
public Inner GetInner() {
Inner inner = new Inner();
return inner;
}
}

静态内部类也有自己的字节码文件,文件格式为 外部类$静态内部类.class

非静态内部类

非静态内部类中,类的5大成员它都可以拥有。但是都是非静态的

  1. 属性:非静态属性
  2. 方法:非静态方法。在抽象的静态内部类中,还可以有抽象方法
  3. 代码块:非静态的
  4. 构造器:有参的和无参的
  5. 内部类:语法上可以,但是太复杂了,一般不这么写

非静态内部类的修饰符

  1. 权限修饰符:4个都可以有
  2. static 没有
  3. abstract 可以有
  4. final 可以有

使用

  1. 在非静态内部类中可以使用外部类的所有成员
  2. 在外部类中,使用非静态内部类有限制。在外部类的静态方法中不允许使用非静态内部类。(static 中不能使用非 static)
  3. 在外部类外面要调用非静态内部类的非静态方法,需要外部类的对象和内部类的对象。例如:
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
38
39
40
41
42
43
44
package com.atguigu.code;


public class TestStaticInner2 {
public static void main(String[] args) {
// 调用 Inner 中的 test 方法
Outer2 outer2 = new Outer2(); // 需要外部类 Outer2 对象
Outer2.Inner inner = outer2.new Inner(); // 需要 Inner 对象
inner.test(); // 调用 test 方法

// 若上面的outer2.new Inner() 写法太奇怪,还可以借助 "外部类中调用非静态内部类,并返回对象" 方法
Outer2.Inner inner2 = outer2.getInner();
inner2.test();
}
}


class Outer2{
private static int i;
private int j;

/**
* 非静态内部类
* @author rex
*
*/
class Inner{
/**
* 非静态内部类中的非静态方法
*/
public void test() {
System.out.println(i);
System.out.println(j);
}
}

/**
* 外部类中调用非静态内部类,并返回对象
* @return
*/
public Inner getInner() {
return new Inner();
}
}

非静态内部类也有自己的字节码文件,文件格式为 外部类$非静态内部类.class

局部内部类

局部内部类的修饰符

  1. 权限修饰符:没有
  2. static 没有
  3. abstract 可以有
  4. final 可以有

使用

  1. 局部内部类的作用域只在类所在的方法中,非常狭窄,这也是局部内部类用得比较少的原因。
  2. 局部内部类中是否能使用外部类的非静态成员变量,要看所在方法是否是静态的。(要遵循 static 中不能使用非 static 的原则)
  3. 局部内部类中可以使用所在方法的局部变量,但是该局部变量必须加 final 声明 (JDK 1.8 之前必须手动加上 final,1.8 开始就自动为我们加上了)
  4. 局部内部类中不能有静态成员(能有静态成员的内部类就只有静态内部类)
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.atguigu.code2;

public class TestLocalInner {

}

class Outer{
public static int i = 10;
public int j = 10;

public void outTest() {
int a = 10; // 局部变量,1.8 开始前面默认就加上类 final

class Inner{
public void test() {
System.out.println(i);
System.out.println(j);
System.out.println(a);
}
}

Inner inner = new Inner();
inner.test();

} // Inner 的作用域只到这儿

public static void outMethod() {

class Inner{
public void test() {
System.out.println(i);
// System.out.println(j); 这里就不能使用j,要遵循 static 中不能使用非 static 的原则
}
}

}
}

局部内部类也有自己的字节码文件,文件格式为 外部类$数字+局部内部类.class。有数字的原因是很可能有多个名称相同的局部内部内,例如上面例子中就有两个局部内部类 Inner

匿名内部类

匿名内部类的声明位置在创建对象的位置,特点是一边声明一边创建对象,且匿名内部类只有唯一的一个对象。匿名内部类没有任何修饰符,也没有名字。匿名内部类有构造器(默认的构造器),但是不能复写(因为没有名字)

使用匿名内部类是一种特殊的局部内部类,所以凡事局部内部类的限制,匿名内部类都有。如下所示

  1. 局部内部类的作用域只在类所在的方法中,非常狭窄,这也是局部内部类用得比较少的原因。
  2. 局部内部类中是否能使用外部类的非静态成员变量,要看所在方法是否是静态的。(要遵循 static 中不能使用非 static 的原则)
  3. 局部内部类中可以使用所在方法的局部变量,但是该局部变量必须加 final 声明 (JDK 1.8 之前必须手动加上 final,1.8 开始就自动为我们加上了)
  4. 局部内部类中不能有静态成员(能有静态成员的内部类就只有静态内部类)
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package com.atguigu.code3;

public class TestNoNameInner {
public static void main(String[] args) {
// 调用父类无参构造,myClass 是 并不是 MyClass 的对象,而是 MyClass 的子类对象
MyClass myClass = new MyClass() {
@Override
public void test() {
System.out.println("匿名内部类重写 MyClass test 方法");
}
};
myClass.test();

// 调用父类有参构造, myClass2 是 并不是 MyClass 的对象,而是 MyClass 的子类对象
MyClass myClass2 = new MyClass("父类有参构造") {
@Override
public void test() {
System.out.println("匿名内部类重写 MyClass test 方法");
}
};
myClass2.test();

// myInner 并不是 MyInner 的对象,而是 MyInner 的子类对象
MyInner myInner = new MyInner() {
@Override
public void test() {
System.out.println("重写接口的抽象方法 test");
}
};
myInner.test();
}
}

/**
* 抽象类
* @author rex
*
*/
abstract class MyClass{
private String info;

// 抽象方法
public abstract void test();

// 有参,无参构造
public MyClass(String info) {
super();
this.info = info;
}

public MyClass() {
super();
}

// get set 方法
public String getInfo() {
return info;
}

public void setInfo(String info) {
this.info = info;
}
}

/**
* 接口
* @author rex
*
*/
interface MyInner {
void test();
}

匿名内部类也有自己的字节码文件,文件格式为 外部类$数字.class。因为没有名字,所以就是数字表示

Comparator

之前学习了 java.lang 包下的 Comparator。现在 java.utils 下还有一个 Comparator 接口。里面有一个 compare 方法,接收两个对象,如果第一个对象大于第二个,返回一个正整数,相等返回0,否则返回负整数。那么为什么会多有一个 Comparator 接口呢,因为如果要对一个对象的多个特性进行比较排序的化,java.utils.Comparator 结合 Arrays.sort 使用更加方便。

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package com.atguigu.code3;

import java.util.Arrays;
import java.util.Comparator;

public class TestComprator {
public static void main(String[] args) {
Student[] arr = new Student[3];
arr[0] = new Student(1, "张三", 88);
arr[1] = new Student(3, "李四", 99);
arr[2] = new Student(2, "王五", 77);

// 方法1: 之前学习的 Arrays.sort 中的排序方式,传入数组,数组元素实现 Comparable 接口中的 compareTo 方法
Arrays.sort(arr);

for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}

System.out.println("-------------------------");

// 方法2: Arrays.sort 支持传入一个待排序的数组,第二个参数为 Comparator 对象。这里的第二个参数是一个匿名内部类
Arrays.sort(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Student student1 = (Student) o1;
Student student2 = (Student) o2;
return student1.getScore() - student2.getScore();
}
});

for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}

// 上面的匿名内部类的写法是对下面这种写法的简写。
// class StudentScoreCompare implements Comparator{
// @Override
// public int compare(Object o1, Object o2) {
// Student student1 = (Student) o1;
// Student student2 = (Student) o2;
// return student1.getScore() - student2.getScore();
// }}
//
// StudentScoreCompare studentScoreCompare = new StudentScoreCompare();
// Arrays.sort(arr, studentScoreCompare);

}
}


class Student implements Comparable{
private int id;
private String name;
private int score;

@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", score=" + score + "]";
}

public Student(int id, String name, int score) {
super();
this.id = id;
this.name = name;
this.score = score;
}

public Student() {
super();
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getScore() {
return score;
}

public void setScore(int score) {
this.score = score;
}

/**
* 借助 java.lang.Comparable 按照 id 进行自然排序
*/
@Override
public int compareTo(Object o) {
Student student = (Student) o;
return this.id - student.id;
}

}