一篇文章让你理解Java的抽象类和接口

我:冉妹子,你知道一个方法两种实现怎么写吗?比如说你要吃面包,而我却想吃面条,你该怎么去编写这个代码呢?

冉妹子:这还不简单吗?看我的

public class A {
	public void eat() {
		System.out.println("吃面包");
	}
}
public class B {
	public void eat() {
		System.out.println("吃面条");
	}
}
public class TestMain {
	public static void main(String[] args) {
		A a = new A();
		a.eat();
		B b = new B();
		b.eat();
	}
}

这样不就行了么?

我:嗯?那又来一个人,想吃米饭呢?

冉妹子:这有啥难得?我再创建一个类,再加一个方法不就行了。

我:那再来很多人呢?

冉妹子:屁事真多,吃空气去吧。。。

我:额。。。。。。今天说个新东西吧---接口

冉妹子:好像听说过这个东西,那你讲讲吧!

我:创建一个通用的接口十分有用,可以让不同的子类用不同的方式去实现这个接口。

  • 基本语法

接口和类的写法类似,只需要将 class 换成 interface就行了。像这样:

public interface People {
}

当然,在了解接口之前,你需要知道什么是抽象类和抽象方法;包含抽象方法的类叫做抽象类。

一、抽象类(抽象方法)

写法是这样的:

public abstract class People {

}

抽象方法的写法是这样的:

public abstract void eat();

注意,这个方法和普通方法的区别在哪?

冉妹子:没有大阔号!是分号结尾

我:对的,没大括号,这个没有大括号,只有分号的,而且用了abstract修饰的方法,我们成为抽象方法。这种方法是没有具体的方法实现的。

冉妹子:没有具体的实现,要这个方法有什么用呢?

我:你想想继承,我们可以用一个类来继承他吗?

public class Student extends People{
	@Override
	public void eat() {
		System.out.println("吃面包");
	}

}

你看,就像这个样子(@Override是一个注解,表示重写父类的方法,如果和父类的方法表述有改变,则编译器会报错),当来了一个新人,只需要去增加一个People的子类,去实现这个抽象方法即可。

冉妹子:。。。额,这么做,不是更麻烦,还多了一个父类,类不更多吗?还没我写的简单呢吧!

我:别慌呀,我们再增加一个子类,来看:

public class Student1 extends People{

	@Override
	public void eat() {
		System.out.println("吃面条");
		
	}
}

这样就有了两个子类,如果用原始的方法来创建这两个子类时,你就需要具体的考虑类的具体类型,并且去知道类中的细节,使用继承抽象类,就没这样的烦恼了,你只需要知道抽象类就行了

public class PeopleMain {
	public static void creatPeople(People p) {
		p.eat();
	}
	public static void main(String[] args) {
		creatPeople(new Student());
		creatPeople(new Student1());
	}
}

像这样设计,你只需要在方法中 new 一个子类,就行了,这种实现依赖于向上转型,子类可以自动转换为父类,而p.eat(),这个方法的调用,编译器之所以能清楚的知道调用的哪个类中的方法,依赖于多态这个特性

这么做,是不是就简单了呢?不用再new一个对象,再调用一次方法了吧!

冉妹子:好像是这么回事,如果有成百上千个类,我就要调用成百上千次的方法,那还不得难受死我!

我:这也是抽象的好处,实际上就是让你不去在意方法的实现细节;比如,我说:你快给我洗脚。我不用在乎你去哪里找的盆子,去哪里接的水,只需要发出命令即可!

冉妹子:还没睡醒,搁着做白日梦呢?

我:额。。。再说两个规则:

  1. 抽象类中可以有普通的方法(不带abstract修饰的方法,并含有方法具体的实现)
  2. 普通类中不能含有抽象方法(只要是有抽象方法,那么这个类也一定要是抽象的)

抽象类大致了解了一下,我们继续说接口。

二、接口

接口使得抽象的概念更向前,抽象类中你可以写个普通方法,可以有具体的实现,但是在接口中没有提供任何形式的具体实现(jdk1.8以后,接口中可以有方法体了),即接口提供了形式,未提供行为。

像上面我们定义的那样,接口的方法定义与之相似:

public interface People {
	void eat();
}

接口的方法中不必要给出权限修饰符,默认就是public,这和类的default有所不同,而 abstract 修饰符也是接口方法中默认拥有的

实现接口的基本语法:

public class Student implements People{
	@Override
	public void eat() {
		System.out.println("吃面包");
	}
}

只需要把 extends 关键字换成 implements 即可。

冉妹子:这么说,抽象类可以代替接口了?我在抽象类中不写具体的方法实现,不也是相当于一个接口吗?

我:额。。。。类可以多继承吗?

冉妹子:不行。。。啊?难道接口可以?

我:嗯,嗯 可以多继承。

演示一下,再来一个接口

public interface Man {
	void drink();

}
public interface A extends People,Man{
	
}

像这样,同事继承了People 和Man 两个接口;同时,接口也可以多实现,像这样:

public class Student implements People,Man{
	@Override
	public void eat() {
		System.out.println("吃面包");
	}

	@Override
	public void drink() {
		System.out.println("喝水");	
	}
}

多实现时,就像这样写就行了。所以在一些特殊场景,还是要选择正确的抽象方式才行。不能盲目崇拜抽象类或接口。

冉妹子:好像懂了,接口的优点也是只用给外部提供一个方法,不让用户看到方法中的具体实现细节,原来你先说抽象类是因为这呀!

我:嗯~孺子可教也。理解的还挺快的。你可以使用接口,来编写复用性更好的代码

冉妹子:戚,小垃圾,类中可以有各种字段,比如基本数据类型的属性等等,接口中有吗?

我:当然也有了,不过跟类中的有所不同

  • 接口中的域

接口中的所有域,自动都是 static 和 final 的(即常量),所以接口就能很便捷的来创建常量,当然,想要创建常量,枚举是一种不错的选择。

注意:常量的命名一般都为大写字母,单词与单词之间使用下划线 " _ " 隔开。

和类中的static修饰的变量没区别---只会在第一次加载时初始化一次。

  • 接口嵌套

接口还有一个很有趣的特性,他可以嵌套,比如:

public interface People {
	public interface People1 {
		void learn();
	}
	void eat();
}

而你要去实现接口 People1时,只需要这么写就行了

public class Student implements People.People1{
	@Override
	public void learn() {
		
	}
}

冉妹子:额,不行了不行了,再听下去,脑袋就要爆炸了。

我:额,那就到此为止吧。来吧,给我洗个脚!

冉妹子:年轻人,我劝你耗子尾汁。

三、总结:

接口和抽象类虽然是很理想的选择,但是并不是无时无刻都要使用这种方式来创建类,这是一种错误的想法,任何的抽象,都应该有具体的需求才是最好的,盲目的抽象,只会加重程序的负担,必要时,你应该重构接口,而不是随时的去添加额外的间接性。合理利用接口和抽象,才能让程序变得更加优秀。

如有错误,欢迎指出,一定及时改正!

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值