11 常用类和泛型
约 6541 字大约 22 分钟
2024-09-01
12.1 包装类
包装类(Wrapper):针对 八种基本数据类型 相应的 引用类型
有了类的特点,就可以调用类中的方法
| 基本数据类型 | 包装类 | 父类 |
|---|---|---|
| boolean | Boolean | Object |
| char | Character | Object |
| int | Integer | Number |
| float | Float | Number |
| double | Double | Number |
| long | Long | Number |
| short | Short | Number |
| byte | Byte | Number |
| void | Void | Object |
12.1.1 装箱和拆箱
手动装箱和拆箱(JDK 5 以前)
int n1 = 100; Integer integer = new Integer(n1); // 手动装箱 Integer integer2 = Integer.valueOf(n1); // 手动装箱 int i = integer.intValue(); // 手动拆箱JAVA自动装箱和拆箱(JDK 5 以后)
n2 = 200; Integer integer3 = n2; // 自动装箱 int j = integer3; // 自动拆箱JAVA虽然可以自动装箱、拆箱,但使用 == 直接比较两个包装类时,仍然是比较其地址。以下比较通常会失败:
Integer ia = 1000; Integer ib = 1000; System.out.print(ia == ib); // falseJAVA但,Java 实现仍有可能使其成立。Byte、Boolean 以及 Short、Integer 中 [-128, 127] 间的值已被包装到固定的对象中。对他们的比较可以成功。
Integer ia = 127; Integer ib = 127; System.out.print(ia == ib); // trueJAVA由此可见,使用 == 直接比较两个包装类会带来不确定性。尽量使用 equals 方法对包装类进行比较。
装箱与拆箱是 编译器 的工作。在生成可执行的字节码文件时,编译器已经插入了必要的方法调用。
12.1.2 包装类和 String 的相互转换
包装类转
String:>Integer integer = 100; >String str1 = integer + ""; //方法1(自动拆箱) >String str2 = integer.toString(); //方法2(toString方法) >String str3 = String.valueOf(integer); //方法3(自动拆箱)JAVAString转包装类:String str4 = "100"; Integer integer2 = Integer.parseInt(str4); //方法1(自动装箱) Integer integer3 = new Integer(str4); //方法2(构造器)JAVA
12.1.3 包装类的常用方法
Integer.MIN_VALUE:返回最大值Double.MAX_VALUE:返回最小值byteValue()、doubleValue()、floatValue()、intValue()、longValue()按各种基本数据类型返回该对象的值
Character.isDigit(int):判断是不是数字Character.isLetter(int):判断是不是字母Character.isUpperCase(int):判断是不是大写字母Character.isLowerCase(int):判断是不是小写字母Characher.isWhitespace(int):判断是不是空格Character.toUpperCase(int):转成大写字母Character.toLowerCase(int):转成小写字母Integer.parseInt(string):将 String 内容转为 intDouble.parseDouble(string)Integer.toBinaryString(int):将数字转为 2 进制表示的字符串Integer.toHexString(int):将数字转为 16 进制表示的字符串Integer.toOctalString(int):将数字转为 8 进制表示的字符串特别地,浮点数类型的包装类只有转成 16 进制的方法。而 Short、Byte 及其他包装类无此方法
int Integer.bitCount(i int):统计指定数字的二进制格式中 1 的数量
12.1.4 strictfp 关键字
由于不同处理器对于浮点数寄存采取不同策略(有些处理器使用 64 位寄存 double,有些则是 80 位),对于浮点数的运算在不同平台上可能出现不同结果。
使用 strictfp 关键字标记的方法或类中,所有指令都会使用严格统一的浮点数运算。
比如,把 main 方法标记为 strictfp
public static strictfp void main(String[] args) {
double ᓚᘏᗢ = 1 / 13.97;
System.out.println(ᓚᘏᗢ);
}JAVA12.2 String 类
String对象用于保存字符串,也就是一组字符序列字符串常量对象是用双引号扩起的字符序列。例如
"你好"字符串的字符使用 Unicode 字符编码。一个字符(不论字母汉字)占 2 字节
常用构造器:
String str1 = new String();String str2 = new String(String original);String str3 = new String(char[] a);String str4 = new String(char[] a, int startIndex, int count);这句意思是:
char[]从startIndex起的count个字符
String实现了接口Serializable和Comparable,可以 串行化和 比较大小串行化:即,可以被网络传输,也能保存到文件
String是final类,不能被继承String有属性private final char[] value;用于存放字符串内容。value是final属性。其在栈中的地址不能修改,但堆中的内容可以修改。
12.2.1 String 构造方法
直接指定
String str1 = "哈哈哈"; JAVA该方法:先从常量池看是否有
"哈哈哈"数据空间。有的场合,指向那个空间;否则重新创建然后指向。这个方法,
str1指向 常量池中的地址。构造器
String str2 = new String("嘿嘿嘿"); JAVA该方法:先在堆中创建空间,里面维护一个
value属性,指向 或 创建后指向 常量池的"嘿嘿嘿"空间。这个方法,
str2指向 堆中的地址
12.2.2 字符串的特性
常量相加,看的是池
String str1 = "aa" + "bb"; //常量相加,看的是池 JAVA上例由于构造器自身优化,相当于
String str1 = "aabb";变量相加,是在堆中
String a = "aa"; String b = "bb"; String str2 = a + b; //变量相加,是在堆中JAVA上例的底层是如下代码
StringBuilder sb = new StringBuilder(); sb.append(a); sb.append(b); str2 = sb.toString(); //sb.toString():return new String(value, 0, count);JAVA
12.2.3 String 的常用方法
以下方法不需死记硬背,手熟自然牢记
boolean equals(String s):区分大小写,判断内容是否相等boolean equalsIgnoreCase(String s):判断内容是否相等(忽略大小写)boolean empty():返回是否为空int charAt(int index):获取某索引处的字符(代码单元)。必须用
char c = str.charAt(15);,不能用char c = str[15];int codePointAt(int index)int length():获取字符(代码单元)的个数—— 代码单元,见 2.6.2.4 字符本质与编码表
IntStream codePoints():返回字符串中全部码点构成的流long codePoints().count():返回真正长度(码点数量)—— 流,见 27.4 Stream API)
int indexOf(String str):获取字符(串)在字符串中第一次出现的索引。如果找不到,返回 -1int indexOf(int char)参数也可以传入一个 int。由于自动类型转换的存在,也能填入 charint indexOf(String str, int index):从 index 处(包含)开始查找指定字符(串)int lastIndexOf(String str):获取字符在字符串中最后一次出现的索引。如果找不到,返回 -1String substring(int start, int end):返回截取指定范围[start, end)的 新 字符串String substring(int index):截取 index(包含)之后的部分String trim():返回去前后空格的新字符串String toUperCase():返回字母全部转为大写的新字符串String toLowerCase():返回字母全部转为小写的新字符串String concat(String another):返回拼接字符串String replace(char oldChar, char newChar):替换字符串中的元素String str1 = "Foolish cultists"; String str2 = str1.replace("cultists", "believers"); //str1不变,str2为改变的值JAVAString[] split(String regex):分割字符串。对于某些分割字符,我们需要转义
String str1 = "aaa,bbb,ccc"; String[] strs1 = str1.split(","); //这个场合,strs = {"aaa", "bbb", "ccc"};4 String str2 = "aaa\bbb\ccc"; String[] strs2 = str2.split("\\"); //"\" 是特殊字符,需要转义为 "\\"JAVAint compareTo(String another):按照字典顺序比较两个字符串(的大小)。返回出现第一处不同的字符的编号差。前面字符相同,长度不同的场合,返回那个长度差。
String str1 = "ccc"; String str2 = "ca"; String str3 = "ccc111abc"; int n1 = str1.compareTo(str2); //此时 n1 = 'c' - 'a' = 2 int n2 = str1.compareTo(str3); //此时 n2 = str1,length - str3.length = -6 int n3 = str1.compareTo(str1); //此时 n3 = 0JAVAchar[] toCharArray():转换成字符数组byte[] getBytes():字符串转为字节数组String String.format(String format, Object... args):(静态方法)格式字符串String name = "Roin"; String age = "1M"; String state = "computer"; String formatStr = "I am %s, I am %s old, I am a %s"; String str = String.format(formatStr, name, age, state); //其中 %s 是占位符。此时,str = "I am Roin, I am 1M old, I am a computer"; //%s 表示字符串替换;%d 表示整数替换;#.2f 表示小数(四舍五入保留2位)替换;%c 表示字符替换JAVAString join(deli, ele...):拼接字符串(ele...),以deli间隔。boolean startsWith(str):测试 str 是否为当前字符串的前缀String repeat(int n):返回该字符串重复 n 次的结果
12.3 StringBuffer 类
java.lang.StringBuffer代表可变的字符序列。可以对字符串内容进行增删。很多方法和
String相同,但StringBuffer是可变长度。同时,StringBuffer是一个容器
StringBuffer的直接父类是AbstractStringBufferStringBuffer实现了Serialiazable,可以串行化- 在父类中,
AbstractStringBuffer有属性char[] value不是final StringBuffer是一个final类,不能被继承
String 对比 StringBuffer
String保存字符串常量,其中的值不能更改。每次更新实际上是更改地址,效率较低StringBuffer保存字符串变量,里面的值可以更改。每次更新是更新内容,不用每次更新地址。
12.3.1 StringBuffer 构造方法
无参构造
StringBuffer strb1 = new StringBuffer(); JAVA创造一个 16 位容量的空
StringBuffer传入字符串构造
String str1 = "abcabc"; StringBuffer strb2 = new StringBuffer(str1);JAVA(上例)创造一个 str1.length + 16 容量的
StringBuffer指定容量构造
StringBuffer strb3 = new StringBuffer(3); JAVA(上例)创造一个 3 容量的空
StringBuffer
12.3.2 String 和 StringBuffer的转换
转
StringBufferString str1 = "abcabc"; StringBuffer strb1 = new StringBuffer(str1); //方法1(构造器) StringBuffer strb1 = new StringBuffer(); strb1 = strb1.append(str1); //方法2(先空再append)JAVA转
StringString str2 = strb1.toString(); //方法1(toString) String str3 = new String(strb1); //方法2(构造器)JAVA
12.3.3 StringBuffer 的常用方法
append(char c):增加append(String s)参数也能是字符串特别的,
append(null);的场合,等同于append("null");delete(start, end):删减 [start, end) 的内容replace(start, end, string):将 start 与 end 间的内容替换为 stringindexOf:查找指定字符串第一次出现时的索引。没找到的场合返回 -1insert:在指定索引位置之前插入指定字符串length():返回字符长度capacity():返回当前的容量String 类对象分配内存时,按照对象中所含字符个数等量分配。
StringBuffer 类对象分配内存时,除去字符所占空间外,会另加 16 字符大小的缓冲区。
对于
length()方法,返回的是字符串长度。对于capacity()方法,返回的是 字符串 + 缓冲区 的大小。
12.4 StringBuilder 类
一个可变的字符序列。此类提供一个与
StringBuffer兼容的 API,但不保证同步(有线程安全问题)。该类被设计成StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先使用该类。因为在大多数实现中,它比起StringBuffer要快。在
StringBuilder是的主要操作是append和insert方法。可以重载这些方法,以接受任意类型的数据。
StringBuilder也继承了AbstractStringBufferStringBuilder也实现了Serialiazable,可以串行化- 仍然是在父类中有属性
char[] value,而且不是final StringBuilder也是一个final类,不能被继承StringBuilder的方法,没有做互斥的处理(没有synchronize),故而存在线程安全问题
12.4.1 String、StringBuffer、StringBuilder 的对比
StringBuilder和StringBuffer类似,均代表可变字符序列,而且方法也一样String:不可变字符序列,效率低,但复用率高StringBuffer:可变字符序列,效率较高,线程安全StringBuilder:可变字符序列,效率最高,存在线程安全问题String为何效率低:String str1 = "aa"; //创建了一个字符串 for(int n = 0; n < 100; n++){ str1 += "bb"; //这里,原先的字符串被丢弃,创建新字符串 } //多次执行后,大量副本字符串留在内存中 //导致效率降低,也会影响程序性能JAVA如上,对
String大量修改的场合,不要使用String
12.5 Math 类
Math.multiplyExact(int n1, int n2):进行乘法运算,返回运算结果通常的乘法
n1 * n2在结果大于那个数据类型存储上限时,可能返回错误的值。使用此方法,结果大于那个数据类型存储上限时,会抛出异常
Math.addExact(int n1, int n2):加法Math.subtractExact(int n1, int n2):减法Math.incrementExact(int n1):自增Math.decrementExact(int n1):自减Math.negateExact(int n1, int n2):改变符号Math.abs(n):求绝对值,返回 |n1|Math.pow(n, i):求幂,返回 n3 ^ iMath.ceil(n):向上取整,返回 >= n3 的最小整数(转成 double)Math.floor(n):向下取整,返回 <=n4 的最小整数(转成 double)Math.floorMod(int n1, int n2):返回 n1 除以 n2 的余数n1 % n2的场合,返回的可能是负数,而不是数学意义上的余数Math.round(n):四舍五入,相当于Math.floor(n5 + 0.5)Math.sqrt(n):求开方。负数的场合,返回NaNMath.random():返回一个 [0, 1) 区间的随机小数Math.sin(n):正弦函数Math.cos(n):余弦函数Math.tan(n)、Math.atan(n)、Math.atan2(n)要注意,上述方法传入的参数是 弧度值。
要得到一个角度的弧度值,应使用:
Math.toRadians(n)Math.exp(n):e 的 n 次幂Math.log10(n):10 为底的对数Math.log():自然对数Math.PI:圆周率的近似值Math.E:e 的近似值
12.6 Arrays 类
Arrays.toString():返回数组的字符串形式int[] nums = {0, 1, 33}; String str = Array.toString(nums); //此时,str = "[0, 1, 33]"JAVA特别的,输入为 null 时返回 “null”
Arrays.sort(arr):排序因为数组是引用类型,使用 sort 排序后,会直接影响到实参。
默认(自然排序)从小到大排序。
Arrays.sort(arr, Comparator c):按照传入的比较器决定排序方法Integer[] nums; ... Comparator<Integer, Integer> c = new Comparator<Integer, Integer>(){ @Override public int compare(Integer o1, Integer o2){ return n2 - n1; // 这个场合,变成从大到小排序 } } Arrays.sort(nums, c);JAVAArrays.binarySearch(array, num):通过二分搜索法查找。前提是必须先排序。找不到的场合,返回 - (low + 1)。即,其应该在的位置的负值
Integer[] nums2 = {-10, -5, -2, 0, 4, 5, 9}; int index = Arrays.binarySearch(nums2, 7); // 此时 index = -7 // 如果 7 存在,应该在第 7 个位置JAVAArrays.copyOf(arr, n):从arr中,复制 n 个元素(成为新的数组)。n > arr.length 的场合,在多余的位置添加
null。n < 0 的场合,抛出异常。该方法的底层使用的是
System.arraycopyArrays.fill(arr, o):用 o 填充num的所有元素。Arrays.equals(arr1, arr2):比较两个数组元素是否完全一致(true/false)Arrays.asList(a, b, c, d):将输入数据转成一个List集合
12.7 System 类
System.exit(0):退出当前程序。0 表示一个状态,正常状态是 0System.arraycopy(arr, 0, newArr, 0 ,3):复制数组元素。上例是:arr 自下标 0 起开始,向 newArr 自下标 0 开始,依次拷贝 3 个值
这个方法比较适合底层调用。我们一般使用
Arrays.copyOf来做System.currentTimeMillis:返回当前时间距离 1970 - 1 - 1 的毫秒数System.gc:运行垃圾回收机制
12.8 BigInteger 和 BigDecimal 类
BigInteger:适合保存更大的整数
BigDecimal:适合保存精度更大的浮点数
//用引号把大数变成字符串
BigInteger bigNum = new BigInteger("100000000000000000000000");JAVA构造方法:
new BigInteger(String intStr):通过一个字符串构建大数BigInteger BigInteger.valueOf(1):通过静态方法,让整数类型转成大数
另外,在对 BigInteger 和 BigDecimal 进行加减乘除的时候,需要使用对应方法
不能直接用 + - * /
常用方法:
BigInteger add(BigInteger):加法运算。返回新的大数BigInteger subtract(BigInteger):减法BigInteger multiply(BigInteger):乘法BigInteger divide(BigInteger):除法运算该方法可能抛出异常。因为可能产生是无限长度小数。
解决方法(保留分子精度):
bigDecimal.divide(bD3, BigDecimal.ROUND_CELLING)一些常量:
BigInteger.ONE、BigInteger.ZERO、BigInteger.TEN分别是 1、0、10one 就是英文的 1,zero 就是英文的 0……这个大家都懂的吧?
12.9 日期类
12.9.1 第一代日期类
Date:精确到毫秒,代表特定瞬间。这里的是 java.util.Date
SimpleDateFormat:格式和解析日期的类
Date d1 = new Date();:调用默认无参构造器,获取当前系统时间。默认输出日期格式是国外的格式,因此通常需要进行格式转换
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH.mm.ss"); String dateFormated = sdf.(d1); //日期转成指定格式。JAVA通过指定毫秒数得到时间:
Date d2 = new Date(10000000000); JAVA把一个格式化的字符串转成对应的 Date:
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日 HH点mm分 E"); Date d3 = sdf2.parse("2021年12月22日 00点03分 星期三");JAVA这个场合,给定的字符串格式应和
sdf2格式相同,否则会抛出异常
12.9.2 第二代日期类
Calendar:构造器是私有的,要通过 getInstance 方法获取实例
Calendar 是一个抽象类,其构造器私有
Calendar c1 = Calendar.genInstance(); //获取实例的方法 JAVA提供大量方法和字段提供给程序员使用
c1.get(Calendar.YEAR):获取年份数c1.get(Calendar.MONTH):获取月份数特别的,实际月份是 返回值 +1。因为 Calendar 的月份是从 0 开始编号的
c1.get(Calendar.DAY_OF_MONTH):获取日数c1.get(Calendar.HOUR):获取小时数(12 小时制)c1.get(Calendar.HOUR_OF_DATE):获取小时数(24 小时制)c1.get(Calendar.MINUTE):获取分钟数c1.get(Calendar.SECOND):获取秒数
Calendar 没有专门的格式化方法,需要程序员自己组合来显示
12.9.3 第三代日期类
JDK 1.0 加入的 Date 在 JDK 1.1 加入 Calendar 后已被弃用
然而,Calendar 也存在不足:
- 可变性:像日期和实际这样的类应该是不可改变的
- 偏移性:年份都是从 1900 年开始,月份都是从 0 开始
- 格式化:只对 Date 有用,对 Calendar 没用
- 其他问题:如不能保证线程安全,不能处理闰秒(每隔 2 天多 1 秒)等
于是,在 JDK 8 加入了以下新日期类:
- LocalDate:只包含 日期(年月日),可以获取 日期字段
- LocalTime:只包含 时间(时分秒),可以获取 时间字段
- LocalDateTime:包含 日期 + 时间,可以获取 日期 + 时间字段
- DateTimeFormatter:格式化日期
- Instant:时间戳
使用
now()方法返回当前时间的对象LocalDateTime ldt = LocalDateTime.now(); //获取当前时间 JAVA获取各字段方法:
ldt.getYear();:获取年份数ldt.getMonth();:获取月份数(英文)ldt.getMonthValue();:获取月份数(数字)ldt.getDayOfMonth();:获取日数LocalDateTime ldt2 = ldt.plusDays(100);:获取 ldt 时间 100 天后的时间实例LocalDateTime ldt3 = ldt.minusHours(100);:获取 ldt 时间 100 小时前的时间实例…
格式化日期:
DateTimeFormatter dtf = new DateTimeFormatter("yyyy.MM.dd HH.mm.ss"); String date = dtf.format(ldt); //获取格式化字符串JAVAInstant和Date类似- 获取当前时间戳:
Instant instant = Instant.now(); - 转换为
Date:Date date = Date.form(instant); - 由
Date转换:Instant instant = date.toInstant;
- 获取当前时间戳:
12 泛型
泛型(generic):又称 参数化类型。是 JDK 5 出现的新特性。解决数据类型的安全性问题。
在类声明或实例化时只要制定好需要的具体类型即可。
举例:
Properties<Person> prop = new Properties<Person>();注意
上例表示存放到 prop 中的必须是 Person 类型。如果编译器发现添加类型不符合要求,即报错。
遍历时,直接取出 Person 而非 Object
编译时,检查添加元素的类型。可以保证如果编译时没发出警告,运行就不会产生 ClassCastException 异常。提高了安全性,使代码更加简洁、健壮。
也减少了向下转型的次数,提高了效率。
案例 1
需要在 for 增强循环中如果要对特定集合内的对象的属性进行遍历
//没有引入泛型需要这样:
/*
Set set = new HashSet();
for(Objecto o: set){//只能定义为Object类型
Stu stu = (Stu) o;//每次循环都进行一次类型转换,效率低.
System.out.prinln(stu.getName);
}
*/
//如果在创建集合时这样做(添加类型参数):
Set<Stu> set = new HashSet<Stu>();
for (Stu stu :set) {//在这里原本只能放Object类型的stu,现在引入泛型后可以指定类型了,不用向下转型了.
System.out.println(stu.getName());
}案例 2
//案例2
//迭代器,在没有引出泛型的代码如下,在右边还是要强制转型.因为返回的是个Object类型的对象,泛型后则直接返回一个指定类型的对象,所以无需强转,直接接收.
while (iterator.hasNext()) {
Map.Entry e = (Map.Entry)iterator.next();//右边强转类型
Stu stu = (Stu) e.getValue();//右边强转类型
System.out.println(stu.getName()+stu.getAge());
}
//引入后(添加<Map.Entry<k,v>>并指定类型),直接可以省去向下转型的操作
Set<Map.Entry<String,Stu>> entrySet = hashMap.entrySet();
Iterator<Map.Entry<String,Stu>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
Map.Entry<String, Stu> next = iterator.next();//无需转类型
Stu stu = next.getValue();//无需转类型
System.out.println(stu.getName()+stu.getAge());
}- 泛型的作用是:可以在类声明是通过一个标识表示类中某个属性的类型,或某个方法返回值的类型,或参数类型。
class P<E> {
E e; //E 表示 e 的数据类型,在定义 P类 时指定。在编译期间即确认类型
public P(E e){ //可作为参数类型
this.e = e;
}
public E doSth(){ //可作为返回类型
return this.e;
}
}实例化时指定 E 的类型,编译时上例所有 E 会被编译器替换为那个指定类型
使用方法:
声明泛型:
interface InterfaceName<T> {...} class ClassName<A, B, C, D> {...}上例 T、A、B、C、D 不是值,而是类型。可以用任意字母代替
实例化泛型:
List<String> strList = new ArrayList<String>(); Iterator<Integer> iterator = vector.interator<Integer>();类名后面指定类型参数的值
注意细节:
泛型只能是引用类型
指定泛型具体类型后,可以传入该类型或其子类类型
在实际开发中往往简写泛型
List<String> strList = new ArrayList<>();编译器会进行类型推断,右边 < > 内容可以省略
实例化不写泛型的场合,相当于默认泛型为
ObjectArrayList arrayList = new ArrayList();//等价于ArrayList<Object> arrayList = new ArrayList<>();可以传入任意类型
自定义泛型类 · 接口:
class Name<A, B...> {...} //泛型标识符 可有多个,一般是单个大写字母表示普通成员可以使用泛型(属性、方法)
泛型类的类型,是在创建对象时确定的。
因此:静态方法中不能使用类的泛型,但可以定义为我泛型方法;使用泛型的数组,也不能初始化。
public static void m1(T a){};//错误用法,静态方法是和类相关的,在类加载时对象还没有创建。而泛型在类创建对象的时候才会指定。T[] ts = new T[3]//错误用法,不允许初始化。创建对象时不指定的场合,默认 Object。建议还是写上
<Object>,大气,上档次自定义泛型接口
interface Name<T, R...> {...}泛型接口,其泛型在 继承接口 或 实现接口 时确定。
//继承接口: interface A extends Name<String,Intager>{...}//指定了泛型 //实现接口: class B implements Name<String,Intager>{...}//指定了泛型
自定义泛型方法:
修饰符 <T, R...> 返回类型 方法名(形参) {...}可以定义在普通类中,也可以定义在泛型类中
//在泛型中 class F<T,R>{ public<U,M> void eat(U u,M m){...}//调用时传入的类型指定了U,M }当泛型方法被调用时,类型会根据传入的数据确定
以下场合
Class C<T> { public void cMethord(T t){...} }
注意!!!没有 < >,不是泛型方法,而是使用了泛型的普通方法
泛型继承:
泛型不具有继承性
<?>:支持任意泛型类型<? extends A>:支持 A 及 A 的子类,规定了泛型的上限(后续还可以增加传入的对象,只要继承了 A 类),只有 A 和 A 的子类可以传入public static void printCollection(List<? extend A> c){...}//一个打印list集合数据的方法,List指定了只能传入A和A的子类。<? super A>:支持 A 及 A 的父类,不限于直接父类,规定了泛型的下限(固定住了),只有 A 和 A 的父类可以使用泛型。
课后练习
public class GenericExercise02 {
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("tom", 20000, new MyDate(1980,12,11)));
employees.add(new Employee("jack", 12000, new MyDate(2001,12,12)));
employees.add(new Employee("tom", 50000, new MyDate(1980,12,10)));
System.out.println("employees=" + employees);
employees.sort(new Comparator<Employee>() {
@Override
public int compare(Employee emp1, Employee emp2) {
//先按照name排序,如果name相同,则按生日日期的先后排序。【即:定制排序】
//先对传入的参数进行验证
if(!(emp1 instanceof Employee && emp2 instanceof Employee)) {
System.out.println("类型不正确..");
return 0;
}
//比较name
int i = emp1.getName().compareTo(emp2.getName());
if(i != 0) {
return i;
}
//下面是对birthday的比较,因此,我们最好把这个比较,放在MyDate类完成
//封装后,将来可维护性和复用性,就大大增强.
return emp1.getBirthday().compareTo(emp2.getBirthday());
}
});
System.out.println("==对雇员进行排序==");
System.out.println(employees);
}
}
/**
* 定义Employee类
* 1) 该类包含:private成员变量name,sal,birthday,其中 birthday 为 MyDate 类的对象;
* 2) 为每一个属性定义 getter, setter 方法;
* 3) 重写 toString 方法输出 name, sal, birthday
* 4) MyDate类包含: private成员变量month,day,year;并为每一个属性定义 getter, setter 方法;
* 5) 创建该类的 3 个对象,并把这些对象放入 ArrayList 集合中(ArrayList 需使用泛型来定义),对集合中的元素进行排序,并遍历输出:
*
* 排序方式: 调用ArrayList 的 sort 方法 ,
* 传入 Comparator对象[使用泛型],先按照name排序,如果name相同,则按生日日期的先后排序。【即:定制排序】
* 有一定难度 15min , 比较经典 泛型使用案例 GenericExercise02.java
*/public class MyDate implements Comparable<MyDate>{
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
@Override
public String toString() {
return "MyDate{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
@Override
public int compareTo(MyDate o) { //把对year-month-day比较放在这里
int yearMinus = year - o.getYear();
if(yearMinus != 0) {
return yearMinus;
}
//如果year相同,就比较month
int monthMinus = month - o.getMonth();
if(monthMinus != 0) {
return monthMinus;
}
//如果year 和 month
return day - o.getDay();
}
} public List<T> list(){
List<T> list = new ArrayList<>();
// 方法1:
// Set<Map.Entry<String,T>> set = this.map.entrySet();
// Iterator<Map.Entry<String,T>> iterator = set.iterator();
// while (iterator.hasNext()) {
// Map.Entry<String, T> next = iterator.next();
// list.add(next.getValue());
// }
// 方法2:
Set<String> keyset = map.keySet();
for (String key:
keyset) {
list.add(get(key));
}
//方法3:
// Collection collections = map.values();
// list.addAll(collections);
return list;
}更新日志
fde06-docs: 添加 doc 技术文档于
