1. 🥘 Object类的概念
Object,翻译一下,对象;物体;目标;目的;物品;东西;宗旨;宾语 是的,这篇博客要讲的跟这些都没有太大关系(哈哈哈) 但有一点你必须记住!!! 它是所有类的爹!!!
言归正传,我们都知道,Object是Java默认提供的一个类。
Java中除了Object类,所有的类都是存在继承关系的。
默认所有的类会继承Object父类
这里就会有小伙伴会问了: 我没看到呀!哪里继承了!一个extends都没有! 咱们贴心的编译器会帮你继承得嘛 你看不见的就叫隐式继承 你能看见的就叫显式继承 你看不见,总不能忽略编译器的功劳吧 (编译器:啊对对对,我就是那个舔狗)
说人话:即所有类的对象都可以用Object的引用进行接收
…emmm,好像不太像人话
那就用代码再说一次人话:👇
class Person{}
class Student{}
public class Test {
public static void main(String[] args){
func(new Person());
func(new Student());
}
public static void func(Object obj){
System.out.println(obj);
}
}
输出结果为
Person@4eec7777 Student@41629346
所以我们在开发过程中Object类是参数得最高统一类型。
Object 类位于 java.lang 包中,编译时会自动导入,我们创建一个类时,如果没有明确继承一个父类,那么它就会自动继承 Object,成为 Object 的子类。
2. 🧋 Object类的构造方法
public Object(){ … }
3. 🍤 Object类的一些方法和属性
Object中存有一些定义好的方法
那么这里就为大家讲解一下其中的几个方法,这几个方法往往都是要牵扯到重写,以此来实现自己想要的功能,所以带大家简单回顾一下重写
3.1 🍜 重写的回顾
🍔 规则
重写的那个方法不可以比 被重写方法(就是父类中的方法) 有更严格的访问级别,可以更加广泛
父类方法中如果是包访问权限,那么子类的重写方法就可以是包访问权限或者是public访问权限
比如,Object类中有个toString方法,我们开始重写这个方法的时候总是忘记public修饰符,贴心的编译器会帮我们纠正错误,这里出错的原因就是:如果没有加上任何访问限定修饰符的方法就是包访问权限,包访问权限比public范围更小,更严格,所以会报错
那么这里又要附上那张传家宝表格了
No | 范围 | private | defaul | protected | public |
---|---|---|---|---|---|
1 | 同一包中的同一类 | 🍉 | 🍉 | 🍉 | 🍉 |
2 | 同一包中的不同类 | 🍉 | 🍉 | 🍉 | |
3 | 不同包中的子类 | 🍉 | 🍉 | ||
4 | 不同包的非子类 | 🍉 |
重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等进行重新编写的一个过程,返回值和形参都不能改变。即外壳不改变,核心重写。
重写的好处在于子类可以根据需要,定义特定的属于子类自己的行为。
也就是说子类能够根据需要来实现父类的方法,又和父类的方法不完全一样,实现自己的特色
那么总结一下:
- 子类在重写父类的方法时,一般必须与父类方法原型一致:修饰符 返回值类型 方法名(参数列表)要完全一致
- JDK7以后,被重写的方法返回值类型可以不同,但是必须是具有父子关系的
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为protected
- 父类被static、private修饰的方法都不能被重写
- 子类和父类在同一个包中,那么子类可以重写父类中的所有方法,除了声明为private和final的方法
- 子类和父类不在同一个包中,那么子类只能够重写父类的 声明为public 和protected的非final方法
- 重写的方法,可以使用 @Override 注解来显式指定。有了这个注解能够帮我们检查这个方法有没有被正确重写。例如不小心讲方法名拼写错了,此时编译器就会发现父类中并没有这个方法,就会编译报错,构不成重写。
🍛 重写和重载的区别
区别点 | 重载(overloading) | 重写(override) |
---|---|---|
参数列表 | 必须修改 | 不同JDK版本不一样,如果修改那必须是继承关系 |
返回类型 | 可以修改 | 一定不能修改 |
访问限定符 | 可以修改 | 不能做出更严格的限制(子类权限大于父类) |
即:方法重载式一个类的多态性的表现,而方法重写式子类与父类的一种多态性表现
3.2 🍱 toString方法
Object类中的 toString() 方法用于返回对象的字符串表示形式。
就是用来返回对象里面的一些信息并用字符串的形式返回。
不用传入参数,默认返回格式为
对象的class名称 + @ + hasCode 的十六进制字符串
老规矩,概念看不懂,代码上来凑:👇
/**
* @author Gremmie102
* @date 2022/4/16 23:32
* @purpose: 调用Object中的toString方法
*/
public class ToStringTest {
public static void main(String[] args) {
// toString() with Object
Object obj1 = new Object();
System.out.println(obj1.toString());
Object obj2 = new Object();
System.out.println(obj2.toString());
Object obj3 = new Object();
System.out.println(obj3.toString());
}
}
那么我们运行出来的结果为👇
java.lang.Object@776ec8df java.lang.Object@4eec7777 java.lang.Object@3b07d329
Tips:这里的哈希值(你可以当作地址值)大家如果运行出来和我的不一样也没关系的,因为对象储存的位置都是不同的
那么我们现在用Array类来调用toString方法
/**
* @author Gremmie102
* @date 2022/4/16 23:45
* @purpose : 用Array的类来调用其父类的toString方法
*/
public class ToStringTest1 {
public static void main(String[] args) {
//创建数组
String[] array = {"葛玉礼","is","假面骑士"};
System.out.println(array.toString());
//数组元素值返回一个字符串的表示形式
//Array继承了Object类,子类可以直接使用父类中的方法
System.out.println(array[0].toString() +
array[1].toString() +
array[2].toString() );
}
}
运行结果为👇
[Ljava.lang.String;@776ec8df 葛玉礼is假面骑士
那么我们现在自己定义一个类,去重写它父类Object中的toString方法
/**
* @author Gremmie102
* @date 2022/4/17 9:53
* @purpose :重写Object中的toString中的方法
*/
public class Student {
String name;
int age;
double score;
public Student() {
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
public void study(){
System.out.println(name+" is studying");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
我们在重写的方法中自定义了需要显现出来的内容
那么我们写个main方法来测试一下
public static void main(String[] args) {
Student student = new Student("Gremmie",19,95);
System.out.println(student);
}
那么运行结果为:
Student{name=’Gremmie’, age=19, score=95.0}
这里为啥直接输入一个引用变量就可以直接显示出这么一堆信息捏?
带大家调试一下,看清它的真面目
👆首先第一行代码new了一个对象,并进入构造方法进行初始化
?不给机会,直接就跳到toString方法那里去了,可能这里是编译器暗中捣鬼,我们就不再深究,会用就OK啦
3.3 🍰 equals方法
我们在Java中使用 == 进行比较的时候遵循以下几点:
- 如果==左右两边是基本类型变量,那么比较的就是变量中的值是否相同
- 如果==左右两边是引用类型变量,那么比较的就是引用变量的地址是否相同
- 如果要比较对象中的内容,那就必须要重写Object中的equals方法,因为equals方法也是默认按照地址来比较的
那么用代码来演示一下没有重写之前的用法
/**
* @author Gremmie102
* @date 2022/4/17 10:27
* @purpose :介绍Object中的equals方法
*/
public class EqualsTest1 {
public static void main(String[] args) {
// Object 类使用 equals() 方法
// 创建两个对象
Object object1 = new Object();
Object object2 = new Object();
// 判断 obj1 与 obj2 是否相等
// 不同对象,内存地址不同,不相等,返回 false
System.out.println(object1.equals(object2)); // false
// obj1 赋值给 obj3
// String 重写了 equals() 方法
// 对象引用,内存地址相同,相等,返回 true
Object object3 = object1;
System.out.println(object1.equals(object3)); // true
}
}
/**
* 运行结果:
* E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=51082:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest EqualsTest1
* false
* true
*
* Process finished with exit code 0
*/
那么我们再写一个Person类,首先是未重写版
/**
* @author Gremmie102
* @date 2022/4/17 10:33
* @purpose :未重写equals的Person类的Person类
*/
public class Person {
private String name;
private int age;
public Person(String name,int age){
this.age = age;
this.name = name;
}
public static void main(String[] args) {
int a = 10;
int b = 10;
Person p1 = new Person("Gremmie",19);
Person p2 = new Person("Gremmie",19);
System.out.println(a==b);
System.out.println(p1==p2);
System.out.println(p1.equals(p2));
}
}
/**
* 运行结果
* E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=51109:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest Person
* true
* false
* false
*
* Process finished with exit code 0
*/
那么下面重写equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;//这里向下转型来比较属性值
return Objects.equals(name, person.name);
//这里运用了方法递归
}
结论:比较对象中内容是否相同的时候,我们就一定要重写equals方法了
3.4 🍟hashCode方法
Object hashCode() 方法用于获取对象的 hash 值。
我们看一下hashCode的源码
这个方法是由native修饰的,返回一个内存地址值
方法的底层是由C/C++编写,我们看不到
/**
* @author Gremmie102
* @date 2022/4/17 11:39
* @purpose :演示了 hashCode() 方法的使用
*/
public class HashCodeTest1 {
public static void main(String[] args) {
// Object 使用 hashCode()
Object obj1 = new Object();
System.out.println(obj1.hashCode());
Object obj2 = new Object();
System.out.println(obj2.hashCode());
Object obj3 = new Object();
System.out.println(obj3.hashCode());
}
}
/**
* 运行结果
* E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=51403:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest HashCodeTest1
* 2003749087
* 1324119927
* 990368553
*
* Process finished with exit code 0
*/
那么我们在String和ArrayList类使用hashCode()方法
因为所有的类都继承了Object,所以可以直接调用
/**
* @author Gremmie102
* @date 2022/4/17 11:43
* @purpose :String 和 ArrayList 类使用 hashCode() 方法
*/
import java.util.ArrayList;
public class HashCodeTest2 {
public static void main(String[] args) {
// String 使用 hashCode()
String str = new String();
System.out.println(str.hashCode()); // 0
// ArrayList 使用 hashCode()
ArrayList<Integer> list = new ArrayList<>();
System.out.println(list.hashCode()); // 1
}
}
那么如果两个引用变量指向的对象都是一样的,那么它们所调用返回的哈希值也是相等的
/**
* @author Gremmie102
* @date 2022/4/17 11:45
* @purpose :引用变量指向一个对象情况下,它们调用hashCode方法
*/
public class HashCodeTest3 {
public static void main(String[] args) {
// Object 使用 hashCode()
Object obj1 = new Object();
// obj1 赋值给 obj2
Object obj2 = obj1;
// 判断两个对象是否相等
System.out.println(obj1.equals(obj2)); // true
// 获取 obj1 与 obj2 的哈希值
System.out.println(obj1.hashCode());
System.out.println(obj2.hashCode());
}
}
/**
* 运行结果
* E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=51415:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest HashCodeTest3
* true
* 2003749087
* 2003749087
*
* Process finished with exit code 0
*/
那么同样,我们也可以重写hashCode方法
现在我们想要两个对象如果所有属性都相同,调用hashCode返回的值都相同
import java.util.Objects;
/**
* @author Gremmie102
* @date 2022/4/17 11:51
* @purpose :重写hashCode()方法
*/
class Human{
public String name;
public int age;
public Human(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return Objects.hash(name,age);
}
}
public class HashCodeTest4 {
public static void main(String[] args) {
Human human1 = new Human("Gremmie",19);
Human human2 = new Human("Gremmie",19);
System.out.println(human1.hashCode());
System.out.println(human2.hashCode());
}
}
/**
* 运行结果
* E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=51508:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest HashCodeTest4
* 277247006
* 277247006
*
* Process finished with exit code 0
*/
这样,我们就可以让属性相同的对象返回相同的哈希值了
结论:
- hashCode方法用来确定对象在内存中存储的位置是否相同
- 事实上hashCode()在散列表中才有用,其他情况下是没用的,在散列表中hashCode()的作用是获取对象的散列码,进而去确定该对象在散列表中的位置
这个方法牵扯到了数据结构,等学习进度差不多了再重新为大家讲解,看不懂没关系
3.5 🍫用来接受引用数据类型
我们已经知道Object类可以接受任意类型的对象,因为Object是所有类的父类,但是Object其实并不仅限于接受类的对象,它可以接受所有的数据类型
比如:类、数组、接口
🌭 接收数组对象
/**
* @author Gremmie102
* @date 2022/4/17 12:30
* @purpose :接收数组对象
*/
public class ObjectTest1 {
public static void main(String[] args) {
//Object接收数组对象,向上转型
Object obj = new int[]{1,2,3,4,5,6};
//向下转型,需要强制转换
int[] data = (int[])obj;
for (int i :data) {
System.out.print(i+" ");
}
}
}
/**
* 运行结果
* E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=52075:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest ObjectTest1
* 1 2 3 4 5 6
* Process finished with exit code 0
*/
🍲 接收接口引用的对象
在Java中,接口本身是不能继承任何类的,它只能继承接口,但Object是很特殊的例子,是强制规定的
/**
* @author Gremmie102
* @date 2022/4/17 12:35
* @purpose :用Object接收接口对象
*/
interface IMessage {
String getMessage();
}
class Message implements IMessage {
String str = "我爱Java";
@Override
public String toString() {
return "Life is not satisfactory";
}
@Override
public String getMessage() {
return str;
}
}
public class ObjectTest2 {
public static void main(String[] args) {
IMessage message = new Message();
// 子类向父接口转型
Object object = message;
// 接口向Object转型
System.out.println(object);
IMessage temp = (IMessage) object;
System.out.println(temp.getMessage());
}
}
/**
*运行结果
* E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=52215:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest ObjectTest2
* Life is not satisfactory
* 我爱Java
* Process finished with exit code 0
*/
我们可以通过合理运用Object来真正达到参数的统一
如果一个类编写的时候希望能够接收所有的数据类型,那么就用Object来完成。
对于Object就介绍到这里,如果哪里讲解的不到位还请大家指出
感谢阅读~