一、抽象类和接口的选择
📋 选择抽象类
📝 在紧密相关的类之间共享代码(有关联的类之间共享代码)
📝 需要除 public 之外的访问权限(接口中全是 public)
📝 需要定义实例变量,非 final 的 静态变量的时候(接口中默认都是public final
)
📋 选择接口
📝 不相关的类实现相同的方法的时候
📝 只是定义 行为(方法),不关心具体是誰实现了行为的时候
📝 想实现类型的多重继承的时候
二、接口中的默认方法
❓ 假如进行接口升级(如增加新的抽象方法),会导致大幅度的代码改动,之前实现该接口的所有类都得改动
📋 若想在不改动之前实现类的前提下进行接口升级,从 Java8 开始有2种方案:
(1) 默认方法
📝 被 default 修饰的方法
📝 默认方法只能是实例方法
📝 默认方法有具体的实现
📝 默认方法不强制子类(实现该接口的类)必须实现它.
❓ 当一个类实现的接口中存在默认方法的时候,该类可以:
📝 啥也不干,沿用接口的该方法的默认实现
📝 重写该默认方法的实现
📝 把自己修改为抽象类-,重新声明抽象方法(抽象方法的方法签名和返回值类型要与父接口的默认方法一致)
❓ 当一个接口继承的父接口中存在默认方法的时候,该类可以:
📝 啥也不干,沿用父接口的该方法的默认实现
📝 重新定义默认方法,覆盖父接口默认方法
📝 声明抽象方法(抽象方法的方法签名和返回值类型要与父接口的默认方法一致)
默认方法细节
① ✏️ 若父类定义的非抽象方法与接口的默认方法的方法签名一样,最终将调用父类的方法(与方法的返回类型无关)
public interface Eatable {
default void eat() {
System.out.println("Eatable_eat()");
}
}
public class Father {
public void eat() {
System.out.println("Father_eat()");
}
}
public class Child extends Father implements Eatable {
public static void main(String[] args) {
Child child = new Child();
// Father_eat()
child.eat();
}
}
② ✏️ 若父类定义的抽象方法与接口的默认方法的方法签名一样,强制要求子类必须实现此抽象方法
public interface Eatable {
default void eat() {
System.out.println("Eatable_eat()");
}
}
public abstract class Father {
public abstract void eat();
}
public class Child extends Father implements Eatable {
public static void main(String[] args) {
Child child = new Child();
/*
Eatable_eat()
Child_eat()
*/
child.eat();
}
@Override
public void eat() {
// 调用父接口的 eat() 方法
Eatable.super.eat();
System.out.println("Child_eat()");
}
}
③ ✏️ 若(父)接口定义的默认方法与其他(父)接口定义的默认方法的方法签名相同的时候,要求子类型(接口或类)也必须实现该默认方法。
📝 实现( implements)的多个接口中的默认方法有相同的方法签名的的时候,该方法的返回类型也必须一样。
public interface Eatable {
default String test(String text) {
System.out.println("Eatable_test(String text)");
return text;
}
}
public interface Testable {
default String test(String txt) {
System.out.println("Testable_test()");
return txt;
}
}
接口:
public interface Demoable extends Eatable, Testable {
@Override
default String test(String text) {
Eatable.super.test("Eatable_test");
Testable.super.test("Testable_test");
System.out.println("Demoable_test(String text)");
return text;
}
}
类:
public class TestDemo implements Eatable, Testable {
@Override
public String test(String text) {
return null;
}
}
(2) 静态方法
📝 接口中定义的静态方法只能通过接口名调用
📝 接口中的静态方法不会被继承
public interface Runnable {
static void move() {
System.out.println("Runnable_move()");
}
}
public interface Eatable extends Runnable {
static void move() {
System.out.println("Eatable_move()");
}
}
public interface Walkable extends Eatable {
static void move() {
System.out.println("Walkable_move()");
}
}
public class TestDemo implements Runnable, Eatable, Walkable {
public static void main(String[] args) {
Runnable.move();
Eatable.move();
Walkable.move();
/*
output:
Runnable_move()
Eatable_move()
Walkable_move()
*/
}
}
三、匿名类(实用)
📋 当接口或抽象类的实现类在整个项目中只使用过一次的时候,可以考虑使用匿名类(Anonymous Class)
接口:
/**
* 一个测试接口
*/
public interface Testable {
String test(String txt);
}
public class TestDemo1 {
public static void main(String[] args) {
/* 匿名类实现 Testable 接口里面的方法 */
Testable testable = new Testable() {
@Override
public String test(String txt) {
System.out.println("匿名类:test(String txt)");
return txt + "_Happy";
}
};
/*
匿名类:test(String txt)
庆医_Happy
*/
System.out.println(testable.test("庆医"));
}
}
抽象类:
/**
* 一个测试抽象类
*/
public abstract class TestAbstract {
public abstract void testTest();
}
public class TestDemo2 {
public static void main(String[] args) {
/* 通过匿名类实现 TestAbstract 里面的抽象方法*/
TestAbstract testAbstract = new TestAbstract() {
public void testTest() {
System.out.println("TestAbstract_testTest()");
}
};
// TestAbstract_testTest()
testAbstract.testTest();
/* 创建完匿名类对象后就调用里面的方法 */
new TestAbstract() {
public void testTest() {
System.out.println("创建完匿名类对象后就调用里面的方法");
}
}.testTest();
}
}
(1) 匿名类细节
📋 匿名类内部不能定义除编译时常量之外的任何static
成员
📖
static
成员通常通过类名访问,匿名类连名字都没有咋访问啊!
📋 匿名类和局部类一样,都只能访问 final 或有效 final 的局部变量
📋 匿名类可以直接访问外部类中的所有成员(即使被声明为 private)
📋 匿名类只有在实例相关的代码块中使用,才可直接访问外部类中的实例成员
📋 匿名类不能自定义构造方法
📋 匿名类可以有初始化块
(2) 匿名类的应用(☆)
① 代码传递
🍀 写一个工具类 codeTimes
🍀 作用:可计算一段代码的执行时间
🍀 可使用接口实现代码传递
工具类:
/**
* 计算某段代码的执行时间
*/
public class CodeTimes {
/**
* 代码块(以 Inter 结尾表示接口)
*/
public interface BlockInter {
void passCode(); // 传递代码
}
public static void check(String description, BlockInter codeBlock) {
if(codeBlock == null) return;
System.out.println("\n----------------------------");
System.out.println("测试功能:" + description);
long beginTime = System.currentTimeMillis();
codeBlock.passCode();
long endTime = System.currentTimeMillis();
long duration = (endTime - beginTime) / 1000;
System.out.println("代码执行花费时间:" + duration + "秒");
System.out.println("----------------------------");
}
}
测试类:
public class TestDemo {
public static void main(String[] args) {
CodeTimes.check("测试 1", new CodeTimes.BlockInter() {
@Override
public void passCode() {
String string = "Hello Friend";
for (int i = 0; i < 30000; i++) {
string += i;
}
}
});
CodeTimes.check("测试2", new CodeTimes.BlockInter() {
@Override
public void passCode() {
test();
}
});
}
/**
* 待测试的方法
*/
public static void test() {
StringBuilder string = new StringBuilder("Hello Friend");
for (int i = 0; i < 30000; i++) {
string.append(i);
}
}
}
② 过滤器
public class Files {
public interface Filter {
boolean accept(String filename);
}
public static String[] getAllFilenames(String dir, Filter filter) {
if(filter == null || dir == null || "".equals(dir)) return new String[];
// 1.获取 dir 文件夹下的全部文件名
String[] filenames = {};
// 2.进行过滤
for (String filename : filenames) {
if (filter.accept(filename)) {
// 文件名符合要求, 保存起来
}
}
// 3.返回所有符合过滤条件的文件名
return null;
}
}
public class TestDemo {
public static void main(String[] args) {
Files.getAllFilenames("D://gq", new Files.Filter() {
@Override
public boolean accept(String filename) {
return filename.contains("爱");
}
});
}
}
③ 回调
public class Life {
public interface Callback {
void person(String money);
void animal();
}
public static void check(String name, Callback callback) {
if(callback == null) return;
boolean result = isGoodPerson(name);
if (result) {
String money = "1000万";
callback.person("\n" + name + "_获得_" + money);
} else {
callback.animal();
}
}
private static boolean isGoodPerson(String name) {
return !name.equals("坏人");
}
}
public class TestDemo {
public static void main(String[] args) {
Life.check("庆医", new Life.Callback() {
@Override
public void person(String money) {
System.out.println(money);
}
@Override
public void animal() {
System.out.println("\n坏人_当动物");
}
});
}
}
🍀 匿名类的作用本质上是 代码传递,第一次学习的时候对它的理解非常懵,只觉得它非常好用。假如你是初学者,我相信你也会很懵。慢慢来,后面有使用场景的时候你就一目了然了。
如有错误,请不吝赐教