包装类和泛型

简介: 包装类和泛型

包装类和泛型严格来说算得上是JavaSE的内容,为什么他们要放在数据集合中?

这和集合类有关,我们在集合类中将会用到大量的泛型和包装类。


1. 包装类


基本介绍


包装类(wrapper)是针对八大基本数据类型相应的引用类型。

既然我们叫他包装类,那么类中肯定有方法:

例如:Integer


dcbca590dcb34b458cf168cf126e323d.png


那么包装类与基本数据类型的根本区别就在于能够调用方法,与此同时体现了面向对象。

我们先来看看吧大基本类型对应的包装类:

基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

除了 Integer 和 Character, 其余基本类型的包装类都是首字母大写。


包装类和基本数据类型的转换

以int和Integer为例

1.jdk5前是手动装箱和拆箱的,装箱:基本类型 ——>包装类型。反之即为拆箱。

2.jdk5(含jdk5)是自动装箱和拆箱的。

3.自动装箱底层调用了valueOf方法。

public class demo {
    public static void main(String[] args) {
        int a = 10;
        Integer val =a;
    }
}

我们来看这段代码,它是如何进行自动拆箱和装箱的。

1. 先运行一遍程序,使之生成字节码文件

2.鼠标右键改Java文件,点击Explorer,如下图:


6b8640939b9346fc8b10e2e95b00f33b.png


点击后:


53c7a4fe47234854b3898720ac3f8080.png



返回上一级目录,找到进入out目录下:



4134a8622a0b4333b558e4656522435b.png


再在目录表中输入cmd进入终端:


68e21911ff024674ac3e78d23f8d5137.png


b17fec55bc8148678b36b356cd42ac67.png


 3.进入终端后输入指令进入反汇编:javap -c demo(你的.Java文件名)


e875d0fcb1024d6f87983779642d43c2.png


我们在反汇编中找到了valuOf()方法。

4. 在idea上选中Integer 按住ctrl + B 进入底层找到valuOf()方法


dd3d17c5f8ad4f34955bc314775c4e4d.png

传进来一个参数i ,返回时new 了一个Integer(i)。



e841ec188ec74ae8b058a02c555956b2.png

所以:


        int a = 10;
        //Integer val =a;//自动装箱
        Integer val = Integer.valueOf(a);//显示装箱
        Integer val2 = new Integer(a);//显示装箱


我们用这两种方法显示装箱。

我们来看看拆箱:


public static void main(String[] args) {
        Integer val =10;
        int a = val;
        System.out.println(a);
    }

b30d081a71a54976b24116244741eabc.png


我们找到intValue :

cced56428509457f98ac26fc9e993888.png

2. 泛型(Generic)


泛型的引出


泛型的主要目的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。

比如:在ArrayList(顺序链表下一章就会讲)中添加数据,我们利用之前学的去添加,并不能对添加的数据类型进行约束(不安全)。

例如:在ArrayList中添加多条狗,但是一不小心传递了一只猫,我们再对其进行遍历,调用foreach需要向下转型(转为Dog),这再运行就会报异常


不使用泛型的情况下:



bcf2086f6c69420c8f7f516c9fd92e88.png

使用泛型后:



69952da967354f9a91863462296523e7.png

它就自动检查想要添加进来的类型。


基本介绍


1. 泛型又称参数化类型,是jdk5.0以后出现的新特征,解决数据类型安全问题。

2. 再类声明或实例化时只要指定好需要的具体类型即可。

3.Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会发生类型转换异常,同时代码更加健壮整洁。

4.泛型的作用是:可以在类声明是通过一个标识符表示某个属性的类型,或者某个方法的放回类型或者参数类型。

例如:

public class Main {
    public static void main(String[] args) {
        Person<Integer> integerPerson = new Person<>(123);
        Person<String> bit = new Person<>("bit");
    }
}
class Person<E> {
    E s ;//E表示S的数据类型,该数据类型在定义Person时指定,
    // 即在编译期间确定E是什么类型
    public Person(E s) {
        this.s = s;
    }
    public E f() {
        return s;
    }
}


语法声明


class 泛型类名称<类型形参列表> {

// 这里可以使用类型参数

}

class 类<K,V> {

}

class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {

// 这里可以使用类型参数

}

class 类<K,V> extends 类<T1> {

// 可以只使用部分类型参数

}


interface 接口<T> {


}  

说明:

1. K、T、V不代表值,而是表示类型

2. 可以使用任意字母表示:

E 表示 Element

K 表示 Key

V 表示 Value

N 表示 Number

T 表示 Type

S, U, V 等等 - 第二、第三、第四个类型


3.类名后的 <T> 代表占位符,表示当前类是一个泛型类


泛型的使用细节和注意事项


1.interface 接口<T> { } 和public class HashSet<E>{ }

T和E只能是同类型的引用

2. 在给泛型指定具体类型后,可以传入该类型或其子类型

例:若指定类型为A类,又需要添加B类,可以B类先继承A类再添加

3.泛型的使用形式

方法1:

ArrayList<Integer> List = new ArrayList<Integer>();

在实际应用中我们一般这么写:

ArrayList<Integer> List = new ArrayList<>();

在我最开始学习面向对象的时候,看起来没有用到,其实默认是Object

Pig pig = new Pig();
//看起来啥都没有写,其实默认是<Objec>
//因为Objec 默认是所有类型的父类
//这也就是个裸类型


裸类型(Raw Type)(了解)


说明:

裸类型是一个泛型类但没有带着类型实参,例如 MyArrayList 就是一个裸类型


MyArray list = new MyArray();

注意: 我们不要自己去使用裸类型,裸类型是为了兼容老版本的 API 保留的机制

下面的类型擦除部分,我们也会讲到编译器是如何使用裸类型的。


泛型的编译


那么,泛型到底是怎么编译的?这个问题,也是曾经的一个面试问题。泛型本质是一个非常难的语法,要理解好他还是需要一定的时间打磨。

代码:以泛型数组为例:

public class demo {
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<Integer>();
    }
}
class MyArray<E> {
     public E[] obj = (E[])new Object[3];
    public E[] getObj() {
        return obj;
    }
    public void setObj(E[] obj) {
        this.obj = obj;
    }
}

我们进入终端,输入指令:javap -c  demo(.java文件名)


4865a97ed25d473e951c09ef44b72e46.png

简单讲解一下反汇编的代码:


8227a7fe08f84bc495b6cead2c4ac2a2.png

 那么在程序编译好了以后跑进JVM,就没有了E[ ] 的概念了,我们泛型是在编译时期才存在的,一但程序运行起来以后,就不存在泛型这个概念了。E[ ] 在编译完成以后,就被擦除为了Object。

泛型在编译期间只做两件事:

1. 存储数据时,帮我们进行自动类型检查

2. 获取元素时,帮我们完成自动类型转换

有关泛型擦除机制的文章截介绍:

Java泛型擦除机制之答疑解惑 - 知乎 (zhihu.com)


自定义泛型


介绍:

1. 一般由单个字母大写表示;

2. 可以有多个大写字母,如: <T,R,M>

3. 普通成员也可以使用泛型


4. 使用泛型的数组不可以初始化(实例化),也就是不可以直接new;因为数组在new时无法确定T的类型,也就无法准确的开辟空间大小。

5. 静态的方法(属性)不可以使用泛型,在类加载时,自定义泛型还未创建(在创建对象时创建),所以JVM无法识别自定义泛型


自定义泛型接口


基本语法:

interface 接口名<T,R> { }

注意细节:

1. 接口中,静态成员也不可以使用泛型(原理如上);

2.泛型接口的类型在继承或者实现接口是确定

3. 如不指定则为默认的Object

自定义泛型方法:

例:

class Car {
    public <T,R> void fly(T t, R r) {
        //实现的代码
    }
}


泛型的继承和通配符说明

1. 泛型不具备继承性

例如:

MyArray<Integer> myArray = new MyArray<Object>();


5c00ac4ca5784fb3aca68dab62332f8d.png


2. 如果需要对传入的类型变量做一定的约束,可以通过类型边界来约束;我们称之为泛型的上界

语法:


class 泛型类名称<类型形参 extends 类型边界> {

...

}

还可以使用通配符,通配符类型(?)

例如:

<? extends A>//表示支持A类以及A的子类

?的默认是实现是<? extends Object>,表示?是继承Object的任意类型。


3. 有泛型的上界,也就有泛型的下界


例如:

<? super A> // 表示支持A类以及A类的父类,并且不局限于A的直接父类


4. List<?>表示任意泛型类型均可接受

举个例子:

import java.util.ArrayList;
import java.util.List;
public class demo {
    public static void printCollection1(List<?> C) {
        for (Object object:C) {
            System.out.println(object);
        }
    }
    public static void printCollection2(List<? extends AA> C) {
        for (Object object:C) {
            System.out.println(object);
        }
    }
    public static void printCollection3(List<? super BB> C) {
        for (Object object:C) {
            System.out.println(object);
        }
    }
    public static void main(String[] args) {
        ArrayList<Object> list1 = new ArrayList<>();
        ArrayList<String> list2 = new ArrayList<>();
        ArrayList<AA> list3 = new ArrayList<>();
        ArrayList<BB> list4 = new ArrayList<>();
    }
}
class BB {
}
class AA extends BB {
}


我们的list1、list2、list3、list4均可以放入printCollection1()中;


我们的list3、list4均可以放入printCollection2()中;


我们的list1、list2均可以放入printCollection3()中;


相关文章
|
Linux Shell
在Linux中如何一次性运行多个命令?
在Linux中如何一次性运行多个命令?
1058 0
|
运维 数据挖掘 测试技术
开箱测评|如何将WGS分析成本降低30%,效能提升40% 【内含Sentieon免费攻略】
以全基因组测序(WGS)场景为例,Sentieon Genomics 工具和 Memory Machine TM Cloud的组合方案,可以使运行时间减少 40%、同时实现成本减少 34%。
614 0
开箱测评|如何将WGS分析成本降低30%,效能提升40% 【内含Sentieon免费攻略】
|
11月前
|
机器学习/深度学习 数据处理
大语言模型中的归一化技术:LayerNorm与RMSNorm的深入研究
本文分析了大规模Transformer架构(如LLama)中归一化技术的关键作用,重点探讨了LayerNorm被RMSNorm替代的原因。归一化通过调整数据量纲保持分布形态不变,提升计算稳定性和收敛速度。LayerNorm通过均值和方差归一化确保数值稳定,适用于序列模型;而RMSNorm仅使用均方根归一化,省略均值计算,降低计算成本并缓解梯度消失问题。RMSNorm在深层网络中表现出更高的训练稳定性和效率,为复杂模型性能提升做出重要贡献。
2572 14
大语言模型中的归一化技术:LayerNorm与RMSNorm的深入研究
|
12月前
|
机器学习/深度学习 人工智能 自然语言处理
Java+机器学习基础:打造AI学习基础
随着人工智能(AI)技术的飞速发展,越来越多的开发者开始探索如何将AI技术应用到实际业务场景中。Java作为一种强大的编程语言,不仅在企业级应用开发中占据重要地位,在AI领域也展现出了巨大的潜力。本文将通过模拟一个AI应用,从背景历史、业务场景、优缺点、底层原理等方面,介绍如何使用Java结合机器学习技术来打造一个AI学习的基础Demo。
589 18
|
存储 Kubernetes 监控
在K8S中,集群可以做哪些优化?
在K8S中,集群可以做哪些优化?
|
机器学习/深度学习 人工智能 Cloud Native
在AI师傅(AI-Shifu.com)学习通义灵码的旅程
在这个数字化时代,编程技能愈发重要。通过AI师傅平台,我接触并学习了阿里云推出的通义灵码。从初识到深入学习,我系统掌握了云计算基础、云原生技术、数据库管理和大数据与人工智能等方面的知识。通过实践项目,我不仅巩固了理论,还提升了实际操作能力。通义灵码的易用性和强大功能,让我对云计算有了全新认识。感谢AI师傅提供的学习机会,推荐大家参与征文活动,共同分享学习成果。
|
关系型数据库 MySQL 数据库
实时计算 Flink版操作报错合集之sqlserver mysql都用的胖包,sqlserver的成功了,mysql报这个错如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
834 6
|
编解码 前端开发 JavaScript
拓宽你的 前端知识:掌握JS、HTML、CSS那些少有人知的应用技巧
这篇文章的主题是拓宽你的 前端知识:掌握JS、HTML、CSS那些少有人知的应用技巧,帮你了解、熟悉一些鲜为人知的知识点,它或许在你的工作中不会那么有用,但是能够丰富你的阅历,在面试中脱颖而出。
|
Linux Scala 开发者
Scala 多版本下载指南
Scala 多版本下载指南
1368 1
|
安全 Java 开发者
ConcurrentHashMap详解
ConcurrentHashMap详解