继承的格式
通过 extends
关键字,可以声明一个子类继承另外一个父类,定义格式如下:
【修饰符】 class 父类 { ... } 【修饰符】 class 子类 extends 父类 { ... }
代码演示
/* * 定义动物类Animal,做为父类 */ class Animal { // 定义name属性 public String name; // 定义age属性 public int age; // 定义动物的吃东西方法 public void eat() { System.out.println(age + "岁的" + name + "在吃东西"); } } /* * 定义猫类Cat 继承 动物类Animal */ class Cat extends Animal { // 定义一个猫抓老鼠的方法catchMouse public void catchMouse() { System.out.println("抓老鼠"); } } /* * 定义测试类 */ public class ExtendDemo01 { public static void main(String[] args) { // 创建一个猫类对象 Cat cat = new Cat(); // 为该猫类对象的name属性进行赋值 cat.name = "Tom"; // 为该猫类对象的age属性进行赋值 cat.age = 2; // 调用该猫的catchMouse()方法 cat.catchMouse(); // 调用该猫继承来的eat()方法 cat.eat(); } } 演示结果: 抓老鼠 2岁的Tom在吃东西
继承的特点一:成员变量
私有化(private)
- 父类中的成员,无论是公有(public)还是私有(private),均会被子类继承。
- 子类虽会继承父类私有(private)的成员,但子类不能对继承的私有成员直接进行访问,可通过继承的公有方法进行访问。
代码如下:
/* * 定义动物类Animal,做为父类 */ class Animal { // 定义name属性 private String name; // 定义age属性 public int age; // 定义动物的吃东西方法 public void eat() { System.out.println(age + "岁的" + name + "在吃东西"); } } /* * 定义猫类Cat 继承 动物类Animal */ class Cat extends Animal { // 定义一个猫抓老鼠的方法catchMouse public void catchMouse() { System.out.println("抓老鼠"); } } /* * 定义测试类 */ public class ExtendDemo01 { public static void main(String[] args) { // 创建一个猫类对象 Cat cat = new Cat(); // 为该猫类对象的name属性进行赋值 //t.name = "Tom";// 编译报错 // 为该猫类对象的age属性进行赋值 t.age = 2; // 调用该猫的catchMouse()方法 t.catchMouse(); // 调用该猫继承来的eat()方法 t.eat(); } }
成员变量不重名
如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。代码如下:
class Fu { // Fu中的成员变量。 int num01 = 3; } class Zi extends Fu { // Zi中的成员变量 int num02 = 4; // Zi中的成员方法 public void show() { // 访问父类中的num, System.out.println("num1 = " + num1); // 访问子类中的num2 System.out.println("num2 = " + num2); } } class ExtendDemo02 { public static void main(String[] args) { // 创建子类对象 Zi z = new Zi(); // 调用子类中的show方法 z.show(); } } 演示结果: num1 = 3 num2 = 4
成员变量重名
如果子类父类中出现重名的成员变量,这时的访问是有影响的。代码如下:
class Fu { // Fu中的成员变量。 int num = 3; } class Zi extends Fu { // Zi中的成员变量 int num = 4; public void show() { // 访问的num到底是子类还是父类? System.out.println("num = " + num); } } class ExtendsDemo03 { public static void main(String[] args) { // 创建子类对象 Zi z = new Zi(); // 调用子类中的show方法 z.show(); } } 演示结果: num = 4
子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用super
关键字,修饰父类成员变量,类似于之前学过的 this
。
使用格式:
super.父类成员变量名
子类方法需要修改,代码如下:
class Zi extends Fu { // Zi中的成员变量 int num = 6; public void show() { //访问父类中的num System.out.println("Fu num=" + super.num); //访问子类中的num System.out.println("Zi num=" + this.num); } } 演示结果: Fu num = 5 Zi num = 6
小贴士:Fu 类中的成员变量是非私有的,子类中可以直接访问。若Fu 类中的成员变量私有了,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用private修饰成员变量,那么如何访问父类的私有成员变量呢?对!可以在父类中提供公共的getXxx方法和setXxx方法。
练习:代码分析
class Father{ int a = 10; int b = 11; } class Son extends Father{ int a = 20; public void test(){ //子类与父类的属性同名,子类对象中就有两个a System.out.println("父类的a:" + super.a);//10 System.out.println("子类的a:" + this.a);//20 System.out.println("子类的a:" + a);//20 //子类与父类的属性不同名,是同一个b System.out.println("b = " + b);//11 System.out.println("b = " + this.b);//11 System.out.println("b = " + super.b);//11 } public void method(int a){ //子类与父类的属性同名,子类对象中就有两个成员变量a,此时方法中还有一个局部变量a System.out.println("父类的a:" + super.a);//10 System.out.println("子类的a:" + this.a);//20 System.out.println("局部变量的a:" + a);//30 } } public class TestInherite2 { public static void main(String[] args) { Son son = new Son(); System.out.println(son.a);//20 son.test(); son.method(30); } }
继承的特点二:成员方法
当类之间产生了关系,其中各类中的成员方法,又产生了哪些影响呢?
成员方法不重名
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。代码如下:
class Fu{ public void show(){ System.out.println("Fu类中的show方法执行"); } } class Zi extends Fu{ public void show2(){ System.out.println("Zi类中的show2方法执行"); } } public class ExtendsDemo04{ public static void main(String[] args) { Zi z = new Zi(); //子类中没有show方法,但是可以找到父类方法去执行 z.show(); z.show2(); } }
成员方法重名——重写(Override)
如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。
代码如下:
在父子类的继承关系当中,创建子类对象,访问成员方法的规则:
创建的对象是谁,就优先用谁,如果没有则向上找。
注意事项:
无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。
重写(Override)
概念:在继承关系当中,方法的名称一样,参数列表也一样。
重写(Override):方法的名称一样,参数列表【也一样】。覆盖、覆写。
重载(Overload):方法的名称一样,参数列表【不一样】。
方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。
方法覆盖重写的注意事项:见下面
重写的应用
子类可以根据需要,定义特定于自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从而进行扩展增强。比如新的手机增加来电显示头像的功能,代码如下:
class Phone { public void sendMessage(){ System.out.println("发短信"); } public void call(){ System.out.println("打电话"); } public void showNum(){ System.out.println("来电显示号码"); } } //智能手机类 class NewPhone extends Phone { //重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能 public void showNum(){ //调用父类已经存在的功能使用super super.showNum(); //增加自己特有显示姓名和图片功能 System.out.println("显示来电姓名"); System.out.println("显示头像"); } } public class ExtendsDemo06 { public static void main(String[] args) { // 创建子类对象 NewPhone np = new NewPhone(); // 调用父类继承而来的方法 np.call(); // 调用子类重写的方法 np.showNum(); } }
这里重写时,用到super.父类成员方法,表示调用父类的成员方法。
注意事项 必须保证父子类之间方法的名称相同,参数列表也相同。 @Override:写在方法前面,用来检测是不是有效的正确覆盖重写。 这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。 子类方法的返回值类型必须【小于等于】父类方法的返回值类型(小于其实就是是它的子类)。 小扩展提示:java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就是Object的子类。 注意:如果返回值类型是基本数据类型和void,那么必须是相同 子类方法的权限必须【大于等于】父类方法的权限修饰符。 小扩展提示:public > protected > 缺省 > private 备注:缺省不是汉字缺省,而是什么都不写,留空。 几种特殊的方法不能被重写 静态方法不能被重写 私有等在子类中不可见的方法不能被重写 final方法不能被重写
练习
1、声明父类:Person类 包含属性:姓名,年龄,性别 属性私有化,get/set 包含getInfo()方法:例如:姓名:张三,年龄:23,性别:男
2、声明子类:Student类,继承Person类 新增属性:score成绩 属性私有化,get/set 包含getInfo()方法:例如:姓名:张三,年龄:23,性别:男,成绩:89
3、声明子类:Teacher类,继承Person类 新增属性:salary薪资 属性私有化,get/set 包含getInfo()方法:例如:姓名:张三,年龄:23,性别:男,薪资:10000
public class Person { private String name; private int age; private char gender; public Person(String name, int age, char gender) { super(); this.name = name; this.age = age; this.gender = gender; } public Person() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public char getGender() { return gender; } public void setGender(char gender) { this.gender = gender; } //包含getInfo()方法:例如:姓名:张三,年龄:23,性别:男 public String getInfo(){ return "姓名:" + name + ",年龄:" + age +",性别:" + gender; } }
public class Student extends Person { private int score; public Student() { } public Student(String name, int age, char gender, int score) { setName(name); setAge(age); setGender(gender); this.score = score; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } //包含getInfo()方法:例如:姓名:张三,年龄:23,性别:男,成绩:89 public String getInfo(){ //方式一: // return "姓名:" + getName() + ",年龄:" + getAge() + ",成绩:" + score; //方法二: return super.getInfo() + ",成绩:" + score; } }
public class Teacher extends Person { private double salary; public Teacher() { } public Teacher(String name, int age, char gender, double salary) { setName(name); setAge(age); setGender(gender); this.salary = salary; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } //包含getInfo()方法:例如:姓名:张三,年龄:23,性别:男,薪资:10000 public String getInfo(){ return super.getInfo() + ",薪资:" + salary; } }
public class TestPersonExer2 { public static void main(String[] args) { Person p = new Person("张三", 23, '男'); System.out.println(p.getInfo()); Student s = new Student("陈琦", 25, '男', 89); System.out.println(s.getInfo()); Teacher t = new Teacher("柴林燕", 18, '女', 11111); System.out.println(t.getInfo()); } }