抽象类和接口(上)
- 一丶抽象类
- 二丶接口
- 写法
- 使用
- 特性
- 1. 不 能 直 接 n e w 接 口 的 实 例 \color{red}{1.不能直接new接口的实例} 1.不能直接new接口的实例
- 2. 关 于 接 口 的 修 饰 符 \color{red}{2.关于接口的修饰符} 2.关于接口的修饰符
- 2. 关 于 接 口 的 成 员 属 性 \color{red}{2.关于接口的成员属性} 2.关于接口的成员属性
- 3. 接 口 抽 象 方 法 不 能 有 方 法 体 \color{red}{3.接口抽象方法不能有方法体} 3.接口抽象方法不能有方法体
- 5. 接 口 编 译 后 字 节 码 文 件 的 格 式 是 . c l a s s \color{red}{5. 接口编译后字节码文件的格式是.class} 5.接口编译后字节码文件的格式是.class
- 6. 重 写 接 口 的 方 法 时 不 能 用 d e f a u l t 修 饰 \color{red}{6.重写接口的方法时不能用default修饰} 6.重写接口的方法时不能用default修饰
- 7. 接 口 中 不 能 有 静 态 代 码 块 和 构 造 方 法 。 \color{red}{7. 接口中不能有静态代码块和构造方法。} 7.接口中不能有静态代码块和构造方法。
- 多继承
- 接口之间的继承
一丶抽象类
什么是抽象类?
顾名思义,首先抽象类它是一个类,那么抽象怎样来理解呢?我们对抽象的定义就是难以理解的,难以用言语形容的。所以依据这里,给出了如下的定义:
在类中没有足够的信息来描绘一个具体的对象,这样的类就叫抽象类。
再具体:
抽象类是一个自下而上的抽象过程,其提供了通用实现,是事物的抽象。我们在实现类的过程中,可以发现有些类有几乎相同的实现,我们把这些几乎相同的实现抽取出来成为抽象类。然后如果有一些差异,那么我们支持抽象方法自定义实现。
写法
抽象类的写法很是简单(具体如下):
public abstract class Persion {//使用abstract关键字来定义一个抽象类public String name;public int age;public Persion(String name,int age) {this.name = name;this.age = age;}public void run(){System.out.println(name + "正在奔跑");}abstract void eat();//使用abstract关键字来定义抽象方法abstract public void body();
}
在这里,我定义了一个“人”这样一个抽象类。我们每个人都会使用两条腿进行奔跑,但是由于南北方的差异和气候环境,人们的饮食习惯,还有身材比例各不相同,所以这里用抽象方法仅仅声明有这个特性。
特性
我们定义抽象类肯定是有许多的规则,具体有哪些,接下来进行分析。
1. 抽 象 类 不 能 直 接 实 例 化 对 象 \color{red}{1.抽象类不能直接实例化对象} 1.抽象类不能直接实例化对象
先放代码:
可以看到,这里我尝试定义一个Persion的对象,但是报出来了一个错误:Persion是抽象的,不能被实例化。
2. 抽 象 方 法 不 能 是 p r i v a t e 的 \color{red}{2. 抽象方法不能是 private 的} 2.抽象方法不能是private的
在继承关系当中,虽然子类也继承了父类的私有制方法,但是它不能进行访问,所以当把方法定义为private的时候,这个时候这个方法不能被抽象类的子类进行重写。同理: 抽 象 方 法 不 能 是 s t a t i c 、 p r i v a t e , f i n a l 修 饰 的 \color{blue}{抽象方法不能是static、private,final修饰的} 抽象方法不能是static、private,final修饰的
接下来进行验证:
首先,我定义一个“南方人类”和“北方人类”。
public class SouthPeople extends Persion{//定义南方人类public SouthPeople(String name, int age) {super(name, age);}@Overridevoid eat() {System.out.println(name + "吃面条");}@Overridepublic void body() {System.out.println(name + "身体小巧");}
}
public class NorthPeople extends Persion{//定义北方人类public NorthPeople(String name, int age) {super(name, age);}@Overridevoid eat() {System.out.println(name + "吃面条");}@Overridepublic void body() {System.out.println(name + "身体小巧");}
}
那么,接下来我们对抽象方法进行测试,看看它会有哪些错误。
1. 关 于 权 限 修 饰 符 问 题 \color{violet}{1.关于权限修饰符问题} 1.关于权限修饰符问题
首先是对于private,很明显报了错误,说abstract和private不能同时出现
再接着对于default 和不写:
这里可以看出,不写和default是两码事,不写可以,但是default就不行。
这是因为:
抽 象 方 法 不 加 权 限 修 饰 符 的 时 候 默 认 是 p u b l i c \color{blue}{抽象方法不加权限修饰符的时候默认是public} 抽象方法不加权限修饰符的时候默认是public
然后最后对于protect
此刻并没有报错,所以是可以的。
2. 关 于 s t a t i c 问 题 \color{violet}{2.关于static问题} 2.关于static问题
如果给一个方法加上了static,那么就是静态方法,此时这个方法就是属于类的行为,不是对象的。
如果加上了static,那么编译器会报错,static和abstract不能同时出现。
3. 子 类 要 重 写 抽 象 类 中 所 有 的 抽 象 方 法 \color{red}{3.子类要重写抽象类中所有的抽象方法} 3.子类要重写抽象类中所有的抽象方法
首先,抽象类一定要被继承,并且子类要重写抽象类中所有的抽象方法!
这里用“北方人类”进行测试。
我这里去掉了NorthPeople{ }中的body()抽象方法,这里出现了异常:NorthPeople必须被声明为抽象类或者重写Persion{ }类中的body()方法。
也就是说:
如果子类没有重写抽象类中所有的抽象方法,那么这个子类也要被声明为抽象类
好处
作为抽象类,子类要对父类方法进行重写,但是如果我不用抽象类,子类也可以对父类方法进行重写,并且对应的功能我也有,那使用抽象类的意义在哪里呢?
其实就相当于多了一层代码的效验器。
具体解释如下:
如上所示,很多工作是需要子类去完成的,而不是父类,如果这个时候我们不使用抽象类的时候,错误的使用了父类,那么此时编译器不会报错。但如果是抽象类的话那么就会报错。
所以:
使用抽象类的意义就在于效验,和final的使用意义相近,final是为了防止用户误
修改,而abstract是为了防止错误的使用。
二丶接口
什么是接口?
接口在生活中随处可见,USB接口,公牛插座接口,手机充电线接口…
那么在JAVA中,什么是接口?
接口是自上而下的抽象过程,其对某些行为进行了规范,是行为的抽象,可以看成是一种引用数据类型。如果说,我需要某一行为,那么我就去实现相对应的接口,但是具体怎么实现,由我自己说了算。
写法
接口的写法和抽象类类似,但是把abstract关键字换成了interface。这里呢,我设计了一个Sing{ }接口,来对此进行讲解。
public interface Sing {//定义一个Sing接口void sing();
}
这里的话对于写法进行规范:
- 创建接口时, 接口的命名一般以大写字母 I 开头.
- 接口的命名一般使用 “形容词” 词性的单词.
- 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.
使用
就像定义中说的,我们需要某个行为,那么我们就去实现相对应的接口。
那么接下来,我们就创建一个对应的对象,来对上述内容进行验证(这里补全上面的代码):
public class TestAbstract {//这是验证抽象类和接口的public static void main(String[] args) {NorthPeople p1 = new NorthPeople("北方人张三",18);p1.eat();p1.sing();SouthPeople p2 = new SouthPeople("南方人李四",18);p2.eat();p2.sing();}
}
运行结果如下:
特性
1. 不 能 直 接 n e w 接 口 的 实 例 \color{red}{1.不能直接new接口的实例} 1.不能直接new接口的实例
什么意思?就是说:
虽然接口是一种引用数据类型,但是不能直接new出他的对象。
代码如下:
这里报错,Sing是抽象的,不能直接实现。
2. 关 于 接 口 的 修 饰 符 \color{red}{2.关于接口的修饰符} 2.关于接口的修饰符
接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是public abstract,其他修饰符都会报错)
然后来看一下正确的形式:
这里的话public abstract是灰色的,意味着他们其实都是可以省略的
2. 关 于 接 口 的 成 员 属 性 \color{red}{2.关于接口的成员属性} 2.关于接口的成员属性
接口中不能有成员变量,只能有常量,如果说要定义一个变量,那么必须加上 public static final对它常量化!!
具体如下:
一旦定义了之后,不能修改!!因为被final化了。
3. 接 口 抽 象 方 法 不 能 有 方 法 体 \color{red}{3.接口抽象方法不能有方法体} 3.接口抽象方法不能有方法体
这里是什么意思呢?就是说,接口定义的方法如果是抽象方法,那么这个方法不能有方法体,相对应的实现应该在子类中完成。
编译器报错如上:接口抽象方法不能有方法体
再接着继续,如果说想要有方法体,那么就不能是抽象方法,这个时候我们就要修改,上面我们说了: 接 口 中 抽 象 方 法 的 修 饰 符 必 须 是 p u b l i c a b s t r a c t , 那 么 我 们 就 要 接口中抽象方法的修饰符必须是public abstract, 那么我们就要 接口中抽象方法的修饰符必须是publicabstract,那么我们就要 把 他 改 为 d e f a u l t 把他改为default 把他改为default
这 里 注 意 , 是 d e f a u l t , 不 是 不 写 ! 不 是 不 写 ! 不 是 不 写 ! ! \color{red}{这里注意,是default,不是不写!不是不写!不是不写!!} 这里注意,是default,不是不写!不是不写!不是不写!!
具体如下:
那么这样定义的意义在哪里呢?
很多时候,我们可能要对抽象方法进行修改,但是呢一旦新增或者减少抽象方法那
么就要对所有其下的子类进行修改,如果子类少了还好说,一旦多了之后,那么无
疑是一个很繁琐的工程,这个时候,我们就要用到default定义的非抽象方法,因
为是非抽象方法,所以子类不需要对该方法进行重写的同时又可以访问到该方法。
5. 接 口 编 译 后 字 节 码 文 件 的 格 式 是 . c l a s s \color{red}{5. 接口编译后字节码文件的格式是.class} 5.接口编译后字节码文件的格式是.class
关于这一点的验证首先我们运行相对应的代码。
然后打开对应项目下的out文件夹,然后找到对应的.class文件。
6. 重 写 接 口 的 方 法 时 不 能 用 d e f a u l t 修 饰 \color{red}{6.重写接口的方法时不能用default修饰} 6.重写接口的方法时不能用default修饰
这里的话,应该是说子类修饰符权限要大于等于父类。
这里意思是说:此修饰符仅仅可以在接口中被使用
其实不仅仅是default,因为接口中的抽象方法修饰符一般是public,所以呢,子类的修饰符也只能public了。
7. 接 口 中 不 能 有 静 态 代 码 块 和 构 造 方 法 。 \color{red}{7. 接口中不能有静态代码块和构造方法。} 7.接口中不能有静态代码块和构造方法。
首先, 接 口 不 是 类 ! ! ! \color{red}{接口不是类!!!} 接口不是类!!!,所以不能有构造方法,因为它想初始化成员变量也没有变量让他初始化呀。然后对于静态代码块,静态代码块初始化成员变量,这一点和上面同理。
先是静态代码块:
再接着对于构造代码块:
多继承
接口允许多继承,但是类只能单继承,为什么?
如果说类之间运行多继承,那么当两个父类其中的某种方法完全相同时,子类不知
道要对那个父类的方法进行重写,但是如果是接口就没有这种顾虑,因为接口没有
方法体,不论写哪一个都不会冲突,不会造成额外影响!
一个类可以继承多个接口,那么,我们创建一个新的包来对此进行验证。
public class Animal {//这是父类动物类,所有动物共用的模板public String name;public int age;public String gender;public Animal(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;}
}public interface Running {//接口跑:定义跑的行为void run();
}public interface Jump {//接口跳:定义跳这个行为void jump();
}public interface Swim {//定义游泳这个行为void swim();
}
接下来定义具体的动物类:
public class TianE extends Animal implements Running,Jump,Swim{//天鹅类public TianE(String name, int age, String gender) {super(name, age, gender);} @Overridepublic void jump() {System.out.println(name + "身体一摇一摆的跳");}@Overridepublic void run() {System.out.println(name + "小脚掌噗嗤的跑");}@Overridepublic void swim() {System.out.println(name + "在水里开心的游");}
}public class Dog extends Animal implements Running,Jump,Swim{//狗类public Dog(String name, int age, String gender) {super(name, age, gender);}@Overridepublic void jump() {System.out.println(name +"两条狗腿开心的蹦");}@Overridepublic void run() {System.out.println(name + "撒开狗腿开心的跑");}@Overridepublic void swim() {System.out.println(name + "漏出狗头开心的游");}
}
然后接下来我们对这个多继承进行实验:
具体操作如上。
这里的话有一个点需要特别注意:
一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。
接口之间的继承
接口和接口之间也是可以继承的。这里的继承,是多继承,使用extends关键字,代码示例如下(仅展示部分关键代码):
public interface RunningFu{void run();
}public interface Running extends RunningFu{//接口跑:定义跑的行为}
这样的话,在实现类中并没有报错。