一.抽象类
1.啥是抽象类
用专业语言描述就是:如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
当然这话说的也很抽象,所以我们来用人话来解释一下抽象类
抛开编程语言这些,就以现实举例,我说猫的话,你头脑能马上浮现一只猫的具象,我说狗的话,你头脑能马上浮现一条狗的具象,但是当我说动物的时候,你头脑是不能浮现动物的具象,或许头脑里一会闪过鸡鸭鱼这些动物的样子,过一会又会闪过牛马羊这些动物的样子,也就是说,你找不到一个具象能够说动物具象化的样子就是这个具象,所以我们称动物就是一个抽象类。当然我们也可以继续往下再抽象,比如狗,又分为各种各样的狗,哈士奇,柴犬,斗牛犬等等,这时狗又可以被称为抽象类,如此类推,可以继续细分抽象类。所以在实际开发中,我们将什么看作为抽象类,注意还是要以实际情况为准
2.抽象类的构成结构
3.抽象类知识点
1.抽象类不能直接实例化对象,也就是说不能通过new 类名()来创建对象
2.被abstract修饰的类就是抽象类,因为是类,所以抽象类跟普通类的基本结构没什么区别,唯一的区别就是多了抽象方法区
3.抽象类的访问修饰符不能用private,抽象方法也是一样不能用private
4.抽象方法也就是被abstract修饰的方法,所以abstract不仅可以修饰类,还可以修饰方法
5.抽象方法是不能被实现的,也就是说只能在这里写个:修饰符 返回类型 方法名(参数);
6.抽象方法不能被final和static修饰
7.抽象类中不一定要有抽象方法,但有抽象方法的一定是抽象类
8.抽象类必须被继承,如果子类不重写抽象类中的抽象方法,否则子类也是抽象类,必须用abstract修饰
现在我们来理解一下这些知识
1.为什么不能实例化对象?原因就如我们刚刚解释什么是抽象类里一样,我说动物,你不能够找出一个具象说它就是动物的具象化,因为动物是有能跑能飞的,你如果说狗,但是狗不会飞,所以狗不能把动物这个抽象类全部描绘清楚,相当于数学里的包含关系,如图
2.抽象类生来就是为了被继承而诞生的,抽象类的作用就是将许多对象的共性集合起来,然后将这些共性变成抽象方法,比如吃饭,但是不同动物吃的不一样,狗吃狗粮,猫吃猫粮,牛吃草,虎吃肉,但它们这些行为整体归纳起来还是就两个字:吃饭。所以我们将这些共性放在抽象类里,继承给某个具体对象时,它能根据自身情况实现里面的方法,这也是多态的概念——即不同对象去完成时会产生不同的状态
3.所以抽象类天生就是当父亲的料,也就是说它是父类圣体,几乎可以看到抽象类就知道它是父类了,所以要想使用父类里的东西,那么它的访问权限符肯定不能是private,那不然子类怎么去实现父类的抽象方法,就相当于一个人把遗产设置为private私人的,活的时候这遗产谁都不给用,那么他就算是死了,这遗产也要连着他一起都烧了,那还怎么让子孙去继承这个遗产,所以不能用private修饰抽象类(用重写规则里的“子类重写方法的访问权限不能比父类中被重写方法的访问权限更低”也说得通,因为private是最低的访问权限了,没有比private更低的访问权限了)
4.因为抽象方法在抽象类里是不能被实现的,所以它注定是要子类被重写的,既然要被重写了,就不能再加final(final修饰变量或字段,表示变为了常量;修饰类,表示此类不能被继承;修饰方法,表示该方法不能被重写),static也是修饰方法表示此方法不能被重写,就好比嘴上说一套,私底下又一套,嘴上说大家快来重写我的抽象方法,结果私底下又给自己的抽象方法加上final或static不让这个抽象方法被重写
5.子类继承了抽象类后,子类可以重写父类中的方法,也可以不重写父类中的方法,如果不重写,那么子类也就变成了抽象类,于是又开始新的一轮继承,就相当于从祖先传下来了一个未解的秘密,如果这一代人没把这个秘密解出来,那么就传给下一代人解,直到这个秘密被解出来为止,所以会出现不同情况,一种是一开始有许多抽象方法,但每一代都实现几个抽象方法,如此类推,另一种是一开始有许多抽象方法,但每一代不仅不实现抽象方法,还在自己这一代再添加一些抽象方法,总的概括来说,就是父债子偿,出来混总该要还的
6.既然核心就是要重写父类里的方法,那么为什么一定要用抽象类呢,普通类也能被重写呀,确实如此,用普通类也能实现上述一切要求,但是使用抽象类相当于多了一重编译器的校验,如果有一天脑袋突然糊涂了,把该重写的方法没有进行重写,那么运行出来结果就会出错,这时候又要在所有的代码里面找错误,如果我们使用抽象类,就可以在我们这种犯糊涂的时候进行报错,让我们尽早发现问题。很多语法存在的意义就是为了“预防出错”,像之前的final也是类似,如果我们给不想被修改的变量加上final,那么当我们不小心修改这个变量时,编译器就会报错,及时提醒我们。
所以充分利用编译器的校验,在实际开发中是非常有意义的
4.抽象类的例子
abstract class Animal{
//抽象方法区
abstract public void eat();
abstract void speak();
//其他区
public String name;
}
class Dog extends Animal{
public Dog(String name){
this.name=name;
}
@Override //利用注解也能利用编译器的检查优势,提高开发效率
public void eat(){
System.out.println(name+"吃狗粮");
}
@Override
public void speak(){
System.out.println(name+"汪汪汪");
}
}
class Cat extends Animal{
public Cat(String name){
this.name=name;
}
@Override
public void eat(){
System.out.println(name+"吃猫粮");
}
@Override
public void speak(){
System.out.println(name+"喵喵喵");
}
}
public class Test {
public static void main(String[] args) {
Dog dog=new Dog("旺财");
Cat cat=new Cat("小猫");
dog.eat();
dog.speak();
cat.eat();
cat.speak();
}
}
二.接口
1.啥是接口
用专业语言解释:多个类的公共规范,是一种引用数据类型
用人话来说,其实和抽象类非常相似,也是将许多对象的共性放在接口里,然后每个对象重写接口里的抽象方法,实现多态。那么它存在的意义是什么呢?就要说回抽象类了,因为抽象类是用extends来继承,变成父类子类的关系,但是在Java里面,只能实现单继承,不支持多继承,所以为了解决这个问题,接口就诞生了。所以就是为了一个类可以实现多个接口。
2.接口的构成结构
3.接口知识点
1.同抽象类一样,接口也是不能被new()创建对象的,即使接口类型是一种引用类型
2.被interface修饰的就是接口,因为接口属于引用类型,不属于类,所以前面没有class了
3.如上图所示,接口中的方法都是抽象方法,即系统默认都是public abstract修饰的,也就是说,接口中的方法是不能在接口里面实现的,同时因为public abstract是系统默认修饰的,所以提高代码简洁性,一般都是不写的(例如阿里的编码规范中就要求不写public abstract)
4.如果一定要实现接口中的方法,用default或static修饰,那么可以有具体的实现(这也是与抽象类不同的地方,抽象类即使加了default和static也不能被实现)
5.同抽象类一样,接口不能被private修饰,里面的抽象方法也不能被private修饰
6.重写接口中的方法不能使用默认的访问权限,因为重写规则里的“子类重写方法的访问权限不能比父类中被重写方法的访问权限更低”可得,接口中的方法默认是public,所以也只能用public来重写接口中的方法
7.如上图所示,接口中的变量系统默认用public static final修饰,所以即使不写,也是被隐式指定的
8.接口中不能有静态代码块和构造方法
9.接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
10.与继承extends不同,类连接接口用implements
11.同抽象类一样,如果一个类连接接口后,如果不想实现接口里的抽象方法,那么这个类就要定义为抽象类,然后继承给其他类,如此循环继承,直到抽象方法全部被实现才可以不变为抽象类
12.接口名一般以大写i为首字母命名
13.如果一个类既需要继承又需要连接接口,那么必须先extends再implements
4.接口的例子
class Animal{
protected String name;
public Animal(String name){
this.name=name;
}
}
interface IFlying{
void fly(); //省略public abstract,提高代码简洁性
}
interface IRunning{
void run();
}
interface ISwimming{
void swim();
}
class Cat extends Animal implements IRunning{ //先extends再implements
public Cat(String name){
super(name); //子类实例化前必须先初始化父类
}
@Override //添加注解,利用编译器的检查优势,提高开发效率
public void run(){ //必须用public才能重写
System.out.println(this.name+"正在用四条腿跑");
}
}
class Duck extends Animal implements IRunning,IFlying,ISwimming{ //实现多接口
public Duck(String name){
super(name); //子类实例化前必须先初始化父类
}
@Override
public void run(){
System.out.println(this.name+"正在用两条腿跑");
}
@Override
public void fly(){
System.out.println(this.name+"正在用翅膀飞");
}
@Override
public void swim(){
System.out.println(this.name+"正在漂在水上");
}
}
public class Test {
public static void main(String[] args) {
Cat cat=new Cat("neko");
Duck duck=new Duck("唐老鸭");
cat.run();
duck.run();
duck.fly();
duck.swim();
}
}