API(Application Programming Interface):应用程序编程接口。
Java API:就是Java提供给我们使用的类,这些类将底层的实现封装起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用。
Object类:类层次结构的跟类,所有类都直接或者间接的继承自该类
构造方法:public Object()
子类的构造方法会默认访问父类的无参构造方法以完成父类数据初始化;
public class Demo1 {
public static void main(String[] args) {
//Object类是所有类的顶层父类。
Object o = new Object();
Object o1 = new Object();
System.out.println(o); //这里打印的是地址值
System.out.println(o1);
System.out.println(o == o1); //false
//boolean equals (Object obj) 指示其他某个对象是否与此对象“相等”,比较两个对象的地址值是否相等
boolean b = o.equals(o1);
System.out.println(b); //false
}
}
案例演示
public int hashCode()
a、返回该对象的哈希码值。默认情况下,该方法会根据对象的地址来计算
b、不同对象的hashCode()一般来说是不相同的,但是,同一个对象的hashCode()值相同
c、不是对象的实际地址值,可以理解为逻辑地址值
public class Demo2 {
public static void main(String[] args) {
Object o = new Object();
System.out.println(o); //java.lang.Object@1540e19d
int hashCode = o.hashCode();
System.out.println(hashCode); //356573597
}
}
案例演示
public final Class getClass()
a、返回此Objiect的运行时类
b、可以通过Class类中的public String getName(),获取对象的真实类的全名称
public class Demo3 {
public static void main(String[] args) {
Object o = new Object();
//返回Obiect.class类的字节码文件对象
Class aClass = o.getClass();
System.out.println(aClass); //class java.lang.Object
//获取对象的真实类的全名称
System.out.println(aClass.getName());//java.lang.Object
Object o1 = new Object();
Class aClass1 = o1.getClass();
System.out.println(aClass1); //class java.lang.Object
System.out.println(aClass1.getName());//java.lang.Object
System.out.println(aClass == aClass1); //true
}
}
A:案例演示
public String toString()
a:返回该对象的字符串表示。
源代码:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
b:它的值等于:
getClass().getName() + '@' + Integer.toHexString(hashCode())
c:由于默认情况下的数据对我们来说没有意义,一般建议重写该方法。
怎么重写, 一般是将该类的所有的成员变量组成返回即可
B:最终版
自动生成
C: 直接输出对应的名称,就是调用对象的toString()方法
public class Demo4 {
public static void main(String[] args) {
//public String toString () 返回该对象的地址值的字符串表现形式
Object o = new Object();
System.out.println(o); //java.lang.Object @ 1540e19d
System.out.println(o.toString()); //java.lang.Object @ 1540e19d
String s = o.toString();
System.out.println(s); //java.lang.Object @ 1540e19d
}
}
案例演示
a:指示其他某个对象是否与此对象“相等”。
源代码:
public boolean equals(Object obj) {
return (this == obj);
}
b:默认情况下比较的是对象的引用是否相同。
c:由于比较对象的引用没有意义,一般建议重写该方法。一般用于比较成员变量的值是否相等
public class Demo5 {
//boolean equals(Object object)指示其他某个对象是否与此对象相等。比较两个对象的地址值是否相等。
public static void main(String[] args) {
Object o = new Object();
Object o1 = new Object();
System.out.println(o == o1); //false
System.out.println(o.equals(o1)); //false
}
}
我们来写一个简单的案例来总结一下上面的内容:
//测试类
public class Test {
public static void main(String[] args) {
//我们创建好了Student类,现在来创建一个对象
Student student = new Student();
//Student类没有继承任何类,我们就默认它继承了Object类
System.out.println(student.hashCode());
System.out.println(student.toString());
System.out.println(student.getClass());
/*没有重写前的输出
* 356573597
* org.westos.demo1.Student@1540e19d
* class org.westos.demo1.Student
* */
//这些方法在我看来没有实际意义,我希望toString()方法可以输出学生类的成员变量
//那么我就重写Objiect类中的toString()方法以获取成员变量
//快捷键是alt+insert,选择toString(),写在学生类里
//我们再来调用toString()方法
System.out.println(student.toString());
/*
* student{name='null', age=0}
* */
Student student1 = new Student("张小凡", 20);
Student student2 = new Student("张小凡", 20);
Student student3 = new Student("鬼厉",25);
//我们已经知道学生类是继承Object类的,Object类中的equals方法是比较地址值
student1.equals(student2); //false
student1.equals(student3); //false
//这里student1,student2,student3不是同一个对象,它们的地址值肯定不同
//我希望重写equals方法,只要姓名和年龄都相同,就输出true
student.equals(student1); //true
student.equals(student2); //false
}
}
//学生类
public class Student {
private String name;
private int age;
//构造方法
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//外界访问成员变量
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//我们重写Objiect类的toString方法,形成我们学生类独特的方法:显示学生的姓名和年龄
//快捷键alt+insert——>toString()
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//重写equals方法
@Override
public boolean equals(Object obj) {
//我们先判断传入的方法是否是参数本身,如果是它自身就没有比较的必要了
if (this == obj) {
return true;
}
//为了让方法更健全,我们考虑一些特殊的情况
//万一传进来的参数不是Student类型怎么办?
//万一传进来的参数是null怎么办?
if (obj == null && !(obj instanceof Student)) {
return false;
}
//向下转型
Student o = (Student) obj;
//如果姓名和年龄都相等就返回true
return this.name.equals(o.name) && this.age == o.age;
}
/*
* 重写equals方法就必须重写hashCode方法,进而保证两个对象能得到相同的哈希码
* 否则两个等价对象可能得到不同的哈希码,在集合框架内可能产生严重的问题
* */
//我们可以利用java.util.Objects类中的hash方法
/*
* public static int hash(Object... values)为输入值序列生成哈希码。
* 生成哈希码,就好像将所有输入值都放入数组一样
* 并且该数组通过调用Arrays.hashCode(Object[])进行哈希处理。
* */
@Override
public int hashCode() {
return Objects.hash(name, age);
}
//上面重写equals方法和hashCode方法可以一键生成:alt+insert——>equals() and hashCode()
}
https://www.cnblogs.com/Eason-S/p/5524837.html
==:比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是相同一个对象。比较的是真正意义上的指针操作。
equals用来比较的是两个对象的内容是否相等。由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。
简单总结一下:
Object类中的equals和 == 并没有区别,只是equals方法不能比较基本类型,只能比较引用类型,从equals的原码就可以看出:
public boolean equals(Object obj) {
return (this == obj);
}
两者产生区别的原因在于有些类(像String、Integer等)对equals进行了重写
来看String类中的equals方法原码:
public boolean equals(Object anObject) {
//判断参数对象是否是其自身
if (this == anObject) {
return true;
}
//判断参数对象是否是String类型
if (anObject instanceof String) {
//向下转型
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
如果没有对equals方法进行重写(比如我们自己写的类)就只能从Object类中继承equals方法,它与 == 是等效的,不同的是==的速度更快。
public class Demo6 {
public static void main(String[] args) {
//我们分别定义两个变量a、b和两个字符串对象c、d来测试
int a = 1;
double b = 1.0;
String c = "A";
//这是一种非常特殊的形式,和new有着本质的区别
//它是java中唯一不需要new就可以产生对象的途径
//以该形式声明的字符串, 只要值相等,任何多个引用都指向同一对象
String d = new String("A");
//现在来看==和equals的区别
System.out.println(a == b); //true
/*
* ==比较的是变量(栈)内存中存放的对象的(堆)内存地址
* 判断两个对象的地址是否相同,即是否是同一个对象
* 如果是具体的阿拉伯数字比较,值相等就为true
* 这里a和b相等因为它们都指向地址为1的堆
* */
System.out.println(c == d); //false
/*
* 如果我们要比较字符串对象c和d的内容是否相同,就不能用==
* 因为很明显c和d是两个对象,它们的地址肯定不同
* Object类中的equals方法和==没有区别,但String类重写了equals
* 所以我们选择用equals方法,判断内容是否相同
* */
System.out.println(c.equals(d)); //true
}
}
clone()的权限修饰符是受保护的,在用的时候,让该类重写该方法,并把该类方法的权限修饰符改为public
使用clone()方法采用的是浅克隆的方式。
对象浅克隆需要注意的细节:
1、如果一个对象需要调用clone方法克隆,那么该对象所属的类必须实现Cloneable接口。
2、Cloneable接口只不过是一个表示接口,没有任何方法。
3、对象的浅克隆就是克隆一个对象的时候,如果被克隆的对象中维护了另外一个类的对象,这时候知识克隆另外一个对象的地址,而没有把另外一个对象也克隆一份。
4、对象的浅克隆也不会调用到构造方法的。
对象的深克隆(后面讲):采用IO流实现,使用ObjectOutputStream将对象写入文件,然后再采用ObjectInputStream读取出来
public class Test {
//有异常我们抛出即可,后面再讲。
public static void main(String[] args) throws CloneNotSupportedException {
//我们首先创建新对象
Person p1 = new Person("李云龙", 50);
//我们接下来调用克隆方法,要确保Person类中重写了clone方法,否则访问不到
Object p2 = p1.clone();
//我们输出两个对象,会发现他们是一样的
System.out.println(p1.toString());
System.out.println(p2.toString());
/*Person{name='李云龙', age=50, address=null}
* Person{name='李云龙', age=50, address=null}*/
//如果我们改变某一个的基本数据类型变量name和age,另一个会不会受影响呢?
p1.name = "赵刚";
System.out.println(p1.toString());
System.out.println(p2.toString());
/*Person{name='赵刚', age=50, address=null}
*Person{name='李云龙', age=50, address=null}*/
//显然它们之间的基本数据类型变量之间并不受影响。
//我们再创建一个对象,并给他赋上引用类型Address
Address address = new Address("晋西北");
Person p3 = new Person("丁伟", 40, address);
//克隆一个新对象
Object p4 = p3.clone();
System.out.println(p3.toString());
System.out.println(p4.toString());
/*Person{name='丁伟', age=40, address=Address{city='晋西北'}}
*Person{name='丁伟', age=40, address=Address{city='晋西北'}}*/
//我们改变引用数据类型变量,它们之间会影响吗?
p3.address.city = "上海";
System.out.println(p3.toString());
System.out.println(p4.toString());
/*Person{name='丁伟', age=40, address=Address{city='上海'}}
*Person{name='丁伟', age=40, address=Address{city='上海'}}*/
//它们之间是互相影响的,一个改变,两个都会发生变化。
/*这就是浅克隆的弊端:不能达到完全复制、相互之间完全没有影响的目的
* 这就需要用到深克隆,我们学了IO流之后再讲*/
}
}
//Person类
//使用clone方法必须满足:
// 实现Cloneable接口
// 使用public访问修饰符重新定义clone方法。
class Person implements Cloneable {
public String name;
public int age;
public Address address;
//构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, Address address) {
this.address = address;
this.age = age;
this.name = name;
}
//重写Object类中的clone方法
//为什么重写,因为原clone方法是受保护的,我们访问不到
//我们只需要用public修饰符重新定义clone方法就行
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
//重写一下toString方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
//地址类
class Address {
public String city;
public Address(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
'}';
}
}
https://blog.csdn.net/jeffleo/article/details/76737560
小结:在Java中对象的克隆有深克隆和浅克隆之分。有这种区分的原因是Java中分为基本数据类型和引用数据类型,对于不同的数据类型在内存中的存储的区域是不同的。
基本数据类型存储在栈中,引用数据类型存储在堆中。
浅克隆不能达到到完全复制、相互之间完全没有影响的目的。
浅克隆不会克隆原对象中的引用类型,仅仅拷贝了引用类型的指向。
深克隆则拷贝了所有。也就是说深克隆能够做到原对象和新对象之间完全没有影响。
而深克隆的实现就是在引用类型所在的类实现Cloneable接口,并使用public访问修饰符重写clone方法。