目录
一 、为什么需要继承
二、准备工作:用java代码先定义狗类、猫类、动物类,这是代码准备如下
三、继承代码实现
四、 子类中访问父类的成员方法
4.1. 成员方法名字不同
4.2 成员方法名字相同
五、子类构造方法
扩展:如果你对子类和父类的代码执行的顺序搞不清楚可以参考:
六、super和this异同点
七、继承种类
八、继承和组合关系
1、概念介绍
2、优缺点对比
3、如何选择
一 、为什么需要继承
Java
中使用类对现实世界中实体来进行描述,类经过实例化之后的产物对象,则可以用来表示现实中的实体,但是现实世界错综复杂,事物之间可能会存在一些关联,那在设计程序是就需要考虑。
比如:
狗和猫,它们都是一个动物,我们可以把猫和狗归结为一个类就是动物类。
二、准备工作:用java代码先定义狗类、猫类、动物类,这是代码准备如下
动物类
// Animal.java
public class Animal{
String name;
int age;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void sleep(){
System.out.println(name + "正在睡觉");
}
}
狗类
// Dog.java
public class Dog{
string name;
int age;
float weight;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void sleep(){
System.out.println(name + "正在睡觉");
}
void Bark(){
System.out.println(name + "汪汪汪~~~");
}
}
猫类
public class Cat{
string name;
int age;
float weight;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void sleep()
{
System.out.println(name + "正在睡觉");
}
void mew(){
System.out.println(name + "喵喵喵~~~");
}
}
三、继承代码实现
在
Java
中如果要表示类之间的继承关系,需要借助
extends
关键字,具体如下
修饰符 class 子类 extends 父类 {
// ...
}
连接上述定义的三个类:
// Animal.java
public class Animal{
String name;
int age;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void sleep(){
System.out.println(name + "正在睡觉");
}
}
// Dog.java
public class Dog extends Animal{
void bark(){
System.out.println(name + "汪汪汪~~~");
}
}
// Cat.Java
public class Cat extends Animal{
void mew(){
System.out.println(name + "喵喵喵~~~");
}
}
// TestExtend.java
public class TestExtend {
public static void main(String[] args) {
Dog dog = new Dog();
// dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
System.out.println(dog.name);
System.out.println(dog.age);
// dog访问的eat()和sleep()方法也是从Animal中继承下来的
dog.eat();
dog.sleep();
dog.bark();
}
}
四、 子类中访问父类的成员方法
4.1. 成员方法名字不同
public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
}
public class Derived extends Base{
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodB(); // 访问子类自己的methodB()
methodA(); // 访问父类继承的methodA()
// methodD(); // 编译失败,在整个继承体系中没有发现方法methodD()
}
}
4.2 成员方法名字相同
public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
public void methodB(){
System.out.println("Base中的methodB()");
}
}
public class Derived extends Base{
public void methodA(int a) {
System.out.println("Derived中的method(int)方法");
}
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodA(); // 没有传参,访问父类中的methodA()
methodA(20); // 传递int参数,访问子类中的methodA(int)
methodB(); // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到
}
}
【上述说明】
通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(
重载
)
,根据调用 方法适传递的参数选择合适的方法访问,如果没有则报错;
问题:如果子类中存在与父类中相同的成员时,那如何在子类中访问父类相同名称的成员呢?
答:可以通过在子类中定义super.方法名进行访问父类中被子类重写的方法。
五、子类构造方法
父子父子,先有父再有子,即:子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法。
public class Base {
public Base(){
System.out.println("Base()");
}
}
public class Derived extends Base{
public Derived(){
// super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(),
// 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,
// 并且只能出现一次
System.out.println("Derived()");
}
}
public class Test {
public static void main(String[] args) {
Derived d = new Derived();
}
}
结果打印:
Base()
Derived()
扩展:如果你对子类和父类的代码执行的顺序搞不清楚可以参考:
优先级从上到下依次递减 |
静态代码(先执行父类静态在执行子类静态块) |
父类实例代码块 |
父类构造代码 |
子类实例代码 |
子类构造代码 |
六、super和this异同点
【
相同点
】
1.
都是
Java
中的关键字
2.
只能在类的非静态方法中使用,用来访问非静态成员方法和字段
3.
在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
【
不同点
】
1. this
是当前对象的引用,当前对象即调用实例方法的对象,
super
相当于是子类对象中从父类继承下来部分成
员的引用
2.
在非静态成员方法中,
this
用来访问本类的方法和属性,
super
用来访问父类继承下来的方法和属性
3.
在构造方法中:
this(...)
用于调用本类构造方法,
super(...)
用于调用父类构造方法,两种调用不能同时在构造
方法中出现
4.
构造方法中一定会存在
super(...)
的调用,用户没有写编译器也会增加,但是
this(...)
用户不写则没有
七、继承种类
注意:被final修饰的类不能被继承
八、继承和组合关系
1、概念介绍
和继承类似
,
组合也是一种表达类之间关系的方式
,
也是能够达到代码重用的效果。组合并没有涉及到特殊的语法
(
诸如
extends
这样的关键字
),
仅仅是将一个类的实例作为另外一个类的字段。
继承表示对象之间是
is-a
的关系
,比如:狗是动物,猫是动物
组合表示对象之间是
has-a
的关系
,比如:汽车
// 轮胎类
class Tire{
// ...
}
// 发动机类
class Engine{
// ...
}
// 车载系统类
class VehicleSystem{
// ...
}
class Car{
private Tire tire; // 可以复用轮胎中的属性和方法
private Engine engine; // 可以复用发动机中的属性和方法
private VehicleSystem vs; // 可以复用车载系统中的属性和方法
// ...
}
// 奔驰是汽车
class Benz extend Car{
// 将汽车中包含的:轮胎、发送机、车载系统全部继承下来
}
组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用组合。
2、优缺点对比
组 合 关 系 | 继 承 关 系 |
---|---|
优点:不破坏封装,整体类与局部类之间松耦合,彼此相对独立 | 缺点:破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性 |
优点:具有较好的可扩展性 | 缺点:支持扩展,但是往往以增加系统结构的复杂度为代价 |
优点:支持动态组合。在运行时,整体对象可以选择不同类型的局部对象 | 缺点:不支持动态继承。在运行时,子类无法选择不同的父类 |
优点:整体类可以对局部类进行包装,封装局部类的接口,提供新的接口 | 缺点:子类不能改变父类的接口 |
缺点:整体类不能自动获得和局部类同样的接口 | 优点:子类能自动继承父类的接口 |
缺点:创建整体类的对象时,需要创建所有局部类的对象 | 优点:创建子类的对象时,无须创建父类的对象 |
3、如何选择
相信很多人都知道面向对象中有一个比较重要的原则『多用组合、少用继承』或者说『组合优于继承。从前面的介绍已经优缺点对比中也可以看出,组合确实比继承更加灵活,也更有助于代码维护。并不是说继承就一点用都没有了,前面说的是【在同样可行的情况下】。有一些场景还是需要使用继承的,或者是更适合使用继承。