什么是抽象类?抽象类的定义是这样的
Java语言中,用abstract 关键字来修饰一个类时,这个类叫作抽象类。抽象类是它的所有子类的公共属性的集合,是包含一个或多个抽象方法的类。抽象类可以看作是对类的进一步抽象。
我们可以理解为,抽象类是对普通类的进一步抽象化,什么叫进一步的抽象?举个例子:
abstract class Animal{
abstract public void play();
abstract public void eat();
abstract public void sleep();
}
class Panda extends Animal{
@Override
public void play() {
System.out.println("熊猫在打滚");
}
@Override
public void eat() {
System.out.println("熊猫吃竹子");
}
@Override
public void sleep() {
System.out.println("熊猫在竹林里睡觉");
}
}
class Cat extends Animal{
@Override
public void play() {
System.out.println("猫喜欢和老鼠玩游戏");
}
@Override
public void eat() {
System.out.println("猫喜欢吃鱼");
}
@Override
public void sleep() {
System.out.println("猫睡在猫窝里");
}
}
public class Demo2 {
public static void main(String[] args) {
Panda panda = new Panda();
panda.play();
panda.eat();
panda.sleep();
Cat cat = new Cat();
cat.play();
cat.eat();
cat.sleep();
}
}
在这段代码中,我们将一个Animal类定义为一个抽象类,在抽象类中我们只定义了几个抽象方法,却没有具体的实现这些方法。当我们去定义Panda和Cat类并继承这个抽象类时。我们在这两个子类中去实现了它们的父类的抽象方法。对于不同的类,这些抽象方法在每个类的具体实现是不一样的。而作为一个父类,只是将它的子类某些共同的行为(功能)抽调出来,这样当以后再有一个狗类,一个猪类…等这些类时,我们同样不需要去自己定义方法,只需要继承抽象类的方法,再去实现即可。这样就是抽象类存在的意义,即使用抽象类可以更好地利用这些共同属性和操作,避免代码的重复编写,从而减少出错的几率,提高程序的开发效率。
以上代码的执行结果:
注意:
- 如果一个类中用abstract修饰的话,这个类就叫做抽象类
- 抽象类中可以有普通方法
- 抽象方法没有方法体
- 抽象类不可以被实例化
- 写一个普通类去继承抽象类
- 在子类中必须要去实现(重写抽象类中的抽象方法)
- 抽象类中的普通方法可以不用重写
- 抽象类可以继承抽象类
- 抽象类可以继承非抽象类
什么是Java接口?Java接口的定义是这样的
Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
通读以上定义,不难看出,接口也是体现Java多态的表现形式之一。仅仅从定义上我们可能很难区分抽象类和方法的区别,但两者有一个最大的区别在于:
抽象类中可以定义一个非抽象方法,给出具体的实现结果,但接口不行。接口不可以定义非抽象方法;只要接口定义了一个抽象方法,那实现这个接口的每一个实现类,都必须去实现这个方法,否则编译就会报错。
相比于抽象类,接口更加抽象一点,因为它不允许自己定义一个非抽象方法。
下边看一个案例:
package com.lzl.day011;
interface Test1{
String[] getChar(String s);
}
interface Test2{
void print(String[] strings);
}
class GetChar implements Test1,Test2{
@Override
public String[] getChar(String s) {
String[] strings = s.split("");
return strings;
}
@Override
public void print(String[] strings) {
for (String s : strings) {
System.out.println(s);
}
}
}
class Reverse implements Test2{
@Override
public void print(String[] strings) {
String s = "";
for (int i = strings.length - 1; i >= 0; i--) {
s += strings[i];
}
System.out.println(s);
}
}
public class Demo4 {
public static void main(String[] args) {
GetChar get = new GetChar();
String[] strings = get.getChar("一板一眼,就会滋生弱点");
get.print(strings);
Reverse reverse = new Reverse();
reverse.print(strings);
}
}
在上边的代码中,我们定义了两个接口Test1,Test2,并且有两个类。GetChar实现了两个接口,而Reverse 只实现了Test2。虽然在接口中定义的抽象方法名和返回值类型,参数列表都是一样的,但我们可以在实现类中根据不同的需求,去编写不同的方法体。以后只要有类需要这些接口中定义的接口,我们就可以去实现对应的接口。
相对于类的单继承来说,接口的优势在于,一个类可以实现多个接口,只要把这些接口的所有抽象方法都实现,就ok
执行结果如下:
需要注意的是:
- 使用implements关键字去实现一个接口
- 接口下边一般不写属性(成员变量);
- 在接口中的方法全都是抽象方法
- 接口不可被实例化,需要新建一个类去实现接口,在该类中重写接口的所有方法,该类叫做接口的实现类
- 接口下边的属性都是常量,默认使用static和final修饰,必须初始化。且常量的名字使用全大写
- JDK1.8以后,接口下可以有默认方法的,这个默认方法带有方法体
- 一个普通类可以实现多个接口,弥补了单继承的局限性
- 一个接口可以去继承另外一个接口
至此,抽象类和接口告一段落