一.继承
1.概念
继承机制:是面向对象程序设计使代码可复用的最重要的手段,它允许程序员在保持原有类的特性的基础上进行扩展,增加新的功能,这样产生的类成为派生类。继承呈现了面型对象程序设计的层次结构,体现了由简单到复杂的过程。
继承主要解决的问题:抽取共性,实现代码复用
2.语法
修饰符 class 子类 extends 父类{
......
}
//举例
class Animal{
String name;
int age;
}
class Dog extends Animal{
String color;
public void bark(){
System.out.println(name+"汪汪叫");
}
}
3.父类成员访问
(1)子类访问父类成员的成员变量
1.子类和父类不存在同名变量
2.子类和父类成员变量同名
(2)子类访问父类的成员方法
1.子类和父类不存在同名变量
2.子类和父类成员变量同名
(3)总结
方法不同时,在子类方法中或通过子类对象访问方法时,优先访问子类自己的,自己没有时再到父类中去找,如果父类也没有就报错
4.关键字super
1.主要作用:在子类方法中访问父类
2.注意:(1)只能在非静态方法中访问;
(2)在子类方法中,访问父类成员的成员变量和方法;
5.子类构造方法
1.子类对象构造时,要先调用基类构造方法,然后执行子类的构造方法
2.子类构造方法中,没有写任何关于基类构造方法的代码,但在构造子类对象时,先执行基类构造方法,然后再执行子类构造方法
(原因)子类对象中,成员由两部分组成:基类继承下来的部分,子类新增的部分。在构造子类对象时先要调用基类的方法,将从基类继承下来的构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整。
3.注意:(1)若父类显式定义无参或默认无参的构造方法,在子类构造方法第一行默认有隐含的super()调用即调用基类构造方法;
(2)若父类构造方法带参数,则此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败;
(3)在子类构造方法中,super()调用父类构造时,必须是子类构造函数中的第一条语句;
(4)super()只能在子类构造方法中出现一次,且不能和this同时出现;
6.super和this的异同
同 | 异 |
1.都是java中的关键字 | 1.this是当前对象的调用,即调用实例方法的对象;super相当于是子类对象中从父类继承下来的部分的成员的引用 |
2.都只能在类的非静态访问中使用,用来访问非静态成员方法和字段 | 2.在非静态成员方法中:this用来访问本类的方法和属性;super用来访问父类继承下来的方法和属性 |
3.在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在 | 3.在构造方法中:this()用于调用本类的构造方法;super用于调用父类构造方法。两种调用不能同时在构造方法中出现 |
4.构造方法中一定会有super()的调用,用户没有写编译器也会增加;但this()用户不写则没有 |
7.再谈初始化
执行顺序:
父类和子类的静态先执行(只执行一次)
父类的实例,父类的构造方法
子类的实例,子类的构造方法
//静态代码块先执行,且值执行一次,在类加载阶段执行;
//当有对象创建时,才会执行实例化代码块,实例化代码块完成后最后构造方法执行
8.protected关键字
注意:父类中private成员变量虽然在子类中不能直接访问,但也继承到子类中了
9.继承方式
1.单继承:class B---->class A
public class A{
//......
}
public class B extends A{
//......
}
2.多层继承:class C---->class B---->class A
public class A{
//......
}
public class B extends A{
//......
}
public class C extends B{
//......
}
3.不同类继承同一个类:class B---->class A<----class C
public class A{
//......
}
public class B extends A{
//......
}
public class C extends A{
//......
}
//java不支持多继承
//如果需要控制继承,此时这个类可以被final修饰,意味着当前类不可以继承,这个类是密封类。
10.final关键字
1.final修饰变量/字段:表示变量不可被修改
2.final修饰类:表示次类不能被继承
3.final修饰方法:表示该方法不能被重写
11.继承与组合
1.组合是一种表达类之间的关系的方式,能够达到代码重用的效果;
组合并没有涉及到特殊用法,仅仅是将一个类的实例作为另一个类的字段;
2.组合表示对象之间的关系是has--a(比如:汽车由引擎,底盘等组成)
class Tire{}
class Engine{}
class Car{
private Tire tire;
private Engine engine;
}
继承别是对象之间的关系是is---a(比如:狗是一种动物)
3.组合和继承都能实现代码复用,一般建议能用组合尽量用组合
二.多态
1.概念
不同的对象完成同一行为时会产生不同的结果
2.多态实现条件
1.必须在继承体系下;
2.子类必须要对父类中方法进行重写;
3.通过父类的引用调用重写的方法;
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法
3.重写
1定义:override,也称覆盖,是子类对父类非静态,非private修饰,非final修饰,非构造方法等的实现过程进行重新编写;返回值、形参(参数个数,参数类型)不能改变
2.好处:可以根据自己需要,定义特定于自己的行为,即子类能根据需要实现父类的方法
3.规则:(1)子类在重写父类的方法时,一般必须与父类方法原型一致:返回值类型,方法名,参数列表(参数类型,参数个数);
(2)被重写的方法返回值类型可以不同,但必须具有父子关系;
(3)访问权限不能比父类中被重写的方法访问权限更低;
(4)父类被private、static修饰的方法,构造方法都不能被重写;
(5)重写的方法可以使用@Override注解来显式指定,能帮助进行一些合法性校验;
(6)被final修饰的方法(密封方法)不能被重写;
4.设计原则:对于已经投入使用的类,最好重新定义一个新的类,来重复利用其中共性的内容或改动新的内容
5.重写和重载的区别
名称 | 重写 | 重载 |
参数列表 | 一定不能修改 | 可以修改 |
返回类型 | 一定不能修改(除非可构成父子关系) | 可修改 |
访问限定符 | 一定不能做更严格的限制(可降低限制) | 可修改 |
即:方法重写是子类与父类的一种多态性表现,方法重载是一个类的多态。
6.动态绑定:(前期/早绑定):在编译时根据用户传递的实参类型就确定了具体调用哪个方法(典型:函数重载)
7.静态绑定:(后期/晚绑定):在编译时不能确定方法的行为,需要等到程序运行时才能确定具体调用哪个类的方法
4.向上转型和向下转型
1.向上转型:
(1)创建一个子类对象,将其当做父类对象来使用
(2)语法格式:父类类型 对象名=new 子类类型();
Animal animal=new cat ("kitty",2);
//animal是父类类型,但是可以引用一个子类对象,因为是从小范围到大范围的转换
(3)应用场景:
方法传参
public animal void func(Animal animal){}
直接赋值
public static void main(String[] args){
Animal animal=new Dog("小白",3);
func(new Dog("小白",3));
}
返回值
public static Animal func(){
return new Dog("小白",3);
}
(4)优点:代码实现更灵活
(5)缺陷:不能调用子类特有的方法
2.向下转型:
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时可能需要调用子类特有的方法,此时将父类引用再还原为子类对象即可,即向下转换
5.使用多态的优劣
(1)好处
1.降低代码的“圈复杂度”,避免使用大量if-else
2.可扩展能力更强
(2)缺陷
1.实行没有多态性:当父类或子类都有同名属性时,通过父类引用,只能引用父类自己的成员属性
2.构造方法没有多态性
6.避免在构造方法中调用要重写的方法
class B{
public B{
func();
}
public void func(){
System.out.println("B.func()");
}
}
class D extends B{
private int num=1;
@Override
public{
System.out.println("D.func()"+num);
}
}
public class Test{
public static void main(String[] args) {
D d=new D();
}
}
//打印结果:D.func()0
1.构造对象的同时会调用B的构造方法;
2.B的构造方法中调用了func方法,此时会触发动态绑定,会调用到D中的func;
3.此时D对象自身还没有构造,此时num处在未初始化的状态,值为0;
4.故在构造函数内,尽量避免使用实例方法,除了final和private方法
结论:用尽量简单的方法时对象进入可工作区域,尽量不要在构造器中调用方法(如果次方法还没被重写,就会触发动态绑定,但此时子类对象还没构造完),可能会出现一些隐藏但极难发现的问题。