泛型和虚拟机

下面是一个很典型的泛型代码, 来自Core Java Volume Ⅰ。

public class Pair<T> {
    private T first;
    private T second;

    public Pair() { first = null; second = null;}

    public Pair(T first, T second) { this.first = first; this.second = second; }

    public T getFirst() { return first; }

    public void setFirst(T first) { this.first = first; }

    public T getSecond() { return second; }

    public void setSecond(T second) { this.second = second; }

JVM没有泛型类型对象,所有的泛型在编译阶段就已经被处理成了普通类和方法。 无论何时定义了一个泛型类型, 都自动提供了一个相应的raw type。raw type指的是出去类型参数后的泛型类型名。擦除(erase)类型变量, 并替换成限定类型(extends)或者替换成Object(没有extends)。即:

  • 如果泛型类型的类型变量没有限定类型(如上例中的Pair<T>) ,那么我们就用Object作为原始类型;
  • 如果有限定(Pair<T extends bondingType>),我们就bondingType作为原始类型;
  • 如果有多个限定(Pair<T extends bondingType1 & bondingType2>),通常将没有方法的接口放在末尾, 第一个放Class(如果有)。用第一个边界的类型变量bondingType1类作为原始类型;

当程序调用泛型方法时, 擦除返回类型后编译器会插入强制类型转换。例如:

    Pair<Employee> buddies = ...;
    Employee buddy = buddies.getFirst();

擦除getFirst的返回类型后将返回Object类型, 编译器自动插入Employee的强制类型转换(称为翻译泛型表达式)。不过在构造器方法返回时, 会出现一些问题, 例如:

     Pair<String> pair1=new Pair("zerods", 2016);          //1     
     Pair<String> pair2=new Pair<String>("zerods", 2016);  //2  

以上构造器编译器全部调用的是 Pair(Object first, Object second)。因此代码1中的new Pair(“zerods”, 2016)可以通过, 但后面想要使用String second = pair1.getSecond();就会报错。但对于代码2却显示没有相应的Pair(String, String)方法(如下图)。
这里写图片描述

事实上这是因为new Pair<String>("zerods", 2016)已经指明了创建对象pair2的类型变量T应该是String的。所以在编译期编译器就知道错误出在第二个参数Integer了。

从以上两行代码可以得出一个结论:创建泛型对象的时候,一定要指出类型变量T的具体类型。让编译器检查出错误,而不是留给JVM运行的时候抛出异常。

类型擦除所带来的两个问题

import java.time.LocalDate;

public class DateInterval extends Pair<LocalDate>{

    @Override
    public void setSecond(LocalDate second) {
        if(second.compareTo(getFirst()) > 0)
            super.setSecond(second);
    }

    @Override
    public LocalDate getSecond() {
        return (LocalDate) super.getSecond(0.clone();
    }
}

public static void main(String[] args) {
        DateInterval interval = new DateInterval(...);
        Pair<LocalDate> pair = interval;                  //超类,多态
        LocalDate date = LocalDate.of(1910, 6, 22);
        pair.setSecond(date);
    }

一个DateInterval 对象是一对LocalDate对象, 想要重写方法setSecond来保证第二个值永远不小于第一个值。这个类擦除后变成:

public class DateInteval extends Pair {
    public void setSecond(LocalDate second) {...}
}

但是别忘了, DateInterval还从Pair继承了public void setSecond(Object second) {...},这显然没有完成重写。当pair调用setSecond时,应该调用DateInterval.setSecond, 这样就形成了冲突。要解决这个问题,编译器在DateInterval类中生成了一个桥方法(bridge method):

    public void setSecond(Object second) {setSecond((LocalDate) second);}

如果DateInterval还重写了getSecond方法,在擦除的类型中则存在两个getSecond方法:

    Date getSecond()
    Object getSecond()//重写定义在父类(Pair)的方法来调用第一个方法,jvm实现,不能编写

Note: 在一个方法覆盖另一个方法时可以指定一个更严格的返回类型,它的机制也是同样使用的桥方法。例如:

public class Employee implements Cloneable {
    public Employee clone() throws CloneNotSupportedException{...}
}
//实际上这里Employee有两个clone()

Employee clone()
Object clone()  //重写了Object类的clone()方法, 调用上面的方法
### 比较Python虚拟机Java虚拟机架构及特性 #### 架构对比 Java虚拟机(JVM)采用静态编译的方式,在源码被转换成字节码之前会先经过一次完整的语法分析语义检查,这使得JVM能够执行更复杂的优化操作。而Python虚拟机(Python VM),通常指的是CPython解释器,它主要通过动态解析的方式来处理Python代码,即边解释边运行[^1]。 对于JVM而言,其设计目标之一就是实现跨平台兼容性,“编写一次,到处运行”。为了达成这一点,JVM内部实现了类加载机制、验证机制以及高效的即时编译技术等组件来支持不同操作系统上的高效执行环境。相比之下,虽然Python也具备一定的可移植性,但由于CPython依赖于特定版本的操作系统库函数接口,因此在某些情况下可能无法达到完全意义上的无缝迁移[^2]。 #### 特性对比 ##### 性能表现 由于JVM具有强大的即时编译能力(Just-In-Time Compilation),可以在程序运行期间将频繁使用的部分热路径转化为本地机器指令从而提高效率;同时配合先进的垃圾收集算法可以有效减少内存碎片化现象的发生。然而,Python作为一种脚本语言,默认情况下并不提供类似的底层优化措施,所以一般认为其整体性能低于同等条件下基于JVM的应用程序[^3]。 ##### 内存管理垃圾回收 正如前面提到过的那样,JVM内置了一套成熟的自动内存管理系统——GC(Garbage Collector), 它可以根据应用程序的实际需求调整堆空间大小并及时清理不再使用的对象所占用的空间资源。而在Python中同样存在引用计数为基础的简单形式的垃圾回收策略,但是当循环引用发生时可能会导致内存泄漏的风险增加。此外,Python还引入了周期性的gc模块来进行额外的对象释放工作以弥补这一不足之处[^4]。 ##### 扩展性插件体系 得益于丰富的API文档支持多样的编程范式(面向对象、编程等),围绕着JVM构建起来的技术生态非常庞大且成熟,像Spring框架这样的企业级解决方案几乎成为了现代Web开发不可或缺的一部分。与此同时,尽管Python本身拥有简洁优雅的语言特性活跃开源社区的支持,但在大项目维护方面或许不如前者那么得心应手[^5]。 ```python import gc def create_cycle(): list_a = [] list_b = [] # Create a cycle between two lists. list_a.append(list_b) list_b.append(list_a) create_cycle() print(gc.collect()) # Explicitly trigger garbage collection and report the number of unreachable objects found. ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值