찾다
Javajava지도 시간Java의 다형성 심층 분석(코드 예제)

이 기사는 Java의 다형성에 대한 심층 분석(코드 예제)을 제공합니다. 이는 특정 참조 가치가 있으므로 도움이 될 수 있습니다.

객체 지향 프로그래밍에는 캡슐화, 상속, 다형성이라는 세 가지 주요 특성이 있습니다.

캡슐화는 클래스의 내부 구현 메커니즘을 숨기며 클래스 사용에 영향을 주지 않고 클래스의 내부 구조를 변경할 수 있으며 데이터도 보호합니다. 내부 세부 정보만 외부 세계에 숨겨지고 액세스 방법만 외부 세계에 노출됩니다.

상속은 상위 클래스 코드를 재사용하는 것입니다. 두 클래스 사이에 IS-A 관계가 있는 경우 상속을 사용할 수 있습니다. , 그리고 상속은 또한 다형성을 실현할 수 있는 길을 열어줍니다. 그렇다면 다형성이란 무엇입니까? 다형성의 구현 메커니즘은 무엇입니까? 하나씩 공개하는 모습을 지켜봐주세요:

소위 다형성이란 프로그램에서 정의한 참조 변수가 가리키는 특정 유형과 참조 변수를 통해 발행되는 메소드 호출이 프로그래밍 중에 결정되는 것이 아니라 프로그래밍 중에 결정되는 것을 의미합니다. 그래야만 참조 변수가 어떤 클래스 인스턴스 객체를 가리키는지 결정할 수 있습니다. 이는 실행 중에 어떤 클래스에서 구현되는지 결정해야 합니다. 프로그램의. 특정 클래스는 프로그램이 실행 중일 때만 결정되기 때문에 소스 프로그램 코드를 수정하지 않고도 참조 변수를 다양한 클래스 구현에 바인딩할 수 있으므로 참조에서 호출하는 특정 메서드가 그에 따라 변경됩니다. 프로그램 코드는 실행 중 프로그램에 바인딩된 특정 코드를 변경하여 프로그램이 여러 실행 상태를 선택할 수 있도록 합니다. 이것은 다형성입니다

예를 들어, 당신은 와인의 신이고 와인을 특별히 좋아합니다. 어느 날 집에 갔는데 테이블 위에 화이트 와인이 가득 담긴 잔이 몇 개 있었는데, 겉으로는 어떤 와인인지 알 수가 없었습니다. 마시고 나서야 어떤 와인인지 짐작할 수 있었습니다. 마시면 건안춘, 또 마시면 우량계, 또 마시면 구의주…       와인 c = ““지우귀지우

” …

여기서 표현되는 것은 다형성입니다. Jiannanchun, Wuliangye 및 Jiuguijiu는 모두 와인의 하위 클래스입니다. 이는 와인의 상위 클래스를 통해서만 서로 다른 하위 클래스를 참조할 수 있습니다. 이는 실행 시 참조 변수의 특정 지점만 알 수 있습니다.

다형성을 이해하려면 "상향 변환"이 무엇인지 이해해야 한다는 것은 사실입니다. 상속에서는 상향 변환을 간략히 소개했습니다. 위의 음주 예에서 Wine(Win)이 상위 클래스이고 Jiannanchun(JNC), Wuliangye(WLY) 및 Jiuguijiu(JGJ)가 하위 클래스입니다. 우리는 다음 코드를 정의합니다: JNC a = new JNC();

Jian Nanchun 객체를 인스턴스화하는 것에 지나지 않는 이 코드를 이해하는 것은 매우 쉽습니다! 하지만 이건 어때요?

      와인 a = 새로운 JNC();                                              JNC가 JNC 개체 인스턴스를 가리키는 JNC 개체 인스턴스에 발생하는 경우가 있습니다. JNC는 Wine에서 상속되기 때문에 JNC는 자동으로 Wine으로 변환될 수 있으므로 a는 JNC 인스턴스 개체를 가리킬 수 있습니다. 이렇게 하면 매우 큰 이점이 있습니다. 상속에서는 하위 클래스가 하위 클래스를 가리키는 상위 클래스 참조 유형을 정의하는 경우 상위 클래스보다 더 강력한 기능을 제공할 수 있다는 것을 알고 있습니다. , 그러면 상위 클래스의 공통 기능을 참조할 수 있을 뿐만 아니라 하위 클래스의 강력한 기능도 사용할 수 있습니다.

그러나 상향 변환에는 몇 가지 단점이 있습니다. 즉, 일부 방법과 속성이 손실되어 얻을 수 없게 된다는 것입니다. 따라서 상위 클래스 타입의 참조는 상위 클래스에 정의된 모든 속성과 메서드를 호출할 수 있지만 하위 클래스에만 존재하는 메서드와 속성의 범위를 벗어납니다---1.

public class Wine {
    public void fun1(){
        System.out.println("Wine 的Fun.....");
        fun2();
    }
    
    public void fun2(){
        System.out.println("Wine 的Fun2...");
    }
}

public class JNC extends Wine{
    /**
     * @desc 子类重载父类方法
     *        父类中不存在该方法,向上转型后,父类是不能引用该方法的
     * @param a
     * @return void
     */
    public void fun1(String a){
        System.out.println("JNC 的 Fun1...");
        fun2();
    }
    
    /**
     * 子类重写父类方法
     * 指向子类的父类引用调用fun2时,必定是调用该方法
     */
    public void fun2(){
        System.out.println("JNC 的Fun2...");
    }
}

public class Test {
    public static void main(String[] args) {
        Wine a = new JNC();
        a.fun1();
    }
}
-------------------------------------------------
Output:
Wine 的Fun.....
JNC 的Fun2...

프로그램 실행 결과에서 a.fun1()은 먼저 상위 클래스 Wine에서 fun1()을 실행한 다음 하위 클래스 JNC에서 fun2()를 실행하는 것을 발견했습니다.

분석: 이 프로그램에서 하위 클래스 JNC는 상위 클래스 Wine의 fun1() 메서드를 오버로드하고 fun2()를 다시 작성하며, 오버로드된 fun1(String a)과 fun1()은 동일한 메서드가 아닙니다. 클래스에는 그러한 메소드가 없고, 상향 변환 후에 메소드가 손실되므로 JNC를 실행하는 Wine 유형 참조는 fun1(String a) 메소드를 참조할 수 없습니다. 하위 클래스 JNC는 fun2()를 다시 작성하므로 JNC를 가리키는 Wine 참조는 JNC의 fun2() 메서드를 호출합니다.

따라서 다형성을 다음과 같이 요약할 수 있습니다.

하위 클래스를 가리키는 상위 클래스 참조가 위쪽으로 변환되었습니다. 하위 클래스에는 존재하지만 하위 클래스에는 존재하지 않는 상위 클래스가 소유한 메서드와 속성에만 액세스할 수 있습니다. 상위 클래스 메서드인 경우 메서드를 오버로드하더라도 이 참조를 사용할 수 없습니다. 하위 클래스가 상위 클래스의 일부 메서드를 재정의하는 경우 이러한 메서드를 호출할 때 하위 클래스에 정의된 메서드(동적 연결, 동적 호출)를 사용해야 합니다.

      对于面向对象而已,多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。

多态的实现

      2.1实现条件

      在刚刚开始就提到了继承在为多态的实现做了准备。子类Child继承父类Father,我们可以编写一个指向子类的父类类型引用,该引用既可以处理父类Father对象,也可以处理子类Child对象,当相同的消息发送给子类或者父类对象时,该对象就会根据自己所属的引用而执行不同的行为,这就是多态。即多态性就是相同的消息使得不同的类做出不同的响应。

      Java实现多态有三个必要条件:继承、重写、向上转型。

         继承:在多态中必须存在有继承关系的子类和父类。

         重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。

         向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。

         只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。

      对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。

      2.2实现形式

      在Java中有两种形式可以实现多态。继承和接口。

      2.2.1、基于继承实现的多态

      基于继承的实现机制主要表现在父类和继承该父类的一个或多个子类对某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为。

public class Wine {
    private String name;
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Wine(){
    }
    
    public String drink(){
        return "喝的是 " + getName();
    }
    
    /**
     * 重写toString()
     */
    public String toString(){
        return null;
    }
}

public class JNC extends Wine{
    public JNC(){
        setName("JNC");
    }
    
    /**
     * 重写父类方法,实现多态
     */
    public String drink(){
        return "喝的是 " + getName();
    }
    
    /**
     * 重写toString()
     */
    public String toString(){
        return "Wine : " + getName();
    }
}

public class JGJ extends Wine{
    public JGJ(){
        setName("JGJ");
    }
    
    /**
     * 重写父类方法,实现多态
     */
    public String drink(){
        return "喝的是 " + getName();
    }
    
    /**
     * 重写toString()
     */
    public String toString(){
        return "Wine : " + getName();
    }
}

public class Test {
    public static void main(String[] args) {
        //定义父类数组
        Wine[] wines = new Wine[2];
        //定义两个子类
        JNC jnc = new JNC();
        JGJ jgj = new JGJ();
        
        //父类引用子类对象
        wines[0] = jnc;
        wines[1] = jgj;
        
        for(int i = 0 ; i < 2 ; i++){
            System.out.println(wines[i].toString() + "--" + wines[i].drink());
        }
        System.out.println("-------------------------------");

    }
}
OUTPUT:
Wine : JNC--喝的是 JNC
Wine : JGJ--喝的是 JGJ
-------------------------------

      在上面的代码中JNC、JGJ继承Wine,并且重写了drink()、toString()方法,程序运行结果是调用子类中方法,输出JNC、JGJ的名称,这就是多态的表现。不同的对象可以执行相同的行为,但是他们都需要通过自己的实现方式来执行,这就要得益于向上转型了。

      我们都知道所有的类都继承自超类Object,toString()方法也是Object中方法,当我们这样写时:

Object o = new JGJ();
      System.out.println(o.toString());

      输出的结果是Wine : JGJ。

      Object、Wine、JGJ三者继承链关系是:JGJ—>Wine—>Object。所以我们可以这样说:当子类重写父类的方法被调用时,只有对象继承链中的最末端的方法才会被调用。但是注意如果这样写:

Object o = new Wine();
System.out.println(o.toString());

  输出的结果应该是Null,因为JGJ并不存在于该对象继承链中。

      所以基于继承实现的多态可以总结如下:对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,执行相同动作产生的行为也就不同。

      如果父类是抽象类,那么子类必须要实现父类中所有的抽象方法,这样该父类所有的子类一定存在统一的对外接口,但其内部的具体实现可以各异。这样我们就可以使用顶层类提供的统一接口来处理该层次的方法。

      2.2.2、基于接口实现的多态

      继承是通过重写父类的同一方法的几个不同子类来体现的,那么就可就是通过实现接口并覆盖接口中同一方法的几不同的类体现的。

      在接口的多态中,指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法。

      继承都是单继承,只能为一组相关的类提供一致的服务接口。但是接口可以是多继承多实现,它能够利用一组相关或者不相关的接口进行组合与扩充,能够对外提供一致的服务接口。所以它相对于继承来说有更好的灵活性。

三、经典实例。

      通过上面的讲述,可以说是对多态有了一定的了解。现在趁热打铁,看一个实例。该实例是有关多态的经典例子,

public class A {
    public String show(D obj) {
        return ("A and D");
    }

    public String show(A obj) {
        return ("A and A");
    } 

}

public class B extends A{
    public String show(B obj){
        return ("B and B");
    }
    
    public String show(A obj){
        return ("B and A");
    } 
}

public class C extends B{

}

public class D extends B{

}

public class Test {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        
        System.out.println("1--" + a1.show(b));
        System.out.println("2--" + a1.show(c));
        System.out.println("3--" + a1.show(d));
        System.out.println("4--" + a2.show(b));
        System.out.println("5--" + a2.show(c));
        System.out.println("6--" + a2.show(d));
        System.out.println("7--" + b.show(b));
        System.out.println("8--" + b.show(c));
        System.out.println("9--" + b.show(d));      
    }
}

运行结果:

1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D

在这里看结果1、2、3还好理解,从4开始就开始糊涂了,对于4来说为什么输出不是“B and B”呢?

首先我们先看一句话:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。这句话对多态进行了一个概括。其实在继承链中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

      分析:

     从上面的程序中我们可以看出A、B、C、D存在如下关系。

Java의 다형성 심층 분석(코드 예제)  

      首先我们分析5,a2.show(c),a2是A类型的引用变量,所以this就代表了A,a2.show(c),它在A类中找发现没有找到,于是到A的超类中找(super),由于A没有超类(Object除外),所以跳到第三级,也就是this.show((super)O),C的超类有B、A,所以(super)O为B、A,this同样是A,这里在A中找到了show(A obj),同时由于a2是B类的一个引用且B类重写了show(A obj),因此最终会调用子类B类的show(A obj)方法,结果也就是B and A。

      按照同样的方法我也可以确认其他的答案。

      方法已经找到了但是我们这里还是存在一点疑问,我们还是来看这句话:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。这我们用一个例子来说明这句话所代表的含义:a2.show(b);

      这里a2是引用变量,为A类型,它引用的是B对象,因此按照上面那句话的意思是说有B来决定调用谁的方法,所以a2.show(b)应该要调用B中的show(B obj),产生的结果应该是“B and B”,但是为什么会与前面的运行结果产生差异呢?这里我们忽略了后面那句话“但是这儿被调用的方法必须是在超类中定义过的”,那么show(B obj)在A类中存在吗?根本就不存在!所以这句话在这里不适用?那么难道是这句话错误了?非也!其实这句话还隐含这这句话:它仍然要按照继承链中调用方法的优先级来确认。所以它才会在A类中找到show(A obj),同时由于B重写了该方法所以才会调用B类中的方法,否则就会调用A类中的方法。

      所以多态机制遵循的原则概括为:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法,但是它仍然要根据继承链中方法调用的优先级来确认方法,该优先级为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

위 내용은 Java의 다형성 심층 분석(코드 예제)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
이 기사는 博客园에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제
带你搞懂Java结构化数据处理开源库SPL带你搞懂Java结构化数据处理开源库SPLMay 24, 2022 pm 01:34 PM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于结构化数据处理开源库SPL的相关问题,下面就一起来看一下java下理想的结构化数据处理类库,希望对大家有帮助。

Java集合框架之PriorityQueue优先级队列Java集合框架之PriorityQueue优先级队列Jun 09, 2022 am 11:47 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于PriorityQueue优先级队列的相关知识,Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,下面一起来看一下,希望对大家有帮助。

完全掌握Java锁(图文解析)完全掌握Java锁(图文解析)Jun 14, 2022 am 11:47 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于java锁的相关问题,包括了独占锁、悲观锁、乐观锁、共享锁等等内容,下面一起来看一下,希望对大家有帮助。

一起聊聊Java多线程之线程安全问题一起聊聊Java多线程之线程安全问题Apr 21, 2022 pm 06:17 PM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于多线程的相关问题,包括了线程安装、线程加锁与线程不安全的原因、线程安全的标准类等等内容,希望对大家有帮助。

详细解析Java的this和super关键字详细解析Java的this和super关键字Apr 30, 2022 am 09:00 AM

本篇文章给大家带来了关于Java的相关知识,其中主要介绍了关于关键字中this和super的相关问题,以及他们的一些区别,下面一起来看一下,希望对大家有帮助。

Java基础归纳之枚举Java基础归纳之枚举May 26, 2022 am 11:50 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于枚举的相关问题,包括了枚举的基本操作、集合类对枚举的支持等等内容,下面一起来看一下,希望对大家有帮助。

java中封装是什么java中封装是什么May 16, 2019 pm 06:08 PM

封装是一种信息隐藏技术,是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法;封装可以被认为是一个保护屏障,防止指定类的代码和数据被外部类定义的代码随机访问。封装可以通过关键字private,protected和public实现。

归纳整理JAVA装饰器模式(实例详解)归纳整理JAVA装饰器模式(实例详解)May 05, 2022 pm 06:48 PM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于设计模式的相关问题,主要将装饰器模式的相关内容,指在不改变现有对象结构的情况下,动态地给该对象增加一些职责的模式,希望对大家有帮助。

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

mPDF

mPDF

mPDF는 UTF-8로 인코딩된 HTML에서 PDF 파일을 생성할 수 있는 PHP 라이브러리입니다. 원저자인 Ian Back은 자신의 웹 사이트에서 "즉시" PDF 파일을 출력하고 다양한 언어를 처리하기 위해 mPDF를 작성했습니다. HTML2FPDF와 같은 원본 스크립트보다 유니코드 글꼴을 사용할 때 속도가 느리고 더 큰 파일을 생성하지만 CSS 스타일 등을 지원하고 많은 개선 사항이 있습니다. RTL(아랍어, 히브리어), CJK(중국어, 일본어, 한국어)를 포함한 거의 모든 언어를 지원합니다. 중첩된 블록 수준 요소(예: P, DIV)를 지원합니다.

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

에디트플러스 중국어 크랙 버전

에디트플러스 중국어 크랙 버전

작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

VSCode Windows 64비트 다운로드

VSCode Windows 64비트 다운로드

Microsoft에서 출시한 강력한 무료 IDE 편집기