常用类库
泛型
概述
泛型,即“参数化类型”(方法中参数的类型可以在使用时创建)。
就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,
此时类型也定义成参数形式(可以称之为类型形参),
然后在使用/调用时传入具体的类型(类型实参)。
如何理解?
相当于一个容器,可以装载不同的东西.
分析:
如方法的参数传递时以前用的是方法的重载当参数不确定时
这时候重载就有缺点: 代码重复率高,效率降低
使用Object参数: 算数运算会有问题, 存在强制类型转换可能会出错
解决:
采用泛型
类型不确定,后续使用时再去指定泛型的具体类型
好处:
- 提高代码复用率
- 泛型中的类型在使用时指定,不需要强制类型转换。(编译器会自动检测)
使用
泛型类
定义一个泛型类:
public class ClassName<T>{
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
常用的泛型
: 通常指的是Element,元素
泛型接口
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;
}
}
泛型方法
泛型限制类型
- 在使用泛型时, 可以指定泛型的限定区域 ,
- 例如: 必须是某某类的子类或 某某接口的实现类,
格式:
代码示例:
public class Demo2 {
public static void main(String[] args) {
Cup<Cola> c = new Cup<>();
}
}
//定义一个饮料接口
interface Drinks{}
//定义一个可乐类,继承饮料类
class Cola implements Drinks{
}
//定义一个杯子类,限定了这个杯子继承饮料类的子类,也就是可乐类
class Cup<T extends Drinks>{
T data;
}
泛型中的通用符 ?
类型通配符是使用?
代替方法具体的类型实参。
1 <? extends Parent> 指定了泛型类型的上届
// ? 代表了Cola是继承Drinks
Cup<? extends Drinks> c1 = new Cup<Cola>();
2 <? super Child> 指定了泛型类型的下届
// ? 表示Cola 的父类是Drinks
Cup<? super Cola> c2 = new Cup<Drinks>();
3 <?> 指定了没有限制的泛型类型
Cup<?> c2 = new Cup<>();
作用
1、 提高代码复用率
2、 泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
注意
在编译之后程序会采取去泛型化的措施。
也就是说Java中的泛型,只在编译阶段有效。
在编译过程中,正确检验泛型结果后,
会将泛型的相关信息擦出,
并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。
也就是说,泛型信息不会进入到运行时阶段。
常用类库
java.util.Objects
equals()
两个对象是否相等
代码示例
import java.util.Objects;
public class Demo01 {
public static void main(String[] args) {
String a = null;
String b = "123";
//会产生空指针异常
System.out.println(a.equals(b));
//能够避免空指针异常
System.out.println(Objects.equals(a,b));
}
}
isNull()
判断是否为空
import java.util.Objects;
public class Demo01 {
public static void main(String[] args) {
String a = null;
System.out.println(Objects.isNull(a));
}
}
requireNonNull()
检查指定的对象引用是否为
null
,如果是,则抛出自定义的
NullPointerException
。 此方法主要用于在具有多个参数的方法和构造函数中进行参数验证.
java.lang.Math
import java.util.Objects;
public class Demo01 {
public static void main(String[] args) {
//绝对值
System.out.println(Math.abs(-100));//100
//最大最小值
System.out.println(Math.max(100,200));//200
System.out.println(Math.min(100,200));//100
//四舍五入
System.out.println(Math.round(3.5));//4
System.out.println(Math.round(-3.5));//-3
//返回小于等于参数的最大整数
System.out.println(Math.floor(3.5));//3.0
System.out.println(Math.floor(-3.5));//-4.0
//返回大于等于参数的最小整数
System.out.println(Math.ceil(3.5));//4.0
System.out.println(Math.ceil(-3.5));//-3.0
}
}
java.math.BigDecimal
概述
通过在控制台运行0.1+0.2 ,会发现float和double 的运算误差
由于这两种类型在运算时可能会产生误差,为了实现精确运算时则需要
借助BigDecimal类加以描述
import java.math.BigDecimal;
public class Demo01 {
public static void main(String[] args) {
//()内不能直接输入double,要输入String类型才能精确转换
BigDecimal b1 = new BigDecimal("0.1");
BigDecimal b2 = new BigDecimal("0.2");
BigDecimal b3 = b1.add(b2);
System.out.println(b3);
}
}
常用构造方法
public BigDecimal(String val){
}
常用方法
下述所有的运算方法,不会影响参与运算的数据本身,运算的结果会被封装为一个新的BigDecima1对象,这个对象会
通过return返回出去。
1.public BigDecimal add(BigDecimal augend){} //加法运算
2.public BigDecimal subtract(BigDecimal augend){} //减法运算
3.public BigDecimal mutiply(BigDecimal augend){} //乘法运算
4.public BigDecimal divide(BigDecimal augend){} //除法运算
java.util.Date
java.util.DateFormat
java.util.Calendar
java.lang.System
-
类包含几个有用的类字段和方法。 它无法实例化。System
类提供的设施包括标准输入,标准输出和错误输出流; 访问外部定义的属性和环境变量; 加载文件和库的方法; 以及用于快速复制阵列的一部分的实用方法。System
String
String 被声明为 final,因此它不可被继承。(Integer 等包装类也不能被继承)
在 Java 8 中,String 内部使⽤ char 数组存储数据。
在 Java 9 之后,String 类的实现改⽤ byte 数组存储字符串,同时使⽤ coder 来标识使⽤了哪种编码。
-
类表示字符串。 Java程序中的所有字符串文字(例如String
)都实现为此类的实例。"abc"
- 字符串是不变的; 它们的值在创建后无法更改。 字符串缓冲区支持可变字符串。 因为String对象是不可变的,所以可以共享它们。
- String的两种赋值方式
- 字面量直接赋值,
string text = "hello";
- 通过关键字new调用String的构造方法赋值 :
String str = new String ("hello");
String 为什么是不可变的、jdk 源码中的 String 如何定义的、为什么这么设计?
首先了解一下什么是 , 不可变对象就是一经创建后, 其对象的内部状态不能被修改, 啥意 思呢? 也就是说不可变对象需要遵守下面几条原则 不可变对象的内部属性都是 final 的不 可变对象的内部属性都是 private 的 不可变对象不能提供任何可以修改内部状态的方法、setter 方法也不行 不可变对象不能被继承和扩展 与其说问 String 为什么是不可变的, 不如说如何把 String 设计成不可变的。 String 类是一种对象, 它是独立于 Java 基本数据类型而存在的, String 你可以把它理解为字符串的集 合, String 被设计为 final 的, 表示 String 对象一经创建后, 它的值就不能再被修改, 任何对 String 值 进行修改的方法就是重新创建一个字符串。String 对象创建后会存在于运行时常量池中, 运行时常量池 是属于方法区的一部分, JDK1 .7 后把它移到了堆中。
String s1 = new String(“abc”) 在内存中创建了几个对象
一个或者两个, String s1 是声明了一个 String 类型的 s1 变量, 它不是对象。使用new关键字会在堆中创建一个对象, 另外一个对象是 ABC, 它会在常量池中创建, 所以一共创建了两个对象; 如果 abc 在常量池中已经存在的话, 那么就会创建一个对象。
字符串常量池
1.方法区(Method Area),又称永久代(Permanent Generation),常称PermGen 位于非堆空间,又称非堆区(Non-Heap-Space)。 方法区是被所有线程共享。 所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。 简单说,所有定义的方法的信息都保存在该区域,此区属于共享区间。 这些区域存储的是:静态变量+常量+类信息(构造方法/接口定义) +运行时常量池。 但是,实例变量 存在堆内存中,和方法区无关。 以上,只是逻辑上的定义。 在HotSpot中,方法区仅仅只是逻辑上的独立,实际上还是包含在Java堆中,也是就说,方式区 在物理上属于Java堆区中的一部分,而永久区(Permanent Generation) 就是方法区的实现。 2.堆(Heap) 一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行。 堆在逻辑上分为三部分(Perm) : 新生代(Young Generation, 常称为YoungGen) 老年代(old Generation, 常称为0ldGen、TenuringGen) 永久代(Permanent Generation,常称为PermGen) 2.1、新生区(New/Young Generation) 新生代(Young Generation) ,常称为YoungGen,位于堆空间; 新生区又分为Eden区 和Survior (幸存区)。 Eden :新创建的对象 Survior 0、1:经过垃圾回收,但是垃圾回收次数小于15次的对象 2.2、养老代(0ld Generation) 老年代(old Generation) ,常称为oldGen,位于堆空间; 0ld :垃圾回收次数超过15次,依然存活的对象 2.3、永久区(Permanent Generation) 永久代(Permanent Generation) ,常称为PermGen,位于非堆空间。 永久区是一个常驻内存区域,用于存放JDK自身所携带的Class ,Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM 才会释放此区域所占用的内存。 2.3.1、方法区的实现的演变: Jdk1.7之前: hotspot虚拟机对方法区的实现为永久代; Jdk1.8及之后: hotspot移除了永久代用元空间(Metaspace), 2.3.2、运行时常量池存和字符串常量池的变化 JDK1.7之前: 运行时常量池(包含 字符串常量池)存放在方法区,此时hotspot虚拟机对方法区的实现为永久代。 JDK1.7 : 字符串常量池被从方法区拿到了堆中; 运行时常量池剩下的东西还在方法区,也就是hotspot 中的永久代。 JDK1.8 : hotspot移除了永久代, 用元空间(Metaspace)取而代之。 这时候,字符串常量池还在堆, 运行时常量池还在方法区,只不过方法区的实现从永久代变成元空间(Metaspace) 。
注意
避免通过"+"拼接字符串 使用StringBuffer StringBuilder
- 字面量直接赋值,