java类和对象:继承、多态、接口、抽象类-1
https://developer.aliyun.com/article/1515765
二、多态
对于几个不同的类,可以根据他们共同特性,将其抽取出来形成一个共同的父类,这个父类中包含了其他类所具有的共同属性,例如这里有一个狗类,一个猫类,和一个鸟类,他们都有眼睛,都有脚,还可以拥有自己的名字等等静态属性,动态属性,就比如都会吃,喝跑等。
但是对于不同的动物来说,比如狗和鸟,他们的跑这个行为是不一样的,对于狗来说是用四条腿来跑,而对于鸟来说,只能用两个脚跳,或者用两个翅膀飞,这个行为和狗对比起来是完全不一样的,那么对于其继承的父类来说,不可能只用父类的方法,来描述两个不同的行为,这些行为都是他们的独自的特性。
这个时候就只需要对其父类的这个方法于子类中进行重写,而不用在每一个子类都实现一个方法,于是就避免了大量重复的代码的编写。同时只需要实例化一个继承这个父类的子类对象,即可调佣相应的方法。
class Animal{ public void action(){ System.out.println("正在跑"); } } class Dog extends Animal{ public void action(){ System.out.println("正在用四条腿跑"); } } class Bird extends Animal{ public void fly(){ System.out.println("正在飞"); } }
例如这里有一个Animal父类,其中一个Dog类和Bird类继承这个Animal类。父类中有action这个方法,但是对于鸟和狗来说,他们的行为是不一样的,比如狗是用四条腿跑,而鸟是用翅膀飞。
public class Main { public static void main(String[] args) { Dog dog = new Dog(); Bird bird = new Bird(); dog.action(); bird.action(); } }
结果如下:
三、抽象类
有时候我们在实例化对象的时候,难免会将一个父类不小心给实例化了,但是有的情况是不允许父类被实例化的。例如一个父类Animal动物类,他被他的子类Dog继承。我们可以将Dog实例化成一个具体的dog,但是你并不能将一个动物给具体抽象出来,一个动物直接抽象出来会是什么?怎么归类?但是我们又只需要实例化子类对象,而不是父类对象。这就是一大麻烦,为了避免这个问题,我们需要将这个父类设置为抽象类,来避免父类被实例化。
在实际解决问题的时候,通常将父类设置为抽象类,然后对这个父类进行继承和多态处理,继承关系中,越是在上方的类越抽象,例如狗类继承哺乳类,哺乳类继承动物类等。
抽象类的语法如下:
public abstract class Test类名{
abstract void function( );
}
abstract为定义抽象类关键字,使用abstract关键字定义的类为抽象类,而使用这个关键字定义的方法被称为抽象方法,这个方法不能有方法体,方法本身没有任何意义,它的作用只存在于被子类重写后。一个类如果有抽象方法,那么他必须被定义为抽象类(如果声明一个抽象方法,就必须将含有这个抽象方法的类定义为抽象类),实际上抽象类除了被继承之外没有任何意义,抽象类同时也保证了类在被继承的时候的安全性。
对于继承这个抽象类的子类来说,子类需要重写这个抽象类中的所有的抽象方法。
四、接口类
举一个很简单的例子,一个父类中存在属性:名字,年龄; 行为:跑,游泳(两个都是抽象方法);
然后我们定一个狗类(假设不会游泳),和一个鱼类都继承这个父类,那么对于狗类和鱼类来说都需要实现跑和游泳这两抽象方法,那么问题来了,狗如何去实现游泳这个方法?那么鱼又如何去实现跑这个方法?鱼不可能会跑,狗不能游泳,或者说狗类不用重写和实现这个方法,那么这个抽象方法是不可能被实现的,但是需要继承这个父类的名字和年龄等属性,为了应对这种问题,接口的概念就出现了。
接口就是为了应对这种父类不能完全将子类中的共同特性抽取出来的情况。
语法如下:
关键字:interface
interface Test(接口名){
void function(); // 接口内的方法,省略abstract关键字。
}
使用implements 来让类实现该接口
在接口中,方法必须被定义为public或abstract的形式其他修饰限定符不被java编译器认可,即使该方法没有被public修饰,也是默认public权限,并且接口中被定义的任何字段都是自动被static和final修饰的。
实现某个接口的类,这个类就必须实现重写这个接口中的方法。
案例如下:
interface Fly{ void fly(); } interface Run{ void run(); } class Dog implements Run{ @Override public void run() { System.out.println("dog is running"); } } class Bird implements Fly{ public void fly(){ System.out.println( "bird is flying"); } } public class Main { public static void main(String[] args) { Dog dog = new Dog(); Bird bird = new Bird(); dog.run(); bird.fly(); } }
结果如图
接口类型也可是实现向上转型:
interface Run{ void run(); } class Dog implements Run{ @Override public void run() { System.out.println("dog is running"); } } public class Main { public static void main(String[] args) { Run run = new Dog(); run.run(); } }
结果如下:
五、final关键字
重写和继承的功能很强大,但是必然也存在着不允许被重写或者继承的情况,这个时候就可以使用final修饰类或者方法,来防止某些不允许被重写和继承的情况。
1、final防止重写
为了方法方法被重写,需要在方法声明的开始处将final指定为修饰符,被final修饰的方法不能被重修。
class A{ final void method(){ System.out.println("hello"); } } class B extends A{ void method(){ System.out.println("hello java"); } }
报错如下:
2、使用final放置继承
在声明类的时候可以在开始的位置加上final关键字,以防止其被其他类继承:
final class A{ // 方法体 } class B extends A{ // 方法体 }
执行后显示
如果一个类被abstract和final同时修饰,是非法的。如果这个类被abstract修饰,那么其中必定包含abstract方法,abstract方法必须被重写,但是被final修饰的类,其中的方法都是隐式的被final修饰,也就是说其中的所有方法都不能被重写,因此同时被final和abstract修饰是没有意义的,是不合法的。
3、final修饰的变量
final不仅可以修饰方法和类,还可以用来修饰成员变量,如果一个变量被final修饰,那么它的值在其生命周期内就不能改变。但是可以给他初始化。final修饰的变量初始化可以直接赋值,也可以使用代码块和构造方法来对其进行初始化,不管怎么样,必须对其选取这几种方法中的一个来初始化
初始化:
1、可以使用代码块
class A{ final int a; { a = 10; } }
如果这个变量同时被static修饰,那么可以使用static静态代码块来对其进行初始化:
1. class A{ 2. final static int a; 3. static { 4. a = 10; 5. } 6. }
2、构造器初始化
class A{ final int a; public A(int a) { this.a = a; } }