在Java中,父类的变量可以引用父类的实例或子类的实例。请阅读代码:
public class demo{
public static void main(string[]args){
animal obj=new animal()
哭声();
obj=new Cat();
哭声();
obj=new Dog();
哭声();
}
}
类动物{
动物调用
公共虚空叫声(){
System.out.println)我不知道怎么做调用它);
类cat extensions animal{
cat calls
public void cry(){
System.out.println(喵喵~);
}
}
类狗扩展动物{
狗吠叫
公共虚空哭泣(){
System.out.println(“bark~”);
运行结果:
I don know know to call
meow~
bark~
上面的代码定义了三个类,即动物、猫和狗。猫和狗的类都继承自动物类。obj变量的类型是animal,它可以指向animal类和catanddog类的实例,这是正确的。换句话说,父类的变量可以引用父类的实例或子类的实例。请注意,反过来是错误的,因为所有的猫都是动物,但不是所有的动物都是猫。
可以看出,OBJ可以是人,猫和狗。它有不同的表现形式,这就是多态性。多态性是指事物具有不同的形式。
另一个例子是“人”,有许多不同的表达式或实现。助教可以是司机、老师、医生等,当你恨自己的时候,你会说“来生做个新人”。然后你可以成为一名司机,教师或医生在你的下一辈子。我们说“人”具有多态性。
多态性的存在有三个必要条件:父变量对子类对象的继承、重写和引用。
以多态方式调用方法时:
首先检查父类中是否存在该方法。否则,将发生编译错误;如果是,请检查子类是否覆盖了该方法。
如果子类覆盖了方法,则调用子类的方法;否则,调用父类的方法。
从上面的例子中,我们可以看到多态性的一个优点是当有很多子类时,不需要定义多个变量。我们只能定义一个父类型的变量来引用不同子类的实例。请看下面的示例:
public class demo{
public static void main(string[]args){
使用多态性,所有者可以喂养许多动物
Master Ma=new Master()
硕士学位(新动物(),新食物());
硕士学位(new Cat(),new Fish());
硕士学位(新狗(),新骨头());
}
}
动物类及其子类
class animal{
public void eat(food f){
System.out.println(“I'm a small animal,eating”+f.getfood());
类cat extends animal{
public void eat(food f){
System.out.println(“我是一只小猫,在吃”+f.getfood());
}
}
类狗扩展动物{
public void eat(Food f){
System.out.println(“I am a dog,eating”+f.getfood());
}
}
食物及其子类class Food{
public String getFood(){
return "事物";
}
}
class Fish extends Food{
public String getFood(){
return "鱼";
}
}
class Bone extends Food{
public String getFood(){
return "骨头";
}
}
// Master类
class Master{
public void feed(Animal an, Food f){
an.eat(f);
}
}
运行结果:
我是一个小动物,正在吃事物
我是一只小猫咪,正在吃鱼
我是一只狗狗,正在吃骨头
Master 类的 feed 方法有两个参数,分别是 Animal 类型和 Food 类型,因为是父类,所以可以将子类的实例传递给它,这样 Master 类就不需要多个方法来给不同的动物喂食。
动态绑定
为了理解多态的本质,下面讲一下Java调用方法的详细流程。
1) 编译器查看对象的声明类型和方法名。
假设调用 obj.func(param),obj 为 Cat 类的对象。需要注意的是,有可能存在多个名字为func但参数签名不一样的方法。例如,可能存在方法 func(int) 和 func(String)。编译器将会一一列举所有 Cat 类中名为func的方法和其父类 Animal 中访问属性为 public 且名为func的方法。
这样,编译器就获得了所有可能被调用的候选方法列表。
2) 接下来,编泽器将检查调用方法时提供的参数签名。
如果在所有名为func的方法中存在一个与提供的参数签名完全匹配的方法,那么就选择这个方法。这个过程被称为重载解析(overloading resolution)。例如,如果调用 func(“hello”),编译器会选择 func(String),而不是 func(int)。由于自动类型转换的存在,例如 int 可以转换为 double,如果没有找到与调用方法参数签名相同的方法,就进行类型转换后再继续查找,如果最终没有匹配的类型或者有多个方法与之匹配,那么编译错误。
这样,编译器就获得了需要调用的方法名字和参数签名。
3) 如果方法的修饰符是private、static、final(static和final将在后续讲解),或者是构造方法,那么编译器将可以准确地知道应该调用哪个方法,我们将这种调用方式 称为静态绑定(static binding)。
与此对应的是,调用的方法依赖于对象的实际类型, 并在运行时实现动态绑。例如调用 func(“hello”),编泽器将采用动态绑定的方式生成一条调用 func(String) 的指令。
4)当程序运行,并且釆用动态绑定调用方法时,JVM一定会调用与 obj 所引用对象的实际类型最合适的那个类的方法。我们已经假设 obj 的实际类型是 Cat,它是 Animal 的子类,如果 Cat 中定义了 func(String),就调用它,否则将在 Animal 类及其父类中寻找。
每次调用方法都要进行搜索,时间开销相当大,因此,JVM预先为每个类创建了一个方法表(method lable),其中列出了所有方法的名称、参数签名和所属的类。这样一来,在真正调用方法的时候,虚拟机仅查找这个表就行了。在上面的例子中,JVM 搜索 Cat 类的方法表,以便寻找与调用 func(“hello”) 相匹配的方法。这个方法既有可能是 Cat.func(String),也有可能是 Animal.func(String)。注意,如果调用super.func(“hello”),编译器将对父类的方法表迸行搜索。
假设 Animal 类包含cry()、getName()、getAge() 三个方法,那么它的方法表如下:
cry() -> Animal.cry()
getName() -> Animal.getName()
getAge() -> Animal.getAge()
实际上,Animal 也有默认的父类 Object(后续会讲解),会继承 Object 的方法,所以上面列举的方法并不完整。
假设 Cat 类覆盖了 Animal 类中的 cry() 方法,并且新增了一个方法 climbTree(),那么它的参数列表为:
cry() -> Cat.cry()
getName() -> Animal.getName()
getAge() -> Animal.getAge()
climbTree() -> Cat.climbTree()
在运行的时候,调用 obj.cry() 方法的过程如下:
JVM 首先访问 obj 的实际类型的方法表,可能是 Animal 类的方法表,也可能是 Cat 类及其子类的方法表。
JVM 在方法表中搜索与 cry() 匹配的方法,找到后,就知道它属于哪个类了。
JVM 调用该方法。
------------------------------------------------------
相关文章
原创互联网未来世界企业政府通讯APP办公节约成本类似马云思维-哇谷IM
公有云和私有云之间有什么区别?类似融云、环信云、网易云、哇谷云?
IM云系统即时通讯公有云、私有云、企业云、海外云-哇谷IM团队
im即时通讯社交软件APP红包技术分析(五):微信红包、聊呗红包、诚信红包、高并发技术
im即时通讯-微信红包、支付宝红包、聊呗红包、诚信红包、谈功能逻辑、容灾、运维、架构等。Q红包
更多文章
.
企业即时通讯服务 | 商用红包功能构架 | 哇谷IM首页 | JM沟通IM下载 | IM功能与价格 | 即时通讯动态 | 热门动态 | 关于哇谷 |联系我们