Java继承

1.继承Tips

(1)子类继承父类必须接受父类所有的东西;

子类继承父类时,子类可以直接访问使用父类 非私有的成员 (包括成员属性和成员方法);

父类无法访问子类 特有 成员;

(2)A is a B :那么B为父类,A为子类;

(3)继承的写法

2.方法重写(需区别于方法重载;)

(1)在子类中定义; 方法名,参数类型,参数顺序,参数个数 都要和父类继承的方法完全一致;

(2)子类重写方法的, 返回值,访问修饰符 ,允许不同于父类继承的方法(但有个范围)

如果父类的方法返回值类型是,void,或int,double等基本数据类型,那么子类中重写方法的返回值需要和父类保持完全一致;

如果父类的方法返回值类型是自定义的(如Animal),那么子类中重写方法的返回值允许是父类(Animal),或是他的子类(Dog、Cat);

实例1:

实例2:

3.两个重写的例子

(1)父类变量指向父类对象的情况下,父类变量还是优先调用父类中的方法,而不是子类中重写的方法的;

(2)子类默认继承父类的非私有成员方法,可以不显示地重写,但子类已经默认有了该方法,所以子类中可以显示地对该继承过来的方法进行重载。

(下图的B选项没有更改参数类型,是重写,但返回值类型错误了)

4.非私有成员属性的“重写”

这儿看看就行,只需要知道有这个情况即可

情况1:

情况2:

1.访问修饰符

(1)private:只允许在本类内进行访问;

(2)public:只要能正确加载public成员所在的类型的对象,那么就可以通过这个类型的对象去访问到public成员;即允许在任意位置访问;

(3)protected:允许在当前类进行访问;同包中的子类可以访问(即子类会继承父类中的protected成员);不同包(跨包)的子类也可以访问(和同包的情况相同);同一个包中的其他类(非子类)可以访问;

跨包的非子类不能访问:

即protected说白了就是,同包中的任何类都可以访问;不同包时,只有子类才能访问。

(4)默认的访问权限,什么都不加:允许在当前类访问;跨包子类不允许访问;同包的子类可以访问;同包的非子类也可以访问;

即默认访问权限是,只有同包才能访问;不同包时,无论是否是子类都不可以访问;

注:对于什么业务场景,使用访问修饰符,这个需要在实际业务中提高选定能力。

2.子类重写方法的访问修饰符

子类重写的方法的访问范围要 >= **父类方法的访问范围;


注加:

注:20200922 (1)子类重写父类中的方法,要保持方法返回值类型不能变;(方法的重载,必须是方法的参数个数、顺序或类型不同才可以,,这样以后,重载方法的访问修饰符是什么就无所谓了)


3.super关键字

(1)super关键字基本用法

super.成员属性super.成员方法 :可以在子类中调用父类的成员;(当然得是子类有访问权限的成员)

如:(图片又臭又长,看个意思就行)

(注:当在子类中使用super关键字后,会在堆中新建一个父类对象的;其实,如果在子类中不写super,也会默认调用父类的无参构造方法的)

如果不使用super关键字,则是默认访问本类中的方法了:(就近?或子类已经重写了,当然选择子类的)(双亲委派机制?)

因为双亲委派机制是Java类加载机制的一种默认实现,而不是Java中的子类默认加载父类构造函数。如果您有任何其他问题,我很乐意帮助您解决。😊

下面是AI的回答:

是的,Java中子类会默认加载父类构造函数。在Java中,当创建对象时,会先自动调用父类的构造函数,再自动调用子类的构造函数。子类所有的构造函数默认调用父类的无参构造函数(其实是默认省略掉了一行代码:super();)。如果父类没有定义构造函数,系统会默认定义一个无参无返回值的构造函数,子类继承时无需在子类构造函数中显式调用super();如果父类定义了构造函数,系统不会再默认定义任何无参无返回值的构造函数,此时子类的构造函数中必须显式调用super([arg0] [,arg1]…),即,显式调用对应的参数个数、对应参数类型与此super([arg0] [,arg1]…)的父类构造函数。¹⁵

源: 与必应的对话, 2023/4/10(1) Java 子类构造函数默认调用父类构造函数super - CSDN博客. https://bing.com/search?q=java+%e5%ad%90%e7%b1%bb+%e9%bb%98%e8%ae%a4+%e5%8a%a0%e8%bd%bd+%e7%88%b6%e7%b1%bb+%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0 访问时间 2023/4/10. (2) Java 子类构造函数默认调用父类构造函数super - CSDN博客. https://blog.csdn.net/jb_home/article/details/110149276 访问时间 2023/4/10. (3) Java继承中父类和子类构造函数的问题 - CSDN博客. https://blog.csdn.net/qq_33699659/article/details/84865699 访问时间 2023/4/10. (4) Java 子类调用父类构造函数----super()详解 - CSDN博客. https://blog.csdn.net/abe_abd/article/details/75255577 访问时间 2023/4/10. (5) 关于Java子类继承时,为什么父类要有无参构造方法_月下伊独舞的博客-CSDN博客. https://blog.csdn.net/DAWNLYS/article/details/114990169 访问时间 2023/4/10.

假如子类没有重写父类的eat()方法,其自然会“调用父类的eat()方法”,因为子类已经继承父类的eat()的方法了,虽然在子类中没有显示地写出eat()方法,但eat()已经是子类的成员方法了。(这样一看,上面的所谓就近的说法就是错误的了,子类继承父类的成员方法,子类可以选择重写,也可照搬不重写,无论如何,子类都有了父类成员方法的一个拷贝(说法可能不准确),不使用super关键字,调用的都是子类中的方法;;;只有使用了super关键字后才是调用的父类中的方法)

(2)super关键字不能在main()方法中使用

因为main方法时static修饰的静态方法,而在静态方法中无法预支对象;静态先于对象,this指示当前类对象,super指示父类对象。(在super的使用场景中,super(好像、大概、也许、可能)需要依附于一个对象,)

(这个还不太明白,,,,,)

1.父类有多个构造方法,子类对象加载过程中,具体选择哪一个父类的构造方法

事先说明:由上节可知,在构造子类对象的过程中:第五步中,当父类和子类都加载完毕后,会回到main方法,继续进行子类对象的实例化构造;;;;然后,第六步中,开始执行子类的构造方法,并没有执行子类的构造方法的内部,而是直接转到了父类的构造方法。上节的那个例子即是直接跳到了父类的无参构造方法,这儿的原因是:

在子类的构造方法的第一行有一个默认的super();即默认调用父类的无参构造,也就是在子类的构造方法的第一行什么都不写的话,就是默认有了super(),也就是默认调用父类的无参构造方法

(由此可见,父类的无参构造方法的重要性,不能不写,否则,对于上面的情况(子类的构造方法的首行什么都不写时,默认有有super(),即默认调用父类的无参构造方法的情况下),会报错的,(即本来要调用父类无参构造,但是没有,所以会报错)

如果,父类写了有参构造,而且构建子类对象的时候,想调用父类的有参构造,则应该:super(name,month),即会调用父类的有参构造方法。

注:上述操作的目的还不明确;;;主要是,在构造子类对象的过程中,得到的父类对象跑哪儿去了,怎么使用父类对象,这一切的目的又是什么?。。。。等以后,了解了类加载过程,JVM之后,可能会有更好的理解,目前暂且如此

待定:

注:一个小例子:

2.this和super比较时的tips

(1)super指代父类对象,和this同理,不能在static方法中使用super;

(2)可以使用this去调用同一类的其他构造方法。

(3)在一个构造方法中,不可以同时出现super和this去调用本类和父类的构造方法;(因为this和super都是需要在第一行的,这就没法处理了)

(4)编译错误和运行时错误有点不同,编译出错是提醒代码又漏洞,但是如果运行时没有执行到漏洞代码,则不影响正常的运行结果;

子类对象加载

(额外注:(与下面的历程“无关”):

(1)当在子类中使用super关键字后,会在堆中新建一个父类对象

(2)假若父类A中有public void m(){}这个方法,B类继承A类。

实例化A类对象a,那么在堆中会开辟一个空间存储a对象,m这个方法也会在a对象的空间中;

实例化B类对象b,那么在堆中会开辟一个空间存储b对象,那么m这个方法会复制一份到b对象所在的空间中吗?

PS:2020、8、8回答,上面的解答可能不太准确,目前没必要深究,只需知道:

方法是放在方法区中的,只有new出来的东西是放在堆内存中的,主要是方法区,堆、栈的关系,以后有机会应该可以通过了解Java虚拟机深入理解这儿的知识,这儿如果对开发造不成太大障碍的话,可以先过掉

子类对象加载的执行过程,下面的主要看流程:

第三步:因为静态成员是隶属于类的,所以为了加载类,需要把static成员加载进来。

下图中第九步说对象已经得到了,似乎不妥,目前理解是,对象的Object式的基本模型已经得到,接下来是要着手得到具体的父类对象了

关乎父类对象的东西都准备完毕。

基本过程是,先加载类,再加载成员属性以得到对象。

要想产生子类的实例对象,会先去调用父类的构造流程,完成父类的构造。

1.Object类

equals(Object obj)方法: 判断调用equals方法的引用和传进来的参数对象引用,两个引用是否一致,即这两个引用是否指向的是同一块内存地址,返回值为boolean; 即一个类中,如果没有重写equals方法,那么此时该类(继承过来的)equals方法和“= =”的作用是一样的,所以,equals是在类型中经常重写的一个方法;( 如String类中重写了equals方法,使其的作用是判断两个字符串的值是否相等 );


两个特殊情况:关于String类本身的问题

以下为上述现象的解:(参考https://www.cnblogs.com/taochen-go/p/9475947.html

代码详解

  1. 首先,通过main()方法进栈。
  2. 然后再栈中定义一个对象s1,去堆中开辟一个内存空间,将内存空间的引用赋值给s1,“hello”是常量,然后去字符串常量池 查看是否有hello字符串对象,没有的话分配一个空间存放hello,并且将其空间地址存入堆中new出来的空间中。
  3. 在栈中定义一个对象s2,然后去字符串常量池中查看是否有”hello”字符串对象,有,直接把”hello”的地址赋值给s2.
  4. 即s1中存的是堆中分配的空间,堆中分配的空间中存的是字符串常量池中分配空间存放”hello”的空间的地址值。而s2中之间存的是字符串常量池中分配空间存放”hello”的空间的地址值。
  5. 由于s1与s2中存放的地址不同,所以输出false。因为,类String重写了equals()方法,它比较的是引用类型的 的值是否相等,所以输出true。即结果为false、true。

即上述涉及到:在创建String变量时,其字符串常量创建和指向的过程需要明确,


重写equals()方法:

根据实际业务需求,可以重写目标类的equals方法,根据实际业务需求设计自己的equals方法逻辑即可;

(又一次体现了对继承方法的重载,当然子类可以不重写继承自父类的方法,而只是重载这个方法也行;且下面也体现了引用判null)


另一个常重写的方法是:toString()方法:

常见的重写格式如下:一般也是根据具体类的业务需求去重写:(比如String类就重写了toString方法,其返回的是字符串的值)

一般来说,根据以往经验,常在bean类中重写toString方法,该方法返回该类所有属性的字符串;

注:(1)Java的基本数据类型没有重写上述equals()和toString()的,字符串String是个类,重写了上述的equals()和toString();(起码目前,是这样理解的)

(2)Java中不允许被重写的方法:1.final 修饰的 、2.static 修饰的 、3.private 修饰的:因为私有的在子类中不可见 、4.如果跨包的话,修饰符缺省的也不能被重写,因为缺省的跨包不可见

(20210424补充:final修饰一个变量指向一个数组,只是代表这个变量不能更改引用指向,对象本身(即这个数组)是可以增加元素或者删除元素的。补充自(补充):数组;ArrayList(List);Arrays;Array(待补充);

1.final关键字作用简介

修饰类 ,使类不能被继承;(如String类就是final修饰的)(子类继承父类的目的就包括,想完善、更改一下父类,被final修饰的类已经是不可更改的究极形态了,自然不能被继承,否则就有点违背Java的设计原则了)

修饰(A类中的)方法 :(B类继承A类)使得在B类中不能重写该方法,但B类对象依旧是可以调用该方法的;即子类可以继承父类中被final修饰的方法的;

注:final不能修饰构造方法(理所当然,如果可以修饰会莫名其妙)

修饰变量:修饰的是方法内的局部变量时 ,就表示,定义的该值不能再被修改了,即该变量只能被赋值一次;对于这种变量可以先定义而不赋值,到使用的时候再赋值就行;

修饰类中的成员属性 :首先,如果在定义该成员属性时就初始化了(赋值了),那么在其他任何地方都不能再对该成员属性重新赋值; 其次,如果在定义该成员属性时没有初始化(只定义了变量而没有赋值),那么可以在构造代码块和构造方法中初始化。。。

( _ _ 注:这儿的深入理解,待以后。。。。__ )

加:如果在定义该成员属性时没有初始化(只定义了变量而没有赋值),且是同时使用static和final修饰,那么可以在静态代码块中初始化;(见单例模式,懒汉式中,静态实例对象不能使用final修饰的场景) (20210707:如果在静态代码块中初始化final变量,那么为什么不在定义的final变量的时候就初始化嘞?所以,在静态代码块中初始化final变量,这有点自己给自己找麻烦的感觉。)


可以发现,Java的基本数据类型时可以直接赋值的;但对于引用类型的数据需要通过实例化的方式去构建这个对象,并且这个对象中还有很多属性值;

且final修饰基本数据类型的变量时,不能二次赋值;;那么当final修饰引用数据类型变量时,该引用类型的变量的引用地址是否可以改变,该引用类型变量里面的属性值是否可以改变呐?

事实是:引用地址不可以发生改变,其中的属性值是可以修改的;(这儿容易理解,final修饰哪个变量,哪个变量就已经是究极形态了,不可更改了)


如果,对于某个变量,我们既不希望其在程序中被修改,同时又希望其是作为全局变量而存在,那么可以同时用final和static去修饰限定。(这种变量可能根据业务需求,适合那种全局性性、不可改的变量)

1.注解简介

前面遇到了子类重写父类方法的一些注意事项和方法,那么是否有一种快捷的方式可以帮助用户快速正确地完成上述需求呐?

例如eclipse工具就提供了一种“Alt + /”的快捷键(以后需要留意IDEA相应的快捷键),快速列出在子类中可以重写的所有的方法

下面的@Override就是一个注解

注:工具看到eat方法被Override修饰后,其会自动核查父类中是否有可以被重写的eat方法,如果没有会报错


源码注解:注解只在源码阶段保留,在编译阶段会被丢弃,如上面的@Override就是源码注解。源码注解主要作用是给编译器做代码检测,一旦程序准备进入编译阶段,源码注解的任务也就完成了。

编译时注解:注解会在编译时期保留,但当虚拟机加载class文件(与平台无关的二进制机器码)时会被丢弃;如Spring框架中的@NotNull就是编译时注解

运行时注解:注解在程序运行的时候还能起作用,甚至会影响到运行逻辑的注解。在程序中还可以通过反射来获取到这类注解。如Spring 框架中的@Autowired注解(依赖注入,其可以在程序运行时,自动将外部传入的数据自动地加载进去)

Spring框架等第三方框架的注解是典型的第三方注解;也可根据业务需求自定义注解

注:除此之外,Java中还有【元注解】,这是注解的注解,如@Target( _ _ 这儿还理解很浅,__ )

2.关于@Override注解和static方法在继承中的一个例子

这个例子看看就行,

但如果这样:就没有报错

对于上面现象的补充:

对于下面的圈住的:父类变量指向子类对象的时,也是调用的父类的create方法

附录

Java中子类是否可以继承父类的static变量和方法而呈现多态特性

静态方法

通常,在一个类中定义一个方法为static,那就是说,无需本类的对象即可调用此方法,关于static方法,声明为static的方法有以下几条限制:

  • 它们仅能调用其他的static 方法。
  • 它们只能访问static数据。
  • 它们不能以任何方式引用this 或super。

无论是static修饰的变量,还是static修饰的方法,我们都知道他们是属于类本身的 ,不是属于某一个对象的,当声明一个对象时,并不产生static变量和方法的拷贝。也就是说,用static修饰的变量和方法在类加载的时候,只分配一块存储空间,所有此类的对象都可以操控此块存储空间;

:这里要说明的时,当子类没有与之同名的static变量(或方法时),子类的对象也可以操控这块内存空间。但是子类并没有继承父类中static修饰的变量和方法。因为static修饰的变量和方法是属于父类本身的。

//————————以上我相信我们每个人都比较清楚。

但是,不知道你有没有注意到这种情况,当存在继承关系时,父类中有一个static修饰的变量(或方法),而子类中也存在一个同名的static修饰的变量(或方法)时,他们到底是否满足“重写”,而最终体现出多态的效果呢??

下面来看一个例子。 父类中有一个static修饰的方法和一个普通的方法,子类中也有一个同名的static方法和一个普通的方法。如下

父类

public class Parent {
    public static void  staticMethod(){
        System.out.println("Parent staticMethod run");
 
    }
    public void method(){
        System.out.println("Parent method run");
 
    }
}

子类

public class Son extends Parent {
    public static void  staticMethod(){
        System.out.println("Son staticMethod run");
 
    }
    public void method(){
        System.out.println("Son method run");
 
    }
 
}

测试类

package com.wrh.teststaticmethod;
 
public class Test {
 
    public static void main(String[] args) {
        Parent child=new Son();
        child.staticMethod();//输出:Parent staticMethod run
 
        Son s=new Son();
        s.staticMethod();
        child.method();//这样才存在多态
 
    }
 
}

运行后的结果如下:

Parent staticMethod run Son staticMethod run Son method run

从结果可以看出:对于静态方法在子类中是不存在“重写”这一说的,就像前面我们提到的,用static关键字修饰的方法和变量都是属于类自己本身的,即使存在继承关系,子类并没有继承父类的static修饰的变量和方法,所以说即使子类和父类中都有同样的static方法和变量,他们是没有任何关系的,他们是相互独立的,他们是属于各自类本身的。因此也是不存在多态特性的。而对于普通方法的调用是存在“重写”而最终呈现出多态特性的。

同样的道理:对于static修饰的变量,当子类与父类中存在相同的static变量时,也是根据“静态引用”而不是根据“动态引用”来调用相应的变量的。

而在父类和子类中对于非static变量和方法,是根据“动态引用”来调用相应的变量和方法。

然而,接着会有这样的问题存在: java中 子类会不会继承父类的static变量和static方法

1)先说static方法:子类会不会继承父类的static方法呢??答案是:不会的,但是是可以访问的。

看如下的代码

public class Parent {
    public static void  staticMethod(){
        System.out.println("Parent staticMethod run");
 
    }
 
}
public class Son extends Parent {
    //...
}

测试类

package com.wrh.teststaticmethod;
 
public class Test {
 
    public static void main(String[] args) {
        Parent child=new Son();
        child.staticMethod();//输出:Parent staticMethod run
        Son s=new Son();
        s.staticMethod();//输出:Parent staticMethod run
    }
}

运行结果如下

Parent staticMethod run Parent staticMethod run

从上面的运行结果可以看出,static方法是可以被子类访问的。

2)接着来看父类的static修饰的变量,是否能够被子类继承呢?? 答案:也是不可以的。但是也是可以被子类访问的。

小结

1)子类是不继承父类的static变量和方法的。因为这是属于类本身的。但是子类是可以访问的。 2)子类和父类中同名的static变量和方法都是相互独立的,并不存在任何的重写的关系。

该文章转载自:https://www.cnblogs.com/coder-who/p/8419184.html感谢!