Java中的常用API
1. 泛型
概述: 泛型,即“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
泛型的作用:
- 提高代码复用率。
- 泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)。
注意:
- 在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。
- 在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦除,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
1.1 泛型在类中的使用:
定义一个泛型类:
public class Person<T>{
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
测试类文件进行调用并传入int类型数据:
public class Main{
public static void main(String[] arg){
Person<int> p = new Person<>( );
//此时new Person<int>中int可省略
}
}
1.2 泛型在接口中的使用:
public interface IntercaceName<T>{
T getData();
}
实现接口时,可以选择指定泛型类型,也可以选择不指定,如下:
- 指定类型:
public class Interface1 implements IntercaceName<String>{
private String text;
@Override
public String getData() {
return text;
}
}
- 不指定类型(创建类对象时再传入):
public class Interface1<T> implements IntercaceName<T> {
private T data;
@Override
public T getData() {
return data;
}
}
1.3 泛型在方法中的使用:
1. 返回值为泛型:
private static <T> T 方法名(T a, T b) {}
2. 返回值为void型:
private static <T> void 方法名(T a, T b) {}
1.4 泛型限制类型:
在使用泛型时,可以指定泛型的限定区域。
例如:必须是某某类的子类或某某接口的实现类。
<T extends 类或接口1 & 接口2>
eg:
interface Parent{} //定义一个接口Parent
class Child implements Parent{} //定义一个类Child实现接口Parent
class Person<T extends Parent>{ //定义一个类Person并使用泛型T,T是实现接口Parent的子类
T data; //T类型的属性
}
//测试类中创建Person并传入泛型类型为Child,Child是实现接口Parent的子类
Person<Child> p = new Person<>();
1.5 泛型中的通配符“?”:
类型通配符是使用”?“代替方法具体的类型实参。
<? extends Parent>
指定了泛型类型的上界类;<? super Child>
指定了泛型类型的下界类;<?>
指定了没有限制的泛型类型;
eg:
interface Parent{}
class Child implements Parent{}
class Person<T>{
T data;
}
//测试类中创建Person对象
Person<? extends Parent> p = new Person<Child>();
Person<? super Child> p = new Person<Parent>();
2. java.util.Objects
概念: Objects类是Java中的公共父类。
3. java.lang.Math(算数运算)
常用方法:
- 四舍五入:
Math.round();
- 小于等于参数的最大整数:
Math.floor();
- 大于等于参数的最小整数:
Math.ceil();
4. java.util.Arrays(数组处理)
概念: 包含用于操作数组的各种方法(例如排序和搜索)。此类还包含一个静态工厂,允许将数组视为列表。
常用方法:
- 打印数组内容:
Arrays.toString(arr);
- 排序数组内容:
Arrays.sort(arr);
- 采用二分查找查找元素a的下标:
Arrays.binarySearch(arr, a);
- 对数组进行动态扩容到num长度:
Arrays.copyOf(arr, num)
5. java.math.BigDecimal(精度控制)
概念: 通过控制台运行0.1+0.2,会发现float和double的运算误差,等于0.300000000004。
由于float类型和double类型在运算时可能会有误差,为了实现精确运算则需要借助java.math.BigDecimal类加以描述。
常用构造方法: public BigDecimal(String val){ }
常用方法:
下述所有的运算方法,不会影响参与运算的数据本身,运算的结果会被封装为一个新的BigDecimal对象,这个对象会通过return返回出去。
- 加法运算:
add(BigDecimal augend);
- 减法运算:
subtract(BigDecimal augend);
- 乘法运算:
multiply(BigDecimal augend);
- 除法运算:
divide(BigDecimal augend);
- BigDecimal对象数据转换为数值类型数据(例如double):
对象名.doubleValue()
注意: 每个数据都需要创建BigDecimal对象,数据之间的算术运算就是BigDecimal对象之间的算术运算,最后的结果也是一个BigDecimal对象。
eg: 最后b3的结果为0.3
BigDecimal b1 = new BigDecimal(0.1);
BigDecimal b2 = new BigDecimal(0.2);
BigDecimal b3 = b1.add(b2);
6. java.util.Date(日期类)
概念: Date类表示特定的时刻,精度为毫秒。
不适合国际化的功能:允许将日期解释为年,月,日,小时,分钟和秒值,它还允许格式化和解析日期字符串。
适合国际化的类:Calendar类应该用于在日期和时间字段之间进行转换,而DateFormat类应该用于格式化和解析日期字符串。
常用构造方法:
Date()
(分配Date对象并对其进行初始化,使其表示分配时间,测量Date到毫秒。直接new的对象为当前计算机内的时间)Date(long date)
(分配Date对象并初始化它以表示自标准基准时间(称为”纪元”)以来的指定毫秒数,即1970年1月1日00:00:00GMT)
常用方法:setTime()
:将此Date对象设置为表示格林尼治标准时间1970年1月1日00:00:00之后的time毫秒的时间点)getTime()
:返回自此Date对象表示的1970年1月1日00:00:00GMT以来的毫秒数。返回值为long类型。after(Date when)
:返回boolean值,测试此日期是否在指定日期之后。before(Date when)
:返回boolean值,测试此日期是否在指定日期之前。
7. java.text.DateFormat(格式化日期类,时差计算)
概念: 格式化日期类。用于格式化日期,用于时间差计算等。是一个抽象类,使用时需要使用其子类SimpleDateFormat。
字符对时间的表示:
字符 | 时间 |
---|---|
y | 年 |
M | 月 |
d | 日 |
H | 时 |
m | 分 |
s | 秒 |
创建格式化日期对象格式:
SimpleDateFormat 对象名 = new SimpleDateFormat(“yyyy年MM月dd日 HH:mm ss”)
其中的字符串进行自定义日期格式,其中“年”“月”“日”可以用其他符号表示,返回一个字符串类型的时间表示。
将Date对象转换为日期字符串: 对象名.format(Date类型的对象)
将日期字符串转换为Date对象: 对象名.parse(“2021年07月21日 18:24 16”)
注意: 日期字符串得符合格式化日期对象设置得日期格式!
eg:
SimpleDateFormat format = new SimpleDateFormat(“yyyy年MM月dd日 HH:mm ss”);
String text = format.format(new Date());
结果text为当前计算机时间例如:2021年07月21日 18:24 16
8. java.util.Calendar(日历类)
概念: 日历类。是一个抽象类,会在其中的getInstance()方法根据内容创建不同国家日历对象。
创建日历对象格式: Calendar c = Calendar.getInstance();
常用方法:
- 设置对象的时间:
set()
//设置该对象的年
c.set(Calendar.YEAR, 2022);
- 获取对象的时间:
get()
//取出年
int year = c.get(Calendar.YEAR);
//取按年算第几天(月从0-11,0表示1月份;周从周日为第一天)
int day = c.get(Calendar.DAY_OF_YEAR);
- 对对象时间进行计算:
add()
//对该对象的年进行加1
c.add(Calendar.YEAR, 1)
- 获取日历时间表示的Date对象:
getTime()
Date d = c.getTime();
- 获取某字段的最大值:
getActualMaxmum()
//获取月最大值
int m = c. getActualMaxmum(Calendar.Month)
9. java.lang.System
概念: System包含几个有用的类字段和方法。它无法实例化。System类提供的设施包括标准输入,标准输出和错误输出流;访问外部定义的属性和环境变量;加载文件和库的方法;以及用于快速复制阵列的一部分的使用方法。
字段和方法:
变量和类型 | 字段 | 描述 |
---|---|---|
static PrintStream | err | “标准”错误输出流。 |
static InputStream | in | “标准”输入流。 |
static PrintStream | out | “标准”输出流。 |
eg:System.err.println();
变量和类型 | 方法 | 描述 |
---|---|---|
static void | exit (int status) | 终止当前运行的Java虚拟机。 |
static void | gc() | 运行垃圾收集器。 |
static long | currentTimeMillis() | 以毫秒为单位返回当前时间。 |
static void | arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 将指 | 定源数组中的数组从指定位置开始复制到目标数组的指定位置。 |
arraycopy的参数:
- src:源数组。
- srcPos:源数组中的起始位置。
- dest:目标数组。
- destPos:目标数据中的起始位置。
- length:要复制的数组元素的数量。
10. String(字符串类中重点)
概念: String类表示字符串。Java程序中的所有字符串文字(例如"abc")都实现为此类的实例。
字符串是不变的;它们的值在创建后无法更改。字符串缓冲区支持可变字符串。 因为String对象是不可变的,所以可以共享它们。
构造器 | 描述 |
---|---|
String(byte[] bytes) | 通过使用平台的默认字符集解码指定的字节数组构造新的 String 。 |
String(byte[] bytes, String charsetName) | 构造一个新的String由指定用指定的字节的数组解码charset 。 |
String(char[] value) | 分配新的 String ,使其表示当前包含在字符数组参数中的字符序列。 |
变量和类型 | 方法 | 描述 |
---|---|---|
String | substring(int beginIndex, int endIndex) | 返回一个字符串,该字符串是此字符串的子字符串。 |
char | charAt(int index) | |
String | trim() | 返回一个字符串,其值为此字符串,删除了所有前导和尾随空格,其中space被定义为其代码点小于或等于 ‘U+0020’ (空格字符)的任何字符。 |
注意:
-
两个String变量的字符串内容相同时,在堆内存中是共用一块存储空间,栈方法区指向的是同一块内存地址。
-
但使用new创建时就不是存储在同一块存储空间,而是开辟新的空间,所以进行比较时要使用equlas进行判断。
-
字符串在堆中存储后无法更改,进行+拼接时,实际上是创建新的内存来存储拼接后的数据,旧数据就变成垃圾内存,由于String的数据在永久代里面所以不会被回收。要尽量避免字符串拼接!!例如进行高循环拼接时会产生大量的垃圾内存,占用内存过大。所以进行大量字符串拼接时,应使用StrinBuffer和StringBuilder类。(可参考下一节:字符串常量池)
11. String子类StrinBuffer和StringBuilder类(字符串拼接)
概念:== 解决字符串拼接的垃圾内存占用问题==,大部分使用方法均与String相同。用于字符串拼接,使用动态扩容的算法通过数组进行字符串拼接,由于是数组所以产生的垃圾可以被良好回收。
不同点:
- StrinBuffer:单线程,是线程安全的实现。
- StringBuilder:多线程,是线程不安全的实现。
格式: - 拼接字符串:
对象名.append(“要拼接的字符串”)
- 转换成字符串类型:
对象名.toString()
eg:
StrinBuffer str = new StrinBuffer();
str.append(“strstr”);
String text = str.toString();
变量和类型 | 方法 | 描述 |
---|---|---|
StringBuffer | reverse() | 导致此字符序列被序列的反向替换。(即字符串中字符头尾翻转) |
12. 字符串常量池
逻辑上的定义:
- 方法区(Method Area),又称永久代(Permanent Generation),又称非堆区(Non-Heap space),方法区,又称永久代(Permanent Generation),常称为PermGen,位于非堆空间,又称非堆区(Non-Heapspace)。
- 方法区是被所有线程共享。所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。简单说,所有定义的方法的信息都保存在该区域,此区属于共享区间。
- 这些区域存储的是: 静态变量+常量+类信息(构造方法/接口定义) +行时常量池。但是,实例变量存在堆内存中,和方法区无关。.
物理上的定义:
在HotSpot中,方法区仅仅只是逻辑上的独立,实际上还是包含在Java堆中,也是就说,方式区在物理上属于Java堆区中的一部分,而永久区(Permanent Generation)就是方法区的实现。
堆(heap):
概念: 一个JWM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行。
堆在逻辑上分为三部分(Perm):
- 新生代(Young Generation,常称为YoungGen)
- 老年代(old Generation,常称为01dGen. TenuringGen)
- 永久代(Permanent Generation,常称为PermGen)
1) 新生区(New/Young Generation)
- 新生代(Young Generation) ,常称为YoungGen,位于堆空间;
- 新生区又分为Eden区和Survior (幸存区) 。
- Eden :新创建的对象
- Survior 0、1:经过垃圾回收,但是垃圾回收次数小于15次的对象
2) 养老代(old Generation)
- 老年代(old Generation) ,常称为01dGen,位于堆空间;
- old :垃圾回收次数超过15次,依然存活的对象
3) 永久区(Permanent Generation)
- 永久代(Permanent Generation) ,常称为PermGen,位于非堆空间。
- 永久区是一个常驻内存区域,用于存放JDK自身所携带的class, Inter face的元数据,也就是说它存储的是运行环境必须的类的信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM才会释放此区域所占用的内存。
方法区的实现的演变:
- Jdk1.7之前: hotspot虚拟机对方法区的实现为永久代;
- Jdk1.8及之后: hotspot移除了永久代用元空间(Metaspace),
运行时常量池存和字符串常量池的变化:
1) J0K1.7之前:
运行时常量池(包含字符串常量池)存放在方法区,此时hotspot虚拟机对方法区的实现为永久代。
2) JDK1.7:
字符串常量池被从方法区拿到了堆中;
运行时常量池剩下的东西还在方法区,也就是hotspot中的永久代。
3) JDK1.8:
hotspot移除了永久代,用元空间(Metaspace)取而代之。这时候,字符串常量池还在堆,运行时常量池还在方法区,只不过方法区的实现从永久代变成元空间(Metaspace)。