Java多态

2023-11-17

  1. 关于引用的进一步理解(交换值)
    因为Java方法在传递参数的时候都是值传递,那么如何通过方法实现2个数的值交换?
    明确:在传引用的时候,到底拿引用干了个啥
class Value {
    public int a;
}

public class Test {

    public static void swap(Value value1, Value value2) {
        int tmp = value1.a;
        value1.a = value2.a;
        value2.a = tmp;
    }

    public static void main(String[] args) {
        //交换值,如果只是简单的定义a和b不太能做到,因为他们在栈上,我们要想办法把他们放到堆上
        Value value1 = new Value();
        Value value2 = new Value();
        value1.a = 10;
        value2.a = 20;
        swap(value1, value2);
        System.out.println(value1.a);
        System.out.println(value2.a);
    }

在这里插入图片描述
将属性a的权限改成private,此时在类外a是被封装起来的,不能够直接赋值了,可以提供get和set方法来实现交换值

class Value {
    private int a;

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }
}

public class Test {

    public static void swap(Value value1, Value value2) {
//        int tmp = value1.a;
        int tmp = value1.getA();
//        value1.a = value2.a;
        value1.setA(value2.getA());
//        value2.a = tmp;
        value2.setA(tmp);
    }

    public static void main(String[] args) {
        //交换值,如果只是简单的定义a和b不太能做到,因为他们在栈上,我们要想办法把他们放到堆上
        Value value1 = new Value();
        Value value2 = new Value();
        //value1.a = 10;
        value1.setA(10);
        //value2.a = 20;
        value2.setA(20);
        swap(value1, value2);
        System.out.println(value1.getA());
        System.out.println(value2.getA());
    }

    public static void main1(String[] args) {
        Derived derived = new Derived();
        derived.fun();
    }
}

运行结果:
在这里插入图片描述

  1. 什么是多态?
    发生多态的3个条件①在继承的条件下②发生向上转型③方法重写
    多态是一种思想,父类引用引用不同对象的时候,表现出来的行为是不一样的。【这就叫做多态】
    多态的前提是:动态绑定
    【一个父类引用 指向的对象不一样,调用重写的方法,会表现出不同的行为。】

  2. 向上转型
    分别定义动物类、狗类、鸟类

class Aniaml {
    public String name;
    public int age;

    public void eat() {
        System.out.println(name + "吃东西");
    }
}

class Dog extends Aniaml {
    public void wangwnag() {
        System.out.println(name + "汪汪汪");
    }
}

class Bird extends Aniaml {
    public String wing;
    public void miaomiao() {
        System.out.println(name + "喵喵喵");
    }
}

public class Test1 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "小狗";
        dog.eat();
        dog.wangwnag();
        System.out.println("===分割===");
        Bird bird = new Bird();
        bird.name = "小鸟";
        bird.eat();
        bird.miaomiao();
    }
}

运行结果:
在这里插入图片描述
如果修改main方法中的实例化对象语句为:

        Aniaml aniaml1 = new Dog();
        aniaml1.name = "小狗";
        aniaml1.eat();
//        aniaml1.wangwang();  //报错,因为只能访问Aniaml类中有的成员
        System.out.println("===分割===");
        Aniaml aniaml2 = new Bird();
        aniaml2.name = "小鸟";
        aniaml2.eat();

此时 Aniaml aniaml1 = new Dog();,左边是动物类,右边是狗类,发生了向上转型,父类引用指向了子类对象。
理论上:等号两边的数据类型必须一致,否则赋值出错。
当发生向上转型之后,此时通过父类的引用只能访问父类自己的成员,不能访问到子类特有的成员,也就是说,只animal能调用Animal类中的方法和属性,不能再访问Dog类和Bird类子类中独有的方法和属性了。

  1. 向上转型的3种场景
    ①直接赋值
        Aniaml aniaml1 = new Dog();
        aniaml1.name = "小狗";
        aniaml1.eat();

②方法传参

    public static void fun(Aniaml aniaml) {
        aniaml.eat();
    }

    public static void main(String[] args) {
        Dog dog = new Dog("小狗");
        fun(dog);
    }

③方法返回值

    public static Aniaml func() {
        return new Dog();
    }

向上转型的优点:让代码实现更简单灵活。(animal能够引用它,说明它一定是个animal的子类,它一定是一个动物)
向上转型的缺点:不能访问子类特有的方法。【除非发生了动态绑定,可以调用子类重写的方法】(animal只能调用自己特有的属性和方法)

  1. 方法重写的引入
    在上述例子种,狗和鸟都是吃饭,按照常理,狗应该吃狗粮,鸟应该吃鸟粮,此时就应该在狗类和鸟类种重写父类的eat方法。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    public static void main(String[] args) {
        Aniaml aniaml1 = new Dog("小狗");
        aniaml1.eat();
        Aniaml aniaml2 = new Bird("小鸟");
        aniaml2.eat();
    }

运行结果:
在这里插入图片描述
分析:在这个过程中,【父类不是只能调用父类的方法吗?这里怎么调用了重写的方法呢?】因为这里发生了动态绑定,编译的时候确实调用的是Animal的eat方法,但是在运行的时候发生了动态绑定,调用了重写的方法。

  1. 动态绑定需要满足的3个条件
    ①向上转型②重写③通过父类引用调用这个父类和子类重写的方法
    【动态绑定是多态的基础】
    动态绑定:也称为后期绑定(晚绑定),在编译的时候不能确定方法的行为,需要等到程序运行的时候,才能确定具体调用哪个类的方法
    静态绑定:也称为前期绑定(早绑定),在编译的时候,根据传入的参数,就能够确定调用哪个方法,典型代表方法重载。

  2. 方法重写的注意点
    重写:是子类对父类非private修饰、非static修饰、非final修饰、非构造方法等进行重新编写。【重写的好处:子类可以根据需要,定义特定于自己的行为,子类可以根据需要实现父类的方法。】
    ①被private修饰的方法不能被重写:因为private权限范围只能在当前类内使用
    ②被static修饰的方法不能被重写:因为static属于类方法,如果被重写,则一个属于动物类,一个属于狗类,和对象毫无关系,分别属于各自所在的类。
    ③被final修饰的方法不能被重写:也叫密封方法
    ④构造方法不能被重写:因为子类的构造方法名字不可能和父类的构造方法的名字一样。
    ⑤子类的访问修饰限定权限要大于等于父类的权限
    private<默认<protected<public

  3. 重写和重载的对比
    重写:①方法名相同 ②参数列表相同③返回值类型相同【当返回值类型不同的时候必须是 父子关系
    重载:①方法名相同②参数列表不同③与返回值类型无关
    【重写的参数列表 一定不能修改,重载的参数列表 必须修改】

  4. 向下转型【不安全】
    将一个子类对象经过向上转型之后当成父类方法使用,这也再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时需要将父类引用再还原为子类对象。
    例如,将Dog和Bird向上转型成Animal后,无法调用Dog中特有的wangwang方法以及Bird中的zhazha方法,此时需要将Animal再次向下转型成Dog和Bird进行调用子类特有的方法。

    public static void main(String[] args) {
        Aniaml aniaml1 = new Dog("小狗");
        Dog dog = (Dog) aniaml1;
        dog.eat();
        Aniaml aniaml2 = new Bird("小鸟");
        Bird bird = (Bird) aniaml2;
        bird.eat();
    }

运行结果:
在这里插入图片描述
分析:animal1向下转型:本来是狗,还原为狗,安全;animal2向下转型:本来是鸟,还原为鸟,安全。
如果本来是狗,向下转型为鸟,则不安全:

    public static void main(String[] args) {
        Aniaml aniaml = new Dog("小狗");
        Bird bird = (Bird) aniaml;
        bird.eat();
    }

编译没有报错,但是在运行的时候出现类型转换异常
在这里插入图片描述
分析:程序可以通过编程,但是运行时抛出异常,本来是狗,现在要强制还原为猫,无法正常还原,不安全。
Java中为了提高向下转型的安全性,引入 instanceof关键字,如果表达式为true,则可以安全转换。

    public static void main(String[] args) {
        Aniaml aniaml = new Dog("小狗");
        if (aniaml instanceof Bird) {
            Bird bird = (Bird) aniaml;
            bird.eat();
        }else {
            Dog dog = (Dog) aniaml;
            dog.eat();
        }
    }

运行结果:
在这里插入图片描述
分析:aniaml instanceof Bird,判断animal是不是引用了Bird对象,如果引用了就可以向下转型。

  1. 多态的优缺点
    例:定义类,完成画图(圆,矩形,花等)
    使用多态完成:
class Shape {
    public void draw() {
        System.out.println("画图形");
    }
}


class Cir extends Shape {
    @Override
    public void draw() {
        System.out.println("画圆");
    }
}

class Rex extends Shape {
    @Override
    public void draw() {
        System.out.println("画矩形");
    }
}

class Flow extends Shape {
    @Override
    public void draw() {
        System.out.println("画花");
    }
}


public class TestDemo {

    public static void func(Shape shape) {
        shape.draw();
    }

    public static void main(String[] args) {
        Cir cir = new Cir();
        Rex rex = new Rex();
        Flow flow = new Flow();
        
        func(cir);
        func(rex);
        func(flow);


    }
}

运行结果:
在这里插入图片描述

圈复杂度:是一种描述一段代码复杂程度的方式,可以简单粗暴的一段代码中条件语句和循环语句出现的个数,这个个数就称为“圈复杂度”。如果一个方法圈复杂度太高,就需要考虑重构,不同公司对于代码的圈复杂度的规范不一样,一般不会超过10。
假如不使用多态,则要使用到if-else语句等进行判断(此时圈复杂度会很高)。

public static void main(String[] args) {
        Cir cir = new Cir();
        Rex rex = new Rex();
        Flow flow = new Flow();
        String[] shapes = {"cir", "rex", "flow","cir", "rex"};

        for (String x: shapes) {
            if (x.equals("cir")) {
                cir.draw();
            }else if (x.equals("rex")) {
                rex.draw();
            }else if (x.equals("flow")) {
                flow.draw();
            }
        }
    }

如果使用多态,则不必写那么多if-else分支语句,代码更简单。

    public static void func(Shape shape) {
        shape.draw();
    }

    public static void main(String[] args) {
        Shape[] shapes = {new Cir(), new Rex(), new Flow(),new Rex(), new Rex()};
        for (Shape x: shapes) {
            func(x);
        }
    }

分析: Shape数组:意味着每个元素都是Shope子类,父类类型的数组可以放子类类型变量。
在func方法中,shape引用 引用饿子类对象不一样,调用的方法表现出来的行为也就不一样——这种思想叫做多态。

如果要增加一个打印三角形。
对于类的实现者:定义一个三角形的类,并重写方法。

class Tri extends Shape {
    @Override
    public void draw() {
        System.out.println("画三角形");
    }
}

对于类的调用者:创建一个三角形的实例。

        Tri tri = new Tri();
        func(tri);

改动成本很低
而对于不用多态的情况,就要把if-else进行一定的改动,改动成本更高。

因此多态的优势:
①能够降低代码的“圈复杂度”,避免使用大量的if-else。
②可扩展能力强,如果要新增一种新的形状,使用多态的方式代码改动成本也比较低。
多态的缺陷:
①属性没有多态性,只有方法有多态性:当父类和子类都有同名的属性的时候,通过父类的引用,只能引用父类自己的成员属性。
②构造方法没有多态性,构造方法也不能被重写
③多态代码运行效率降低

  1. 避免在构造方法中调用重写的方法
    有坑的代码,创建两个类,B是父类,D是子类,D中重写func方法。并且在父类B的构造方法中调用func。
class B {

    public B(){
        func();
    }
    public void func() {
        System.out.println("B的方法");
    }
}

class D extends B {
    int num;
    public D() {
        num = 1;
    }
    @Override
    public void func() {
        System.out.println(num + " C重写B的方法");
    }
}


public class TestDemo2 {
    public static void main(String[] args) {
//B是父类,D是子类,D中重写func方法。并且在父类B的构造方法中调用func。
        D d = new D();
    }
}

运行结果:
在这里插入图片描述
分析:在B的构造方法中调func,刻板印象会认为调用的是B的func方法,但是通过实际的运行结果发现调用的是D中的func方法,并且num的值为0,因为此时父类B的构造方法都还没执行完成,更别说子类D的构造了。
注意:当在父类的构造方法当中,去调用父类和子类重写的方法的时候,此时会调用子类的重写方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java多态 的相关文章

  • Spring JDBC 模板。如何获取pl/sql脚本的结果变量

    我正在使用 NamedParameterJdbcTemplate 来运行 pl sql 脚本 但我不知道如何获取out变量的值 id out 提前致谢 String script declare begin if myFunc id in
  • 我在使用 JavaFX 绘制十字时遇到问题

    我正在尝试编写代码 在网格上对角绘制 3 个形状 前两个形状是正方形和圆形 我能做到 然而 第三种形状让我有些悲伤 我应该画一个十字 T 版本 而不是 X 每次我写出代码时 它看起来就像一个侧面 我知道我只是错过了一些简单的东西 但我真的很
  • 使用 JavaScript 与 Web 服务器通信的 Applet 是否可以迁移到 JWS?

    只是分享一些信息 希望对社区有用 由于各种浏览器停止支持插件 Applet 的可用性已经下降 Google 已决定停止对 NPAPI 插件的支持 EDGE 不支持插件 Firefox 也不鼓励使用插件 Mozilla 可能会跟进该套件 我们
  • 如何在JavaFX中有效地滚动和缩放大图像?

    作为图像处理应用程序的一部分 我需要创建具有缩放 滚动和矢量叠加功能的简单查看器模块 图像相当大 40000x20000 这使得 ImageView 上的操作变慢 缓冲等 在 JavaFX 中处理巨大图像时 改善用户体验的最佳选项是什么 我
  • Java:while循环冻结程序

    我正在制作一个游戏 我需要每 3 秒更新一次 JProgressBar 为此 我使用 while 循环 问题是我的程序由于 while 循环而冻结 我在其他问题中读到它 他们没有帮助我解决这个问题 我不知道如何解决 这是我的代码 publi
  • 从另一个进程捕获 system.out 消息

    我有一个 JVM 1 它启动 JVM 2 我希望能够在 JVM 1 中监视来自 JVM 2 的 System out println 调用 直接的方法是 JVM A 执行系统命令来启动 JVM B 然后 JVM A 读取 B 的所有输出 S
  • 在 json 中解析尾随字符

    我正在尝试检查 json 是否有效 并且我遇到了奇怪的行为 当我将一些字符附加到可解析的 json 时 jackson 和 gson 都会解析它 并且它们会忽略尾随字符 我想检查 json 是否严格有效 请帮忙 我尝试了几个标志mapper
  • Java - 调整图像大小而不损失质量

    我有 10 000 张照片需要调整大小 因此我有一个 Java 程序来执行此操作 不幸的是 图像的质量损失很大 而且我无法访问未压缩的图像 import java awt Graphics import java awt AlphaComp
  • JTree ConvertValueToText 返回在更改时被截断

    我有一个自定义树实现convertValueToText 此实现取决于某些全局状态 如果返回的字符串比先前返回的字符串更长 实际上我认为更宽 因为以像素为单位触发它 则文本将被截断并用 填充 当重绘是由 取消 选择元素或某个元素引起时 情况
  • Eclipse Oxygen - 该项目未构建,因为其构建路径不完整

    我刚刚安装了 Eclipse Oxygen 并尝试在工作台中打开现有项目 但收到此错误 该项目未构建 因为其构建路径不完整 不能 找到 java lang Object 的类文件 修复构建路径然后尝试 建设这个项目 我尝试右键单击该项目 转
  • Eclipse RCP - 将视图与编辑器区域堆叠?

    在开发 Eclipse RCP 应用程序时 是否可以将视图与编辑器区域堆叠在一起 像这样 我有多个列表 表格 我想创建一种预览组合 当通过单击鼠标选择列表上的项目时 我希望我的预览合成显示该项目的数据 如果用户双击某个项目 我想在预览合成后
  • 合并和颜色样式不适用于 Apache POI excel 2003 格式

    在 Apache POI 中 我为某些单元格应用了一些样式并合并了这些单元格 当我在 2010 年或 2007 年打开时 它工作正常 但在 2003 年 格式样式消失了 每次保存 2003 Excel 文件之前都会弹出兼容性检查对话框 请参
  • 如何在java中从包含.0的浮点数中删除小数部分

    我只想删除包含的浮点数的小数部分 0 所有其他数字都是可以接受的 例如 I P 1 0 2 2 88 0 3 56666 4 1 45 00 99 560 O P 1 2 2 88 3 567 4 1 45 99 560 有什么方法可以做到
  • 如何在 QueryDSL 中选择文字

    我目前正在开发一个使用 queryDSL 和 hibernate 的项目 其中它需要一个选择文字 按照发布的示例here https stackoverflow com questions 18691317 querydsl how to
  • java中永远不会出现的异常

    我为点和向量编写一个类 我想用它们来计算向量的点和范数 这些是点类和向量类 public class Point public float x y public class MyVector public Point start end 我
  • IntelliJ - 无效源版本:17

    我已经在 IntelliJ 中使用 Gradle 创建了一个使用 Java 17 的新 Java 项目 运行我的应用程序时出现错误Cause error invalid source release 17 我的设置 我已经安装了openjd
  • 带等待/通知的同步块与不带等待/通知的同步块之间的区别?

    如果我只是使用synchronized 不是wait notify方法 它仍然是线程安全的吗 有什么不同 Using synchronized使方法 块一次只能由一个线程访问 所以 是的 它是线程安全的 这两个概念是结合在一起的 而不是相互
  • 在Java内存管理中,“PS”代表什么?

    每当我看到 Java 中对内存的引用时 各种空格总是以 PS 为前缀 PS 是什么意思 它开始困扰我 到目前为止我唯一的猜测是 泳池空间 但这将是多余的 例子 PS伊甸园空间 PS 幸存者空间 PS 终身空间 老一代 PS Perm Gen
  • 对于双核手机,availableProcessors() 返回 1

    我最近购买了一部 Moto Atrix 2 手机 当我尝试查看手机中的处理器规格时 Runtime getRuntime availableProcessors 返回 1 proc cpuinfo 也仅包含有关处理器 0 的信息 出于好奇
  • 背景图像隐藏其他组件,例如按钮标签等,反之亦然

    如何解决此代码中组件的隐藏问题 代码运行没有错误 但背景图片不显示 如何更改代码以获取背景图像 使用验证方法时 它在validation 中创建错误 public class TEST public TEST String strm Jan

随机推荐

  • Android获取当前位置的三种方式及其使用方法

    1 GPS定位 2 基站定位 此类位置的获取有赖于手机无线通讯信号 当手机处在信号覆盖范围内 手机可以获得该区域 即通讯术语中的 小区 的识别号 因为这些识别号是惟一的 因此可以将识别号和地理坐标对应起来 因此根据识别号就可以知道地理位置
  • Unity3D 使用TextMeshPro中文字体

    这一篇简单描述一下如果使用unity的一个强大的文字组件或者插件 开始 第一步 如果你是unity2018版本的话 在编辑器里面的AssetPackgeManager找到这个插件 没有的话就在搜索框里面搜索下载 如果你是低于2018的版本
  • Xilinx ISE系列教程(8):读取FPGA芯片唯一ID号

    文章目录 toc 应用场景 方法1 通过JTAG读取 方法2 调用原语读取 DNA PORT原语的使用 DNACLK频率注意 本文是Xilinx ISE系列教程的第8篇文章 用过单片机的朋友都知道 单片机芯片内部都有一串序列号 比如STM3
  • OkHttp库简介

    一直以来 Java并没有什么比较好用的HTTP库 JDK自带的HTTP类又非常旧 难以使用 今天我发现了一个使用比较广泛的OkHttp库 它在安卓和Java领域都有使用 在Github上的星数有两万多 所以我们可以放心的使用 安装 先来看看
  • linkToDeath机制了解和使用

    转自 http www jianshu com p e38f08e34686 在学习Binder和AIDL的过程中遇到的一些有意思的事情 linkToDeath机制 我们先看看官网如何介绍 When working with remote
  • pytorch:参数pin_memory=True和non_blocking=True的作用

    目录 一 pin memory 二 non blocking 一 pin memory pin memory是dataloader 的参数 默认值为False 其作用是是否把把数据存放在锁页内存中 主机的内存根据物理内存 内存条 与虚拟内存
  • 服务器2012系统截屏,Windows Server 2012 R2 Preview界面截图

    微软将在BUILD大会上公开发布Windows 8 1 预览版 Windows 8 1是Windows Blue升级项目的一部分 那么一直以来与Windows客户端如影随形的Windows Server呢 是的 Blue项目并非仅为Wind
  • 微信小程序和APP优劣势大对比

    小程序的优势 1 无需下载 随走随关 2 功能丰富 体验更简便 3 接口众多 可以进行不断的开发 4 流量入口大 背靠日活9 6亿的微信 5 有强大的微信生态环境 小程序对比APP的好处 1 开发成本低 2 开发门槛低 3 获客成本低于Ap
  • 星星之火-47: 5G的八大组网方案

    目录 1 5G组网方案概述 2 选项3系列 4G LTE接入网 5G NR 接入网 4G LTE核心网 3 选项2 5G NR 5G 核心网 4 选项7系列 4G LTE接入网 5G NR接入网 5G核心网 5 选项4系列 4G LTE接入
  • Qt进程间通信

    进程是操作系统的基础之一 一个进程可以认为是一个正在执行的程序 我们可以把进程当做计算机运行时的一个基础单位 关于进程的讨论已经超出了本章的范畴 现在我们假定你是了解这个概念的 在 Qt 中 我们使用 QProcess 来表示一个进程 这个
  • .getClass.getClassLoader.getResourceAsStream的方式加载文件,总是为null加载不到数据

    记录一个问题 我在用如下的代码加载配置文件的时候 总是加载不到数据 文件位置的对的 SparkSessionBase getClass getClassLoader getResourceAsStream spark conf proper
  • Unity之脚本API笔记一(Transform详解及使用方法)

    一 什么是Transform 场景中的每一个物体都有一个Transform 用于存储和操作对象的位置 旋转和缩放 存在层级关系 父级和子级 二 常用变量与属性 位置 1 位置 position 世界坐标 localposition 相对坐标
  • https通讯过程,常见的状态码,DNS解析过程

    一 https通讯过程 1 客户端发起HTTPS请求 然后连接到服务器的443端口 2 传送服务器的证书给客户端 自己颁发的证书需要客户端验证通过 才可以继续访问 而使用受信任的公司申请的证书则不会弹出提示页面 3 客户端收到服务器端的证书
  • JDBC入门

    JDBC 1 JAVA DATABASE CONNECTION 导入jar包 驱动 加载驱动类 Class forName 类名 给出url username password 其中url背下来 jdbc 使用DriverManger来得到
  • Tensorflow学习笔记(一)拟合线性平面 逐句解析

    TensorFlow Python API 依赖 Python 2 7 版本 Python 程序生成了一些三维数据 然后用一个平面拟合它 import tensorflow as tf import numpy as np 使用 NumPy
  • 基于CentOS 7.6安装及配置APISIX 3.0环境

    最近一直在研究微服务相关内容 通过对比各大API网关 发现新起之秀 APISIX无论从开源程度上来讲还是功能上 都拥有很大的优势 经历了几天折磨一样的学习 目前在本地环境中配置成功了一套 以供自己留存吧 实在是网上的很多文章要么太老了 要么
  • 小程序项目实战(二)

    此文章用于总结自己的知识点 有这个项目有兴趣的伙伴可以点击下方链接购买学习 小程序音乐项目开发实战 大神coderwhy新课 学习视频教程 腾讯课堂课程简介https ke qq com course 4162214 一 了解小程序中的基础
  • 自己创建下拉框数组

    自己创建1 8的数组供下拉框选择 this scanPositions new Array 8 fill null map i index gt return label index 1 车 value index 1
  • 2023年第1季社区Task挑战赛开启,等你来战!

    社区Task挑战赛是面向社区开发者开展的代码或教程征集活动 该挑战赛为社区中热爱FISCO BCOS及周边组件的开发者提供了探索区块链技术 挑战技术难题的舞台 该挑战赛去年在社区成功举办了3季 共吸引了数百名开发者报名 前3季都有哪些有趣的
  • Java多态

    关于引用的进一步理解 交换值 因为Java方法在传递参数的时候都是值传递 那么如何通过方法实现2个数的值交换 明确 在传引用的时候 到底拿引用干了个啥 class Value public int a public class Test p