🎉🎉🎉写在前面:
博主主页:🌹🌹🌹戳一戳,欢迎大佬指点!
博主秋秋:QQ:1477649017 欢迎志同道合的朋友一起加油喔💪
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个小菜鸟嘿嘿
-----------------------------谢谢你这么帅气美丽还给我点赞!比个心-----------------------------
继承和多态
🌈一,继承
🌟1.1,为什么要继承
首先,我们已猫和狗两个类进行下对比,我们就可以发现,它们两个类有共同的地方,那既然是公有的,为什么不进行抽离呢?这样就不用写两遍了,这其实就是继承的思想。
class Animal{//动物类
public String name;
public int age;
}
class Dog extends Animal{
public void bark(){
System.out.println(this.name + "正在汪汪汪");
}
}
class Cat extends Animal{
public void miao(){
System.out.println(this.name + "正在miaomiao");
}
}
当我们进行共性的抽取之后,就得到了如上的代码,其中extends表示是继承的关系。
总结而言,继承的作用就是为了实现共性的抽离,进而实现代码的复用。
🌟1.2,继承的语法
访问修饰符 class 子类名 extends 父类名{}
class Animal{
public String name;
public int age;
}
class Dog extends Animal{
public String color;
public void bark(){
System.out.println(this.name + "正在汪汪汪");
}
}
看这段代码,Dog类继承了Animal类,那可能有同学要问了,现在如果说new一个dog对象,它的内存图是怎么样的呢?
🚩注意:
🌖1,子类会将父类中的成员变量或者成员方法继承到子类中了。
🌖2,子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了。
🌟1.3,父类成员的访问
1.3.1,子类访问父类中的成员变量
🌖1,子类与父类不存在同名成员变量
class Father{
public int date1 = 1;
public int date2 = 2;
}
class Son extends Father{
public int date3 = 3;
public int date4 = 4;
public void func(){
System.out.println(this.date1);
System.out.println(this.date2);
System.out.println(this.date3);
System.out.println(this.date4);
}
}
public class TestDemo {
public static void main(String[] args) {
Son son = new Son();
son.func();
}
}
程序运行截图:
内存布局:
当我们的子类继承父类后,子类的对象里面不仅仅存储着自己特有的成员属性,也有从父类那里继承过来的成员属性,所以通过子类的对象引用this都是可以访问到的。
🌖2,子类与父类存在同名的成员变量
class Father{
public int date1 = 111;
public int date2 = 2;
}
class Son extends Father{
public int date1 = 1;
public int date3 = 3;
public int date4 = 4;
public void func(){
System.out.println(this.date1);
System.out.println("父类同名成员" + super.date1);
System.out.println(this.date2);
System.out.println(this.date3);
System.out.println(this.date4);
}
}
public class TestDemo {
public static void main(String[] args) {
Son son = new Son();
son.func();
}
}
程序运行截图:
内存布局:
当我们存在同名的成员变量时,你通过子类对象的引用this去访问的话,它肯定默认是优先访问子类的同名成员变量,如果我们想访问父类的同名成员变量的话,要用 super.变量名 去访问才行,,super代表的是从父类继承过来的属性的引用,有的书上也说super是父类的引用,但是这种说法其实是不准确的。
🚩总结:
当我们在子类方法或者通过子类对象进行访问成员变量的时候,遵循就近原则,优先访问子类自己的,如果没有就去父类中找,父类中也没有那就编译报错。针对同名的成员变量,this只能访问子类自己的,要访问父类的同名成员变量,用super去访问。
1.3.1,子类访问父类中的成员方法
🌖1,子类父类不存在同名方法
class Father{
public void fatherMethod(){
System.out.println("父类方法");
}
}
class Son extends Father{
public void sonMethod(){
System.out.println("子类方法");
}
public void func(){
this.fatherMethod();
this.sonMethod();
}
}
public class TestDemo {
public static void main(String[] args) {
Son son = new Son();
son.func();
}
}
程序运行截图:
既然子类是继承了父类的,所以通过子类当前对象的引用this就可以都调用到。
🌖2,子类和父类存在同名的方法
class Father{
public void MethodA(int val){
System.out.println("父类方法MethodA " + val);
}
public void MethodB(){
System.out.println("父类方法MethodB");
}
}
class Son extends Father{
public void MethodA(){
System.out.println("子类方法MethodA");
}
public void MethodB(){
System.out.println("子类方法MethodB");
}
public void func(){
this.MethodA(10);
this.MethodA();
this.MethodB();
}
}
public class TestDemo {
public static void main(String[] args) {
Son son = new Son();
son.func();
}
程序运行截图:
当子类和父类存在同名的成员方法的时候,依然是就近原则,优先访问子类的,注意,对于同名的方法,是有可能发生重载的,重载不一定是发生在同一个类里面,比如这里的MethodA父类子类就发生了重载,对于这种的话,虽然是同名,我们通过this依然是能够精确访问到的,但是如果同名方法发生的是重写的话(名字,返回值,参数列表都一样),那这个时候就只会优先访问子类自己的方法了,要访问父类的只能用关键字super。
🌟1.4,super关键字
super关键字的作用:
🌖1,super.成员名 访问父类的成员
🌖2,super.方法名 访问父类的方法
🌖3,super() 调用父类的构造方法
🚩注意:和this一样,只能在非静态方法里面使用。
🌟1.5,子类的构造方法
class Animal{
public String name;
public int age;
public Animal(String name,int age){//父类构造方法
this.name = name;
this.age = age;
}
}
class Dog extends Animal{
public String color;
public Dog(String name,int age,String color){//子类构造方法
super(name,age);
this.color = color;
}
public void bark(){
System.out.println(this.name + " 正在汪汪汪");
}
}
public class TestDemo {
public static void main(String[] args) {
Dog dog = new Dog("大黄",4,"黄色");
dog.bark();
}
}
要实例化出子类对象,那首先肯定得有父类对象,先有父才有子,所以在子类的构造方法里面我们得显式的调用父类的构造方法,来对继承过来的属性进行初始化。
🚩注意:
🌖1,如果说父类子类都没有显式的写构造方法,这种情况下,父类子类编译器都会默认提供无参构造方法,并且子类的构造方法默认是给你调用了父类的构造方法的。(在无参构造的情况下,子类父类的构造方法都不写,或者写其中一个都没有问题,因为编译器会我们提供,并且调用父类构造方法)
🌖2,如果说父类的构造方法是有参的,那么这个时候子类的构造方法必须自己写,并且调用父类的这个有参构造。
🌖3,在子类的构造方法中,super()调用父类构造方法,只能放在子类构造方法的第一行。
🌖4,super()在子类构造方法中只出现一次,并且不能与this()同时出现(因为两个都要争着放在第一行,矛盾了)。并且如果父类是无参构造的情况下,super()是隐藏存在的,所以这个时候注意别说没看到super(),然后你去用了this()也是错的。
🌟1.6,super与this
🌟1.7,初始化问题
在前面的类里面,我们就探讨过在一个类里面的初始化顺序的问题,那现在涉及到继承了,它的初始化顺序又是怎样的呢?
class Animal{
public String name;
public int age;
static{
System.out.println("Animal的静态代码块");
}
{
System.out.println("Animal的实例代码块");
}
public Animal(){
System.out.println("Animal的构造方法");
}
}
class Dog extends Animal{
public String color;
static{
System.out.println("Dog的静态代码块");
}
{
System.out.println("Dog的实例代码块");
}
public Dog(){
System.out.println("Dog的构造方法");
}
}
public class TestDemo {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println("===========");
Dog dog1 = new Dog();
}
}
程序运行截图:
对于继承当中的初始化顺序,总结来说,就是由父及子,静态先行,并且静态内容只初始化一次。(我们讨论的顺序针对的是成员,代码块以及构造方法的执行顺序,方法都是要调用才会执行)
🌟1.8,protected关键字
对于protected修饰符,对于在同一个包里面,是没有任何限制的,不管是不是在一个类,但是涉及到继承上,在不同包上,必须是子类才可以访问到。
这里主要说下不同包中的子类,在子类中需要利用super才能访问。
//包demo1下
package demo1;
public class Test {
protected int a;
public static void main(String[] args) {
}
}
//包demo2下
package demo2;
import demo1.Test;
public class Demo extends Test {
public void func(){
System.out.println(super.a);
}
public static void main(String[] args) {
//Test test = new Test();
// System.out.println(test.a);访问方式是错的
Demo dmo = new Demo();
dmo.func();
}
}
🌟1.9,继承的方式
🌟1.10,final关键字
🌖1,修饰变量,表示这是一个常量,不能被修改
final int a = 10;
🌖2,修饰类,表示该类不能被继承
final class B extends A{};//类B不能在被继承
🌖3,修饰方法,表示该方法不能被重写。
🌟1.11,继承与组合
继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物
组合表示对象之间是has-a的关系,比如:汽车与其各个零部件之间,汽车是由它们组成
class Engine{//引擎类
}
class Tire{//轮胎类
}
...
class Car{//车类
public Engine engine;
public Tire tire;
}
和继承一样,组合也是能够实现代码的复用的,因为对于组合而言,只是将某一个类的实例作为了另外一个类的字段。
最后,今天的文章分享比较简单,就到这了,如果大家觉得还不错的话,还请帮忙点点赞咯,十分感谢!🥰🥰🥰