IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 一文带你深入了解Java擦除机制 -> 正文阅读

[Java知识库]一文带你深入了解Java擦除机制

前言

??大家好,我是小窝,今天来为大家介绍擦除机制在java中的应用。
说到擦除机制,我们不得不提Java中的泛型,因为擦除机制可以说就是为Java泛型而构造出来的。在这篇博客里我们先简单了解下泛型,关于泛型的详细知识,博主之后将会再写一篇博客带你领略泛型的妙处所在。🔔因为完全理解泛型得首先了解擦除机制,而初步认识现在就够理解了。

📖博客主页:海绵宝宝养的小窝
👏欢迎关注🔥点赞💓收藏?评论💬
??看前先三连🙉养成好习惯🙈
📅首发时间:🌴 2022.01.08 🌴
??生活不止眼前的苟且,还有诗和远方💫
🔑参考书籍:📚《Effective Java》、📚《深入理解Java虚拟机》
💨博主在校大学生,水平有限,如有错误请及时评论或私信告知我哟??
博主的码云地址,日常代码及博客代码会在上面
在这里插入图片描述
本文收录专栏📓《深入理解Java虚拟机》,大家可以多多订阅呀,毕竟是免费的 😉此时不白嫖何时白嫖 🎁

📌什么是擦除机制

🍊Java在编译后的字节码(.class)文件中是不包含泛型中的类型信息的,使用泛型的时候加上的类型参数,会在编译的时候被擦除,这个过程就叫做类型擦除机制。

🍺泛型

关于泛型,我们先来简单了解下。
??泛型可以理解为对类型的抽象。以前我们定义一个属性或者方法的时候,我们都会明确具体的类型,比如int、String、void等等,但泛型不同,泛型是一个参数化类型,即不明确类型,只有在具体调用对象的时候,才传递实际类型实参。指定了泛型参数的类型就是一个具体化了的类型。

🌰举个栗子:

    List arrayList = new ArrayList();
         arrayList.add("aaa");
         arrayList.add(100);
         for (int i = 0; i < arrayList.size(); i++) {
             String item = (String) arrayList.get(i);
             System.out.println("泛型测试,item = "+item);
         } 

毫无疑问,程序的运行结果会以崩溃结束
在这里插入图片描述
ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,在使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型就应运而生了。
我们将ArrayList的初始化改一下:
List<String> arrayList = new ArrayList();
那么编译器将会直接在编译阶段就报错提醒我们只能存放String类型的数据。
在这里插入图片描述
这个<>里括起来的就是一种参数化的类型(也就是泛型),例如String、Integer、Float等。记住要用包装类,不能用int、char等基础数据类型。

正如我们前面所说泛型的类型参数会在编译的时候擦除,怎么证明呢?很简单,但我们写了如下代码后:
👇


> public class Test {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<String,String>();
        map.put("三国演义","罗贯中");
        map.put("西游记","吴承恩");
        System.out.println(map.get("三国演义"));
        System.out.println(map.get("西游记"));
    }
}

因为泛型只在编译期就会被擦除,我们可以用反编译查看代码内的泛型是否被擦除。

我们使用命令反编译后:
👇在这里插入图片描述
发现泛型果然被擦除了,并且还将类型参数全都擦成了Object这个具体的类型。

为什么会被擦成object呢?
??原来,在JDK5.0之前,容器存储的对象都只有具有Java的通用类型:Object。单根继承结构意味着所有的东西都是Object类型,所以该容器可以存储任何东西(就像我们最开始写的代码一样,可以存,但取会报错),但是由于容器只存储Object,所以当将对象引入容器时,他必须被向上转型成Object。

📌擦除机制的过程

??擦除机制过程可以理解为将泛型类变成普通Java代码的过程。一般包括两个步骤:
🍊1:将所有的泛型参数用其最左边界(最顶级的父类型)类型替换,默认则是Object。
🍊2:擦除泛型

🌰举个栗子:

public class MyClass < T extends TestB > {
    private T object;
    public void setObject (T object){
       this.object = object;
    }
    public T getObject(){
       return object;
    }
    public static void main(String[] args) {
      MyClass<TestC> testCMyClass = new MyClass<TestC>();
      testCMyClass.setObject(new TestC());
      TestC testC = testCMyClass.getObject();
      System.out.println(testC);
    }
}

在擦除之后,你可以把它理解为
👇

public class MyClass  {
   private TestB object;
   public void setObject (TestB object){
      this.object = object;
   }
   public TestB getObject(){
      return object;
   }
   public static void main(String[] args) {
     MyClass testCMyClass = new MyClass();
     testCMyClass.setObject(new TestC());
     TestC testC = testCMyClass.getObject();
     System.out.println(testC);
   }
}

擦除机制过程真有这么简单?
我们再来看一个栗子🌰

public class Parent<T> {
    public Number get(T key){//number几乎包含了所有数据类型
        return 0;
    } 
}

class child1 extends Parent<String>{
    public Number get(String key){
        return 1;
    } 
}

class child2 extends Parent<String>{
    public Integer get(String key){
        return 2;
    }
}

我们知道有擦除机制在,上述代码的泛型都会被擦除,并且类型参数会被擦成Object,那么子类的重写方法的参数应该是Object的,但我们写成上述也并没有报错,这是怎么回事呢?

🍺桥接方法

看到标题你们应该猜出来了吧,没错,这是因为编译器为我们自动生成了桥接方法
那什么是桥接方法呢?

栗子🌰

public class Parent {
    public Number get(){//number几乎包含了所有数据类型
        return 0;
    }
}

class child1 extends Parent{
    public Number get(){
        return 1;
    } 
}

class child2 extends Parent {
    public Integer get(){
        return 2;
    } 
}

我们先将代码转换成非泛型来瞧瞧,通过反编译
🍊child1
在这里插入图片描述

🍊child2
在这里插入图片描述
原来如此,编译器为我们自动生成了桥接方法,为父类和子类之间架起了一座桥梁。
🔑那么同理,原来的我们也通过反编译查看:
🍊parent
在这里插入图片描述

🍊child1
在这里插入图片描述
🍊child2
在这里插入图片描述

??因为父类的T被擦成了object,所以编译器为我们先重写了父类的参数类型为object的方法,再通过该桥接方法区调用自己的重写的方法。

📌擦除机制带来的影响

上面也说了类型擦除机制的实现原理-类型擦除指的是通过类型参数合并,将泛型实例关联到同一份字节码上,在运行期间类型参数丢失。就单单只有一份字节码这个事上就会出现许多匪夷所思的问题。

🍊不能用同一个泛型类的实例区分方法签名

栗子🌰

public class Test {
    public void test(List<String> a){
        System.out.println("String");
    }
    public void test(List<Integer> b){
        System.out.println("Integer");
    }
}

??这样是不行的,因为List< String >和List< Integeer >在类型擦除后都变成了List,那么两个方法的签名就一模一样了,编译器就会直接报错。

🍊不能同时catch同一个泛型异常类的多个实例

??原理同上一条

💡还有许多都是因为擦除机制带来的问题,博主在此就不一一举例了,只需明白在使用泛型时要考虑到擦除机制带来的影响。

📌结语

这篇博客的分享就到此结束了。下一篇博主将就泛型来详细介绍。

如果觉得文章写的不错的话,可以给个三连不🙈谢谢支持了

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-01-11 23:51:22  更:2022-01-11 23:52:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:33:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码