系列文章目录
前言
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。
一、抽象(abstract)类
抽象类,说白了就是包含抽象方法的类。那什么是抽象方法?抽象方法是一种特殊的方法:抽象方法只有声明,而没有具体的实现。抽象方法说白了就是只有方法的声明,没有方法体。
抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。因为抽象类中含有无具体实现的方法,所以不能用抽象类创建对象。
上图Cat必须实现抽象方法run(),如果不实现,编译通不过。可以选择实现run()方法或者定义Cat为抽象类。
完整截图:
- 为什么需要抽象类?如何定义抽象类?
是一种模版模式。抽象类为所有子类提供了一个通用模板。子类可以在这个模版基础上进行扩展。
通过抽象类,可以避免子类设计的随意性。通过抽象类,我们可以做到严格限制子类的设计,使子类之间更加通用。 - 抽象类要点
有抽象方法的类只能定义为抽象类;只要包含一个抽象方法的抽象类,该类必须要定义为抽象类,不管是否还包含其他方法;
抽象类中可以包含普通方法(有具体实现的方法),当然也可以不包含抽象方法;
抽象类不能被实例化,即不能用new来实例化抽象类;
抽象类可以包含属性、方法、构造方法。但是构造方法不能用new来实例,只能用来被子类调用;
抽象类只能用来被继承;
抽象方法必须被子类实现,即由子类来进行重写。(不实现的话,编译器会报错)
【示例】
下面通过一下的小程序深入理解抽象类
因此在类Animal里面只需要定义这个enjoy()方法就可以了,使用abstract关键字把enjoy()方法定义成一个抽象方法,定义如下:public abstract void enjoy();
从某种意义上来说,抽象方法就是被用来重写的,所以在父类声明的抽象方法一定要在子类里面重写。如果真的不想在子类里面重写这个方法,那么可以再在子类里面把这个方法再定义为抽象方法,因为子类觉得我去实现也不合适,应该让继承我的子类去实现比较合适,因此也可以在继承这个子类的下一个子类里面重写在父类里面声明的抽象方法,这是可以的。
这里有一个规则:既然父类里面的方法是抽象的,那么对于整个类来说,它就有一个没有实现的方法,这个方法不知道怎么去实现,那么这个类是就是残缺不全的,因此这个类应该被定义为一个抽象类。所以前面这样声明的声明的class Animal应该要在class的前面加上abstract,即声明成这样:abstract class Animal,这样Animal类就成了一个抽象类了。Animal类的最终定义代码如下:
/**
* 父类Animal
* 在class的前面加上abstract,即声明成这样:abstract class Animal
* 这样Animal类就成了一个抽象类了
*/
abstract class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
/**
* 抽象方法
* 这里只有方法的定义,没有方法的实现。
*/
public abstract void enjoy();
}
Java语言规定,当一个类里面有抽象方法的时候,这个类必须被声明为抽象类。
子类继承父类时,如果这个父类里面有抽象方法,并且子类觉得可以去实现父类的所有抽象方法,那么子类必须去实现父类的所有抽象方法,如:
/**
* 子类Dog继承抽象类Animal,并且实现了抽象方法enjoy
* @author gacl
*
*/
class Dog extends Animal {
/**
* Dog类添加自己特有的属性
*/
public String furColor;
public Dog(String n, String c) {
super(n);//调用父类Animal的构造方法
this.furColor = c;
}
@Override
public void enjoy() {
System.out.println("狗叫....");
}
}
这个父类里面的抽象方法,子类如果觉得实现不了,那么把就子类也声明成一个抽象类,如:
/**
* 这里的子类Cat从抽象类Animal继承下来,自然也继承了Animal类里面声明的抽象方法enjoy(),
* 但子类Cat觉得自己去实现这个enjoy()方法也不合适,因此它把它自己也声明成一个抽象的类,
* 那么,谁去实现这个抽象的enjoy方法,谁继承了子类,那谁就去实现这个抽象方法enjoy()。
* @author gacl
*
*/
abstract class Cat extends Animal {
/**
* Cat添加自己独有的属性
*/
public String eyeColor;
public Cat(String n, String c) {
super(n);//调用父类Animal的构造方法
this.eyeColor = c;
}
}
这里的子类Cat从抽象类Animal继承下来,自然也继承了Animal类里面声明的抽象方法enjoy(),但子类Cat觉得自己去实现这个enjoy()方法也不合适,因此它把它自己也声明成一个抽象的类,那么,谁去实现这个抽象的enjoy方法,谁继承了子类,那谁就去实现这个抽象方法enjoy()。如:
/**
* 子类BlueCat继承抽象类Cat,并且实现了从父类Cat继承下来的抽象方法enjoy
* @author gacl
*
*/
class BlueCat extends Cat {
public BlueCat(String n, String c) {
super(n, c);
}
/**
* 实现了抽象方法enjoy
*/
@Override
public void enjoy() {
System.out.println("蓝猫叫...");
}
}
完整的测试代码如下:
二、接口(interface)
接口,比抽象类还要抽象的类。
如:接口MyInterface
实现类:MyClass
-
为什么需要接口?接口和抽象类的区别?
接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了“规范和具体实现的分离。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是……则必须能……”的思想。(如果你是天使,则必须能飞,如果你是汽车,则必须能跑……)
接口的本质是契约,就像我们人间的法律一样,制定好后大家都遵守。
项目的具体需求多变,我们必须以不变应万变才能从容开发,此处的不变就是规范。因此,我们开发项目往往都是面向接口编程! -
如何定义接口?
格式: -
接口要点
子类通过implements来实现接口中的规范;
接口不能创建实例,但是可以用于声明引用变量类型;
一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public;
接口支持多继承。
接口和接口之间可以相互继承,类和类之间可以相互继承,类和接口之间,只能是类来实现接口。
JAVA是只支持单继承的,但现实之中存在多重继承这种现象,如“金丝猴是一种动物”,金丝猴从动物这个类继承,同时“金丝猴是一种值钱的东西”,金丝猴从“值钱的东西”这个类继承,同时“金丝猴是一种应该受到保护的东西”,金丝猴从“应该受到保护的东西”这个类继承。这样金丝猴可以同时从 “动物类”、“值钱的东西类”、“应该受到保护的东西” 这三个类继承,但由于JAVA只支持单继承,因此金丝猴只能从这三个类中的一个来继承,不能同时继承这三个类。因此为了封装现实生活中存在的多重继承现象,为了实现多继承,可以把其中的两个类封装成接口。使用接口可以帮助我们实现多重继承。
接口(interface)是一种特殊的抽象类,在这种抽象类里面,所有的方法都是抽象方法,并且这个抽象类的属性(即成员变量)都是声明成“public static final 类型 属性名”这样的,默认也是声明成“public static final”即里面的成员变量都是公共的、静态的,不能改变的。因此在接口里面声明常量的时候,可以写成“public static final 类型 常量名=value(值)”这样的形式,也可以直接写成“类型 常量名=value(值)”如:“public static final int id=10”可以直接写成“int id=10”这样的形式,因为在接口里面默认的属性声明都是“public static final”的,因此“public static final”可以省略不写。在接口里面声明的抽象方法可以不写abstract关键字来标识,因为接口里面所有的方法都是抽象的,因此这个“abstract”关键字默认都是省略掉的,如在一个接口里面声明这样的三个方法:“public void start()”、“public void run()”、“public void stop()”这三个方法前面都没有使用abstract关键字来标识,可它们就是抽象方法,因为在接口里面的声明的方法都是抽象方法,因此在接口里面的抽象方法都会把abstract关键字省略掉,因为默认声明的方法都是抽象的,所以就没有必要再写“abstract”字了,这一点与在抽象类里面声明抽象方法时有所区别,在抽象类里面声明抽象方法是一定要使用“abstract”关键字的,而在接口里面声明抽象方法可以省略掉“abstract”。注意:在接口里面声明的抽象方法默认是“public(公共的)”的,也只能是“public(公共的)”之所以要这样声明是为了修正C++里面多重继承的时候容易出现问题的地方,C++的多继承容易出现问题,问题在于多继承的多个父类之间如果他们有相同的成员变量的时候,这个引用起来会相当地麻烦,并且运行的时候会产生各种各样的问题。JAVA为了修正这个问题,把接口里面所有的成员变量全都改成static final,成员变量是static类型,那么这个成员变量就是属于整个类里面的,而不是专属于某个对象。对于多重继承来说,在一个子类对象里面实际上包含有多个父类对象,而对于单继承来说,子类对象里面就只有一个父类对象。多继承子类对象就有多个父类对象,而这些父类对象之间可能又会存在有重复的成员变量,这就非常容易出现问题,因此在JAVA里面避免了这种问题的出现,采用了接口这种方式来实现多继承。作为接口来说,一个类可以从接口继承(或者叫实现接口),这也是多继承,接口里面的成员变量不专属于某个对象,都是静态的成员变量,是属于整个类的,因此一个类去实现多个接口也是无所谓的,不会存在对象之间互相冲突的问题。实现多个接口,也就实现了多重继承,而且又避免了多重继承容易出现问题的地方,这就是用接口实现多重继承的好处。