11.抽象类和接口
本章目标
- 抽象类
- 接口
- 内部类
本章内容
在父类中,有时我们引入了某个方法,但是它存在的目的就是为了利用多态性,在父类中不能提供有意义的实现,更合适的做法是把方法的具体实现留给子类去完成,那这个方法该怎么定义?
一、抽象类
1、抽象概念
抽象:考察特定应用程序相关问题的某些方面的过程
抽象分为两类:
- 数据抽象:识别与特定的应用程序相关的属性过程抽象:
- 识别方法,将注意力集中在过程的参数和返回值,而不是实现
2、抽象方法
2.1、什么是抽象方法
在父类中,有时我们引入了某个方法,但是它存在的目的就是为了利用多态性,在父类中不能提供有意义的实现,更合适的做法是把方法的具体实现留给子类去完成。
为了使类、方法设计的目的更加明确,对于这种准备留给子类实现的方法使用abstract
关键字将其设置为抽象方法。
抽象方法没有方法体,在声明时直接用;
号结束
public abstract void working();
2.2、抽象方法的特征:
- 声明时没有方法体
- 使用abstract关键字修饰
- 不能与private同时使用
- 不能与final同时使用
- 不能与static同时使用
- 不能修饰构造方法
3、抽象类
在面向对象的概念中,我们知道所有的对象都是通过类来描绘的,但是反过来却不是这样。并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。
含有抽象方法的类,即成为抽象类,必须使用abstract关键字修饰。
3.1、抽象类的特点:
- 不能实例化,可以有对象变量用以指向子类的对象
- 必须被继承,并在子类中实现全部功能
- 如果一个子类没有实现抽象基类中所有的抽象方法,则子类也成为一个抽象类。
- 我们可以将一个没有任何抽象方法的类声明为abstract,避免由这个类产生任何的对象。
- 抽象类可以有构造方法,抽象类中可能包含一些需要初始化的字段或属性。当子类继承抽象类时,子类的构造函数会隐式地调用父类的构造函数,以确保父类的字段被正确初始化
3.2、对于抽象类与抽象方法的限制如下:
- 凡是用abstract修饰符修饰的类被称为抽象类。
- 凡是用abstract修饰符修饰的成员方法被称为抽象方法。
- 抽象类中可以有零个或多个抽象方法,也可以包含非抽象的方法。
- 抽象类中可以没有抽象方法,但是,有抽象方法的类必须是抽象类。
- 对于抽象方法来说,在抽象类中只指定其方法名及其类型,而不书写其实现代码。
- 抽象类不能创建对象,创建对象的工作由抽象类派生的子类来实现。
4、示例一
4.1、声明抽象类
public abstract class Employee{
private String empId;
private String empName;
public abstract void training();
public abstract void working();
……
}
4.2、Salesman子类继承
public class Salesman extends Employee {
……
@Override
public void training(){
System.out.println(`背资料!……再背一遍资料!……`);
}
@Override
public void working(){
System.out.println(`签订单!……再签一张订单!……`);
}
……
}
4.3、Manager子类继承
public class Manager extends Employee {
……
@Override
public void training() {
System.out.println(`拓展训练!……研讨会?旅游?……`);
}
@Override
public void working() {
System.out.println(`开会!……分任务!……罚款!……`);
}
……
}
5、示例二
5.1、声明抽象类
声明抽象类Quadrangle(四边形),其中有两个方法,一个是求周长getPerimeter()
,一个是求面积getArea()
public abstract class Quadrangle {
public double getPerimeter(double... side) {
return side[0]+side[1]+side[2]+side[3];
}
abstract double getArea(double... side);
}
5.2、Square子类继承
public class Square extends Quadrangle {
@Override
void getArea(double... side) {
return side[0]*side[1];
}
}
其它示例效果一样
二、接口
类与类之间的协议,一套标准、约束、规范等
1、什么是接口
interface
(接口)关键字使抽象的概念更深入了一层。我们可将其想象为一个纯
抽象类。
- 仅仅定义了类
应该提供哪些功能
,而不涉及如何实现这些功能; - 接口中仅仅包括了常量以及一系列方法的定义(但并不进行实现);
- 接口中定义的方法需要靠其他类来实现;
- 接口不是类,因此不能通过
new
关键字来实例化一个接口,但是可以声明接口类型的变量,变量必须引用该接口某个实现类的对象。
2、语法
2.1、声明接口
[修饰符] interface 接口名[extends 父接口名列表]{
常量数据成员声明;
抽象方法声明;
default方法声明;
}
2.2、接口成员
接口中的数据成员都是用public static final修饰的,写法如下:
修饰符 数据成员类型 数据成员名=常量值;
或
数据成员名=常量值;
2.3、接口方法
接口中的方法成员都是用public abstract修饰的,写法如下:
修饰符 返回值类型 方法名(参数列表);
或
返回值类型 方法名(参数列表);
2.4、default方法
从jdk1.8开始,接口中可以定义多个default修饰的方法,可以带方法体
default void method1(){//default修饰的方法
}
默认方法主要优势:
1、提供了一种扩展接口的方法,而不破坏现有代码。
如果一个已经投入使用的接口需要扩展一个新的方法,在JDK8以前,我们必须再该接口的所有实现类中都添加该方法的实现,否则编译会出错。如果实现类数量很少且我们有修改的权限,可能工作量会少,但是如果实现类很多或者我们没有修改代码的权限,这样的话就很难解决了。而默认方法提供了一个实现,当没有显式提供时就默认采用这个实现,这样新添加的接口就不会破坏现有的代码。
2、默认方法另一个优势是该方法是可选的,子类可以根据不同的需求而且经override或者采用默认实现
2.5、static方法
从jdk1.8开始,接口中可以定义一个或者更多个静态方法(带方法体)。接口中的静态方法只能通过接口本身调用,不能通过实现类的实例或子类的实例调用。静态方法与接口本身关联,与具体的实现类无关。
1.8之前针对字符串操作,通常通过另一个工具类StringUtil 来提供静态操作,并不是最好的选择。Java 8 为接口新增静态方法后,可以把常用的工具方法直接写在接口上,可以更好地组织代码,更易阅读和使用
interface StaticInterface {
static void print() {
System.out.println("static interface");
}
}
//调用,接口名称.静态方法名(),只能通过接口本身调用,不能通过实现类的实例或子类的实例调用
public static void main(String[] args) {
StaticInterface.print();
}
注意:
接口的static方法不能被子接口继承; 使用static修饰的接口中的方法必须有主体; 接口的static不能被实现类重写或直接调用; 接口的static方法只能被接口本身调用:接口名.静态方法名。
3、接口的特征
- 使用
interface
关键字,必须声明为public或者不加任何修饰符。接口的修饰符隐含决定接口内方法和变量的修饰符。 - 实现接口的类同接口一样,修饰符要么是public的,要么没有修饰符。而且如果类中实现了某个接口中的方法,那么所有实现的方法必须声明为public的。
- 如果有类
implements
某个接口,但没有实现接口中所有的方法,那么该类必须声明为abstract的。可以声明一个接口的引用变量,那么所有实现此接口的类的对象引用都可以赋给此引用变量。 - 一个接口可以继承(extends)其他接口,实现一个继承了其他接口的接口时,必须实现该接口中声明的和所继承的所有其他接口中的方法。
- 在类中,用
implements
关键字就可以实现接口。一个类若要调用多个接口时,可在implements后用逗号隔开多个接口的名字
4、示例
4.1、声明图形接口
public interface Shapes {
public static final double PI = 3.14159;
public abstract double getArea();
public abstract double getPerimeter();
}
4.2、声明圆实现接口
一个类实现接口,那么该类必须实现接口中所有方法
/**
* 声明表示圆形的实现类Circle
*
* @author 冯Sir
*
*/
public class Circle implements Shapes {
private int radius;
public double getArea() {
return this.radius * this.radius * PI;
}
public double getPerimeter() {
return 2 * this.radius * PI;
}
public Circle(int radius) {
this.radius = radius;
}
}
4.3、声明四边形实现接口
四边形只实现了求周长的方式,一个类实现接口,如果不能实现接口中所有方法,那么该类要定义为抽象类
public abstract class Quadrangle implements Shapes {
private double sidea;
private double sideb;
private double sidec;
private double sided;
public Quadrangle(double sidea, double sideb, double sidec, double sided) {
super();
this.sidea = sidea;
this.sideb = sideb;
this.sidec = sidec;
this.sided = sided;
}
@Override
public double getPerimeter() {
double perimeter = sidea + sideb + sidec + sided;
return perimeter;
}
……
}
4.4、创建正方形继承四边形
public class Square extends Quadrangle {
public Square(double sidea, double sideb, double sidec, double sided) {
super(sidea, sideb, sidec, sided);
}
@Override
public double getArea() {
double area = getSidea() * getSidec();
return area;
}
}
4.5、测试
public class Test {
public static void main(String[] args) {
Square square = new Square(1,1,1,1);
double area = square.getArea();
System.out.println(area);
}
}
5、案例(贯穿项目相关)
针对Employee类建立一个接口(IEmployeeDao)
接口里有对应的操作方法如:填加,删除,查询,修改,比如addEmployee(Employee emp)
针对接口建立一个实现类(EmployeeDaoImpl),并实现接口中的所有方法
5.1、创建接口
该接口只包含部分方法,
package com.woniuxy.dao;
import com.woniuxy.entity.Employee;
public interface IEmployeeDao {
public int addEmp(Employee employee);
public int updateEmp(Employee employee);
public Employee[] queryAll();
public Employee queryById(int empId);
public int delete(int empId);
}
5.2、创建实现类
数组版实现方式
package com.woniuxy.dao.impl;
import com.woniuxy.dao.IEmployeeDao;
import com.woniuxy.entity.Employee;
public class EmployeeDaoImpl implements IEmployeeDao {
Employee[] emps = new Employee[10];
private int count = 0;
@Override
public int addEmp(Employee employee) {
emps[count++] = employee;
return 1;
}
@Override
public int updateEmp(Employee employee) {
for (int i = 0; i < emps.length; i++) {
if (emps[i].getEmpId() == employee.getEmpId()) {
emps[i] = employee;
}
}
return 1;
}
@Override
public Employee[] queryAll() {
// TODO Auto-generated method stub
return emps;
}
@Override
public Employee queryById(int empId) {
Employee employee = new Employee();
for (int i = 0; i < emps.length; i++) {
if (emps[i].getEmpId() == empId) {
employee = emps[i];
}
}
return employee;
}
@Override
public int delete(int empId) {
for (int i = 0; i < emps.length; i++) {
if (emps[i].getEmpId() == empId) {
emps[i] = null;
}
}
return 1;
}
}
5.3、测试类
测试类只测试一下添加即可,完整操作可以放到后面集合中
package com.woniuxy;
import java.util.Scanner;
import com.woniuxy.dao.IEmployeeDao;
import com.woniuxy.dao.impl.EmployeeDaoImpl;
import com.woniuxy.entity.Employee;
public class Main {
public static void main(String[] args) {
System.out.println("欢迎进入员工管理系统!!");
Scanner scanner = new Scanner(System.in);
IEmployeeDao empDao = new EmployeeDaoImpl();
while(true) {
System.out.println("请选择操作:1、查询全部 2、添加 3、修改 4、删除 5、根据id查询 9 、退出 ");
int num = scanner.nextInt();
if(num==9) {
break;
}else if(num==1) {
Employee[] queryAll = empDao.queryAll();
for (Employee employee : queryAll) {
if(employee!=null) {
System.out.println(employee);
}
}
}else if(num==2) {
System.out.println("请输入员工编号");
int id = scanner.nextInt();
System.out.println("请输入员工姓名");
String name = scanner.next();
System.out.println("请输入性别 ");
String sex = scanner.next();
System.out.println("请输入薪水");
double salary = scanner.nextDouble();
System.out.println("请输入年龄");
int age = scanner.nextInt();
Employee employee = new Employee(id, name, age, sex, salary);
int result = empDao.addEmp(employee);
if(result>0) {
System.out.println("插入成功");
}else {
System.out.println("插入失败");
}
}else if(num==3) {
System.out.println("请输入员工编号");
int id = scanner.nextInt();
System.out.println("请输入员工姓名");
String name = scanner.next();
System.out.println("请输入性别 ");
String sex = scanner.next();
System.out.println("请输入薪水");
double salary = scanner.nextDouble();
System.out.println("请输入年龄");
int age = scanner.nextInt();
Employee employee = new Employee(id, name, age, sex, salary);
int result = empDao.updateEmp(employee);
if(result>0) {
System.out.println("修改成功");
}else {
System.out.println("修改失败");
}
}else if(num==4) {
System.out.println("请输入员工编号");
int id = scanner.nextInt();
int result = empDao.deleteEmp(id);
if(result>0) {
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
}else if(num==5) {
System.out.println("请输入员工编号");
int id = scanner.nextInt();
Employee employee = empDao.queryById(id);
System.out.println(employee);
}
}
}
}
三、内部类(理解)
内部类从JDK1.1开始引入,它的作用域由包含它的类的作用域决定。
1、定义
可以在一个类的内部定义另一个类,这种类称为内部类。
内部类分为两种类型:
- 成员内部类
- 方法内部类
- 匿名内部类
- 静态嵌套类
平常我们使用的最多的是匿名内部类
2、场景
使用内部类的主要原因包括:
- 内部类可以方便的访问该类定义所在作用域中的数据,包括私有数据;
- 可以对同一个包中的其他类隐藏;
- 使用匿名内部类可以很方便的定义回调方法,这一优点在Swing开发中大量地被使用到
3、特性
- 内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类命和$符号。
- 内部类不能用普通的方式访问。
- 内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的
4、成员内部类
作为类的一个成员存在,和类成员是同级别的
public class Outer {
private int i = 10;
public void makeInner() {
Inner in = new Inner();
in.seeOuter();
}
class Inner {
public void seeOuter() {
System.out.print(i);
}
}
public static void main(String[] args) {
Outer out = new Outer();
out.makeInner();
}
}
5、方法内部类
在方法内定义的类
- 定义在方法中的内部类只在方法内部可见,在外部类及外部类的其它方法中都不可见。
- 方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化
public class Outer {
public void doSomething() {
final int a = 10;
class Inner {
public void seeOuter() {
System.out.println(a);
}
}
Inner in = new Inner();
in.seeOuter();
}
public static void main(String[] args) {
Outer out = new Outer();
out.doSomething();
}
}
6、匿名内部类
顾名思义,没有名字的内部类,只有实现的接口或继承的父类的名字
public class Test {
public static void main(String[] args) {
Car car = new Car() {
public void drive() {
System.out.println("Driving another car!");
}
};
car.drive();
}
}
class Car {
public void drive() {
System.out.println("Driving a car!");
}
}
思维导图
本文来自博客园,作者:icui4cu,转载请注明原文链接:https://www.cnblogs.com/icui4cu/p/18802591