ZB-014-java接口和抽象类

抽象类

  • 不可实例化
    • 反证法:假如我们允许实例化抽象类的实例,那么该实例调用抽象方法是怎样的?没有方法体?
  • 可以实例化的东西一定要补全所有的方法体
  • 可以包含抽象方法
  • 可以包含成员变量
1
2
3
4
5
6
7
8
9
public abstract class Animal{
public abstract void 跑();
}

public class Bird extends Animal{
public void 跑(){
// 跑的实现逻辑
}
}

接口

  • 接口部分的实现了多继承
  • 接口不是类
  • 接口的扩展
  • 接口只代表一种功能
  • 一个类只能继承一个类,但是却能实现若干的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
interface Aaa {
// 接口里的成员默认是 public static final

/*
int a = 1;
等价于
public static final int a = 1;
而我们知道 final的应该大写
*/

// 所以正确的写法应该是
int A = 1;
}

接口可以包含什么

  • 若干个方法(默认 public)
  • 若干个常量(默认 public static final)
  • extends 接口
  • 默认方法
    • Since Java8
    • 一种妥协的产物
    • 可以用来实现 minxin
    • 菱形继承

在经典的接口里,java8之前,接口里的方法不能有方法体的 java8之后可以了

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
在 C++ 中 允许多继承
class A{
f(){ ....}
}
class B{
f(){ ....}
}

class C extends A,B{
//
}
// 菱形继承
new C().f(); // 调用的是谁?此时产生了歧义


// 而 java 是单根继承(不够灵活),于是提出了 “接口”允许多实现。
// 把接口的功能理解为一种能力
// 那如何区分 类继承体系和接口呢?
// 假设你是个老板Boss 你想找几个人干活,对于class来说就是找几个“人”来干活
// 对于interface来说就是 找几个“能干活”的人来
interface A{
f();
}
interface B{
f();
}
class C implements A,B{
f(){
....
}
}

// 而在java8 之后,这种现象被改变了。妥协了
// 当一个接口发布出去 就不能在改了,不能增添或减少成员,因为一旦成型,再次添加方法后,导致所有实现类都报错。必须实现接口的所有方法。

// 由于这种向后兼容性。所以这种发布之后,再也不修改的情况是个天真的想法——人的认知是有限的
// 所以在 java 工作体系运行20年之后,突然发现某些接口 如 List 想给当年的List添加一个 sort(); 但是很不幸 你不能打破“向后兼容性”, 这样导致 List的实现类全报错了,必须实现 sort方法才行,问题升级,你老板非要实现,但是你要改 N 多地方。

// 于是产生了一个妥协的产物——默认方法
这个 sort 就在 List 中
// 因为无法变成抽象方法,所以必须写实现,所以 sort 有一个默认的实现使得之前没有实现 sort方法的实现类也能正常工作

default void sort(Comparator<? super E> c){
Object[] a = this.toArray();
Array.sort(a,(Comparator) c);
ListIterator<E> i = this.listIterator();
for(Object e : a){
i.next();
i.set((E) e);
}
}

默认方法

  • java8 之前

    1
    2
    3
    4
    interface A{
    void a();
    void b();
    }
  • java8 之后

    1
    2
    3
    4
    5
    6
    7
    8
    interface A{
    void a();
    void b();
    default void c(){
    // 实现
    }
    }
    // 这样原来实现 A 接口的实现类就可以得到兼容

java8 接口默认方法引出的问题

  • 当年极力避免的 C++ 的多继承,导致父类有相同f();的问题出现了
  • 当年极力避免的 “二义性” 现在又妥协了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    interface A{
    void a();
    default void c(){
    // 实现
    }
    }

    interface B{
    void a();
    default void c(){
    // 实现
    }
    }

    class C implatements A,B{
    {
    c(); // 此时报错了
    }
    }

接口和抽象类总结

共同点

  • 抽象的,不可实例化
  • 可包含抽象方法(没有方法体,非 static/private/final )

不同点

  • 抽象类是类可以包含类的一切,接口只能包含受限的成员(public static final)和方法(public abstract,java8之后可以 default的)
  • 抽象类只能单一继承,接口可以多实现

instanceof 不仅能检查是不是一个类的实例还可以检测是不是一个接口实现类的实例

什么是 API(application program interface)

什么是UI(User interface)

多态实战

Files.walkFileTree 当你用一个不知道意思的方法的时候,你最好看一下JDK文档 JDK的文档是最好的教材

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Walks a file tree.
*
* <p> This method walks a file tree rooted at a given starting file. The
* file tree traversal is <em>depth-first</em> with the given {@link
* FileVisitor} invoked for each file encountered. 看到这就够了

public static Path walkFileTree(Path start,
Set<FileVisitOption> options,
int maxDepth,
FileVisitor<? super Path> visitor)
throws IOException
{ ... }
- 沿着给定的一个 目录
- 深度优先

FileVisitor 是什么?就是一个接口,如果你实现它,就可以在访问文件的过程中进行自定义控制

FileVisitor接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface FileVisitor<T> {

// 文件夹访问之前
FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
throws IOException;

// 访问文件夹做什么
FileVisitResult visitFile(T file, BasicFileAttributes attrs)
throws IOException;

// 访问失败做什么
FileVisitResult visitFileFailed(T file, IOException exc)
throws IOException;

// 访问文件夹之后
FileVisitResult postVisitDirectory(T dir, IOException exc)
throws IOException;
}

FileVisitResult 访问文件的4个结果

1
2
3
4
5
6
public enum FileVisitResult {
CONTINUE,
TERMINATE,// 终止
SKIP_SUBTREE, // 忽略子树
SKIP_SIBLINGS; // 忽略所有兄弟
}

MyFileVisitor

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
package com.github.hcsp.polymorphism;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;

public class FileFilter {
public static void main(String[] args) throws IOException {
Path projectDir = Paths.get(System.getProperty("user.dir"));
Path testRootDir = projectDir.resolve("test-root");
if (!testRootDir.toFile().isDirectory()) {
throw new IllegalStateException(testRootDir.toAbsolutePath().toString() + "不存在!");
}

List<String> filteredFileNames = filter(testRootDir, ".csv");
System.out.println(filteredFileNames);
}

/**
* 实现一个按照扩展名过滤文件的功能
*
* @param rootDirectory 要过滤的文件夹
* @param extension 要过滤的文件扩展名,例如 .txt
* @return 所有该文件夹(及其后代子文件夹中)匹配指定扩展名的文件的名字
*/
public static List<String> filter(Path rootDirectory, String extension) throws IOException {
Files.walkFileTree(rootDirectory,new MyFileVisitor());
return null;
}
}

// 分割线,另一个 java文件里
// MyFileVisitor.java
public class MyFileVisitor implements FileVisitor {
@Override
public FileVisitResult preVisitDirectory(Object dir, BasicFileAttributes attrs) throws IOException {
System.out.println(dir);
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFile(Object file, BasicFileAttributes attrs) throws IOException {
System.out.println(file);
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFileFailed(Object file, IOException exc) throws IOException {
System.out.println(file);
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult postVisitDirectory(Object dir, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
}

除此之外,我们可以找到 FileVisitor 的实现类(骨架类),因为我们不需要实现所有的接口

  • 找到了
1
2
3
4
5
6
7
public class FileFilterVisitor extends SimpleFileVisitor<Path>{
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file);
return FileVisitResult.CONTINUE;
}
}

需求添加,我们需要知道过滤文件的扩展名,和返回过滤后的集合

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
package com.github.hcsp.polymorphism;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;

public class FileFilter {
public static void main(String[] args) throws IOException {
Path projectDir = Paths.get(System.getProperty("user.dir"));
Path testRootDir = projectDir.resolve("test-root");
if (!testRootDir.toFile().isDirectory()) {
throw new IllegalStateException(testRootDir.toAbsolutePath().toString() + "不存在!");
}

List<String> filteredFileNames = filter(testRootDir, ".csv");
System.out.println(filteredFileNames);
}

public static List<String> filter(Path rootDirectory, String extension) throws IOException {
FileFilterVisitor visitor = new FileFilterVisitor(extension);
Files.walkFileTree(rootDirectory,visitor);
return visitor.getFilterNames();
}
}

// 分割线,另一个 java文件里
// FileFilterVisitor.java
// 使用骨架实现
public class FileFilterVisitor extends SimpleFileVisitor<Path>{
private String extension;
private List<String> filterNames = new ArrayList<>();

public FileFilterVisitor(String extension) {
this.extension = extension;
}

public List<String> getFilterNames() {
return filterNames;
}

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file);
if(file.getFileName().toString().endsWith(extension)){
filterNames.add(file.getFileName().toString());
}
return FileVisitResult.CONTINUE;
}
}

再次升级需求:问题如下

  • 你的 FileFilterVisitor 不在一个文件里,看代码要分屏看两个文件,而且还需要定制构造器 传递extension参数

于是你把 FileFilterVisitor.java 移到 FileFilter.java

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
package com.github.hcsp.polymorphism;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;

public class FileFilter {
public static void main(String[] args) throws IOException {
Path projectDir = Paths.get(System.getProperty("user.dir"));
Path testRootDir = projectDir.resolve("test-root");
if (!testRootDir.toFile().isDirectory()) {
throw new IllegalStateException(testRootDir.toAbsolutePath().toString() + "不存在!");
}

List<String> filteredFileNames = filter(testRootDir, ".csv");
System.out.println(filteredFileNames);
}

public static List<String> filter(Path rootDirectory, String extension) throws IOException {
FileFilterVisitor visitor = new FileFilterVisitor(extension);
Files.walkFileTree(rootDirectory,visitor);
return visitor.getFilterNames();
}
}

class FileFilterVisitor extends SimpleFileVisitor<Path>{
private String extension;
private List<String> filterNames = new ArrayList<>();

public FileFilterVisitor(String extension) {
this.extension = extension;
}

public List<String> getFilterNames() {
return filterNames;
}

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file);
if(file.getFileName().toString().endsWith(extension)){
filterNames.add(file.getFileName().toString());
}
return FileVisitResult.CONTINUE;
}
}

即使移到了一个java文件里,但是你还是觉得麻烦

内部类(一个类包含在另一类中)

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
public class FileFilter {
public static void main(String[] args) throws IOException {
Path projectDir = Paths.get(System.getProperty("user.dir"));
Path testRootDir = projectDir.resolve("test-root");
if (!testRootDir.toFile().isDirectory()) {
throw new IllegalStateException(testRootDir.toAbsolutePath().toString() + "不存在!");
}

List<String> filteredFileNames = filter(testRootDir, ".csv");
System.out.println(filteredFileNames);
}

public static List<String> filter(Path rootDirectory, String extension) throws IOException {
FileFilterVisitor visitor = new FileFilterVisitor(extension);
Files.walkFileTree(rootDirectory,visitor);
return visitor.getFilterNames();
}

// 内部类
static class FileFilterVisitor extends SimpleFileVisitor<Path>{
private String extension;
private List<String> filterNames = new ArrayList<>();

public FileFilterVisitor(String extension) {
this.extension = extension;
}

public List<String> getFilterNames() {
return filterNames;
}

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file);
if(file.getFileName().toString().endsWith(extension)){
filterNames.add(file.getFileName().toString());
}
return FileVisitResult.CONTINUE;
}
}
}

还是觉得麻烦,因为要传递extension

匿名内部类

好处

  • 相近的两块逻辑组合到一起,避免两个逻辑在两个文件中,所带来的注意力不集中问题
  • 参数 extension 不需要构造器来传递了。可以直接在匿名内部类中访问
    • 匿名内部类可以毫无障碍的访问 外围的变量
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

public class FileFilter {
public static void main(String[] args) throws IOException {
Path projectDir = Paths.get(System.getProperty("user.dir"));
Path testRootDir = projectDir.resolve("test-root");
if (!testRootDir.toFile().isDirectory()) {
throw new IllegalStateException(testRootDir.toAbsolutePath().toString() + "不存在!");
}

List<String> filteredFileNames = filter(testRootDir, ".csv");
System.out.println(filteredFileNames);
}

// 匿名内部类
public static List<String> filter(Path rootDirectory, String extension) throws IOException {
List<String> names = new ArrayList<>();
Files.walkFileTree(rootDirectory,new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file);
if(file.getFileName().toString().endsWith(extension)){
names.add(file.getFileName().toString());
}
return FileVisitResult.CONTINUE;
}
});
return names;
}
}

以上就是多态的应用

  • 通过修改一小块的功能去完成一个更加灵活性的功能
  • 通过去覆盖(重写)一个方法来实现更加灵活的功能

  • 代码参考

  • 我的pr