当前位置: 首页 > news >正文

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

目录

前言 : 

一、为什么需要多态 : 

        1.白璧微瑕 : 

        2.举栗(请甘雨,刻晴,钟离吃饭):

        3.代码 : 

        4.问题 : 

二、什么是多态 : 

        1.定义 : 

        2.多态的实现步骤(重要) : 

三、多态的使用 : 

        1.多态中成员方法的使用(重要) : 

                ①使用规则 : 

                ②代码演示 : 

                ③利用多态实现“旅行者请璃月人吃饭” : 

        2.多态中成员变量的使用 : 

                ①使用规则 : 

                ②代码演示 : 

四、多态的优点和缺点 : 

        1.益处 : 

        2.弊端 : (重点)

五、类型转换 :

        Δ前言 : 

        1.向上转型(自动): 

                ①含义 : 

                ②语法格式 : 

                ③代码演示 : 

        2.向下转型(强制): 

                ①含义 : 

                ②语法格式 : 

                ③代码演示 : 

        3.注意事项 : 

        4.instanceof关键字(重要):

                ①概述 : 

                ②用法 : 

                ③代码演示 : 

六、Java的动态绑定机制(重要): 

七、多态的应用 : 

八、抽象类,抽象方法,abstract关键字详解 : 

九、final关键字详解 : 

十、static关键字(类变量和类方法)详解 : 

十一、接口详解 : 

十二、Debug详解 : 

十三、面向对象总结 : 


前言 : 

        Hi,guys.这篇博文是Java面向对象三大特性篇的第三篇———多态篇。之前我们就一直强调,面向对象三大特性中,封装是继承的前提,继承是多态的前提。多态也是实际开发中运用最多,最广泛的一个特性!因此,多态的重要性不言而喻。本篇博文,内容包括但不限于多态的介绍多态的使用详解多态中的类型转化机制动态绑定机制详解抽象类详解final关键字、static关键字详解接口详解等等,感谢阅读
        这篇博文算是三大特性篇的最后一篇😎,之后up会将三大特性对应的三篇博客合并起来,并在此基础之上写一篇总结Java面向对象的十万+字博文,力争以最通俗易懂的方式让初学者快速入门并理解面向对象的灵魂。
        还有是CSDN貌似不欢迎长文章😂,因此up计划将这篇博文中部分内容比较长的知识点,单独再出篇博文,然后以插入链接的方式呈现给大家。谢谢理解,感谢阅读!

一、为什么需要多态 : 

        1.白璧微瑕 : 

        继承的使用,给我们带来了极大的便利,不但提高了代码的可维护性,而且提高了代码的复用性,使我们免于敲写过多繁冗重复的代码。然而,甘瓜苦蒂,天下物无全美我们承认,继承已经是个很🐂🖊的特性了。但在某些情况下,继承也会显出它的颓势

        2.举栗(请甘雨,刻晴,钟离吃饭):

        这不,马上海灯节就要到了。 旅行者答应要请刻晴,钟离,和甘雨一起去新月轩吃晚饭。为了让大家开心,旅行者提前了解了三个人喜爱的食物 : 刻晴喜欢吃金丝虾球,钟离喜欢吃豆腐,甘雨则喜欢吃清心。 现在让你用Java来描述这件事情,你怎么做?
        思路
        首先,刻晴,钟离和甘雨是三个不同的对象,因此我们需要分别定义三个类来模拟和描述刻晴,钟离,甘雨。同理,金丝虾球,杏仁豆腐和清心也是三个不同的对象,因此也需要分别定义三个类来模拟这三种食物。而三类人,三类食物均有共同属性;且刻晴,钟离和甘雨均有各自特有的行为;因此考虑使用封装和继承特性来实现
        假设刻晴,钟离和甘雨都是璃月人,那么我们就可以定义一个父类来表示璃月人,然后让表示刻晴,钟离和甘雨的子类去继承表示璃月人的父类。
        同理 : 假设金丝虾球,豆腐,和清心都属于料理,那么我们就可以定义一个父类来表示料理,然后让表示金丝虾球,豆腐和清心的子类去继承表示料理的父类。
        根据假设,先定义一个Liyue_people类,然后再分别定义Keqing类,Zhongli类和Ganyu类,并让它们继承Liyue_people类。同时,在Keqing类,Zhongli类和Ganyu类中定义它们的特有方法。其中 : 
        Keqing类特有方法——天街巡游 : sky_street_cruise()
        Zhongli类特有方法——
天星 : sky_stars()
        Ganyu类特有方法——
降众天华 : descend_to_heaven()

        同样根据假设,先定义Cooking类,然后再分别定义Shrimp_balls类,Bean_curd类和Qingxin类,并让它们继承Cooking类。注意 : 每种料理都要有名字,营养,和味道三个属性。假设新月轩在海灯节期间会稿活动,部分菜肴有优惠,我们需要在Cooking类中定义一个cooking_info()方法,来打印出料理的基本信息(菜品名,营养,风味等)。
        定义Traveler类(旅行者类),并在Traveler类中分别定义方法名为my_treat的三个重载方法,第一个方法需要传入一个刻晴类对象和一个金丝虾球对象;第二个方法需要传入一个钟离类对象和一个豆腐类对象;第三个方法则需要传入一个甘雨类对象和一个清心类对象。利用方法重载可以实现请不同对象吃饭的需求。
        最后定义Treat类进行测试。在Treat类先创建好刻晴,钟离和甘雨的吃饭对象,以及金丝虾球,豆腐和清心的料理对象。之后再定义旅行者类对象,并调用旅行者类中的my_treat方法(),传入对象参数即可成功请客吃饭,过一个完美的海灯节。
        关系图(如下) : 

       

        3.代码 : 

        为了简洁,up直接将Keqing类,Zhongli类和Ganyu类都写在Liyue_people类的源文件中,将Shrimp_balls类,Bean_curd类和Qingxin类都写在Cooking类的源文件中。
        Liyue_people类,Keqing类,Zhongli类和Ganyu类代码如下 :        

package knowledge.polymorphism.introduction;//父类 : 璃月人类
public class Liyue_people {//成员变量private String name;private int age;private String gender;//构造器public Liyue_people() {}public Liyue_people(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;}//setter,getter方法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 getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}
}//子类 : 刻晴类
class Keqing extends Liyue_people {public Keqing(String name, int age, String gender) {super(name, age, gender);}//刻晴————天街巡游public void sky_street_cruise() {System.out.println("剑光如我,斩尽芜杂!");}
}
//子类 : 钟离类
class Zhongli extends Liyue_people {public Zhongli(String name, int age, String gender) {super(name, age, gender);}//钟离————天星public void sky_stars() {System.out.println("天动万象!");}
}
//子类 : 甘雨类
class Ganyu extends Liyue_people {public Ganyu(String name, int age, String gender) {super(name, age, gender);}//甘雨————降众天华public void descend_to_heaven() {System.out.println("为了岩王帝君!");}
}

       Cooking类,Shrimp_balls类,Bean_curd类和Qingxin类代码如下 :                         

package knowledge.polymorphism.introduction;//父类 : 料理类
public class Cooking {//成员变量private String food_name;private String nutrition;private String flavor;//构造器public Cooking() {}public Cooking(String food_name, String nutrition, String flavor) {this.food_name = food_name;this.nutrition = nutrition;this.flavor = flavor;}//setter,getter方法public String getFood_name() {return food_name;}public void setFood_name(String food_name) {this.food_name = food_name;}public String getNutrition() {return nutrition;}public void setNutrition(String nutrition) {this.nutrition = nutrition;}public String getFlavor() {return flavor;}public void setFlavor(String flavor) {this.flavor = flavor;}//打印出菜肴信息的方法public void cooking_info() {System.out.print("\t菜品: " + this.food_name);System.out.print("\t\t营养:" + this.nutrition);System.out.println("\t\t风味:" + this.flavor);}
}//子类 : 金丝虾球类
class Shrimp_balls extends Cooking {public Shrimp_balls(String food_name, String nutrition, String flavor) {super(food_name, nutrition, flavor);}
}
//子类 : 豆腐类
class Bean_curd extends Cooking {public Bean_curd(String food_name, String nutrition, String flavor) {super(food_name, nutrition, flavor);}
}
//子类 : 清心类
class Qingxin extends Cooking {public Qingxin(String food_name, String nutrition, String flavor) {super(food_name, nutrition, flavor);}
}

        Traveler类代码如下 :  

package knowledge.polymorphism.introduction;//旅行者类
public class Traveler {//反正是个哑巴,要什么属性//打工人行为 : //1.请刻晴吃金丝虾球public void my_treat(Keqing keqing, Shrimp_balls shrimpBalls) {System.out.println("海灯节,👴" + "请" + keqing.getName() + "吃" + shrimpBalls.getFood_name());}//2.请钟离吃杏仁豆腐public void my_treat(Zhongli zhongli, Bean_curd bean_curd) {System.out.println("海灯节,👴" + "请" + zhongli.getName() + "吃" + bean_curd.getFood_name());}//3.请甘雨吃清心public void my_treat(Ganyu ganyu, Qingxin qingxin) {System.out.println("海灯节,👴" + "请" + ganyu.getName() + "吃" + qingxin.getFood_name());}
}

        Treat类代码如下

package knowledge.polymorphism.introduction;public class Treat {public static void main(String[] args) {//1.创建要吃饭的角色对象//创建刻晴对象Keqing keqing = new Keqing("刻晴", 18, "女");//创建钟离对象Zhongli zhongli = new Zhongli("摩拉克斯", 6000, "男");//创建甘雨对象Ganyu ganyu = new Ganyu("王小美", 3000, "女");//2.创建被吃的食物对象//创建金丝虾球对象Shrimp_balls shrimp_balls = new Shrimp_balls("金丝虾球", "蛋白质是🐂🥩的六倍!", "鲜香味美");//创建杏仁豆腐对象Bean_curd bean_curd = new Bean_curd("杏仁豆腐", "富含微生物", "清甜爽口");//创建清心对象Qingxin qingxin = new Qingxin("清心", "妹说就是0卡", "苦的一批");//3.创建旅行者对象Traveler traveler = new Traveler();//4.打扫干净屋子再请客System.out.println("===============欢迎来到新月轩===============");System.out.println("---------------以下是本店的海灯节热门菜肴 : ");shrimp_balls.cooking_info();bean_curd.cooking_info();qingxin.cooking_info();System.out.println("\n旅行者:\"哎呀我去,整挺好,不用挑了!👴全都要!\"\n");traveler.my_treat(keqing, shrimp_balls);    // 传入刻晴对象和金丝虾球对象traveler.my_treat(zhongli, bean_curd);      // 传入钟离对象和杏仁豆腐对象traveler.my_treat(ganyu, qingxin);          // 传入甘雨对象和清心对象}
}

        Treat类代码如下 : 

        4.问题 : 

        不知道大家发现没有,旅行者每请一个角色吃饭,都得重新定义一个新的重载方法,现在才请了三五个角色吃饭,可能还觉得没那么麻烦。但是,提瓦特大陆上的角色成百上千啊。到时候难道要定义100个方法,1000个方法?既然都是请客,为什么不能在一个方法中实现呢。就拿方才我们举得栗子来打个比方,既然刻晴,钟离,和甘雨都是璃月人,为什么我们不直接定义一个方法来请璃月人吃饭?这样不就省事儿多了!
        这时候我们开始认识到继承特性的一些弊端 : 只有继承的程度,无法实现诸如“旅行者直接请璃月人吃饭”的需求。那怎么办?害!铺垫一大堆废话,不就为了讲咱的多态么!这不就来了!

二、什么是多态 : 

        1.定义 : 

                所谓多态,其实字面意思就是多种状态,没那么复杂。在Java中,方法和对象,都可以体现出多态。
                ①对于方法,多态表现在方法重载和方法重写上。
                方法重载 : 同一行为,形参不同则表现形式不同
                方法重写 : 同一行为,调用者不同则表现形式不同
                ②对于对象,多态表现在同一对象在不同情况下表现出不同的状态或行为。对象不仅仅是体现多态,更重要的应用多态。因此,对象的多态就是Java多态的核心

        2.多态的实现步骤(重要) : 

                有继承(或实现)关系。(继承是多态的前提。"实现"指实现类或接口,后面我们会讲到)
                有方法重写
                父类引用指向子类对象
                对于第三条,要特别补充一些内容。我们先来举个栗子吧。
                eg : 假设有Cat类继承了Animal类,如下所示 : 

public class Animal {public void eat() {}
}class Cat extends Animal {public void eat() {System.out.println("🐱喜欢吃🥩");}
}

                那么父类引用指向子类对象即 

    Animal animal = new Cat();

                Δ解释 :                
                在上面这行代码中,等号左面的animal是一个Animal类型的引用变量,但是,这个Animal类型的引用所指向的对象,即堆空间中真正的对象,却是一个Cat类型。这就叫做父类引用指向子类对象。有什么用呢?别着急,下面多态的使用就会正式介绍到。这里我们要说一点别的内容 : 
                在多态中,我们将等号左边的这个引用变量的类型,称为编译类型。而将等号右边的——在堆空间中new出来的——真正的对象的类型,称为运行类型。其中,编译类型决定了引用变量可以调用哪些属性和行为;而运行类型则是在程序运行过程中jvm实际使用的类型
                比方说,现在我们通过animal对象来调用eat() 方法,因为编译类型是Animal类,因此在编译时,编译器要判断Animal类有没有eat() 方法。诶,有eat() 方法,那就可以调用。但在实际调用时,jvm会优先使用运行类型Cat类中的eat() 方法,因此打印结果为“🐱喜欢吃🥩”。

三、多态的使用 : 

        1.多态中成员方法的使用(重要) : 

                ①使用规则 : 

                编译看左(即左边的编译类型有没有这个成员,这决定了我们能不能调用目标成员)
                运行看右(即右边的运行类型中的该成员,才是运行中实际使用的成员)             

                ②代码演示 : 

                up就以Animal类为父类,Cat类为子类,TestMethod类为测试类(置于同一包下)。我们给Animal类定义一些动物共同的属性,例如species_name(物种名),average_life(平均寿命),living_habit(生活习性)等;定义一些动物共同的行为,例如eat(吃),sleep(睡)等。
                然后我们在Cat类中重写eat()方法和sleep()方法,并在TestMethod类中使用多态,根据多态中成员方法的使用规则 : 如果我们以Animal类的引用去调用eat()或者sleep()方法,实际运行中,优先使用的一定是子类Cat类中的eat()和sleep()方法

                Animal类代码如下

package knowledge.polymorphism.about_method;public class Animal {//成员变量private String species_name;    //物种名private int average_life;       //平均寿命private String living_habit;    //生活习性//构造器public Animal() {}public Animal(String species_name, int average_life, String living_habit) {this.species_name = species_name;this.average_life = average_life;this.living_habit = living_habit;}//setter, getter方法public String getSpecies_name() {return species_name;}public void setSpecies_name(String species_name) {this.species_name = species_name;}public int getAverage_life() {return average_life;}public void setAverage_life(int average_life) {this.average_life = average_life;}public String getLiving_habit() {return living_habit;}public void setLiving_habit(String living_habit) {this.living_habit = living_habit;}//成员方法public void eat() {System.out.println("这是Animal类的eat()方法,需要被重写捏😋");}public void sleep() {System.out.println("这是Animal类的sleep()方法,需要被重写捏🤗");}
}
/*按下insert键后,输入会变成改写模式,需要再按下一次才能取消。*/

                Cat类代码如下 : 

package knowledge.polymorphism.about_method;public class Cat extends Animal {//构造器public Cat() {}public Cat(String species_name, int average_life, String living_habit) {super(species_name, average_life, living_habit);}//重写父类的eat()方法和sleep()方法@Overridepublic void eat() {System.out.println("🐱🐱喜欢吃🥩捏");}@Overridepublic void sleep() {System.out.println("🐱🐱一般蜷缩着睡觉,且不喜欢在光线强烈的地方睡");}//定义成员方法用于打印出猫的基本信息public void cat_info() {System.out.println("猫的物种学名:" + this.getSpecies_name() +"\t猫的平均寿命 : " + this.getAverage_life() +"\t猫的生活习性 : " + this.getLiving_habit());}
}

                TestMethod类代码如下

package knowledge.polymorphism.about_method;public class TestMethod {public static void main(String[] args) {//父类引用指向子类对象Animal animal = new Cat();animal.setSpecies_name("Felinae");      //Felinae是猫的拉丁学名animal.setAverage_life(15);animal.setLiving_habit("喜欢独自生活,喜欢干净,喜欢肉类");System.out.println("简单介绍一下猫这一物种 : ");((Cat)animal).cat_info();               //强转类型,后面会讲到。System.out.println("------------------------------------------");//调用成员方法animal.eat();animal.sleep();}
}

                运行结果 :  

                果然不出我们所料,实际运行的eat() 方法和sleep() 方法是Cat类中重写过后的方法
                为了更进一步验证我们的结论,现在我们把Cat类中的两个重写方法都给注释掉,如下图所示 :  

                此时,若通过animal引用再次调用eat() 方法和 sleep() 方法,因为子类重写方法被注释,因此会调用父类的eat() 和 sleep() 方法,运行结果如下

                通过演示,相信大家对于多态中成员方法的使用已有了一定的了解。但这时候可能有p小将(personable小将,指风度翩翩的人)出来说了 :  你就用了Cat一个子类,也好意思说自己是多态😅,我咋看不出来捏😅?          

                不愧是p小将,一针见血的😓!没错,前面我们说过——对象的多态才是Java多态的核心!同一对象在不同情况下表现出不同的状态或行为称为对象的多态。现在我们使用父类引用仅调用了Cat一个运行类型,没有体现多种状态
                好滴,于是我们在本包下新建一个Dog类,Dog类也继承Animal类。此时,继承关系如下图

                🆗,我们先讲Cat类中重写的eat() 方法和sleep方法恢复, 如下图所示 : 

                然后在Dog类中对eat() 和sleep() 方法进行重写,并且也定义一个dog_info() 方法,用于打印出狗的基本信息Animal类和Cat类代码保持不变

                Dog类代码如下

package knowledge.polymorphism.about_method;public class Dog extends Animal {//构造器public Dog() {}public Dog(String species_name, int average_life, String living_habit) {super(species_name, average_life, living_habit);}//重写父类eat() 方法 和 sleep() 方法@Overridepublic void eat() {System.out.println("🐕也喜欢吃🥩捏");}@Overridepublic void sleep() {System.out.println("🐕想睡就睡,活在当下");}//定义成员方法用于打印出狗的基本信息public void dog_info() {System.out.println("狗的物种学名:" + this.getSpecies_name() +"\t狗的平均寿命 : " + this.getAverage_life() +"\t狗的生活习性 : " + this.getLiving_habit());}
}

                在TestMethod类中,当指向Cat对象的animal引用变量调用完成员方法后,我们改变animal引用的指向,使它指向一个Dog类对象,并去调用Dog类中重写的方法
                TestMethod类代码如下

package knowledge.polymorphism.about_method;public class TestMethod {public static void main(String[] args) {//1.父类引用指向子类对象Animal animal = new Cat();animal.setSpecies_name("Felinae");      //Felinae是猫的拉丁学名animal.setAverage_life(15);animal.setLiving_habit("喜欢独自生活,喜欢干净,喜欢肉类");System.out.println("简单介绍一下猫这一物种 : ");((Cat)animal).cat_info();               //强转类型,后面会讲到。System.out.println("--------------------------------");//调用成员方法animal.eat();animal.sleep();System.out.println("=======================================================");//2.改变animal引用变量的指向,使其指向一个Dog类对象animal = new Dog();animal.setSpecies_name("Canis lupus familiaris");animal.setAverage_life(12);animal.setLiving_habit("喜欢啃骨头,喜欢嗅闻东西,喜欢摇尾巴");System.out.println("简单介绍一下狗这一物种 : ");((Dog)animal).dog_info();               //强制向下转型,后面会讲到。System.out.println("--------------------------------");//调用成员方法animal.eat();animal.sleep();}
}

                运行结果 : 

                通过运行结果我们可以看出 : animal引用变量指向Cat类对象时,运行的eat() 和 sleep() 方法就是Cat类中的方法,而当我们改变animal引用的指向,使其指向Dog类对象时,运行的方法就变成了Dog类中的方法。
                什么概念?
                同一引用变量,调用相同的方法,却因为指向的对象不同而产生了不同的结果。而我们平时习惯于将指向对象的引用变量当作对象的简称。那么,此处animal对象体现的不就是多态么。

                ③利用多态实现“旅行者请璃月人吃饭” : 

                有了多态后,前面的“旅行者请吃饭”问题便可以迎刃而解了 :
                我们只需要在Traveler类(旅行者类)中定义一个my_treat() 方法,但与之前不一样的是,形参类型定义为Liyue_people类和Cooking类,即角色对象和料理对象各自的父类。如下所示  :

    public void my_treat(Liyue_people liyue_people, Cooking cooking) {System.out.println("海灯节,👴" + "请" + liyue_people.getName() + "吃" + cooking.getFood_name());}

                注意,这时候, 因为Liyue_people类是Keqing类,Zhongli类和Ganyu类的父类,因此,不管你传入这三个对象中的哪一个,都会形成相当于" Liyue_people liyue_people = new 子类对象;"的形式,即父类引用指向子类对象——多态的形式。此时,传入的是哪个对象,就调用哪个对象的方法。Cooking类和它的子类也是同理。
                璃月人类,刻晴类,钟离类,甘雨类以及料理类,金丝虾球类,豆腐类,清心类代码均不变Traveler类代码如下

package knowledge.polymorphism.introduction;public class Traveler {//利用多态,一个方法解决请客吃饭问题public void my_treat(Liyue_people liyue_people, Cooking cooking) {System.out.println("海灯节,👴" + "请" + liyue_people.getName() + "吃" +cooking.getFood_name());}
}

                Treat类代码也不变,不过注意,此时Treat类中使用的my_treat() 方法已变化
                运行结果 : 

                利用多态,将父类类型作为形参,一个方法照样实现了我们的需求😎!

        2.多态中成员变量的使用 : 

                ①使用规则 : 

                编译看左(即左边的编译类型有没有这个成员,这决定了我们能不能调用目标成员)
                运行看左多态关系中,成员变量是不涉及重写的

                ②代码演示 : 

                1)up以Fruit类(水果类)为父类,子类Apple类(苹果类)和Grape类(葡萄类)分别继承Fruit类,以TestField类为测试类
                2)我们在父类Fruit类中定义一些水果公有的属性(不用private修饰),例如species_name(物种学名), sweetness(平均甜度), shape(形态特征)。并分别在Apple类和Grape类中定义它们自己的这三个同名属性。
                3)最后在测试类中,分别建立Fruit类和Apple类,Grape类间的多态关系,并通过Fruit类型的引用去调用这三个属性并输出。根据多态关系中成员变量的使用规则,输出的三个属性应该每次都以Fruit类中的为基准。
                4)为使代码简洁,up将两个子类都写在Fruit类的源文件中。Fruit类,Apple类,Grape类代码如下 :         

package knowledge.polymorphism.about_field;//父类 : Fruit类
public class Fruit {//成员变量String species_name = "物种学名噢";double sweetness = 9.5;             //水果的平均甜度大致为8~10左右。String shape = "水果的外观形状捏";//构造器public Fruit() {}public Fruit(String species_name, double sweetness, String shape) {this.species_name = species_name;this.sweetness = sweetness;this.shape = shape;}}//子类 : 葡萄类
class Grape extends Fruit {//成员变量String species_name = "Vitis_vinifera_L";       //葡萄的拉丁学名double sweetness = 16.5;                        //葡萄的平均甜度String shape = "长得和葡萄差不多";               //葡萄的形态特征//构造器public Grape() {super();}public Grape(String species_name, double sweetness, String shape) {super(species_name, sweetness, shape);}
}//子类 : 苹果类
class Apple extends Fruit {//成员变量String species_name = "Malus_pumila_Mill";       //苹果的拉丁学名double sweetness = 8.5;                          //苹果的平均甜度String shape = "长得和苹果一样";                  //苹果的形态特征//构造器public Apple() {super();}public Apple(String species_name, double sweetness, String shape) {super(species_name, sweetness, shape);}
}

                TestField类代码如下 :  

package knowledge.polymorphism.about_field;public class TestField {public static void main(String[] args) {//多态Fruit fruit = new Apple();System.out.println("苹果的物种学名:" + fruit.species_name);System.out.println("苹果的平均甜度:" + fruit.sweetness);System.out.println("苹果的形态特性:" + fruit.shape);System.out.println("---------------------------------");//改变fruit引用变量的指向,使其指向Grape类对象。fruit = new Grape();System.out.println("葡萄的物种学名:" + fruit.species_name);System.out.println("葡萄的平均甜度:" + fruit.sweetness);System.out.println("葡萄的形态特性:" + fruit.shape);}
}

                运行结果

        果然不出我们所料,不管你fruit引用指向的是哪个子类水果对象,直接调用成员变量,永远优先使用Fruit类本身的属性。这时候p小将(personable小将,指风度翩翩的人)又要出来bb问了 : 🤬tnnd(太难弄哒),好不容易知道多态有个能解决继承缺陷的用处,怎么现在又用不了子类的属性了😅?那你写这么一堆干嘛,博主你搁这儿搞笑呢🤗?
        p小将你先别急。以上演示只是要说明 : 多态关系中,直接使用成员变量的规则是编译看左,运行看左即使子类定义了与父类同名的属性,但本质上那也是子类特有的属性了。我们之前一直在说,封装是继承的前提,继承是多态的前提。了解了封装和继承就知道,实际开发中通过对象直接调用属性的情况是不常见的,不符合封装的要求。我们编写的类应该尽量靠近JavaBean标准。那我们就不能在多态的前提下使用子类的属性了吗?当然不是😎,这不,多态的优缺点总结就来了。

四、多态的优点和缺点 : 

        1.益处 : 

                可维护性 : 基于继承关系,只需要维护父类代码,提高了代码的复用性,降低了维护工作的工作量。
                可拓展性 : 把不同的子类对象都当作父类看待,屏蔽了不同子类对象间的差异,做出了通用的代码,派生类的功能可以被基类的方法或引用变量所调用,以适应不同的需求,实现了向后兼容

        2.弊端 : (重点

                父类引用不能直接使用子类的特有成员。

                这也很好解释 : 前面在讲多态的实现步骤时我们说过——编译类型决定了引用变量可以调用哪些属性和行为;而运行类型则是在程序运行过程中jvm实际使用的类型。父类引用,说明编译类型是父类类型,以父类类型编译当然只能使用父类中存在的成员。当然,这里所说的成员包括成员变量和成员方法,这二者在多态关系中的使用略有不同:使用的成员变量必须是在父类中存在的,且成员变量不涉及重写;使用的成员方法也必须是在父类中存在的,但是如果子类重写了父类方法,优先调用子类重写的方法。
                从jvm内存的角度解释就是 : .java文件经"javac.exe"命令编译后会生成.class的字节码文件,当代码中需要使用到某个类,该类的字节码文件就会加载进入方法区,而jvm识别并执行的就是字节码文件。因此,编译类型为父类类型,那jvm识别的当然是这个类的字节码文件,子类的特有成员,根本就不在这个字节码文件里面,jvm当然不认识。而对于子类重写的方法,父类字节码文件中包含有被重写方法的信息,jvm能够识别。而因为父类引用真正指向的是堆空间中的子类类型对象,所以此时会优先从堆空间中的子类对象里面找,使用子类重写后的方法,若子类没有重写,根据继承机制,则使用父类的该方法。

五、类型转换 :

        Δ前言 : 

                当需要使用到子类特有功能,比如要使用子类重写后的方法,或者要使用子类的特有成员,这时候就需要进行类型转换。类型转换分为向上类型转换向下类型转换两种。其中,向下转型是一个重点知识。        

        1.向上转型(自动): 

                ①含义 : 

                即子类类型转换成父类类型(父类引用指向子类对象)。向上转型是自动进行的,我们的多态就是一种向上转型。eg : Animal animal = new Cat();

                ②语法格式 : 

        父类类型  父类引用变量  =  new  子类类型();

                ③代码演示 : 

                这个说实话😅没啥好演示的。因为我们前面举过的所有多态的例子,都属于向上转型。  请继续看向下转型,那才是重点。

        2.向下转型(强制): 

                ①含义 : 

                即父类类型转换成子类类型。为什么叫强制类型转化呢? 因为向下转型不会自动发生,需要人为强转。并且,向下转型改变了编译类型,而编译类型决定了我们可以使用哪些成员,当编译类型由父类类型转换为子类类型后,我们当然可以使用子类的特有成员了。因此,我们说要使用子类的特有功能,靠的就是向下转型

                ②语法格式 : 

        子类类型  子类引用变量  =  (子类类型) 父类引用变量。

        或者 直接使用 " ((子类类型)父类引用变量) " 的方式来调用子类特有成员,而不去做接收。

                 什么意思呢?给大家举个栗子 : 
                eg : Animal animal = new Cat();      
                      Cat cat = new (Cat) animal;  后一条语句将Animal类型的引用变量animal转换成了子类Cat类类型的引用变量cat,相当于animal和cat两个引用指向了同一Cat对象,但堆空间中实际存在的Cat对象本身并没有变化

                ③代码演示 : 

                演示Ⅰ:

                我们就先来解决刚刚在演示多态中成员变量的使用时,Fruit类引用无法直接调用Apple类和Grape类成员变量的问题。
                Fruit类,Apple类,Grape类代码均不变,大家可以往上翻翻看,就在上面“多态中成员变量的使用”这一部分。当然,懒得去翻也没关系,重在演示向下类型转换。我们只需要在TestField类输出语句中的成员变量前使用强制类型转换,即可成功输出子类特有成员。
                TestField类代码如下 : 

package knowledge.polymorphism.about_field;public class TestField {public static void main(String[] args) {//多态Fruit fruit = new Apple();System.out.println("苹果的物种学名:" + ((Apple)fruit).species_name);System.out.println("苹果的平均甜度:" + ((Apple)fruit).sweetness);System.out.println("苹果的形态特性:" + ((Apple)fruit).shape);System.out.println("---------------------------------");//改变fruit引用变量的指向,使其指向Grape类对象。fruit = new Grape();System.out.println("葡萄的物种学名:" + ((Grape)fruit).species_name);System.out.println("葡萄的平均甜度:" + ((Grape)fruit).sweetness);System.out.println("葡萄的形态特性:" + ((Grape)fruit).shape);}
}

                运行结果 : 

                演示Ⅱ : 

                不知道大家还记不记得在“多态中的成员方法的使用”的演示中,我们在Cat类和Dog类中分别定义了cat_info()方法和dog_info()方法,用于打印出Cat对象和Dog对象的基本信息,其实up在当时已用了强制向下转型,大家不用再往上翻了😂,我给大家放个截图吧,如下 : 

                当时我们将Animal类型的引用变量animal分别向下转型为了Cat类引用和Dog类引用。以调用它们特有的方法,运行结果如下 : 

                演示Ⅲ : 

                不知道大家是否还记得😂,在开篇多态的引用中,我们举了旅行者请刻晴,钟离,甘雨吃饭的栗子, 当时up还分别在刻晴类,钟离类和甘雨类中定义了它们特有的成员方法,但是没有再测试类中调用!其实就是为了等现在演示呢😆。
                Keqing类,Zhongli类,Ganyu类各自的特有成员方法如下,我还是直接放截图吧 : 

                Treat类(请客吃饭的测试类)代码如下 : 

package knowledge.polymorphism.introduction;public class Treat {public static void main(String[] args) {//在多态关系下,调用Keqing类,Zhongli类,Ganyu类的特有成员方法//刻晴Liyue_people lp1 = new Keqing("刻晴", 18, "女");System.out.println("刻晴特有的行为是天街巡游:");Keqing kq = (Keqing) lp1;kq.sky_street_cruise();System.out.println("------------------------------------------------");//钟离lp1 = new Zhongli("摩拉克斯", 6000, "男");System.out.println("钟离特有的行为是天星:");Zhongli zl = (Zhongli) lp1;zl.sky_stars();System.out.println("------------------------------------------------");//甘雨lp1 = new Ganyu("王小美", 3000, "女");System.out.println("甘雨特有的行为是降众天华:");Ganyu gy = (Ganyu) lp1;gy.descend_to_heaven();}
}

                运行结果 : 

         

        3.注意事项 : 

                只有在继承关系的继承上才可以进行类型转换,否则会报出ClassCastException(类型转换异常)。如下图所示 : 

                在对引用变量进行向下转型之前,必须保证该引用变量指向的——堆空间中真正的对象的类型就是目标类型。(重要
                比如,Animal animal = new Cat(); 现在animal引用指向了一个Cat类型的对象,如果要对animal引用进行强制向下转型,就只能转换成Cat类型的引用;如果想转换成其他类型的引用,就需要先改变animal引用的指向,使其指向目标类型的对象。否则,同样会报出类型转换异常

                ③那么,我们在进行向下转型之前,怎么就能知道——当前引用指向的对象是不是我们想要的目标类型的对象呢?
                答案是 : 在进行强制类型转化之前,使用instanceof关键字来进行判断。

        4.instanceof关键字(重要):

                ①概述 : 

                instanceof关键字,可以判断指定对象是否为指定的类型,并返回一个boolean类型的值,常与if条件语句一起使用。instanceof关键字在多态的应用——多态参数和多态数组中也会频繁使用,当然,多态的应用我们在后面会讲到的。

                ②用法 : 

        对象名  instanceof  数据类型             

                说明 : 
                1° 前面的“对象名”即引用变量。
                2° 实际参与判断的是引用变量指向的——堆空间中真正的对象。

                ③代码演示 : 

                up以Person类为父类,让Teacher类和Student类分别继承Person类,最后在Test_instanceof类中进行测试。为了代码简洁,up将Teacher类和Student类写在了Person类的源文件中。
                我们要在测试类干什么?
                在测试类中,我们先建立Person——Teacher类之间的多态,然后利用instanceof关键字来判断Person引用指向的对象是否为Teacher类型,以及是否为Student类型,如果是Teacher类型,就利用强制向下转型去调用Teacher类中的特有方法;接着,改变Person类型引用的指向,使其指向Student类型的对象,利用instanceof关键字对当前引用指向的对象重新进行判断,以确定对象类型,若确定当前对象是Student类型,同理,利用强转去调用Student类特有的成员方法。
                Person类,Teacher类,Student类代码如下 : 

package knowledge.polymorphism.ceof_demo;public class Person {                //父类/*因为仅演示instanceof关键字,因此父类暂时不以JavaBean标准来敲,当然,等讲到多态的应用时,我们还会用到instanceof关键字,到时候代码肯定会向JavaBean标准靠拢。这里大伙儿就先将就看看⑧😋。*/
}class Teacher extends Person {        //子类//定义Teacher类的特有成员方法public void teach() {System.out.println("教书育人,重在德行,有德无德,大相径庭。");}
}
class Student extends Person {        //子类//定义Student类的特有成员方法public void what_time() {System.out.println("嘿,哥们儿,几点了?还有几分钟下课?");}
}

                Test_instanceof类代码如下 : 

package knowledge.polymorphism.ceof_demo;public class Test_instanceof {public static void main(String[] args) {//1.多态先整上。Person person = new Teacher();//判断当前对象的类型boolean boolean_1 = (person instanceof Teacher);System.out.println("当前对象是Teacher类型吗: " + boolean_1);boolean boolean_2 = (person instanceof Student);System.out.println("当前对象是Student类型吗: " + boolean_2);System.out.println("----------------------------------------------");//确定对象类型后,进行强制向下转换,调用该类特有的成员方法if (person instanceof Teacher) {System.out.println("当前对象指向的对象是Teacher类型,可以将引用强转为Teacher类型");((Teacher) person).teach();} else if (person instanceof Student) {System.out.println("当前对象指向的对象是Student类型,可以将引用强转为Student类型");((Student) person).what_time();}System.out.println("\n===========================================================\n");//2.改变person引用的指向,并重复上一轮的步骤。//判断当前对象的类型person = new Student();boolean boolean_3 = (person instanceof Teacher);System.out.println("当前对象是Teacher类型吗: " + boolean_3);boolean boolean_4 = (person instanceof Student);System.out.println("当前对象是Student类型吗: " + boolean_4);System.out.println("----------------------------------------------");//确定对象类型后,进行强制向下转换,调用该类特有的成员方法if (person instanceof Teacher) {System.out.println("当前对象指向的对象是Teacher类型,可以将引用强转为Teacher类型");((Teacher) person).teach();} else if (person instanceof Student) {System.out.println("当前对象指向的对象是Student类型,可以将引用强转为Student类型");((Student) person).what_time();}}
}

                注意测试类中的代码,我们是先以一个boolean类型做接收,以证明使用instanceof关键字返回的是boolean类型,后又搭配 if 条件语句来调用子类特有的方法。其实学了static关键字之后,我们就可以把判断部分的代码单独写在main方法外的一个静态方法中,不用这么繁冗写两遍了。
                运行结果

六、Java的动态绑定机制(重要): 

                限于博客字数已过20000,从动态绑定机制开始,up将把知识点的讲解另外放到一篇博文中,然后把博客的链接给大家放在这里,以增强大家的阅读体验感谢理解,博文链接如下

https://blog.csdn.net/TYRA9/article/details/128880552?spm=1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/128880552?spm=1001.2014.3001.5501

七、多态的应用 : 

                多态的应用常见有多态数组和多态参数博文链接如下

https://blog.csdn.net/TYRA9/article/details/128920758?spm=1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/128920758?spm=1001.2014.3001.5501

八、抽象类,抽象方法,abstract关键字详解 : 

                抽象类详解博文链接如下 : 

https://blog.csdn.net/TYRA9/article/details/129137305?spm=1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/129137305?spm=1001.2014.3001.5501

九、final关键字详解 : 

                final关键字详解博文链接如下 : 

https://blog.csdn.net/TYRA9/article/details/129097055?spm=1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/129097055?spm=1001.2014.3001.5501

十、static关键字(类变量和类方法)详解 : 

                static关键字详解博文链接如下 : 

https://blog.csdn.net/TYRA9/article/details/129039405?spm=1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/129039405?spm=1001.2014.3001.5501

十一、接口详解 : 

https://blog.csdn.net/TYRA9/article/details/129174384?spm=1001.2014.3001.5501icon-default.png?t=N176https://blog.csdn.net/TYRA9/article/details/129174384?spm=1001.2014.3001.5501

十二、Debug详解 : 

https://blog.csdn.net/TYRA9/article/details/128884528?spm=1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/128884528?spm=1001.2014.3001.5501

十三、多态篇总结 : 

        🆗,以上就是面向对象篇章——多态篇的全部内容了。讲真的,多态篇的内容真是巨多😂,要是up不放链接的话,可能这一篇的字数就过10万字了。回顾一下,我们从一个有趣的例子引入了多态的必要性;接着介绍了Java中非常非常重要的动态绑定机制。接着又详细介绍了多态的使用;然后是抽象类,final关键字,static关键字,和接口这四大块对多态的补充和应用。最后,up又给大家补充了一篇介绍Java Dubug的博文。其实Debug中我也是主要演示了一些最基本的逻辑语句和动绑机制,按道理讲Debug更应该放到继承篇。但是我觉得讲面向对象讲得好好的,突然插入这么个玩意儿,多少有些抽象,于是最后还是把Debug放到了多态篇。好滴,之后up会出一篇总结性质的博文,把Java面向对象三大特性——封装、继承,多态三大节合并为一章,并且做一些对面向对象基础的引入和内容的完善。我们不见不散😎!感谢阅读!

相关文章:

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

目录 前言 : 一、为什么需要多态 : 1.白璧微瑕 : 2.举栗(请甘雨,刻晴,钟离吃饭): 3.代码 : 4.问题 : 二、什么是多态 : 1.定义 : 2.多态的实现步骤(重要) : 三、多态的使用 : 1.多态中成员方法的使用(重要…...

git push origin master 情况

📢📢📢📣📣📣哈喽!大家好,我是「奇点」,江湖人称 singularity。刚工作几年,想和大家一同进步🤝🤝一位上进心十足的【Java ToB端大厂领…...

ElasticSearch查询优化routing

如果一个索引分片多达一百,再加上每个分片数据量大的情况下ES查询速度会慢,这种情况可以根据业务情况考虑使用_routing优化。 _routing 路由 当索引一个文档的时候,文档会被存储在一个主分片上。在存储时一般都会有多个主分片。Elasticsearch 如何知道一个文档应该放置在哪…...

【HashMap 1.7和1.8】

Java中的HashMap是一种常用的数据结构,用于存储键值对。在Java 1.7和1.8中,HashMap的实现有一些不同。 Java 1.7中的HashMap实现是基于“拉链法”的哈希表。每个哈希桶(bucket)是一个链表,存储了散列值相同的键值对。当键值对数量过多时&…...

【Zabbix实战之故障处理篇】Zabbix监控中文乱码问题解决方法

【Zabbix实战之故障处理篇】Zabbix监控中文乱码问题解决方法 一、问题展现1.查看Zabbix仪表盘2.问题分析二、检查Zabbix环境1.检查Zabbix监控主机2.检查Zabbix各组件状态三、在宿主机安装中文字体库1.安装中文字体2.查看字体文件四、安装中文字库1.查看Zabbix所有组件容器2.拷贝…...

学习(mianshi)必备-ClickHouse高性能查询/写入和常见注意事项(五)

目录 一、ClickHouse高性能查询原因-稀疏索引 二、ClickHouse高性能写入-LSM-Tree存储结构 什么是LSM-Tree 三、ClickHouse的常见注意事项和异常问题排查 一、ClickHouse高性能查询原因-稀疏索引 密集索引: 在密集索引中,数据库中的每个键值都有一个索引记录&…...

在Kotlin中探索 Activity Results API 极简的解决方案

Activity Results APIActivity Result API提供了用于注册结果、启动结果以及在系统分派结果后对其进行处理的组件。—Google官方文档https://developer.android.google.cn/training/basics/intents/result?hlzh-cn一句话解释:官方Jetpack组件用于代替startActivity…...

样式冲突太多,记一次前端CSS升级

目前平台前端使用的是原生CSSBEM命名,在多人协作的模式下,容易出现样式冲突。为了减少这一类的问题,提升研效,我调研了业界上主流的7种CSS解决方案,并将最终升级方案落地到了工程中。 样式冲突的原因 目前遇到的样式…...

如何解决报考PMP的那些问题?

关于PMP的报考条件,报考PMP都需要什么条件呢?【学历条件】:需要满足23周岁/高中毕业5年以上/大专以上学历,三个满足一个即可;【PDU条件】:报考PMP需要PDU证明(学习项目管理课程的学时证明&#…...

数据结构栈的经典OJ题【leetcode最小栈问题大剖析】【leetcode有效的括号问题大剖析】

目录 0.前言 1.最小栈 1.1 原题展示 1.2 思路分析 1.2.1 场景引入 1.2.2 思路 1.3 代码实现 1.3.1 最小栈的删除 1.3.2 最小栈的插入 1.3.3 获取栈顶元素 1.3.4 获取当前栈的最小值 2. 有效的括号 0.前言 本篇博客已经把两个关于栈的OJ题分块,可以根据目…...

数据结构与算法之打家劫舍(一)动态规划思想

动态规划里面一部题目打家劫舍是一类经典的算法题目之一,他有各种各样的变式,这一篇文章和大家分享一下打家劫舍最基础的一道题目,掌握这一道题目,为下一道题目打下基础。我们直接进入正题。一.题目大家如果刚接触这样的题目&…...

无人驾驶路径规划论文简要

A Review of Motion Planning Techniques for Automated Vehicles综述和分类0Motion Planning for Autonomous Driving with a Conformal Spatiotemporal Lattice从unstructured环境向structured环境的拓展,同时还从state lattice拓展到了spatiotemporal lattice从而…...

C++ sort()函数和priority_queue容器中比较函数的区别

普通的queue是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。priority_queue中元素被赋予优先级。在创建的时候根据优先级进行了按照从大到小或者从小到大进行了自动排列(大顶堆or小顶堆)。可以以O(log n) 的效率查找…...

STM32开发(14)----CubeMX配置ADC

CubeMX配置ADC前言一、什么是ADC?二、实验过程1.单通道ADC采集STM32CubeMX配置代码实现2.多通道ADC采样(非DMA)STM32CubeMX配置代码实现3.多通道ADC采样(DMA)STM32CubeMX配置代码实现总结前言 本章介绍使用STM32CubeMX对ADC进行配置的方法&a…...

Simple RNN、LSTM、GRU序列模型原理

一。循环神经网络RNN 用于处理序列数据的神经网络就叫循环神经网络。序列数据说直白点就是随时间变化的数据,循环神经网络它能够根据这种数据推出下文结果。RNN是通过嵌含前一时刻的状态信息实行训练的。 RNN神经网络有3个变种,分别为Simple RNN、LSTM、…...

【原创】java+swing+mysql生肖星座查询系统设计与实现

今天我们来开发一个比较有趣的系统,根据生日查询生肖星座,输入生日,系统根据这个日期自动计算出生肖和星座信息反馈到界面。我们还是使用javaswingmysql去实现这样的一个系统。 功能分析: 生肖星座查询系统,顾名思义…...

CentOS 环境 OpneSIPS 3.1 版本安装及使用

文章目录1. OpenSIPS 源码下载2. 工具准备3. 编译安装4. opensips-cli 工具安装5. 启动 OpenSIPS 实例1. OpenSIPS 源码下载 使用以下命令即可下载 OpenSIPS 的源码,笔者下载的是比较稳定的 3.1 版本,读者有兴趣也可前往 官方传送门 sudo git clone htt…...

SQL95 从 Products 表中检索所有的产品名称以及对应的销售总数

描述 Products 表中检索所有的产品名称:prod_name、产品id:prod_idprod_idprod_namea0001egga0002socketsa0013coffeea0003colaOrderItems代表订单商品表,订单产品:prod_id、售出数量:quantityprod_idquantitya0001105…...

平时技术积累很少,面试时又会问很多这个难题怎么破?别慌,没事看看这份Java面试指南,解决你的小烦恼!

前言技术面试是每个程序员都需要去经历的事情,随着行业的发展,新技术的不断迭代,技术面试的难度也越来越高,但是对于大多数程序员来说,工作的主要内容只是去实现各种业务逻辑,涉及的技术难度并不高&#xf…...

SQL Server 数据库的备份

为何要备份数据库? 备份 SQL Server 数据库、在备份上运行测试还原过程以及在另一个安全位置存储备份副本可防止可能的灾难性数据丢失。 备份是保护数据的唯一方法 。 使用有效的数据库备份,可从多种故障中恢复数据,例如: 介质…...

NCNN Conv量化详解1

1. NCNN的Conv量化计算流程 正常的fp32计算中,一个Conv的计算流程如下: 在NCNN Conv进行Int8计算时,计算流程如下: NCNN首先将输入(bottom_blob)和权重(weight_blob)量化成INT8,在INT8下计算卷积,然后反量化到fp32,再和未量化的bias相加,得到输出(top_blob) 输入和…...

Redis大key多key拆分方案

业务场景中经常会有各种大key多key的情况, 比如:1:单个简单的key存储的value很大2:hash, set,zset,list 中存储过多的元素(以万为单位)3:一个集群存储了上亿的…...

python的类如何使用?兔c同学一篇关于python类的博文概述

本章内容如目录 所示: 文章目录1. 创建和使用类1.1 创建第一个python 类1.2 版本差异1.3 根据类创建实例1. 访问属性2. 调用方法3. 创建多个实例2. 使用类和实例2.1 给属性指定默认值2.2 修改属性的值3. 继承3.1 子类的 __init __()3.2 给子类定义属性和方法3.3 重写…...

Day60 动态规划总结

647. 回文子串 回文的做法注定我们得从里面入手,逐渐扩散到边界 初始化:准备一个ans,找到一个回文子串加一个 dp [[0] * n for _ in range(n)]ans 0 遍历公式: 当s[i]s[j]的时候,只要里面还是回文串,就能…...

UVM仿真环境搭建

环境 本实验使用环境为: Win10平台下的Modelsim SE-64 2019.2 代码 dut代码: module dut(clk,rst_n, rxd,rx_dv,txd,tx_en); input clk; input rst_n; input[7:0] rxd; input rx_dv; output [7:0] txd; output tx_en;reg[7:0] txd; reg tx_en;always…...

Azure AI基础到实战(C#2022)-认知服务(1)

目录 Azure 认知服务概述计算机视觉概述数据隐私和安全性计算机视觉快速入门光学字符识别 (OCR)OCR APIOCR 常用功能Azure 门户准备两种部署方式OCR项目实战之车牌识别Azure 认知服务概述 Azure 认知服务是基于云的人工智能 (AI) 服务,可帮助开发人员在不具备直接的 AI 或数据…...

光栅化Triangles(笔记)

field of view (可见区域) 该角度越大,需要透视投影的角度越大,成像显示的内容越多 有Y值,则可得出成像范围 屏幕: 典型的光栅处理设备所有像素都被表示为x,y坐标轴形式 3D方块成像步骤: 先将其所在平面化为 与屏幕等长等宽的形式: 如何将一个三角形拆成像素?采样…...

【Oarcle】如何显示日本年号的日期格式 ?

语句大于一切,还需要语言吗? 1. SELECT TO_CHAR(SYSDATE,EEYY/MM/DD,NLS_CALENDAR JAPANESE IMPERIAL) from dual;结果是: 令和05/02/25 Oracle SQL文中,年月日的显示,一定要使用双引号括起来,如 select…...

57_Pandas中的json_normalize将字典列表转换为DataFrame

57_Pandas中的json_normalize将字典列表转换为DataFrame 可以使用 pandas.json_normalize() 将具有公共键的字典列表转换为 pandas.DataFrame。 由于它是一种常用的JSON格式,可以通过Web API获取,所以能够将其转换为pandas.DataFrame是非常方便的。 在…...

OpenAPI SDK组件之javassist字节码

javassist介绍 Javassist是一个开源的分析、编辑和创建Java字节码的类库,主要优点是简单,不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。 apisdk应用javassist 在apisdk中主要依靠javassist增强开发者声明的开放…...