当前位置: 首页 > news >正文

Java中的object类

1.Object类是什么?
🟪Object 是 Java 类库中的一个特殊类,也是所有类的父类(超类),位于类继承层次结构的顶端。也就是说,Java 允许把任何类型的对象赋给 Object 类型的变量。

🟦Java里面除了Object类,所有的类存在继承关系的。

🟩Object 类位于 java.lang 包中,编译时会自动导入, 当一个类被定义后,如果没有指定继承的父类,那么默认父类就是 Object 类。

Object类

①java.lang.Object是所有类的超类。java中所有类都实现了这个类中的方法。

②Object类是我们学习JDK类库的第一个类。通过这个类的学习要求掌握会查阅API帮助文档。

③现阶段Object类中需要掌握的方法:

       ● toString:将java对象转换成字符串。

       ● equals:判断两个对象是否相等。

④现阶段Object类中需要了解的方法:

      ●  hashCode:返回一个对象的哈希值,通常作为在哈希表中查找该对象的键值。Object类的默认实现是根据对象的内存地址生成一个哈希码(即将对象的内存地址转换为整数作为哈希值)。  

      ●  hashCode()方法是为了HashMap、Hashtable、HashSet等集合类进行优化而设置的,以便更快地查找和存储对象。

      ●  finalize:当java对象被回收时,由GC自动调用被回收对象的finalize方法,通常在该方法中完成销毁前的准备。

      ●  clone:对象的拷贝。(浅拷贝,深拷贝)

                    protected修饰的只能在同一个包下或者子类中访问。

                    只有实现了Cloneable接口的对象才能被克隆。

一、toString方法

 Object类中的toString()方法:1. Object类设计toString()方法的目的是什么?这个方法的作用是:将java对象转换成字符串的表示形式。
2. Object类中toString()方法的默认实现是怎样的?
 public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}
 默认实现是:完整类名 + @ + 十六进制的数字这个输出结果可以等同看做一个java对象的内存地址。

例如:

Date类:

public class Date {private int year;private int month;private int day;public Date() {this(1970,1,1);}public Date(int year, int month, int day) {this.year = year;this.month = month;this.day = day;}public int getYear() {return year;}public void setYear(int year) {this.year = year;}public int getMonth() {return month;}public void setMonth(int month) {this.month = month;}public int getDay() {return day;}public void setDay(int day) {this.day = day;}
}

DateTest测试类:


public class DateTest {public static void main(String[] args) {DateTest dateTest = new DateTest();//创建DateTest对象String s = dateTest.toString();System.out.println(s);Date d = new Date();//创建Date对象String s1 = d.toString();System.out.println(s1);}
}

运行结果:

  • 功能:该方法用于将 Java 对象转换为字符串表示形式。默认情况下,Object类的toString方法返回的字符串格式为类名@十六进制哈希码,例如com.example.MyClass@12345678。但在实际应用中,通常会在自定义类中重写toString方法,以便返回更有意义的对象信息,比如对象的属性值等。

例如:在Date类中重写toString方法

 @Overridepublic String toString() {return this.year + "年" + this.month + "月" + this.day + "日";}

运行结果:

注意点:当println()输出的是一个引用的时候,会自动调用“引用.toString()”

Date类:

public class Date {private int year;private int month;private int day;public Date() {this(1970,1,1);}public Date(int year, int month, int day) {this.year = year;this.month = month;this.day = day;}public int getYear() {return year;}public void setYear(int year) {this.year = year;}public int getMonth() {return month;}public void setMonth(int month) {this.month = month;}public int getDay() {return day;}public void setDay(int day) {this.day = day;}
}

DateTest测试类:

public class DateTest {public static void main(String[] args) {DateTest dateTest = new DateTest();//创建DateTest对象String s = dateTest.toString();System.out.println(s);Date d = new Date();//创建Date对象String s1 = d.toString();System.out.println(s1);Date d3 = new Date(2008, 5, 12);// 当println()输出的是一个引用的时候,会自动调用“引用.toString()”System.out.println(d3); // 2008年5月12日System.out.println(d3.toString()); // 2008年5月12日}
}

运行结果:

  Date d4 = null;System.out.println(d4); // "null"System.out.println(d4 == null ? "null" : d4.toString());//System.out.println(d4.toString()); // 空指针异常。

运行结果:

在 Java 中,当你使用 System.out.println() 方法输出一个引用类型的对象时,会自动调用该对象的 toString() 方法,下面我们从源码层面来剖析其原因。

1. System.out 的本质

在 Java 里,System.out 是 PrintStream 类的一个实例,它是标准输出流。System 类中有如下静态初始化代码块来初始化 out 变量:

public final class System {// ... 其他代码 ...public final static PrintStream out = null;static {// 初始化 out 为标准输出流setOut0(new PrintStream(new BufferedOutputStream(FileOutputStreamDescriptor.out), true));}// ... 其他代码 ...
}

2. println() 方法调用流程

当你调用 System.out.println(Object x) 方法时,会进入 PrintStream 类中的相应 println 方法。以下是 PrintStream 类中 println(Object x) 方法的源码:

public class PrintStream extends FilterOutputStream implements Appendable, Closeable {// ... 其他代码 ...public void println(Object x) {String s = String.valueOf(x);synchronized (this) {print(s);newLine();}}// ... 其他代码 ...
}

在这个方法中,它首先调用了 String.valueOf(x) 方法将传入的对象 x 转换为字符串 s,然后将这个字符串打印出来并换行。

3. String.valueOf(Object obj) 方法

接下来看 String.valueOf(Object obj) 方法的源码,它位于 String 类中:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {// ... 其他代码 ...public static String valueOf(Object obj) {return (obj == null) ? "null" : obj.toString();}// ... 其他代码 ...
}

从这段源码可以看出,String.valueOf(Object obj) 方法会先判断传入的对象 obj 是否为 null,如果是 null 则返回字符串 "null";如果不是 null,则直接调用该对象的 toString() 方法。

总结

综上所述,当你使用 System.out.println() 方法输出一个引用类型的对象时,println 方法内部会调用 String.valueOf() 方法将对象转换为字符串,而 String.valueOf() 方法又会去调用对象的 toString() 方法(对象不为 null 的情况下),所以最终就实现了自动调用对象的 toString() 方法来输出对象的字符串表示形式。这就是为什么在 Java 中使用 println() 输出引用时会自动调用 引用.toString() 的原因。


二、equals方法

 Object类中的equals方法:

1. Object类设计equals方法的作用是什么?目的是什么?

①equals方法的作用是:判断两个对象是否相等。

②equals方法的返回值是true/false 

③true代表两个对象相等。

④false代表两个对象不相等。


2. Object类中对equals方法的默认实现是怎样的?

public boolean equals(Object obj) {return (this == obj);}

 a.equals(b) 表面是a和b的比较。实际上方法体当中是:this和obj的比较


3. 关于 == 运算符的运算规则:

 == 永远只有一个运算规则,永远比较的是变量中保存的值之间的比较。     

只不过有的时候这个值是基本数据类型。有的时候这个值是对象的内存地址。

⑴基本数据类型

==用于比较两个基本数据类型(如intbytecharlongfloatdoubleboolean)时,它直接比较的是它们的值。例如:

int num1 = 5;
int num2 = 5;
System.out.println(num1 == num2); // 输出 true

⑵引用数据类型

对于引用数据类型(如类、接口、数组等),==比较的是两个变量所保存的对象的内存地址。也就是说,如果两个引用变量指向同一个对象,那么==比较的结果为true;否则为false。例如:

String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // 输出 false,因为str1和str2指向不同的对象

⑶而在 Java 中,要比较两个对象的内容是否相等,通常需要使用对象的equals方法(前提是该类已经重写了equals方法)。例如,对于String类,equals方法被重写来比较字符串的内容:

String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1.equals(str2)); // 输出 true

总结来说,==运算符在基本数据类型和引用数据类型的比较行为上有所不同,使用时需要注意。在比较对象内容时,通常使用equals方法更为合适。


4. equals方法为什么要重写? 

因为Object类中的equals方法在进行比较的时候,比较的是两个java对象的内存地址。

我们希望比较的是对象的内容。只要对象的内容相等,则认为是相同的。


5.注意点

字符串的比较不能使用 ==,必须使用equals方法进行比较。

⑴字符串String类型已经重写了equals方法。

在Java中,String类确实重写了Object类的equals方法,用于比较两个字符串对象的内容是否相等,而不是比较对象的内存地址(即==的比较逻辑)。这是字符串操作中非常关键的特性,也是String类设计的核心之一。


String类的equals方法源码解析

String类中的equals方法重写如下:

public boolean equals(Object anObject) {if (this == anObject) { // 1. 先检查是否是同一个对象(内存地址相同)return true;}if (anObject instanceof String) { // 2. 检查是否为String类型String anotherString = (String) anObject;int n = value.length;if (n == anotherString.value.length) { // 3. 比较长度是否一致char v1[] = value; // 当前字符串的字符数组char v2[] = anotherString.value; // 目标字符串的字符数组int i = 0;while (n-- != 0) { // 4. 逐个字符比较if (v1[i] != v2[i])return false;i++;}return true;}}return false;
}

关键特性

  1. 内容比较
    equals方法比较的是字符串的实际字符内容,而非对象的内存地址。例如:

    String s1 = new String("hello");
    String s2 = new String("hello");
    System.out.println(s1.equals(s2)); // true(内容相同)
    System.out.println(s1 == s2);      // false(内存地址不同)
  2. 效率优化

    • 先检查是否是同一个对象(this == anObject),直接返回true

    • 比较长度是否相同,长度不同直接返回false

  3. 字符级比较
    对字符串的每个字符进行逐一比对,确保所有字符完全一致。

⑵equals方法重写需要“彻底”重写。

Address类:

public class Address {private String city;private String street;public Address() {}public Address(String city, String street) {this.city = city;this.street = street;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}@Overridepublic String toString() {return "Address{" +"city='" + city + '\'' +", street='" + street + '\'' +'}';}@Overridepublic boolean equals(Object obj) {if(obj == null) return false;if(this == obj) return true;if(obj instanceof Address){Address a = (Address) obj;return this.city.equals(a.city) && this.street.equals(a.street);}return false;}
}

User类:

public class User {private String name;private Address address;//对象引用public User() {}public User(String name, Address address) {this.name = name;this.address = address;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", address=" + address +'}';}@Overridepublic boolean equals(Object obj) {// u.equals(u2)// this就是u  obj就是u2if(obj == null) return false;if(this == obj) return true;if(obj instanceof User){User user = (User) obj;if(this.name.equals(user.name)  && this.address.equals(user.address)){return true;}}return false;}
}

Test类:

public class Test {public static void main(String[] args) {// 创建家庭住址对象//得先创建家庭住址对象,才能创建用户对象Address a = new Address("北京", "大兴");// 创建用户对象User u = new User("张三", a);// 创建家庭住址对象2Address a2 = new Address("北京", "大兴");// 创建用户对象2User u2 = new User("张三", a2);//输出false,虽然u和u2内容一样,但它们的内存地址不一样System.out.println(u.equals(u2));}
}

运行结果:

“彻底” 重写的理解

      “彻底” 重写 equals 方法要求在比较对象时,不仅要比较当前类的属性,还要递归地比较所有引用类型的属性。在 User 类中,address 是一个引用类型的属性,如果 Address 类没有重写 equals 方法,那么在 User 类的 equals 方法中比较 address 属性时,默认会使用 Object 类的 equals 方法,即比较引用是否相等,而不是比较 address 对象的内容是否相等。这样即使两个 User 对象的 name 和 address 的内容都相同,但由于 address 对象的引用不同,equals 方法仍然会返回 false,这显然不符合我们的预期。

       因此,为了确保 equals 方法能够准确地比较两个对象的内容是否相等,我们需要在每个包含引用类型属性的类中重写 equals 方法,并且在比较引用类型属性时,调用该属性所属类重写的 equals 方法,从而实现 “彻底” 重写。

三、hashCode()方法

关于Object类的hashCode()方法:
*      hashCode:返回一个对象的哈希值,通常作为在哈希表中查找该对象的键值。
*      Object类的默认实现是根据对象的内存地址生成一个哈希码(即将对象的内存地址转换为整数作为哈希值)。
*      hashCode()方法是为了HashMap、Hashtable、HashSet等集合类进行优化而设置的,以便更快地查找和存储对象

* * hashCode()方法在Object类中的默认实现: 这是一个本地方法,底层调用了C++写的动态链接库程序:xxx.dll

示例1:

public class Test01 {public static void main(String[] args) {Test01 t = new Test01();int i = t.hashCode();System.out.println(i); //随机输出哈希码Test01 t2 = new Test01();int i1 = t2.hashCode();System.out.println(i1);System.out.println(new Object().hashCode());System.out.println(new Object().hashCode());System.out.println(new Object().hashCode());System.out.println(new Object().hashCode());System.out.println(new Object().hashCode());System.out.println(new Object().hashCode());}
}

运行结果:

  • 功能描述:返回该对象的哈希码值。哈希码主要用于在哈希表等数据结构中快速定位和存储对象。在Object类中,默认的哈希码是根据对象的内存地址生成的。但如果重写了equals方法,通常也需要重写hashCode方法,以保证相等的对象具有相同的哈希码,这是为了满足一些集合类(如HashMapHashSet)的内部逻辑要求。

四、finalize()方法

关于Object类中的finalize()方法:
*      finalize:当java对象被回收时,由GC自动调用被回收对象的finalize方法,通常在该方法中完成销毁前的准备
*      从Java9开始,这个方法被标记已过时,不建议使用。作为了解。

* 在Object类中是这样实现的:很显然,这个方法是需要子类重写的。

protected void finalize() throws Throwable { }
  • 功能描述:当垃圾回收器确定不存在对该对象的更多引用时,由垃圾回收器在回收对象之前调用此方法。在这个方法中,可以进行一些资源释放、清理等操作,但不建议过度依赖这个方法,因为垃圾回收的时机是不确定的,而且在 Java 9 及以后的版本中,finalize方法已被标记为@Deprecated,逐渐不推荐使用。

回收对象的例子:

Person类:


public class Person {@Overrideprotected void finalize() throws Throwable {System.out.println(this + "即将被回收");}
}

测试类Test02:

public class Test02 {public static void main(String[] args) {for (int i = 0; i < 10000; i++) {//垃圾到一定程度才会调用垃圾回收器Person p1 = new Person();p1 = null;   //p1等于null,即为垃圾对象// 建议启动垃圾回收器(这只是建议启动垃圾回收器)if(i % 1000 == 0){System.gc();//System里的gc()方法就是启动垃圾回收器}}}
}

运行结果:

五、clone()方法

1.浅拷贝(Shallow Copy)

定义

浅拷贝会创建一个新对象,新对象的基本数据类型属性会复制一份新的值,而对于引用数据类型的属性,仅仅复制其引用,也就是新对象和原对象的引用类型属性会指向同一个内存地址。这意味着如果通过新对象修改引用类型属性所指向的对象内容,原对象的该属性内容也会被修改。

实现条件

在 Java 中,要实现浅拷贝,类需要满足以下条件:

  • 实现 Cloneable 接口。
  • 重写 Object 类的 clone() 方法。

User类:

public class User {private int age;public User() {}public User(int age) {this.age = age;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"age=" + age +'}';}}

诺添加以下代码

当你在 Java 代码中遇到 Unhandled exception: java.lang.CloneNotSupportedException 错误时,这通常是因为你调用了 clone() 方法,但没有对可能抛出的 CloneNotSupportedException 异常进行处理。下面为你详细分析该异常出现的原因以及解决办法。

异常原因

在 Java 里,clone() 方法定义在 Object 类中,其签名为 protected native Object clone() throws CloneNotSupportedException。这表明 clone() 方法可能会抛出 CloneNotSupportedException 异常。如果一个类没有实现 Cloneable 接口却调用了 clone() 方法,或者调用 clone() 方法时没有对 CloneNotSupportedException 进行处理,就会产生这个编译错误。

解决办法

1. 实现 Cloneable 接口

要使用 clone() 方法,类必须实现 Cloneable 接口。Cloneable 接口是一个标记接口,本身不包含任何方法,它只是告诉 Java 虚拟机该类可以被克隆。

2. 处理 CloneNotSupportedException 异常

可以使用 try-catch 块捕获该异常,或者在方法签名中使用 throws 关键字声明抛出该异常。

  public void Test() throws CloneNotSupportedException {this.clone();}
UserTest类
public class UserTest {public static void main(String[] args) {//创建User对象User user=new User(20);//克隆一个user对象user.clone();}
}
// 克隆一个user对象
// 报错原因:因为Object类中的clone()方法是protected修饰的。
// protected修饰的只能在:本类,同包,子类中访问。

Object 类中的 clone() 方法定义如下:

protected native Object clone() throws CloneNotSupportedException;

怎么解决clone()方法的调用问题?

在子类中重写该clone()方法。

   为了保证clone()方法在任何位置都可以调用,建议将其修饰符修改为:public

代码如下:

User类:

public class User {private int age;public User() {}public User(int age) {this.age = age;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"age=" + age +'}';}/*  public void Test() throws CloneNotSupportedException {this.clone();}*/@Overridepublic Object clone() throws CloneNotSupportedException{return super.clone();}
}

UserTest类:

public class UserTest {public static void main(String[] args) throws CloneNotSupportedException {//创建User对象User user=new User(20);//克隆一个user对象Object obj=user.clone();//克隆出的新对象,用Object去接收}
}

运行结果:

凡事参加克隆的对象,必须实现一个标志接口:java.lang.Cloneable *      

java中接口包括两大类:  一类是:起到标志的作用,标志型接口。 

 另一类是:普通接口。

当你遇到 java.lang.CloneNotSupportedException 异常时,这表明你尝试克隆一个不支持克隆操作的对象。在 Java 中,要使用 clone() 方法进行对象克隆,被克隆的类必须满足以下两个条件:

  1. 该类必须实现 java.lang.Cloneable 接口,Cloneable 是一个标记接口,它本身不包含任何方法,只是用于告诉 Java 虚拟机该类可以被克隆。
  2. 通常需要在该类中重写 Object 类的 clone() 方法。

异常原因分析

根据你给出的异常堆栈信息 Exception in thread "main" java.lang.CloneNotSupportedException: lianxi.oop29.User,可以知道问题出在 lianxi.oop29.User 类上,该类可能没有实现 Cloneable 接口,从而导致调用 clone() 方法时抛出 CloneNotSupportedException 异常。

User类:

public class User implements Cloneable{//打个标记,该类被克隆了private int age;public User() {}public User(int age) {this.age = age;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"age=" + age +'}';}/*  public void Test() throws CloneNotSupportedException {this.clone();}*/@Overridepublic Object clone() throws CloneNotSupportedException{return super.clone();}
}

UserTest类:

public class UserTest {public static void main(String[] args) throws CloneNotSupportedException {//创建User对象User user=new User(20);//克隆一个user对象Object obj=user.clone();//克隆出的新对象,用Object去接收System.out.println(user);// 修改克隆之后的对象的age属性User copyUser = (User) obj;copyUser.setAge(100);System.out.println("克隆之后的新对象的年龄:" + copyUser.getAge());System.out.println("原始对象的年龄:" + user.getAge());}
}

运行结果:

  • 浅拷贝的定义:对于基本数据类型字段直接复制值,对于引用类型字段仅复制引用地址(共享同一对象)。

  • 代码实现

    • User 类实现了 Cloneable 接口,并调用 super.clone()

    • 由于 age 是 int(基本数据类型),拷贝时会直接复制值,因此修改拷贝对象的 age 不会影响原始对象。

    • 若 User 类中存在引用类型字段(例如 Address 对象),浅拷贝会导致原始对象和拷贝对象共享该引用对象(修改一处会影响另一处)。


 为什么输出结果中 age 不同?

  • 基本数据类型的特点int 的值直接存储在对象内存中,浅拷贝会直接复制该值到新对象。

  • 代码逻辑验证

    User user = new User(20);        // 原始对象 age=20
    User copyUser = (User) user.clone(); // 拷贝对象 age=20(独立值)
    copyUser.setAge(100);            // 仅修改拷贝对象的 age
    • 最终 user.age 仍为 20copyUser.age 变为 100


 如果 User 类包含引用类型字段,浅拷贝会如何?

假设 User 类中添加引用类型字段 Address

class User implements Cloneable {private int age;private Address address;  // 引用类型字段// 省略其他代码...@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone(); // 浅拷贝:address字段共享同一对象}
}class Address {private String city;// 省略 getter/setter...
}
  • 验证代码

    User user = new User(20, new Address("Beijing"));
    User copyUser = (User) user.clone();copyUser.getAddress().setCity("Shanghai"); 
    System.out.println(user.getAddress().getCity()); // 输出 "Shanghai"(被修改)
    • 浅拷贝的副作用:原始对象和拷贝对象共享 address,修改一处会影响另一处。

浅拷贝内存图

Address类:

public class Address {private String city;private String street;public Address() {}public Address(String city, String street) {this.city = city;this.street = street;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}@Overridepublic String toString() {return "Address{" +"city='" + city + '\'' +", street='" + street + '\'' +'}';}
}

User类:

public class User implements Cloneable{private String name;private Address addr;public User() {}public User(String name, Address addr) {this.name = name;this.addr = addr;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", addr=" + addr +'}';}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}

测试类Test:

public class Test {public static void main(String[] args) throws CloneNotSupportedException {//创建住址对象Address a=new Address("北京","海淀");//创建User对象User user1=new User("李四",a);User user2=(User) user1.clone();//克隆一个User对象因为返回的是Object对象,所以需要转型System.out.println(user1);System.out.println(user2);user2.getAddr().setCity("天津");System.out.println("===================");System.out.println(user1);System.out.println(user2);}
}

运行结果:

jvm图

解释:

     克隆时,只克隆了User类型对象,Address没有一起克隆

     当修改city为“天津”时,city=ox11指向“天津”,因为User1和user2同时指向Address,所以同时修改为“天津”

      当引用数据类型需要克隆时(即Addr),则为深克隆

2.深拷贝(Deep Copy)

定义

深拷贝会创建一个新对象,新对象的所有属性,包括基本数据类型和引用数据类型,都会被复制一份新的值。这意味着新对象和原对象在内存中是完全独立的,修改新对象的任何属性都不会影响原对象。

实现方式

实现深拷贝有多种方式,常见的有手动实现和使用序列化与反序列化。

手动实现深拷贝

手动实现深拷贝需要在 clone() 方法中递归地复制引用类型的属性。

实现代码:

Address类:

public class Address {private String city;private String street;public Address() {}public Address(String city, String street) {this.city = city;this.street = street;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}@Overridepublic String toString() {return "Address{" +"city='" + city + '\'' +", street='" + street + '\'' +'}';}
}

User类:

public class User implements Cloneable{private String name;private Address addr;public User() {}public User(String name, Address addr) {this.name = name;this.addr = addr;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", addr=" + addr +'}';}
/*@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}*/@Overridepublic Object clone() throws CloneNotSupportedException {// 重写方法,让其达到深克隆的效果。// User要克隆,User对象关联的Address对象也需要克隆一份。Address copyAddr = (Address)this.getAddr().clone();//克隆一份AddrUser copyUser = (User)super.clone();//克隆一份UsercopyUser.setAddr(copyAddr);   //把之前克隆的Addr的地址给新Userreturn copyUser;}
}

但是注意:克隆Address,Address也要重写clone()方法,不然会报错

步骤:1.Address类添加克隆标识

           2.重写clone()方法

           3.protected修改为public

所以完整代码为:

Address类:

public class Address implements Cloneable{private String city;private String street;public Address() {}public Address(String city, String street) {this.city = city;this.street = street;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getStreet() {return street;}public void setStreet(String street) {this.street = street;}@Overridepublic String toString() {return "Address{" +"city='" + city + '\'' +", street='" + street + '\'' +'}';}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}

User类:

public class User implements Cloneable{private String name;private Address addr;public User() {}public User(String name, Address addr) {this.name = name;this.addr = addr;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", addr=" + addr +'}';}
/*@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}*/@Overridepublic Object clone() throws CloneNotSupportedException {// 重写方法,让其达到深克隆的效果。// User要克隆,User对象关联的Address对象也需要克隆一份。Address copyAddr = (Address)this.getAddr().clone();//克隆一份AddrUser copyUser = (User)super.clone();//克隆一份UsercopyUser.setAddr(copyAddr);   //把之前克隆的Addr的地址给新Userreturn copyUser;}
}

测试类Test:

public class Test {public static void main(String[] args) throws CloneNotSupportedException {//创建住址对象Address a=new Address("北京","海淀");//创建User对象User user1=new User("李四",a);User user2=(User) user1.clone();//克隆一个User对象因为返回的是Object对象,所以需要转型System.out.println(user1);System.out.println(user2);user2.getAddr().setCity("天津");System.out.println("===================");System.out.println(user1);System.out.println(user2);}
}

运行结果为:

相关文章:

Java中的object类

1.Object类是什么&#xff1f; &#x1f7ea;Object 是 Java 类库中的一个特殊类&#xff0c;也是所有类的父类(超类),位于类继承层次结构的顶端。也就是说&#xff0c;Java 允许把任何类型的对象赋给 Object 类型的变量。 &#x1f7e6;Java里面除了Object类&#xff0c;所有的…...

html2canvas绘制页面并生成图像 下载

1. 简介 html2canvas是一个开源的JavaScript库&#xff0c;它允许开发者在用户的浏览器中直接将HTML元素渲染为画布&#xff08;Canvas&#xff09;&#xff0c;并生成图像。以下是对html2canvas的详细介绍&#xff1a; 2. 主要功能 html2canvas的主要功能是将网页中的HTML元…...

Certum OV企业型通配符SSL

随着网络攻击手段的不断演变&#xff0c;仅仅依靠HTTP协议已无法满足现代企业对数据安全的需求。SSL证书&#xff0c;特别是经过严格验证的组织验证型SSL证书&#xff0c;成为了保护网站数据传输安全、提升用户信任度的标配。 一、Certum OV企业型通配符SSL概述 Certum&#…...

2024年Web前端最新Java进阶(五十五)-Java Lambda表达式入门_eclipse lambda(1),面试必备

对象篇 模块化编程-自研模块加载器 开源分享&#xff1a;【大厂前端面试题解析核心总结学习笔记真实项目实战最新讲解视频】 Arrays.sort(players, sortByName); // 1.3 也可以采用如下形式: Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2))); ??其…...

JVM 四虚拟机栈

虚拟机栈出现的背景 由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的。不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。优点是跨平台&#xff0c;指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实现同样的功能需要更多…...

V103开发笔记1-20250113

2025-01-13 一、应用方向分析 应用项目&#xff1a; PCBFLY无人机项目&#xff08;包括飞控和手持遥控器&#xff09;&#xff1b; 分析移植项目&#xff0c;应用外设资源包括&#xff1a; GPIO, PWM,USART,GPIO模拟I2C/SPI, ADC,DMA,USB等&#xff1b; 二、移植项目的基本…...

Page Assist - 本地Deepseek模型 Web UI 的安装和使用

Page Assist Page Assist是一个开源的Chrome扩展程序&#xff0c;为本地AI模型提供一个直观的交互界面。通过它可以在任何网页上打开侧边栏或Web UI&#xff0c;与自己的AI模型进行对话&#xff0c;获取智能辅助。这种设计不仅方便了用户随时调用AI的能力&#xff0c;还保护了…...

Cookie及Session---笔记

目录 Cookiecookie简介cookiesession的认证方式tpshop完整登录实现-cookie Sessionsession简介session自动管理cookietpshop完整登录实现-sessioncookie和session的区别获取响应结果指定内容 Cookie cookie简介 工程师针对HTTP协议是无连接无状态特性所设计的一种技术&#x…...

【Block总结】DASI,多维特征融合

论文信息 HCF-Net&#xff08;Hierarchical Context Fusion Network&#xff09;是一种新提出的深度学习模型&#xff0c;专门用于红外小目标检测。该论文于2024年3月16日发布&#xff0c;作者包括Shibiao Xu、ShuChen Zheng等&#xff0c;主要研究机构为北京邮电大学。该模型…...

LabVIEW的智能电源远程监控系统开发

在工业自动化与测试领域&#xff0c;电源设备的精准控制与远程管理是保障系统稳定运行的核心需求。传统电源管理依赖本地手动操作&#xff0c;存在响应滞后、参数调节效率低、无法实时监控等问题。通过集成工业物联网&#xff08;IIoT&#xff09;技术&#xff0c;实现电源设备…...

4.PPT:日月潭景点介绍【18】

目录 NO1、2、3、4​ NO5、6、7、8 ​ ​NO9、10、11、12 ​ 表居中或者水平/垂直居中单元格内容居中或者水平/垂直居中 NO1、2、3、4 新建一个空白演示文稿&#xff0c;命名为“PPT.pptx”&#xff08;“.pptx”为扩展名&#xff09;新建幻灯片 开始→版式“PPT_素材.doc…...

《迪拜AI展:探寻中东人工智能发展的璀璨新篇》

迪拜&#xff1a;AI 浪潮下的闪耀明珠 迪拜&#xff0c;这座位于阿拉伯半岛东部、波斯湾东南岸的城市&#xff0c;犹如一颗璀璨的明珠&#xff0c;在中东地区散发着独特的魅力。它是阿拉伯联合酋长国的第二大城市&#xff0c;也是迪拜酋长国的首府 &#xff0c;凭借优越的地理位…...

axios如何利用promise无痛刷新token

目录 需求 需求解析 实现思路 方法一&#xff1a; 方法二&#xff1a; 两种方法对比 实现 封装axios基本骨架 instance.interceptors.response.use拦截实现 问题和优化 如何防止多次刷新token 同时发起两个或以上的请求时&#xff0c;其他接口如何重试 最后完整代…...

R语言 | 使用 ComplexHeatmap 绘制热图,分区并给对角线分区加黑边框

目的&#xff1a;画热图&#xff0c;分区&#xff0c;给对角线分区添加黑色边框 建议直接看0和4。 0. 准备数据 # 安装并加载必要的包 #install.packages("ComplexHeatmap") # 如果尚未安装 library(ComplexHeatmap)# 使用 iris 数据集 #data(iris)# 选择数值列&a…...

TensorFlow 简单的二分类神经网络的训练和应用流程

展示了一个简单的二分类神经网络的训练和应用流程。主要步骤包括&#xff1a; 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与部署 加载和应用已训练的模型 1. 数据准备与预处理 在本例中&#xff0c;数据准备是通过两个 Numpy 数…...

蓝桥杯备考:模拟算法之字符串展开

P1098 [NOIP 2007 提高组] 字符串的展开 - 洛谷 | 计算机科学教育新生态 #include <iostream> #include <cctype> #include <algorithm> using namespace std; int p1,p2,p3; string s,ret; void add(char left,char right) {string tmp;for(char ch left1;…...

[创业之路-282]:《产品开发管理-方法.流程.工具 》-1- 优秀研发体系的特征、IPD关注的4个关键要素、IPD体系的7个特点

目录 一、优秀研发体系的特征 二、IPD关注的4个关键要素 1. 组织管理 2. 市场管理 3. 流程管理 4. 产品管理 三、IPD体系的7个特点 1、产品开发是投资行为&#xff1a; 2、基于市场的产品研发&#xff1a; 3、平台化开发&#xff0c;大平台&#xff0c;小产品&#x…...

Node.js 与 PostgreSQL 集成:深入 pg 模块的应用与实践

title: Node.js 与 PostgreSQL 集成:深入 pg 模块的应用与实践 date: 2025/2/5 updated: 2025/2/5 author: cmdragon excerpt: 随着 JavaScript 在服务器端编程中的兴起,Node.js 已成为构建高性能网络应用程序的重要平台。PostgreSQL 则以其强大的特性以及对复杂数据结构的…...

【Uniapp-Vue3】从uniCloud中获取数据

需要先获取数据库对象&#xff1a; let db uniCloud.database(); 获取数据库中数据的方法&#xff1a; db.collection("数据表名称").get(); 所以就可以得到下面的这个模板&#xff1a; let 函数名 async () > { let res await db.collection("数据表名称…...

LeetCode 0090.子集 II:二进制枚举 / 回溯

【LetMeFly】90.子集 II&#xff1a;二进制枚举 / 回溯 力扣题目链接&#xff1a;https://leetcode.cn/problems/subsets-ii/ 给你一个整数数组 nums &#xff0c;其中可能包含重复元素&#xff0c;请你返回该数组所有可能的 子集&#xff08;幂集&#xff09;。 解集 不能 …...

Pytest+selenium UI自动化测试实战实例

今天来说说pytest吧&#xff0c;经过几周的时间学习&#xff0c;有收获也有疑惑&#xff0c;总之最后还是搞个小项目出来证明自己的努力不没有白费。 环境准备 1 确保您已经安装了python3.x 2 配置python3pycharmselenium2开发环境 3 安装pytest库pip install p…...

黑马点评 - 商铺类型缓存练习题(Redis List实现)

首先明确返回值是一个 List<ShopType> 类型那么我们修改此函数并在 TypeService 中声明 queryTypeList 方法&#xff0c;并在其实现类中实现此方法 GetMapping("list")public Result queryTypeList() {return typeService.queryTypeList();}实现此方法首先需要…...

C++ 创建和配置dll与lib库

C简明教程&#xff08;13&#xff09;创建和配置dll与lib库_怎样生成lib库和dll库-CSDN博客 C 动态库与静态库详解 一、为什么要引入库的概念 在 C 编程中&#xff0c;随着项目规模的不断扩大&#xff0c;代码量也会急剧增加。如果将所有代码都写在一个源文件中&#xff0c;…...

深度剖析 Veo2 工具:解锁 AI 视频创作新境界

在当下这个 AI 技术日新月异的时代,各种 AI 工具如雨后春笋般涌现,让人目不暇接。今天,我就来给大家好好说道说道谷歌旗下的 Veo2,这可是一款在 AI 视频创作领域相当有分量的工具。好多朋友都在问,Veo2 到底厉害在哪?好不好上手?能在哪些地方派上用场?别着急,今天我就…...

LabVIEW自定义测量参数怎么设置?

以下通过一个温度采集案例&#xff0c;说明在 LabVIEW 中设置自定义测量参数的具体方法&#xff1a; 案例背景 ​ 假设使用 NI USB-6009 数据采集卡 和 热电偶传感器 监测温度&#xff0c;需自定义以下参数&#xff1a; 采样率&#xff1a;1 kHz 输入量程&#xff1a;0~10 V&a…...

JVM执行流程与架构(对应不同版本JDK)

直接上图&#xff08;对应JDK8以及以后的HotSpot&#xff09; 这里主要区分说明一下 方法区于 字符串常量池 的位置更迭&#xff1a; 方法区 JDK7 以及之前的版本将方法区存放在堆区域中的 永久代空间&#xff0c;堆的大小由虚拟机参数来控制。 JDK8 以及之后的版本将方法…...

数据治理项目为什么沦为了PPT工程?

数据治理项目为什么沦为了PPT工程&#xff1f; 数据治理项目为什么沦为PPT工程数据治理项目面临的深层挑战数据治理项目的破局之道 "这个项目明明做了快一年了&#xff0c;怎么感觉还在原地踏步&#xff1f;"数据治理小张最近很烦恼。 整天泡在会议室里&#xff0c;写…...

module ‘matplotlib.cm‘ has no attribute ‘get_cmap‘

目录 解决方法1&#xff1a; 解决方法2&#xff0c;新版api改了&#xff1a; module matplotlib.cm has no attribute get_cmap 报错代码&#xff1a; cmap matplotlib.cm.get_cmap(Oranges) 解决方法1&#xff1a; pip install matplotlib3.7.3 解决方法2&#xff0c;新版…...

HTML5 教程之标签(3)

HTML5 <center> 标签 (已废弃) 定义和用法 <center> 标签对其包围的文本进行水平居中处理。HTML5不支持使用<center>标签&#xff0c;因此有关该标签的更多信息&#xff0c;请参考“HTML <center>标签”部分&#xff01; 示例: <center>这个…...

告别传统办公软件,这款编辑器让你事半功倍!

文章目录 1 界面的多样性2 性能优化3 文档编辑器的新功能4 外部文本支持5 体验感想 ONLYOFFICE最近发布了文档8.2版本&#xff0c;带来了众多新特性和性能改进。作为一名用户和开发者&#xff0c;我对这些更新进行了深入的体验&#xff0c;感受到了不少亮点。 新版本特别强调了…...

AI协助探索AI新构型自动化创新的技术实现

一、AI自进化架构的核心范式 1. 元代码生成与模块化重构 - 代码级自编程&#xff1a;基于神经架构搜索的强化学习框架&#xff0c;AI可通过生成元代码模板&#xff08;框架的抽象层定义、神经元结点-网络拓扑态的编码抽象定义&#xff09;自动组合功能模块。例如&#xff0…...

全能型免费内网穿透工具,全面支持macOS、Windows、Linux及Docker系统

1. 登陆官网网址并注册帐号 ngrok | API Gateway, Kubernetes Networking Secure Tunnels 2 下载并安装工具 3. 启动工具 在命令行执行 ngrok http http://localhost:8080 其中端口可换成用户自己想要穿透的端口 4. 获取穿透地址 命令执行后会出现如下画面&#xff0c;红…...

Web - CSS3浮动定位与背景样式

概述 这篇文章主要介绍了 CSS3 中的浮动定位、背景样式、变形效果等内容。包括 BFC 规范与创建方法、浮动的功能与使用要点、定位的多种方式及特点、边框与圆角的设置、背景的颜色、图片等属性、多种变形效果及 3D 旋转等&#xff0c;还提到了浏览器私有前缀。 BFC规范与浏览…...

VUE之组件通信(二)

1、v-model v-model的底层原理&#xff1a;是:value值和input事件的结合 $event到底是啥&#xff1f;啥时候能.target 对于原生事件&#xff0c;$event就是事件对象 &#xff0c;能.target对应自定义事件&#xff0c;$event就是触发事件时&#xff0c;所传递的数据&#xff…...

Gauss高斯:建表语法,存储方式,OLTP和OLAP,系统时间,数组,分组(grouping set,rollup)

数据库和表的语法 数据库 表 oracle,高斯, hive的默认存储方式都是列式存储 存储方式 高斯数据库&#xff08;GaussDB&#xff09;支持列式存储和行式存储 OLTP 与 OLAP OLTP&#xff08;联机事务处理&#xff0c;Online Transaction Processing&#xff09;是一种用于管理…...

Java基础进阶

Java基础进阶 异常 概述 异常就是程序出现了不正常的情况 具体分为&#xff1a;Throwable—>(Error Exception);Exception—>(RuntimeException 非RuntimeException) Throwable类是Java语言中所有错误和异常的祖宗类&#xff1b;&#xff08;上面还有Object类) Thr…...

【数据结构】链表应用1

链表应用 面试题 02.02.返回倒数第k个节点题目描述思路解题过程复杂度 查找相同后缀题目描述解题思路完整代码&#xff1a; 删除绝对值相等的节点题目描述解题思路代码 面试题 02.02.返回倒数第k个节点 题目描述 实现一种算法&#xff0c;找出单向链表中倒数第 k 个节点。返回…...

python gltf生成预览图

使用Python生成GLTF模型的预览图 随着3D技术的不断发展&#xff0c;GLTF&#xff08;GL Transmission Format&#xff09;逐渐成为了Web和移动应用程序中最流行的3D文件格式之一。GLTF文件不仅能以较小的体积存储复杂的3D模型&#xff0c;还支持动画、材质、光照和纹理等特性。…...

HTTP和HTTPS协议详解

HTTP和HTTPS协议详解 HTTP详解什么是http协议http协议的发展史http0.9http1.0http1.1http2.0 http协议的格式URI和URL请求request响应response http协议完整的请求与响应流程 HTTPS详解为什么使用HTTPSSSL协议HTTPS通信过程TLS协议 HTTP详解 什么是http协议 1、全称Hyper Tex…...

实战:利用百度站长平台加速网站收录

本文转自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/33.html 利用百度站长平台加速网站收录是一个实战性很强的过程&#xff0c;以下是一些具体的步骤和策略&#xff1a; 一、了解百度站长平台 百度站长平台是百度为网站管理员提供的一系列工…...

专门记录台式电脑常见问题

1、蓝屏死机&#xff0c;检查内存硬盘和cpu 2、拆内存条&#xff0c;用橡皮擦金手指 3、放主板静电&#xff0c;扣主板电池 4、系统时间不正确&#xff0c;主板电池没电 5、开机键坏了 6、电脑主机的风扇转&#xff0c;正常通电运行&#xff0c;但显示器没信号。看键盘的num键&…...

数据库系统概念第六版记录 一

1.关系型数据库 关系型数据库&#xff08;Relational Database&#xff0c;简称 RDB&#xff09;是基于关系模型的一种数据库&#xff0c;它通过表格的形式来组织和存储数据。每个表由若干行&#xff08;记录&#xff09;和列&#xff08;字段&#xff09;组成&#xff0c;数据…...

本地Ollama部署DeepSeek R1模型接入Word

目录 1.本地部署DeepSeek-R1模型 2.接入Word 3.效果演示 4.问题反馈 上一篇文章办公新利器&#xff1a;DeepSeekWord&#xff0c;让你的工作更高效-CSDN博客https://blog.csdn.net/qq_63708623/article/details/145418457?spm1001.2014.3001.5501https://blog.csdn.net/qq…...

Meta Sapiens AI论文解读:人类视觉模型基石初现,AI 未来走向何方?

一、引言 在本文中&#xff0c;我们将深入探讨 Meta AI 的一项新成果&#xff0c;该成果发表于一篇题为《Sapiens&#xff1a;人类视觉模型的基础》的研究论文中。这篇论文介绍了一系列模型&#xff0c;这些模型针对四项以人类为中心的基本任务&#xff0c;正如我们在上面的演示…...

输入类控件和多元素控件【QT】

文章目录 输入类控件QLineEdit Text EditCombo BoxSpin BoxDialSlider多元素控件QListWidget TableWidetTreeWidgetQGroupBoxTab Widget# QVBoxLayout# QHBoxLayoutQGridLayoutQFormLayout 输入类控件 QLineEdit 例如&#xff1a; 实现一个用户输入姓名 密码 电话 性别 的功能…...

一键开启/关闭deepseek

一键开启/关闭 Deepseek对应下载的模型一键开启 Deepseek&#xff0c;一键关闭Deepseek双击对应的bat&#xff0c;就可以启动https://mbd.pub/o/bread/Z56YmpZvbat 下载&#xff1a;https://mbd.pub/o/bread/Z56YmpZv 可以自己写下来&#xff0c;保存成bat文件&#xff0c;也可…...

gitea - fatal: Authentication failed

文章目录 gitea - fatal: Authentication failed概述run_gitea_on_my_pkm.bat 笔记删除windows凭证管理器中对应的url认证凭证启动gitea服务端的命令行正常用 TortoiseGit 提交代码备注END gitea - fatal: Authentication failed 概述 本地的git归档服务端使用gitea. 原来的用…...

Spring AI 智能体通过 MCP 集成本地文件数据

作者&#xff1a;刘军 Model Context Protocol&#xff08;MCP&#xff09;简介 模型上下文协议&#xff08;即 Model Context Protocol&#xff0c;MCP&#xff09; [ 1] 是一个开放协议&#xff0c;它规范了应用程序如何向大型语言模型&#xff08;LLM&#xff09;提供上下…...

音视频入门基础:RTP专题(5)——FFmpeg源码中,解析SDP的实现

一、引言 FFmpeg源码中通过ff_sdp_parse函数解析SDP。该函数定义在libavformat/rtsp.c中&#xff1a; int ff_sdp_parse(AVFormatContext *s, const char *content) {const char *p;int letter, i;char buf[SDP_MAX_SIZE], *q;SDPParseState sdp_parse_state { { 0 } }, *s1…...

MyBatis XML文件配置

目录 一、 配置连接字符串和MyBatis 二、书写持久层代码 2.1 添加Mapper接口 2.2 添加UserlnfoXMLMapper.xml 三、增删改查 3.1 、增&#xff08;Insert&#xff09; 3.2、删&#xff08;Delete) 3.3、改 (Update) 3.4、查 (Select) MyBatisXML的方式需要以下两步&am…...