1 包装类
引言
- 什么是包装类?
- 包装类其实就是8种基本数据类型对应的引用类型
- 为什么要提供包装类?
- Java为了实现一切皆对象,为8种基本类型提供了对应的引用类型
- 后面的集合和泛型其实也只能支持包装类,不支持基本类型
1.1 基本类型包装类
基本类型包装类的作用:将基本数据类型封装成对象 的好处在于可以在对象中定义更多的功能方法操作该数据
常用的操作之一:用于基本数据类型与字符串之间的转换
基本数据类型对应的包装类:
1.2 包装类的特有功能
1️⃣ 可以把基本类型的数据转成字符串类型(用处不大😕 )
🙋举个栗子:
public class Demo {
public static void main(String[] args) {
// 可以把基本类型的数据转成字符串类型(没啥用)
Integer a=23;
String rs=a.toString();
System.out.println(rs+666); // 23666
String rs1=Integer.toString(a);
System.out.println(rs1+666); // 23666
// 可以直接+字符串变字符串
String s = a + "666";
System.out.println(s); // 23666
}
}
2️⃣ 可以把字符串类型的数值转成真实的数据类型(真的很有用😋)
🙋举个栗子:
public class Demo {
public static void main(String[] args) {
// 使用valueOf创建包装类对象的两种方式
Integer i1 = Integer.valueOf(200);
Integer i2 = Integer.valueOf("200");
System.out.println(i1); // 200
System.out.println(i2); // 200
String a="23";
// 将字符串转成整数的两种方式:parseXxx、valueOf
// int i = Integer.parseInt(a);
Integer i = Integer.valueOf(a);
System.out.println(i+1); // 24
String b = "99.9";
// double v = Double.parseDouble(b);
Double v = Double.valueOf(b);
System.out.println(v+1); // 100.9
}
}
1.3 自动装箱 / 拆箱
- 自动装箱:把基本数据类型转换为对应的包装类类型,基本类型的数据和变量可以直接赋值给包装类型的变量
- 自动拆箱:把包装类类型转换为对应的基本数据类型,包装类型的数据和变量可以直接赋值给基本类型的变量
🙋举个栗子:
public static void main(String[] args) {
// 更为简单地获取Integer的方式
Integer i1 = 100;
// 对象 = 默认是一个基本数据类型
//jdk1.5的特性 --- 自动装箱
//装箱: 把一个基本数据类型 变量对应的包装类.
//自动: Java底层会帮我们自动的调用valueOf方法.
System.out.println(i1);
//jdk1.5的特性 --- 自动拆箱
//拆箱: 把一个包装类型 变成对应的基本数据类型
int i2 = i1;
System.out.println(i2);
Integer i3 = 100; //自动装箱机制
i3 += 200;//i3 = i3 + 200;
//会把i3这个对象变成基本数据类型100.
//100 + 200 = 300
//把基本数据类型300再次自动装箱变成Integer对象赋值给i3
System.out.println(i3);
//细节:null可赋值给引用类型,但是不可以赋给基本类型
// int a=null; // 报错
Integer i4 = null;
if(i4 != null){
i4 += 200;
System.out.println(i4);
}
❗️注意:
- 包装类的变量默认值可以是
null
- 在使用包装类类型的时候,如果做操作,最好先判断是否为
null
,推荐的是,只要是对象在使用前就必须进行不为null
的判断
练习:字符串中数据的处理
//需求:有一个字符串:“91 27 46 38 50”,把其中的每一个数存到int类型的数组中
//步骤:
//定义一个字符串
//把字符串中的数字数据存储到一个int类型的数组中
//遍历数组输出结果
public static void main(String[] args) {
String s = "91 27 46 38 50";
//获取字符串中的每一个数字.
String[] strArr = s.split(" ");
//创建一个int类型的数组.
int [] numberArr = new int[strArr.length];
//把strArr中的数据进行类型转换并存入到int数组中
for (int i = 0; i < strArr.length; i++) {
int number = Integer.parseInt(strArr[i]);
numberArr[i] = number;
}
//遍历int类型的数组
for (int i = 0; i < numberArr.length; i++) {
System.out.println(numberArr[i]);
}
}
2 泛型
2.1 泛型概述
不写泛型的弊端
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add(123);
Iterator it = list.iterator();
while(it.hasNext()){
String next = (String) it.next(); // 强制类型转换
int len = next.length();
System.out.println(len);
}
}
引入泛型,泛型是 JDK5 中引入的特性,它提供了编译时类型安全检测机制
- 泛型的好处
- 统一 数据类型
- 把运行时期的问题提前到了编译期间
- 避免了强制类型转换
- 泛型的定义格式
<类型>
:指定一种类型的格式,尖括号里面可以任意书写,一般只写一个字母。例如:<E>
、<T>
(不知道什么类型)<类型1,类型2…>
:指定多种类型的格式,多种类型之间用逗号隔开。例如:<E,T>
、<K,V>
集合体系的全部接口和实现类都是支持泛型的使用的。
泛型可以使用的地方:
- 类后面 —— 泛型类
- 方法申明上 —— 泛型方法
- 接口后面 —— 泛型接口
我们也可以自定义泛型
2.2 自定义泛型类
如果一个类的后面有 <E>
,表示这个类是一个泛型类,即定义类的同时定义了泛型的类
创建泛型类对象时,必须要给这个泛型确定具体的数据类型。
泛型类格式:
修饰符 class 类名<类型> { }
范例:
public class Test<T>{}
此处的 T
可以随便写为任意标识,常见的如T
、E
、K
、V
等形式的参数用于表示泛型
🙋举个栗子:
需求:模拟ArrayList
集合自定义一个集合MyArrayList
集合,完成添加和删除功能的泛型设计
import java.util.ArrayList;
public class MyArrayList<E> {
private ArrayList list=new ArrayList();
public void add(E e){
list.add(e);
}
public void remove(E e){
list.remove(e);
}
@Override
public String toString() {
return list.toString();
}
}
测试类
public class Test {
public static void main(String[] args) {
MyArrayList<String> list=new MyArrayList<>();
list.add("111");
list.add("222");
list.add("333");
list.remove("222");
System.out.println(list); // [111, 333]
MyArrayList<Integer> list2=new MyArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
list2.remove(1);
System.out.println(list2);// [2, 3]
}
}
泛型类的原理:
- 把出现泛型类变量的地方全部替换成传输的真实数据类型
2.3 自定义泛型方法
定义方法的同时定义了泛型的方法就是泛型方法,其作用是,方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性
定义泛型方法格式:
修饰符 <类型> 返回值类型 方法名(类型 变量名) { }
范例:
public <T> void show(T t){}
🙋举个栗子:
需求:给你任何一个类型的数组,都能返回它的内容,也就是实现Arrays.toString(数组)
的功能
public class Demo {
public static void main(String[] args) {
String[] names={"小明","拉拉","小子"};
printArray(names); // [小明,拉拉,小子]
Integer[] nums={12,34,67};
printArray(nums); // [12,34,67]
}
public static <T> void printArray(T[] arr) {
if (arr != null) {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]).append(i == arr.length - 1 ? "]" : ",");
}
System.out.println(sb);
} else {
System.out.println(arr);
}
}
}
🙋🙋 再举个栗子:
定义一个泛型方法,传递一个集合和四个元素,将元素添加到集合中并返回
import java.util.ArrayList;
public class Demo {
public static void main(String[] args) {
ArrayList<String> list1 = addElement(new ArrayList<String>(), "a", "b", "c", "d");
System.out.println(list1); // [a, b, c, d]
ArrayList<Integer> list2 = addElement(new ArrayList<Integer>(), 1, 2, 3, 4);
System.out.println(list2); // [1, 2, 3, 4]
}
public static <T> ArrayList<T> addElement(ArrayList<T> list, T t1, T t2, T t3, T t4) {
list.add(t1);
list.add(t2);
list.add(t3);
list.add(t4);
return list;
}
}
泛型方法的原理:
- 把出现泛型变量的地方全部替换成传输的真实数据类型
2.4 自定义泛型接口
使用了泛型定义的接口就是泛型接口,其作用是可以让实现类选择当前需要操作的数据类型
泛型接口格式:
修饰符 interface 接口名<类型> { }
范例:
public interface Generic<T>{}
🙋举个栗子:
需求:教务系统,提供一个接口,可约束一定要完成的数据(学生、老师)的增删改查操作
老师类
public class Teacher {
}
学生类
public class Student {
}
自定义泛型接口
public interface Data<E> {
void add(E e);
void delete(int id);
void update(E e);
void query(int id);
}
学生数据的增删改查
public class StudentData implements Data<Student>{
@Override
public void add(Student student) {
}
@Override
public void delete(int id) {
}
@Override
public void update(Student student) {
}
@Override
public void query(int id) {
}
}
老师数据的增删改查
public class TeacherData implements Data<Teacher>{
@Override
public void add(Teacher teacher) {
}
@Override
public void delete(int id) {
}
@Override
public void update(Teacher teacher) {
}
@Override
public void query(int id) {
}
}
泛型接口的使用方式:
- 实现类也不给具体的泛型
- 实现类确定具体的数据类型
泛型接口的原理:
- 实现类可以在实现接口的时候传入自己操作的数据类型,这样重写的方法都将是针对该类型的操作
2.5 类型通配符
E
、T
、K
、V
是在 定义 泛型的时候使用的
而 ?
可以在 使用 泛型的时候代表一切类型
类型通配符:<?>
ArrayList<?>
:表示元素类型未知的 ArrayList,它的元素可以匹配任何的类型- 但是并不能把元素添加到 ArrayList中了,获取出来的也是父类类型
类型通配符上限:<? extends 类型>
ArrayListList <? extends Number>
:它表示的类型是Number或者其子类型
类型通配符下限:<? super 类型>
ArrayListList <? super Number>
: 它表示的类型是Number或者其父类型
🙋举个栗子:
import java.util.ArrayList;
public class Demo {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Number> list3 = new ArrayList<>();
ArrayList<Object> list4 = new ArrayList<>();
method(list1);
method(list2);
method(list3);
method(list4);
getElement1(list1);
getElement1(list2);//报错
getElement1(list3);
getElement1(list4);//报错
getElement2(list1);//报错
getElement2(list2);//报错
getElement2(list3);
getElement2(list4);
}
// 泛型通配符: 此时的泛型?,可以是任意类型
public static void method(ArrayList<?> list) {
}
// 泛型的上限: 此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(ArrayList<? extends Number> list) {
}
// 泛型的下限: 此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(ArrayList<? super Number> list) {
}
}