目录
继承
基本语法
package animal; public class Animal { public String name; public void eat(String food) { System.out.println(this.name + "吃" + food); } }
package animal; public class Cat { public String name; public void eat(String food) { System.out.println(this.name + "吃" + food); } }
package animal; public class Dog { public String name; public void eat(String food) { System.out.println(this.name + "吃" + food); } }
我们发现其中存在了大量的冗余代码,3个类的代码完全相同。
仔细分析, 我们发现 Animal 和 Cat 以及 Bird 这几个类中存在一定的关联关系:Dog is an Animal、Cat is an Animal
这三个类都具备一个相同的 eat 方法, 而且行为是完全一样的。
这三个类都具备一个相同的 name 属性, 而且意义是完全一样的。
此时我们就可以让 Cat 和 Dog 分别继承 Animal 类, 来达到代码重用的效果。Animal 这样被继承的类, 我们称为 父类 , 基类 或 超类,对于像 Cat 和 Dog 这样的类,我们称为 子类, 派生类。
当一个类继承了另一个类,另一个类中所有的属性和方法子类就天然具备了。
Java中使用 extends 表示类的继承
package animal; public class Animal { public String name; public void eat(String food) { System.out.println(this.name + "吃" + food); } }
package animal; public class Cat extends Animal { }
package animal; public class Dog extends Animal { }
package animal; public class Test { public static void main(String[] args) { Animal animal = new Animal(); animal.name = "动物"; animal.eat("食物"); Dog dog = new Dog(); dog.name = "狗子"; dog.eat("狗粮"); } } //输出结果 动物吃食物 狗子吃狗粮
继承的规则
继承的规则:
a. 要能使用继承,前提必须满足类之间的 is a 关系
b. 一个子类只能使用 extends 继承一个父类。(单继承)
Java 中不允许多重继承。extends 后面只能跟一个父类,允许多层继承(没法当儿子,可以当孙子)。
狸花猫 Lihua 这个类不能继承 Animal、Cat 等多个类。
c. 子类会继承父类的所有属性和方法,显示继承(public属性和方法可以直接使用)、隐式继承(private属性和方法)。子类其实也继承了这个属性和方法,但是无法直接使用。
隐式继承
package animal; public class Animal { public String name; // age 这个属性只在当前类的内部可见。子类也继承了,但是不能直接使用。 private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void eat(String food) { System.out.println(this.name + "吃" + food); } }
package preson; import animal.Animal; public class Person extends Animal { public void fun() { // name 属性是 protected 权限,在子类 Person 中可以直接使用 System.out.println(name); // age 属性是 private 权限,在子类 Person 中不能直接使用 System.out.println(age); } }
package animal; public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.name = "狗子"; dog.age = 2; //报错,无法直接使用 //子类其实也继承了这个属性和方法 //通过使用父类提供的 set get可以使用父类的 private 属性 dog.setAge(10); System.out.println(dog.name + " " + dog.getAge() + "岁"); } } //输出结果 狗子 10岁
隐式继承:子类没法直接使用该属性,必须通过父类提供的方法来操作。虽然我继承了父亲,但是无法直接使用父亲的物品(父类的私有属性)。只有通过父亲的允许(父类提供方法)才能使用。
同理:静态的属性和方法是归于某个类所有,当一个类继承了另一个类,所有静态属性和方法也被继承。能否直接使用,要看权限是不是 public 。
1. public 权限的静态属性
//Animal.java Animal类单独的java文件 package animal; public class Animal { public static String test = "123";//公有属性 } //Dog.java Dog类单独的java文件 package animal; public class Test { public static void main(String[] args) { Dog dog = new Dog(); System.out.println(Dog.test); } } //输出 123
2. private 权限的静态属性
//Animal.java Animal类单独的java文件 package animal; public class Animal { private static String test = "123";//私有属性 } //Dog.java Dog类单独的java文件 package animal; public class Test { public static void main(String[] args) { Dog dog = new Dog(); System.out.println(Dog.test); } } //输出错误 java: test在 animal.Animal 中是 private 访问控制
关于 protected 访问权限
protected 权限 > default 包权限,protected 的范围包含了 default 可见的范围。
范围:protected = default + 继承。
对于不同包的有继承关系的子类 和 同一个包的无继承关系的类(不包含子包) 来说,protected 修饰的属性和方法是可以访问的。
//Animal.java Animal类单独的java文件 package animal; public class Animal { //protected 修饰,当前类和其子类是可见的,没有继承关系的类之间不可见 protected String name = "测试"; } } //Test.java Test类单独的java文件 package animal; import preson.Person; public class Test { public static void main(String[] args) { Animal animal = new Animal(); // 在同一个包的无继承关系的类中调用 protected System.out.println(animal.name); } } //输出结果 测试
此处 Animal类 与 Test类 在一个 animal 包内。虽然两个类没有继承关系,但是 Test 类中也可以调用 Animal 类中的 protected属性。
//Animal.java Animal类单独的java文件 package animal; public class Animal { //protected 修饰,当前类和其子类是可见的,没有继承关系的类之间不可见 protected String name = "测试"; } } //Test.java Test类单独的java文件 package preson; import animal.Animal; public class Test { public static void main(String[] args) { Animal animal = new Animal(); System.out.println(animal.name);//错误 } } //输出结果 java: name 在 animal.Animal 中是 protected 访问控制
此处Test 类在 person 包中,Animal 类在 animal 包中。两个类既不在一个包中,也不构成继承关系,故输出错误。
//Animal.java Animal类单独的java文件 package animal; public class Animal { //protected 修饰,当前类和其子类是可见的,没有继承关系的类之间不可见 protected String name = "测试"; } //Person.java Person类单独的java文件 package preson; import animal.Animal; public class Person extends Animal { } //Test.java Test类单独的java文件 package preson; public class Test { public static void main(String[] args) { Person per = new Person(); System.out.println(per.name); // per.name错误 } } //输出错误 java: name 在 animal.Animal 中是 protected 访问控制
虽然 Person 类继承了 Animal 类,但是 per.name 这行代码发生在 Test 类中。Test 类与 Animal 类没有任何关系。
protected 只在 同包的类或不同包具有继承关系的类内部 可见。
package a; public class Base { protected String name = "小明"; private int age = 10; } package b; import a.Base; public class SubType extends Base { //子类未定义 name 属性 public void fun() { System.out.println(name);//正确 System.out.println(this.name);//正确 System.out.println(super.name);//正确 //无法在父类外部,使用父类引用 Base base = new Base(); System.out.println(base.name);//报错 //可以通过本类引用 SubType subType = new SubType(); System.out.println(subType.name);//正确 } }
碰到继承权限的属性,通过 this 或 super 在子类中使用。
要产生一个子类对象,默认先产生父类对象
//Animal.java Animal类单独的java文件 package animal; public class Animal { public Animal() { System.out.println("1.先调用父类的构造方法"); } } //Person.java Person类单独的java文件 package preson; import animal.Animal; public class Person extends Animal { public Person() { System.out.println("2.再产生子类对象,调用子类对象的构造方法"); } } //Test.java Test类单独的java文件 package preson; public class Test { public static void main(String[] args) { Person per = new Person(); } } //输出结果 1.先调用父类的构造方法 2.再产生子类对象,调用子类对象的构造方法
当调用 new Person 无参构造产生子类对象之前,先默认调用父类的构造方法产生父类对象然后才会执行子类的构造方法。
继承中的 this 与 super 关键字
this 关键字
当有继承关系时
this 关键字默认先在当前类中寻找同名属性;若没找到,继续向上寻找父类中是否有同名属性。
若直接使用 name。编译器默认都是 this.name 。1. 当前类中寻找同名属性
//Person.java Person类单独的java文件 package supertest; public class Person { public String name = "person"; } //China.java China类单独的java文件 package supertest; public class China extends Person { public String name = "china"; public void fun() { // 在访问成员变量的时候,推荐加上this关键字,尤其是在有继承的时候 System.out.println(this.name);//就近匹配原则 } public static void main(String[] args) { China china = new China(); china.fun(); } } //输出结果 china
2. 子类无同名属性,寻找父类中同名属性
//Person.java Person类单独的java文件 package supertest; public class Person { public String name = "person"; } //China.java China类单独的java文件 package supertest; public class China extends Person { public void fun() { System.out.println(this.name);//寻找父类同名属性 } public static void main(String[] args) { China china = new China(); china.fun(); } } //输出结果 person
若此时父类成员属性变为 private; privat String name = "person";
//输出错误 java: name 在 supertest.Person 中是 private 访问控制
super 关键字
super 修饰属性
this 直接从当前类中找同名属性,若不存在再向上搜索。类似的, super 先从直接父类中寻找同名属性,若不存在再向上寻找。
//Animal.java Animal类单独的java文件 package supertest; public class Animal { protected String name = "animal"; } //Person.java Person类单独的java文件 package supertest; public class Person extends Animal { //China类的父类Person类未定义属性 } //China.java China类单独的java文件 package supertest; public class China extends Person { protected String name = "china"; public void fun() { System.out.println(super.name); } public static void main(String[] args) { China china = new China(); china.fun(); } } //输出结果 aniaml
若父类未定义属性,执行 super.name 语句,super 先从直接父类中寻找同名属性,若不存在再向上寻找。
若此时父类成员属性变为 private; private String name = "person";
//Person.java Person类单独的java文件 package supertest; public class Person extends Animal { private String name = "person"; }
使用super.name 会报错。其实 super.name 找到了 Person 类中的 name(若找到了,就不再向上寻找),但因为是私有属性,所以无权访问(隐式继承)。
super 修饰方法
1. 修饰构造方法
//Animal.java Animal类单独的java文件 package supertest; public class Animal { protected String name = "animal"; public Animal() { System.out.println("Animal的无参构造"); } } //Person.java Person类单独的java文件 package supertest; public class Person extends Animal { public String name = "person"; public Person() { System.out.println("Person的无参构造"); } } //China.java China类单独的java文件 package supertest; public class China extends Person { public String name = "china"; public China() { System.out.println( "China的无参构造"); } public static void main(String[] args) { China china = new China(); } } //输出结果 Animal的无参构造 Person的无参构造 China的无参构造
当产生子类对象时,默认先产生父类对象。若父类对象还有继承,继续向上先产生祖类对象。
//Person.java Person类单独的java文件 package supertest; public class Person { public String name = "person"; public Person(String name) { this.name = name; System.out.println("Person的有参构造"); } } //China.java China类单独的java文件 package supertest; public class China extends Person { public String name = "china"; //显示使用 super 调用父类的有参构造 public China() { //super();默认调用父类的无参构造 System.out.println( "China的无参构造"); } public static void main(String[] args) { China china = new China(); } } //输出报错
若此时 Person 类只有有参构造,Person默认的无参构造就不再产生。此时需要显示使用 super 调用父类的有参构造。
小结:
super 修饰构造方法
1. super(父类构造方法的参数);
2. super(); //直接父类的无参构造,可写可不写
若父类中不存在无参构造,则子类构造方法的首行必须显示使用super(有参构造)。
//Person.java Person类单独的java文件 package supertest; public class Person { public String name = "person"; public Person(String name) { this.name = name; System.out.println("Person的有参构造"); } } //China.java China类单独的java文件 package supertest; public class China extends Person { protected String name = "china"; public China() { //先要产生父类对象,就得显示调用父类的构造方法 super("父类"); System.out.println("China的无参构造"); } public static void main(String[] args) { China china = new China(); } } //输出结果 Person的无参构造 China的无参构造
在一个构造方法中无法显式的同时出现使用 this() 和 super() 。
//Person.java Person类单独的java文件 package supertest; public class Person { public String name = "person"; public Person() { System.out.println("Person的无参构造"); //3. 执行 Person 的无参 } public Person(String name) { this.name = name; System.out.println("Person的有参构造"); } } //China.java China类单独的java文件 package supertest; public class China extends Person { protected String name = "china"; public China() { System.out.println("China的无参构造"); //5. 执行China 类的无参 } public China(String name) { //super(); //2. 编译器隐式调用父类 Person 的无参 this(); //4. 调用 China 类的无参 System.out.println("China的有参构造"); //6. 执行 China 类的有参 } public static void main(String[] args) { China china = new China("儿子"); //1. 主方法开始 } } //输出结果 Person的无参构造 China的无参构造 China的有参构造
1. 从主方法开始先执行 China 类的的有参构造,编译器默认隐式调用 super() 语句在 China 有参构造首行(若显示使用则报错);
2. super 语句会调用父类 Person 类的无参构造;
3. 调用后执行 Person 的无参构造
4. this() 语句调用 China 的无参构造
5. 调用后执行 China 的无参构造
6. 最后执行 China 的有参构造
2. 修饰普通方法
super 修饰普通方法,和修饰属性,直接从父类中寻找同名方法。
//Person.java Person类单独的java文件 package supertest; public class Person { public String name = "person"; public Person() { System.out.println("Person的无参构造"); } public void fun() { System.out.println("Person的fun方法"); } } //China.java China类单独的java文件 package supertest; public class China extends Person { protected String name = "china"; public China() { System.out.println("China的无参构造"); } public void fun() { System.out.println("China的fun方法"); } public void test() { fun(); //默认执行最近的 fun 方法 super.fun(); //执行父类的 fun 方法 } public static void main(String[] args) { China china = new China(); china.test(); } } //输出结果 Person的有参构造 China的无参构造 China的fun方法 Person的fun方法
this 可以表示当前对象的引用,super不能指代当前父类的对象引用。
报错
继承中的 final 关键字
final 修饰 Person 类,则 China 类无法继承 Person 类
类之间的组合关系
类和类之间继承关系,相当于 is a
类A is a类B——>类A继承于类BDog extends Animal { }
类和类之间组合关系,相当于 has a
School has a student
School has a teacher
//Student.java Student类单独的java文件 public class Student { } //Teacher.java Teacher类单独的java文件 public class Teacher { } //School.java School类单独的java文件 public class School { public Student[] students; public Teacher[] teachers; }