目录
一、抽象类
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一 般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
abstract关键字的使用
abstract表示抽象的,可以用来修饰类和方法。
abstract修饰类
用abstract关键字来修饰一个类,这个类叫做抽象类
1、抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
2、抽象类中一定有构造器,便于子类实例化时使用
3、开发中都会提供抽象类的子类,让子类对象实例化完成相关的操作。
4、抽象类中可以没有抽象方法
abstract修饰方法
用abstract关键字来修饰一个方法,这个方法叫做抽象方法
1、抽象方法只有方法的声明,没有方法体
public abstract void method();
2、抽象方法只能用在抽象类中
说明:当在一个类中声明抽象方法,则表明此抽象方法不希望被调用,如果该类不是抽象类,则表明此抽象方法有被调用的可能性,因此抽象方法只能定义在抽象类中。(java的保护机制)
3、若子类重写了父类中的所有抽象方法后,此类才可以被实例化;若子类中没有重写父类中的所有的抽象方法,此子类必须也是一个抽象类,需用abstract修饰
说明:父类包括间接父类
abstract使用注意
1、abstract不能用来修饰属性、构造器等
2、abstract不能修饰私有方法、静态方法、final的方法、final的类
练习
编写一个Employee类,声明为抽象类, 包含如下三个属性:name,id,salary。
提供必要的构造器和抽象方法:work()。
对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。
请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。
Employee类
public abstract class Employee {
private String name;
private int id;
private double salary;
public Employee() {
//构造器
}
public Employee(int id,String name,double salary) {
//构造器
this.id=id;
this.name=name;
this.salary=salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public abstract void work();
}
Manager类
public class Manager extends Employee{
private double bonus;
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
/**
* 构造器
*/
public Manager() {
super();
}
/**
* @param id 账号
* @param name 姓名
* @param salary 工资
*/
public Manager(int id, String name, double salary,double bonus) {
super(id, name, salary);
this.bonus=bonus;
}
public void work() {
System.out.println("经理管理员工");
}
}
CommonEmployee类
public class CommomEmployee extends Employee {
@Override
public void work() {
System.out.println("普通员工当牛做马");
}
}
测试类
public class EmployeeTest {
public static void main(String[] args) {
Manager manager=new Manager(1001,"light",10000,200);
CommomEmployee common=new CommomEmployee();
manager.work();
common.work();
}
}
运行结果如下
二、接口(interface)
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是Java不支持多重继承。有了接口,就可以得到多重继承的效果。接口和类是两个并列的结构
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。
继承是一个"是不是"的关系,而接口实现则是 "能不能" 的关系。
接口的使用
1、接口(interface)是抽象方法和常量值定义的集合。
2、java中接口和类是两个并列的结构
3、接口中不能定义构造器,即接口不能被实例化
4、java开发中,接口都通过类去实现(implements)的方式来使用(类实现接口)
- 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
- 如果实现类中没有覆盖接口中的所有抽象方法,那么此类仍为一个抽象类
5、定义接口中的成员
- JDK7之前接口中只能定义全局常量和抽象方法
- 全局常量声明:由public static final 修饰的(一般可以省略)
- 抽象方法声明:由public abstract 修饰的(一般可以省略)
- JDK8之后接口中还能定义静态方法和默认方法
- 接口中定义的静态方法,只能通过接口来调用,采用“接口.方法”
- 通过实现类的对象,可以调用接口中的默认方法
- 如果实现类重写了接口中的默认方法,仍然调用的是重写后的方法
- 如果实现类继承的父类和实现的接口中声明了同名同参数的默认方法,那么实现类在没有重写此方法的时候,默认调用的是父类中的同名同参数的方法(类优先原则——仅针对方法)
- 如果实现类中实现了多个接口,而多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,会报错(接口冲突)。因此必须在实现类中重写此方法。
- 调用接口中的默认方法:接口名.super.方法名
6、java类可以实现多个接口(一定程度上弥补了单继承的局限性)
格式: class A extends B implements C,D,E{}
7、接口与接口之间可以继承 ,且可以多继承
格式:
interface A{
}
interface B{
}
interface C extends A,B{
}
8、接口的使用体现多态性
9、接口实际上可以看做是一种规范,定义的是一组规则,体现了现实世界中“如果你是/要...则 必须能...”的思想
接口和抽象类之间的对比
区别点 | 抽象类 | 方法 |
---|---|---|
定义 | 包含抽象方法的类 | 主要是抽象方法和全局常量的集合 |
组成 | 构造方法、抽象方法、普通方法、变量 | 常量、抽象方法(jdk8.0:默认方法、静态方法) |
使用 | 子类继承抽象类(extends) | 子类实现接口(implements) |
关系 | 抽象类可以实现多个接口 | 接口不能继承抽象类,但允许继承多个接口 |
常见设计模式 | 模板方法 | 简单工厂、工厂方法、代理方式 |
对象 | 可以通过对象的多态性产生实例对象 | |
局限 | 抽象类有单继承性 | 接口没有此局限 |
实际 | 作为一个模板 | 是作为一个标准或是一种能力 |
选择 | 如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局限 |
练习
1、改错
答案如下:
2、定义一个接口用来实现两个对象的比较。
interface CompareObject{
public int compareTo(Object o); //若返回值是 0 , 代表相等; 若为正数,代表当 前对象大;负数代表当前对象小
}
定义一个Circle类,声明redius属性,提供getter和setter方法
定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。在 ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。
定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo 方法比较两个类的半径大小。
- Circle类
public class Circle {
private double redius;
public double getRedius() {
return redius;
}
public void setRedius(double redius) {
this.redius = redius;
}
/**
* @param redius 半径
* 带参数构造器
*/
public Circle(double redius) {
super();
this.redius = redius;
}
/**
* 无参构造器
*/
public Circle() {
super();
}
}
- ComparableCircle类
interface ComparableObject{
public int compareTo(Object o);
}
public class ComparableCircle extends Circle implements ComparableObject {
/**
* 无参构造器
*/
public ComparableCircle() {
super();
}
/**
* @param redius 半径
*/
public ComparableCircle(double redius) {
super(redius);
}
/**
* 比较两圆半径大小,相等返回0,正数说明当前对象大,负数说明当前对象小
*/
@Override
public int compareTo(Object o) {
if(this==o) {
return 0;
}
if(o instanceof ComparableCircle) {
ComparableCircle o1=(ComparableCircle)o;
if(this.getRedius()>o1.getRedius()) {
return 1;
}else if(this.getRedius()==o1.getRedius()) {
return 0;
}else {
return -1;
}
}else {
return 0;
}
}
}
- InterfaceTest类
public class InterfaceTest {
public static void main(String[] args) {
ComparableCircle circle1=new ComparableCircle(3.6);
ComparableCircle circle2=new ComparableCircle(3.6);
int compareResult = circle1.compareTo(circle2);
System.out.println(compareResult);
}
}
测试结果如下:
三、内部类(Inner class)
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
Inner class的名字不能与包含他外部类的类名相同
内部类的分类
内部类可分为成员内部类(静态、非静态)和局部内部类(方法内、代码块内、构造器内)
成员内部类
1、成员内部类作为外部类成员
- 可以调用外部类的结构
- 可以被static修饰
- 可以被四种不同权限修饰符修饰(public、缺省、protected、private)
注意:
- 外部类访问成员内部类的成员,需要声明为“内部类.成员”或“内部类对象.成员”的方式
- 成员内部类可以直接使用外部类的所有成员,包括私有的数据
- 当向要在外部类的静态成员部分使用内部类时,可以考虑将内部类声明为静态的
2、成员内部类作为外部类的类
- 成员内部类内可以声明属性、方法、构造器
- 可以被final修饰、表示此类不能被继承;不被final修饰,则表示此类可以被继承
- 可以被abstract修饰,表示此类不能被实例化
局部内部类
1、局部内部类只能声明在方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
2、局部内部类的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父类的接口类型
总结
1、成员内部类和局部内部类,在编译后都能生成字节码文件
2、格式:
- 成员内部类:外部类$内部类.class
- 局部内类L外部类$数字 内部类.class
注意
1、在局部内部类中,如果调用局部内部类所声明的方法的局部变量时,要求此局部变量声明为final的
2、jdk7及7之前要求局部变量必须显示声明;jdk8及8以后可以省略final的声明