在JAVA中,这一次介绍的一个新的概念叫重写。惯例走起,先看定义:
重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程
进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定
于自己的行为。 也就是说子类能够根据需要实现父类的方法。
好,了解了定义以后我们肯定好奇对于重写我们学了后到底有什么用。事实上学习重写是为了引出下面对于多态的介绍。
子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致。
被重写的方法返回值类型可以不同,但是必须是具有父子关系的。
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
父类被static、private修饰的方法、构造方法都不能被重写。
重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写
这两幅图说明了重写需要注意的点。非常重要。
标准定义:
通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。
这个定义在刚开始看到的时候觉得很抽象。但是不着急,我们一点一点来说。首先,我们需要搞清楚一点就是,我们在什么情况下,会用到多态这个概念?
在java中要实现多态,必须要满足如下几个条件,缺一不可:
❤1. 必须在继承体系下
❤2. 子类必须要对父类中方法进行重写
❤3. 通过父类的引用调用重写的方法
class Shape {
public void draw() {
System.out.println("画画");
}
}
class Cycle extends Shape {
public void draw() {
System.out.println("⚪!");
}
}
class Flower extends Shape {
public void draw() {
System.out.println("❀!");
}
}
public class Test {
public static void drawMap(Shape shape) {
shape.draw();
}
public static void main(String[] args) {
Cycle cycle = new Cycle();
Flower flower = new Flower();
drawMap(cycle);
drawMap(flower);
System.out.println("===================");
Shape[] shapes = {cycle, flower};
for (Shape shape: shapes) {
shape.draw();
}
}
}
分析:
上面代码我们首先定义一个Shape类接着定义两个子类来继承Shape这个父类,重点我们将目光转移到main函数。我们用分割线完成了两个方法。第一种实例化两个对象,然后直接传参。两种都是调用shape.draw也就是父类的。我们看看运行结果:
这个运行结果表明两种方法都能输出图形
这一部分我们简单来说一下就好了
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
Animal animal = new Cat("元宝",2);
public class TestAnimal {
// 2. 方法传参:形参为父类型引用,可以接收任意子类的对象
public static void eatFood(Animal a){
a.eat();
}
// 3. 作返回值:返回任意子类对象
public static Animal buyAnimal(String var){
if("狗".equals(var) ){
return new Dog("狗狗",1);
}else if("猫" .equals(var)){
return new Cat("猫猫", 1);
}else{
return null;
}
}
public static void main(String[] args) {
Animal cat = new Cat("元宝",2); // 1. 直接赋值:子类对象赋值给父类对象
Dog dog = new Dog("小七", 1);
eatFood(cat);
eatFood(dog);
Animal animal = buyAnimal("狗");
animal.eat();
animal = buyAnimal("猫");
animal.eat();
}
}
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
在JAVA当中有一种定义方式就是abstract定义抽象类,在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的。因此抽象类的定义就是:
如果
一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
我们以动物为例,用一张图来说明一下继承等什么可以设计抽象类:
在JAVA中一个类如果被abstract修饰,就是抽象类,抽象类中被abstract修饰的啊方法称为抽象方法,抽象方法不用给出具体的实现体
abstract class Shape {
public void draw() {
}
public abstract void fun();
}
public class Test {
public static void main(String[] args) {
}
}
这其实时一个非常简单而且并没有写完的一个抽象类。
抽象类也是类,内部可以包含普通方法和属性,甚至构造方法
注意:
- 抽象类不能直接实例化对象
- 抽象方法不能是 private 的
- 抽象方法不能被final和static修饰,因为抽象方法要被子类重写
- 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用abstract 修饰
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法。也就是说不能直接实例化但是你在子类重写父类(也就是抽象类)的方法,然后来实现
接口,提起接口我们是不是第一反应就是电脑USB接口,或者移动电源啥的。既然叫接口,那它一定是有接口的公共特点比如说最基本的大部分电器都能共同使用…那么我们总体概括来说:
接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
接口的定义和类的定义格式基本上时一样的,只是将class关键字换成interface关键字,这就定义了一个接口。我们通过代码来分析:
abstract class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
}
interface IRunning {
public void run();
}
interface ISwimming {
void swim();
}
interface CC extends ISwimming,IRunning {
@Override
void swim();
@Override
void run();
}
class Dog extends Animal implements IRunning{
public Dog(String name) {
super(name);
}
@Override
public void run() {
System.out.println(name+ "正在用四条腿跑!");
}
}
class Fish extends Animal implements ISwimming{
public Fish(String name) {
super(name);
}
@Override
public void swim() {
System.out.println(name+ "鱼正在游泳!");
}
}
class Duck extends Animal implements IRunning,ISwimming {
public Duck(String name) {
super(name);
}
@Override
public void run() {
System.out.println(name+ "正在用两条腿跑");
}
@Override
public void swim() {
System.out.println(name+ "正在用两条腿游泳!");
}
}
public class Test {
public static void walk(IRunning irunning) {
irunning.run();
}
public static void swim(ISwimming iswimming) {
iswimming.swim();
}
public static void main(String[] args) {
walk(new Dog("旺财"));
walk(new Duck("唐老鸭"));
System.out.println("=================");
swim(new Duck("唐老鸭2号"));
swim(new Fish("十七秒"));
}
}
我们看到这个代码,首先我们创建了一个Animal的动物抽象类。里面定义了一个name和一个带有name的构造方法,接下来定义了两个ISwimming和IRunning的接口,用fish和duck来继承。我们来看运行结果:
通过这里我们可以总结:
(1)接口不能直接使用,必须要有一个“实现类”来实现该接口,实现接口里面的所有抽象方法。
(2)子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
(3)接口类型是一种引用类型,但是不能直接new接口的对象
(4)接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是public abstract,其他修饰符都会报错)
(5)重写接口中方法时,不能使用默认的访问权限
(6) 接口中不能有静态代码块和构造方法
(7). 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
(8). 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
(9). jdk8中:接口中还可以包含default方法。
第五点我们可以看这个代码:
public interface USB {
double brand = 3.0; // 默认被:final public static修饰
void openDevice();
void closeDevice();
}
public class TestUSB {
public static void main(String[] args) {
System.out.println(USB.brand); // 可以直接通过接口名访问,说明是静态的
// 编译报错:Error:(12, 12) java: 无法为最终变量brand分配值
USB.brand = 2.0; // 说明brand具有final属性
}
}
在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。
上面第一个代码中Duck就是继承了两个接口。但要注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。
接口后面其实还有一个克隆和深浅拷贝的问题,我们有时间下次再说,本期内容写到这就到此为止了。多态抽象类接口这些内容在Java中时十分重要的,后期很多东西例如图书管理系统等等都需要用到上面的知识,这一部分一定要好好学习。下一期,我们不见不散。
生活是属于每个人自己的感受,不属于任何别人的看法。就像活着,人是为了活着本身而活着的,而不是为了活着之外的任何事物活着。