Object 类
Object类是类层次结构的根。每个类都有 Object作为超类。所有对象,包括数组,实现这个类的方法
Object 常见的 API
API | 用法 |
clone() | 创建并返回此对象的副本 |
toString() | 返回对象的字符串表现形式 |
equals(Object obj) | 指示是否有其他对象 “等于” 这一个 |
clone
对象克隆
public class Demo{ public static void main(String[] args) { Person p = new Person(12, "zhangssan"); Person p1 = null; { try{ p1 = (Person)p.clone(); } catch(CloneNotSupportedException e) { e.printStackTrace(); } { System.out.println(p); // Person@123456 System out.println(p1); // Person@114514 } }
从这边代码可以看到
p1
使用clone
方法 克隆 对象p
地址却是不一样的, 说明我们开辟了一块新的栈内存
浅拷贝 与 深拷贝
- 浅拷贝
// Person 类 class Person implements Cloneable { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } @Override // throws xxx 异常声明 或使用 try catch public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } // Main 程序入口类 public class Main { public static void main(String[] args) throws CloneNotSupportedException { Person person1 = new Person("Alice", 25); Person person2 = (Person) person1.clone(); System.out.println("person1: " + person1); System.out.println("person2: " + person2); // 修改person1的字段 person1.setName("Bob"); person1.setAge(30); // 验证浅拷贝的效果 System.out.println("person1: " + person1); System.out.println("person2: " + person2); } }
- 只可以在子类中重写克隆方法, 因为
Main
函数不是类的实例方法, 它无法被继承或重写- 可以看到
Person
类实现了一个接口Cloneable
, 这个Cloneable
就称之为标记接口
, 它表示该类的实例可以被克隆, 看开看你会发现它只是一个 …{}
运行如下
person1: Person{name='Alice', age=25} person2: Person{name='Alice', age=25} person1: Person{name='Bob', age=30} person2: Person{name='Alice', age=25}
- 深拷贝
// Person 类 class Person implements Cloneable { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } @Override // throws xxx 异常声明 或使用 try catch public Object clone() throws CloneNotSupportedException { // 创建新的Person对象 Person clonedPerson = (Person) super.clone(); // 修改为深拷贝 clonedPerson.name = new String(this.name); // 对于基本类型,直接复制值 clonedPerson.age = this.age; return clonedPerson; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } // Main 程序入口类 public class Main { public static void main(String[] args) throws CloneNotSupportedException { Person person1 = new Person("Alice", 25); Person person2 = (Person) person1.clone(); System.out.println("person1: " + person1); System.out.println("person2: " + person2); // 修改person1的字段 person1.setName("Bob"); person1.setAge(30); // 验证深拷贝的效果 System.out.println("person1: " + person1); System.out.println("person2: " + person2); } }
运行如下
person1: Person{name='Alice', age=25} person2: Person{name='Alice', age=25} person1: Person{name='Bob', age=30} person2: Person{name='Alice', age=25}
首先,创建了一个名为
person1
的Person
对象,其姓名为"Alice"
,年龄为25
然后,通过调用clone()
方法创建了一个名为person2
的新对象,它与person1
具有相同的属性值。在验证深拷贝效果之前,我们修改了
person1
的姓名为"Bob"
,年龄为30
最后,输出了person1
和person2
的详细信息。可以看到,
person1
的改变没有影响到person2
,说明这是一个深拷贝。因此,person1
被成功拷贝到了person2
它们之间没有共享字段的引用。
toString()
转换字符串
toString() 方法源码
public String toString() { return "Person{name='" + name + "', age=" + age + "}"; // 例如有name age字段 }
使用
// 入口函数 Main public class Main { public static void main(String[] args) { Person person = new Person("John", 25); System.out.println(person.toString()); } }
输出:
Person{name=“John”, age=25}
equals(Object obj)
地址比较
简单比较两个对象的引用
Object obj1 = new Object(); Object obj2 = obj1; if (obj1 == obj2) { // 两个对象的引用相等 }
判断两个对象的地址是否相等
obj1.equals(obj2);
Objects 类
Objects是一个工具类,提供了很多操作对象的静态方法给我们使用
它与 Object
类有着相同的方法名,功能都是类似的
Objects.equals(Object obj1, Object obj2)
非空比较
Objects.equals 先做非空判断,再做对象比较
源码解析
public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); }
isNull(Object obj)
判断null
判断对象是否为
null
若为null
返回true
nonNull(Object obj)
判断非null
判断对象是否不为
null
若不为null
返回true
包装类
基本数据类型对应的包装类列表
基本数据类型 | 包装类 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
自动装箱
可以自动把基本类型的数据转换成对象
例如
Integer a1 = 12;
自动拆箱
可以自动把包装类型的对象转换成对应基本数据类型
例如
int a4 = a1;
字符串操作 | StringBuilder,StringBuffer,StringJoiner
StringBuilder
,StringBuffer
,StringJoiner
都是一些字符串的操作方法,它们有时比String
更适合做字符串的修改操作,效率也会更高,代码也会更简洁
StringBuilder
字符串操作
StringBuilder
在操作字符串的时候效率非常高(频繁拼接,修改建议使用StringBuilder
)
基本使用
StringBuilder s = new StringBuilder(); StringBuilder s = new StringBuilder("hello"); // 拼接内容 s.append(12); s.append("hi"); s.append(true); // 返回内容: “hello12hitrue” // 支持链式编程 s.append(true).append("haha")
使用 StringBuilder
拼接数组
public static void main(String[] args) { System.out.println(getArrayList(new int[] { 1, 123, 456 })); } // 实现方法 public static String getArrayList(int[] arr) { if (arr == null) { return null; } // 实例化字符串操作类 StringBuilder StringBuilder sb = new StringBuilder(); sb.append("["); for (int i = 0; i < arr.length; i++) { if (i == arr.length - 1) { // 若拼接到最后一个不加 , sb.append(arr[i]); } else { sb.append(arr[i]).append(","); } } sb.append("]"); return sb.toString(); // 返回 字符串表现形式 }
StringBuffer
字符串操作
与
StringBuilder
相似,但是StringBuilder
是线程不安全的,StringBuffer
是线程安全的
StringJoiner
字符串拼接
与
StringBuilder
相似,也是用来操作字符串的,也可以看成是一个容器,创建之后里面的内容是可变的
好处:
不仅能提高字符串的操作效率,并且在有些场景下使用它操作字符串,代码会更简洁
基本使用
StringJoiner s = new StringJoiner(间隔符号); StringJoiner s = new StringJoiner(间隔符号, 开始符号, 结束符号);
StringJoiner
相对于StringBuilder
来说还是比较简单, 一般只可以操作简单的字符拼接,如果需要频繁插入,删除字符,建议使用StringBuilder
操作
StringJoiner s = new StringJoiner(", "); s.add("java1"); s.add("java2"); // java1, java2 // ---------------------------------- StringJoiner s = new StringJoiner(", ", "[", "]"); s.add("java1"); s.add("java2"); // [java1, java2]
使用
StringJoiner
拼接
public static void main(String[] args) { System.out.println(getArrayData(new int[] { 1, 2, 3 })); } public static string getArrayData(int[] arr) { // 判断 arr 非空 if (arr == null) { return null; } // arr 数组存在 StringJoiner sj = new StringJoiner(", ", "[", "]"); for(int i = 0; i < arr.length; i++) { sj.add(arr[i] + ""); } return sj.toString(); }
Math
,System
,Runtime
Math
数学类
Math
代表数学,是一个工具类,里面提供的都是对数据进行操作的一些静态方法
System
系统类
System.currentTimeMillis(); // 获取当前系统的时间(毫秒值) long 类型
Runtime
运行类
小知识:
Runtime
是个单例设计模式的类
简单使用
Runtime r = Runtime.getRuntime(); // 返回与当前Java应用程序关联的运行时对象
BigDecimal
解决小数失真运算
在一些商业项目中,在进行一些小数运算时,必须要求精准,使用传统运算符时可能会出现小数运算失真的情况
System.out.println(0.2 + 0.1); // 0.30000000000000004
采用 java.util.math 中的 BigDecimal 类 来运算解决失真问题
// 转为 字符串 封装成 BigDecimal 对象来运算 BigDecimal a1 = new BigDecimal(Double.toString(a)); BigDecimal b1 = new BigDecimal(Double.toString(b));
// 优化 // 推荐用以下方式把小数转换为字符串再得到BigDecimal对象来使用(更简洁) BigDecimal a1 = BiDecimal.valueOf(a); BigDecimal b1 = BiDecimal.valueOf(b); BigDecimal b = a1.add(b1); BigDecimal b = a1.subtract(b1); BigDecimal b = a1.multiply(b1); BigDecimal b = a1.divide(b1);
Date 类
before Java 1.8
在 Java 1.8 之前,Date 对象是可变的,所以处理时间类需要谨慎
创建时间对象(部分过时)
Date currentDate = new Date(); // 使用当前日期和时间创建Date对象 Date specificDate = new Date(year, month, day); // 使用指定的年、月、日创建Date对象(过时的)
获取日期和时间(过时的)
int year = date.getYear(); // 获取年份(从1900年开始计算) int month = date.getMonth(); // 获取月份(从0开始计算,0表示一月) int day = date.getDate(); // 获取天数(月份中的日期) int hours = date.getHours(); // 获取小时w int minutes = date.getMinutes(); // 获取分钟 int seconds = date.getSeconds(); // 获取秒钟
设置日期和时间(过时的)
date.setYear(year); // 设置年份 date.setMonth(month); // 设置月份 date.setDate(day); // 设置天数 date.setHours(hours); // 设置小时 date.setMinutes(minutes); // 设置分钟 date.setSeconds(seconds); // 设置秒钟
格式化和解析时间对象(部分过时)
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 生成规定格式的时间对象(过时的) String formattedDate = formatter.format(date); // 格式化Date对象为指定格式的字符串 Date parsedDate = formatter.parse("2022-01-01 12:00:00"); // 将字符串解析为Date对象