包装类
基本数据类型和包装类是Java中处理数据的两种不同方式。
基本数据类型(Primitive Types):
Java的基本数据类型是直接存储数据的原始类型,包括以下8种类型:
byte:1字节,用于表示整数
short:2字节,用于表示整数
int:4字节,用于表示整数
long:8字节,用于表示长整数
float:4字节,用于表示单精度浮点数
double:8字节,用于表示双精度浮点数
char:2字节,用于表示字符
boolean:1位,用于表示布尔值(true或false)
包装类(Wrapper Classes):
为了方便在基本数据类型和对象之间进行转换,Java提供了对应的包装类。每个基本数据类型都有对应的包装类,命名规则是将首字母大写,例如:
Byte:对应byte
Short:对应short
Integer:对应int
Long:对应long
Float:对应float
Double:对应double
Character:对应char
Boolean:对应boolean
包装类提供了许多有用的方法来处理基本数据类型,例如进行转换、比较、解析等。它们还允许将基本数据类型作为对象使用,在集合类中存储和操作。
基本数据类型和包装类的区别:
- 存储方式:基本数据类型直接存储数据值,而包装类是将数据值封装在对象中。
- 空值表示:基本数据类型没有空值,但包装类可以表示空值通过null。
- 默认值:基本数据类型有各自的默认值(如0、0.0、false等),而包装类的默认值是null。
- 内存占用:基本数据类型占用的内存比包装类少,因为基本数据类型直接存储数据值,而包装类需要额外的空间用于存储对象的引用。
- 包装类提供了许多实用的方法来处理基本数据类型,例如类型转换、数学运算等,而基本数据类型没有这些方法。
在Java中,自动装箱(Autoboxing)和拆箱(Unboxing)机制允许基本数据类型和包装类之间的自动转换。这使得在需要使用对象的情况下可以直接使用基本数据类型,而无需手动进行类型转换。
装箱和拆箱
又称“显示装箱”。
int i = 10;
// 装箱操作,新建一个 Integer 类型对象,将 i 的值放入对象的某个属性中
Integer ii = Integer.valueOf(i);//是手动装箱
Integer ij = new Integer(i);//是手动装箱
// 拆箱操作,将 Integer 对象中的值取出,放到一个基本数据类型中
int j = ii.intValue();
Integer a = new Integer(10);
//显示拆箱 拆箱为自己指定的元素
int c = a.intValue();
System.out.println(c);
double d = a.doubleValue();
System.out.println(d);
自动装箱和自动拆箱
自动装箱,又称“隐式装箱”,是指在编译阶段,Java编译器会自动将基本类型转换为对应的包装类型,而不需要显式地调用构造函数来完成装箱操作。
nt i = 10;
Integer ii = i; // 自动装箱
Integer ij = (Integer)i; // 自动装箱
int j = ii; // 自动拆箱
int k = (int)ii; // 自动拆箱
严格来说,int k = (int)ii; 不是自动装箱,而是强制类型转换,将基本数据类型int转换为包装类型Integer。在这种情况下,需要注意i的值不能超出Integer类型的取值范围,否则会抛出NumberFormatException异常。
易错题:
1、下列代码输出什么,为什么?
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);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
这段代码主要是对Java中自动装箱的缓存机制进行测试。Java中对于byte、short、int类型的自动装箱,如果值在[-128, 127]之间,则会将其缓存起来,重复使用同一个对象。
在这段代码中,首先声明了两个Integer类型的变量a和b,并将它们赋值为127,然后声明了两个Integer类型的变量c和d,并将它们赋值为128。
在输出语句中,通过"=="运算符比较a和b、c和d的值是否相等,如果相等则输出true,否则输出false。
由于a和b的值都在[-128, 127]之间,因此它们会被缓存起来,重复使用同一个对象,所以a和b指向的是同一个对象,因此a==b的结果为true。
而c和d的值都不在[-128, 127]之间,因此它们不会被缓存起来,每次自动装箱时都会创建一个新的Integer对象,所以c和d指向的不同对象,因此c==d的结果为false。
因此,这段代码的输出结果为:
true
false
2、下列在Java语言中关于数据类型和包装类的说法,正确的是()
A.基本(简单)数据类型是包装类的简写形式,可以用包装类替代基本(简单)数据类型
B. long和double都占了64位(64bit)的存储空间。
C.默认的整数数据类型是int,默认的浮点数据类型是float。
D.和包装类一样,基本(简单)数据类型声明的变量中也具有静态方法,用来完成进制转化等
答案:B
A. 错误。基本数据类型和包装类是不同的。基本数据类型是直接存储数据的原始类型,而包装类是对基本数据类型进行封装的类。虽然在某些情况下,基本数据类型可以自动转换为对应的包装类,但它们并不是同一概念。
C.默认的浮点数据类型是double。
D. 错误。基本数据类型声明的变量不具有静态方法。只有包装类才有静态方法,用于提供一些辅助功能,如进制转换等。基本数据类型的变量只能访问与其对应的基本数据类型的功能和操作,无法调用静态方法。
泛型
不可以实例化泛型。
数组是很特殊的,不可以整体强制类型转换。
public Object[] array = new Object[10];
//public T[] array = new T[10]; 不允许 实例化一个泛型数组
//public T[] array = (T[])new Object[10];//这样写也不好!!
public T[] array = (T[])new Object[10];
public static void main3(String[] args) {
//泛型是如何编译的
MyArray<String> myArray = new MyArray<>();
myArray.set(0,"hello");
//String[] ret = (String) myArray.getArray();
//没意义,已经强制转换过了!本质它是一个 Object数组,啥都能放,你怎么确定放的就是字符串?
//String[] ret = myArray.getArray();也还是不行
Object[] ret = myArray.getArray();
System.out.println(Arrays.toString(ret));
}
public Object[] getArray() {
return array;
}
/* public T[] getArray() {
return array;
}*/
//那我就要有这个方法呢?
public T[] getArray() {
return (T[])array;
}
所以不管怎么样,返回什么,我们都用Object来接收。源码里面也是这么写的。
List
List是Java集合框架中的一个接口,它表示一个有序的、可重复的元素集合。List接口继承自Collection接口,并在其基础上添加了一些与索引相关的操作方法。
这是List 的官方文档:列表 (Java Platform SE 8 ) (oracle.com)
List的特点包括:
- 有序性:List中的元素按照它们被添加的顺序进行存储,并且可以通过索引访问和操作元素。每个元素都有一个与之关联的索引,从0开始递增。
- 可重复性:List中可以存储重复的元素,即同一个元素可以出现多次。
- 动态大小:List的大小是可变的,可以根据需要动态地添加或删除元素。
List接口提供了许多常用的方法,使我们能够对集合中的元素进行增加、删除、修改、查找等操作。一些常用的List实现类包括:
- ArrayList:基于数组实现的动态数组,支持快速随机访问,但插入和删除操作可能较慢。
- LinkedList:基于链表实现的双向链表,支持快速的插入和删除操作,但随机访问较慢。
- Vector:类似于ArrayList,但是是线程安全的,适用于多线程环境。
- Stack:基于Vector实现的栈数据结构,支持先入后出的操作。
List接口提供了一系列的方法,如:
添加元素:add、addAll
获取元素:get、indexOf、lastIndexOf
删除元素:remove、removeAll、clear
修改元素:set
遍历元素:forEach、iterator、listIterator
判断元素是否存在:contains、isEmpty
获取列表大小:size
截取子列表:subList
其他:sort、reverse、shuffle等
List的灵活性和功能丰富性使得它成为Java中常用的集合类型之一,可以方便地操作和管理有序的元素集合。无论是需要保持元素顺序、支持重复元素,还是进行索引操作,List都是一个很好的选择。
注意:在编程中,一般会使用 List<String> ad = new ArrayList<>(); 的形式来创建 ArrayList 对象,因为这样代码的可读性更好,而且灵活性更高。这是因为,List 接口是 ArrayList 类的一个父接口,通过使用 List 类型的引用来指向 ArrayList 对象,可以使代码更具有通用性。这样写的好处是,以后如果需要更改为其他类型的 List(如 LinkedList),只需要改变声明的时候的类型,而不需要修改实例化的代码。
虽然直接使用 ArrayList<String> ad = new ArrayList<>(); 也是可以的,但是不够灵活,不利于后期代码的维护和扩展。当然,使用 ArrayList<String> ad = new ArrayList<>(); 也有好处,它能够调用的方法更多。