Java面向对象-抽象类/接口类/内部类
抽象类/抽象方法
抽象类
抽象类除了定义抽象方法外,与普通类一样- 可定义成员变量/方法,由于抽象类无法实例化,因此只具有语法意义;
- 可定义static变量/方法,可通过类名直接访问;
- 可继承/实现其他类/接口;
子类继承抽象父类,- 子类为非抽象类:强制重写抽象父类中的所有抽象方法,继承其他成员变量与成员方法;
- 子类为抽象类:不必强制实现抽象父类中的抽象方法,继承其他成员变量与成员方法,由于抽象子类也无法实例化,因此继承只具有语法意义;
抽象类只能作为父类被子类继承,因此抽象类不可实例化,无法创建实例对象抽象类中可以没有抽象方法,有抽象方法的类必须是抽象类;抽象类命名使用Abstract或者Base开头;
- 可定义成员变量/方法,由于抽象类无法实例化,因此只具有语法意义;
- 可定义static变量/方法,可通过类名直接访问;
- 可继承/实现其他类/接口;
- 子类为非抽象类:强制重写抽象父类中的所有抽象方法,继承其他成员变量与成员方法;
- 子类为抽象类:不必强制实现抽象父类中的抽象方法,继承其他成员变量与成员方法,由于抽象子类也无法实例化,因此继承只具有语法意义;
抽象方法
// 抽象方法不可有方法体,{}表示方法体为空,不代表没有
public abstract double sum();
// 抽象方法不可有方法体,{}表示方法体为空,不代表没有
public abstract double sum();
抽象方法只有声明,没有方法体;- 抽象方法的存在意义在于子类继承重写
- private/static与abstract不能同时修饰方法;
- 抽象方法的存在意义在于子类继承重写
- private/static与abstract不能同时修饰方法;
接口(interface)
接口定义了完全抽象的类,体现规范与实现分离的设计理念;// 接口可继承多个父接口
[publc/包访问权限] interface 接口名 extends 接口1, 接口2, ... {
...
}
接口访问修饰符是public/包访问权限,可通过extends关键字继承多个直接父接口,获得父接口内的所有方法/常量;(任何使用C++函数指针的地方,考虑使用Java中的接口)
// 接口可继承多个父接口
[publc/包访问权限] interface 接口名 extends 接口1, 接口2, ... {
...
}
接口访问修饰符是public/包访问权限,可通过extends关键字继承多个直接父接口,获得父接口内的所有方法/常量;(任何使用C++函数指针的地方,考虑使用Java中的接口)
接口成员
接口中不包含构造器,包含:
成员变量:只能为static常量,默认使用public static final修饰,可通过接口名直接访问;
接口方法:
接口方法:
- 抽象方法:形式上定义普通方法系统将自动使用public abstract修饰,编写时不加任何修饰符,保持代码简洁;
- static方法(不推荐使用):必须要有方法体,可通过接口名直接访问;
- default方法(不推荐使用):必须要有方法体;
- Java8引入用于最终能顺畅使用Lambda表达式;
- 子类不必强制重写default方法,子类内部通过interfaceName.super.defaultMethodName()调用;
// 默认方法只需在普通方法头前加default关键字
defalut String test();
- 内部类/内部接口/内部枚举均默认用public abstract修饰;
子类实现接口
类可使用implements关键字实现多个接口,语法格式:
[修饰符] class 类名 extends 父类名 implements 接口1, 接口2... {
类体部分;
}
- 类实现接口后,类中必须重写接口内的所有抽象方法,所有方法必须是public方法,否则该类必须定义为抽象类(因为保留了从父接口继承到的抽象方法);
- 子类可以向上转型为多个父类接口类型,实现多态;
接口与抽象类辨析
比较项 接口 抽象类 普通变量 N Y static变量 Y Y 构造器 N Y 普通方法 N Y 抽象方法 Y Y static方法 Y Y default方法 Y N 创建实例 N N “继承”机制 可实现多接口 单一继承
abstract class和interface所反映出的设计理念不同。- abstract class表示的是"is-a"关系;
- interface表示的是"like-a"关系;
接口/抽象类不能定义实例,但是可以定义引用变量指向子类对象(向上转型),实现多态;
比较项 | 接口 | 抽象类 |
普通变量 | N | Y |
static变量 | Y | Y |
构造器 | N | Y |
普通方法 | N | Y |
抽象方法 | Y | Y |
static方法 | Y | Y |
default方法 | Y | N |
创建实例 | N | N |
“继承”机制 | 可实现多接口 | 单一继承 |
- abstract class表示的是"is-a"关系;
- interface表示的是"like-a"关系;
回调(call-back) / 钩子(Hook)
回调机制可通过- public stract方法,强制子类重写方法;
- 接口定义,子类重写实现;
回调/钩子函数定义参数为抽象类/接口变量,根据实际需要传入实际的子类引用,实现回调/钩子,本质上是多态的应用;
- public stract方法,强制子类重写方法;
- 接口定义,子类重写实现;
对象克隆
浅拷贝(默认,默认为Object类中的clone方法):原始变量和拷贝变量引用同一个对象,拷贝域分为;
- 数值或基本数据类型:对应拷贝;
- 子对象引用:拷贝结果会使两个域引用同一个对象,导致原始对象域克隆对象共享该部分信息;
深拷贝(必须实现):对象所在类实现Object类中的clone方法,对象中包含:
- 实现Cloneable接口;
- 使用public修饰自定义clone()方法;
class Person implements Cloneable{
...
// 重写clone方法
public Person clone() throws CloneNotSupportException {
Person cloned = (Person) super.clone(); // 调用默认浅拷贝
cloned.boy = (Boy) boy.clone(); // 拷贝子对象
}
}
Cloneable接口是Java提供的标记接口(tagging interface)之一,标记接口内部没有方法;
内部类(inner class)
定义在类中的类叫“内部类”,与之相对的类是“外部类”;
内部类方法对同一个包中的其他类透明;
内部类时一种编译器现象,与JVM无关;
内部类自动拥有访问外部类所有成员的权限(无视控制符),当外部类对象创建一个内部类对象时,该内部类对象会秘密捕获一个外部类对象引用(显式表示为OutClassName.this),内部类通过外部类引用来访问外部类所有成员
局部内部类
局部内部类:定义在方法内部的类,只对所在方法可见;
局部内部类只能引用外部类的final变量;
局部类不可用public, private控制符声明;
非static内部类
非静态内部类访问外部类:
非静态内部类作为『外部类的成员』可访问外部类成员变量/成员方法;
—>机制:在非静态内部类实例中,保存了外部类实例的引用:
// 内部类在内部访问内部类成员
this.内部类成员;
// 内部类在内部访问外部类成员
OutClass.this.外部类成员;
非静态内部类中不可以定义static成员;
外部类内部访问非静态内部类:与平常使用普通类无差别;
—>外部类中的静态方法,不可访问非静态内部类;
外部类以外使用非静态内部类:
OuterClass.InnerClass Instance = new OuterClass().new InnerConstructor();
static/静态内部类
// 外部类不可用static修饰
static class StaticInnerClass {
成员:static/非static;
}
static内部类属于外部类本身/不属于外部类实例,
static内部类访问外部类:
—>static内部类对象寄生在外部类的类本身中,不是寄生在外部类实例中;
—>static内部类对象只持有外部类的类引用,不持有外部类对象的引用;
—>static内部类不能访问外部类的实例成员;
static内部类只可访问外部类的static成员;
接口内可定义内部接口,默认用public static修饰,使用场景少;
外部类内部访问静态内部类
StaticInnerClass.static成员; // 通过静态内部类名直接访问静态内部类成员;
外部类以外使用静态内部类:
静态内部类是外部类类相关的—>创建静态内部类对象时无须创建外部类对象
StaticOutClass.StaticInnerClass Instance = new StaticOutClass.StaticInnerClassConstructor();
所有博文来自个人为知笔记,内容多为读书笔记和理解内容;