先赞后看,养成习惯!!! ^ _ ^ ❤️ ❤️ ❤️
码字不易,大家的支持就是我坚持下去的动力,点赞后不要忘记关注我哦
📘 本系列文章为本人在学习路上遇到的问题和解决方法,在这里撰写成文是为了巩固知识和帮助其他友友。
个人主页 🔍: 加瓦糕手1
专栏链接 📁: 问题分析简介
如有错误,请您指正批评 ^ _ ^
1. 包装类
在Java中,由于基本类型不是继承自Object,为了在反省代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型。
1.1 基本数据类型和对应的包装类

1.2 装箱和拆箱
Java中自动实现了装箱和拆箱,也就是说可以用包装类进行基本数据运算。
java
int i = 10;
// 装箱操作,新建⼀个 Integer 类型对象,将 i 的值放⼊对象的某个属性中
Integer ii = Integer.valueOf(i);
Integer ij = new Integer(i);
// 拆箱操作,将 Integer 对象中的值取出,放到⼀个基本数据类型中
int j = ii.intValue();
下列代码输出什么,为什么?
java
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);
}
输出结果分别为true、false,因为比较引用类型的时候,比较的是内存地址,Java创建了一个Integer缓存池,范围为-128~127,返回的是同一个对象。
2. 什么是泛型
一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。
2.1 语法
java
class 泛型类名称<类型形参列表> {
//这里可以使用类参数
}
class ClassName<T1, T2, ..., Tn> {
}
java
class 泛型类名称<类型形参列表> extends 继承类/* 这⾥可以使⽤类型参数 */ {
// 这⾥可以使⽤类型参数
}
class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
// 可以只使⽤部分类型参数
}
代码示例:
java
class MyArray<T>{
public Object[] array=new Object[10];
public T getPos(int pos){
return (T)array[pos];
}
public void setVal(int pos,T val) {
this.array[pos] = val;
}
}
public class test {
public static void main(String[] args) {
MyArray<Integer> myArray=new MyArray<>();
myArray.setVal(0,0);
myArray.setVal(1,1);
Integer s=myArray.getPos(1);
System.out.println(s);
}
}
代码解释:
- 类名后的<T>代表占位符,表示当前类是一个泛型类。
3. 泛型类的使用
3.1 语法
java
泛型类<类型实参> 变量名; // 定义⼀个泛型类引⽤
new 泛型类<类型实参>(构造⽅法实参); // 实例化⼀个泛型类对象
3.2 示例
java
MyArray<Integer> list = new MyArray<Integer>();
注意:泛型只能接受类,所有的基本数据类型必须使用包装类。
3.3 泛型推导
当编译器可以根据上下文推导出类型实参时,可以省略类型实参的填写
java
MyArray<Integer> list = new MyArray<>(); // 可以推导出实例化需要的类型实参为
4. 泛型如何编译的
4.1 擦除机制
1.基本概念
在编译时,Java编译器会将泛型类型信息从代码中移除,这个过程就叫做类型擦除。
擦除后,泛型类型会被替换为其边界类型(通常是Object)或者是指定的类型。
2. 擦除过程
将泛型参数替换为其边界或Object。
在必要的地方插入类型转换以保持类型安全。
生成桥接方法以保持多态性。
3. 示例:
擦除前
java
class MyArray<T> {
public Object[] array = new Object[10];
public T getPos(int pos) {
return (T)this.array[pos];
}
public void setVal(int pos,T val) {
this.array[pos] = val;
}
}
擦除后
java
class MyArray {
public Object[] array = new Object[10];
public Object getPos(int pos) {
return this.array[pos];
}
public void setVal(int pos, Object val) {
this.array[pos] = val;
}
}
4.2 关于桥接方法
泛型类型擦除可能导致子类和父类方法的签名不一致。
为了维护Java的多态性,需要桥接方法来确保子类方法能够正确覆盖父类方法。
java
public class Node<T> {
T data;
public void setData(T data) {
this.data = data;
}
}
public class StringNode extends Node<String> {
@Override
public void setData(String data) {
super.setData(data);
}
}
类型擦除后代码变为
java
public class Node {
Object data;
public void setData(Object data) {
this.data = data;
}
}
public class StringNode extends Node {
public void setData(String data) {
super.setData(data);
}
}
此时StringNode的setData方法并没有真正覆盖父类的setData方法(参数类型不同)。为了解决这个问题,编译器会在StringNode中生成一个桥接方法:
java
// 编译器⽣成的桥接⽅法
public void setData(Object data) {
setData((String) data);
}
5. 泛型的上界
在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。
5.1 语法
java
class 泛型类名称<类型形参 extends 类型边界> {
...
}
5.2 示例
java
public class MyArray<E extends Number> {
...
}
只接受Number的子类型作为E的类型实参。
java
MyArray<Integer> l1; // 正常,因为 Integer 是 Number 的⼦类型
MyArray<String> l2; // 编译错误,因为 String 不是 Number 的⼦类型
5.3 复杂示例
java
public class MyArray<E extends Comparable<E>> {
...
}
E必须是实现了Comparable接口的。
6. 泛型方法
6.1 定义语法
java
⽅法限定符 <类型形参列表> 返回值类型 ⽅法名称(形参列表) { ... }
6.2 示例
java
public class Util {
//静态的泛型⽅法 需要在static后⽤<>声明泛型类型参数
public static <E> void swap(E[] array, int i, int j) {
E t = array[i];
array[i] = array[j];
array[j] = t;
}
}
6.3 使用示例-可以类型推导
java
Integer[] a = { ... };
swap(a, 0, 9);
String[] b = { ... };
swap(b, 0, 9);
6.4 使用示例-不适用类型推导
java
Integer[] a = { ... };
Util.<Integer>swap(a, 0, 9);
String[] b = { ... };
Util.<String>swap(b, 0, 9);
7. 通配符
?用于在泛型的使用,即为通配符
7.1 通配符解决什么问题
示例:
java
class Message<T>{
private T message;
public T getMessage(){
return message;
}
public void setMessage(T message){
this.message=message;
}
}
public class test2 {
public static void main(String[] args) {
Message<String> message=new Message<>();
message.setMessage("你好");
fun(message);
}
public static void fun(Message<String> message){
System.out.println(message.getMessage());
}
}
以上程序会带来新的问题,如果现在泛型的类型设置的不是String,而是Integer。
java
public static void main(String[] args) {
Message<Integer> message=new Message<>();
message.setMessage(99);
fun(message);
}
public static void fun(Message<String> message){
System.out.println(message.getMessage());
}
我们需要的解决方案:可以接收所有的泛型类型,但是又不能够让用户随意修改。这种情况就需要使用通配符"?"来处理。
范例:使用通配符
java
public static void main(String[] args) {
Message<Integer> message=new Message<>();
message.setMessage(99);
fun(message);
}
public static void fun(Message<?> message){
System.out.println(message.getMessage());
}
在"?"的基础上又产生了两个子通配符:
?extends类:设置通配符上限
?super类:设置通配符下限
7.2 通配符上界
语法:
java
<? extends 上界>
<? extends Number>//可以传⼊的实参类型是Number或者Number的⼦类

java
class Food{
}
class Fruit extends Food{
}
class Apple extends Fruit{
}
class Banana extends Fruit{
}
class Plate<T>{
private T data;
public T getData(){
return data;
}
public void setData(T data){
this.data=data;
}
}
public class TestDemo {
public static void main(String[] args) {
Plate<Apple> plate=new Plate<>();
plate.setData(new Apple());
fun(plate);
Plate<Banana> plate2 = new Plate<>() ;
plate2.setData(new Banana());
fun(plate2);
}
public static void fun(Plate<? extends Fruit> temp ){
System.out.println(temp.getData());
}
}
此时无法在fun函数中对temp进行添加元素,因为temp接收的是Fruit和他的子类,此时存储的元素应该是哪个子类无法确定。所以添加会报错!
7.3 通配符下界
语法
java
<? super 下界>
<? super Integer>//代表 可以传⼊的实参的类型是Integer或者Integer的⽗类类型

java
class Food{
}
class Fruit extends Food{
}
class Apple extends Fruit{
}
class Banana extends Fruit{
}
class Plate<T>{
private T data;
public T getData(){
return data;
}
public void setData(T data){
this.data=data;
}
}
public class TestDemo {
public static void main(String[] args) {
Plate<Fruit> plate=new Plate<>();
plate.setData(new Fruit());
fun(plate);
Plate<Food> plate2 = new Plate<>() ;
plate2.setData(new Food());
fun(plate2);
}
public static void fun(Plate<? super Fruit> temp ){
System.out.println(temp.getData());
}
}