面向对象进阶3
抽象类和抽象方法
- 抽象方法:将共性的行为抽取到父类之后,由于每一个子类执行的内容是不一样的,所以在父类中不能确定具体的方法体,该方法就可以定义为抽象方法
- 抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类
抽象类的定义格式
public abstract class 类名{}
抽象方法的定义格式
public abstract 返回值类型 方法名(参数列表);
没有方法体,直接;
结束
注意事项
- 抽象类不能实例化,也就是不能创建对象
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 抽象类可以有构造方法
这里的构造方法的作用:当创建子类对象的时候,给属性进行赋值的 - 抽象类的子类
a. 要么重写抽象类中的所有抽象方法
b. 要么子类本身也是一个抽象类
抽象类在实际开发中有何意义
规范代码
子类必须强制以某种格式重写,否则会报错
接口
- 就是一种规则,是对行为的抽象
- 与抽象类的异同:接口更侧重于行为,而抽象类更侧重于事物
定义接口和使用
- 使用关键字
interface
定义
public interface 接口名 {}
- 接口不能实例化
- 接口和类之间是实现关系,通过
implements
关键字表示
public class 类名 implements 接口名 {}
- 接口的子类,叫做实现类
a. 要么重写接口中的所有抽象方法(常用)
b. 要么本身是抽象类
注意
- 接口与类之间是实现关系,可以单实现,也可以多实现。
public class 类名 implements 接口名1, 接口名2 {}
- 实现类在继承一个类的同时实现多个接口
public class 类名 extends 父类 implements 接口名1, 接口名2 {}
接口中成员的特点
- 成员变量
只能是常量
默认修饰符:public static final
- 构造方法
没有,因为接口不能创建对象,所以没有构造方法 - 成员方法
只能是抽象方法
默认修饰符:public abstract
a. JDK7以前:接口中只能定义抽象方法
b. JDK8新特性:接口中可以定义有方法体的方法
c. JDK9新特性:接口中可以定义私有方法 - 示例:
public interace InterfaceDemo {
// 定义成员变量
int a = 10; // 默认是public static abstract修饰
//等于
public static final int a = 10;
//定义成员方法
void show(); // 默认是public abstract修饰
// 等于
public public static show();
}
接口和类之间的关系
类和类的关系
继承关系。只能单继承,不能多继承,但是可以多层继承
类和接口的关系
实现关系。可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
如果多个接口中有同名的抽象方法怎么办?
只用重写一次
接口和接口的关系
继承关系,可以单继承,也可以多继承
细节:如果实现类实现了最下面的子接口,那么就需要重写所有的抽象方法
小练习:
描述:
# 编写带有接口和抽象类的标准Javabean类
乒乓球运动员和篮球运动员,乒乓球教练和篮球教练
跟乒乓球相关的人员需要学习英语
乒乓球运动员:姓名、年龄、学习打乒乓球、说英语
篮球运动员:姓名、年龄、学习打篮球
乒乓球教练:姓名、年龄、教打乒乓球、说英语
篮球教练:姓名、年龄、教打篮球
分析:
分析:接口类--->说英语
Javabean类:
1. Person(抽象类)
属性:name、age
方法:构造方法、get和set
2. 运动员(抽象类)
属性:继承Person
方法:构造方法、抽象方法--->学习
3. 教练(抽象类)
属性:继承Person
方法:构造方法、抽象方法--->教
4. 乒乓球运动员:
属性:继承Sporter,实现Speak接口
方法:重写study和speak
5. 篮球运动员
属性:继承Sporter
方法:重写study
6. 乒乓球教练
属性:教练Coach,实现Speak接口
方法:重写teach和speak
7. 篮球教练
属性:教练Coach
方法:重写teach
实现:
根据分析实现,此处省略
关于接口的扩展
JDK8开始接口中新增的方法
一、JDK8之后,接口中可以定义有方法体的方法(默认、静态)
- 允许接口中定义默认方法,需要使用关键字
default
修饰
作用:解决接口升级的问题
接口中默认方法的定义格式:public default void show(...args){...}
接口中默认方法的注意事项:
a. 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
b. public可以省略,但是default不可以省略
c. 如果实现了多个接口,多个接口中存在相同名字的默认方法,就必须对该方法进行重写 - 允许在接口中定义静态方法,需要用
static
修饰
接口中静态方法的定义格式:public static void show(..args){...}
接口中静态方法的注意事项:
a. 只能通过接口名调用,不能通过实现类名或对象名调用
b. public可以省略,static不能省略
c. 静态方法是不需要重写的
二、JDK9之后,接口中也可以定义私有方法
只为接口内部提供服务
定义格式: - 格式1:
private void show(){}
为默认方法服务的 - 格式2:
private static void method(){}
为静态方法服务的
接口的应用
- 把多个类都有可能用到的规则都设计成接口。对于实现类来讲,想要实现类有什么功能,就实现对应的接口
- 在定义方法的时候,将参数定义为一个接口类型,那么这个方法就可以接受该接口的所有实现类作为参数。这种方式称为接口多态
例如:
// 方法定义
public void show(Impel imp){
...
}
// 方法调用--->Impel imp = new ClassImpel()--->接口的多态
ClassImpel kkk = new ClassImpel();
show(kkk)
// 这个kkk是Impel的实现类对象
适配器设计模式
什么是设计模式?
是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性
什么是适配器设计模式?
解决接口与接口实现类之间的矛盾问题
具体理解:也就是如果一个接口中存在好多个抽象方法,在实现类中只想要重写其中一个,但是接口的抽象方法都是全部都要重写的。这时就需要用到适配器设计模式
如何实现
a. 新建一个中间类:Xxx(接口名)Adapter
,实现接口
b. 对接口中所有的方法进行空实现
c. 在真正的实现类继承这个中间类XxxAdapter
并重写用到的那一个方法
注意
这个中间类创建对象是没有意义的,所以一般加上abstract
,给写成抽象类,
内部类
类的五大成员:
属性、方法、构造方法、代码块、内部类
什么是内部类?
在一个类的里面,再定义一个类
为什么要内部类?
1.内部类表示的事物是外部类的一部分
2.内部类单独出现没有什么意义
就比如汽车的发动机,发动机就是汽车的一个内部类、ArrayList的迭代器
内部类的访问特点:
1.内部类可以直接访问外部类的成员,包括私有
2.外部类要访问内部类的成员,必须创建对象
示例:
public class Car {
String carName;
int carAge;
String carColor;
// 在外部不能访问内部类里的成员
// 例
public void show(){
System.out.println(engineAge); // 是错误的写法
// 必须先创建内部类对象
Engine e = new Engine();
sout(e.engineAge);
}
class Engine {
String engineBrand;
int engineAge;
public void show(){
System.out.println(carAge);
}
}
}
内部类的分类:
1.成员内部类—了解
2.静态内部类—了解
3.局部内部类—了解
4.匿名内部类—掌握
成员内部类
成员内部类的代码如何写
- 写在成员位置,属于外部类的成员
- 可以被一些修饰符所修饰,比如:private、空着不写、protected、public、static
- 成员内部类里面,JDK16之前不能定义静态变量,JDK16之后才可以
如何创建成员内部类的对象
方式一:
在外部类中编写方法,对外提供内部类的对象(一般来讲,只有使用private修饰内部类的时候会使用这种方法)
例:
private class Inner{
...
}
public Inner getInstance() {
return new Inner;
}
方式二:
直接创建:外部类名.内部类名 对象名 = new 外部类对象.内部类对象
例:Outer.Inner oi = new Outer().new Inner()
成员内部类如何获取外部类的成员变量
如果不重名,直接使用即可。
针对重名的情况
例:
package com.day.a02innerclassdemo2;
public class Outer {
private int a = 10;
class Inner {
private int a = 20;
void show() {
int a = 30;
System.out.println(a); // 内部变量
System.out.println(this.a);// 内部类的成员变量
System.out.println(Outer.this.a); // 外部类的成员变量
}
}
}
内部类的内存图
内部类对象里会有一个隐藏的this,记录外部类的地址,所以Outer.this
记录了外部类对象的地址值
静态内部类
- 也是成员内部类的一种
- 只能访问外部类中的静态变量和静态方法。如果要访问非静态的需要创建外部类的对象
例:
public class Outer {
int a = 10;
static int b = 20;
static class Inner {
public void show() {
Outer o = new Outer(); // 创建外部类对象,访问非静态方法
System.out.println(o.a);
}
public static void show2() {
Outer o = new Outer(); // 创建外部类对象,访问非静态方法
System.out.println(o.a);
}
}
}
创建静态内部类对象的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名()
例:Outer.Inner oi = new Outer.Inner();
如何调用静态内部类中的方法
1.调用非静态方法的格式: 先创建对象,用对象调用
例: oi.show()
调用非静态方法
2.调用静态方法的格式: 外部类名.内部类名.方法名()
例:Outer.Inner,show2()
局部内部类
1.将内部类定义在方法里面,类似于方法里面的局部变量
2.外界无法直接使用,需要在方法内创建对象并使用
3,可以直接访问外部类的成员,也可以访问方法中的局部变量
例:
public class Outer {
int b = 20;
// 局部内部类
public void show() {
int a = 10;
class Inner {
String name;
int age;
public void method1(){
System.out.println(b);
System.out.println(a);
System.out.println("局部内部类中的方法1");
}
public static void method2(){
System.out.println("局部内部类中的方法2");
}
}
// 创建局部内部类的对象
Inner i = new Inner();
System.out.println(i.name);
i.method1();
}
}
匿名内部类
本质是隐藏了名字的内部类。可以写在成员位置,也可以写在局部位置
格式:
new 类名或接口名(){
重写方法;
};
格式的细节:
包含了1.继承/实现关系
2.方法的重写
3.创建对象
整体就是一个类的子类对象或者接口的实现类对象
例:
// 先写一个接口Swik,定义一个抽象方法swim
public interface Swim {
public abstract void swim();
}
// 再写一个测试类
package com.day.a05innerclassdemo5;
public class Test {
public static void main(String[] args) {
// 匿名内部类
new Swim() {
// 重写swim中所有的抽象方法
@Override
public void swim() {
System.out.println("重写了Swim中的抽象方法swim");
}
};
}
}
**使用场景:
当方法的参数是接口或者类时
以接口为例:可以传递这个接口的所有实现类对象。如果实现类只使用一次,就可以使用匿名内部类简化代码