一.抽象类
1.定义
抽象类 = 普通类 + 抽象方法
抽象方法 = 声明却未实现的方法(没有方法体)
注意:
(1)没有方法体的方法不一定都是抽象方法哦,还有可能是本地方法;
(2)所有抽象方法要求使用abstract关键字来定义;抽象方法所在的类也必须使用abstract关键字来定义;
(3)抽象类中包含抽象方法,而抽象方法不包含方法体,即没有具体实现。因此抽象类不能直接产生实例化对象;
下面来看一个抽象类的例子:
//抽象水果类
abstract class Fruit{
private String name;//属性,水果名
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void getFruitInfo();//抽象方法
}
其实说到这里呢,我们会很发现,抽象类的最大特点是什么呢?就是假如说我们有很多水果类,苹果、芒果、火龙果...,它们都有水果名这个属性,那么都可以通过继承抽象水果类来获得父类的属性,也可以在此基础上扩展自己的特性;
来,看一个芒果
class mango extends Fruit{
@Override
public void getFruitInfo() {
System.out.println("Hello,我是芒小果");
}
}
看,抽象类是不是很棒呢。但是,Java中的继承是单继承哦,下面这种情况是不可以的。
2.抽象类使用原则
(1)所有抽象类必须有子类;
(2)抽象类的子类必须覆写抽象类的所有抽象方法(子类不是抽象类),方法覆写要注意权限问题;
(3)抽象类的对象可以通过对象多态性利用子类为其实例化;
(4)private和abstract不能一起同时使用;
呐,一个经典的例子(在我心里是经典哦):
abstract class Person{
private String name;//属性
//普通方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//抽象方法
public abstract void getPersonInfo();
}
class Student extends Person{
@Override
public void getPersonInfo() {
System.out.println("I'm a student");
}
}
public class Test {
public static void main(String[] args) {
Person person = new Student();//向上转型
person.getPersonInfo();//子类覆写的方法
}
}
输出:
经典吧,哈哈哈,对,我就是一个小学生
3.抽象类的相关规定
(1)抽象类只是比普通类多了一些抽象方法而已;
抽象类中也有构造方法哦,并且子类也照样遵循对象实例化流程。实例化子类时一定先调用父类构造方法。
栗子:在抽象类中定义构造方法
/**
* @Author:Star
* @Date:Created in 15:55 2019/10/14
* @Description:
*/
abstract class Person{
private String name;//属性
public Person() {//构造方法
System.out.println("***Person***");
}
//普通方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//抽象方法
public abstract void getPersonInfo();
}
class Student extends Person{
public Student() {//构造方法
System.out.println("###Student###");
}
@Override
public void getPersonInfo() {
System.out.println("I'm a student");
}
}
public class Test {
public static void main(String[] args) {
new Student();
}
}
输出:
另外,补充一点点,如果父类没有无参构造,那么子类构造必须使用super明确指出使用父类哪个构造方法。
栗子:
/**
* @Author:Star
* @Date:Created in 16:22 2019/10/14
* @Description:
*/
abstract class A{
public A(){//3.调用父类构造
this.print();//4.调用被子类覆写的方法
}
public abstract void print();
}
class B extends A{
private int num = 100;
public B(int num){//2.调用子类实例化对象
super();//3.隐含的一行语句,实际要先调用父类构造
this.num = num;//7.为类中属性初始化
}
@Override
public void print() {//5.此时子类对象的属性还没有被初始化
System.out.println(this.num);//6.对应其数据类型的默认值
}
}
public class Test01 {
public static void main(String[] args) {
new B(30);//1.实例化子类对象
}
}
程序的执行顺序就是按照1,2,...,7来执行的,所以要注意哦,即输出为0(对象属性对应数据类型的默认值);
输出:
(2)抽象类可以不定义任何抽象方法,但此时仍然无法直接实例化对象;
(3)final与abstract不能同时出现;private与abstract也不能同时出现;
抽象类一定不能使用final声明,因为使用final声明的类不允许有子类;而抽象类必须有子类;相应的,抽象方法也不能使用private定义,因为抽象方法必须要能被覆写。
(4)抽象类也分为外部抽象类与内部抽象类。内部抽象类中也可以使用static定义来描述外部抽象类;
内部抽象类的抽象方法与外部抽象类的抽象方法无关。
当前直接继承哪个抽象类,就覆写其抽象方法(若直接继承外部抽象类,则只需要覆写外部抽象类的所有抽象方法即可
栗子:内部抽象类
abstract class A{
public abstract void printA();
abstract class B{
public abstract void printB();
}
}
class X extends A{
public void printA(){}
class Y extends B{
public void printB(){}
}
}
如果在外部抽象类中使用了static那就是语法错误,但是内部抽象类允许使用static;
栗子:使用了static的内部抽象类
abstract class A{
public abstract void printA();
static abstract class B{
public abstract void printB();
}
}
class X extends A,B{
public void printB(){}
}
二.接口
1.定义
接口 = 抽象方法 + 全局变量(JDK8以前)
栗子:接口定义
interface IFruit{
public static final String name = "水果";//全局变量
public abstract void print();//抽象方法
}
注意:
(1)接口优先原则;即可使用接口又可使用抽象类时,优先考虑使用接口
(2)使用interface定义接口,为了区分接口,在所有接口前面追加字母I;
(3)子类实现接口,使用implements关键字,并且子类可以同时实现多个接口(接口多继承);
(4)子类必须覆写所有抽象方法。子类命名一般使用Impl结尾;
(5)多个接口若有共同子类,可以通过子类进行相互转换(父接口之间的相互转换)-new在哪
栗子:子类实现接口和父类接口之间的转换
/**
* @Author:Star
* @Date:Created in 17:18 2019/10/14
* @Description:
*/
interface IFruit{
public static final String name = "水果";//全局变量
public abstract void print();//抽象方法
}
interface IApple{
public abstract String getApple();
}
class bigAppleImpl implements IFruit,IApple{
@Override
public void print() {
System.out.println(IFruit.name);
}
@Override
public String getApple() {
return IFruit.name;
}
}
public class Test02 {
public static void main(String[] args) {
IFruit f = new bigAppleImpl();//向上转型,为父接口实例化对象
f.print();
IApple a = (IApple) f;
System.out.println(a.getApple());
}
}
输出:
在上面的程序中,IFruit,IApple两个接口可以通过它们共同的子类相互转换;(嘘...可能这个栗子不是很形象,但是基本内容已经显示出来啦)
2.接口的使用原则
(1)接口中只允许public权限;(不管是属性还是方法,其权限都是public)
在接口中,public、static、final、abstract均可以省掉不写,抽象类中一个也别想跑;
规范:接口中的属性与方法不要加任何修饰符,public也不写,保持代码的简洁性;(关于这个规范,最终解释权是:好像忘记在哪看的了,应该是阿里的编码规约)
(2)当一个子类即需要实现接口又需要继承抽象类时,先使用extends继承一个抽象类,而后使用implements实现多个接口;
栗子应该是这样的:
/**
* @Author:Star
* @Date:Created in 17:47 2019/10/14
* @Description:
*/
//接口
interface IMessage{
public void print();
}
//抽象类
abstract class News{
public abstract void getNews();
}
class MessageImpl extends News implements IMessage{
@Override
public void print() {
System.out.println("message~~~");
}
@Override
public void getNews() {
System.out.println("news~~~");
}
}
public class Test03 {
public static void main(String[] args) {
IMessage message = new MessageImpl();
message.print();
News news = (News) message;
news.getNews();
}
}
输出:
(3)一个接口可以使用extends继承多个接口;
再给一个栗子哦:
interface A{
void printA();
}
interface B{
void printB();
}
interface C extends A,B{
void printC();
}
class Impl implements C{
public void printA(){}
public void printB(){}
public void printC(){}
}
(4)抽象类可以使用implements实现多个接口,相反,接口无法继承抽象类;(接口是更纯粹的抽象类)
(5)接口可以定义一系列的内部结构,包括:内部普通类,内部接口;其中,使用static定义的内部接口就相当于一个外部接口;
看,这颗栗子:
interface A{
void printA();
static interface B{
void printB();//使用static定义,描述一个外部接口
}
}
3.接口的应用
(1)定义标准(USB)
(2)表示能力(行为)(购买商品)
(3)在分布式开发中暴露远程服务方法
三.抽象类与接口的区别
其实,抽象类与普通类相比最大的特点是约定了子类的实现要求,但是抽象类存在单继承局限。如果要约定子类的实现要求并避免单继承局限就需要使用接口。
哈哈哈,有好多栗子啊,好饿,糖炒栗子和这个更配哦。
一起加油,一起进步(耶耶耶)