Java动态类型语言支持

news2024/11/23 21:50:33

JDK7发布字节码首位新成员——invokedynamic指令。以实现动态类型语言支持。也是为JDK8里可以顺利实现Lambda表达式而做的技术储备。我们将在本文详细了解动态语言支持这项特性出现的背景和它的意义与价值。

1 动态类型语言

动态类型语言的关键特征是它的类型检查的主体过程是运行期而不是编译器进行的。变量无类型而变量值才有类型。

1.1 类型检查

类型检查是指验证操作接受的是否为合适的类型数据以及赋值是否符合类型要求。最自然的方式是认为检查发生在运行时,即当设计到具体的数据值时,即动态类型检查(也称运行时检查)。编译时检查(静态检查)通过对程序的静态分析,检查所有使用值的使用操作、调用和赋值,在程序运行前排除潜在的类型错误。

public class Test1 {
    static class HelloWord {
        public void sayHello() {
            System.out.println("Hello Java");
        }
    }
    public static void main(String[] args) {
        HelloWord helloWord = new HelloWord();
        helloWord.sayHello();
    }
}

图 上述代码的字节码

Java在编译器将sayHello方法完整的符号引用生成出来,并作为方法调用指令的参数存储到Class文件中。而其他动态类型语言在编译期最多只能确定方法名称、参数、返回值这些信息,而不会去确定方法所在的具体类型。

1.2 静态类型语言与动态类型语言的特点

静态类型语言

能在编译器确定变量类型,编译器可以提供全面严谨的类型检查,有利于稳定性及让项目容易达到更大的规模。

动态类型语言

可以为开发人员提供极大灵活性,某些在静态类型语言中要花大量臃肿代码来实现的功能,由动态类型语言去做可能会很清晰简洁,提升开发效率。

表 静态类型语言与动态类型语言的特点

2 Java动态语言支持的技术背景

《Java虚拟机规范》第一版中规划了一个愿景:“在未来,我们会对Java虚拟机进行适当的扩展,以便更好地支持其他语言运行于Java虚拟机之上。”目前确实已经有许多动态类型语言运行于Java虚拟机之上了。

JDK7以前,Java虚拟机层面对动态类型语言的支持一直都有所欠缺。主要表现在方法调用方面: 字节码的指令集中,4条方法调用指令的第一个参数都是被调用的方法的符号引用,在编译时就已经确定好。而动态类型语言只有在运行期才能确定方法的接收者。

3 java.lang.invoke包

3.1 方法句柄

方法句柄是一个强类型的,能被直接执行的引用。该引用可以指向常规的静态方法或者实例方法,也可以指向构造器或者字段。当指向字段时,方法句柄实则指向包含字段访问字节码的虚构方法,语义上等价于目标字段的getter或者setter方法。

方法句柄的类型(MethodType)是由所指向方法的参数类型以及返回类型组成的。它是用来确认方法句柄是否适配的唯一关键。当使用方法句柄时,我们并不关心方法句柄所指向方法的类名或者方法名。

3.1.1 方法句柄的使用

方法句柄包含两个重要的类,MethodHandle和MethodType。

MethodHandle调用时有两个方法invoke和invoeExact,后者要求参数类型与底层方法的参数完全匹配,前者则在有出入时做修改如包装类型。

MethodType,不可变对象,是对方法的一个映射,在lookup时也是通过它来寻找的。

public class MethodHandleDemo {

    public void sayWord(String word) {
        System.out.println("MethodHandleDemo:" + word);
    }

    public static class TempClass {
        public void sayWord(String word) {
            System.out.println("TempClass:" + word);
        }
    }

    public static void main(String[] args) throws Throwable {
        MethodType methodType = MethodType.methodType(void.class, String.class);
        MethodHandle sayWord1 = MethodHandles.lookup().findVirtual(MethodHandleDemo.class, "sayWord", methodType);
        sayWord1.invoke(MethodHandleDemo.class.newInstance(),"hello sayWord1");

        MethodHandle sayWord2 = MethodHandles.lookup().findVirtual(TempClass.class, "sayWord", methodType);
        sayWord2.invoke(TempClass.class.newInstance(),"sayWord2");
    }
}
/*
运行结果:
MethodHandleDemo:hello sayWord1
TempClass:sayWord2
 */

MethodHandle方法

字节码

描述

findStatic

invokestatic

调用静态方法

findSpecial

invokespecial

调用实例构造方法,私有方法,父类方法

findVirtual

invokevirtual

调用所有虚方法

findVirtual

invokeinterface

调用接口方法,会在运行时再确定一个实现此接口的对象

表 MethodHandle方法与字节码的对应

3.1.2 方法句柄与反射

/**
 * 反射demo
 */
public class ReflectDemo {

    public void sayWord(String word) {
        System.out.println("ReflectDemo:" + word);
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Class<?> aClass = Class.forName("day06.ReflectDemo");
        Method method = aClass.getMethod("sayWord", String.class);
        method.invoke(aClass.newInstance(),"hello reflect");
    }
}
/*
运行结果:
ReflectDemo:hello reflect
 */

MethodHandle在使用方法和效果上与Reflection有众多相似之处。它们也有以下这些区别:

1)两者本质都是在模拟方法调用,反射是模拟Java代码层次,MethodHandle是模拟字节码层次的方法调用。

2)反射是重量级,而MethodHandle是轻量级。

3.2 invokedynamic指令

每一处含有invokedynamic指令的位置都被称作“动态调用点”,其第一个参数是CONSTANT_InvokeDynamic_info常量。

类型

名称

含义

u1

tag

名称,值为InvokeDynamic

u2

bootstrap_method_attr_index

引导方法,有固定的参数,并且返回值规定是CallSite对象,这个对象代表了真正要执行的目标方法调用

u2

name_and_type_index

方法类型MethodType

表 CONSTANT_InvokeDynamic_info结构

public class DynamicDemo {
    public static void main(String[] args) {
        String[] array = {"2hello", "1java", "5other"};
        List<String> list = Arrays.asList(array);
        list.sort((o1, o2) -> o1.compareTo(o2));
/*        等效于:
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
 */
        System.out.println(list);
    }
}

图 上面代码的部份字节码

#7 后面的0只是作为占位,没其他用处。#0代码引导方法取BootstrapMethod属性表的第0项。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/549852.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MJ discord 添加应用配置

discord 添加机器人 https://discord.com/developers/applications 刷新token后显示&#xff0c;即机器人Token&#xff0c;后续配置到 mj.discord.bot-token 如图勾选后&#xff0c;打开url进行授权 选择Midjourney Bot所在的服务器 勾上这两个选项&#xff0c;点击 Save Cha…

图片转Excel表格,识别准确率的重要性

摘要&#xff1a;随着科技的不断发展&#xff0c;图片转Excel表格的应用越来越广泛。通过OCR技术实现图片转Excel表格&#xff0c;OCR识别准确率的提高对于信息录入的精度以及后续数据分析的可靠性具有非常重要的作用。本文探讨了影响OCR识别准确率的因素&#xff0c;并提出了提…

2.MATLAB篇——基本操作与矩阵输入

>> cos(((12345)^5)^0.5)ans -0.3623>> help sinsin - 参数的正弦&#xff0c;以弧度为单位此 MATLAB 函数 返回 X 的元素的正弦。sin 函数按元素处理数组。该函数同时接受实数和复数输入。 对于 X 的实数值&#xff0c;sin(X) 返回区间 [-1, 1] 内的实数值。 对于…

数据结构-单调栈2

这里是解了一个新的题目&#xff0c;然后对于有重复值的单调栈做了一些改进&#xff08;只适用于特殊题目&#xff09;&#xff1a;有重复值的单调栈不再使用ArrayList或者LinkedList&#xff0c;而是像无重复值的那样直接使用下标&#xff0c;这种方法能保证最终的正确性&…

Python图片转字符画,太好玩啦(46)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日主题 什么是字符画呢&#xff1f; 如何用Python将图片转为字符画&#xff1f; 我们先找一幅原画&#xff0c;比如它吧&…

亚马逊云科技 一周回顾 – 2022 年 7 月 18 日

上周&#xff0c;Amazon 峰会&#xff08;纽约&#xff09;在贾维茨中心线下举办&#xff0c;有数千名与会者以及 100 多家赞助商和合作伙伴参加。在主题演讲中&#xff0c;Amazon 首席开发人员倡导者 Martin Beeby 谈到了云基础设施的创新如何帮助客户适应挑战并抓住新的机遇。…

知识积累(1)

&#xff08;1&#xff09; 当您在Git中看到消息 "HEAD is now at 1343ccb FAB-17419 Fix off_chain_data sample error (#146)" 时&#xff0c;这是Git告知您当前所在的分支和最新的提交哈希。 这条消息通常出现在使用Git命令后&#xff0c;如git pull或git check…

【Java校招面试】实战面经(四)

目录 前言一、Http协议状态码301和302的区别二、Time Wait状态的作用是什么&#xff1f;三、ConcurrentHashMap在JDK1.7和JDK1.8的区别四、MySQL的优化&#xff1a;怎么优化SQL、用过MySQL的性能分析工具吗&#xff1f;五、反转数组的算法六、JDBC怎么使用的&#xff0c;什么是…

Redis三种模式——主从复制、哨兵模式、集群

目录 一、Redis模式二、Redis主从复制2.1 主从复制概述2.2 主从复制2.3 Redis主从复制过程2.4 搭建Redis主从复制2.4-1 环境部署2.4-2 安装Redis2.4-3 修改 Redis 配置文件&#xff08;Master节点操作&#xff09;2.4-4 修改 Redis 配置文件&#xff08;Slave节点操作&#xff…

『手撕 Mybatis 源码』03 - 解析映射配置文件

解析映射配置文件 SQL 映射文件只有很少的几个顶级元素&#xff08;按照定义顺序列出&#xff09; select 元素允许你配置很多属性来配置每条语句的行为细节 <selectid"select"parameterType"int"parameterMap"deprecated"resultType&quo…

Qt之界面 自定义标题栏、无边框、可移动、缩放

实现效果 注意&#xff1a;由于需要调用 Windows 上的头文件与库&#xff0c;所以不能跨平台&#xff0c;只支持 Windows 系统。如果想要跨平台&#xff0c;可以使用鼠标等事件实现&#xff0c;具体百度搜索参考下 自定义标题栏 titleBar.h #ifndef TITLEBAR_H #define TITL…

[Nacos] Nacos Client向Server发送注册请求和心跳请求 (二)

文章目录 1.Nacos Client的自动注册原理和实现2.Naocs Client向Server发送注册请求3.Nacos Client向Server发送心跳请求 Nacos Client的任务: 向Server发送注册请求, 向Server发送心跳请求, Client获取所有的服务, Client定时更新本地服务, Client获取要调用服务的提供者列表 …

Robot Dynamics Lecture Notes学习笔记之关节空间动力学控制

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 关节空间动力学 关节空间动力学控制关节阻抗调节重力补偿逆动力学控制 关节空间动力学控制 目前的工业机器人几乎完全依赖于关节位置控制的概念。它们建立在PID控制器的基础…

MySQL数据库期末实验报告(含实验步骤和实验数据)

MYSQL实验 实验步骤 1.创建数据库salesmanage 2.创建数据表&#xff1a;员工表&#xff0c;部门表&#xff0c;销售表&#xff1b; &#xff08;1&#xff09;员工表&#xff08;(员工号(CHAR)&#xff0c;员工姓名(CHAR)&#xff0c;性别(CHAR)&#xff0c;年龄(INT)&…

前端部署项目后nginx转发接口404(页面正常)

目录 1.前言 2. 场景复现&#xff1a; 3.问题的原因&#xff1a; 4.使用nginx一般要注意的小细节&#xff1a; 1. location / 写在下面&#xff0c;其他的转发如/v1写在上面​编辑 2.如何查看nginx转发请求到哪里了&#xff1f; 3.怎么写自己的前端路径&#xff1f; 5.使…

实验六 自动驾驶建模与仿真

【实验目的】 了解Matlab/Simulink软件环境&#xff0c;熟悉Simulink建模步骤&#xff1b;了解车辆运动控制的基本原理&#xff0c;学会简单的车辆运动控制建模及仿真&#xff1b;了解自动驾驶建模的基本过程&#xff0c;了解典型ADAS系统模型的应用特点。了解自动驾驶相关函数…

【SpringCloud组件——Nacos】

前置准备&#xff1a; 分别提供订单系统&#xff08;OrderService&#xff09;和用户系统&#xff08;UserService&#xff09;。订单系统主要负责订单相关信息的处理&#xff0c;用户系统主要负责用户相关信息的处理。 一、服务注册与发现 1.1、在父工程当中引入Nacos依赖 …

JavaScript实现输入数值判断是否为质数、合数的代码

以下为实现输入数值判断是否为质数、合数的程序代码和运行截图 目录 前言 一、输入数值判断是否为质数、合数 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.本博文代码可…

通讯录实现的需求分析和架构设计

本文实现的是通讯录产品的需求分析和架构设计&#xff0c;重点在于结构层次的设计&#xff0c;方便代码阅读和维护。 一、通讯录实现的需求分析 1、通讯录的功能清单 添加一个人员打印显示所有人员删除一个人员查找一个人员保存文件加载文件 2&#xff0c;数据存储信息 人员…

实际开发中一些实用的JS数据处理方法

写在开头 JavaScript 是一种脚本语言&#xff0c;最初是为了网页提供交互式前端功能而设计的&#xff0c;而现在&#xff0c;通过 Node.js&#xff0c;JavaScript 还可以用于编写服务器端代码。 JavaScript 具有动态性、基于原型的面向对象特性、弱类型、多范式、支持闭包执行…