文章目录
一、抽象类(abstract)
1、抽象类的基本概念
在Java中,普通的类是一个完善的功能类,使用class关键字修饰,可以直接通过 new 关键字去实例化类的对象,并且在普通类中可以包含有构造方法、普通方法、static方法、常量和变量等内容。
而抽象类是指在普通类原有的基础上添加 abstract 关键字进行声明,在普通类原本的结构里面增加抽象方法的组成部分,而拥有抽象方法的类就是抽象类。
那么什么叫抽象方法呢?在以往的普通类中定义的方法通常都有"{}",这个大括号便是方法的方法体,通常会在方法体中去定义一些代码来实现一些特定的功能。而在实例化对象之后,对象便可以直接使用类中的方法。而抽象方法并没有"{}"方法体,而是直接使用 " ; "结尾,所以说抽象方法便是没有方法体的方法。
抽象类Demo代码展示:
public abstract class A{//定义一个抽象类
public void fun(){//普通方法
System.out.println("存在方法体的方法");
}
public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
}
2、抽象类的使用
1、抽象类是否可以实例化?
abstract class A {
public void fun(){//普通方法
System.out.println("存在方法体的方法");
}
public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
}
class B{
public static void main(String[] args) {
A a = new A();//此时已进入编译器异常
}
}
通过实例化抽象类可知,抽象类是无法实例化的。因为在实例化之后,类对象是可以调用类中的属性和方法的,但是抽象类中存在这抽象方法,抽象方法并没有方法体,而没有方法体便无法调用该方法,也就无法产生实例化对象。
2、抽象类的使用原则
- 抽象类的使用原则如下:
- 抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法,就失去了定义抽象方法的意义),缺省情况下默认为public;
- 抽象类不可以使用final关键字修饰,因为抽象类必须要有子类,但是final不能有子类,两者相互冲突。
- 抽象类不能直接实例化,需要依靠子类采用向上转型的方式处理;
- 抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类;
- 在继承抽象类时,子类是普通类的情况下,必须覆写抽象类中所有的抽象方法。如果子类是抽象类便不需要全部覆写
Demo代码示例:
abstract class A{//定义一个抽象类
public void fun(){//普通方法
System.out.println("存在方法体的方法");
}
public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
}
//单继承
class B extends A{//B类是抽象类的子类,是一个普通类
@Override
public void print() {//强制要求覆写
System.out.println("Hello World !");
}
}
public class TestDemo {
public static void main(String[] args) {
A a = new B();//向上转型
a.print();//被子类所覆写的过的方法
}
}
运行结果:
- 现在就可以清楚的发现:
- (1)抽象类继承子类里面有明确的方法覆写要求,而普通类之间的继承关系中可以有选择性覆写需要的方法。
- (2)抽象类实际上就比普通类多了一些抽象方法而已,其他组成部分和普通类完全一样;
- (3)普通类对象可以直接实例化,但抽象类的对象必须经过向上转型之后才可以得到。
虽然一个类的子类可以去继承任意的一个普通类,可是从开发的实际要求来讲,普通类尽量不要去继承另外一个普通类,而是去继承抽象类。
3、抽象类中是否存在构造方法?
Demo代码示例:
abstract class A{//定义一个抽象类
public A(){
System.out.println("*****A类构造方法*****");
}
public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
}
//单继承
class B extends A{//B类是抽象类的子类,是一个普通类
public B(){
System.out.println("*****B类构造方法*****");
}
@Override
public void print() {//强制要求覆写
System.out.println("Hello World !");
}
}
class TestDemo {
public static void main(String[] args) {
A a = new B();//向上转型
}
}
运行结果:
通过以上运行结果可以看出抽象类是存在构造方法的。
因为抽象类中也可以定义一些普通的方法和属性,那么抽象类中便一定存在构造方法,其存在目的是为了属性的初始化以及方法的调用。
并且子类对象实例化的时候,依然满足先执行父类构造,再执行子类构造的顺序。
4、抽象类使用static 声明的场景
- 抽象外部类使用static声明场景
抽象外部类使用static声明时,直接进入编译期异常,所以抽象外部类不可以使用static关键字声明。
- 抽象内部类使用static声明场景
Demo代码示例:
abstract class A{//定义一个抽象类
static abstract class B{//static定义的内部类属于外部类
public abstract void print();
}
}
class C extends A.B{
public void print(){
System.out.println("**********");
}
}
public class TestDemo {
public static void main(String[] args) {
A.B ab = new C();//向上转型
ab.print();
}
}
由此可见,外部抽象类不允许使用static声明,而内部的抽象类运行使用static声明。使用static声明的内部抽象类相当于一个外部抽象类,继承的时候使用“外部类.内部类”的形式表示类名称。
注:任何时候,如果要执行类中的static方法的时候,都可以在没有对象的情况下直接用类名.方法的形式调用,对于抽象类也一样。
Demo代码示例:
abstract class A{//定义一个抽象类
static abstract class B{//static定义的内部类属于外部类
public abstract void print();
public static void show(){
System.out.println("show");
}
}
}
class C extends A.B{
@Override
public void print() {
System.out.println("覆写了B类的抽象方法");
}
}
class TestDemo {
public static void main(String[] args) {
/*A.B ab = new C();//向上转型
ab.print();*/
A.B.show();
}
}
3、 抽象类的应用—模板设计模式
现要求实现一个程序,可以实现三种不同事物的行为。
- 例如,现在有三类事物:
- (1)机器人:充电,工作;
- (2)人:吃饭,工作,睡觉;
- (3)猪:进食,睡觉。
1、首先先定义一个抽象行为类
public abstract class Action{
//定义全局变量
public static final int EAT = 1 ;
public static final int SLEEP = 3 ;
public static final int WORK = 5 ;
public abstract void eat();
public abstract void sleep();
public abstract void work();
public void commond(int flags){
switch(flags){
case EAT:
this.eat();
break;
case SLEEP:
this.sleep();
break;
case WORK:
this.work();
break;
default:
break;
}
}
}
2、定义一个机器人的类:
class Robot extends Action{
@Override
public void eat() {
System.out.println("机器人充电");
}
@Override
public void sleep() {
}
@Override
public void work() {
System.out.println("机器人工作");
}
}
3、定义一个人的类:
class Human extends Action{
@Override
public void eat() {
System.out.println("人吃饭");
}
@Override
public void sleep() {
System.out.println("人睡觉");
}
@Override
public void work() {
System.out.println("人工作");
}
}
4、定义一个猪的类:
class Pig extends Action{
@Override
public void eat() {
System.out.println("猪进食");
}
@Override
public void sleep() {
System.out.println("猪睡觉");
}
@Override
public void work() {
}
}
5、定义测试实现类:
public class TestDemo {
public static void main(String[] args) {
fun(new Robot());
fun(new Human());
fun(new Pig());
}
//创建fun() ,方法的参数类型是Action抽象类,参数是Action抽象类对象
public static void fun(Action action){
action.commond(Action.EAT);
action.commond(Action.SLEEP);
action.commond(Action.WORK);
}
}
所有继承抽象类的子类如果要想正常的完成操作,必须按照指定的方法进行覆写才可以,而这个时候抽象类所起的功能就是一个类定义模板的功能。
二、接口(interface)
1、接口的基本概念
-
接口由关键字 interface 修饰
- 格式:public interface 接口名 {}
- 格式:public interface 接口名 {}
-
类与类之间的关系是继承,类与接口之间的关系是实现。实现接口时使用的是 implements 关键字
- 格式:class 类名 implements 接口名 {}
- 格式:class 类名 implements 接口名 {}
-
想要使用接口,便需要将接口实现,并且接口本身是无法实例化的,但是可以实例化接口的实现类
-
接口中有且只有抽象方法,没有其他方法。
- 并且其中的抽象方法都有默认的修饰符:public abstract(虽然不写也没问题,但还是建议手动将修饰符补全,不然看着别扭…)
- 并且其中的抽象方法都有默认的修饰符:public abstract(虽然不写也没问题,但还是建议手动将修饰符补全,不然看着别扭…)
-
因为接口主要作用是对功能进行扩展,其中只有抽象方法,没有具体的实现,所有接口中也没有构造方法。
接口实现,Demo代码示例:
定义有增删改查方法的接口
public interface UserService {
//错误: 需要<标识符>,接口没有构造方法
//public UserService() {}
//接口方法不能带有方法体
//public void show() {}
void add();//添加
void del();//删除
void up();//修改
void query();//查询
}
定义接口的实现类
//实现 UserService 接口
public class UserServiceImpl implements UserService{
//重写UserService 接口中所有的抽象方法
@Override
public void add() {
System.out.println("add");
}
@Override
public void del() {
}
@Override
public void up() {
}
@Override
public void query() {
}
}
定义测试类
public class Demo01 {
public static void main(String[] args) {
UserService user = new UserServiceImpl();
user.add();
}
}
运行结果:
接口中定义常量
- 接口中的成员变量只能是常量,且都为静态,并且必须赋值。
- 默认修饰符:public static final(可以不写,但是建议补全…)
- 接口中常量名称,使用完全大写的字母,下划线分割。(推荐)
Demo代码示例:
public interface MyInterfaceConst {
//就是一个常量,一旦赋值,不可以修改
public static final int NUM_OF_MY_CLASS=10;
}
--------------------------------------------------
public class DemoInterface {
public static void main(String[] args) {
//只能使用不可修改
System.out.println( MyInterfaceConst.NUM_OF_MY_CLASS);
}
}
注:接口中定义的常量应为所有类频繁使用的常量,但并不是每个类都使用了接口中定义的所有常量,因而导入了不必要的常量到这个类中,并且导入后这个类的子类也会基础导入的常量,这样会导致混乱,应当避免此种用法。
2、接口与类,接口与接口之间的关系
- 类与接口是实现关系
- 一个类可以实现一个接口,也可以同时实现多个接口
- 一个类在继承一个类的同时,还可以实现单个或者多个接口
- 接口与接口之间是继承关系,可以单继承,也可以多继承
- 但与抽象类实现接口一样无需重写接口中的抽象方法,意义不大
Demo代码示例:
//接口一
interface Father{
public abstract void show();
}
----------------------------------------------------
//接口二
interface Mother {
public abstract void show2();
}
-------------------------------------------------------
//接口三
interface Sister extends Father,Mother {
}
-------------------------------------------------------
//创建 Son 类 ,继承 Object ,同时实现多个接口
public class Son extends Object implements Father,Mother {
//重写Father接口中的抽象方法
public void show() {
System.out.println("show son");
}
//重写Mother 接口中的抽象方法
public void show2() {
System.out.println("show2 son");
}
}
public class TestInterfaceDemo {
public static void main(String[] args) {
//创建对象
Father f = new Son();
f.show();
//f.show2(); //报错
Mother m = new Son();
//m.show(); //报错
m.show2();
}
}
输出结果:
show2 son
show son
三、抽象类和接口的区别
-
成员区别
- 抽象类:
- 成员变量:可以变量,也可以常量
- 构造方法:有
- 成员方法:可以有抽象方法,也可以有普通方法
- 接口:
- 成员变量:只可以常量,且不可修改
- 成员方法:只有抽象方法
- 抽象类: