Java基础-面向对象第二特征之继承(Inheritance)

                                      作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.继承的概述

 在现实生活中,继承一般指的是子女继承父辈的财产。在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成了一种关系体系。例如公司中的研发部员工,运维部员工,人事部员工都属于员工,程序中便可以描述为研发部员工和维护部员工继承自员工,同理,JavaEE工程师和Python工程师继承自研发部员工,而网络运维工程师和系统运维工程师继承自维护部员工,人事部经理和培训专员继承自人事部员工。这些员工之间会形成一个继承体系,具体如下图所示:

  在Java中,类的继承是指在一个现有的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称作父类,子类会自动拥有父类所有可继承的属性和方法。

二.继承的使用方式(extends)

1>.什么是父类和子类

  多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要从抽取出来的那个类扩展(extends)即可,需要扩展的类称为子类(也叫派生类),抽取出来的那个类称为父类(也可以叫超类或者基类)。

2>.继承的定义格式和使用

  继承一个类使用关键字extends来实现,格式为:"class 子类 extends 父类{}",下面有一个很简单的继承案例,如下:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ //定义员工类:成员变量(name),成员方法(work)
public class Employee {
String name; public void work(){
System.out.println("员工在工作.....");
}
}

Employee.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ //定义研发部员工,属于员工中的一种,研发员工继承员工类,使用关键字extends。
public class Development extends Employee{
public void print(){
System.out.println(name);
}
}

Development.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ public class Test {
public static void main(String[] args){
Development d = new Development();
d.name = "yinzhengjie";
d.print();
}
}

3>.继承的好处

  a>.继承的出现提高了代码的复用性,提高了软件开发效率;

  b>.继承的出现让类与类之间产生了关系,提供了多态的前提(多态本篇博客暂时不涉及)。

4>.继承的注意事项

  a>.在Java中,类只支持单继承,不允许多继承,也就是说一个类只能有一个直接父类,不可以有多个父类。

  b>.Java支持多层继承,多重继承(在Java中,子类和父类是一种相对概念,也就是说一个类是某个父类的同时,也可以是另一个类的子类。例如:C类:同时拥有A和B的属性和方法;B类:只拥有A类属性和方法)

  c>.多个类中可以继承一个父类。

5>.继承的体系

  继承也有自己的体系,我们可以下面的一张图来说明特点。

6>.继承后子类父类成员变量的特点

  子类的对象,在调用成员变量的时候遵循以下顺序:

    a>.先在子类的方法中找(方法是压栈执行,里面的变量存在栈内存中,属于基本数据类型);

    b>.再找子类的成员变量,子类自己有就使用自己的(类中的成员变量属于引用数据类型,成员变量存在堆内存中,需要用到关键字this);

    c>.最后再找父类的成员变量,子类没有的话就调用父类的(需要用到关键字super);

    d>.如果以上步骤都没有找到,就会编译报错!

  说白了,就是遵循就近原则!下面的3个文件是我实际测试的代码。

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ public class Father {
int age = 40;
}

Father.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ public class Son extends Father {
int age = 18; //该变量存在与堆内存中。 public void print() {
int age = 20; //该变量随着方法弹栈执行,因此改变量存在栈内存。
System.out.println("当前作用域的age变量的值为:"+age);
System.out.println("本类中成员变量age的值为:"+this.age);
System.out.println("父类中成员变量age的值为:"+super.age);
}
}

Son.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ public class Test {
public static void main(String[] args){
Son s = new Son();
s.print();
}
} /*
以上代码此时结果如下:
当前作用域的age变量的值为:20
本类中成员变量age的值为:18
父类中成员变量age的值为:40
*/

Test.java 文件内容

7>.子类重写父类方法(Overrride|Overwrite)

  子类中,出现了和父类一模一样的方法(函数的返回值类型,函数名,参数列表都一样)的时候,子类重写父类的方法,也可以说是子类覆盖了父类的方法(当子类需要父类功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样即沿袭了父类的功能又定义了子类特有的内容。)。方法的重写是指两个类中的名词哟,而方法的重载指的的同一个类中的名词,大家不要混淆哟!

  a>.子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为重写或者复写;

  b>.父类中私有方法不可以被重写,当然,私有方法也不能被继承;

  c>.在子类重写方法中,继续使用被重写的方法可以通过super.方法名(...);

  d>.覆盖注意事项:覆盖时,子类方法权限一定要大于父类方法权限;除了访问权限之外,其它部分和父类方法保持一致;静态只能覆盖静态。

  方法重写是应该注意,子类方法覆盖父类方法,必须要保证权限大于等于父类权限。下面的三个文件就是一个简单的重写案例:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ public class Father {
public void print() {
System.out.println("父类的print()方法!");
}
}

Father.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ public class Son extends Father {
public void print() {
System.out.println("子类的print()方法!");
}
}

Son.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ public class Test {
public static void main(String[] args){
Son s = new Son();
s.print();
}
} /*
以上代码此时结果如下:
子类的print()方法!
*/

Test.java 文件内容

  总结:当一个类中是另一个类中的一种时,可以通继承,来继承属性和功能。如果父类具备的功能内容需要子类特殊定义时,进行方法重写。

8>.子类的实例化过程

  a>.子类中所有的构造方法默认都会访问父类中空参构造的构造方法,因为每一个构造方法的第一行都有一条默认的语句super(),除非第一行用this或super显式调用了其它的构造方法;

  b>.子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的,也就是父类对象必须在子类对象初始化前完成初始化操作(没有父类,就没有子类);

  c>.当父类中没有空参构造方法时,子类的构造方法必须通过this或者super语句制定要访问的构造方法;

三.抽象类的使用方式(abstract)

1>.抽象类概述

a>.抽象定义

  抽象就是从多个事物中将共性,本质的内容抽取出来(例如:狼和狗共性都是犬科,犬科就是抽象出来的概念)。

b>.抽象类

  Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。

c>.抽象方法的由来

  多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,值抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。(例如:狼和狗都有吼叫的方法,可是吼叫的内容是不一样的,所以抽象出啦ID全科虽然有吼叫的功能,但是并不明确吼叫的细节。)

2>.抽象类的由来

  如果在父类中定义一个各个子类都有不同实现的方法,那么子类需要重写这个方法,为什么父类还要定义这个方法呢?直接去掉不行么?答案是肯定的,去掉当然可以啊,只不过这样就没法使用多态啦!由于多态的要求:父类引用指向子类实例,但父类不能调用子类独有的方法,为了使用多态,父类中必须定义有这个方法,但是方法的实现又会被子类重写。

  为了既能使用多态,又能省去在父类中现实的麻烦,Java提供了抽象方法,在父类中只定义方法签名,不用写方法的实现。

3>.抽象类的特点

  a>.抽象类和抽象方法必须使用abstract关键字来修饰;

  b>.抽象方法只有方法声明,没有方法体,定义在抽象类中(格式:“修饰符  abstract   返回值类型   方法名(形参列表);”);

  c>.抽象类不可以被实例化,换句话说,就是抽象类不能用new 创建抽象类的对象(从理论上来说:抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例,例如:犬科是一种抽象的概念,真正存在的是狼和狗。从调用角度来说:假设抽象类可以创建对象,那么调用其抽象方法将变得毫无意义。);

  d>.抽象类通过子类实例化,如果子类继承了抽象类,重写了一部分的抽象方法,这个子类还是抽象类(换句话说,子类需要实现所有的抽象方法后才可以创建对象,否在该子类依然是抽象类)。

4>.抽象类的应用案例

a>.雇员示例

需求:

  公司中程序员有姓名,工号,薪水,工作内容。

  项目经理除了有姓名,工号,薪水,还有奖金,工作内容。

  程序员和项目经理都是雇员,但是工作内容却不同,所以工作反方应该定义成抽象反方。

  接下来,我们就来实现一下这个案例:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ //雇员抽象类,定义:姓名,工号,薪水
public abstract class Employee{
private String name;
private String id;
private int salary; public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setId(String id){
this.id = id;
}
public String getId(){
return id;
}
public void setSalary(int salary){
this.salary = salary;
}
public int getSalary(){
return salary;
} public void show(){
System.out.printf("姓名:%s\n工号:%s\n薪资:%d\n",name,id,salary);
} //抽象方法work
public abstract void work();
}

Employee.java 抽象类定义

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ //项目经理类
public class Manager extends Employee{
//奖金
private int money;
public void setMoney(int money){
this.money = money;
}
public int getMoney(){
return money;
}
//重写show方法
public void show(){
System.out.printf("姓名:%s\n工号:%s\n薪资:%d\n奖金:%d\n",getName(),getId(),getSalary(),getMoney());
} //实现抽象方法
public void work(){
System.out.println("项目经理开会...");
}
}

Manager.java 子类定义

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ //程序员类
public class Programmer extends Employee{
//实现抽象方法
public void work(){
System.out.println("程序员写代码...");
}
}

Programmer.java 子类定义

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ //雇员测试类
public class EmployeeDemo{
public static void main(String[] args){
Programmer p = new Programmer();
p.setName("tom");
p.setId("9527");
p.setSalary(8000);
p.show(); //调用的是抽象类(Employee)的方法
p.work(); //调用的是程序员类(Programmer)实现抽象方法的方法 Manager m = new Manager();
m.setName("John");
m.setId("0001");
m.setSalary(7000);
m.setMoney(8000);
m.show(); //调用的是项目经理类(Manager)重写的方法
m.work(); //调用的是项目经理类(Manager)实现抽象方法的方法
}
} /*
以上代码测试结果如下:
姓名:tom
工号:9527
薪资:8000
程序员写代码...
姓名:John
工号:0001
薪资:7000
奖金:8000
项目经理开会...
*/

EmployeeDemo.java 测试类文件内容

b>.抽象类作为形参

  抽象类作为形参,此时需要传入的参数是此抽象类实现的一个对象。抽象类作为返回值类型,实际上返回的是此抽象类子类的一个对象。下面的简单的代码实现:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ //定义一个抽象类A
abstract class A{
public abstract void show();
}
//子类B继承抽象类A
class B extends A{
//子类B实现父类的抽象方法
public void show(){
System.out.println("B.show()...");
}
}
//子类C继承抽象类A
class C extends A{
//子类C实现父类的抽象方法
public void show(){
System.out.println("C.show()...");
}
}
//定义将抽象类A作为形参的类
class ParameterAbstract{
public void ParameterAbstract(A a){
//此时需要传入的参数是此抽象类的实现类的一个对象,也就是说,此时的a一定是一个A的子类对象,
a.show();
}
public A getA(){
B b = new B();
//实际上返回的是此抽象类子类的一个对象
return b;
}
} public class AbstractDemo{
public static void main(String[] args){
B b = new B();
//用匿名对象的方式调用ParameterAbstract方法,传入的参数是抽象类实现子类的一个对象
new ParameterAbstract().ParameterAbstract(b); C c = new C();
ParameterAbstract t = new ParameterAbstract();
//调用ParameterAbstract类的构造方法
t.ParameterAbstract(c);
}
} /*
以上代码执行结果如下:
B.show()...
C.show()...
*/

5>.抽象类的细节

  a>.抽象类可以不可以不包含任何抽象方法?

    答:可以,此时这个类的作用就是不让外界实例化。

  b>.抽象类能否有构造方法?

    答:抽象类中可以有成员变量,而构造方法的作用就是对成员变量初始化的,所以,可以有构造方法。

  c>.抽象类关键字abstract不可以和那些关键字共存?

    答:第一个关键字是final(final方法不能被子类重写,而抽象反方就是需要子类重写的,两者冲突);第二个关键字是static(静态可以通过类名调用,但是本类中并没有真正的反方体);第三个关键字是private(私有的方法子类访问不到,无法实现)。

四.面向对象常用的关键字介绍

1>.final

  继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些类在描述之后,不想被继承,或者有些类中的部分方法功能是固定的,不想让子类重写。可是当子类继承了这些特殊类之后,就可以对其中的方法进行重写,那怎么解决呢?

  要解决上述的这些问题,需要使用到一个关键字final,final的意思为最终,不可变。final是个修饰符,它可以用来修饰类,类的成员(不包括构造方法),以及局部变量。

  a>.final修饰类则为最终类,即不可以被继承,但是可以继承其它类。

  b>.final修饰的方法不可以被覆盖,但父类中没有被final修饰方法,子类覆盖(重写)后可以加final。

  c>.final修饰的变量称为常量,这些变量只能赋值一次。

  d>.final修饰的引用类型的变量值为对象地址值,地址值就不能更改,但是地址对象的属性可以修改;

  e>.final修饰成员变量,需要在创建对象前赋值,否则报错(构造方法,是创建对象中的事情,可以为成员变量赋值,set方法是创建对象之后的事情,不能为final修饰的成员变量赋值哟!);

2>.this

 作用:this代表当前正在调用方法的对象

 使用场景: a>.setXxx方法中对成员变量赋值:区分成员变量和局部变量;

       b>.构造方法互相调用(注意:构造方法中使用this或者super调用其它构造方法的话必须是构造方法的第一条语句);

       c>.方法中调用本类其它方法(“this(....);”);

3>.super

  super和this的用法类似,this代表本类对象的引用,super代表父类对象的内存的标识

  使用场景:a>.子父类出现同名成员时,用super进行区分(“super.成员变量”或者“super.成员方法()”);

          b>.子类使用super调用父类的构造方法(“super(....);”)

4>.static

  在定义类的时候,类中都会有相应的属性和方法。而属性和方法都是通过创建本类对象调用的。当在调用对象的某个方法,这个方法没有访问到对象的特有数据时,方法创建这个对象有些多余。可是不创建对象,方法又调用不了,这是就会想,那么我们能不能不创建对象,就可以调用方法呢?

  可以的,我们可以通过关键字static来实现,static它是静态修饰符,一般用来修饰类中的成员。

  a>.被static修饰的成员变量属于类(随着类的加载而加载),不属于这个类的某个对象(也就是说,多个对象在访问或修改static修饰的成员变量时,其中一个对象将static成员变量值进行了修改,其它对象中的static成员变量值跟着改变,即多个对象共享同一个static成员变量,这时被static修饰的成员变量并不在堆中,而是在方法区中保存);

  b>.被static修饰的成员可以直接通过类名的方式访问;

  c>.优先与对象存在(也就是说用static修饰的方法不能直接调用非静态的方法,如果你非要在静态方法中调用非静态方法,那就只能现在静态方法中创建一个对象,然后通过调用对象中的方法的形式调用非静态方法);

  d>.静态方法中不能写this也不能写super;

  f>.静态方法只能访问静态成员(变量,方法),当然静态方法也可以间接访问非静态方法(需要现在静态中new出来一个对象,通过对象调用相应的方法即可),非静态的方法可以访问静态成员;

  g>.被这个类的所有对象共享;

  什么时候使用static呢?推荐方式:如果方法没有调用过非静态成员,建议将方法定义为静态!

5>.静态变量和实例变量的区别

  a>.所属不同

    静态变量属于类,也称为类变量。实例变量属于对象,也称为对象(实例)变量。

  b>.在内存中的位置不同

    静态变量在方法区中的静态区,实例变量在堆内存。

  c>.生命周期不同

    静态变量随着类的加载而加载,随着类的消失而消失,实例变量随着对象的创建而存在,随着对象的消失而消失。

  d>.调用方法不同

   静态变量可以通过类名和对象名两名方式调动,推荐使用类名调用实例变量只能使用对象名的方式调用。

Java基础-面向对象第二特征之继承(Inheritance)的更多相关文章

  1. Java基础--面向对象编程3(继承)

    1.继承的作用 为了提取两个类中公共的代码,可以使用继承抽取重复性的代码到一个公共类中. 这个公共的类称为父类(super class),继承于父类的类称为子类(sub class). 2.java继 ...

  2. 第二十七节:Java基础面向对象-静态,单例模式,继承详情知识点

    前言 Java基础面向对象-静态,单例模式,继承详情知识点.静态-static关键字,static变量,静态代码块,代码块(不加静态),对象创建过程,单例模式,继承. 静态-static关键字 // ...

  3. Java基础-面向对象第一特性之封装(Encapsulation)

    Java基础-面向对象第一特性之封装(Encapsulation) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.理解什么是面向过程和面向对象 面向过程与面向对象都是我们编程中 ...

  4. Java基础-面向对象第三大特性之多态(polymorphism )

    Java基础-面向对象第三大特性之多态(polymorphism) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.多态概述 多态是继封装,继承之后,面向对象的第三大特性,多态的 ...

  5. Java中面向对象三大特征

    也就是说在这里"人"是多态的, 在不同的形态时,特征行为是不一样的, 这里的"人", 同时有两种形态,一种是教师形态,一种是学生形态,所对应的特征行为分别是&q ...

  6. java基础面向对象之类与对象

    java基础面向对象之类与对象 2017-01-14 1.面向对象的基本概念 以一种组建化的形式进行代码设计 1)在面向对象程序设计中包含有如下几种特性 •封装性:保护内部结构的安全性 •继承性:在已 ...

  7. Java的OOP三大特征之一——继承

    Java的OOP三大特征之一——继承 子类继承父类的特征和行为(属性和方法),使得子类具有父类的各种属性和方法.或子类从父类继承方法,使得子类具有父类相同的行为. 特点:在继承关系中,父类更通用.子类 ...

  8. 九、Java基础---------面向对象封装、继承、多态

    封装 1.1 基本概念  封装(encapsulation)是面向对象三大特征之一,它是指将对象的状态信心隐藏在对象的内部,不允许外部直接进行访问,而是通过该类提供的方法来实现对内部信息的操作和访问. ...

  9. Java基础-四要素之一《继承》

    继承的概念: 继承在本职上是特殊——一般的关系,即常说的is-a关系.子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的一些属性或方法. 继承是所有OOP语言不可缺少的部分,在java中使 ...

随机推荐

  1. OO学习第二阶段总结

    面向对象课程的第二个阶段结束了,作业们由简单的玩具模式步入到复杂的多线程地狱模式,由之前的算法简单实现有坑转变成算法复杂实现有很大的坑.一个最重要的心得就是一定要在动手敲代码实现之前对整个程序的实现做 ...

  2. css方法div固定在网页底部

    css .bottom{width:%;height:40px;background:#ededed;;}/*重点后两句*/ body <div class="bottom" ...

  3. gogoing软件NABCD

    N,need 需求:gogoing项目目前打算做得是一个基于石家庄铁道大学在校大学生对于短期节假日出行旅游的指南.最关键的定义为“穷游”.“穷”则体现在以小的花销去实现最完美的旅游方式.我们的gogo ...

  4. ListViewAnimations使用时报错NoClassDefFoundError: com.nineoldandroids.animation.Animator

    见 https://github.com/nhaarman/ListViewAnimations/issues/294 解决: Add this to your dependencies in you ...

  5. 经验分享(Android开发)

    以前对于Android开发一点了解都没有,当然,以前觉得是一件很高大上的事情,而且是我没有能力去做的工作,但是在这个小组合作开发Android后,我觉得我有了很大的进步,当然我的进步也是Android ...

  6. web国际化,在不同的浏览环境,显示不同的语言

    所谓国际化就是支持多种语言,web应用在不同的浏览环境中可以显示出不同的语言.假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回 ...

  7. 联想本win10 virtualbox 安装centos

    (1)必须开发操作系统虚拟化功能,参考该百度经验 https://jingyan.baidu.com/article/8275fc864d423e46a03cf638.html (2)调整虚拟机硬盘和 ...

  8. Eslint 配置及规则说明(报错)

    https://blog.csdn.net/violetjack0808/article/details/72620859 https://blog.csdn.net/hsl0530hsl/artic ...

  9. BZOJ5100 POI2018Plan metra(构造)

    容易发现要么1和n直接相连,要么两点距离即为所有dx,1+dx,n的最小值.若为前者,需要满足所有|d1-dn|都相等,挂两棵菊花即可.若为后者,将所有满足dx,1+dx,n=d1,n的挂成一条链,其 ...

  10. JVM类加载机制详解(二)类加载器与双亲委派模型

    在上一篇JVM类加载机制详解(一)JVM类加载过程中说到,类加载机制的第一个阶段加载做的工作有: 1.通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件).而获取的方式,可 ...