public class Person {
private int age; private static int count = 1; private static void run() { System.out.println("Person - run"); } public static class Car { // 静态嵌套类 public void test() { Person person = new Person(); // 静态嵌套类可以直接访问外部类中的成员(包括 private) System.out.println(person.age); // 0 Person.count = 1; Person.run(); // Person - run System.out.println(count); // 1 run(); // Person - run } }
}
public static void main(String[] args) { Person p = new Person(); // 静态嵌套类的使用 Person.Car c = new Person.Car(); // 如果之前 import Person.Car; 可以直接使用; // Car c = new Car(); c.test(); } ``` [](https://gitee.com/vip204888/java-p7)什么情况使用嵌套类? ----------------------------------------------------------------------------- 如果类 A 只用在类 C 内部,可以考虑将类 A 嵌套到类 C 中; * 封装性更好 * 程序包更加简化 * 增强可读性、维护性 如果类 A 需要经常访问类 C 的**非公共成员**,可以考虑将类 A嵌套到类 C 中; * 另外也可以根据需要将类 A 隐藏起来,不对外暴露 如果需要**经常访问非公共的实例成员**,设计成内部类(非静态嵌套类),否则设计成静态嵌套类; * 如果必须先有 C 实例,才能创建 A 实例,那么可以将 A 设计为 C 的内部类 [](https://gitee.com/vip204888/java-p7)局部类(Local Class) =================================================================================== **局部类**:定义在**代码块**中的类(可以定义在方法中、for 循环中、if 语句中等) 局部类不能定义除**编译时常量**以外的任何 `static` 成员; 局部类只能访问 `final` 或者 **有效`final`** 的局部变量; * 从 Java 8 开始,如果**局部变量没有被第二次赋值**,就认定为是**有效`final`** 局部类可以直接访问外部类中的所有成员(即使被声明为 `private`) * 局部类只有定义在实例相关的代码块中,才能直接访问外部类中的实例成员(实例变量、实例方法) 局部类示例: ``` public class TestLocalClass { private int a = 1; private static int b = 2; private static void test1() {} private void test2() {} public void test3() { int c = 2; class LocalClass { static final int d = 4; void test4() { System.out.println(a + b + c + d); test1(); test2(); } } new LocalClass().test4(); } } ``` [](https://gitee.com/vip204888/java-p7)抽象类(Abstract Class)与接口(Interface) ==================================================================================================== [](https://gitee.com/vip204888/java-p7)抽象类 ---------------------------------------------------------------------- **抽象方法**:被 `abstract` 修饰的实例方法 * 只有方法声明,没有方法实现(参数列表后面没有大括号,而是分号) * 不能是 `private` 权限(因为定义抽象方法的目的让子类去实现) * 只能定义在抽象类、接口中 **抽象类**:被 `abstract` 修饰的类 * 可以定义抽象方法 * 不能实例化,但可以自定义构造方法 * 子类必须实现抽象父类中的所有抽象方法(除非子类也是一个抽象类) * 可以像非抽象类一样定义成员变量、常量、嵌套类型、初始化块、非抽象方法等 也就说,**抽象类也可以完全不定义抽象方法** 常见使用场景: * 抽取子类的公共实现到抽象父类中,要求子类必须要单独实现的定义成抽象方法 实例: ``` public abstract class Shape { protected double area; protected double girth; public double getArea() { return area; } public double getGirth() { return girth; } public void show() { calculate(); System.out.println(area + "_" + girth); } protected abstract void calculate(); }
public class Rectangle extends Shape {
private double width; private double height; public Rectangle(double width, double height) { super(); this.width = width; this.height = height; } @Override protected void calculate() { area = width * height; girth = (width + height) * 2; }
}
public class Circle extends Shape { private double radius; public Circle(double radius) { this.radius = radius; } @Override protected void calculate() { double half = Math.PI * radius; area = half * radius; girth = half * 2; } }
public static void main(String[] args) {
Rectangle rectangle = new Rectangle(10, 20); rectangle.show(); Circle circle = new Circle(30); circle.show();
}
[](https://gitee.com/vip204888/java-p7)接口(Interface) -------------------------------------------------------------------------------- **API**(Application Programming Interface) * **应用编程接口**,提供给开发者调用的一组功能(无须提供源码) **Java 中的接口**: * 一系列方法声明的集合 * 用来定义规范、标准 **接口中可以定义的内容**: * **抽象方法(可以省略 abstract)** * **常量(可以省略 static、final)** * **嵌套类型** * 从 Java 8 开始可以定义:**默认方法(`default`)**、**静态方法** 上述可以定义的内容都是隐式 public 的,因此可以省略 public 关键字 * 从 Java 9 开始可以定义:**private 方法** * 不能自定义构造方法、不能定义(静态)初始化块、不能实例化 **接口的细节**: 一个类可以通过 `implements` 关键字实现一个或多个接口 * 实现接口的类必须实现接口中定义的所有抽象方法,除非它是个抽象类 * 如果一个类实现的多个接口中有相同的抽象方法,只需要实现此方法一次 * `extends` 和 i`mplements` 可以一起使用,`implements` 必须写在 `extends` 的后面 * 当父类、接口中的方法签名一样时,那么返回值类型也必须一样 一个接口可以通过 `extends` 关键字继承一个或者多个接口 * 当多个父接口中的方法签名一样时,那么返回值类型也必须一样 [](https://gitee.com/vip204888/java-p7)抽象与接口的对比(如何选择) --------------------------------------------------------------------------------- 抽象类和接口的用途还是有点类似,该如何选择? 何时选择**抽象类**? * 在**紧密相关的类**之间共享代码 * 需要除 public 之外的访问权限 * 需要定义实例变量、非 final 的静态变量 何时选择**接口**? * **不相关的类**实现相同的方法 * 只是**定义行为**,不关心具体是谁实现了行为 * 想实现类型的**多重继承** [](https://gitee.com/vip204888/java-p7)接口的升级问题(默认方法、静态方法) ===================================================================================== 如果接口需要升级,比如增加新的抽象方法:会导致大幅的代码改动,以前实现接口的类都得改动 若想在不改动以前实现类的前提下进行接口升级,从 Java 8 开始,有 2 种方案: * **默认方法**(Default Method) * **静态方法**(Static Method) [](https://gitee.com/vip204888/java-p7)默认方法(Default Method) --------------------------------------------------------------------------------------- * 用 `default` 修饰默认方法 * 默认方法只能是实例方法 **默认方法的使用**: 当一个类实现的接口中有默认方法时,这个类可以: * **啥也不干**,沿用接口的默认实现 * 重新定义默认方法,**覆盖**默认方法的实现 * 重新声明默认方法,将默认方法声明为抽象方法(此类必须是抽象类) 当一个接口继承的父接口中有默认方法时,这个接口可以: * **啥也不干**,沿用接口的默认实现 * 重新定义默认方法,**覆盖**默认方法的实现 * 重新声明默认方法,将默认方法声明为抽象方法 简单示例:`Eatable` 中有默认方法,`Dog`啥也不干,`Cat` 覆盖默认方法。
public interface Eatable {
// 默认方法 default void eat(String name) { System.out.println("Eatable - eat - " + name); }
}
// Eatable接口中新增了方法, 但是没有影响到Dog类 public class Dog implements Eatable {}
// Eatable接口中新增了方法, Cat类中可以覆盖
public class Cat implements Eatable {
@Override public void eat(String name) { Eatable.super.eat(name); System.out.println("Cat - eat - " + name); }
}
public static void main(String[] args) { Dog dog = new Dog(); dog.eat("bone"); // Eatable - eat - bone Cat cat = new Cat(); cat.eat("fish"); // Eatable - eat - fish // Cat - eat - fish } ``` 如果父类定义的非抽象方法与接口的默认方法相同时,最终将调用父类的方法(**就近原则**): ``` public class Animal { public void run() { System.out.println("Animal - run"); } }
public interface Runnable {
default void run() { System.out.println("Runnable - run"); }
}
public class Dog extends Animal implements Runnable {}
public static void main(String[] args) {
Dog dog = new Dog(); dog.run(); // 继承的父类、实现的接口中都有 run() 方法, 默认调用父类的 // Animal - run
}
如果父类定义的抽象方法与接口的默认方法相同时,要求子类实现此抽象方法 * 可以通过 `super` 关键字调用接口的默认方法
public interface Runnable {
default void run() { System.out.println("Runnable - run"); }
}
public abstract class Animal { public void run() {} }
public class Dog extends Animal implements Runnable {
@Override public void run() { // 父类的抽象方法run方法与接口中的run方法相同, 要求实现父类的抽象方法 Runnable.super.run(); // 可以通过super调用接口的默认方法 System.out.println("Dog - run"); // Runnable - run // Dog - run }
}
如果(父)接口定义的默认方法与其他(父)接口定义的方法相同时,要求子类型实现此默认方法: 例:`Runnable` 和 `Walkable` 两个父接口中定义的**默认方法**都是 `run()`,`Testable` 继承了两个父类,则要求实现默认方法 `run()` ,`Dog` 类同理。
public interface Runnable {
default void run() { System.out.println("Runnable - run"); }
}
public interface Walkable { default void run() { System.out.println("Walkable - run"); } }
// Testable 父接口继承了 Runnable 父接口和 Walkable 父接口
// 他们都有默认方法 run, 要求 Testable 接口实现该默认方法
public interface Testable extends Runnable, Walkable {
@Override default void run() { Runnable.super.run(); Walkable.super.run(); System.out.println("Testable - run"); }
}
// Dog 类实现了 Runnable、Walkable 两个接口 // 他们都有默认方法 run, 要求 Dog 类实现该默认方法 public class Dog implements Runnable, Walkable { @Override public void run() { Runnable.super.run(); Walkable.super.run(); System.out.println("Dog - run"); } }
public static void main(String[] args) {
Dog dog = new Dog(); dog.run(); // Runnable - run // Walkable - run // Dog - run
}
再看一个例子:
public interface Animal {
default String myself() { return "I am an animal."; }
}
public interface Fire extends Animal {}
public interface Fly extends Animal {
@Override default String myself() { return "I am able to fly."; }
}
public class Dragon implements Fly, Fire {}
public static void main(String[] args) {
Dragon dragon = new Dragon(); System.out.println(dragon.myself()); // I am able to fly.
}
[](https://gitee.com/vip204888/java-p7)静态方法(Static Method) -------------------------------------------------------------------------------------- * 接口中定义的静态方法只能通过接口名调用,**不能被继承**;
public interface Eatable {
static void eat(String name) {