本文主要是介绍Java基础知识------面向对象(2),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1、继承-方法重写
1.1、重写父类方法 (override)
在继承关系中,子类会自动继承父类中定义的方法,但有时在子类中需要对继承的方法进行一些修改.即对父类的方法进行重写.
需要注意的是,在子类中重写的方法需要和父类被重写的方法具有相同的方法名,参数列表以及返回值类型.
- 总结 : 子类重新实现父类的方法,就是方法的重写.执行的时候,如果方法被重写了,那么优先执行子类的方法.
- 说明 : 虽然子类也拥有父类的行为.但是子类的这个行为实现方式和父类是不一样的.这个时候,子类就可以按照自己的方式重写父类的方法.重写父类方法的步骤就是直接在子类的实现中,按照自己的方式实现父类的方法就可以了.如果子类重写了父类的方法,那么通过子类来调用这个方法时,调用的是子类自己重写的方法. 如果子类没有重写父类的方法,则调用的是父类的方法.
-
代码案例 : 有一个 "人" 类.拥有一个sayHi的方法. 又有三个子类,"中国人", "韩国人", "日本人". 三个子类都各自重写了sayHi方法.因为三个子类对同一种行为都有其不同的表现形式.
- class ExtendsDemo {
- public static void main(String[] args) {
- Japanese j = new Japanese();
- j.sayHi();
- Korean k = new Korean();
- k.sayHi();
- Chinese c = new Chinese();
- c.sayHi();
- }
- }
- class Person{
- // 功能: 打招呼
- public void sayHi(){
- System.out.println("大家好,我是人");
- }
- }
- class Japanese extends Person{
- public void sayHi(){
- System.out.println("亚麻得,八嘎...");
- }
- }
- class Korean extends Person{
- public void sayHi(){
- System.out.println("阿尼哈斯有,泡菜思密达...");
- }
- }
- class Chinese extends Person{
- public void sayHi(){
- System.out.println("你吃了没?...");
- }}
继承体系中方法调用的顺序 :
如 : 在子类中,调用一个对象方法.
1.首先会在子类中查找该方法.
2.如果子类中没有 那么向上一层父类中查找.
3.如果上一层父类中也没有 再向上一层爷爷中查找.
4.直到Object 类.如果还是没有,直接报错.
注意 : 如果上面某一层中找到了该方法 那么直接调用,不会再向上查找了.
注意 : 子类重写父类方法时,不能使用比父类中被重写的方法更严格的访问权限.
public 公共 protected 默认 private 私有
2.super 关键字
当子类重写父类的方法后,子类对象将无法访问父类被重写的方法.为了解决这个问题.在Java中专门提供了一个suepr关键字用于访问父类的成员. 例如访问父类的成员变量,成员方法和构造方法.
- super 指代的父类.如果子类重写了父类的方法,那么调用的时候,必然优先调用子类的那个重写方法,但是如果还想调用父类那个方法的话,那么就需要使用 super 来调用.
第一步: Japanese 的打招呼.
- class ExtendsDemo
- {
- public static void main(String[] args)
- {
- Japanese j = new Japanese();
- j.sayHi();
- }
- }
- class Person
- {
- // 功能: 打招呼
- public void sayHi()
- {
- System.out.println("大家好,我是人");
- }
- }
- class Japanese extends Person
- {
- public void sayHi()
- {
- System.out.println("亚麻得,八嘎...");
- }
- }
第二步: Japanese 要证明自己是人. super.sayHi();
第三步: 为 Person 增加一个 name 的成员变量.并修改一下 Japanese 类的输出语句.
- class ExtendsDemo
- {
- public static void main(String[] args)
- {
- Japanese j = new Japanese();
- j.sayHi();
- }
- }
- class Person
- {
- // 属性: 姓名
- String name = "人类";
- // 功能: 打招呼
- public void sayHi()
- {
- System.out.println("大家好,我是人");
- }
- }
- class Japanese extends Person
- {
- public void sayHi()
- {
- super.sayHi();
- System.out.println(name+"说: 亚麻得,八嘎...");
- }
- }
第四步: 为 Japanese 增加一个 name 的成员变量.与父类的成员变量重名.再次输出查看. 默认name之前就有 this 关键字.
- class ExtendsDemo
- {
- public static void main(String[] args)
- {
- Japanese j = new Japanese();
- j.sayHi();
- }
- }
- class Person
- {
- // 属性: 姓名
- String name = "人类";
- // 功能: 打招呼
- public void sayHi()
- {
- System.out.println("大家好,我是人");
- }
- }
- class Japanese extends Person
- {
- String name;
- public void sayHi()
- {
- super.sayHi();
- System.out.println(name+"说: 亚麻得,八嘎...");
- System.out.println(this.name+"说: 亚麻得,八嘎...");
- }
- }
第五步:super.name 访问父类的成员变量. super代表在父类中查找,不会在子类中查找. 如果不写,默认为this, this表示首先在子类中查找,如果找不到,再去父类中查找.
第六步: 个性化的 Japanese 打招呼.
- class ExtendsDemo
- {
- public static void main(String[] args)
- {
- Japanese j = new Japanese();
- j.name = "苍老师";
- j.sayHi();
- }
- }
- class Person
- {
- // 属性: 姓名
- String name = "人类";
- // 功能: 打招呼
- public void sayHi()
- {
- System.out.println("大家好,我是人");
- }
- }
- class Japanese extends Person
- {
- String name;
- public void sayHi()
- {
- super.sayHi();
- System.out.println(name+"说: 亚麻得,八嘎...");
- System.out.println(this.name+"说: 亚麻得,八嘎...");
- System.out.println(super.name+"说: 亚麻得,八嘎...");
- }
- }
2.1父子类中构造方法的特点
当用一个子类的构造创建一个类的对象时,子类的构造方法总是先调用父类的某个构造方法,也就是说,如果子类的构造方法没有明显地指明使用父类的哪个构造方法子类就调用父类的不带参数的构造方法. 由于子类不继承父类的构造方法,因此子类在其构造方法中需要使用 super 来调用父类的构造方法,而且 super 必须是子类构造方法中的头一条语句。
总结:子类的每个构造方法中均有默认的super(),调用父类的空参构造,如果手动调用父类构造会覆盖默认的super()。
- class Demo
- {
- public static void main(String[] args)
- {
- Son son = new Son();
- }
- }
- class Father
- {
- // 属性
- // 构造方法
- public Father()
- {
- System.out.println("Father 构造方法被调用 ...");
- }
- // 行为
- }
- class Son extends Father
- {
- // 属性
- // 构造方法
- public Son()
- {
- System.out.println("Son 构造方法被调用 ...");
- }
- // 行为
}
注意 : 构造方法一旦重载, 系统就不再提供默认的无参构造方法了.
- class Father
- {
- // 属性
- String name;
- // 构造方法
- /*
- public Father()
- {
- System.out.println("Father 构造方法被调用 ...");
- }
- */
- public Father(String name)
- {
- this.name = name;
- System.out.println("name="+name);
- }
- // 行为
- }
- class Son extends Father
- {
- // 属性
- // 构造方法
- public Son()
- {
- System.out.println("Son 构造方法被调用 ...");
- }
- // 行为
- }
解决方案一 : 在子类中手动显式调用父类存在的构造方法.
- class Father
- {
- // 属性
- String name;
- // 构造方法
- /*
- public Father()
- {
- System.out.println("Father 构造方法被调用 ...");
- }
- */
- public Father(String name)
- {
- this.name = name;
- System.out.println("name="+name);
- }
- // 行为
- }
- class Son extends Father
- {
- // 属性
- // 构造方法
- public Son()
- {
- super("超人");
- System.out.println("Son 构造方法被调用 ...");
- }
- // 行为
- }
解决方案二 : 父类中定义一个无参的构造方法,方便子类的创建与调用.
- class Father{
- // 属性
- String name;
- // 构造方法
- public Father(){
- System.out.println("Father 构造方法被调用 ...");
- }
- public Father(String name){
- this.name = name;
- System.out.println("name="+name);
- }
- // 行为
- }
- class Son extends Father{
- // 属性
- // 构造方法
- public Son(){
- System.out.println("Son 构造方法被调用 ...");
- }
- // 行为
- }
总结: 在定义一个类时,如果没有特殊需求,尽量在类中定义一个无参的构造方法,避免被继承子类创建对象时出现错误.
2.2使用super(参数列表):调用父类构造方法初始化
说明 : 使用 super(参数列表); 实现调用父类的的构造方法让父类为自己的成员属性赋值.
实现方案一 :
- class Demo
- {
- public static void main(String[] args)
- {
- Student stu = new Student("张三", 38, '男', "itcast001");
- stu.introduce();
- }
- }
- class Person{
- // 属性
- String name;
- int age;
- char gender;
- }
- class Student extends Person{
- String stuNumber;
- public Student(String name, int age, char gender, String stuNumber){
- this.name = name;
- this.age = age;
- this.gender = gender;
- this.stuNumber = stuNumber;
- }
- void introduce()
- {
- System.out.println("大家好, 我叫" + name + ", 我今年" + age + "岁了. " + gender);
- System.out.println("我的学号是: " + stuNumber);
- }
- }
实现方案二 :
- class Demo
- {
- public static void main(String[] args) {
- Student stu = new Student("张三", 38, '男', "itcast001");
- stu.introduce();
- }
- }
- class Person
- {
- // 属性
- String name;
- int age;
- char gender;
- }
- class Student extends Person
- {
- String stuNumber;
- public Student(String name, int age, char gender, String stuNumber)
- {
- super.name = name;
- super.age = age;
- super.gender = gender;
- this.stuNumber = stuNumber;
- }
- void introduce(){
- System.out.println("大家好, 我叫" + name + ", 我今年" + age + "岁了. " + gender);
- System.out.println("我的学号是: " + stuNumber);
- }
- }
实现方案三 : 标准写法:调用父类的的构造方法让父类为自己的成员属性赋值.
- class Demo
- {
- public static void main(String[] args)
- {
- Student stu = new Student("张三", 38, '男', "itcast001");
- stu.introduce();
- }
- }
- class Person
- {
- // 属性
- String name;
- int age;
- char gender;
- public Person(String name, int age, char gender)
- {
- this.name = name;
- this.age = age;
- this.gender = gender;
- }
- public Person()
- {
- }
- }
- class Student extends Person
- {
- String stuNumber;
- public Student(String name, int age, char gender, String stuNumber)
- {
- super(name, age, gender);
- this.stuNumber = stuNumber;
- }
- void introduce()
- {
- System.out.println("大家好, 我叫" + name + ", 我今年" + age + "岁了. " + gender);
- System.out.println("我的学号是: " + stuNumber);
- }
- }
2.3子类的实例化过程
问题 : 为什么每个类的构造方法当中都需要调用隐式的 super(); 构造方法语句呢?
1、为什么任何一个类(不包含Object)的构造方法中需要一个隐式的super() 语句?
因为任何的子类在继承了父类之后,都会继承到父类的成员变量,这时在创建子类对象的时候,会在子类的对象空间中分配出空间存储父类的成员变量。一方面父类的成员变量初始化动作需要由父类的自己的构造方法完成,另一方面必须调用到Object中的构造方法对象才能真正创建成功(Object类中的本地方法申请系统资源)。所以在任何类的构造方法中有一个隐式的super()语句。
2、如果一个类中的所有构造方法全部私有化了,请问还可以有子类吗?子类能实例化吗?
一个类的构造方法全部私有,这时这个类是无法再有子类的。就不存在实例化问题。
3、如果一个类没有空参数的构造方法,问这个类可以有子类吗?子类可以实例化吗?
如果这个类还有其他的构造方法可以被访问,那么这个类就可以有子类。
这时要求在子类的构造方法中必须手动的书写super语句,同时在super( 具体的参数 )。根据指定的参数去调用父类类型相同的构造方法。
4、this调用构造方法和super调用父类构造方法可以共存吗?
不可以。因为this和super调用构造方法都必须在构造方法中的第一行。
this是调用本类其他构造方法对当前这个对象初始化,而super是调用父类构造方法进行初始化,而初始化动作在一个构造方法中只能有一个。
在一个类中有多个构造方法,之间可以使用this调用,但不能形成嵌套调用。最后肯定有一个构造方法中是没有this调用的,它必然会有super语句调用父类的构造方法。
总结:this表示本类对象,super表示对父类的引用。
3.抽象类
3.1 抽象类引入
当定义一个类时,常常需要定义一些方法来描述该类的行为特征,但有时这些方法的实现方式是无法确定的. 例如定义了一个 Animal 类, shout() 方法用于表示动物的叫声,但是针对不同的动物,叫声也是不同的.因此在 shout() 方法中无法准确描述动物的叫声.
针对这一问题, Java允许在定义方法时不写方法体,不包含方法体的方法称为抽象方法.抽象方法必须使用 abstract 关键字来修饰.
抽象类和抽象方法的简单演示 :
由于每种动物的吼叫方式都不同,父类不知该如何实现方法体,因为干脆不写方法体.
- abstract void shout(); // 定义了抽象方法 shout();
当一个类中包含了抽象方法,该类必须使用abstract关键字来修饰,使用abstract关键字修饰的类为抽象类.
- // 定义抽象类 Animal
- abstract class Animal
- {
- // 定义抽象方法 shout();
- abstract void shout();
- }
在定义抽象类时需要注意,包含抽象方法的类必须声明为抽象类,但抽象类可以不包含任何抽象方法,只需使用 abstract 关键字来修饰即可.
另外,抽象类是不可以被实例化的,因为抽象类中有可能包含抽象方法,抽象方法是没有方法体的,不可以被调用.如果想调用抽象类中定义的方法,则需要创建一个子类.在子类中将抽象类中的抽象方法进行实现.
演示步骤如下 :
没有方法体的方法需要定义为抽象.
包含了抽象方法的类必须定义为抽象类.
测试实例化一个抽象类的对象:
演示代码如下 :
- class AbstractDemo
- {
- public static void main(String[] args)
- {
- // 1. 创建一个 Animal 类的对象
- // Animal a = new Animal(); 编译失败,不能实例化对象
- // a.shout();
- }
- }
- // 定义一个 Animal 类
- abstract class Animal
- {
- // 行为: 吼叫
- abstract void shout();
- }
3.2 抽象类举例
- " 定义 狗, 猫, 狼 三个类."
- 共同的行为:
- 1. shout(); 吼叫
- 2. eat(); 吃
抽象类:
使用某个类描述行为的时候,如果这个类中在描述这个行为时,只知道该类具有此行为,但无法清楚描述该行为的具体实现代码,此时就可以使用抽象方法来描述该行为,由于类中有抽象的方法,此类必须定义为抽象类。
- // 定义一个 Animal 类
- abstract class Animal
- {
- // 行为 : 吼叫
- abstract void shout();
- // 行为 : 吃
- abstract void eat();
- }
当一个类继承某个抽象类,此时该子类需要把抽象类中的所有抽象方法全部进行重写, 给出具体的实现方法体。如果子类没有把父类中的抽象方法全部重写完,或者根本就没有重写,此时这个子类也必须是一个抽象类。抽象类是不可以被实例化的.因此子类如果想被实例化,就必须全部重写父类的抽象方法.
- class AbstractDemo
- {
- public static void main(String[] args)
- {
- // 1. 创建一个 Dog 类的对象
- Dog d = new Dog();
- d.shout();
- d.eat();
- // 2. 创建一个 Cat 类的对象
- Cat c = new Cat();
- c.shout();
- c.eat();
- // 3. 创建一个 Wolf 类的对象
- Wolf w = new Wolf();
- w.shout();
- w.eat();
- // 1. 创建一个 Animal 类的对象
- // Animal a = new Animal(); 编译失败,不能实例化对象
- // a.shout();
- }
- }
- // 定义一个 Animal 类
- abstract class Animal
- {
- // 行为: 吼叫
- abstract void shout();
- // 行为: 吃
- abstract void eat();
- }
- // 定义一个 Dog 类
- class Dog extends Animal
- {
- // 行为: 吼叫
- void shout()
- {
- System.out.println("汪汪汪...");
- }
- // 行为: 吃
- void eat()
- {
- System.out.println("跟我混,有骨头吃...");
- }
- }
- // 定义一个 Cat 类
- class Cat extends Animal
- {
- void shout()
- {
- System.out.println("喵喵喵...");
- }
- void eat()
- {
- System.out.println("跟我混,有鱼吃...");
- }
- }
- // 定义一个 Wolf 类
- class Wolf extends Animal
- {
- void shout()
- {
- System.out.println("嗷嗷嗷...");
- }
- void eat()
- {
- System.out.println("跟我混,有肉吃...");
- }
- }
在Java中使用类,一般我们都会去先查阅这个类继承体系顶层父类中的功能,在继承体系的顶层,描述的功能都是不具体的功能,都是抽象类. 也就是说我们是无法直接调用这些不具体的功能,但是我们知道了该类具备了哪些共性的方法.
因此开发中的做法是,创建顶层这个父类的引用,然后创建一个子类对象,随后就可以使用这个父类的引用,调用我们在顶层看到的功能了。(这就是多态,后面会讲)
3.3 抽象类的特点
1、抽象类一定是父类吗?
是的,因为抽象类是不断向上抽取而来的. 一定是父类,但不一定是顶层父类。
抽象类中通常都有抽象方法,而抽象类中的抽象方法要求必须由子类来重写(由具体的子类来实现其中的方法体)。
子类的主要作用 : 重写抽象类中的全部抽象方法。
2、抽象类可以继承其他类吗?
抽象类还是一个类,因此它必须具备类的继承特点。它可以有父类。
3、由于抽象类不能实例化对象,那么请问抽象类是否有构造方法 ?
有构造方法。但是这个类不能创建对象。因为抽象类一定有子类,而创建子类对象的时候,在子类的构造方法中有super语句会找自己父类的构造方法进行初始化动作。所以抽象类的构造方法是用来给子类进行初始化的.
4、抽象类中可以没有抽象方法吗?
可以。它的存在意义就是不让外界创建当前这个类的对象。
5、抽象关键字不能和哪些关键字共存?
private :父类中私有的成员变量和方法子类是不知道的,因此使用private 和abstract关键字一起修饰方法,导致子类根本无法知道父类中有这个抽象方法,从而子类无法实现重写。
static:如果使用static和abstract关键字一起修饰抽象方法,导致这个方法使用类名直接调用,从而无需创建对象, 而抽象方法是没有具体的方法实现体的.因此调用抽象方法是没有任何意义的。
final :final修饰的方法子类是无法重写的,而abstract修饰的抽象方法,目的就是为了要求子类进行重写。
抽象类何时使用:
当描述事物体系,一般在描述所有体系中最共性的内容时,通常是只知道体系的共性功能,却无法书写具体功能体,这时会使用抽象方法表示,那么这个类一定要使用抽象类表示。
综合案例---员工类系列定义
案例介绍
某IT公司有多名员工,按照员工负责的工作不同,进行了部门的划分(研发部员工、维护部员工)。研发部根据所需研发的内容不同,又分为JavaEE工程师、Android工程师;维护部根据所需维护的内容不同,又分为网络维护工程师、硬件维护工程师。
公司的每名员工都有他们自己的员工编号、姓名,并要做它们所负责的工作。
- 工作内容
- JavaEE工程师:员工号为xxx的 xxx员工,正在研发淘宝网站
- Android工程师:员工号为xxx的 xxx员工,正在研发淘宝手机客户端软件
- 网络维护工程师:员工号为xxx的 xxx员工,正在检查网络是否畅通
- 硬件维护工程师:员工号为xxx的 xxx员工,正在修复打印机
请根据描述,完成员工体系中所有类的定义,并指定类之间的继承关系。进行XX工程师类的对象创建,完成工作方法的调用。
案例分析
- 根据上述部门的描述,得出如下的员工体系图
- 根据员工信息的描述,确定每个员工都有员工编号、姓名、要进行工作。则,把这些共同的属性与功能抽取到父类中(员工类),关于工作的内容由具体的工程师来进行指定。
- 工作内容
- JavaEE工程师:员工号为xxx的 xxx员工,正在研发淘宝网站
- Android工程师:员工号为xxx的 xxx员工,正在研发淘宝手机客户端软件
- 网络维护工程师:员工号为xxx的 xxx员工,正在检查网络是否畅通
- 硬件维护工程师:员工号为xxx的 xxx员工,正在修复打印机
- 创建JavaEE工程师对象,完成工作方法的调用
案例代码实现
- 根据员工体系图,完成类的定义
定义员工类(抽象类)
public abstract class Employee {
private String id;// 员工编号
private String name; // 员工姓名
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//工作方法(抽象方法)
public abstract void work();
}
- 定义研发部员工类Developer 继承 员工类Employee
public abstract class Developer extends Employee {
}
- 定义维护部员工类Maintainer 继承 员工类Employee
public abstract class Maintainer extends Employee {
}
- 定义JavaEE工程师 继承 研发部员工类,重写工作方法
public class JavaEE extends Developer {
@Override
public void work() {
System.out.println("员工号为 " + getId() + " 的 " + getName() + " 员工,正在研发淘宝网站");
}
}
- 定义Android工程师 继承 研发部员工类,重写工作方法
public class Android extends Developer {
@Override
public void work() {
System.out.println("员工号为 " + getId() + " 的 " + getName() + " 员工,正在研发淘宝手机客户端软件");
}
}
- 定义Network网络维护工程师 继承 维护部员工类,重写工作方法
public class Network extends Maintainer {
@Override
public void work() {
System.out.println("员工号为 " + getId() + " 的 " + getName() + " 员工,正在检查网络是否畅通");
}
}
- 定义Hardware硬件维护工程师 继承 维护部员工类,重写工作方法
public class Hardware extends Maintainer {
@Override
public void work() {
System.out.println("员工号为 " + getId() + " 的 " + getName() + " 员工,正在修复打印机");
}
}
- 在测试类中,创建JavaEE工程师对象,完成工作方法的调用
public class Test {
public static void main(String[] args) {
//创建JavaEE工程师员工对象
JavaEE ee = new JavaEE();
//设置该员工的编号
ee.setId("000015");
//设置该员工的姓名
ee.setName("小明");
//调用该员工的工作方法
ee.work();
}
}
这篇关于Java基础知识------面向对象(2)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!