目录
1.继承的概念
2. 继承语法
3. 父类成员的访问
3.1 子类中访问父类的成员变量
3.1.1 子类和父类不存在同名成员变量
3.1.2 子类和父类成员变量同名
3.2 子类中访问父类的成员方法
3.2.1 成员方法名不同
3.2.2 成员方法名相同
4. super关键字
5. 子类构造方法
6. 再谈初始化
7. protected 关键字
8. 继承方式
9. final 关键字
9.1 修饰变量或字段
9.2 修饰类
10. 继承与组合
1.继承的概念
继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生的新的类称为派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
继承主要解决的问题是:共性的抽取,实现代码复用;
2. 继承语法
在java中,表示类的继承关系需要使用到exends关键字:
修饰限定符 子类 extends 父类{
//...
}
class Animal{
public String name;
public int age;
public void fun1(){
System.out.println("It is an animal.");
}
}
class Dog extends Animal{
public void Bark(){
System.out.println("The dog named " +name+" is barking.");
}
}
class Cat extends Animal{
public void Meow(){
System.out.println("The cat named "+ name+" is meowing.");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.fun1();
dog.name = "Mike";
dog.Bark();
Cat cat = new Cat();
cat.fun1();
cat.name= "Mary";
cat.Meow();
}
}
输出结果为:
注:(1)子类会将父类中的成员变量或成员方法继承到子类中;
(2)子类继承父类之后,必须要新增自己特有的成员以体现与父类的不同,否则不需要继承;
3. 父类成员的访问
3.1 子类中访问父类的成员变量
3.1.1 子类和父类不存在同名成员变量
class Base{
public int a = 10;
public int b = 20;
}
class Derived extends Base{
public int c = 30;
public void fun(){
System.out.println("a = "+a);
System.out.println("b = "+b);
System.out.println("c = "+c);
}
}
public class Main {
public static void main(String[] args) {
Derived derived = new Derived();
derived.fun();
}
}
输出结果为:
结论1:当子类当中不存在与父类同名的属性,在子类内读写属性时,如果子类中存在该属性则直接读取,如果子类当中不存在该属性就去父类当中寻找;
3.1.2 子类和父类成员变量同名
class Base{
public int a = 10;
public int b = 20;
}
class Derived extends Base{
public int a = 100;
public int b = 200;
public int c = 30;
public void fun(){
System.out.println("a = "+a);
System.out.println("b = "+b);
System.out.println("c = "+c);
}
}
public class Main {
public static void main(String[] args) {
Derived derived = new Derived();
derived.fun();
}
}
输出结果为:
结论2:当子类当中存在与父类同名的属性,当在子类内读写属性时,会优先读取子类的属性;
如果需要指定访问父类中的属性,则需使用super关键字:
class Base{
public int a = 10;
public int b = 20;
}
class Derived extends Base{
public int a = 100;
public int b = 200;
public int c = 30;
public void fun(){
System.out.println("a = "+ super.a);
System.out.println("b = "+ super.b);
System.out.println("c = "+c);
}
}
public class Main {
public static void main(String[] args) {
Derived derived = new Derived();
derived.fun();
}
}
输出结果为:
注:结论2:当在子类内读写属性时,如果依次在子类、父类中都查询无果,则会编译报错;
3.2 子类中访问父类的成员方法
3.2.1 成员方法名不同
class Base{
public int a = 10;
public int b = 20;
public void methodBase(){
System.out.println("Base function.");
}
}
class Derived extends Base{
public int a = 100;
public int b = 200;
public int c = 30;
public void methodDerived(){
System.out.println("Derived function.");
}
public void fun1(){
methodBase();
methodDerived();
}
}
public class Main {
public static void main(String[] args) {
Derived derived = new Derived();
derived.fun1();
}
}
输出结果为:
3.2.2 成员方法名相同
class Base{
public int a = 10;
public int b = 20;
public void methodBase(){
System.out.println("Base function(methodBase).");
}
}
class Derived extends Base{
public int a = 100;
public int b = 200;
public int c = 30;
public void methodDerived(){
System.out.println("Derived function.");
}
public void methodBase(){
System.out.println("Derived function(method Base).");
}
public void fun1(){
methodBase();
}
}
public class Main {
public static void main(String[] args) {
Derived derived = new Derived();
derived.fun1();
}
}
输出结果为:
在子类中访问父类方法与在子类中访问父类属性的规则相同,就近访问子类的方法,如果该方法在子类中不存在就去父类中查询,如果两个类都查询无果则报错;同样,在子类访问与父类同名的方法时如需指定访问父类方法则需使用super关键字:
class Base{
public int a = 10;
public int b = 20;
public void methodBase(){
System.out.println("Base function(methodBase).");
}
}
class Derived extends Base{
public int a = 100;
public int b = 200;
public int c = 30;
public void methodDerived(){
System.out.println("Derived function.");
}
public void methodBase(){
System.out.println("Derived function(method Base).");
}
public void fun1(){
super.methodBase();
}
}
public class Main {
public static void main(String[] args) {
Derived derived = new Derived();
derived.fun1();
}
}
输出结果为:
注:
class Base{
public void methodBase(){
System.out.println("Base function(methodBase).");
}
}
class Derived extends Base{
public void methodBase(int a){
System.out.println("Derived function(method Base).");
}
public void fun1(){
methodBase();
}
}
在上文代码中:
① 分别位于父类和子类中的methodBase方法构成重载;
② 由于子类中的该方法需要一个int型参数,而在调用时并未传参,故而无需使用super.methodBase,也调用的是父类的该方法:
在java官方文档中关于重载的定义如下:
详见: https://docs.oracle.com/javase/specs/index.html
4. super关键字
在上文已介绍,super关键字的主要功能是:在子类中指定访问父类的属性或方法:
super.data:在子类当中访问父类的成员属性;
super.func():在子类当中访问父类的成员方法;
注:① 只能在非静态方法内使用;
② 在子类方法中访问父类的属性或方法;
5. 子类构造方法
子类对象构造时需要先调用其父类的构造方法,然后再执行子类的构造方法;
class Animal{
public String name;
public int age;
public void fun1(){
System.out.println("It is an animal.");
}
public Animal(){
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Dog extends Animal{
public String breed;
public Dog(String name,int age,String breed){
super(name,age);
//显式调用父类构造方法对父类成员进行初始化
this.breed = breed;
}
public void Bark(){
System.out.println("The dog named " +name+" is barking.");
}
@Override
public String toString() {
return "Dog{" +
"breed='" + breed + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Cat extends Animal{
public String color;
public Cat(String name,int age,String color){
//super();
//当父类拥有无参构造方法时,编译器会默认给子类增加一个父类的构造方法
// 此时可以不再显式书写super();
super(name,age); //为传参完整,此处以带参父类构造方法为例
this.color = color;
}
public void Meow(){
System.out.println("The cat named "+ name+" is meowing.");
}
@Override
public String toString() {
return "Cat{" +
"color='" + color + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("Mike",5,"Labrador");
System.out.println(dog);
Cat cat = new Cat("Mary",5,"grey");
System.out.println(cat);
}
}
输出结果为:
注:(1)子类调用父类构造方法时必须将super语句放在子类构造函数的第一行,否则就会报错;
(2)super(...)只能在子类构造方法中出现一次且与this不能同时出现;
(3)当没有在父类中显式书写带参构造方法时,编译器会默认增加一个无参的构造方法,故而建议在编写类尤其是有可能发生继承拥有子类时,重载一个无参的构造方法与一个带参的构造方法;
6. 再谈初始化
class Animal{
public String name;
public int age;
//静态代码块
static{
System.out.println("Animal static{}");
}
//实例代码块
{
System.out.println("Animal{}");
}
//无参构造方法
public Animal(){
System.out.println("Animal(){}");
}
}
class Dog extends Animal{
public String breed;
//无参构造方法
public Dog(){
System.out.println("Dog(){}");
}
//静态代码块
static{
System.out.println("Dog static{}");
}
//实例代码块
{
System.out.println("Dog {}");
}
}
public class Main {
public static void main(String[] args) {
Dog dog1 = new Dog();
System.out.println("------------");
Dog dog2 = new Dog();
}
}
输出结果为:
7. protected 关键字
创建如下包与类:
(1)同一包中的不同类:
试运行以下代码:
以上代码可以正常运行,说明protected修饰的属性在同一包中的不同类中可以进行访问;
(2)不同包中的子类:
注:(1)上文代码的前提是TestDemo1类是public修饰的;
(2)private修饰的成员不是没有被继承,而是继承了无法访问;
8. 继承方式
(1)在Java中只支持以下几种继承方式:
单继承(B继承A),多继承(C继承B,B继承A),不同类继承同一个类(B和C都继承A);
(2)C++支持多继承,但Java不支持多继承(C继承A和B);
(3)一般我们不希望有超过三层的继承关系,如果想通过语法进行限制,则需使用final关键字;
9. final 关键字
9.1 修饰变量或字段
final int A= 10;
A=20;
报错如下:
final修饰变量或字段表示常量,代表不能被修改,并建议名称改为大写;
9.2 修饰类
运行如下代码:
final class Animal{
public String name;
public int age;
public Animal(){
System.out.println("Animal(){}");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Dog extends Animal{
public String breed;
public Dog(String name,int age,String breed){
super(name,age);
//显式调用父类构造方法对父类成员进行初始化
this.breed = breed;
}
public Dog(){
System.out.println("Dog(){}");
}
public void Bark(){
System.out.println("The dog named " +name+" is barking.");
}
}
class Cat extends Animal{
public String color;
public Cat(String name,int age,String color){
super(name,age);
this.color = color;
}
public void Meow(){
System.out.println("The cat named "+ name+" is meowing.");
}
}
public class Main {
public static void main(String[] args) {
Dog dog1 = new Dog();
Dog dog2 = new Dog();
}
}
报错如下:
final修饰类表示该类不能再被继承;
注:① 如果需要将成员变量定义为final,语法规定必须同时给定一个初始值;
② 运行如下代码:
public static void main(String[] args) {
final int[] array = {1,2,3};
array = new int[10];
}
报错如下:
因为此时final修饰的是引用array,即array保存的值(指向的对象)是不可以更改的,但是array引用指向对象的内容是可以改变的,即以下代码可以正常运行:
public static void main(String[] args) {
final int[] array = {1,2,3};
array[0]=2;
}
10. 继承与组合
与继承类似,组合也是一种表示类与类之间关系的方式(a part of);
以教师、学生、学校以及学院类为例:
class Students{ //学生类
}
class Teachers{ //教师类
}
class Campus{
public Students[] students = new Students[3];
public Teachers[] teachers = new Teachers[3];
//将另外一个类作为当前类的一个属性称为组合
}
class academy extends Campus{ //学院类继承学校类
}
public class Main {
public static void main(String[] args) {
Campus campus = new Campus();
}
}
内存分布: