7.01 抽象类的定义
使用了关键词abstract声明的类叫作“抽象类”。如果一个类里包含了一个或多个抽象方法,类就必须指定成abstract(抽象)。“抽象方法”,属于一种不完整的方法,只含有一个声明,没有方法主体。
(1)抽象类定义的格式
修饰符 abstract 类名 {
类体
}
(2)抽象方法格式:
修饰符 abstract 返回值类型 方法名();
注意:
1. 在抽象类中的方法不一定是抽象方法,含有抽象方法的类必须定义成抽象类。
2.如果从一个抽象类继承,而且想生成新类型的一个对象,就必须为基础类中的所有抽象方法提供方法定义。
代码演示
public abstract class People{
private String name;
//声明抽象方法,没有方法体,直接分号结束
public abstract void talk();
public void eat(){
System.out.println(name+”正在学习”);
}
}
代码讲解:上述代码我们定义一个抽象方法talk,方法和类名都是abstract修饰,抽象方法没有方法体,直接分号结束。抽象类还能够定义普通方法eat()。
(3)抽象类定义规则:
1.抽象类和抽象方法必须用abstact关键字来修饰。
2.抽象类不能直接实例化,也就是不能直接使用new关键字产生对象。但可以:抽象类 变量=new 继承抽象类的实例类();
People p = new People(); //错误,People是抽象类,无法实例化
3.抽象方法定义时,只需要声明,不需要实现。
4.含抽象方法的类必须被声明为抽象类,抽象类的子类必须实现所有的抽象方法后,才能被实例化,否则这个子类还是个抽象类
提示:
子类在继承抽象类后,我们实现其方法体,用的是“实现”而不是“重写”。
(4)抽象类的用法
例:定义一个抽象类People,定义两个Student,Teacher子类,继承People
public class AbstractDemo{
public static void main(String[] args) {
Student stu = new Student(“dove”, 22);
Teacher tea = new Teacher(“wang”, 32);
stu.talk();
tea.talk();
}
}
abstract class People{ //抽象父类
private String name;
private int age;
public abstract void talk();//声明一个抽象方法talk()
}
class Student extends People{ //子类Student
private String name;
private int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
public void talk() { //重写talk
System.out.println(“学生—>姓名:”+this.name+”,年龄”+this.age);
}
}
class Teacher extends People{ //子类Teacher
private String name;
private int age;
public Teacher(String name,int age){
this.name = name;
this.age = age;
}
public void talk() { //重写talk
System.out.println(“老师—>姓名:”+this.name+”,年龄”+this.age);
}
}
代码结果:
学生—>姓名:dove,年龄22
老师—>姓名:wang,年龄32
注意:
在java中,抽象类中可以有自己的构造方法,但这些构造方法需要通过子类去调用,因为抽象类不能直接实例化。
7.02 接口的定义
是一种特殊的抽象类。只能够由抽象方法和全局变量组成。
(1)接口的定义
使用interface来定义一个接口。接口定义同类的定义类似,也是分为接口的声明和接口体,其中接口体由常量定义和方法定义两部分组成。
格式:
[修饰符] interface 接口名 [extends 父接口名列表]{
[public] [static] [final] 常量;
[public] [abstract] 方法;
}
修饰符:
可选,用于指定接口的访问权限,可选值为public。如果省略则使用默认的访问权限。
接口名:
必选参数,用于指定接口的名称,接口名必须是合法的Java标识符。一般情况下,要求首字母大写。
extends 父接口名列表:
可选参数,用于指定要定义的接口继承于哪个父接口。当使用extends关键字时,父接口名为必选参数。
方法:
接口中的方法只有定义而没有被实现。
(2)接口的实现
接口在定义后,就可以在类中实现该接口。在类中实现接口可以使用关键字implements。
格式:
[修饰符] class <类名> [extends 父类名] [implements 接口列表]{
}
修饰符:
可选参数,用于指定类的访问权限,可选值为public、abstract和final。
类名:
必选参数,用于指定类的名称,类名必须是合法的Java标识符。一般情况下,要求首字母大写。
extends 父类名:
可选参数,用于指定要定义的类继承于哪个父类。当使用extends关键字时,父类名为必选参数。
implements 接口列表:
可选参数,用于指定该类实现的是哪些接口。当使用implements关键字时,接口列表为必选参数。当接口列表中存在多个接口名时,各个接口名之间使用逗号分隔。
在类中实现接口时,方法的名字、返回值类型、参数的个数及类型必须与接口中的完全一致,并且必须实现接口中的所有方法。
注意:
实现多接口时可能出现常量或方法名冲突的情况,解决该问题时,如果常量冲突,则需要明确指定常量的接口,这可以通过“接口名.常量”实现。如果出现方法冲突时,则只要实现一个方法就可以了。
(3)接口的使用原则
由于接口里面存在有抽象方法,所以接口对象不可能直接使用关键字new进行实例化的操作。
· 接口必须要有子类,但是此时一个子类可以使用implements关键字实现多个接口;
· 接口的子类(如果不是抽象类),必须要覆写接口中的全部抽象方法;
· 接口的对象可以利用子类对象的向上转型进行实例化操作。
注意:
1.在接口里面没有写上public,默认为 public abstract~ 最终的访问权限也是public,绝对不是default。
2.一个抽象类可以去继承一个抽象类,一个接口却可以使用extends关键字同时继承多个接口(但是不能继承抽象类)。
3.在接口里面可以定义普通内部类、抽象内部类、内部接口。
7.03 接口的应用
(1)工厂设计模式
顾名思义,就是用来生产对象的。工厂模式最大的优点就是:解耦。
例:
假如生产小米手机,小米手机有很多系列,小米note、红米note等;假如小米note生产需要的配件有825的处理器,6英寸屏幕,而红米只需要650的处理器和5寸的屏幕就可以了;用抽象工厂来实现:
public interface Cpu { //cpu接口和实现类
void run();
}
class Cpu650 implements Cpu {
@Override
public void run() {
System.out.println("625 也厉害");
}
}
class Cpu825 implements Cpu {
@Override
public void run() {
System.out.println("825 处理更强劲");
}
}
public interface Screen { //屏幕接口和实现类
void size();
class Screen5 implements Screen {
@Override
public void size() {
//5寸
}
}
class Screen6 implements Screen {
@Override
public void size() {
System.out.println("6寸");
}
}
public interface PhoneFactory { //工厂接口
Cpu getCpu(); //使用的cpu
Screen getScreen(); //使用的屏幕
}
public class XiaoMiFactory implements PhoneFactory { //具体工厂实现类:小米手机工厂
@Override
public Cpu getCpu() {
return new Cpu.Cpu825(); //高性能处理器
}
@Override
public Screen getScreen() {
return new Screen.Screen6(); //6寸大屏
}
}
public class HongMiFactory implements PhoneFactory { //具体工厂实现类:红米手机工厂
@Override
public Cpu getCpu() {
return new Cpu.Cpu650(); //高效处理器
}
@Override
public Screen getScreen() {
return new Screen.Screen5(); //小屏手机
}
}
以上例子可以看出,抽象工厂可以解决一系列的产品生产的需求,对于大批量,多系列的产品,用抽象工厂可以更好的管理和扩展;
(2)代理设计模式
范例:程序(皇帝宠幸妃子)
interface Subject {// 整个操作的核心主题
public void make();
}
class RealSubject implements Subject {
@Override
public void make() {
System.out.println("过程……");
}
}
class ProxySubject implements Subject {
private Subject subject;
// 要接收一个真实主题的操作对象
public ProxySubject(Subject subject) {
this.subject = subject;
}
public void prepare() {
System.out.println("准备……");
}
@Override
public void make() {
this.prepare();
this.subject.make();
this.destory();
}
public void destory() {
System.out.println("销毁");
}
}
public class Demo {
public static void main(String args[]) {
Subject sub = new ProxySubject(new RealSubject());
sub.make();// 调用的是代理主题的操作
}
}
7.03 接口与抽象类的区别
1)关键字
- 抽象类 Abstract class
- 接口 Interface class
2)组成
- 抽象类 构造方法、抽象方法、普通方法、static方法、常量、变量
- 接口 抽象方法、全局常量
3)子类使用
-
class 子类 extends 抽象类
-
class 子类 implements 接口,接口,……
4)关系
-
可以实现多可接口
-
不能够继承抽象类,却可以继承多个父接口
5)权限
-
各种权限
-
public
6)限制
-
单继承局限
-
没有单继承局限
7)子类
- 抽象类和接口都必须有子类,子类必须要覆写全部的抽象方法
8)实例化对象
- 依靠子类对象的向上转型进行对象的实例化。
7.04 内部类
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来了解一下这四种内部类的用法。
1.成员内部类
成员内部类是最普通的内部类,它的定义为位于另一个类的内部。
形如下面的形式:
class Circle {
double radius = 0;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println("drawshape");
}
}
}
这样看起来,类Draw像是类Circle的一个成员,Circle称为外部类。成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
注意:
1.当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
(1)外部类.this.成员变量
(2) 外部类.this.成员方法
2.在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问。
3.成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。
2.局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
class People{
public People() {
}
}
class Man{
public Man(){
}
public People getWoman(){
class Woman extends People{ //局部内部类
int age =0;
}
return new Woman();
}
}
注意,局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
3.匿名内部类
匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。
4.静态内部类
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。
静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
}
}
7.05 小结
1.抽象类和常规类一样,都有数据和方法,但是不能用 new 操作符创建抽象类的实例 。
2.非 抽 象 类 中 不 能 包 含 抽 象 方 法。如果抽象类的子类没有实现所有被继承的父类抽象方法,就必须将该子类也定义为抽象类 。
3.包含抽象方法的类必须是抽象类。但是,抽象类可以不包含抽象的方法 。
4.即使父类是具体的,子类也可以是抽象的 。
5.接口是一种与类相似的结构,只包含常量和抽象方法。接口在许多方面与抽象类很相近,但抽象类除了包含常量和抽象方法外 , 还可以包含变量和具体方法 。
6.在 Java 中,接口被认为是一种特殊的类。就像常规类一样,每个接口都被编译为独立的字节码文件 。
7.接口 java.lang.Comparable 定义了 compareTo 方法。Java 类库中的许多类都实现Comparable 。
8.接口 java.lang.Cloneable 是一个标记接口。实现 Cloneable 接口的类的对象是可克隆的 。
9.一个类仅能继承一个父类,但一个类却可以实现一个或多个接口 。
10.一个接口可以继承一个或多个接口 。