首页 > 编程语言 >Java多态及类初始化顺序

Java多态及类初始化顺序

时间:2023-10-13 14:24:00浏览次数:45  
标签:Java 及类 子类 System 多态 static println 父类 out

多态

多态是Java面向对象的三大特性之一,多态建立于封装和继承之上,指对于父类中定义的属性和方法被子类继承后,可以具有不同的数据类型或表现出不同的行为。
可分为编译时多态运行时多态, 编译时多态是静态的,通过方法的重载体现,通过编译之后会产生不同的方法;运行时多态通过动态绑定实现。
Java实现多态有3个必要条件:继承重写向上转型

  • 继承:多态中必须存在子类继承父类的关系;
  • 重写:子类对父类方法进行重写,运行时就会动态调用子类重写的方法;
  • 向上转型:将子类的引用赋给父类对象,父类类型 引用名 = new 子类类型();

多态的实现

一个对象的编译类型和运行类型可以不一致,编译类型在定义对象时就确定了,不能改变,运行类型可以随运行时具体传入的对象引用而改变。
编译类型由=左边的类型确定,运行类型由=右边的类型确定。
instanceof操作符用于判断对象的运行类型是否为XX类型或其子类型。

看下面的的例子:

class A {
    int val1 = 1;
    void test() {
        System.out.println("A test");
    }

    void test2() {
        System.out.println("A test2");
    }
}
public class B extends A {
    int val1 = 11;
    int val2 = 2;
    @Override
    public void test() {
        System.out.println("B test");
    }
    public void test3() {
        System.out.println("B test3");
    }

    public static void main(String[] args) {
        A a = new B(); //向上转型
        a.test(); // 调用子类重写的方法
        a.test2(); //调用父类方法
        ((B) a).test3(); //无法直接调用子类特有方法
        B b = (B)a; //向下转型
        b.test(); //调用子类重写的方法
        b.test2(); //调用父类方法
        b.test3(); //调用子类特有方法
        System.out.println(a.val1); //访问父类成员变量
        System.out.println(((B) a).val2); //无法直接访问子类特有成员变量
        System.out.println(b.val1);
        System.out.println(b.val2);
    }
}

输出:

B test
A test2
B test3
B test
A test2
B test3
1
2
11
2

动态绑定

动态绑定又称后期绑定,即在运行时根据具体对象的类型进行绑定。

  1. 当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定;
  2. 当调用对象属性时,无动态绑定机制,哪里声明,哪里使用。

静态绑定又称前期绑定,程序执行前方法就已经被绑定,Java中的方法只有final、static、private和构造方法是前期绑定。

向上转型与向下转型

多态的向上转型:
父类的引用指向了子类的对象,父类类型 引用名 = new 子类类型();,子类对象可以调用父类的所有成员,但不能调用子类特有的成员(在编译阶段,能调用哪些成员是由编译类型决定),最终运行效果看子类[运行类型]的具体实现(调用方法时,先从子类[运行类型]开始查找)。

多态的向下转型:
子类类型 引用名 = (子类类型) 父类引用,只能强转父类的引用,不能强转父类的对象;要求父类的引用必须是指向当前目标类型的对象;向下转型后可以调用子类类型的所有成员。

多态的应用

多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。

public static void main(String[] args) {
    //Person Student Teacher都有say()方法
    Person[] persons = new Person[5];
    persons[0] = new Person("adam", 18);
    persons[1] = new Student("adam", 18, 99);
    persons[2] = new Student("bob", 19, 70.1);
    persons[3] = new Teacher("chuck", 30, 3000);
    persons[4] = new Teacher("dick", 29, 3100);

    for (int i = 0; i < persons.length; i++) {
        //persons[i]编译类型为Person,运行类型根据实际情况由JVM判断
        System.out.println(persons[i].say()); //动态绑定机制
        //teach()是Teacher类特有方法,study()是Student类特有方法
        if (persons[i] instanceof Student) {
            ((Student) persons[i]).stduy(); //向下转型
        } else if (persons[i] instanceof Teacher) {
            ((Teacher) persons[i]).teach(); //向下转型
        } else {
            System.out.println("类型有误");
        }
    }
}

多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型。

public class PloyTest {
    public static void main(String[] args) {
        //父类Employee,子类Worker和Manager继承Employee
        Worker frank = new Worker("Frank", 1000);
        Manager gordon = new Manager("Gordon", 20000, 2000);
        PloyTest ployTest = new PloyTest();
        ployTest.showEmpAnnual(frank);
        ployTest.showEmpAnnual(gordon);

        ployTest.testWork(frank);
        ployTest.testWork(gordon);
    }

    public void showEmpAnnual(Employee e) {
        System.out.println(e.getAnnual());
    }

    public void testWork(Employee e) {
        if (e instanceof Worker) {
            //work()为Worker类专有方法
            ((Worker) e).work(); //向下转型
        } else if (e instanceof Manager) {
            //manage()为Manager类专有方法
            ((Manager) e).manage(); //向下转型
        } else {
            System.out.println("无操作");
        }
    }
}

类初始化顺序

静态代码块:static{}声明,在JVM加载类时执行,且仅执行一次;
构造代码块:在类中用{}声明,每次实例化对象时执行。
执行顺序是:静态代码块->main方法->构造代码块->构造方法。

对于单个类的情况

public class TestA {
    public static int val1;
    public int val2;

    static {
        val1 = 1;
        System.out.println("A静态代码块" + val1);
        val3 = 3;
        // System.out.println(val3);
        // 编译异常Illegal forward reference
    }
    static int val3;
    {
        val2 = 2;
        System.out.println("A构造代码块" + val2);
    }

    public TestA() {
        System.out.println("A构造方法");
    }

    public static void main(String[] args) {
        TestA testA1 = new TestA();
        TestA testA2 = new TestA();
    }
}

输出:

A静态代码块1
A构造代码块2
A构造方法
A构造代码块2
A构造方法

对于单个类来说,静态变量、静态代码化块、变量、构造代码块、构造方法,执行顺序是:
(静态变量、静态代码块)早于(变量、构造代码块)早于 构造方法。
另外,类初始化时规定了,静态代码块只能访问到声明于它之前的静态变量,对于声明在静态代码块之后的变量,只能赋值不能访问,否则出现编译异常非法前向引用

对于继承的情况

class A {
    A() {
        System.out.println("A constructor");
    }

    static {
        System.out.println("A static");
    }
    {
        System.out.println("A code block");
    }
}
public class B extends A {
    B() {
        System.out.println("B constructor");
    }

    static {
        System.out.println("B static");
    }
    {
        System.out.println("B code block");
    }

    public static void main(String[] args) {
        System.out.println("B main");
        A a = new A();
        System.out.println("---");
        B b = new B();
        System.out.println("---");
    }
}

输出:

A static
B static
B main
A code block
A constructor
---
A code block
A constructor
B code block
B constructor
---

对于继承的情况,类初始化顺序是:父类静态成员及静态代码块 -> 子类静态成员及静态代码块 -> 子类main方法 -> 父类普通成员初始化 -> 父类构造方法 -> 子类普通成员初始化 -> 子类构造方法。

多态的情况:

package test;
class A {
    A() {
        System.out.println("A constructor");
    }

    static {
        System.out.println("A static");
    }
    {
        System.out.println("A code block");
    }
}
public class B extends A {
    B() {
        System.out.println("B constructor");
    }

    static {
        System.out.println("B static");
    }
    {
        System.out.println("B code block");
    }

    public static void main(String[] args) {
        System.out.println("main");
        A a = new B();
    }
}

输出:

A static
B static
main
A code block
A constructor
B code block
B constructor

最后运行时绑定的对象是子类对象。

标签:Java,及类,子类,System,多态,static,println,父类,out
From: https://www.cnblogs.com/KRDecad3/p/17761992.html

相关文章

  • Java SWT Image 图像 —— 透明度 alpha数组
    对于图像深度是2、4、8的图像,可以指定transparentPixel。对于直接图像,要使用alpha或者alpha数组,alpha值0到255,0表示完全透明的,数值越大表示越是不透明,255表示完全不透明,可以只是设置一个alpha值,作用于所有的像素点,也可以给所有的像素点设置自己的透明的值。 如: 的alpha的数组值为......
  • Java SWT Image 图像 —— mask
    原文链接:http://www.eclipse.org/articles/Article-SWT-images/graphics-resources.htmlPaletteDatapaletteData=newPaletteData(newRGB[]{newRGB(0,0,0),newRGB(255,255,255)});ImageDatasourceData=newImageData(32,32,1,paletteData);ImageDatamaskData=......
  • Java SWT Image 图像 —— 变灰、变亮变黑、旋转、反色、拉伸、透明叠加
    图像变灰图像变灰在桌面应用程序中有着广泛的应用。例如,一个图标被作为一个按钮的背景,我们需要一个灰色效果的图标作为按钮的背景来表示这个按钮处于禁用状态。在SWT中,基于已经存在的图像来创建一个具有灰色效果的图像,我们可以使用构造函数Image(Displaydisplay,Imageimage,in......
  • Java并发之AQS
    一、AQS是什么AbstractQueuedSynchronizer抽象同步队列,简称AQS,它是Java并发包的根基,并发包中的锁就是基于AQS实现的。AQS是基于一个FIFO的双向队列,其内部定义了一个节点类Node,Node节点内部的SHARED用来标记该线程是获取共享资源时被阻挂起后放入AQS队列的,EXCLUSIVE用来标记......
  • Java内存模型-JMM
    内存模型与内存区域 很多时候,二者会混淆,所以咱们先做区分:内存模型与内存区域内存区域是指JVM运行时将数据分区域存储,强调的是对空间的分配内存模型(JMM)是定义了线程和主内存之间的抽象关系。即定义了JVM在计算机内存中的工作方式内存模型抽象关系定义线程之间的共享区......
  • java学习日记day02
    java学习日记day02冯诺伊曼体系)cmd指令......
  • Java8新特性之Optional容器(七)
    1.Optional介绍Optional是Java8提供的一个容器对象,可以包含一个为null或者不为null的对象;使用该对象可以更方便的避免项目中的NPE,在新版的SpringDataJPA中已经实现了对该类的支持;注意该类是被final修饰的,同时没有实现任何接口;publicfinalclassOptional<T>{pri......
  • java框架中的controller层、dao层、domain层、service层、view层【转】
      1.Controller层:接口层,用户访问请求时对接。  Controller层负责具体的业务模块流程的控制,在此层里面要调用Serice层的接口来控制业务流程,控制的配置也同样是在Spring的配置文件里面进行,针对具体的业务流程,会有不同的控制器,我们具体的设计过程中可以将流程进行抽象归......
  • 【Java】用户在线人数统计的简单实现
    一、需求效果:就是进入首页时能查看在线人数,没有特定要求,那我就不刷这个接口了就进入首页加载一次 二、实现思路:思路参考博客:https://blog.csdn.net/GitLuckyd/article/details/124488063如果是以前那种JSP的单体项目,可以用Servlet的监听器API来做但是不管是Servlet还......
  • Java也能做OCR!SpringBoot 整合 Tess4J 实现图片文字识别
    前言今天给大家分享一个SpringBoot整合Tess4j库实现图片文字识别的小案例,希望xdm喜欢。文末有案例代码的Git地址,可以自己下载了去玩玩儿或继续扩展也行。话不多说,开整吧。什么是Tess4j库先简单给没听过的xdm解释下,这里要分清楚Tesseract和Tess4j的区别。Tesseract是一个......