1、路径小知识
相对路径:将当前项目作为根目录(test.Test.src.com.changyonglei)
绝对路径/完整路径:带上盘符:(D:\IdeaProjects\src\test\Test\src\com\changyonglei\Student.java)
2、Object类:
Java帮助文档中的解释:
Object:Class Object是类Object结构的根。 每个class都有Object作为超类。 所有对象(包括数组)都实现了这个类的方法。 java中每个类都直接或者间接的继承了Object类
下面是Object类中的方法:
(1)public int hashCode():
返回对象的哈希码值。
支持这种方法是为了散列表,如HashMap提供的那样。
注意:这里的哈希码值是根据哈希算法计算出来的一个值。这个值和地址有关系,但是这里返回的地址值并不是实际的地址值 现在就简单理解为地址值的另外一种表现形式。
(2)public final Class getClass():返回的是该对象的类对象
返回此Object的运行时类。 返回的类对象是被表示类的static synchronized方法锁定的对象。
public class StduentTest {
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(s1.hashCode()); // 1163157884
Student s2 = new Student();
System.out.println(s2.hashCode()); // 1956725890
Student s3 = s1;
System.out.println(s3.hashCode()); // 1163157884
System.out.println("=========================================");
Student s4 = new Student();
System.out.println(s4.getClass());
// class test.Test.src.com.changyonglei.Student
Class studentClass = s4.getClass();
// 返回由类对象表示的实体的名称(类,接口,数组类,原始类型或void),作为String 。
System.out.println(studentClass.getName());
// test.Test.src.com.changyonglei.Student
//链式编程
System.out.println(s4.getClass().getName());
// test.Test.src.com.changyonglei.Student
}
}
(3) public String toString():返回对象的字符串表示形式。
一般来说, toString方法返回一个“textually代表”这个对象的字符串。结果应该是一个简明扼要的表达,容易让人阅读。 建议所有子类覆盖此方法。
该toString类方法Object返回一个由其中的对象是一个实例,该符号字符`的类的名称的字符串@ ”和对象的哈希码的无符号的十六进制表示。
换句话说,这个方法返回一个等于下列值的字符串:
getClass().getName() + '@' + Integer.toHexString(hashCode())
简单理解:未重写前toString的实际意思就是:
该符号字符的类的名称的字符串+字符'@'+该对象的地址值
Integer: public static String toHexString(int i) :返回整数参数的字符串表示形式,作为16位中的无符号整数。 将哈希值转化一个地址值。
我们虽然掌握了toString()的方法使用,但是呢打印的一个结果是一个我们看不懂的地址值,换句话我们拿到这个结果没有意义 返回对象的字符串表示形式,实际上我们更想去看的是该对象中各个成员变量的值。
恰好toString()方法是被public修饰的,也恰好它的返回值是String类型的,所以我们可以在其他类中对它做重写。
简单理解就是toString在重写之后表示的就是打印某个类中的各个成员变量的值
今后无特殊情况不需要自己手写,自动生成即可。
例子:一个标准类的4.0版本:
成员变量:使用private关键字修饰
构造方法:一个无参。一个带所有参数的方法。
成员方法:setXxx(...)/getXxx()
toString():自动生成即可,替代掉我们之前show方法。
public class Student2 extends Object { private String name; private int age; public Student2() { } public Student2(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; } //--------重写之后的toString方法-------- @Override public String toString() { return "Student2{" + "name='" + name + '\'' + ", age=" + age + '}'; } //========重写之后的toString方法======== } // 测试类 public class StudentTest2 { public static void main(String[] args) { Student2 s = new Student2(); System.out.println(s.toString()); // 输出未进行赋值前的对象s中的成员变量的默认值 System.out.println("======================================="); System.out.println(s.getClass().getName()); // 输出对象s所在文件的相对路径 System.out.println("======================================="); // toString()等价于getClass().getName() + '@' + Integer.toHexString(hashCode()) // s.getClass().getName()+'@'+ Integer.toHexString(s.hashCode()) // this.getClass().getName()+'@'+ Integer.toHexString(this.hashCode()) System.out.println(s.toString()); System.out.println(s.getClass().getName()+"@"+Integer.toHexString(s.hashCode())); // 输出未进行赋值前的对象s中的成员变量的默认值 // 输出相对路径+当前对象的地址值 System.out.println("========================================"); Student2 s2 = new Student2("阿杰", 18); System.out.println(s2.toString()); // 输出赋值后的s中的成员变量的值
运行结果:
(4) public boolean equals(Object obj):指示一些其他对象是否等于此。
今后我们想要弄清楚一个方法的实现的时候,想要弄明白结果是为什么的时候,看源码
将鼠标光标放置要看的方法上,按下ctrl+鼠标左键查看源码
通过观察源码发现:Object类中的equals方法实现底层依旧是==
public boolean equals(Object obj) {111
return (this == obj);
}
而==比较引用数据类型的时候,比较是地址值,当地址值不一样的时候,返回的是false
==:
基本数据类型的时候:比较的是两个值是否一样
引用数据类型的时候:比较的是两个对象的地址值是否一样
equals:
只能比较的引用数据类型
实际开发的时候,调用equals方法更希望它比较的是成员变量的值是否一样
所以我们应该在子类中进行重写 不需要我们自己动手,自动生成即可
总结: equals重写之前:子类若是没有重写equals方法,使用的是父类Object类中的方法,比较的是地址值。
equals重写之后:子类要是重写了equals方法,比较的是成员变量值是否相同。
import java.util.Objects; public class Student3 extends Object { private String name; private int age; public Student3() { } public Student3(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; } // @Override // public String toString() { // // @Override public String toString() { return "Student2{" + "name='" + name + '\'' + ", age=" + age + '}'; } // return "姓名:" + name + ",年龄:" + age; // } //--------重写之后的equals方法-------- //s1.equals(s2) @Override public boolean equals(Object o) { //this -- s1 //o -- s2 if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student3 student3 = (Student3) o; return age == student3.age && Objects.equals(name, student3.name); } //========重写之后的equals方法======== } // 测试类: public class StudentTest3 { public static void main(String[] args) { Student3 s1 = new Student3("小刘", 18); Student3 s2 = new Student3("小刘", 18); System.out.println(s1==s2); // false Student3 s3 = s1; System.out.println(s1==s3); // true System.out.println("=========================="); System.out.println(s1.equals(s2)); // false //true System.out.println(s1.equals(s3)); // true System.out.println(s1.equals(s1)); // true // 虽然我们搞清楚了equals的比较方式,但是我们观察现实生活中,姓名一样,年龄一样,就说明是同一 // 个人,应该返回的是true Student3 s4 = new Student3("小王", 19); System.out.println(s1.equals(s4)); } }
(5)protected void finalize():
当垃圾收集确定不再有对该对象的引用时,垃圾收集器在对象上调用该对象。 一个子类覆盖了处理系统资源或执行其他清理的finalize方法。 简单理解这个方法为用于垃圾回收的,什么时候回收,不确定 GC机制,标记法。
(6) protected Object clone():
创建并返回此对象的副本。 执行特定的克隆工作。
其他包中的子类要想使用被protected修饰的方法,使用super关键字调用。 clone的方法Object执行特定的克隆操作。
首先,如果此对象的类不实现接口Cloneable ,则抛出CloneNotSupportedException 。 一个类要想使用clone(),就必须实现Cloneable接口 通过观察API发现,Cloneable接口中没有常量,也没有抽象方法 今后看到类似于Cloneable一样,里面什么都没有的接口,我们称之为标记接口。
拷贝在IT行业中常见两种:
浅拷贝: 浅拷贝是指我们拷贝出来的对象的内部引用类型变量和原来的对象内部引用类型变量的地址值是一样的(指向的是同一个对象) 但是整个拷贝出来的对象和新对象不是同一个地址值。
深拷贝: 全部拷贝对象的内容,包括内存的引用类型也进行拷贝,拷贝的时候,重新创建一个对象,成员变量值和原来被拷贝的一样。 但是后续再对拷贝后的引用数据类型变量做修改,不会影响到原来被拷贝的对象。
拷贝案例:
首先需要定义一个空类Demo,用于在实现克隆的类中定义引用数据类型的变量。
public class Demo { }
定义实现克隆接口的子类:
import java.util.Objects; //Cop1实现了标记接口Cloneable,表示这个允许被克隆 class Cop1 extends Object implements Cloneable { private String name; private int age; private Demo demo;//定义引用数据类型变量 public Cop1() { } public Cop1(String name, int age, Demo demo) { this.name = name; this.age = age; this.demo=demo; } 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; } public Demo getDemo() { return demo; } public void setDemo(Demo demo) { this.demo = demo; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Cop1 cop1 = (Cop1) o; return age == cop1.age && name.equals(cop1.name) && demo.equals(cop1.demo); } @Override public String toString() { return "Cop1{" + "name='" + name + '\'' + ", age=" + age + ", demo=" + demo + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
这里是测试类:
// 测试类 public class Test7Demo { public static void main(String[] args) throws CloneNotSupportedException { Demo demo=new Demo(); Cop1 c1=new Cop1("阿杰",24,demo); Object c2 = c1.clone();//c1克隆对象c2。隐含一个多态 Cop1 c3=(Cop1)c1;//向下转型 System.out.println(c1.toString());//拷贝前对象的成员变量值 System.out.println(c3.toString());//拷贝后对象的成员变量值 System.out.println("============================="); System.out.println("c1中demo对象的地址值:"+c1.getDemo().hashCode()); System.out.println("c2中demo对象的地址值:"+c3.getDemo().hashCode()); System.out.println("============================="); System.out.println("c1整体对象的地址值:"+c1.hashCode()); System.out.println("c2整体对象的地址值"+c2.hashCode()); System.out.println("============================="); } } //clone()到底是浅拷贝还是深拷贝 //clone()是属于浅拷贝的
运算结果:
我们可以发现拷贝后的对象与原来对象相比:
引用数据类型变量demo地址值一样,但整体对象c1和c2(c3)的地址值不同。说明clone()是浅拷贝