黑马程序员-------笔记整理(知识拓展------自动拆装箱)

本文详细解释了Java中的自动装箱(Autoboxing)与自动拆箱(Auto-Unboxing)机制,包括其原理、具体应用、局限性和注意事项。通过实例展示了如何在Java代码中使用自动装箱与拆箱,以及它们在提高代码简洁性和性能优化方面的作用。

        装箱,拆箱可以让编译器来自动完成在基本类型和它们的包裹对象之间的转化工作,从而能够用一种更简单的方式,来避免同时存在两种类型所带来的一些麻烦。     

        在Java程序中,可以往一个容器类(无论是Collection还是Map)里直接放入一个对象;但是如果打算放入的是一个数字、字符或布尔值的话,就要先加入一个转换成对象的步骤。造成这种现象的原因是,在Java语言当中一直存在着两种非常不同的类型:

  •     “引用类型”(Reference Types),包括所有的类和接口。这些类型的数据被看作对象,所以可以用一个Object型的变量来保存。、
  •     “基本类型”(Primitive Types),包括:byte、short、int、long、float、double、char和boolean。这些类型的数据不是对象,因此也不能用Object型的变量来保存。

同时采用这样两种类型,可以得到一些性能方面的好处——因为基本类型的数据不是对象,所以创建得更快、占用的空间更少、收回它们占用的资源也更容易;但是,这样的做法同时也会造成一些编码方面的问题——例如,不能定义一个变量(或数组),让它既能保存基本类型的数据,又能保存引用类型的数据。

1) 自动封装(Autoboxing)

在J2SE 1.5之前,要使用以下语句才能将int包装为一个Integer对象:

Integer integer = new Integer(10);

在 J2SE 1.5之后提供了自动装箱的功能,就可以直接使用以下语句来打包基本数据类型:

Integer integer = 10;====》》隐式等于 new Integer(10)

在进行编译时,编译器再自动根据您写下的语句,判断是否进行自动装箱动作。在上例中integer参考的会是Integer类的实例。同样的动作可以适用于 boolean、byte、short、char、long、float、double等基本数据类型,分别会使用对应的打包类型(Wrapper Types)Boolean、Byte、Short、Character、Long、Float或Double。

例: 
public class AutoBoxDemo { 
    public static void main(String[] args) { 
        Integer data1 = 10; 
        Integer data2 = 20;  
       System.out.println(data1.doubleValue() / 3);// 转为double值再除以3 
        System.out.println(data1.compareTo(data2));// 进行两个值的比较 
    } 
} 
    

程序看来简洁了许多,data1与data2在运行时就是Integer的实例,可以直接进行对象操作。执行的结果如下:

3.3333333333333335 
    –1 
    

自动装箱运用的方法还可以如下:

 int i = 10; 
    Integer integer = i; 
    也可以使用更一般化的java.lang.Number类来自动装箱。例如: 
    Number number = 3.14f; 
    

3.14f会先被自动装箱为Float,然后指定给number

2) 自动拆箱(Auto-Unboxing)

从J2SE1.5开始可以自动装箱,也可以自动拆箱(unboxing),也就是将对象中的基本数据形态信息从对象中自动取出。例如下面这样写是可以的:

 
    Integer fooInteger = 10;    //自动装箱
    int fooPrimitive = fooInteger; //自动拆箱
    

fooInteger引用至自动装箱为Integer的实例后,如果被指定给一个int类型的变量fooPrimitive,则会自动变为int类型再指定给fooPrimitive。在运算时,也可以进行自动装箱与拆箱。例如:

 
    Integer i = 10; 
    System.out.println(i + 10); 
    System.out.println(i++); 
    

上例中会显示20与10,编译器会自动进行自动装箱与拆箱,也就是10会先被装箱,然后在i + 10时会先拆箱,进行加法运算;i++该行也是先拆箱再进行递增运算。再来看一个例子:

 
    Boolean boo = true; 
    System.out.println(boo && false);     
    

同样的boo原来是Boolean的实例,在进行AND运算时,会先将boo拆箱,再与false进行AND运算,结果会显示false。

1) 发生装箱(Autoboxing)的具体时机

主要有这么三种:

1) 把基本类型的数据赋给引用类型的变量时。例如把一个int型的数据赋给一个Integer型变量。

例:赋给引用类型的变量基本类型的数据 
    Integer i = 31415; 
    

2) 把基本类型的数据传给引用类型的参数时。例如给一个定义成Object的参数传递一个boolean型的数据。

例:传给引用类型的参数基本类型的数据 
    HashMap map = new HashMap(); 
    map.put(true, null); 
    

3)把基本类型的数据往引用类型上强制转化时。例如在一个long型的数据前面加上(Long)。

例:从基本类型的数据到引用类型上强制转化 
    System.out.println((Long) 27828L); 
    

4)装箱的局限

装箱的机制有一个局限——只能把基本类型的数据转换为它相对应的类以及父类。

类似这样的代码是不能工作的,尽管int型的数据完全可以用一个Long对象来表示:

例:不能同时进行自动向上转型和Autoboxing 
    int i = 27828; 
    System.out.println((Long) i);/* 编译时出错 */ 
    

这是因为这段代码实际上相当于:

例:装箱操作会在自动向上转型之前发生 
    int i = 27828; 
    System.out.println((Long) Integer.valueOf(i));/* 编译时出错 */ 
    

而Integer并不是Long的子类,所以这个转化无法进行。如果一定要进行这种操作,需要手工追加一次转型:

例:需要先强制向上转型,再作Boxing 
    int i = 27828; 
    System.out.println((Long)(long) i);  //先讲i转换为long类型,然后再将long类型装箱

发生拆箱(Auto-Unboxing)的具体时机

则主要有这么七种:

1) 把包裹类对象赋给基本类型的变量时。例如把一个Integer型的数据赋给一个int型变量。

例:赋给基本类型的变量包裹类对象 
    int i = new Integer(32); 
    

2) 把包裹类对象传给基本类型的参数时。例如给一个定义成boolean的参数传递一个Boolean型的数据。

例:传给基本类型的参数包裹类对象 
    JFrame frame = new JFrame("^_^"); 
    frame.setSize(320, 200); 
    frame.setVisible(new Boolean(true)); 
    

3)把包裹类对象往基本类型上强制转化时。例如在一个Long型的数据前面加上(long)。

例:从包裹类对象到基本类型的强制转化 
    Long l = new Long(31415L); 
    System.out.println((long) l); 
    

4)把包裹类对象当作运算符的操作数时。例如在两个Byte型的数据之间放上“+”号。

例:把包裹类对象当作运算符的操作数 
    Byte a = new Byte((byte) 1); 
    Byte b = new Byte((byte) -1); 
    System.out.println(((a++) << 2) + (~b));/* 输出“4” */ 
    System.out.println(a);/* 输出“2” */ 
    

5)用包裹类对象来指定数组的大小时。当然,从语义上说,这个对象的类型必须是Byte、Short、Integer或Character。

例:用包裹类对象来指定数组的大小 
    Character size = new Character(’★’);/* Unicode: 9733 */ 
    int[] integers = new int[size];/* 生成一个可放9733个int元素的数组 */ 
    

6)把包裹类对象在switch语句里使用时。当然,从语义上说,这个对象的类型必须是Byte、Short、Integer或Character。

例:在switch语句里使用包裹类对象 
    Character c = new Character(’a’); 
    switch (c) { 
        case ’a’: 
        case ’e’: 
        case ’i’: 
        case ’o’: 
        case ’u’: 
            System.out.println("A Vowel in English"); 
            break; 
        default: 
            System.out.println("Not A Vowel in English"); 
            break; 
    } 
    

7)把Boolean对象在if/for/while/do-while语句中作为条件表达式使用时。

例:把Boolean对象作为条件表达式 
    Boolean bool = new Boolean(Math.random() > 0.5); 
    if (bool) { 
        System.out.println("Aye!"); 
    } else { 
        System.out.println("Nay!"); 
    } 
    

拆箱的局限

Auto-Unboxing的机制则有这样一个局限——只能把包裹类对象往它们对应的基本类型(以及容纳范围更广的类型)上转化。

类似这样的代码是不能工作的,尽管32并未超出byte所能表示的范围:

例:不能同时进行Auto-Unboxing和强制向下转型 
    Integer i = new Integer(32); 
    System.out.println((byte) i);/* 编译时出错 */ 
    

这是因为编译器并不认可同时进行Auto-Unboxing和强制向下转型的操作,所以这个转化无法进行。如果一定要进行这种操作,需要手工补充一次转型:

例:需要先作Unboxing,再强制向下转型 
    Integer i = new Integer(32); 
    System.out.println((byte)(int) i); 
    

不过同时进行Auto-Unboxing和强制向上转型的操作是没有问题的,所以下面的代码工作得很正常:

例:可以同时进行Auto-Unboxing和强制向上转型 
    Integer i = new Integer(32); 
    System.out.println((double) i); 

其它不能自动转化的情况

除去强制类型转化时的限制之外,还有这样一些情况下不会发生Autoboxing/Auto-Unboxing:

1)基本类型的数组和包裹类数组之间不会自动转化。这样的代码完全不被编译器接受:

例:元素可以,容器不行 
    int[] ints = {1, 2, 3}; 
    Integer[] integers = ints;/* 编译时出错 */ 
    

2)不能对着基本类型的表达式来调用包裹类里的方法。这样的申请会被编译器彻底拒绝:

例:没有方法,就是没有方法 
    int i = 1; 
    byte b = i.byteValue();/* 编译时出错 */ 
    

null的转化问题

Java里的引用类型可以有一个特别的取值——“null”。试图对null进行Auto-Unboxing操作会导致一个“NullPointerException”。

例如这段代码就会在运行时抛出异常,尽管在编译期间会表现得非常正常:

例:表面上,只是普通的赋值 
    Integer i = null; 
    int j = i;/* 运行时错误 */ 
    

这是因为这段代码实际上相当于:

例:实际上,是在试图调用null的方法 
    Integer i = null; 
    int j = i.intValue();/* 运行时错误 */ 
    

而试图调用null的方法是一种不被虚拟机认可的行为。

归纳总结

借助Autoboxing/Auto-Unboxing机制,可以用一种更简单的方式,来解决同时存在两套类型系统而造成的一些不方便。不过,这种机制并没有解决所有的相关问题,有些工作还是需要靠手工操作来进行。另外,由于不恰当的使用这一机制会造成一些性能方面的负面影响,所以在使用的时候需要谨慎。



本项目构建于RASA开源架构之上,旨在实现一个具备多模态交互能力的智能对话系统。该系统的核心模块涵盖自然语言理解、语音转文本处理以及动态对话流程控制三个主要方面。 在自然语言理解层面,研究重点集中于增强连续对话中的用户目标判定效能,并运用深度神经网络技术提升关键信息提取的精确度。目标判定旨在解析用户话语背后的真实需求,从而生成恰当的反馈;信息提取则专注于从语音输入中析出具有特定意义的要素,例如个体名称、空间位置或时间节点等具体参数。深度神经网络的应用显著优化了这些功能的实现效果,相比经典算法,其能够解析更为复杂的语言结构,展现出更优的识别精度与更强的适应性。通过分层特征学习机制,这类模型可深入捕捉语言数据中隐含的语义关联。 语音转文本处理模块承担将音频信号转化为结构化文本的关键任务。该技术的持续演进大幅提高了人机语音交互的自然度与流畅性,使语音界面日益成为高效便捷的沟通渠道。 动态对话流程控制系统负责维持交互过程的连贯性与逻辑性,包括话轮转换、上下文关联维护以及基于情境的决策生成。该系统需具备处理各类非常规输入的能力,例如用户使用非规范表达或对系统指引产生歧义的情况。 本系统适用于多种实际应用场景,如客户服务支持、个性化事务协助及智能教学辅导等。通过准确识别用户需求并提供对应信息或操作响应,系统能够创造连贯顺畅的交互体验。借助深度学习的自适应特性,系统还可持续优化语言模式理解能力,逐步完善对新兴表达方式与用户偏好的适应机制。 在技术实施方面,RASA框架为系统开发提供了基础支撑。该框架专为构建对话式人工智能应用而设计,支持多语言环境并拥有活跃的技术社区。利用其内置工具集,开发者可高效实现复杂的对话逻辑设计与部署流程。 配套资料可能包含补充学习文档、实例分析报告或实践指导手册,有助于使用者深入掌握系统原理与应用方法。技术文档则详细说明了系统的安装步骤、参数配置及操作流程,确保用户能够顺利完成系统集成工作。项目主体代码及说明文件均存放于指定目录中,构成完整的解决方案体系。 总体而言,本项目整合了自然语言理解、语音信号处理与深度学习技术,致力于打造能够进行复杂对话管理、精准需求解析与高效信息提取的智能语音交互平台。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值