多形の意味
ポリモーフィズムは1つのタイプ、複数の形式です。
Javaは、主に2つのカテゴリに分類されます。
メソッドのポリモーフィズムは
、メソッドのオーバーロードとメソッドのオーバーライドに反映されます。
オブジェクトのポリモーフィズムは
、親オブジェクトと子オブジェクトの間の変換に反映されます。(父と息子の間には相続関係があります)
親クラス
上向きの変化
スーパータイプ参照は、サブタイプオブジェクトを指します。
子类型转换成父类型
向上转型
自动类型转换
Persoon a=new Student();
a.fun()
スーパータイプの参照aは、スタックメモリ内のサブタイプオブジェクトを指します。
!!!
ただし、Javaコンパイラでは、親タイプのみがaに対して認識されます。
コンパイラは、aのデータ型がPerson型であることを検出します。
!!!ただし、ポインティングフェーズでは、最下層に実際に存在するオブジェクトはサブクラスであるため、実行中のサブクラスのオブジェクトのままです。
アップキャスト、コンパイラは親クラスに依存し、エグゼキュータはサブクラスに依存します
java程序永远分为编译阶段和运行阶段;
先分析编译阶段,再分析运行阶段,编译无法通过,就无法运行
编译阶段的编译器检查a这个引用的数据类型为Person,
当person.class字节码当中存在的fun()方法,a.fun()编译才能通过。
只有静态绑定成功之后才有后续的运行。
但是在程序的运行阶段,JVM堆内存中真实的对象是Student对象,
那么a.fun()一定会调用Student的fun方法,
此时,发生了程序的动态绑定,运行阶段绑定
class Person
{
public void fun1()
{
System.out.println("****fun1,来自父类");
}
public void fun2(int a)
{
System.out.println("****fun2,来自父类"+a);
}
public void fun3()
{
System.out.println("****fun3,来自父类");
}
}
class Student extends Person
{
public void fun1()
{
System.out.println("****fun1,来自子类");
}
public void fun2(int c,int b)
{
System.out.println("****fun2,来自子类"+(c+b));
}
/*public void fun2(int a)
{
System.out.println("****fun2,来自子类"+a);
}*/
public void fun4()
{
System.out.println("****fun4,来自子类");
}
}
public class Test
{
public static void main(String[] args)
{
//向上转型
//编译器取决于父类,执行器取决于子类
Person p=new Student();
p.fun1();//方法覆写时,执行子类的,因为底层真实存在的是子类对象
p.fun2(2);//方法重载时,将自动执行父类的
((Student)p).fun2(2,3);//向下转型//方法重载时,若要执行子类方法,需要强制转化为子类方法
p.fun3();//向上转型,父类所有方法都可以直接调用
((Student)p).fun4();//向上转型,只有子类存在的方法,需要强制转化
}
}
メソッドが共有されて同じである場合、それはサブクラスを指します;(上方変換のため、基礎となるオブジェクトはサブクラスオブジェクトです)
。メソッドがサブクラスに存在しない場合、それは親クラスを指します。
原因是:父类型引用执行子类型,导致程序在编译阶段绑定和运行阶段绑定
出现了两种不同的状态
ダウンキャスト
((Student)p).fun2(2,3);を直接追加でき
ます。StudentS=(Student)p; S.fun2(2,3)に変更することもできます。
呼び出されたメソッドがサブタイプに固有であり、親タイプに存在しない場合は、ダウンキャストする必要があります。
親クラスは子クラスに変わり、2つの間に継承関係があります。
親クラスは存在せず、子クラスが存在します(サブカテゴリを実行するには強制する必要があります)
サブカテゴリ
両方のクラスが親クラスから継承する場合
エラー1など
Student 和Teacher 都继承了Person
その後、
Person p=new Student();
Teacher T=(Teacher)p;
2つのサブクラスの相互変換は、コンパイラー段階で渡すことができますが、実行することはできません。
理由:コンパイラはpのデータ型がPersonで
あることを検出するため、PersonとTeacherの間に継承があり、Personはスーパータイプであり、Teacherはダウンキャストであるサブタイプであり、構文は修飾されています。
ただし、実行中はプログラムが異常になります。JVMヒープメモリに実際に存在するのはStudentであり、StudentとTeacherの間に継承がなく、相互に変換できないため、ClassCastException例外が発生します。これは、常に下方変換中に発生します。
下向きの変換ではコンパイルは渡されますが、操作が必ずしも間違っているわけではなく、セキュリティ上のリスクがあります。
上向きの変換は、コンパイルが完了している限り、操作に問題はありません。
上記はClassCastExceptionの例外です。
ダウンキャスト中の例外を回避する方法
上記の重要なポイント:解決するのではなく、回避するだけです。
継承関係のない2つのサブクラスは相互に変換できないためです。
演算子:instanceof
構文形式:(
データ型名のインスタンスを引用)
**操作の実行結果:**ブール型、trueまたはfalse
操作結果について:trueおよびfalse
(p instanceof Person)
true表示:
p这引用指向的对象是Person类型
fals表示:
p这个引用,指向的对象不是一个Person类型
例
//如何避免
if(p instanceof Teacher)//如果p是一个teacher类型对象
{
Teacher T=(Teacher) p;
p.fun1();
}
else if(p instanceof Student)//如果是一个Student类型对象
{
Student S=(Student)p;
S.fun4();
}
//为什么这么做呢???判断其是引用P是哪个类型对象后,
//再将其强制转换,赋予一个新的引用。这个现在可能看不出来,
//但是以后子类多了,进行instanceof判断,避免Exception异常,
//这是一个编程的好习惯,并不是必须写。
あなたはなぜこれをやっているのですか???Pを参照するオブジェクトのタイプを判断した後、
強制的に変換され、新しい参照が割り当てられます。これは現在は表示されない可能性
がありますが、例外を回避するためにinstanceof判定を実行するために、将来的にはさらに多くのサブクラスがあります。
これは優れたプログラミング習慣であり、記述する必要はありません。
判断のインスタンスの後、新しい参照があります。この参照は、サブクラスメソッドを呼び出すときに、1つずつキャストする必要はありません。
アプレット
class Master
{
public void feed(Cat c)
{
c.eat();
}
}
class Cat
{
public void eat()
{
System.out.println("小猫在吃鱼");
}
}
public class Test
{
public static void main(String[] args)
{
Master m=new Master();
Cat c=new Cat();
m.feed(c);
}
}
しかし、子犬を追加して食べる必要がある場合、小さなウサギは野菜を食べ、小さなトラは肉を食べます
基础的各项小动物类都要写
而且在Master里都要加上
public void feed(Dog d)
{
d.eat();
}
public void feed(Tuz t)
{
t.eat();
}
public void feed(Hu h)
{
h.eat();
}
これは面倒
です。実際、これらはすべてメソッドの現れです。
メイン関数が呼び出されたときにマスターが大きな変更を加える必要がないようにこのメソッドを作成する方法。
つまり、動物に継承させる方法です。このスキルパックを食べるペットの能力
、次にマスターのフィード(x)、xのデータタイプはペットな
ので、フィード(x)、xは参照であり、ペットのデータタイプの参照であり、次のことができます。ペットを指す、またはペットのサブカテゴリ、猫、犬を指すことができます
class Master
{
public void feed(Pet c)
{
c.eat();
}
}
class Pet
{
public void eat()
{
}
}
class Cat extends Pet//子类猫狗继承了 宠物的吃 行为
{
public void eat()
{
System.out.println("小猫在吃鱼");
}
}
class Dog extends Pet
{
public void eat()
{
System.out.println("小狗在吃s");
}
}
public class Test
{
public static void main(String[] args)
{
Master m=new Master();
Cat c=new Cat();//在堆内存创建了一个猫对象
m.feed(c);//feed(c0) c0又是一个宠物引用,而宠物和猫阿狗有继承关系,所以将执行子类的函数
Dog d=new Dog();
m.feed(d);
}
}
これを行うには、小動物クラスを追加してペットを継承するだけです。
マスターはペットのデータ型を参照します(親クラスには、使用する必要があるものと同じメソッドが必要です)。
メインで動物を生成します。メソッドObject、および前に学習したPetデータ型の参照によると、Petのサブオブジェクトに直面すると、最下層は実際にはPetのサブクラスCat、Dogなどになります。
上記も抽象化指向のプログラミングであり、結合が低く、強力な拡張。、Petクラス
での多形性の役割を参照し、プログラムの結合を減らし、拡張性を向上させます。
提唱者:抽象指向のプログラミングの使用Petクラスeatは抽象的で空であり、実際、そのサブクラスが呼び出されると、 2つの継承関係により、Petデータ型はサブクラスオブジェクトを参照し、サブクラスメソッドを実行します。