java 面向对象三大特性之继承 万字详解(超详细)

举报
Cyan_RA9 发表于 2023/04/03 18:04:32 2023/04/03
【摘要】 非常详细的java继承博文!

目录

 

前言 : 

一、为什么需要继承: 

二、什么是继承(Warning : 篇幅较长)?

        1.继承概述:

        2.子类继承父类之后达到的效果 :

                ①如何使用继承?

                ②代码演示 : 

        3.继承的使用场景:

                eg1 :

                eg2 :  

        4.继承的优点和缺点 : 

                Δ优点 : 

                Δ缺点 : 

三、 继承关系中成员变量的使用(重点) : 

        Δ前言 : 

        1.Java中查找变量的原则:

        2.Java中查找变量的顺序:

        3.关于super关键字 : 

        4.直接访问父类变量的方式 : 

        5.new关键字创建对象后,对象初始化顺序 : 

        6.代码演示 : 

                声明 :

                ①查找变量原则的演示 : 

                ②查找变量顺序的演示 : 

                ③super关键字使用父类成员变量的演示 : 

四、继承关系中成员方法的使用 : 

        1.Java中查找方法的原则 : 

        2.Java中查找方法的顺序 : 

        3.super关键字访问父类成员方法 : 

        4.子父类中有定义重名方法的情况 : 

        5.代码演示 : 

                声明 : 

                ①Java中查找方法的原则及顺序演示 : 

                ②super关键字访问父类成员方法的演示 : 

                ③子父类重名方法的演示 : 

五、继承关系中构造器的使用(重点):

        1.前言 : 

        2.结论 : 

        3.代码演示 : 

                Δ前言 : 

                ①结论1,2演示 : 

                ②当父类有参无参都有,子类构造器的使用情况 : 

                ③当父类有参有无参无,子类构造器的使用情况 : 

                ④当父类有参无无参有,子类构造器的使用情况 : 

                ⑤当父类有参无参都无,子类构造器的使用情况 : 

                Δ人话(②~⑤总结):

                ⑥关于this(); 语句对本类构造器的调用(了解即可) : 

六、Java中继承的特点 : 

        1.Java仅支持单继承。

        2.父类私有成语子类无法继承。

        3.父类构造器子类不能继承。

        4.继承关系往往体现了" is a " 的关系。

七、继承的本质——java 继承内存图解(重要):

八、方法重写 : 

        1.定义 : 

        2.关键 : 

        3.注意事项 : 

        4.方法重写和方法重载的区别 : 

        5.代码演示 : 

                ①方法重写演示 : 

                ②@Override注解演示 :  

                ③方法重写的注意事项演示 :

九、Java四大访问权限修饰符 : 

        1.介绍 : 

        2.权限 : 

        3.演示 : 

                ①private修饰符演示 : 

                ②默认修饰符 : 

                ③protected修饰符 : 

                ④public修饰符 : 

        4.总结 : 

                ①四大修饰符总结 : 

                ②关于修饰类 : 

        5.延申(关于类和源文件的关系) : 

十、总结 :          


前言 : 

        Hi,guys.这篇博文算是Java面向对象三大特性篇的第二篇———继承博文内容包括但不限于继承的介绍super关键字详解属性、行为,以及构造器在子父类中的使用详解方法重写Java中四种访问权限修饰符等等注意代码中的注释往往包含了讲解补充,别错过。文章讲解详细,因此篇幅较长,大家可以有选择性的点击目录跳转查看。③标注(重要)的地方是重点内容。感谢阅读

一、为什么需要继承: 

        类用来模拟现实事物。对于一切现实生活中存在的事物,比如猫,狗,鼠,人,飞船,树木......我们都可以定义一个对应的类,然后定义它们的属性和行为,再通过实例化该类的方式来模拟一个具体的这类事物,比如我家养了一只🐖叫佩奇,那么我就可以在实例化Pig类时,初始化Pig对象的属性name为"佩奇"。

        但是,现实事物无穷无尽呀!就光说动物,目前世界上存在的动物就有超过上百万种!家喻户晓的一些动物像是🐖,🐕,🐱,🐀,🐍,🐟,🐅,👽,👻,🧟‍♂️等等乱七八糟的也有几十种甚至上百种。如果我们每模拟一种动物,我们都要在类中定义它的各种属性(比如物种名,颜色,地域,食性,栖息地等等)和行为(比如吃喝拉撒,衣食住行等等),那不是很麻烦吗?

        实际上,所有的动物都有物种名,都有颜色,都有栖息地,都要吃喝拉撒!鉴于所有的动物都有这些共同点,我们就诞生出这么个想法来 : 我们直接上升一个维度,不再模拟零零散散的诸如🐖呀,🐎呀,🐏呀各种动物,而是直接去模拟动物本身!没错!为什么不直接定义一个Animal类(动物类)呢,直接在Animal类中定义诸如name,age,color,habitat这些属性,还有诸如eat(),drink(),sleep()这些行为,然后让每个模拟某种特定动物的类去和Animal类产生一种特殊关系,使得每一个和Animal类有特殊关系的类,都可以直接或间接访问Animal类中的这些属性和行为

        如此这般,以后我们再定义类来模拟某种动物时,岂不是省去了反复定义这些公共属性和行为的麻烦?我们只需要定义这种动物的特有的属性和行为,比如变色龙会变色,蜣螂会滚粪球等等,而像物种名呀,寿命呀,颜色呀,吃喝拉撒呀这些公有的属性和行为,就让它用Animal类的不就完了。其实,我们说的这种所谓特殊的关系,已经在Java中实现!我们把这种特殊关系,称为“继承”,Java中通过extends关键字实现。当一个类“继承”了另一个类,我们称前者为子类,后者为父类,对应于我们方才的说法,即子类与父类发生了这种特殊关系,这种特殊关系就是继承。

二、什么是继承(Warning : 篇幅较长)?

        1.继承概述:

                继承就是让类与类之间产生父子关系,即出现了父类和子类。继承实际就是一种关系

                我们来对比一下类的定义 : 类是一系列具有相同属性和行为的事物的统称。而将一系列相关事物共同的属性和行为提取出来的过程,称为抽象。现在,我们把定义中的“事物”换作“类”,即,多个类中相同的属性和行为抽象出来,并定义在同一类中,这便是父类。而所有通过extends关键字来声明继承父类的类,都叫做该父类的子类。简单来说 : 

                被继承的类叫做父类(也叫超类,基类

                继承的类叫做子类(也叫派生类

        2.子类继承父类之后达到的效果 :

                ①如何使用继承?

                Java中如果使用继承,需要用到extends关键字,在定义子类时,子类类名后跟上extends + 要继承的父类类名,即可。举个栗子,假设Cat类继承了Animal类,如下 :  

public class Cat extends Animal {
    //Cat类继承了Animal类
}

image.gif

                那么,子类继承父类后有什么效果呢?                

                Ⅰ子类拥有了父类的所有非私有成员(成员变量、成员方法),并可以直接访问它们。

                对于父类的私有成员(成员变量、成员方法),子类只能通过父类提供的公有方法间接访问父类的私有成员。

                同时,子类也可以有自己特有的属性(成员变量)和行为(成员方法)。

                ②代码演示 : 

                我们以Fruit类来模拟水果。水果的种类也是数不胜数,葡萄🍇就是万千水果中的一种,我们以Grape类来模拟葡萄。水果是不是都有水果名、甜度、大小(尺寸)、营养等等这些共性的属性;而且水果都需要进行光合作用,呼吸作用这些共性的行为。葡萄显然是水果的一种,因此我们想让Grape类继承Fruit类,这样Grape类中不必再重复定义这些共性的属性和行为了,使代码简洁高效。继承情况如下图所示 :

image.png         

演示效果Ⅰ:

                在effect包下,我们先新建一个Fruit类当作父类,再新建一个Grape类当作子类,让子类Grape继承父类Fruit,最后建一个TestEffect类作为演示类。在Fruit类中,我们定义species_name(物种名)、size(尺寸)、sweetness(甜度),nutrition(营养)这些成员变量定义photosynthesis([ˌfoʊtoʊˈsɪnθəsɪs] 光合作用), respiratory_action([ˈrespərətɔːri] 呼吸作用)这些成员方法

                Fruit类代码如下

package knowledge.succeed.effect;
//父类:Fruit
public class Fruit {
    //成员变量
    String species_name;    //物种名
    String size;            //尺寸
    double sweetness;       //甜度
    String nutrition;       //营养
    //成员方法
    public void photosynthesis() {        //光合作用
        System.out.println("光合作用消耗二氧化碳和水,生成氧气和有机物");
    }
    public void respiratory_action() {    //呼吸作用
        System.out.println("呼吸作用消耗葡萄糖和氧气,生成水,二氧化碳和ATP");
    }
}

image.gif

                可以看到,Fruit类中的成员变量和成员方法均为非私有,那么Grape类继承Fruit类后必然拥有了Fruit类所有的成员。Grape类代码如下

package knowledge.succeed.effect;
//子类 : Grape
public class Grape extends Fruit{
    //啥都没写捏🤗
}

image.gif

                TestEffect类代码如下

package knowledge.succeed.effect;
public class TestEffect {
    public static void main(String[] args) {
        Grape grape = new Grape();
    //1.测试成员变量
        grape.sweetness = 11;
        grape.species_name = "葡萄";
        grape.size = "单个葡萄:small;整串葡萄:normal";
        grape.nutrition = "糖类,矿物质,维生素";
        System.out.println("species_name = " + grape.species_name);
        System.out.println("size = " + grape.size);
        System.out.println("sweetness = " + grape.sweetness);
        System.out.println("nutrition = " + grape.nutrition);
        System.out.println("----------------------------------------");
    //2.测试成员方法
        grape.photosynthesis();         //葡萄光合作用
        grape.respiratory_action();     //葡萄呼吸作用
    }
}

image.gif

                运行结果 :

image.png        

  可以看到,由于Fruit类中没有私有成员,Grape类继承Fruit类后,随即拥有了Fruit类中的所有非私有成员,也就是拥有了Fruit类中的所有成员。我们在TestEffect类中通过"对象."的形式可以直接调用Fruit类中的属性和行为,IDEA并未报错,并且输出结果也在预期之内。

                演示效果Ⅱ:

                以上是Fruit类的成员都是非私有的情况,现在,我们为Fruit类中的size和species_name成员变量添加private修饰符,将这两个属性改为私有成员。如下图所示 : 

image.png

                但是,刚改完,IDEA立马给出了报错,错误出现在TestEffect类,如下图所示 image.png   学完封装后,相信大家对这个问题早已司空见惯。在封装中,如果类中有私有成员,那么实例化该类后,不能通过"对象."的形式来直接调用该类的私有成员,仅能通过公共的方法来间接访问这些私有成员(比如setter,getter方法)。现在就是这么个情况,如出一辙😎!只不过这里Grape类对象调用的私有成员是它的父类Fruit类的成员,仅此而已。

                因此,我们也是老办法招呼,在Fruit类中给出这两个属性的setter,getter方法,然后在TestEffect类调用setter,getter方法就⭐了?Fruit类代码如下 :        

package knowledge.succeed.effect;
//父类:Fruit
public class Fruit {
    //成员变量
    private String species_name;    //物种名
    private String size;            //尺寸
    double sweetness;               //甜度
    String nutrition;               //营养
    //两个私有成员species_name和size的setter,getter方法
    public String getSpecies_name() {
        return species_name;
    }
    public void setSpecies_name(String species_name) {
        this.species_name = species_name;
    }
    public String getSize() {
        return size;
    }
    public void setSize(String size) {
        this.size = size;
    }
    //成员方法
    public void photosynthesis() {
        System.out.println("光合作用消耗二氧化碳和水,生成氧气和有机物");
    }
    public void respiratory_action() {
        System.out.println("呼吸作用消耗葡萄糖和氧气,生成水,二氧化碳和ATP");
    }
}

image.gif

                Grape类代码不变,我们略过。TestEffect类代码如下

package knowledge.succeed.effect;
public class TestEffect {
    public static void main(String[] args) {
        Grape grape = new Grape();
    //1.测试成员变量(注意species_name和size这两个属性在调用时的变动)
        grape.setSpecies_name("葡萄");
        grape.setSize("单个葡萄:small;整串葡萄:normal");
        grape.sweetness = 11;
        grape.nutrition = "糖类,矿物质,维生素";
        System.out.println("species_name = " + grape.getSpecies_name());
        System.out.println("size = " + grape.getSize());
        System.out.println("sweetness = " + grape.sweetness);
        System.out.println("nutrition = " + grape.nutrition);
        System.out.println("----------------------------------------");
    //2.测试成员方法
        grape.photosynthesis();         //葡萄光合作用
        grape.respiratory_action();     //葡萄呼吸作用
    }
}

image.gif

                运行结果

image.png         

演示效果Ⅲ:

                我们已经看到了,对于父类的私有成员和非私有成员,子类继承后使用效果的不同。但别忘了,我们上面的第三条还说了:子类继承父类后,除了拥有了父类的非私有成员,子类还可以拥有自己的特有成员

                现在,我们来想一想,葡萄有没有其他水果没有的一些特有的属性,或者行为?up想了很久🤔:

image.png

                葡萄特有的行为的话,up想到的是葡萄可以用来酿葡萄酒,其他的任何一种水果,都没法酿葡萄酒捏🤗。葡萄特有的属性,up是真想不出来,硬要说的话,葡萄一定是葡萄😋!因此,我们可以在Grape类中定义一个成员变量grape,一个成员方法grape(grape也有葡萄酒的意思😂)。

                Fruit类代码不变。Grape类代码如下

package knowledge.succeed.effect;
//子类 : Grape
public class Grape extends Fruit{
    //Grape类的特有属性
    String grape;
    //grape属性的getter,setter方法
    public String getGrape() {
        return grape;
    }
    public void setGrape(String grape) {
        this.grape = grape;
    }
    //Grape类的特有行为
    public void grape() {
        System.out.println("除了葡萄!还有谁?能酿葡萄酒!");
    }
}

image.gif

                TestEffect类代码如下

package knowledge.succeed.effect;
public class TestEffect {
    public static void main(String[] args) {
        Grape grape = new Grape();
    //1.测试Grape类的特有成员变量
        grape.setGrape("葡萄一定是葡萄!😅");
        System.out.println("grape = " + grape.getGrape());
        System.out.println("----------------------------------------");
    //2.测试Grape类特有成员方法
        grape.grape();
    }
}

image.gif

                输出结果

image.png

        3.继承的使用场景:

                当多个类中存在相同的属性和行为时,可以将这些相同的内容提取出来并放到一个新的类中(也就是父类),然后再让这些类和父类产生继承关系,以实现代码复用

                eg1 :

                Dog类,Pig类,Cat类,Cow类都是在模拟动物,因而它们都会有species_name(物种名),average_age(平均寿命),以及sex(性别)等相同的属性,也都会有sleep(睡觉),eat(吃),drink(喝)等相同的行为。这时我们就可以将这些相同的属性或行为提取到Animal类中,然后让这些类分别与Animal类(动物类)发生继承关系

                关系图如下

image.png

                代码如下

                复习我们之前讲过的JavaBean标准代码规范😎,这里的Animal类我们以标准JavaBean格式敲子父类之间属性,行为,构造器的使用我们下面会讲到),Animal类成员变量全部私有化并提供访问它们的公共方法;成员方法全部公共化。这里我们也可以继续贯彻上文提到的——子类继承父类后达到的效果——我们可以分别在Dog类,Cat类,Pig类和Cow类定义它们的特有属性和行为。比如:

                Dog🐶可以看家,因此可以在Dog类中定义一个look_after_the_house()方法;

                Cat🐱可以上树,因此可以在Cat类中定义一个climb_tree()方法;

                Pig🐖可以打鼾,因此可以在Pig类中定义一个snore()方法;

                Cow🐂可以生产牛奶,因此可以在Cow中定义一个milk()方法。   

                一个父类四个子类,一共五个类up都放在同一个.java文件里了(省事儿)。我们在situation(使用情景)包下创建一个Animal类,接着在Animal类的源文件中继续敲完四个子类,就不用新建新的源文件了。注意:此时四个子类均不可以用public修饰!

                Animal类,Dog类,Cat类,Pig类,Cow类代码如下 :  

package knowledge.succeed.situation;
public class Animal {       /**父类 : Animal类*/
    //父类属性(私有)
    private String species_name;    //物种名
    private double average_age;     //平均寿命
    private String sex;             //性别
    //父类构造器
    public Animal() {
    }
    public Animal(String species_name, double average_age, String sex) {
        this.species_name = species_name;
        this.average_age = average_age;
        this.sex = sex;
    }
    //父类私有属性的获取方法
    public String getSpecies_name() {
        return species_name;
    }
    public void setSpecies_name(String species_name) {
        this.species_name = species_name;
    }
    public double getAverage_age() {
        return average_age;
    }
    public void setAverage_age(double average_age) {
        this.average_age = average_age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    //父类行为(公有)
    public void sleep() {
        System.out.println("都给👴睡睡睡睡睡😴😴😴😴😴");
    }
    public void eat() {
        System.out.println("吃喝拉撒,吃排第一位,什么地位不用我说了吧?");
    }
}
//-----------------------------------------------------------
class Dog extends Animal {      /**子类 : Dog类*/
    public void look_after_the_house() {
        System.out.println("👴虽然🐶,但👴能看家!");
    }
}
class Cat extends Animal {      /**子类 : Cat类*/
    public void climb_tree() {
        System.out.println("👴虽然🐱,但👴能爬树!");
    }
}
class Pig extends Animal {      /**子类 : Pig类*/
    public void snore(){
        System.out.println("👴虽然🐖,但👴能打鼾!");
    }
}
class Cow extends Animal {      /**子类 : Cow类*/
    public void milk(){
        System.out.println("👴虽然🐂,但👴能挤奶!");
    }
}

image.gif

                接着,up还是在situation包下,新建一个Test类,测试我们敲好的类的继承效果。

                Test类代码如下

package knowledge.succeed.situation;
//继承的使用情景:测试Animal类的四个子类。
public class Test {
    public static void main(String[] args) {
    //1.测试第一个子类:Dog类
        Dog dog = new Dog();
        dog.setSpecies_name("狗");
        dog.setAverage_age(12.5);
        dog.setSex("公的");
        System.out.println("dog's species_name = " + dog.getSpecies_name());
        System.out.println("dog's average_age = " + dog.getAverage_age());
        System.out.println("dog's sex = " + dog.getSex());
        System.out.println("------------------------------------------");
        dog.sleep();
        dog.eat();
        dog.look_after_the_house();
        System.out.println("==========================================");
        System.out.println();
    //2.测试第二个子类:Cat类
        Cat cat = new Cat();
        cat.setSpecies_name("猫");
        cat.setAverage_age(14);
        cat.setSex("母的");
        System.out.println("cat's species_name = " + cat.getSpecies_name());
        System.out.println("cat's average_age = " + cat.getAverage_age());
        System.out.println("cat's sex = " + cat.getSex());
        System.out.println("------------------------------------------");
        cat.sleep();
        cat.eat();
        cat.climb_tree();
        System.out.println("==========================================");
        System.out.println();
    //3.测试第三个子类:Pig类
        Pig pig = new Pig();
        pig.setSpecies_name("猪");
        pig.setAverage_age(20);
        pig.setSex("雄的");
        System.out.println("pig's species_name = " + pig.getSpecies_name());
        System.out.println("pig's average_age = " + pig.getAverage_age());
        System.out.println("pig's sex = " + pig.getSex());
        System.out.println("------------------------------------------");
        pig.sleep();
        pig.eat();
        pig.snore();
        System.out.println("==========================================");
        System.out.println();
    //4.测试第四个子类:Cow类
        Cow cow = new Cow();
        cow.setSpecies_name("牛");
        cow.setAverage_age(25);
        cow.setSex("雌的");
        System.out.println("cow's species_name = " + cow.getSpecies_name());
        System.out.println("cow's average_age = " + cow.getAverage_age());
        System.out.println("cow's sex = " + cow.getSex());
        System.out.println("------------------------------------------");
        cow.sleep();
        cow.eat();
        cow.milk();
    }
}

image.gif

                输出结果:(太多了截图放不下,因此这里以GIF图展示)                

image.png

 

                eg2 : 

                坤坤,相信大家都不陌生吧。坤坤凭借男团选秀节目《偶像练习生》巨C出道😍!随后开始疯狂圈粉,路人对人坤坤的态度也是纷纷好转🤗。如今在某浪超话榜上,坤坤总是稳稳坐在前三,而且坤坤也是唯一一个超话粉丝打破千万的明星🤩!甚至在2019年,坤坤还担任了热血淋漓👊,肌肉碰撞💪的NBA🏀的新春大使!但是,如此丰功伟绩🏆总会引起一些人的分外眼红🤬。因此,坤坤的粉丝群体中难免有少许的黑粉,我们称为小黑子,也就是little_black;而真爱党,我们称为ikun;还有一些人既不是真爱党也不是小黑子,我们称为路人党,也就是road_man。                              

                因此,要模拟坤坤粉丝团体,我们可以定义一个Kunkun_Fans类,表示坤坤的粉丝群体,作为父类。而小黑子,真爱党,路人党都属于坤坤的粉丝群体,因此可以分别定义Little_black类,Ikun类,Road_man类来继承Kunkun_Fans类。因为三个群体都是粉丝,因此它们会有相同的属性如love_degree(爱的程度),love_year(粉坤坤的时间,按年计),love_slogan()等等;以及相同的行为如beat_call(为坤坤打call)等。

                关系图如下

image.png

                代码如下 :  

                我们在eg2包下创建父类Kunkun_Fans,仍旧以JavaBean类标准来敲父类。和eg1类似,仍旧将父类和子类放在一个.java源文件里,省事儿,即敲完父类后接着敲子类,但是子类不可以再定义成public类型,因为源文件有且仅能有一个public关键字修饰的类,且只要源文件中存在public修饰的类,该类的类名就必须与源文件名相同(这个我们之后统一讲面向对象时会讲到

                Kunkun_Fans类,Little_black类,Ikun类,Road_man类代码如下

package knowledge.succeed.situation.eg2;
public class Kunkun_Fans {      /** 父类 : Kunkun_Fans类*/
    //父类的成员变量(私有)
    private String love_degree;
    private String love_year;
    private String love_slogan;
    //父类的构造器
    public Kunkun_Fans() {
    }
    public Kunkun_Fans(String love_degree, String love_year, String love_slogan) {
        this.love_degree = love_degree;
        this.love_year = love_year;
        this.love_slogan = love_slogan;
    }
    
    //父类私有属性的获取方法
    public String getLove_degree() {
        return love_degree;
    }
    public void setLove_degree(String love_degree) {
        this.love_degree = love_degree;
    }
    public String getLove_year() {
        return love_year;
    }
    public void setLove_year(String love_year) {
        this.love_year = love_year;
    }
    public String getLove_slogan() {
        return love_slogan;
    }
    public void setLove_slogan(String love_slogan) {
        this.love_slogan = love_slogan;
    }
    //父类的成员方法(公开)
    public void beat_call(String call) {
        System.out.println(call);
    }
}
//---------------------------------------------------------------
class Little_black extends Kunkun_Fans {        /** 子类 : Little_black*/
}    
class Ikun extends Kunkun_Fans{                 /** 子类 : Ikun*/
}
class Road_man extends Kunkun_Fans {            /** 子类 : Road_man*/
}

image.gif

                接着,我们仍在eg2包下,创建一个Test2类,用来测试继承的使用情景eg2

                Test2类代码如下 :

package knowledge.succeed.situation.eg2;
//测试继承的使用情景之eg2:
public class Test2 {
    public static void main(String[] args) {
        //1.测试第一个子类 : Little_black
        Little_black lb = new Little_black();
        lb.setLove_degree("最爱kunkun😍,没有之一");
        lb.setLove_year("永远!forever!🤗");
        lb.setLove_slogan("🐔你太美~~🐔你实在是太美~");
        System.out.println("lb's love_degree = " + lb.getLove_degree());
        System.out.println("lb's love_year = " + lb.getLove_year());
        System.out.println("lb's love_slogan = " + lb.getLove_slogan());
        System.out.println("----------------------------------------------------");
        lb.beat_call("即使全世界都与KunKun为敌,我们也会毫不犹豫地站在全世界这边!");
        System.out.println("=====================================================");
        System.out.println();
        //2.测试第二个子类 : Ikun
        Ikun ikun = new Ikun();
        ikun.setLove_degree("挚爱kunkun");
        ikun.setLove_year("maybe,forever too.");
        ikun.setLove_slogan("微博不倒,陪kun到老!");
        System.out.println("ikun's love_degree = " + ikun.getLove_degree());
        System.out.println("ikun's love_year = " + ikun.getLove_year());
        System.out.println("ikun's love_slogan = " + ikun.getLove_slogan());
        System.out.println("----------------------------------------------------");
        ikun.beat_call("你知不知道我们家icon多么努力!");
        System.out.println("=====================================================");
        System.out.println();
        //3.测试第三个子类 : Road_man
        Road_man road_man = new Road_man();
        road_man.setLove_degree("随缘,忽高忽低");
        road_man.setLove_year("随缘,忽长忽短");
        road_man.setLove_slogan("随便");
        System.out.println("road_man's love_degree = " + road_man.getLove_degree());
        System.out.println("road_man's love_year = " + road_man.getLove_year());
        System.out.println("road_man's love_slogan = " + road_man.getLove_slogan());
        System.out.println("----------------------------------------------------");
        road_man.beat_call("你干嘛~");
    }
}

image.gif

                输出结果 : (GIF图)

image.png

        4.继承的优点和缺点 : 

                Δ优点

                        提高了代码的复用性,实现了功能复用。

                        结构清晰,简化认识。(类于类之间的关系简洁明了)

                        便于扩展新功能(子类可以重写父类的方法,还可以定义自己的方法)

                        易维护性

                Δ缺点

                        打破了封装性(父类向子类暴露了实现细节)

                        高耦合性,类与类之间的依赖性高了,不符合“低耦合,高内聚”的程序设计要求.

三、 继承关系中成员变量的使用重点) : 

        Δ前言 : 

                继承关系中成员变量的使用,和继承关系中成员方法的使用,其实是大同小异!因此,up着重演示前者,尽可能详细地讲解。希望大家注意这个模块。

        1.Java中查找变量的原则:

                “就近原则”                

        2.Java中查找变量的顺序:

                局部变量—>成员变量—>父类—>更高的父类—>......—>Object        

                人话 : 强龙不压地头蛇,儿子没有找爹要。

                Δ注意 : 

                        ①Object类是Java中所有类的顶层父类!(之后我们讲到API时会详细阐释)

                        ②如果本类没有该变量,父类中有该变量但是为父类私有时,IDEA会报错!因为私有属性无法在其他类直接访问(已讲过🌶)

                        ③如果从局部位置开始一直找到Object类也没有找到该变量,IDEA会报错!

                        ④第②种情况和第③种情况的报错性质是不同的。前者是因为找到了但没法直接用,后者是因为干脆没找到。这是两回事儿了。

        3.关于super关键字 : 

                类似于this关键字(封装篇我们已讲过捏🤗),super关键字也是一个指针,当然了,在Java中叫做引用。我们知道,this是对象的隐藏属性,是一个指向当前对象的引用。而super,则是指向当前对象父类的引用(即父类内容内存空间的标识)。当new关键字创建子类对象时,子类对象的堆空间中会有一部分用于存放父类的内容,即继承自父类的非私有成员。super就指向这么一部分。可以理解为,super指向的部分是在this指向的部分的范围内。在使用时,this从本类开始找,super从父类开始找。光嘴上说多少还是太抽象,来张内存图直观清晰的展示一下 : 

image.png

        4.直接访问父类变量的方式 : 

                super.父类变量名 (仅能访问非私有属性,所以是直接访问)

        5.new关键字创建对象后,对象初始化顺序 : 

                先初始化父类内容,再初始化子类内容。(原因是创建子类对象时,优先调用父类的构造器,构造器在继承中的使用——文章后面会讲到。)

        6.代码演示 : 

                声明 :

                up以Father类作为演示父类,以Son类作为演示子类,以TestVariable类作为演示测试类以下代码演示均围绕这三个类展开

                ①查找变量原则的演示 : 

                其实我们在封装篇中,讲到this关键字的强龙地头蛇布局定式时,就已经讲过Java中查找变量的原则了。因此,封装篇掌握的小伙伴儿们可以跳过①②演示。                

                我们在Father类中定义一个成员变量temp,并给它显式赋值为11;在Son类中也定义一个变量temp,并给它显式赋值为5;然后在Son类中定义一个printTemp方法用来打印temp变量,并且,在printTemp方法内部,我们再定义一个局部变量,也叫temp。

                接着我们就可以在TestVariable类中创建子类对象并通过子类对象调用printTemp方法。如果按照就近原则,当然是优先输出printTemp方法内部——局部位置的temp😎!那到底是不是这样捏🤗?我们拭目以待:

                Father类代码如下

package knowledge.succeed.aboutfield;
    //父类 : Father (仅作为演示,无实际意义)
public class Father {
    int temp = 11;
}

image.gif

                Son类代码如下

package knowledge.succeed.aboutfield;
    //子类 : Son (仅作为演示,无实际意义)
public class Son extends Father {
    int temp = 5;
    public void printTemp() {
        int temp = 55555;
        System.out.println("按照就近原则,默认输出的temp = " + temp);
    }
}

image.gif

                TestVariable类代码如下  :                

package knowledge.succeed.aboutfield;
    //测试类 : TestVariable
public class TestVariable {
    public static void main(String[] args) {
        Son son = new Son();
        son.printTemp();
    }
}

image.gif

                输出结果

image.png

                很明显,与我们的猜测完全一致😎! 

                ②查找变量顺序的演示 : 

                在演示①的代码基础上,我们注释掉printTemp方法中局部变量的定义,其余代码不变。如下图所示 : 

image.png

                根据Java中查找变量的顺序,如果局部位置没有变量,要接着扩大到本类查找,恰好!我们在本类中也有temp变量😋。那么,不出意外的话,默认输出的temp就会从55555变成5。 

                好的,我们还是运行TestVariable类,输出结果如下 : 

image.png

                🐂,果不其然!Go on,我们趁热打铁,把Son类中的temp变量也给注释掉!其余代码不变。如下图所示 : 

image.png

                那么,根据变量查找原则,局部位置没有!本类成员位置也没有!接下来就该找他爹算账了,你别说!他爹Father类还真定义了temp变量。因此,打印出的temp变量应该由5变成11。                    继续运行TestVariable类,运行结果如下 : 

image.png

                🆗,看来Java中默认的查找变量的顺序还真是这样!诶,这时候就要有p小将(personable小将,指风度翩翩的人)要挑刺儿提问了有本事你把Father类的temp变量也给注释掉!敢不?

                啧,不愧是p小将,6😅。好滴,刚准备引出我们常见的两个问题嘞,我们就从容应战,如下图所示,我们把父类的temp变量也注释掉,其余代码不变。看看会怎么样:

image.png

                根据我们刚刚的注意事项,算求,估计大家也记不住😂。给大家再搬过来,如下图

image.png

                没错,如果我们把Father类的temp变量也注释掉,而Father类默认继承Object类(Object类是所有类的顶层父类),Object类是不会存在这种我们人为定义的成员变量的。因此,再次运行TestVariable类,IDEA该报错了捏🤗。运行结果如下

image.png

                果不其然,IDEA报错 : 显示找不到符号temp。这就是我们之前说过的——如果从局部位置开始,一直找到Object类都没有找到该变量,IDEA会报错

                当然,还有一种错误,也是初学者使用继承时经常遇到的:父类成员变量根据JavaBean标准用了private修饰符修饰,这时候如果找到了父类,即使父类中有该变量IDEA也会报错,因为父类私有啊!你不能跨类直接使用。🆗,接下来,我们给父类中的temp变量增加private修饰符,看看效果如何,如下图所示

image.png

                龟龟!刚改完,还没运行呢,就给报错了😂。如下图所示 : 


image.png

                是的,这就是我们方才说的——如果本类没有该变量,父类中有该变量但是为父类私有时,IDEA会报错!因为私有属性无法在其他类直接访问

                ③super关键字使用父类成员变量的演示 : 

                在三大特性之封装篇里,我们讲过,this关键字可以解决“强龙🐉不压地头蛇🐍”的布局定式,其实就是解决了局部变量和本类成员变量的命名冲突问题。this关键字使得我们可以在局部变量存在的情况下,避开Java就近原则的约束,在局部位置使用成员变量。

                而super关键字,在作用上与this关键字有着异曲同工之妙。无非super就是从父类开始找么!在Java变量查找顺序中直接跳到了父类这一环节,避开Java查找顺序的约束,在局部位置使用父类成员变量。因此,我们可以认为:父类成员变量和子类局部变量的命名冲突问题,可以理解为强龙🐉不压地头蛇🐍的一个变式。而super关键字可以轻松解决这个问题。

                🆗,正片开始!

                我们在Father类中定义3个成员变量,分别为temp1,temp2,temp3其中,temp1~3均不添加修饰符(即非私有)。然后分别在子类Son类中的成员位置和局部位置(printTemp方法内)也定义temp1,temp2,temp3的同名变量。仍然使用printTemp方法来打印变量,但这次,我们想利用该方法同时打印出printTemp方法内局部变量、Son本类成员变量,以及Son类的父类Father类的成员变量。              

                Father类代码如下

package knowledge.succeed.aboutfield;
//父类 : Father (仅作为演示,无实际意义)
public class Father {
    int temp1 = 11;
    int temp2 = 12;
    int temp3 = 13;
}

image.gif

                Son类代码如下

package knowledge.succeed.aboutfield;
//子类 : Son (仅作为演示,无实际意义)
public class Son extends Father {
    public void printTemp() {
        int temp1 = 55555;
        int temp2 = 5555;
        int temp3 = 555;
        System.out.println("按照就近原则,直接输出的temp1 = " + temp1);
        System.out.println("按照就近原则,直接输出的temp2 = " + temp2);
        System.out.println("按照就近原则,直接输出的temp3 = " + temp3);
        System.out.println("----------------------------------------------------------");
        System.out.println("使用this关键字解决强龙地头蛇布局定式,temp1 = " + this.temp1);
        System.out.println("使用this关键字解决强龙地头蛇布局定式,temp2 = " + this.temp2);
        System.out.println("使用this关键字解决强龙地头蛇布局定式,temp3 = " + this.temp3);
        System.out.println("----------------------------------------------------------");
        System.out.println("强龙地头蛇变式之输出父类成员变量temp1 = " + super.temp1);
        System.out.println("强龙地头蛇变式之输出父类成员变量temp2 = " + super.temp2);
        System.out.println("强龙地头蛇变式之输出父类成员变量temp3 = " + super.temp3);
    }
}

image.gif

                TestVariable类代码如下

package knowledge.succeed.aboutfield;
//测试类 : TestVariable
public class TestVariable {
    public static void main(String[] args) {
        Son son = new Son();
        son.printTemp();
    }
}

image.gif

                输出结果

image.png

四、继承关系中成员方法的使用 : 

        1.Java中查找方法的原则 : 

                “就近原则”(和查找变量的原则一样

        2.Java中查找方法的顺序 : 

                本类方法—>父类方法—>更高一级的父类—>......Object(顶层父类) 

                整体和查找变量的顺序略有出入。因为Java中方法不能嵌套,因此不存在所谓“局部方法”,直接就从本类中开始找了。

        3.super关键字访问父类成员方法 : 

                通过"super.方法名" 的形式调用(注意: 仅能访问父类的非私有方法)

        4.子父类中有定义重名方法的情况 : 

                当父类方法无法满足实际需求,需要拓展父类方法的功能时;或者当父类方法的功能需要被重新实现时,我们可以在子类中定义一个与父类方法同名的方法

                注意 :

                这里的“重名方法”,有两种情况

                一种就是子类方法和父类方法的返回值类型、方法名,参数列表都相同,达到了方法重写的效果。(其实就是我们后面要讲到的方法重写,仅方法体内的语句不同)

                另一种就是子类方法和父类方法的方法名相同而参数列表不同,这么做IDEA不会报错,实际使用时可以达到方法重载的效果。(然而这并不符合方法重载的定义,仅仅是因为子类拥有了父类的非私有成员,而这么类比一下)。

        5.代码演示 : 

                声明 : 

                upAnimal类作为演示父类,以Chicken类作为演示子类,以TestMethod类作为演示测试类以下代码演示均围绕这三个类展开

                ①Java中查找方法的原则及顺序演示 : 

                        我们在父类Animal类和子类Chicken类中都定义一个公有的eat方法,然后在TestMethod类创建子类对象,并通过“对象.”的形式调用eat方法。根据就近原则,默认调用的肯定是Chicken类的eat方法

                        Animal类代码如下

package knowledge.succeed.aboutmethod;
//父类 : Animal类(JavaBean标准)
public class Animal {
    //成员变量
    private String name;
    private int age;
    private String sex;
    //构造器
    public Animal() {
    }
    public Animal(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    //成员变量的getter和setter方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    //成员方法
    public void eat() {
        System.out.println("来看看热量是多少?妹说就是0卡!");
    }
}

image.gif

                        Chicken类代码如下

package knowledge.succeed.aboutmethod;
//子类 : Chicken类
public class Chicken extends Animal {
    public void eat() {
        System.out.println("怎么这鸡胸到我嘴里变成泡面了?😭");
        System.out.println("我被诅咒啦!😭");
    }
}

image.gif

                        TestMethod类代码如下

package knowledge.succeed.aboutmethod;
//测试类 : TestMethod类
public class TestMethod {
    public static void main(String[] args) {
        Chicken chicken = new Chicken();
        chicken.eat();
    }
}

image.gif

                        运行结果

image.png          

根据运行结果来看,确实优先调用了子类的eat方法,符合我们预期。

                接下来,我们将子类中的eat方法注释掉,其他代码不变如下图所示

image.png

                然后我们再次运行TestMethod类,根据Java中查找方法的顺序,本类找不到,就要去它的父类找。 因此,这次调用eat方法的输出结果,肯定是父类eat方法中的内容。

                输出结果如下

image.png 

                ②super关键字访问父类成员方法的演示 : 

                        演示Ⅰ:

                        根据①中的代码,父类的eat方法中,仅告诉我们妹说就是0卡!但它没说让我们吃啥呀。因此,我们想对这个方法做一些补充。up在子类中定义一个zeroCalorie(0卡)方法,先在zeroCalorie方法中通过"super.eat();" 来调用父类的eat方法,然后在zeroCalorie方法中补充一条输出语句,告诉我们吃的是鸡肉(左旋溜达🐔)。

                        Animal类代码不变。

                        Chicken类代码如下 : 

package knowledge.succeed.aboutmethod;
//子类 : Chicken类
public class Chicken extends Animal {
    public void zeroCalorie(String thin) {
        super.eat();
        System.out.println("快吃吧!左旋溜达🐔!\n" + thin);
    }
}

image.gif

                        TestMethod代码如下

package knowledge.succeed.aboutmethod;
//测试类 : TestMethod类
public class TestMethod {
    public static void main(String[] args) {
        Chicken chicken = new Chicken();
        chicken.zeroCalorie("少吃点,要不明天得瘦死┭┮﹏┭┮");
    }
}

image.gif

                运行结果

image.png 

诶,确实,通过super关键字,我们成功在子类方法中调用了父类方法。 

                        演示Ⅱ : 

                        设若父类的eat方法为私有方法。我们怎么办?很简单。通过this关键字和super关键字的配合,就可以轻松解决这个问题。首先在父类中定义一个公开的eatEX方法,然后在eatEX方法中通过this关键字调用本类的eat方法。而在子类zeroCalorie方法中," super.eat(); " 要变成   " super.eatEX(); "。

                        Animal类代码如下

package knowledge.succeed.aboutmethod;
//父类 : Animal类(JavaBean标准)
public class Animal {
    //成员变量
    private String name;
    private int age;
    private String sex;
    //构造器
    public Animal() {
    }
    public Animal(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    //成员变量的getter和setter方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    //成员方法
    private void eat() {
        System.out.println("来看看热量是多少?妹说就是0卡!");
    }
    public void eatEX() {
        this.eat();
    }
}

image.gif

                Chiken类代码如下

package knowledge.succeed.aboutmethod;
//子类 : Chicken类
public class Chicken extends Animal {
    public void zeroCalorie(String thin) {
        super.eatEX();
        System.out.println("快吃吧!左旋溜达🐔!\n" + thin);
    }
}

image.gif

                TestMethod类代码不变。

                TestMethod类运行结果如下 :  

image.png

                ③子父类重名方法的演示 : 

                        前面我们也说了,子父类重名方法有两种情况。在演示Java中方法的查找原则及顺序时,我们在子父类中定义的eat方法的方法名,参数列表,返回值类型都相同,这其实就是第一种情况,也就是方法重写的情况。因此这里不再赘述。(方法重写在文章后面还会详细讲到)

                        重点说一下第二种情况 : 就是子类和父类方法的方法名相同但参数列表不同。参数列表不同显然不符合方法重写了,但是由于子类也拥有父类的非私有方法,因此我们不由得联想到了方法重载的效果。我们仍以eat方法为栗,让子类中的eat方法增加String类型的形参,以便告诉我们吃得0卡的是什么东西。

                        Animal类代码不变。

                        Chicken类代码如下

package knowledge.succeed.aboutmethod;
//子类 : Chicken类
public class Chicken extends Animal {
    public void eat(String food) {
        System.out.println("怎么这鸡胸到我嘴里变成泡面了?😭");
        System.out.println("我被诅咒啦!😭");
        System.out.println("吃得什么东西?" + food);
    }
}

image.gif

                        TestMethod类代码如下

package knowledge.succeed.aboutmethod;
//测试类 : TestMethod类
public class TestMethod {
    public static void main(String[] args) {
        Chicken chicken = new Chicken();
        chicken.eat("左旋儿溜达🐔");
    }
}

image.gif

                运行结果 :

image.png

五、继承关系中构造器的使用重点):

        1.前言 : 

                按理说,我们应该先讲构造器在继承中的使用,再讲成员变量,成员方法在继承关系中的使用。但是吧,构造器这玩意儿,太tm抽象。所以,up才决定放在最后去讲。废话少说,正片如下 : 

                大家是否还记得(多半是不记得了🤗)我们在前面“继承关系中成员变量的使用”中说过:继承关系中,对象的初始化顺序为 : 先初始化父类内容,后初始化子类内容。那么,大家想想:谁来初始化对象的内容?没错,构造器呀!这也是继承设计中的基本思想 : 父类的构造器初始化父类内容,子类的构造器初始化子类内容

                但这时候就要有p小将(personable小将,指风度翩翩的人)出来问了 : 吹牛逼谁也会!看你前面写的代码。子类中连个构造器都懒得写,创建子类对象都是无参构造,哦,就算是子类有系统默认给的无参构造,可以初始化子类内容,那你父类内容怎么初始化捏?🤗

                不愧是p小将,6。但是,风度翩翩的人,你也先别急。

                下面,up直接将继承关系中构造器使用的几个结论告诉大家。然后再一一进行代码演示,保证你看完就🆗,正片如下

        2.结论 : 

                结论① : 

                创建子类对象时,优先调用父类的构造器。这句话对应了我们的“先初始化父类内容,后初始化子类内容”。但是,这究竟是如何做到的?请看结论②

                结论② :          

                子类构造器的第一行,默认隐含语句super(); 用于调用父类的默认无参构造

                结论③ : 

                根据我们在封装篇对构造器的理解,父类默认含有无参构造。但是,如果你在父类中只定义了有参构造,父类没有无参构造,这时在子类构造器中,可以用super(参数) 的形式来访问父类的某个指定的带参构造。你可能会问:为什么要这么做?   

                因为方才我们也说了,继承关系中,对象的初始化顺序为 : 先初始化父类内容,后初始化子类内容,是!没错!子类构造器第一行默认的" super(); " 可以调用父类的无参构造,从而达到初始化父类内容的效果。但是现在,父类中没有无参构造!如果你不调用父类有参构造来初始化父类内容,你就没做到继承关系中对象的初始化顺序,就要报错!如下图所示 :

image.png

所以,这么做是迫不得已,你不这么干,是不会编译通过的!。对了,up在这里还要再次强调一点:此处父类是用JavaBean标准敲的,子类只能通过继承到的公有的setter,getter方法来修改或获取父类的私有属性的值。因此,从本质上来说,除子类特有属性外,子类访问的属性根本上都是父类的属性,只不过借你一用罢了。而且,大家想想带参构造的存在的意义是什么?当然是为了在对属性进行构造器初始化时,就将属性的值修改为我们想要的值,从而省去了无参构造初始化之后,再次调用setter方法修改属性值的步骤。之前我们在非继承关系中,使用带参构造创建子类对象时,本质和setter方法一样,即通过this语句将形参赋值给本类的属性。然而在继承关系中,因为子类要访问父类的属性,因此比平时多了一个步骤,多在了子类构造器中的"super(参数);"语句上,super语句将形参赋值给了父类的构造器。因此,若是通过含有super(参数);语句的子类带参构造来创建对象,参数的传递途径是 : 调用子类构造时传入的形参——> super(参数)对应父类构造器中的形参——> 赋值给父类构造器中的this.xxx(即赋值给父类的属性)。

为什么up要在这里好端端的要强调这一点?

                大家想想,super()也好,super(参数)也好,调用父类构造器的目的是什么?是为了优先初始化父类内容,是为了满足继承中对象的初始化条件,满足继承设计的基本思想。对于子类无参构造来说,本来呢,子类无参构造器第一行隐含语句super(),无参对应无参,明明白白,整整齐齐,服服帖帖,按理说初始化之后,父类的属性的值,仍然是属性对应数据类型的默认值但是,在上述情况——父类仅有带参构造的情况下,我们在子类的无参构造中也必须调用super(参数)了!大家有没有想过后果是什么?原先,无参对应无参,调用super() 初始化之后,再通过setter方法修改父类属性的值,现在,tmd一个super(参数)的调用就解决了,我一个无参构造啊,居然达到了有参构造的效果😎!

                同理,如果父类仅有空参构造,没有带参构造,那子类的带参构造就无实际意义了,只能当空参构造用。当然这里强调的内容既是铺垫,也是总结,现在初看可能感觉略显晦涩,没关系!马上下面就是代码演示了,大家可以在代码演示后,再次翻回来看看,绝对柳暗花明!   

                结论④ : 

                看过封装篇的小伙伴儿应该记得,我们再讲到this关键字时,提到了一嘴this()调用本类构造器,我们下面就会进行代码演示。注意 : this() 和 super() 不可以同时出现在同一构造器中。因为——this() 和 super() 语句都必须位于构造器的首句! 

        3.代码演示 : 

                Δ前言 : 

                        up将代码演示部分分为六(1+4+1)部分!首先,会给大家演示一下所谓创建子类对象时优先调用父类构造器,以及子类构造方法第一行默认隐含语句super(),这是第一部分。然后,我们知道,继承关系中子父类构造器的定义情况,无非四种——(父类有参无参都有;父类有参有无参无;父类有参无无参有;父类有参无参都无)第二 ~ 四部分up会对这四种情况进行一一演示,并进行规律的总结。最后,就是this() 和 super()的使用问题,相比起来这就不算一个重点了,这是第三部分

                PS : upPerson类作为演示父类,以Worker类作为演示子类,以TestConstructor类作为演示测试类以下代码演示均围绕这三个类展开

                ①结论1,2演示 : 

                我们仍以JavaBean标准来敲父类,在父类的无参构造和有参构造中,分别增加一条输出语句,使得构造器被调用时可以给出提示。对于子类Worker类,我们先什么都不写。没错,让它空空如也😎。然后我们在TestConstructor类中,通过系统默认给出的无参构造来创建Worker类对象,看看运行结果如何。                   

                Person类代码如下

package knowledge.succeed.aboutconstructor;
/**
 * 父类 : Person类
 */
public class Person {
    //成员变量
    private String name;
    private int age;
    //无参构造
    public Person() {
        System.out.println("这句话输出,说明Person父类的空参构造被调用");
    }
    //有参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("这句话输出,说明Person父类的带参构造被调用,且" + "name = " + name);
    }
    //getter,setter方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

image.gif

                Worker类代码如下

package knowledge.succeed.aboutconstructor;
/*  ΔPS :
    因为父类中的name属性和age属性都是私有属性,
    所以子类继承后,并没有这两个属性,但拥有有父类的可以访问这两个属性的公有方法,
    因此,子类对象通过setter,getter方法交互的属性,本质都是父类的属性。
 */
/** 子类 : Worker类 */
public class Worker extends Person{
}

image.gif

                TestConstrutor类代码如下 :

package knowledge.succeed.aboutconstructor;
//测试类 : TestConstructor类
public class TestConstructor {
    public static void main(String[] args) {
        Worker worker1 = new Worker();
    }
}

image.gif

                运行结果

image.png

可以看到,明明我们用的是子类的默认无参,创建的也是子类对象,却显式父类的无参构造被成功调用了。因此我们可以证明结论①——创建子类对象时,优先调用父类的构造器。以及结论②——子类构造器的第一行,默认隐含语句super();

                当然,使用默认的无参构造显然对比不明显!

                接下来,我们在子类Worker类中,自己定义一个Worker类的无参构造,并且要求同父类一样,再被调用时给出提示。Person类和TestConstructor类代码不变。Worker类代码如下

package knowledge.succeed.aboutconstructor;
/** 子类 : Worker类 */
public class Worker extends Person{
    public Worker() {
        //super();      //子类构造器首句默认隐含super();语句,因此可写可不写。
        System.out.println("这句话输出,说明Worker子类的无参构造被调用");
    }
}

image.gif

                运行效果如下 :  

image.png   

 看这次经过对比以后,是不是明显多了——父类无参构造中的输出语句优先输出,说明创建子类对象时,优先调用父类构造器。而我们在子类构造器中并没有主动去写有效的super();语句,这说明子类构造器的第一行隐含语句super();

                这里还要提前和大家强调一下,只要是在子类构造器中使用了"super();"语句,该语句必须位于构造器首句,否则报错!如下图所示 : 

image.png

                ②当父类有参无参都有,子类构造器的使用情况 : 

                父类有参无参都有,往往是最理想的情况。因为这个时候可以无参对无参,有参对有参。即,在子类无参构造中通过“super();” 调用父类的无参构造;在子类有参构造中通过“super(参数);” 调用父类的有参构造

                父类Person类代码仍旧不变,子类Worker类代码如下

package knowledge.succeed.aboutconstructor;
/** 子类 : Worker类 */
public class Worker extends Person{
    public Worker() {
        super();            //无参对无参
        System.out.println("这句话输出,说明Worker子类的无参构造被调用");
    }
    public Worker(String name, int age) {
        super(name, age);   //有参对有参
        System.out.println("这句话输出,说明Worker子类的有参构造被调用");
    }
}

image.gif

                TestConstructor类代码如下

package knowledge.succeed.aboutconstructor;
//    测试类,用来演示构造方法的调用的
public class TestConstructor {
    public static void main(String[] args) {
        Worker worker1 = new Worker();
        worker1.setName("Sun");
        System.out.println();
        Worker worker2 = new Worker("Moon", 11);
        System.out.println();
        System.out.println("worker1'name = " + worker1.getName());
        System.out.println("worker2'name = " + worker2.getName());
    }
}

image.gif

                运行结果

image.png      

当然,以上是最常见,最舒服的形式。但是大家知道,现实往往是荒诞的😅!在父类有参无参都有的情况下,你也可以选择在子类空参中通过 super(参数) 来调用父类带参,以及在子类带参中通过super调用父类空参。这么做会造成一个效果 : 调用子类空参最终改变了父类的属性值,空参达到了带参的效果(是不是我们上面强调过的);而子类带参最终无法改变父类的属性值,带参没有了实际意义,带参达到了空参的效果。斯国一!

                但是有一点需要注意,子类无参构造调用super(参数)时,因为子类构造的参数列表为空,因此,此时的super(参数)中传入的参数必须为常量,而不能传入变量

                Person类代码仍旧不变!Worker类代码如下 :        

package knowledge.succeed.aboutconstructor;
/** 子类 : Worker类 */
public class Worker extends Person{
    public Worker() {
        super("我是谁?", 30);    //无参对有参了
        System.out.println("这句话输出,说明Worker子类的无参构造被调用");
    }
    public Worker(String name, int age) {
        super();              //有参对无参了
        System.out.println("这句话输出,说明Worker子类的有参构造被调用");
    }
}

image.gif

                TestConstructor类代码如下

package knowledge.succeed.aboutconstructor;
//    测试类,用来演示构造方法的调用的
public class TestConstructor {
    public static void main(String[] args) {
        Worker worker1 = new Worker();
        System.out.println();
        System.out.println("调用setter方法之前,worker1'name = " + worker1.getName());
        worker1.setName("Sun");
        System.out.println();
        Worker worker2 = new Worker("Moon", 11);
        System.out.println();
        System.out.println("worker1'name = " + worker1.getName());
        System.out.println("worker2'name = " + worker2.getName());
    }
}

image.gif

                运行结果 : 

image.png

结果符合预期——子类无参构造中super传入的"我是谁?"成功赋值给了父类私有属性name;而子类有参构造中,构造器参数列表的形参无法传递给父类构造器,成了摆设。         

                ③当父类有参有无参无,子类构造器的使用情况 : 

                这**不就是结论③一开篇便提出的情况嘛!(大家看我的博客会发现——在分享某个东西时,我喜欢先让大家了解个大概,即剧透一下,后面再详细解释。我觉得这么挺好,有助于大家加深理解!

                我们来分析一下这种情况: 首先,父类定义了有参构造,系统不再提供默认的无参构造,因此父类仅有参构造可调用。什么意思呢?意思就是你子类构造器第一行不能再使用super()来调用父类无参构造了,原因也很简单——人家父类现在就没得无参,你上哪儿调用啊?

                那我们怎么办?没无参你调用有参不就完了么!多好理解的事儿!有啥用啥,物尽其用!

好滴,我们先把父类的空参构造给注释掉,其他代码不变如下图所示

image.png

                如果注释掉父类的无参构造后,有头铁娃依然坚持要在子类构造中使用super (),就会报错,如下图所示

image.png

                因此,这时候我们只能使用有参,不然你编译就不通过呀。

                Worker类代码如下

package knowledge.succeed.aboutconstructor;
/** 子类 : Worker类 */
public class Worker extends Person{
    public Worker() {
        super("工人一号", 100);  //无参构造形参列表为空,super() 中只能直接传入常量
        System.out.println("这句话输出,说明Worker子类的无参构造被调用");
    }
    public Worker(String name, int age) {
        super(name, age);       //有参构造形参列表中就有传入的name和age,可以传入变量。
        System.out.println("这句话输出,说明Worker子类的有参构造被调用");
    }
}

image.gif

                TestConstuctor类代码如下

package knowledge.succeed.aboutconstructor;
//    测试类,用来演示构造方法的调用的
public class TestConstructor {
    public static void main(String[] args) {
        Worker worker1 = new Worker();
        System.out.println();
        Worker worker2 = new Worker("Moon", 11);
        System.out.println();
        System.out.println("worker1'name = " + worker1.getName());
        System.out.println("worker2'name = " + worker2.getName());
    }
}

image.gif

                运行结果 : 

imageimage.gif编辑

                ④当父类有参无无参有,子类构造器的使用情况 : 

                其实吧,这个我们在演示②中已经演示过🌶。无妨,大家现在已经差不多知道个回事儿了。父类仅含无参,那我们子类构造中就只能调用super(),此时子类带参构造无实际意义,因为你无法通过super(参数)来传递形参。

                这次,我们将Person类中的无参构造恢复,把带参构造注释掉,其他代码不变如下图所示

image.png        

Worker类代码如下

package knowledge.succeed.aboutconstructor;
/** 子类 : Worker类 */
public class Worker extends Person{
    public Worker() {
        super();    //无参对无参
        System.out.println("这句话输出,说明Worker子类的无参构造被调用");
    }
    public Worker(String name, int age) {
        super();    //有参此时无实际意义
        System.out.println("这句话输出,说明Worker子类的有参构造被调用");
    }
}

image.gif

                TestConstructor类代码如下 :                                                                 

package knowledge.succeed.aboutconstructor;
//    测试类,用来演示构造方法的调用的
public class TestConstructor {
    public static void main(String[] args) {
        Worker worker1 = new Worker();
        worker1.setName("Sun");
        System.out.println();
        Worker worker2 = new Worker("Moon", 11);
        System.out.println();
        System.out.println("worker1'name = " + worker1.getName());
        System.out.println("worker2'name = " + worker2.getName());
    }
}

image.gif

                运行结果  :

image.png 

通过运行结果可以看出,子类有参构造创建的对象,输出name属性的值却为默认值null,所以我们才说,有参构造此时无实际意义。 

                ⑤当父类有参无参都无,子类构造器的使用情况 : 

                父类有参无参都没有,实际和上一种情况——父类有参无无参有一致。只不过这里的无参不再是自定义的无参构造,而是系统默认的了,因此创建子类对象时不再有关于父类构造器的提示语句了。

                将Person类中的两个构造器都给注释掉如下图所示

image.png

                Worker类代码不变,TestConstructor类代码也不变🤡。
                运行结果如下 :  

image.png

                Δ人话(②~⑤总结):

                        根据父类构造器的四种定义情况的演示,我们可以总结出以下规律 :

                1° 父类有参√  无参√,子类无参随便,有参必须super(对应参数),否则无实际意义
                2° 父类有参√  无参×,子类无参有参都只能super(参数)
                3° 父类有参×  无参√,子类只能无参,有参无实际意义
                4° 父类有参×  无参×,(默认含无参,同上一种情况)

                ⑥关于this(); 语句对本类构造器的调用(了解即可) : 

                就像super();或super(参数);语句可以调用父类构造器,this();或this(参数);语句用于调用本类构造器。使用时,需要注意两点

                同super语句一样,this语句也必须位于构造器的第一行

                this()和seper()不能同时存在于同一构造器中。(原因就是它们都必须在构造器第一行)

                我们先恢复父类Person类的两个构造,如下如图所示 : 

image.png

                 接着,我们在子类Worker类中定义一个子类特有属性hobby,表示工人的爱好。注意!这时候如果我们想利用带参构造初始化子类对象,就需要传入三个参数,而且其中两个参数最终赋值给了父类的成员变量,还有一个参数则最终赋值给了子类成员变量hobby。其实这时候我们只要在子类定义一个三个形参的有参构造,然后先通过super(参数);来调用父类带参构造,完成对父类属性的初始化,然后再补充上一句this.hobby = hobby; 完成对子类特有属性的初始化,也就算大功告成了。

                但是这毕竟是this调用本类构造的演示😂,还是要用一下this()语句的。方法也很简单 子类要定义两个带参构造,其中一个构造器有两个形参,调用super(参数)完成对父类对象的初始化,另一个构造器有三个形参,先通过this(参数) 调用本类带参构造(即两个形参的那个带参构造),完成对父类属性的初始化。再通过this语句完成对本类特有属性hobby的初始化

                Person类代码如下

package knowledge.succeed.aboutconstructor;
/**
 * 父类 : Person类
 */
public class Person {
    //成员变量
    private String name;
    private int age;
    //无参构造
    public Person() {
        System.out.println("这句话输出,说明Person父类的空参构造被调用");
    }
//    有参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("这句话输出,说明Person父类的带参构造被调用,且" + "name = " + name);
    }
    //getter,setter方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

image.gif

                Worker类代码如下

package knowledge.succeed.aboutconstructor;
/** 子类 : Worker类 */
public class Worker extends Person{
    private String hobby;
    public Worker() {
        //super();      //默认隐含super();
        System.out.println("这句话输出,说明Worker子类的无参构造被调用");
    }
    public Worker(String name, int age) {
        super(name, age);
        System.out.println("这句话输出,说明Worker子类的有参构造被调用");
    }
    public Worker(String name, int age, String hobby) {
        this(name, age);
        //super(name, age);
        this.hobby = hobby;
    }
    public String getHobby() {
        return hobby;
    }
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
}
/*
    关于this() :
    与super()调用父类构造器不同,this()可以调用本类的构造器,
    但是this() 和 super() 在同一构造器中只能同时出现一个,且必须置于构造器首句。
 */

image.gif

                TestConstructor类代码如下 :  

package knowledge.succeed.aboutconstructor;
//    测试类,用来演示构造方法的调用的
public class TestConstructor {
    public static void main(String[] args) {
        Worker worker1 = new Worker();
        worker1.setName("Sun");
        System.out.println();
        Worker worker2 = new Worker("Moon", 11);
        System.out.println();
        Worker worker3 = new Worker("Star", 88, "木大木大木大木大木大");
        System.out.println("----------------------------");
        System.out.println("worker1'name = " + worker1.getName());
        System.out.println("worker2'name = " + worker2.getName());
        System.out.println("worker3'name = " + worker3.getName());
        System.out.println("Worker3'hobby = " + worker3.getHobby());
        //PS : 创建子类对象时,实际内部相当于这么写(C语言):
        //Worker * pWorker = (Worker * ) malloc(sizeof(Worker));
    }
}

image.gif

                运行结果如下 : 

image.png

六、Java中继承的特点 : 

        1.Java仅支持单继承。

                单继承指每个类最多有且只能继承一个类,不能同时继承多个类。同时继承多个类会报错,举个栗子吧:假设up同时拥有华为,苹果和Oppo手机,定义Huawei类,Apple类,以及Oppo类,再定义Up类去同时继承这三各类如下GIF图所示

image.png

                报错图如下所示

image.png

                但是,Java支持多层继承多层继承指当父类衍生出一个子类后,该子类可以作为父类继续衍生出它的子类,如此往复,循环不止。比如:假设up有一部Vivo手机。定义Phone类作为父类,衍生出Vivo子类,而Vivo子类可以继续衍生出Up子类,如下图GIF图所示

image.png

        2.父类私有成语子类无法继承。

                这就不用说了吧,什么是继承继承的使用场景我们演示过很多次🌶。发生继承关系后,子类拥有了父类的非私有成员

        3.父类构造器子类不能继承。

                这也挺好理解的 : 构造器用于初始化对象。父类构造器用于初始化父类对象,子类构造器用于初始化子类对象。你就是继承了父类构造器你也没个J8用啊😅。只不过为了初始化子类对象中的父类内容,我们才在子类构造器中通过super来调用父类构造器。

        4.继承关系往往体现了" is a " 的关系。

                只有在子类符合“ is a ”父类的情况下才使用继承,其他情况下不建议使用。像up之前举过的栗子:grape(葡萄)满足“ is a ”fruit(水果);Dog,Pig,Cat等符合“is a” animal,等等均是如此。

七、继承的本质——java 继承内存图解重要):

                仅仅会使用继承是远远不够的。我们还必须了解到在使用继承时,jvm内存中究竟发生了什么。up之前写过的一篇博文详细介绍了Java使用继承时,在内存中实际发生了什么,有助于初学者加深对Java继承以及内存模型的印象,有兴趣的小伙伴儿可以点击链接查看,链接如下 :      https://blog.csdn.net/TYRA9/article/details/128729074?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167625371116800211569247%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=167625371116800211569247&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-128729074-null-null.blog_rank_default&utm_term=%E7%BB%A7%E6%89%BF&spm=1018.2226.3001.4450

八、方法重写

        1.定义 : 

                如果子类中定义了一个返回值类型、方法名,参数列表都与父类中某个方法相同的方法,只是方法体中的语句不同,我们称为:子类重写了父类的某个方法。方法重写也叫方法的复写、覆盖

                那我们什么时候要用到方法重写呢?  其实就是我们在之前定义重名方法时说过的——当父类方法不满足现实需求,需要拓展父类方法的功能时,就要重写父类方法,以重新实现父类功能。              

        2.关键 : 

                想达到方法重写,必须满足返回值类型、方法名,参数列表都相同!即——外壳不变,内部重写。(实际子类返回值类型也可以是父类返回值类型的子类

                子类的重写方法可以用@Override注解来标注。(up以后会单独出博文讲解Java注解)

        3.注意事项 : 

                父类私有方法不能被重写

                子类方法访问权限不能小于父类方法,即访问权限 : 子类 ≥ 父类。(本文后面我们就会讲到Java四大访问修饰符) 

                子类不能比父类抛出更大的异常。因为看这篇文章的估计多数是初学者,还不知道什么是异常,这里推荐去看up写得Java万字详解系列之异常篇

        4.方法重写和方法重载的区别 : 

                这是初学者容易混淆的地方。其实压根儿没那么多事。直接给大家一张图,一目了然

image.png

                如果要再解释的通俗易懂一些的话,你可以理解为: up现在手里有一张弓剑,就随便一张弓吧,比如下面这张破弓 : 

image.png

                那么,方法重写就相当于——本来我每次射一支普通箭,现在我给箭头上装了微型炸药,使得我可以每次射一支爆破箭。而方法重载就相当于还是原来那普通箭,只不过我每次可以射出多支箭了。 

                其实你就把二者的定义都记住不就⭐了么,也没多少😋。

        5.代码演示 : 

                ①方法重写演示 : 

                我们定义父类Phone类,仍然以JavaBean标准来敲,并在其中定义一个call方法,代表打电话。定义子类为Vivo类来继承Phone类。你可能会有疑惑——为什么是Vivo?一张图告诉你为什么: 

image.png

                没错,我们要在子类中重写父类的call方法,来给坤坤打电话🤩,当然就要用坤坤同款啦😋。

                Person类代码如下 :                

package knowledge.succeed.re_method;
public class Phone {
    //成员变量
    private String brand;       //手机品牌
    private double price;       //手机发售价格
    //构造器
    public Phone() {
    }
    public Phone(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }
    //getter,setter方法
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    //成员方法
    public void call() {
        System.out.println("手机都可以打电话📞.");
    }
}

image.gif

                Vivo类代码如下

package knowledge.succeed.re_method;
public class Vivo extends Phone {
    @Override
    public void call() {        //重写了父类的call方法
        super.call();
        System.out.println("🐔坤哥在吗?");
    }
}

image.gif

                TestOverride类代码如下

package knowledge.succeed.re_method;
public class TestOverride {
    public static void main(String[] args) {
        Vivo vivo = new Vivo();
        vivo.call();
    }
}

image.gif

                运行结果如下 : 

image.png  

                ②@Override注解演示 :  

                 @Override 注解可以强制一个子类必须重写父类方法或者实现接口的方法,否则就会编译出错。                

                现在,我们修改子类Vivio类中call方法的形参列表,使其不满足重写父类call方法的条件,如下图所示 : 

image.png

                这时候,如果你给子类call方法添加@Override注解,IDEA会报错。如下图所示 :  image.png

                ③方法重写的注意事项演示 :

                我们在Phone类新建一个私有的music方法,如下图所示 : 

image.png

                然后我们在Vivo类试图重写music方法,如下图所示 : 

image.png

                大家注意看子类中的music方法,与父类music方法相比,满足返回值类型,方法名,参数列表都相同的条件,而且子类使用public修饰,毫无疑问public的访问权限 > private的访问权限,因此访问权限也满足重写的条件。但是奇怪的是,方法旁边居然没有给出提示!这时有人可能会有疑惑:什么提示?要知道我们使用的可是目前智能程度T0级别的集成编辑器,子类重写父类方法,IDEA会在方法旁边给出提示,就拿我们在演示①中重写的call方法为栗,如下图所示: 

image.png

                而且,你将鼠标悬浮在提示图标上,还会跳出提示窗口,如下GIF所示 : 

image.png

                现在子父类的music方法压根没有任何提示,这说明子类中的music方法和父类私有的music方法没有重写关系。这相当于子类重新定义了一个它特有的方法。其实也好理解,你子类都不能通过super关键字访问父类私有方法,怎么重写?

                再来说说修饰符的问题,子类的修饰符要求访问权限 ≥ 父类的否则报错。我们仍以music方法为栗,将父类music方法改为public修饰符,子类music方法改为默认修饰符(即什么都不写)。如下图所示

image.png               

不出所料,子类music方法立刻报错,如下图所示 : 

image.png  

九、Java四大访问权限修饰符 : 

        1.介绍 : 

                访问权限修饰符指的是用来修饰成员变量,成员方法和类,来确定它们的访问权限的修饰符。Java四大访问权限修饰符分别是private、默认、protected,public。              

                private和public在封装篇已经讲过了,这里我们来简单复习一下 : private修饰的成员只能在本类使用,而public修饰的成员可以被所有类使用。重点说一下默认和protected。

                默认修饰符,指的就是没写修饰符。默认修饰符修饰的成员允许在本包下使用

                protected修饰符修饰的成员除了可以在本包下使用,在其子类中也可以使用

        2.权限 : 

                四大修饰符按照根据访问权限从小到大的原则依次是 : private <  默认 < protected  < public。给大家来张图更清晰直观,如下图所示 : 

image.png   

        3.演示 : 

                ①private修饰符演示 : 

                        新建一个Private_demo类,并在同一包下创建TestAccess类作为演示类。如下图所示 : 

image.png                         

                        Private_demo类代码如下

package knowledge.succeed.access;
public class Private_demo {
    private int temp_1 = 11;
    public void printTemp() {
        temp_1 = 5;
        System.out.println("temp_1 = " + temp_1);
    }
}

image.gif

                        TestAccess类代码如下

package knowledge.succeed.access;
public class TestAccess {
    public static void main(String[] args) {
        Private_demo private_demo = new Private_demo();
        //private_demo.temp_1 = 3;    直接调用temp_1变量会报错。
    }
}

image.gif

                         在TestAccess类中直接调用temp_1变量, IDEA报错,如下图所示 : 

image.png

                        在Private_demo的本类方法printTemp直接调用temp_1变量是可行的,不会报错。接着我们在TestAccess类中调用此方法,可以成功输出temp_1变量。

                        TestAccess类代码如下

package knowledge.succeed.access;
public class TestAccess {
    public static void main(String[] args) {
        Private_demo private_demo = new Private_demo();
        private_demo.printTemp();
    }
}

image.gif

                        运行结果

image.png

                ②默认修饰符 : 

                        默认比private的访问权限高一级,本包下的类都可以访问默认修饰符修饰的成员。

                        如下图所示 : 

image.png

                        Default类代码如下

package knowledge.succeed.access;
public class Default_demo {
    int temp_2 = 11;
}

image.gif

                        TestAccess类代码如下 :

package knowledge.succeed.access;
public class TestAccess {
    public static void main(String[] args) {
        Default_demo default_demo = new Default_demo();
        default_demo.temp_2 = 1111;
        System.out.println("temp_2 = " + default_demo.temp_2);
    }
}

image.gif

                        我们在本包下的TestAccess类中,可以直接调用temp_2变量,也可以直接输出它。

                        运行结果

image.png

                        但是,当我们在其他包下的类中调用temp_2变量时,IDEA仍会报错,如下图所示:

image.png

image.png       

IDEA会提示我们,不能跳出本包下访问默认修饰符修饰的成员。 

                ③protected修饰符 : 

                        protected修饰符则比默认修饰符还高一级。使得——即使是跳出本包外,只要是其子类仍可以直接调用protected修饰的成员。

                        我们在access包下的Protected_demo类中定义temp_3变量,然后在access_2包下的测试类中尝试调用temp_3变量(注意,测试类要继承Protected_demo类),如下图所示 : 

image.png

                        Protected_demo类代码如下

package knowledge.succeed.access;
public class Protected_demo {
    protected int temp_3 = 1000;
}

image.gif

                        TestAccess_2类代码如下

package knowledge.succeed.access_2;
import knowledge.succeed.access.Protected_demo;
public class TestAccess_2 extends Protected_demo {
    public static void main(String[] args) {
        TestAccess_2 testAccess_2 = new TestAccess_2();
        testAccess_2.temp_3 = 222;
        System.out.println("temp_3 = " + testAccess_2.temp_3);
    }
}

image.gif

                        运行结果 : 

image.png

                ④public修饰符 : 

                        public修饰的成员,在任何类中都可以使用。在access包下新建Public_demo类,并在其中建立temp_4变量;然后在access_2包下的测试类中调用temp_4变量。注意:测试类既非本包下的类,又非Public_demo的子类,如下图所示 : 

image.png

                        Public_demo类代码如下

package knowledge.succeed.access;
public class Public_demo {
    public int temp_4 = 666;
}

image.gif

                        TestAccess_2类代码如下

package knowledge.succeed.access_2;
import knowledge.succeed.access.Public_demo;
public class TestAccess_2 {
    public static void main(String[] args) {
        Public_demo public_demo = new Public_demo();
        public_demo.temp_4 = 11;
        System.out.println("temp_4 = " + public_demo.temp_4);
    }
}

image.gif

                        运行结果

image.png

        4.总结 : 

                ①四大修饰符总结 : 

                private修饰符 : 强调自己使用,只能在本类下使用。
                默认 : 强调本包使用,只能在本类,同包下的类中使用。
                protected : 强调子类使用,只能在本类,同包下的类,以及其子类中使用。

                public : 强调公开使用,没有只能,谁都可以用。         

                ②关于修饰类 : 

                修饰类时,只能使用默认修饰符和public修饰符。(下面的延申中会更详细讲解) 

        5.延申(关于类和源文件的关系) : 

                ①一个Java源文件中可以定义多个类,源文件的基本组成部分是类
                ②源文件中定义的类,最多只能有一个类被public修饰,其他类的个数不限。
                ③如果源文件中有被public修饰的类,那么源文件名必须与该类类名保持一致
                ④如果源文件中没有被public修饰的类,那么源文件名只要符合命名规范就可以。
                ⑤main函数不一定非得写在public修饰的类中,也可以将main函数写在非public修饰的类中,然后通过指定运行非public类,这样入口方法就是非public类的main方法。

十、总结 :          

        🆗,费了这么大功夫总算是把面向对象三大特性的第二关BOSS给干掉了😄。回顾一下,我们从继承的引入开始,到继承的举栗成员变量、成员方法,以及构造器在继承关系中的使用;以及继承的内存图解等等,中间这几个标注了"重点"字样的都做了很详细的讲解,最后我们又说到了方法重写和Java四大访问修饰符。

        在Java面向对象三大特性里有句话说的好:封装是继承的前提,继承是多态的前提。相信之后的多态篇,我们能完美结局。好滴,感谢阅读

System.out.println("END------------------------------------------------------------");                  

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。