Java包装类泛型编程
Java包装类&泛型编程
个人主页:顾漂亮
文章专栏:Java数据结构
文章目录
- Java包装类&泛型编程
- 1.包装类
- 1.1装箱和拆箱
- 1.2面试题 -- java中`Integer`类的缓存机制
- **`Integer` 的缓存机制**
- **`==` 比较的是对象引用**
- 2.泛型编程
- 2.1什么是泛型
- 2.2泛型代码示例
- 2.3泛型类的使用
- 2.4泛型如何编译
- 2.5泛型的上界
- 3.通配符
- 3.1 `? extends 类` : 设置通配符上界
- 3.2`? super 类` : 设置通配符下界
- 3.3 总结
1.包装类
- 在Java中,由于基本类型不是继承自
Object
,为了在泛型代码中可以支持基本类型,Java给每一个基本类型对应了一个包装类型 - 除了
Integer
和Charater
,其余基本类型的包装类都是首字母大写
1.1装箱和拆箱
public static void main(String[] args) {
int a = 10;
Integer b = a;//自动装箱
Integer c = (Integer) a;//显示装箱
int d = b;//自动拆箱
int e = c;//显示拆箱
}
1.2面试题 – java中Integer
类的缓存机制
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a);//127
System.out.println(b);//127
System.out.println(c);//128
System.out.println(d);//128
System.out.println(a == b);//true
System.out.println(c == d);//false
System.out.println(c.equals(d));//true
}
Integer
的缓存机制
Java 对 Integer
类有一个缓存机制,范围为 -128
到 127
。当你通过自动装箱(如 Integer a = 127;
)创建 Integer
对象时:
- 如果值在
-128
到127
之间,Java 会从缓存中返回同一个Integer
对象。 - 如果值超出这个范围(如
128
),Java 会创建一个新的Integer
对象。
在你的代码中:
Integer c = 128;
和Integer d = 128;
的值都超出了缓存范围(128 > 127
),因此c
和d
是两个不同的Integer
对象。
==
比较的是对象引用
在 Java 中,==
用于比较两个对象的引用(即内存地址),而不是对象的值。由于 c
和 d
是两个不同的对象,它们的引用(内存地址)不同,因此 c == d
的结果是 false
。
c != d
是因为128
超出了Integer
的缓存范围,导致c
和d
是两个不同的对象。==
比较的是对象引用(内存地址),而不是值。- 如果需要比较值,请使用
equals
方法。
2.泛型编程
2.1什么是泛型
一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的
代码,这种刻板的限制对代码的束缚就会很大。----- 来源**《Java编程思想》**对泛型的介绍。
泛型是在JDK1.5引入的新的语法,通俗讲,泛型:就是适用于许多许多类型。从代码上讲,就是对类型实现了参数
化。
2.2泛型代码示例
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;
}
}
public class TestDemo {
public 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);
}
}
- 上述代码,看似是可以存放任何类型的数据,但是我们很难用一种类型去接受其中的数据。所以,更多情况下,我们希望的是只能持有一种数据类型
将上述代码改为泛型类:
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;
}
}
public class TestDemo {
public void main(String[] args) {
MyArray myArray = new MyArray();
myArray.setVal(0,10);
myArray.setVal(1,12);//编译时,自动进行类型转换
myArray.setVal(2,"ghr");//字符串也可以存放
System.out.println(myArray.getPos(2));
}
}
注释:
- 类名后的代表占位符,表示当前类是一个泛型类
- 规范:
- E表示Element
- K表示Key
- V表示Value
- N表示Number
- T表示Type
2.3泛型类的使用
LinkedList<Integer> list1 = new LinkedList<>();
ArrayList<String> list2 = new ArrayList<>();
注意:
-
泛型只能接受类,所有的基本数据类型必须使用包装类,其中传入的参数必须全部是
Object
的子类 -
上述代码第二个
<>
可以不填写类,编译器可以推导出实例化需要的类型实参为Integer
-
泛型的优点:数据类型参数化,编译时自动进行类型检查和转换
2.4泛型如何编译
-
在编译的过程当中,将所有的T替换为Object这种机制,我们称为:**擦除机制**
-
Java的泛型机制是在编译级别实现的,编译器生成的字节码在运行期间并不包含泛型的类型信息
2.5泛型的上界
示例1:
class Test<E extends Number> {
}
没有指定类型边界E,可以默认视为E extends Object
示例2:
class Test<E extends Comparable<E>> {
}
E必须是实现了**Comparable
**接口的
3.通配符
?
用在泛型中,即通配符
//使用示例
class Message<T> {
private T message ;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class TestDemo {
public static void main(String[] args) {
Message<Integer> message = new Message<>() ;
message.setMessage(99);
fun(message);//编译出错,只能传入String类型参数
}
public static void fun(Message<String> temp){
System.out.println(temp.getMessage());
}
}
//解决方案
class Message<T> {
private T message ;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class TestDemo {
public static void main(String[] args) {
Message<String> message = new Message<>() ;
message.setMessage("我爱学习编程!!");
message.setMessage(99);
fun(message);
}
public static void fun(Message<?> temp){//将String替换为通配符 ?
System.out.println(temp.getMessage());
}
}
注意:通配符的使用场景:可以接受所有泛型类型,但是又不能够让用户随意修改。这种情况就需要使用通配符来解决问题。
3.1 ? extends 类
: 设置通配符上界
class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Banana extends Fruit {
}
class Message<T> { // 设置泛型
private T message ;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
public class TestDemo {
public static void main(String[] args) {
Message<Apple> message = new Message<>() ;
message.setMessage(new Apple());
fun(message);
Message<Banana> message2 = new Message<>() ;
message2.setMessage(new Banana());
fun(message2);
}
// 此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以无法修改
public static void fun(Message<? extends Fruit> temp){
//temp.setMessage(new Banana()); //仍然无法修改!
//temp.setMessage(new Apple()); //仍然无法修改!
System.out.println(temp.getMessage());
}
}
3.2? super 类
: 设置通配符下界
class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Plate<T> {
private T plate ;
public T getPlate() {
return plate;
}
public void setPlate(T plate) {
this.plate = plate;
}
}
public class TestDemo {
public static void main(String[] args) {
Plate<Fruit> plate1 = new Plate<>();
plate1.setPlate(new Fruit());
fun(plate1);
Plate<Food> plate2 = new Plate<>();
plate2.setPlate(new Food());
fun(plate2);
}
public static void fun(Plate<? super Fruit> temp){
// 此时可以修改!!添加的是Fruit 或者Fruit的子类
temp.setPlate(new Apple());//这个是Fruit的子类
temp.setPlate(new Fruit());//这个是Fruit的本身
//Fruit fruit = temp.getPlate(); 不能接收,这里无法确定是哪个父类
System.out.println(temp.getPlate());//只能直接输出
}
}
3.3 总结
通配符类型 | 语法 | 使用场景 | 特点 |
---|---|---|---|
上界通配符 | <? extends T> | 接受 T 或其子类的集合 | 只能读取,不能写入(除了 null ) |
下界通配符 | <? super T> | 接受 T 或其父类的集合 | 可以写入 T 或其子类的对象,读取为 Object |
无界通配符 | <?> | 接受任意类型的集合 | 只能读取为 Object ,不能写入(除了 null ) |