Java面向对象特征2之继承性
1 - 什么是继承?派生?
/*
①一个新类从已有的类获得其已有的特性,称之为类的继承。
②从已有的类(父类/基类)产生一个新的子类(派生类。称之为类的派生)。
*/
2 - 为什么要有继承?
/*
面向对象的特征2:继承性
继承性的好处
①减少代码的冗余,提高代码的复用性
②便于功能的扩展
③为之后多态性的使用,提高了前提
*/
3 - 继承性的使用格式
/*
继承性的使用格式:class A extends B{}
A:子类、派生类、subclass
B:父类、超类、基类、superclass
*/
4 - 子类继承父类以后有哪些不同?
/*
体现1:一旦子类A继承父类B以后,子类A中就获取了父类中声明的所有结构:属性、方法特别的:父类中声明为private的属性或方法,子类继承父类以后,任然认为获取了父类中私有的结构,只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
体现2:子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的扩展。子类和父类的关系,不同于子集和集合的关系。
extend:延展、扩展
*/
5 - Java中继承性的规定
/*
①一个类可以被多个子类继承
②Java中类的单继承性:一个类只能有一个父类
③子父类是相对的概念。
④子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
⑤子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
*/
*
6 - java.lang.Object类的理解
/*
1.如果我们没有显示的声明一个类的父类的话,则此类继承于java.lang.Object类
2.所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
3.意味着,所有的java类具有java.lang.Object类声明的功能
*/
7 - 类的继承性练习
/*
练习1
*/
/*
代码实现
*/
package com.lzh.exer; /* * 定义一个ManKind类,包括成员变量int sex和int salary。方法void manOrWoman(),根据sex的值显示"man"(sex==1)或者"woman"(sex==0). * 方法 void employeed(),根据salary的值显示"no job"(salary==0)或者"job"(salary!=0) */ public class ManKind { private int sex; // 性别 private int salary; // 薪资 public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } // 构造器 public ManKind() { } public ManKind(int sex,int salary) { this.sex = sex; this.salary = salary; } // 方法 public void manOrWoman() { if(sex == 1) { System.out.println("man"); }else if(sex == 0) { System.out.println("woman"); } } public void emploeey() { // if(salary != 0) { // System.out.println("job"); // }else { // System.out.println("on job"); // } String res = (salary == 0)? "no job" : "job"; System.out.println(res); } }
package com.lzh.exer; /* * 定义类Kids继承ManKind,并包括成员变量int yearsOld,方法printAge()打印yearsOld的值 */ public class Kids extends ManKind{ private int yearsOld; public int getYearsOld() { return yearsOld; } public void setYearsOld(int yearsOld) { this.yearsOld = yearsOld; } /** * 无参构造器 */ public Kids() { } /** * 有参构造器 */ public Kids(int yearsOld) { this.yearsOld = yearsOld; } // 方法 public void printAge() { System.out.println("I am "+yearsOld); } }
package com.lzh.exer; /* * 定义类KidsTest,在类的main方法中实例化Kids的对象someKid,用该对象访问其父类的成员变量即方法 */ public class KidsTest { public static void main(String[] args) { Kids someKid = new Kids(22); int yearsOld = someKid.getYearsOld(); System.out.println(yearsOld); // 22 someKid.printAge(); someKid.setSex(0); someKid.setSalary(0); someKid.emploeey(); // no job someKid.manOrWoman(); // woman } }
/*
练习2
*/
/*
代码实现
*/
package com.lzh.exer1; public class Circle { private double redius; public double getRedius() { return redius; } public void setRedius(double redius) { this.redius = redius; } // 构造器 public Circle() { this.redius = 1.0; } // 返回圆的面积 public double findArea() { return Math.PI * redius * redius; } }
package com.lzh.exer1; public class Cylinder extends Circle{ private double length; // 高 public double getLength() { return length; } public void setLength(double length) { this.length = length; } /** * 无参构造器 */ public Cylinder() { length = 1.0; } // 返回圆柱的体积 public double findVolume() { // return Math.PI * getRedius() * getRedius * getLength(); return findArea() * getLength(); } }
package com.lzh.exer1; public class CylinderTest { public static void main(String[] args) { Cylinder cylinder = new Cylinder(); cylinder.setRedius(2.1); // 设置底面半径 cylinder.setLength(3.4); // 设置圆柱的高 // 计算圆柱的体积 double volume = cylinder.findVolume(); System.out.println("圆柱的体积为"+volume); // 计算圆的面积 double area = cylinder.findArea(); System.out.println("圆的面积为"+area); } }
8 - 方法的重写(overrides / overwrite)
/*
1-定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
2-应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
3-重写的规定
方法的声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常类型 {
// 方法体
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
①子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
②子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
>特殊情况:子类不能重写父类中声明为private权限的方法
③返回值类型:
>父类被重写的返回值类型是void,则子类重写的方法的返回值类型只能是void
>父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类(Object->String)
>父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型
④子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
注意:子类和父类中同名同参数的方法要么否声明为非static的(考虑重写),要么都声明为static的(不是重写)
*/
package com.lzh.overwrite; public class Person { String name; int age; public Person() { } public Person(String name,int age) { this.name = name; this.age = age; } public void eat() { System.out.println("吃饭"); } public void walk(int m) { show(); System.out.println("走了"+m+"千米"); } private void show() { System.out.println("我是一个人"); } public Object info() { return null; } public double info1() { return 1.0; } }
package com.lzh.overwrite; public class Student extends Person{ String major; public Student() { } public Student(String major) { this.major = major; } public void study() { System.out.println("学习"); } public void exam() { System.out.println("考试"); } // 重写父类的方法 // 方法要与父类的方法名和形参列表 public void eat() { System.out.println("学生应该多吃有营养的事物"); } public void show() { System.out.println("我是一个学生"); } public String info() { return null; } // public int info1() { // return 1.0; // } 编译不通过 public double info1() { return 100.0; } }
package com.lzh.overwrite; /* * 方法的重写(over) */ public class Test { public static void main(String[] args) { Student s = new Student("计算机"); // s.eat(); // 调用重写的方法 s.walk(10); // s.study(); // s.exam(); } }
9 - super关键字的使用
/*
super关键字的使用
1-super理解为:父类的
2-可以用来调用:属性、方法、构造器
3-super的使用:调用属性和方法
①我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显示的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
②特殊情况:当子类和父类中定义了同名的属性时,我们想要在子类中调用父类中声明的属性,则必须显示的使用"super.属性"的方式,表明调用的是父类中声明的属性。
③特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显示的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
4-super调用构造器
①我们可以在子类的构造器中显示的使用"super(形参列表)"的方式,调用父类中声明的指定构造器
②"super(形参列表)"的使用,必须声明在子类构造器的首行
③我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
④在构造器的首行,没有显示的声明"this(形参列表)"或"super(形参列表)"时,则默认调用的是父类中空参的构造器:super();
⑤在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
*/
/*
代码示例
*/
package com.lzh.exer; public class Person { String name; int age; int id = 11; // 身份证号(假设) public Person() { } public Person(String name) { this.name = name; } public Person(String name,int age) { this(name); this.age = age; } public void eat() { System.out.println("人:吃放"); } public void walk() { System.out.println("人:走路"); } }
package com.lzh.exer; public class Student extends Person{ String major; int id = 22; // 学号 public Student() { } public Student(String major) { this.major = major; } public Student(String name,int age,String major) { super(name,age); // 调用父类构造器 this.major = major; } @Override public void eat() { // TODO Auto-generated method stub super.eat(); System.out.println("学生:多吃有营养的食物"); } public void study() { // this.eat(); 此时在当前类中找到了eat()方法,不再去父类中查看 super.eat(); // 直接跳过当前类去父类中查找。 System.out.println("学生:学习"); } public void showInfo() { System.out.println("没用super的 id: "+this.id); System.out.println("name: "+this.name+","+"age: "+super.age); System.out.println("用super的 id: "+super.id); } }
package com.lzh.exer; public class SuperTest { public static void main(String[] args) { Student s = new Student("计算机"); s.showInfo(); // id等于11是Person类中设置的 s.study(); System.out.println("*****************************"); Student s1 = new Student("howie", 22, "IT"); s1.showInfo(); } }
10 - 子类对象的实例化过程
/*
子类对象实例化的全过程
1 - 从结果上来看:
子类继承父类以后,就获取了父类中声明的属性或方法
创建子类的对象,在对空间中,就会加载所有父类中声明的属性
2 - 从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器
直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用
3 - 说明:
虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
*/
图解
*