一、继承
1、 继承的作用:
基本作用:子类继承父类,代码可以得到复用。
主要(重要)作用:因为有了继承关系,才有了后期的方法覆盖和多态机制。
2、 继承的相关特性
2.1 B类继承A类,则称A类为超类(superclass)、父类、基类,
B类则称为子类(subclass)、派生类、扩展类。
class A{}
class B extends A{}
superclass 父类
subclass 子类
2.2 java 中的继承只支持单继承,不支持多继承,C++中支持多继承,
这也是 java 体现简单性的一点,换句话说,java 中不允许这样写代码:
class B extends A,C{ } 这是错误的。
2.3 虽然 java 中不支持多继承,但有的时候会产生间接继承的效果,
例如:class C extends B,class B extends A,也就是说,C 直接继承 B,
其实 C 还间接继承 A。
2.4 java 中规定,子类继承父类,除构造方法不能继承之外,剩下都可以继承。
但是私有的属性无法在子类中直接访问。(父类中private修饰的不能在子类中
直接访问。可以通过间接的手段来访问 getter()。)
2.5 java 中的类没有显示的继承任何类,则默认继承 Object类,Object类是
java 语言提供的根类(老祖宗类),也就是说,一个对象与生俱来就有
Object类型中所有的特征。
System.out.println(引用);
当直接输出一个“引用”的时候,println()方法会先自动调用“引用.toString()”,
然后输出toString()方法的执行结果。
可以理解为引用在堆内存中的地址。
2.6 继承也存在一些缺点,例如:CreditAccount 类继承 Account 类会导致它
们之间的耦合度非常高,Account 类发生改变之后会马上影响到 CreditAccount 类
二、方法覆盖
1、 什么时候使用方法覆盖:
子类继承父类后,当继承的方法无法满足当前子类的业务需求时,
子类对方法进行重写,进行“方法的覆盖”。
方法覆盖又叫做方法重写 Override、Overwrite
方法覆盖的条件:
① 两个类必须有继承关系;
② 重写之后的方法和之前的方法有:
相同的返回值类型
相同的方法名
相同的形参列表
③ 访问的权限不能更低,可以更高;
④ 抛出异常不能更多。
回顾方法重载 Overload
条件: 同一个类中、方法名相同、参数列表不同(个数、类型、顺序不同)
2、 当子类对父类继承过来的方法进行“方法覆盖”后,
子类调用该方法时,执行覆盖之后的方法。
3、 方法覆盖的注意事项:
方法覆盖只是针对方法,和属性无关;
私有方法无法覆盖;
构造方法不能被继承,所以构造方法也不能被覆盖;
方法覆盖只针对于实例方法,静态方法覆盖没有意义。
4、 Object类中toString()方法的覆盖
toString()方法存在的作用就是:将java对象转换成字符串形式。
大多数的java类toString()方法都是需要覆盖的。因为Object类中提供的toString()
方法输出的是一个java对象的内存地址。
toString()方法具体怎么进行覆盖:
格式可以自己定义,或者根据需求或项目要求的。
public String toString(){
return year + "年" + month + "月" + day + "日";
}
// 输出xxxx年x月x日
三、多态
1、 向上转型和向下转型的概念。
向上转型:子—>父 (upcasting)
又被称为自动类型转换:Animal a = new Cat();
向下转型:父—>子 (downcasting)
又被称为强制类型转换:Cat c = (Cat)a; 需要添加强制类型转换符。
什么时候需要向下转型?
需要调用或者执行子类对象中特有的方法。
必须进行向下转型,才可以调用。
向下转型有风险吗?
容易出现ClassCastException(类型转换异常)
怎么避免这个风险?
instanceof运算符
① 可以在程序运行阶段动态判断某个引用指向的对象的类型;
② instanceof的语法:
(引用 instanceof 类型)
③ instanceof运算符的运算结果只能是true/false;
④ 假设c是一个引用,c变量保存的内存地址指向了堆中的对象。
当(c instanceof Cat)为true表示:
c引用指向的堆内存的java对象是Cat类型;
当(c instanceof Cat)为false表示:
c引用指向的堆内存的java对象不是Cat类型;
养成好习惯,向下转型之前一定要使用instanceof运算符进行判断。
不管是向上转型还是向下转型,首先他们之间必须有继承关系,这样编译器就不会报错。
2、 什么是多态。
多种形态,多种状态,编译和运行有两个不同的状态。
编译期叫做静态绑定。
运行期叫做动态绑定。
Animal a = new Cat();
编译的时候编译器发现a的类型是Animal,所以编译器会去Animal类中找move()方法
找到了,绑定,编译通过。但是运行的时候和底层堆内存当中的实际对象有关
真正执行的时候会自动调用“堆内存中真实对象”的相关方法。
a.move();
多态的典型代码:
父类型的引用指向子类型的对象。
编译阶段:绑定父类方法,
运行阶段:绑定子类方法。
3、 多态的作用:
降低程序的耦合度,提高程序的扩展力。
减少后续开发的修改程度。
public class Master{
public void feed(Dog d){}
public void feed(Cat c){}
}
以上的代码中表示:Master和Dog以及Cat的关系很紧密(耦合度高)。导致扩展力很差。
public class Master{
public void feed(Pet pet){
pet.eat();
}
}
以上的代表中表示:Master和Dog以及Cat的关系就脱离了,Master关注的是Pet类。
这样Master和Dog以及Cat的耦合度就降低了,提高了软件的扩展性。
这里提到了一个软件开发原则:
七大原则最基本的原则:OCP(对扩展开放,对修改关闭)
目的是:降低程序耦合度,提高程序扩展力。
面向抽象编程,不建议面向具体编程。
面向对象的三大特征:
封装、继承、多态
有了封装的整体概念后,
对象和对象之间产生了继承,
有了继承之后,才有了方法的覆盖和多态。