第1节 常用类库(上)
面向对象更多是思想上的东西,常用类库更多是工具上的东西,熟能生巧,多整理笔记。
一、泛型
1.1 概述
泛型,即“参数化类型”。就是将类型由原来具体类型进行参数化 ,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参 ),然后在使用/调用时传入具体的类型(类型实参) 。
1.2 泛型的使用
泛型一般在类、接口和方法中使用,其中主要是类中用的更多。
1.2.1 泛型在类中的使用
先来看定义的时候的格式:
package com.kaikeba.coreclasslibrary;
public class Person<A> {
private String name;
private int age;
//这里是形参,实际使用该类时需要给定实参,在该类中可以用A来代替一种不确定的类型
private A data;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public A getData() {
return data;
}
public void setData(A data) {
this.data = data;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", data=" + data +
'}';
}
}
再看使用时的格式:
package com.kaikeba.coreclasslibrary;
public class genericParadigm {
public static void main(String[] args) {
//实际使用时指定具体的类型给泛型,那么该类中所有的A全部替换为Integer
Person<Integer> p = new Person<>();
}
}
看下面传入不同的类型,setData
方法的参数列表中data的类型也随之变化:
1.2.2 泛型在接口中的使用
定义时格式如下所示,和类中基本一致:
public interface 接口名<T> {
T getData();
}
接口都是要被类实现才有意义,实现接口时,可以选择指定泛型类型,也可以选择不指定,如下所示:
1、指定类型
//指定类型:这样的类使用时无需再给泛型具体的类型,直接使用即可
public class 类名 implements 接口名<String> {
private String text;
@Override
public String getData() {
return text;
}
}
具体的使用案例:
package com.kaikeba.coreclasslibrary;
public class genericParadigm2 {
public static void main(String[] args) {
Apple a = new Apple();
a.text = "123";
System.out.println(a.getData());
}
}
interface Fruit<T>{
T getData();
}
//实现接口时指定类型
class Apple implements Fruit<String>{
String text;
@Override
public String getData() {
return text;
}
}
结果为:123
2、不指定类型:
//不指定类型:使用类时需要给定具体的类型
public class 类名<T> implements 接口名<T> {
private T text;
@Override
public T getData() {
return text;
}
}
具体的使用案例:
package com.kaikeba.coreclasslibrary;
public class genericParadigm2 {
public static void main(String[] args) {
Apple<String> a = new Apple<>();
a.text = "123";
System.out.println(a.getData());
}
}
interface Fruit<T>{
T getData();
}
//实现接口时不指定类型
class Apple<T> implements Fruit<T>{
T text;
@Override
public T getData() {
return text;
}
}
结果:123
1.2.3 泛型在方法中的使用
泛型方法的格式如下,注意位置与类和接口不一样:
private static <T> T 方法名(T a, T b) {}
想用T的地方可以用T,不想用的地方就用自己想用的类型即可,上述的格式案例中全用了T,下面看个例子:
package com.kaikeba.coreclasslibrary;
public class genericParadigm {
public static void main(String[] args) {
//根据传入的实际参数类型,自动获取泛型的类型
print(123);
}
public static <A> void print(A a) {
System.out.println(a);
}
}
结果:123
1.3 泛型限制类型
在使用泛型的时候,可以指定泛型的限定区域,例如:必须是某某类的子类 或 某某接口的实现类,格式:
<T extends 类或接口1 & 接口2>
注意:这里不论是类还是接口,都是使用extends
关键字。
package com.kaikeba.coreclasslibrary;
public class genericParadigm2 {
public static void main(String[] args) {
// 如果传入的不是水果的实现类,会报错
// Plate<String> p = new Plate<>();
// p.data = "String";
// 如果传入的符合要求,则正确
Plate<Apple> p = new Plate<>();
}
}
//定义一个水果接口
interface Fruit<T>{
T getData();
}
//实现水果接口的苹果类
class Apple<T> implements Fruit<T>{
T text;
@Override
public T getData() {
return text;
}
}
//泛型限制只能传入水果接口的实现类
class Plate<T extends Fruit>{
T data;
}
1.4 泛型中的通配符——?
类型通配符是使用 ? 代替方法具体的类型实参。
-
<? extends Parent>
指定了泛型类型的上界,必须是Parent或其子类; -
<? super Child>
指定了泛型类型的下界,必须是Child或其父类; -
<?>
指定了没有限制的泛型类型。
package com.kaikeba.coreclasslibrary;
public class genericParadigm3 {
public static void main(String[] args) {
//不能将一个装着苹果的盘子看成一个装着水果的盘子
//多态建立的基础:左右对象是父子关系,此时是同级别类,只是泛型是父子类关系而已
//多态不能用在容器的内容上
Plate3<? extends Fruit3> p1 = new Plate3<Apple3>();
Plate3<? super Apple3> p2 = new Plate3<Fruit3>();
}
}
interface Fruit3{}
class Apple3 implements Fruit3{}
class Plate3<T>{//也可以多个泛型
T data;
}
1.5 泛型的作用以及注意事项
作用:
-
提高代码的复用率
-
泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
注意事项:
在编译之后程序会采取去泛型化的措施,也就是Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦除,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法,也就是说,泛型信息不会进入到运行时阶段。
二、java.util.Objects类
Objects类以及下面介绍的每一种类库,最好的学习方法就是看源码以及注释,其次可以配合中文手册,下面就是对其的整理。
简介: 此类由静态实用程序方法组成,用于对对象进行操作,或在操作前检查某些条件。这些实用程序包括空安全 (null-safe) 或空容忍 (null-tolerant) 方法,用于计算对象的哈希代码、返回对象的字符串、比较两个对象以及检查索引或子范围值是否超出范围等等。
2.1 checkFromIndexSize
2.2 checkFromToIndex
2.3 checkIndex
上面这三个判断索引范围,自己写也很简单。。。
2.4 compare
注意不是返回o,是返回0,还是看源码注释比较靠谱。。。
2.5 deepEquals
这个暂时用不到,用到了再来补充笔记。
2.6 euqals
这个目前比较重要,翻译的实在不太行,以后还是多看源码吧。
它的作用在于,如果一个对象为null
,那么它调用默认Object
的equals
方法,会报空指针异常:
package com.kaikeba.coreclasslibrary.objects;
import java.util.Objects;
/**
* Objects
*/
public class Demo {
public static void main(String[] args) {
String s1 = null;
String s2 = "456";
System.out.println(s1.equals(s2));
}
}
结果为:
Exception in thread "main" java.lang.NullPointerException
at com.kaikeba.coreclasslibrary.objects.Demo.main(Demo.java:12)
因为null
对象没有equals
方法。
但是如果使用Objects.equals
则不会:
package com.kaikeba.coreclasslibrary.objects;
import java.util.Objects;
/**
* Objects
*/
public class Demo {
public static void main(String[] args) {
Person p1 = null;
Person p2 = new Person();
System.out.println(Objects.equals(p1, p2));
}
}
结果为:
false
看一下equals
的源码就知道了:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
首先判断a和b是否指向同一个对象,是则返回true
,不是则看右边的括号,如果a为null
,直接返回false
,不空才调用Object.equals
方法进行比较,这样可以避免因为空对象导致的异常抛出。
2.7 hash
哈希值目前还没介绍,后续补充。。。
2.8 hashCode
返回一个非空对象的哈希码,空对象返回0。
源码如下:
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
2.9 isNull,nonNull
public static boolean isNull(Object obj) {
return obj == null;
}
public static boolean nonNull(Object obj) {
return obj != null;
}
2.10 requireNonNull、requireNonNullElse、requireNonNullElseGet
2.11 toString
三、java.lang.Math
Math
类包含用于执行基本数值运算的方法,如初等指数、对数、平方根和三角函数。
看几个较常用的:
package com.kaikeba.coreclasslibrary.math;
/**
* Math
*/
public class Demo {
public static void main(String[] args) {
System.out.println(Math.abs(-100));
System.out.println(Math.min(100, 200));
//返回最接近的整数
System.out.println(Math.round(100.55));
System.out.println(Math.round(-100.55));
//返回小于等于参数的最大整数
System.out.println(Math.floor(3.5));
System.out.println(Math.floor(-3.5));
//返回大于等于参数的最大整数
System.out.println(Math.ceil(3.5));
System.out.println(Math.ceil(-3.5));
}
}
结果如下:
100
100
101
-101
3.0
-4.0
4.0
-3.0
四、java.util.Arrays
该类包含用于操作数组的各种方法(例如排序和搜索)。 此类还包含一个静态工厂,允许将数组视为列表。
如果指定的数组引用为null,则此类中的方法都抛出NullPointerException
,除非另有说明。
4.1 binarySearch
除此之外,还有各种其他类型的二分查找,都是在一个数组中查找指定的值,返回找到的索引。
4.2 compare
比较两个数组
4.3 copyOf
将一个数组里的内容复制到一个新的长度的数组中。
4.4 copyOfRange
将指定数组的指定范围内容复制到新数组中。
4.5 equals
比较两个数组是否相同。
4.6 fill
将指定的元素分配给数组中每一个元素。
4.7 mismatch
查找并返回两个数组中第一个不匹配的索引。
4.8 sort
按升序对数组进行排序。
4.9 toString
返回数组内容的字符串表示形式。
上述的方法并没有全部总结,还有一些不常用的方法没有整理。
下面看一个例子:
package com.kaikeba.coreclasslibrary.arrays;
import java.util.Arrays;
public class Demo {
public static void main(String[] args) {
int[] arr = {8,1,2,3,4,5,6,7};
//直接输出arr是地址信息
System.out.println(arr);
//下面可以输出数组的内容,但是很麻烦
for(int i=0; i<arr.length; i++) {
if(i == arr.length - 1) {
System.out.print(arr[i]);
}else {
System.out.print(arr[i] + ", ");
}
}
System.out.println();
//Arrays中的方法都是静态的,直接用类名调用,无需新建对象
//toString方法可以直接输出数组内每个元素
System.out.println(Arrays.toString(arr));
//sort方法可以升序排序
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
//二分查找指定元素的索引,前提:数组必须有序
System.out.println(Arrays.binarySearch(arr, 6));
//动态扩容
arr = Arrays.copyOf(arr, 15);
System.out.println(Arrays.toString(arr));
}
}
结果如下所示:
[I@7c30a502
8, 1, 2, 3, 4, 5, 6, 7
[8, 1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
5
[1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0]
五、java.math.BigDecimal
5.1 概念
通过在控制台运行0.1+0.2,会发现float和double的运算误差:
package com.kaikeba.coreclasslibrary.bigdecimal;
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
System.out.println(0.1+0.2);
}
}
结果:
0.30000000000000004
虽然误差很小,但是在类似于银行这种地方,这些小误差累计起来还是很严重的,所以需要一种严格准确的计算类。为了实现精确运算就需要借助java.math.BigDecimal
类对要计算的对象加以描述。
5.2 常用构造方法
有很多构造方法,最常用字符串来指定精确值:
public BigDecimal(String val) {
}
如:
BigDecimal b1 = new BigDecimal("0.1");
BigDecimal b2 = new BigDecimal("0.2");
5.3 常用方法
有很多关于数学运算的方法,最常用的还是加减乘除四个。下述所有的运算方法,不会影响参与运算的数据本身,运算的结果会被封装为一个新的BigDecimal
对象,这个对象会通过return
返回出去。
-
public BigDecimal add(BigDecimal augend)
:加法运算 -
public BigDecimal subtract(BigDecimal augend)
:减法运算 -
public BigDecimal multiply(BigDecimal augend)
:乘法运算 -
public BigDecimal divide(BigDecimal augend)
:除法运算
看一个例子:
package com.kaikeba.coreclasslibrary.bigdecimal;
import java.math.BigDecimal;
public class Demo {
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("0.1");
BigDecimal b2 = new BigDecimal("0.2");
BigDecimal b3 = b1.add(b2);
System.out.println(b1);
System.out.println(b2);
System.out.println(b3);
}
}
结果:
0.1
0.2
0.3