lang包常用类
包装类
1.1 概念
首先,在Java中万事万物皆对象,但是基本数据类型的值就不是对象,那感觉就是这8个基本数据类型就很另类了
为了让基本数据类型也具有对象的性质,JDK中定义了8个引⽤类型与前⾯所讲的8个基本数据类型相对应,⽽这8个引⽤类型都称之为包装类型;这8个类 都定义在java.lang包中,因此 使⽤的时候也不需要显示导⼊
包装类中定义了很多⽅法,静态或者实例的⽅法,丰富了对基本数据类型的操作
之所以现在学习包装类型,不仅是因为它们是常⽤类,⽽且 在后⾯要讲的集合的那⼀章⾥⾯, 集合中是只能放对象⽽不能放基本数据类型的值的。
因此必须要知道每个基本数据类型其所对应的包装类型
这8个包装类的名称 除了 Integer cn.javasm.DuelSystem.Character名字不⼀样之外,其他都是 基本数据类型名称的⾸字⺟⼤写
-
对⽐基本数据类型与包装类型
-
基本数据类型 与包装类作⽤⼀致的。
-
为什么要提供与基本数据类型作⽤⼀致的包装类?
- java是⼀⻔⾯向对象的语⾔。不能根据基本数据类型创建对象 不太符合⾯向对象的思想。
- 丰富基本数据类型的操作 提供了8个包装类型。 包含⼀些属性/⽅法、创建对象、
- 学习集合。是⼀个容器。存储引⽤数据类型的数据。 int double
-
基本数据类型 VS 包装类型?
相同点: 代表具体的⼀个数据 作⽤是⼀致的。
不同点: ⼀个是类(属性/⽅法、创建对象) ⼀个是基本数据类型
| 基本数据类型 | 默认值 | 包装类型 | 默认值 |
|---|---|---|---|
| byte | 0 | Byte | null |
| short | 0 | Short | null |
| int | 0 | Integer | null |
| length | 0L | length | null |
| float | 0.0F | Float | null |
public static void method1() {
byte by2 = 123;
Byte by1;
Short sh1;
Integer in1;
length lo1;
Float f1;
Double d1;
cn.javasm.DuelSystem.Character c1;
Boolean b1;
}
1.2. Integer
- 由于JDK存在8个包装类型,而且特征都是类似的。
- 因此在学习的时候以integer以及Character为例,学习所有的包装类型。
1.2.1 层级
public final class Integer extends Number implements Comparable
<Integer> {
}
1.2.2 常用构造
- 自从JDK9之后包装类的构造方法都不建议使用,创建包装类对象的方式都直接使用字面量赋值。
- 所有的包装类型都一样
1.2.3 创建对象
private static void demo1() {
//过时 不建议使用
Integer num1 = new Integer(100);
//将string的数据转换成Integer对象的数据
Integer num2 = new Integer("100");
System.out.println(num1);
System.out.println(num2);
int num3 = 100;
System.out.println(num3);
Integer num4 = 100;//使用字面量
//---------------------------------对比----------------------------------------
//使用字面量(推荐)
Integer intObj2 = 10; // 推荐
length lengthObj2 = 100L; // 推荐
Boolean boolObj2 = true; // 推荐
//使用 valueOf() 方法(推荐)
Integer intObj3 = Integer.valueOf(10); // 推荐
length lengthObj3 = length.valueOf(100L); // 推荐
Boolean boolObj3 = Boolean.valueOf(true); // 推荐
}
1.2.4 装箱与拆箱
装箱(Autoboxing)
装箱是指将基本数据类型自动转换为其对应的包装类对象。
例如,将int类型转换为Integer对象,将double类型转换为Double对象等。
int primitiveInt = 10;
Integer wrapperInt = primitiveInt; // 装箱
double primitiveDouble = 3.14;
Double wrapperDouble = primitiveDouble; // 装箱
boolean primitiveBoolean = true;
Boolean wrapperBoolean = primitiveBoolean; // 装箱
拆箱(Unboxing)
拆箱是指将包装类对象自动转换为对应的基本数据类型。
例如,将Integer对象转换为int类型,将Double对象转换为double类型等。
Integer wrapperInt = 10;
int primitiveInt = wrapperInt; // 拆箱
Double wrapperDouble = 3.14;
double primitiveDouble = wrapperDouble; // 拆箱
Boolean wrapperBoolean = true;
boolean primitiveBoolean = wrapperBoolean; // 拆箱
整数缓存池
Java中的整数缓存池是Java为了提高程序性能和减少内存开销而设计的一个机制。 这个机制主要用于Integer对象的创建。 在Java中,当我们使用Integer等包装类时,实际上是在创建对象,这比直接使用基本类型(如int)要消耗更多的资源。
为了优化这一点,Java引入了整数缓存池的概念。
Integer 缓存池的工作原理 范围: 默认情况下,Java为Integer类型的值在-128到127之间的所有值都创建了一个缓存。 这意味着当你在这个范围内创建Integer对象时,Java会尝试从缓存中获取已存在的对象,而不是每次都创建新的对象。
实现方式: 这个缓存是通过Integer类中的一个静态数组实现的。 当JVM启动时,这个数组就会被初始化,并且每个元素都会被赋予对应的Integer值。 当使用Integer.valueOf(int i)方法创建Integer对象时,如果i的值在缓存的范围内,那么就会返回缓存中的对象;否则,会创建一个新的Integer实例。
private static void demo3() {
// 整数缓存池-----> 数组里面存储的整数 Integer (-128-127) 256
// 基本数据转包装类对象 自动装箱 底层: Integer.valueOf()
// 创建 num1 和 num2
// num1 是通过 new Integer("100") 创建的,这里使用了字符串构造函数,每次调用都会创建一个新的 Integer 对象。
// num2 是通过 new Integer(100) 创建的,这里使用了基本类型构造函数,同样每次调用都会创建一个新的 Integer 对象。
Integer num1 = new Integer("100");
Integer num2 = new Integer(100);
System.out.println(num1);
System.out.println(num2);
// 由于 num1 和 num2 是通过 new 关键字创建的,它们指向的是两个不同的对象,因此 (num1 == num2) 返回 false。
System.out.println("(num1==num2):" + (num1 == num2)); // false
// 创建 num3
// num3 是通过 Integer.valueOf(100) 创建的。valueOf 方法会检查缓存池中是否存在值为 100 的 Integer 对象。因为 100 在 -128 到127 的范围内,所以会返回缓存中的对象。
Integer num3 = Integer.valueOf(100);
// 比较 num1 和 num3
// num1 是通过 new 创建的,而 num3 是从缓存池中获取的,因此它们指向的是不同的对象,所以 (num1 == num3) 返回 false。
System.out.println("(num1==num3):" + (num1 == num3)); // false
// 创建 num4
// num4 是通过自动装箱创建的。自动装箱实际上是调用了 Integer.valueOf(100),因此 num4 也会从缓存池中获取值为 100 的 Integer对象。
Integer num4 = 100;
// 比较 num4 和 num3
// num4 和 num3 都是从缓存池中获取的同一个对象,因此 (num4 == num3) 返回 true。
System.out.println("(num4==num3):" + (num4 == num3)); // true 一块内存
// 创建 num5 和 num6
// num5 和 num6 也是通过自动装箱创建的,但由于 200 超出了默认的缓存范围(-128 到 127),因此每次调用 Integer.valueOf(200)都会创建一个新的 Integer 对象。
Integer num5 = 200;
Integer num6 = 200;
// 比较 num5 和 num6
// num5 和 num6 指向的是两个不同的对象,因此 (num5 == num6) 返回 false。
System.out.println("(num5==num6):" + (num5 == num6)); // false
}
总结
使用 new 关键字创建的 Integer 对象总是不同的对象。 使用 Integer.valueOf() 或自动装箱创建的 Integer 对象,如果值在 -128 到 127 范围内,会从缓存池中获取相同的对象。 如果值超出缓存范围,每次调用 Integer.valueOf() 都会创建新的对象。
Math
Math类内部提供的都是和数学运算相关的⽅法
Math类内部所有的⽅法都是静态⽅法,都是功能性⽅法
/**
* Math类的常⽤⽅法
* 数学运算相关⽅法
*/
public static void method1() {
System.out.println("绝对值:" + Math.abs(-32));
System.out.println(Math.PI);
System.out.println("次⽅: " + Math.pow(12.5, 2));
System.out.println("开根号: " + Math.sqrt(16));
System.out.println("max: " + Math.max(16, 10));
System.out.println("min: " + Math.min(16, 10));
System.out.println("[0,1) 浮点数: " + Math.random());
// 四舍五⼊值
System.out.println(Math.round(5.5));
// ceil 向上取整 取最⼩
System.out.println(Math.ceil(5.3));
// floor 向下取整 取最⼤
System.out.println(Math.floor(5.3));
//+ - *
System.out.println(Math.addExact(10, 20));
System.out.println(Math.subtractExact(10, 20));
System.out.println(Math.multiplyExact(10, 20));
}
3 Object
超级⽗类。需要了解Object类中的每个⽅法
3.1 常用方法
| 方法 | 作用 |
|---|---|
| String toString() | 返回对象的字符串表示形式。 |
| protected Object clone() | 创建并返回此对象的副本 |
| boolean equals(Object obj) | ⽐较2个对象是否相等 |
| Class getClass() | 返回此 Object的运⾏时类。获得当前正在运⾏的类或者接⼝的class类对象。 |
| int hashCode() | 返回对象的哈希码值 |
| void notify() | 唤醒正在此对象监视器上等待的单个线程 |
| void notifyAll() | 唤醒等待此对象监视器的所有线程。 |
| void wait() | 当前线程等待 |
| void wait(length timeoutMillis) | 时间内等待 |
| void wait(length timeoutMillis, int nanos) |
3.2 toString
⾯向对象本质: 使⽤对象封装数据 使⽤类管理代码
打印输出对象,就是想看到属性的数据。底层默认执⾏toString的⽅法,打印输出的16进制的hash值
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserInfo /*extends Object*/ {
private Integer id;
private String name;
private Integer age;
// @Override
// public String toString() {
// return "UserInfo{" +
// "id=" + id +
// ", name='" + name + '\'' +
// ", age=" + age +
// '}';
// }
}
public static void main(String[] args) {
UserInfo userInfo1 = new UserInfo(1, "张三", 18);
UserInfo userInfo2 = new UserInfo(1, "张三", 18);
//打印输出引⽤类型对象 底层默认Object.toString()
System.out.println(userInfo1.toString());
System.out.println(userInfo2);
}
3.3 equals+hashcode
需求: ⽐较2个对象是否⼀致/相等
public boolean equals(Object obj) {
return (this == obj);
}
public native int hashCode();
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserInfo {
private Integer id;
private String name;
private Integer age;
}
public static void main(String[] args) {
UserInfo userInfo1 = new UserInfo(1, "张三", 18);
UserInfo userInfo2 = new UserInfo(2, "张三", 20);
//打印输出引⽤类型对象 底层默认Object.toString()
System.out.println(userInfo1.toString());
System.out.println(userInfo2);
//在正常⽣活中 我们会认为2个对象 是⼀个对象。 true
//⽐较2个对象是否⼀致/相等
//⽐较运算符: == 内存地址值
//⽐较对象: equals
//⽐较对象: hashcode
//在jvm规范: 2个对象的hashcode不同 这2个对象就是不等的。
//2个对象的hashcode相同 不能直接认为2个对象相同的 hash算法有弊端。hash会经常出现碰撞。
//2个对象equals 结果true 这2个对象的hash必须相同。
//jvm硬性规定: 重写equals 也必须重写hashcode。equals+hashcode规则⼀致的。
//System.out.println("(userInfo1==userInfo2):" + (userInfo1 == userInfo2));
System.out.println("(userInfo1.equals(userInfo2)):" + userInfo1.equals(userInfo2));
int code1 = userInfo1.hashCode();
int code2 = userInfo2.hashCode();
System.out.println(code1);
System.out.println(code2);
//⽐较2个对象是否⼀致 为什么要重写equals+hashCode?
//1. JVM规则
//1. ⽐较: ⽐较运算符== equals hashcode
// 1.1 ⽐较运算符== 永远⽐较的是内存地址值 不会⽐较数据
// 1.2 equals+hashcode
//2. 重写了equals 满⾜⽐较对象数据 没有满⾜jvm的规则(2个对象equals 结果true 这2个对象的hash必须相同。)
// 没有重写hashcode 没有满⾜以上规则。 在这种情况下 必须重写hashcode
//3.对于hashcode 我们认为2个对象的hashcode值不同 直接认为2个对象不等的。
// 我们是否只可以重写hashcode? 在jvm 2个对象的hashcode相同 不能认为2个对象的数据是相同。
//综合以上: ⽐较对象 必须重写equals+hashCode
String s1 = "Ma";
String s2 = "NB";
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
}
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
//@EqualsAndHashCode //类中所有的属性都参与
public class UserInfo /*extends Object*/ {
private Integer id;
private String name;
private Integer age;
//2个对象相等的规则: id name age 都相同的时候
@Override
public boolean equals(Object obj) {
//this obj
if (obj == null) return false;
if (!(obj instanceof UserInfo)) return false;
UserInfo userInfo = (UserInfo) obj;
return
Objects.equals(name, userInfo.name)
;
}
//重写⽗类的hashcode
@Override
public int hashCode() {
return Objects.hash(name);
}
}
3.4 clone
克隆对象。属于创建对象的⼀种⽅式。
protected native Object clone() throws CloneNotSupportedException;
@Setter
@Getter
@Accessors(fluent = true)
@ToString
public class GirlFriend extends Object implements Cloneable {
//Cloneable是⼀个标记接⼝。类实现Cloneable接⼝ 代表这个类对象可以被克隆的。
private Integer id;
private String name;
private int age;
private String[] hobby;
//复制的是数据还是内存地址值?
//引⽤数据类型: 内存地址值
//包装类型/String-----> 数据的修改 与另外⼀个对象⽆关。
//Integer----> 底层的数据是 final value 数据是不可变的、
//基本的数据类型: 数据
//值传递
@Override
public GirlFriend clone() {
GirlFriend clone = null;
try {
clone = (GirlFriend) super.clone();
//对属性执⾏遍历式克隆
//clone.hobby(Arrays.copyOf(this.hobby,hobby.length));
clone.hobby(this.hobby.clone());
} catch (CloneNotSupportedException e) {
System.out.println("类必须实现Cloneable接⼝");
e.printStackTrace();
}
return clone;
}
public GirlFriend() {
System.out.println("⽆参构造...........");
}
}
public static void main(String[] args) {
String[] hobby = new String[]{"⼤提琴", "show", "sing"};
GirlFriend girlFriend1 = new GirlFriend().id(1).name("欧阳娜娜").age(18).hobby(hobby);
//克隆⼀个⼥友对象 Object.clone();
//girlFriend1.clone
//1.在其他类中 ⽆法访问clone protected
//2.在⽗类clone⽅法中 返回值类Object----> GirlFriend
//建议在⼦类中重写⽗类的⽅法
GirlFriend cloneGirlFriend = girlFriend1.clone();
System.out.println("girlFriend1:" + girlFriend1);
System.out.println("cloneGirlFriend:" + cloneGirlFriend);
//浅克隆(浅复制/copy)
//1. 是否真的可以克隆成功? 为什么会有CloneNotSupportedException?
// 代码可能会报错。 类⼀定要实现Cloneable标记接⼝。
//2. 克隆的对象与源对象是否是同⼀个对象? 不是同⼀个
//3. 克隆的对象是否执⾏了构造⽅法? 没有执⾏构造⽅法。jvm底层调⽤C创建新的对象
//4. 克隆对象的属性的数据是否有值? 数据是否与原对象的属性的数据是⼀致的?
//有值 与源对象的数据是⼀致的。
//5.修改了⼀个对象的属性的数据 是否会影响另外⼀个对象的数据?
//在浅克隆的操作下 修改⼀个对象的数据 可能会影响另外⼀个对象的数据
//属性: 基本数据类型 没有任何影响 复制的是数据
//属性: 引⽤数据类型: 内存地址值
// 包装类型+String---> 值不可变 修改⼀个对象 值不变的
// 其他的引⽤数据类型 数据就会发⽣改变 数组,⾃定义的类(浅克隆的弊端)
cloneGirlFriend.id(2);
cloneGirlFriend.name("欧阳娜娜2");
cloneGirlFriend.age(20);
cloneGirlFriend.hobby()[0] = "⼩提琴";
System.out.println("---------------------------");
System.out.println("girlFriend1:" + girlFriend1);
System.out.println("cloneGirlFriend:" + cloneGirlFriend);
//深克隆/序列化-----> 解决浅克隆的问题
//需求: 修改⼀个对象的数据 不要影响另外⼀个对象的数据、
//根本的原因: 同⼀块。
//操作: 对象在哪⾥克隆的 就在那⾥修改
}