各位朋友们,大家好!今天我为大家分享的是Java上面一个很重要的知识点:继承,继承也被称为Java的灵魂之一。
在生活中,我们都见过猫和狗这两种动物吧,我们都知道Java是一种面向对象的语言,我们可以创建出任何我们想要的对象,那么如果我们想要用Java来创建出猫和狗该怎么办呢?在创建之前我们首先得知道猫和狗有什么特性。我们都知道猫和狗都有名字、体重、身高,都会吃饭、睡发出叫声,那么我们可以通过创建类将他们的特性包含在内。
//猫class Cat { String name; float weight; float hight; public void Eat() { System.out.println(name+"正在吃"); } public void Sleep() { System.out.println(name+"在睡觉"); } public void bark(){System.out.println(name+"汪汪汪~~~");}}
//狗class Dog { String name; float weight; float hight; public void Eat() { System.out.println(name+"正在吃"); } public void Sleep() { System.out.println(name+"正在睡觉"); } public void bark(){System.out.println(name+"喵喵喵~~~");}}
当我们看到两个类的时候,我们可能会发现问题:那就是这两个代码中有很多重复的成员变量,那么如果我们要想大大降低代码的复用率该怎么办呢?这里最好的解决方法就是使用我们所说的继承,因为猫和狗都属于动物,我们可以将猫和动物以及其他类型的动物的共性都给提出来作为父类,子类只需要继承父类就可以拥有父类所具有的特性。
//Animal作为父类具有子类的共特性class Animal { String name; float weight; float hight; public void Eat() { System.out.println(name+"正在吃"); } public void Sleep() { System.out.println(name+"在睡觉"); }}
我们子类要想继承父类,我们需要用到extends这个关键字。格式是
class 子类类名 extends 父类类名{}
所以我们上面的代码可以简化成这样:
class Animal { String name; float weight; float hight; public void Eat() { System.out.println(name+"正在吃"); } public void Sleep() { System.out.println(name+"在睡觉"); }}class Cat extends Animal{ public void bark(){System.out.println(name+"汪汪汪~~~");}}class Dog extends Animal{ public void bark(){System.out.println(name+"喵喵喵~~~");}}
而猫和狗的内存分配大概是这样的。
一般在子类中我们可以直接访问父类的成员变量。就像这样:
class Animal { String name = "Animal"; float weight; float hight; public void Eat() { System.out.println(name+"正在吃"); } public void Sleep() { System.out.println(name+"在睡觉"); }}class Cat extends Animal{ public void Method() { System.out.println(name); } public void Bark() { System.out.println(name+"喵喵喵~~~"); }}public class Main { public static void main(String[] args) { Cat cat =new Cat(); cat.Method(); }}
这种是子类和父类没有相同的成员变量,如果子类和父类有相同的成员变量时,我们访问的又是谁的成员变量呢?
class Animal { String name = "Animal"; float weight; float hight; public void Eat() { System.out.println(name+"正在吃"); } public void Sleep() { System.out.println(name+"在睡觉"); }}class Cat extends Animal{ String name = "大黄"; public void Method() { System.out.println(name); } public void Bark() { System.out.println(name+"喵喵喵~~~"); }}public class Main { public static void main(String[] args) { Cat cat =new Cat(); cat.Method(); }}
我们可以看到这里他访问的是子类自己拥有的成员,也就是说在这里遵守就近原则,方法也是如此。
class Animal { String name = "Animal"; float weight; float hight; public void Eat() { System.out.println(name+"正在吃"); } public void Sleep() { System.out.println(name+"在睡觉"); } public void Method1() { System.out.println("this is Animal's Method"); }}class Cat extends Animal{ String name = "大黄"; public void Method() { System.out.println(name); } public void Method1() { System.out.println("this is Cat's Method"); } public void Bark() { System.out.println(name+"喵喵喵~~~"); }}public class Main { public static void main(String[] args) { Cat cat =new Cat(); cat.Method1(); }}
那么如果我们想在子类中访问相同的成员的父类的成员该怎么办呢?
super关键字
我们需要用到super这个关键字来访问父类中与子类具有相同名字的成员。
class Animal { String name = "Animal"; float weight; float hight; public void Eat() { System.out.println(name+"正在吃"); } public void Sleep() { System.out.println(name+"在睡觉"); } public void Method1() { System.out.println("this is Animal's Method"); }}class Cat extends Animal{ String name = "大黄"; public void Method() { System.out.println(name); } public void Method1() { System.out.println("this is Cat's Method"); } public void Method2() { System.out.println(super.name); } public void Bark() { System.out.println(name+"喵喵喵~~~"); }}public class Main { public static void main(String[] args) { Cat cat =new Cat(); cat.Method2(); }}
我们也可以用super来访问父类的方法。
class Animal { String name = "Animal"; float weight; float hight; public void Eat() { System.out.println(name+"正在吃"); } public void Sleep() { System.out.println(name+"在睡觉"); } public void Method1() { System.out.println("this is Animal's Method"); }}class Cat extends Animal{ String name = "大黄"; public void Method() { System.out.println(name); } public void Method1() { System.out.println("this is Cat's Method"); } public void Method2() { System.out.println(super.name); } public void Method3() { Method1(); super.Method1(); } public void Bark() { System.out.println(name+"喵喵喵~~~"); }}public class Main { public static void main(String[] args) { Cat cat =new Cat(); cat.Method3(); }}
当我们的父类中有构造方法时,我们在构造子类的时候需要在构造方法的第一行使用super(),来先帮助父类完成构造,然后再完成自己的构造方法。
class Base { public Base() { System.out.println("Base"); }}class Derived extends Base{ public Derived() { super(); System.out.println("Derived"); }}public class Main { public static void main(String[] args) { Derived deriver = new Derived(); }}
不仅如此我们都知道如果你的类中没有构造方法,编译器会默认帮你生成一个无参的构造方法,如果你的类中还有继承关系,那么在构造方法的第一行还会默认有super()。
class Base { public Base() { System.out.println("Base"); }}class Derived extends Base{}public class Main { public static void main(String[] args) { Derived deriver = new Derived(); }}
但是如果你的父类中有有参数的构造方法,你就必须先帮助父类完成构造,否则编译器会报错。
this关键字
我们在实例化一个类时,往往是先完成这个类具有的构造方法,而构造方法往往是给类的成员变量赋值的,那么我们怎样通过构造方法给成员变量赋值呢?
这里就需要this,this代表当前类实例的一个引用,它可以在构造器、成员方法和代码块中使用,表示当前类的实例对象。
class Base { String name; public Base(String name) { this.name = name; System.out.println(name); }}class Derived extends Base{ public Derived() { super("大黄"); }}public class Main { public static void main(String[] args) { Derived deriver = new Derived(); }}
this的功能不止这些,我们都知道方法可以发生重载,构造方法也是如此。
我们可以使用this(),来完成该类中的其他的构造方法。
class Base { String name; int age; public Base(String name) { //完成该类中带有两个参数的构造方法 this(name,0); this.name = name; System.out.println(name); } public Base(String name,int age) { this.name = name; this.age = age; System.out.println(name+age); }}class Derived extends Base{ public Derived() { super("大黄"); }}public class Main { public static void main(String[] args) { Derived deriver = new Derived(); }}
但是这里我们需要注意的是this()跟super()一样,都必须放在构造方法的第一行,也就是说super()和this()不能同时出现。
class Person { public String name; public int age; public Person(String name, int age) { this.name = name; this.age = age; System.out.println("构造方法执行"); } { System.out.println("实例代码块执行"); } static { System.out.println("静态代码块执行"); }}public class Main { public static void main(String[] args) { Person person1 = new Person("zhangsan",10); System.out.println("==========================="); Person person2 = new Person("lisi",18); }}
我们看看这段代码的结果是什么?
通过这段代码我们可以知道静态代码块、实例代码块和构造方法的执行顺序是:先执行静态代码块,然后是实例代码块,最后是构造方法,并且静态代码块只会执行一次。
那么如果有继承关系的时候执行顺序又是怎样的呢?
class Person { public String name; public int age; public Person(String name, int age) { this.name = name; this.age = age; System.out.println("Person:构造方法执行"); } { System.out.println("Person:实例代码块执行"); } static { System.out.println("Person:静态代码块执行"); }}class Student extends Person{ public Student(String name,int age) { super(name,age); System.out.println("Student:构造方法执行"); } { System.out.println("Student:实例代码块执行"); } static { System.out.println("Student:静态代码块执行"); }}public class Main { public static void main(String[] args) { Student student1 = new Student("zhangsan",19); System.out.println("==========================="); Student student2 = new Student("lisi",20); }}
也就是说先执行父类的静态代码块,然后是子类的静态代码块,然后是父类的实例代码块,父类的构造方法,最后是子类的实例代码块和构造方法。
我们的子类是否可以访问父类的所有成员呢?其实并不是。
我们可以看到这里的c是不能被访问的,因为父类的c被private修饰的,他只能在当前类中使用,那么如果我们只让父类的子类使用的成员该怎么办呢?我们可以使用protected。
你其他的没有继承父类的类就不能使用被protected修饰过的成员,这样大大提高了代码的安全性。
如果我们的类不想被继承,我们可以在类的前面加上final修饰,代表他是最终类,该类不可被继承。final修饰变量表明该变量不可被改变,他的属性也就从变量变成了常量。final修饰方法,表明这个方法不可被重写,重写我们后面再讲。
在Java中继承是一个非常实用的特性,我们应该正确的使用它。继承关系可以有很多层,但是不建议超过三层,并且在Java中不可以多继承,也就是说一个类不能继承多个类,就像一个儿子不能有多个父亲一样,父类可以有多个子类。
来源地址:https://blog.csdn.net/m0_73888323/article/details/130449175