java包装类简单认识泛型

news2024/11/24 20:46:16

1 包装类

Java 中,由于基本类型不是继承自 Object ,为了在泛型代码中可以支持基本类型, Java 给每个基本类型都对应了一个包装 类型。类中比如由属性/方法 使用比较方便

1.1 基本数据类型和对应的包装类  

1.2 装箱和拆箱

装包/装箱 : 基本数据类型=>包装类类型

拆箱/拆包 : 包装类型=>基本类型 

int i = 10;
// 装箱操作,新建一个 Integer 类型对象,将 i 的值放入对象的某个属性中
Integer ii = Integer.valueOf(i);
Integer ij = new Integer(i);   // 显示装箱 底层还是调用Integer.valueOf(i);

// 拆箱操作,将 Integer 对象中的值取出,放到一个基本数据类型中
int j = ii.intValue();  // 显示拆箱

1.3 自动装箱和自动拆箱  

可以看到在使用过程中,装箱和拆箱带来不少的代码量,所以为了减少开发者的负担, java 提供了自动机制
int i = 10;
Integer ii = i; // 自动装箱
Integer ij = (Integer)i; // 自动装箱

int j = ii; // 自动拆箱
int k = (int)ii; // 自动拆箱

阿里面试题

下列代码输出什么,为什么?
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b);
System.out.println(c == d);
}

答案: 

解释: 四行代码都是对应自动装箱操作, 而自动装箱操作底层还是调用Integer.valueOf(i);

cache 是一个提前开辟好的长度为256的数组,

cache[0] 存放的是 -128  (-128 + (- ( -128 ) ) = 0 )

cache[255]存放的是127 (127 + (- ( -128 ) ) = 0 )

也就是说当传入valueOf()的值范围在-128~127之间时, 返回的值都是实现设置好的,因此为true

但128超出范围, 必须new一个新值, 两个值在不同地址上因此只能是false 

2 什么是泛型

一般的类和方法,只能使用具体的类型 : 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。--- Java 编程思想》对泛型的介绍。
通俗讲,泛型: 就是适用于许多许多类型 从代码上讲,就是对类型实现了参数化。
可以认为传递的是类型

3 引出泛型

实现一个类,类中包含一个数组成员,使得数组中可以存放 任何类型 的数据,也可以根据成员方法返回数组中某个下标位置上的值?
思路:
  1. 我们以前学过的数组,只能存放指定类型的元素,例如:int[] array = new int[10]; String[] strs = new String[10];
  2. 所有类的父类,默认为Object类。数组是否可以创建为Object?
class MyArray {
    public Object[] objects = new Object[10];
    public Object getPos(int pos) {
        return objects[pos];
    }
    public void setVal(int pos,Object val) {
        objects[pos] = val;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        MyArray myArray = new MyArray();
        myArray.setVal(0,10);
        myArray.setVal(1,"hello");//字符串也可以存放
        String ret = myArray.getPos(1);//编译报错
        System.out.println(ret);
    }
}

编译报错地方  必须进行强制类型转化才能输出

String ret = (String)myArray.getPos(1);//编译报错
System.out.println(ret);
问题:以上代码实现后 发现
1. 任何类型数据都可以存放
2. 1 号下标本身就是字符串,但是确编译报错。必须进行强制类型转换
虽然在这种情况下,当前数组任何数据都可以存放,但是,更多情况下,我们还是希望他只能够持有一种数据类型。而不是同时持有这么多类型。所以,泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译 器去做检查。 此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。
/**
 * @param <T>  T是一个占位符,表示当前类是一个泛型类
 *
 */
class MyArray2<T> {
    //public Object[] objects = new Object[10];
    public T[] objects = (T[])new Object[10];
    //public T[] objects = new T[10];  //1

    public T getPos(int pos) {
        return objects[pos];
    }
    public void setVal(int pos,T val) {
        objects[pos] = val;
    }
}
public static void main2(String[] args) {
        MyArray<Integer> myArray1 = new MyArray<Integer>(); //2
        myArray1.setVal(0,48);
        myArray1.setVal(1,10);
        int val = myArray1.getPos(1); //3
        System.out.println(val);
        System.out.println("=======");
        MyArray<String> myArray2 = new MyArray<>();
        myArray2.setVal(0,"hello");
        myArray2.setVal(1,"bit");
        String ret = myArray2.getPos(1);
        System.out.println(ret);
    }
1.注释 1 处,不能 new 泛型类型的数组
        意味着: T [] ts = new T [ 5 ]; // 是不对的, 这样写会报错, 报错原因后边说
        因为泛型不能在运行中被识别, 只能在编译中才能被识别
        课件当中的代码:T[] array = (T[])new Object[10]; 是否就足够好,答案是未必的。这块问题一会儿介绍。

2. 注释 2 处,类型后加入 <Integer> 指定当前类型
3. 注释 3 处,不需要进行强制类型转换

泛型存在的意义:

  1. 在编译的时候, 帮我进行类型的检查
  2. 在编译的时候, 帮我进行类型的转换

运行的时候是没有泛型概念的, JVM当中是没有泛型的

4. 泛型是如何编译的 

4.1 擦除机制

那么,泛型到底是怎么编译的?这个问题,也是曾经的一个面试问题。泛型本质是一个非常难的语法,要理解好他 还是需要一定的时间打磨
在编译的过程当中,将所有的 T 替换为 Object 这种机制,我们称为: 擦除机制

Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息。  

4.2 提出问题:

4.2.1

为什么,public T[] objects = new T[10]; 是不对的,编译的时候,替换为Object,不是相当于:Object[] ts = new Object[5]吗?那既然会替换为Object为什么会报错呢?

答案: 因为Java数组语法上要求new后边必须跟一个具体类型

4.2.2 为什么T[] array = (T[])new Object[10 不够好

       我们提供一个getArray()方法来 实例化泛型类型数组
当我们运行getArray()方法时候就会报错,哪怕强转都会报错,  因为擦除机制编译时返回的是Object类型,
而Object类型无论如何都不能转为Integer具体类型
返回的 Object 数组里面,可能存放的是任何的数据类型,可能是 String ,可能是 Person ,运行的时
候,直接转给 Integer 类型的数组,编译器认为是不安全的

改进方法1: 用Object类型接收 

既然不允许强转Object  那就只能用Object类型接收 

class MyArray2<T> {
    //public Object[] objects = new Object[10];
    public T[] objects = (T[])new Object[10];
    //public T[] objects = new T[10];  //1

    public T getPos(int pos) {
        return objects[pos];
    }
    public void setVal(int pos,T val) {
        objects[pos] = val;
    }
    public Object[] getArray() {   // 也可以返回T[] 都一样最后也编译成Object[]
        return objects;
    }
}
public static void main2(String[] args) {
    MyArray<Integer> myArray1 = new MyArray<Integer>(); 
    Object[] integers = myArray1.gerArray();    
}

要是必须用具体Integer类型来接收呢? 更好的

改进方法2: 用反射创建指定类型的数组(了解即可)

改进方法1 myArray1内部创建过程中用是泛型类型数组, 而方法2内部创建过程中直接使用指定类型数组, 当然就不在需要因为擦除机制编译成Object类型了

class MyArray2<T> {
    //public Object[] objects = new Object[10];
    public T[] objects;
    //public T[] objects = new T[10];  //1

    /**
    * 通过反射创建,指定类型的数组
    * @param clazz
    * @param capacity
    */
    public MyArray(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    public T getPos(int pos) {
        return objects[pos];
    }
    public void setVal(int pos,T val) {
        objects[pos] = val;
    }
    public T[] getArray() {
        return objects;
    }
}
public static void main2(String[] args) {
    MyArray2<Integer> myArray2 = new MyArray2<>(Integer.class,10);
    Integer[] integers = myArray2.getArray();   
}

最佳方法

那在实际工作中通常只需要直接使用Object类型数组就可以了, 只需要注意一下setVal()时存放T类型就可以了, getPos()取数据时候强制类型转换就可以了, 当然也需要用Object类型数组接收getArray()结果

我们以后就用这个方式写代码 

 

5. 泛型的上界

问题引入 , 红色部分出现的原因?

答案:
<>当中必须存放引用数据类型, 而引用数据类型的数据之间比较大小必须通过 实现 Comparable 接口,并调用其中的compareTo()方法
但是泛型T在编译的时候被擦除成Object类型, 而Object类型并没有实现Comparable接口,因此出现报错
这个  extends Comparable <E > 就是为泛型添加一个上界, 避免被擦除成Object , 而是擦除成实现了 Comparable 接口的类型(即上界)
另外传入的泛型T 必须 实现Comparable 接口的类型(即上界类型)

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

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

相关文章

Linux使用docker安装elasticsearch-head

一、elasticsearch-head的安装启动 #下载镜像 docker pull alivv/elasticsearch-head #启动 docker run -d --name eshead -p 9100:9100 alivv/elasticsearch-head 查看日志 docker logs -f eshead 出现如下证明启动成功 浏览器访问9100端口&#xff0c;出现以下页面也说明…

java 线程安全问题 三种线程同步方案 线程通信(了解)

线程安全问题 线程安全问题指的是&#xff0c;多个线程同时操作同一个共享资源的时候&#xff0c;可能会出现业务安全问题。 下面代码演示上述问题&#xff0c;先定义一个共享的账户类&#xff1a; public class Account {private String cardId; // 卡号private double mone…

分布式版本控制工具——git

✅<1>主页&#xff1a;&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;Linux——git ☂️<3>开发环境&#xff1a;Centos7 &#x1f4ac;<4>前言&#xff1a;git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很…

路由缓存问题 | vue-router的导航守卫

路由缓存问题 带参路由&#xff0c;当参数发生变化时&#xff0c;相同的组件实例将被复用&#xff0c;组件的生命周期钩子不会被调用&#xff0c;导致数据无法更新。 两种解决方法&#xff1a; 1. 给 RouterView绑定key值&#xff0c;即 <RouterView :key"$route.ful…

SpringBoot原理-自动配置-原理分析-源码跟踪

自动配置原理 SpringBootApplication 该注解标识在SpringBoot项目的启动类上&#xff0c;是SpringBoot中最为重要的注解&#xff0c;该注解由三个部分组成。 SpringBootConfiguration&#xff1a;该注解与Configuration注解作用一样&#xff0c;用来声明当前类为一个配置类Comp…

解决SVN文件不显示绿色小钩图标问题

问题描述&#xff1a; 今天重新安装了SVN&#xff0c;发现从中央服务器拉取文件到本地仓库后&#xff0c;对应的文件没有绿色的小钩图标&#xff0c;于是查了一下解决方案&#xff0c;在这里总结一下。 解决方案一&#xff1a; 原因&#xff1a;状态缓存设置问题造成的。 在…

2024年java面试--mysql(1)

系列文章目录 2024年java面试&#xff08;一&#xff09;–spring篇2024年java面试&#xff08;二&#xff09;–spring篇2024年java面试&#xff08;三&#xff09;–spring篇2024年java面试&#xff08;四&#xff09;–spring篇2024年java面试–集合篇2024年java面试–redi…

Java设计模式-结构性设计模式(代理设计模式)

简介 为其他对象提供⼀种代理以控制对这个对象的访问&#xff0c;属于结构型模式。客户端并不直接调⽤实际的对象&#xff0c;⽽是通过调⽤代理&#xff0c;来间接的调⽤实际的对象应用场景 各⼤数码专营店&#xff0c;代理⼚商进⾏销售对应的产品&#xff0c;代理商持有真正的…

算法训练营day46|动态规划 part08:完全背包 (LeetCode 139. 单词拆分、多重背包理论基础)

文章目录 139. 单词拆分 (求排列方法)回溯思路分析背包思路分析代码实现思考总结 多重背包理论基础 139. 单词拆分 (求排列方法) 题目链接&#x1f525;&#x1f525; 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict&#xff0c;判定 s 是否可以被空格拆分为一个或多…

一文读懂LSTM及手写LSTM结构

torch.nn.LSTM是PyTorch中用于创建长短时记忆网络&#xff08;Long Short-Term Memory&#xff09;的类。LSTM是一种用于处理序列数据的循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;变体。 官方给出的LSTM API 文档 以下是 torch.nn.LSTM 的…

LORA项目源码解读

大模型fineturn技术中类似于核武器的LORA&#xff0c;简单而又高效。其理论基础为&#xff1a;在将通用大模型迁移到具体专业领域时&#xff0c;仅需要对其高维参数的低秩子空间进行更新。基于该朴素的逻辑&#xff0c;LORA降低大模型的fineturn门槛&#xff0c;模型训练时不需…

Redis-带你深入学习数据类型list

目录 1、list列表 2、list相关命令 2.1、添加相关命令&#xff1a;rpush、lpush、linsert 2.2、查找相关命令&#xff1a;lrange、lindex、llen 2.3、删除相关命令&#xff1a;lpop、rpop、lrem、ltrim 2.4、修改相关命令&#xff1a;lset 2.5、阻塞相关命令&#xff1a…

deepin V23通过flathub安装steam畅玩游戏

deepin V23缺少32位库&#xff0c;在星火商店安装的steam,打开报错&#xff0c;无法使用&#xff01; 通过flathub网站安装steam,可以正常使用&#xff0c;详细教程如下&#xff1a; flathub网址&#xff1a;主页 | Flathub 注意&#xff1a;flathub下载速度慢&#xff0c;只…

【笔试强训选择题】Day38.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01; 文章目录 前言一、Day…

ChatGPT实战与私有化大模型落地

文章目录 大模型现状baseline底座选择数据构造迁移方法评价思考 领域大模型训练技巧Tokenizer分布式深度学习数据并行管道并行向量并行分布式框架——Megatron-LM分布式深度学习框架——Colossal-AI分布式深度学习框架——DeepSpeedP-tuning 微调 资源消耗模型推理加速模型推理…

基于SSM的学院实验中心管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

从数据页的角度看 B+Tree

InnoDB 是如何存储数据的&#xff1f; MySQL支持多种存储引擎&#xff0c;不同的存储引擎&#xff0c;存储数据的方式也不相同&#xff0c;我们最常使用的是 InnoDB 存储引擎。 在数据库中的记录是按照行来存储的&#xff0c;但是数据库的读取并不是按照 [ 行] 为单位&#x…

MySQL进阶 —— 超详细操作演示!!!(上)

MySQL进阶 —— 超详细操作演示&#xff01;&#xff01;&#xff01;&#xff08;上&#xff09; 一、存储引擎1.1 MySQL 体系结构1.2 存储引擎介绍1.3 存储引擎特点1.4 存储引擎选择 二、索引2.1 索引概述2.2 索引结构2.3 索引分类2.4 索引语法2.5 SQL 性能分析2.6 索引使用2…

BUUCTF rip 1

使用linux的file命令查看基本信息 64位 使用IDA64位进行反编译 看到gets就肯定有栈溢出 能看到有一个 _system函数&#xff0c;改函数能执行系统命令 既然反编译有这个函数说明有地方调用了他 果然在一个fun函数中有调用&#xff0c;执行的命令是 /bin/sh 也就是一个后门函数&…

【C++ • STL • 力扣】详解string相关OJ

文章目录 1、仅仅翻转字母2、字符串中的第一个唯一字符3、字符串里最后一个单词的长度4、验证一个字符串是否是回文5、字符串相加总结 ヾ(๑╹◡╹)&#xff89;" 人总要为过去的懒惰而付出代价 ヾ(๑╹◡╹)&#xff89;" 1、仅仅翻转字母 力扣链接 代码1展示&…