이 글은 주로 Java 다형성의 정의와 사용법을 소개하고, 다형성의 개념과 기능을 더 자세히 분석하며, Java 정의 및 객체지향 다형성 구현과 관련된 조작 기술도 함께 설명합니다.
예제에서는 Java 다형성의 정의와 사용법을 설명합니다. 참조를 위해 모든 사람과 공유하세요.
다형성은 다음을 통해 반영됩니다.
1 인터페이스와 인터페이스를 구현하고 인터페이스에서 동일한 메서드를 다루는 여러 다른 클래스
2 상위 클래스 및 상속됨 상위 클래스 상위 클래스에서 동일한 메소드의 여러 다른 하위 클래스 구현을 다룹니다.
1. 기본 개념
다형성: 객체에 메시지를 보내고 객체가 스스로 응답할 동작을 결정하도록 합니다. 에게. 동적 메소드 호출은 서브클래스 객체 참조를 슈퍼클래스 객체 참조 변수에 할당하여 구현됩니다.
Java의 이 메커니즘은 다음 원칙을 따릅니다. 슈퍼클래스 객체 참조 변수가 서브클래스 객체를 참조할 때 참조 변수의 유형이 아닌 참조된 객체의 유형에 따라 호출되는 멤버 메소드가 결정되지만 호출되는 메소드는 메소드여야 합니다. 즉, 하위 클래스에 의해 재정의된 메서드입니다.
a가 클래스 A의 참조인 경우 a는 클래스 A의 인스턴스 또는 클래스 A의 하위 클래스를 가리킬 수 있습니다.
a가 인터페이스 A에 대한 참조인 경우 a는 인터페이스 A를 구현하는 클래스의 인스턴스를 가리켜야 합니다.
2. Java 다형성 구현 메커니즘
SUN의 현재 JVM 구현 메커니즘은 핸들(handle)에 대한 포인터입니다. 이 핸들은 포인터 쌍입니다. 실제로 이 테이블에는 두 개의 포인터가 있습니다. 하나는 개체가 포함된 메서드 테이블을 가리키고 다른 하나는 개체의 유형을 나타내는 클래스 개체를 가리킵니다. Java 힙에서 할당된 블록은 메모리 공간을 확보합니다.
3. 요약
1. 동적 메서드 호출은 하위 클래스 개체 참조를 슈퍼클래스 개체 참조 변수에 할당하여 수행됩니다.
DerivedC c2=new DerivedC(); BaseClass a1= c2; //BaseClass 基类,DerivedC是继承自BaseClass的子类 a1.play(); //play()在BaseClass,DerivedC中均有定义,即子类覆写了该方法
분석:
1. 하위 클래스 유형의 객체 인스턴스를 슈퍼클래스 참조로 덮어쓸 수 있는 이유는 무엇입니까?
상향 변환을 자동으로 실현합니다. 이 명령문을 통해 컴파일러는 자동으로 하위 클래스 인스턴스를 위로 이동하고 범용 유형인 BaseClass가 됩니다. 2. a.play()
는 하위 클래스 또는 상위 클래스에서 정의한 메서드를 실행합니까?
. 런타임 동안 해당 메소드는 객체 참조 a의 실제 유형을 기반으로 획득됩니다. 이것이 바로 다형성이 존재하는 이유입니다. 기본 클래스 객체 참조는 다른 하위 클래스 객체 참조에 할당되며 이 메서드를 실행할 때 다른 동작을 표시합니다.
a1=c2인 경우에도 a1과 c2라는 두 개의 핸들이 있지만 a1과 c2는 동일한 데이터 메모리 블록과 다른 기능 테이블을 갖습니다. a.play()
将执行子类还是父类定义的方法?
子类的 。在运行时期,将根据a这个对象引用实际的类型来获取对应的方法 。所以才有多态性 。一个基类的对象引用,被赋予不同的子类对象引用,执行该方法时,将表现出不同的行为 。
在a1=c2的时候,仍然是存在两个句柄,a1和c2,但是a1和c2拥有同一块数据内存块和不同的函数表 。
2、不能把父类对象引用赋给子类对象引用变量
BaseClass a2=new BaseClass(); DerivedC c1=a2;//出错
在java里面,向上转型是自动进行的,但是向下转型却不是,需要我们自己定义强制进行 。
c1=(DerivedC)a2;
2. 상위 클래스 객체 참조를 하위 클래스 객체 참조 변수에 할당할 수 없습니다
class Human{ void run(){输出 人在跑} } class Man extends Human{ void run(){输出 男人在跑} }
Java에서는 상향 변환이 자동으로 수행되지만 강제로 수행하려면 직접 정의해야 합니다.
c1=(DerivedC)a2;
강제 변환, 즉 하향 변환을 수행합니다.
3 매우 간단하면서도 복잡한 규칙을 기억하세요. 유형 참조는 참조 유형만 참조할 수 있습니다. 자체 메서드와 변수.
부모 클래스 참조가 하위 클래스 객체를 가리키면 최종적으로 하위 클래스의 메서드가 실행되기 때문에 이 규칙이 잘못되었다고 말할 수 있습니다.
사실 이는 모순이 아닙니다. 왜냐하면 Late Binding을 사용하고 동적 연산 시 유형에 따라 하위 클래스 메서드가 호출되기 때문입니다. 그리고 하위 클래스의 이 메서드가 상위 클래스에 정의되어 있지 않으면 오류가 발생합니다.
예를 들어 DerivedC 클래스는 BaseClass에 정의된 함수를 상속하는 것 외에도 여러 함수(예: myFun())를 추가합니다.
분석:부모 클래스 참조를 사용하여 하위 클래스를 가리키면, 실제로 jvm은 컴파일러에서 생성된 유형 정보를 사용하여 변환을 조정합니다.
이렇게 이해하시면 되는데, 이는 상위 클래스에 포함되지 않은 함수를 가상 함수 테이블에서 보이지 않게 설정하는 것과 같습니다. 가상 함수 테이블의 일부 함수 주소가 서브클래스에서 다시 작성되어 객체 가상 함수 테이블의 가상 함수 항목의 주소가 서브클래스에서 완료된 메소드 본문의 주소로 설정되었을 가능성이 있습니다.
🎜🎜4. Java와 C++의 다형성 비교🎜🎜🎜jvm의 다형성 지원 솔루션은 C++의 많은 컴파일러가 유형 정보와 가상 함수 정보를 하나의 가상 함수 테이블에 저장한다는 점만 제외하면 C++의 다형성 지원 솔루션과 거의 동일합니다. 구별하는 기술. 🎜🎜Java는 유형 정보와 기능 정보를 분리합니다. Java에서 상속한 후 하위 클래스는 자체 가상 함수 테이블을 재설정합니다. 이 가상 함수 테이블의 항목은 두 부분으로 구성됩니다. 상위 클래스에서 상속된 가상 함수와 하위 클래스의 자체 가상 함수입니다. 🎜🎜가상 함수 호출은 가상 함수 테이블을 통해 간접적으로 호출되므로 다형성을 이룰 수 있습니다. final로 선언된 함수를 제외한 Java의 모든 함수는 후기 바인딩을 사용합니다. 🎜四. 1个行为,不同的对象,他们具体体现出来的方式不一样,
比如: 方法重载 overloading 以及 方法重写(覆盖)override
class Human{ void run(){输出 人在跑} } class Man extends Human{ void run(){输出 男人在跑} }
这个时候,同是跑,不同的对象,不一样(这个是方法覆盖的例子)
class Test{ void out(String str){输出 str} void out(int i){输出 i} }
这个例子是方法重载,方法名相同,参数表不同
ok,明白了这些还不够,还用人在跑举例
Human ahuman=new Man();
这样我等于实例化了一个Man的对象,并声明了一个Human的引用,让它去指向Man这个对象
意思是说,把 Man这个对象当 Human看了.
比如去动物园,你看见了一个动物,不知道它是什么, “这是什么动物? ” “这是大熊猫! “
这2句话,就是最好的证明,因为不知道它是大熊猫,但知道它的父类是动物,所以,这个大熊猫对象,你把它当成其父类 动物看,这样子合情合理.这种方式下要注意 new Man();
的确实例化了Man对象,所以ahuman.run()
这个方法 输出的 是 “男人在跑 “如果在子类 Man下你 写了一些它独有的方法 比如 eat(),而Human没有这个方法,在调用eat方法时,一定要注意 强制类型转换 ((Man)ahuman).eat()
,这样才可以…
对接口来说,情况是类似的…
package domatic; //定义超类superA class superA { int i = 100; void fun(int j) { j = i; System.out.println("This is superA"); } } // 定义superA的子类subB class subB extends superA { int m = 1; void fun(int aa) { System.out.println("This is subB"); } } // 定义superA的子类subC class subC extends superA { int n = 1; void fun(int cc) { System.out.println("This is subC"); } } class Test { public static void main(String[] args) { superA a = new superA(); subB b = new subB(); subC c = new subC(); a = b; a.fun(100); a = c; a.fun(200); } }
/* * 上述代码中subB和subC是超类superA的子类,我们在类Test中声明了3个引用变量a, b, * c,通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用 。也许有人会问: * “为什么(1)和(2)不输出:This is superA” 。 * java的这种机制遵循一个原则:当超类对象引用变量引用子类对象时, * 被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法, * 但是这个被调用的方法必须是在超类中定义过的, * 也就是说被子类覆盖的方法 。 * 所以,不要被上例中(1)和(2)所迷惑,虽然写成a.fun(),但是由于(1)中的a被b赋值, * 指向了子类subB的一个实例,因而(1)所调用的fun()实际上是子类subB的成员方法fun(), * 它覆盖了超类superA的成员方法fun();同样(2)调用的是子类subC的成员方法fun() 。 * 另外,如果子类继承的超类是一个抽象类,虽然抽象类不能通过new操作符实例化, * 但是可以创建抽象类的对象引用指向子类对象,以实现运行时多态性 。具体的实现方法同上例 。 * 不过,抽象类的子类必须覆盖实现超类中的所有的抽象方法, * 否则子类必须被abstract修饰符修饰,当然也就不能被实例化了 */
以上大多数是以子类覆盖父类的方法实现多态.下面是另一种实现多态的方法———–重写父类方法
1.JAVA里没有多继承,一个类之能有一个父类 。而继承的表现就是多态 。一个父类可以有多个子类,而在子类里可以重写父类的方法(例如方法print()),这样每个子类里重写的代码不一样,自然表现形式就不一样 。这样用父类的变量去引用不同的子类,在调用这个相同的方法print()的时候得到的结果和表现形式就不一样了,这就是多态,相同的消息(也就是调用相同的方法)会有不同的结果 。举例说明:
//父类 public class Father{ //父类有一个打孩子方法 public void hitChild(){ } } //子类1 public class Son1 extends Father{ //重写父类打孩子方法 public void hitChild(){ System.out.println("为什么打我?我做错什么了!"); } } //子类2 public class Son2 extends Father{ //重写父类打孩子方法 public void hitChild(){ System.out.println("我知道错了,别打了!"); } } //子类3 public class Son3 extends Father{ //重写父类打孩子方法 public void hitChild(){ System.out.println("我跑,你打不着!"); } } //测试类 public class Test{ public static void main(String args[]){ Father father; father = new Son1(); father.hitChild(); father = new Son2(); father.hitChild(); father = new Son3(); father.hitChild(); } }
都调用了相同的方法,出现了不同的结果!这就是多态的表现!
위 내용은 Java의 다형성 정의 및 사용법 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!