Object类与内部类

本文详细介绍了Java中的Object类及其toString(),equals(),和hashCode()方法,以及静态内部类、实例内部类、匿名内部类和局部内部类的概念和用法。通过实例演示了如何重写这些方法以满足自定义需求。
摘要由CSDN通过智能技术生成

废话不多说,直接进入正题。

目录

一、Object类

1.介绍Object类

2.toString方法

3.equals方法

4.hashCode方法

二、内部类

1.静态内部类

2.实例内部类

3.匿名内部类

4.局部内部类


一、Object类

1.介绍Object类

(1)Object类就是所有类的父类/超类,也称为祖先类。也就是说所有的类都会默认继承于Object类,即使你没有定义继承关系

(2)Object类具有的方法

1)在本文中,我们重点介绍三个方法:toString()、equal()、hashCode()

2)因为Object类是所有类的父类,所以我们创造的任何类,都可以直接使用上面的方法,也可以重写上述的方法(当然,构造方法除外)

下面介绍三个常用的重写方法

2.toString方法

(1)不重写打印自定义类型对象时

可见,打印出来的是一堆乱码。

(2)了解打印方法的背后执行流程

这也就是为什么直接打印,会得出一些看不懂的数据。要想改变结果,就需要重写父类的toString()方法。

(3)重写方法

class Person {
    public String name = "张三";
    public int age = 10;

    @Override
    public String toString() {
        return "名字:"+name+" 年龄:"+age;
    }
}
public class Test1 {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person);
    }
}

打印结果:

当子类重写了toString方法后,最终打印时,调用的toString()方法也就是重写的方法。

(4)使用编译器生成

第一步:右键鼠标或者Alt + Lnsert,并且点击Generate…

第二步:点击toString()

第三步:勾选。当只有一个被选择的时候,按住ctrl再进行勾选即可

生成的结果展示:

小结:当我们想打印自定义类型中的数据,并且打印的结果并非目标值时,可以重新toString()方法。简易记忆法:System.out.println()方法最终调用的是toString()方法

3.equals方法

该方法用于比较两个对象是否相等(相同),但是不能比较两个对象的大小关系

3.1.介绍方法背景

(1)两个普通变量比较是否相等

结果是明显的,是我们所想要的。

这也是我们所想要的 

(2)比较两个实例对象时

结果大失所望

(3)再比较自定义类型对象时

结果仍是大失所望,这里的原因是:复杂类型和引用类型不能使用==进行比较是否相等,因为==比较的是他们的地址是否相等,很显然,new出来的对象地址是不会相等的。但是前面简单的变量和字符串却可以,因为他们的地址一样。

(4)使用equals方法比较

啊?不是说equals方法是用来比较两个对象是否相同的吗?为什么结果还是不对?这就是下面想要介绍的了

3.2.查看equals方法执行过程

(1)方法背后调用

当我们以为equals方法底层很厉害的时候,点开却发现,底层也是使用==比较,难怪说比较的使用和预期不一样。

(2)这样设置的原因是编译器也不知道程序员想要比较的对象是什么。如果是复杂类型对象,像上面的Son,是比较名字是否相等呢?还是比较年龄是否相等呢?

3.3.重新equals方法

(1)自定义写

自定义的比较规则:先查看其中一个对象是否为null,再比较名字是否相同,最后比较年龄。如果三者全部一样,则可以认为是同一个人,也就是两个对象相同。

 运行结果:结果就是预期,很正确

上述的几点解释:第一点:使用参数时,需要先向下转型,因为父类对象无法访问到子类对象的成员;第二点:不是说重新equals方法吗?怎么还调用equals方法比较?原因是当String类原码重写了equals方法,当String类型去调用时,可以正确的。

2)String类型重写equals方法

这样比较时,结果也是正常的

3)查看String类重写的equals方法

不用去理解重写是如何实现的,只需要记住,如果是String类型,可以直接调用equals方法。

(2)编译器生成

和上面生成toString()方法差不多 

1)第一步

2)第二步

3)按照默认选项,一直next下去即可

 

4)结果

equals方法和hashCode()方法一起生成的,如果不需要再直接删除hashCode()方法即可

 

4.hashCode方法

这个方法的作用是比较两个对象在内存中存储的位置是否相同。比如两个对象内容一样,就可以认为是同一个对象,也就应该存放在同一块内存空间上面。

 拿下面的例子举例

(1)未重写时

(2)重写后

本文的hashCode()方法就介绍这么多,后续在数据结构的哈希表中还有用处(hashCode的重点用法)

二、内部类

        内部类介绍,通常的来说,就是在一个类的内部再定义一个类,作为外部类的一个成员。内部类的定义方式和之前介绍过的组合写法类似。

1.静态内部类

(1)语法格式

static class 类名 {}

(2)定义静态内部类

class Outer {
    public int data1 = 1;
    public int data2 = 2;
    public static int data3 = 3;

    //定义静态内部类
    static class Inter {
        public int data4 = 4;
        public static int data5 = 5;
        public void func() {
            System.out.println("我是静态内部类的方法~");
        }
        public Inter() {
            System.out.println("我是静态内部类的构造方法!");
        }
    }
    public void func() {
        System.out.println("我是外部类的方法~");
    }

}

由此可见,静态内部类中也可以有自己的成员变量、成员方法和构造方法,而且方法名与变量名可以与外部类的名字相同。

(3)静态内部类的访问方式

时刻牢记,内部类也是属于外部类的一个成员。

实例化静态内部类对象:

访问成员和方法:

使用静态内部类的引用,是无法访问到外部类的任何成员的。

(4)在静态内部类中访问外部类

这样操作可以发现,在静态内部类中,可以直接访问外部内的静态成员,非静态不可以直接访问。

非静态成员则需要通过对象的引用来访问。同样,访问方法的方式和访问成员变量一样,这里不再过多介绍。

(5)静态内部类的优点和注意事项

优点:访问静态内部类对象不需要创造外部类对象,因为这样的优点,该内部类后续用的场景较多。

注意事项:

1)在静态内部类中只能访问外部类中的静态成员;如果需要访问,则需要先在内部类中创造外部类对象,使用对象的引用进行访问。

2)不使用外部类对象的引用访问 内部类和外部类名字相同的成员时(静态成员与方法),会优先访问静态内部类的

2.实例内部类

实例内部内的定义位置和静态内部类类似,但是定义的格式少了static关键字

(1)语法格式

class 类名 {}

(2)定义实例内部类

(3)定义实例内部类对象

像静态内部类的形式创造对象,结果是得到一场串的波浪号。很明显,创建实例内部类的对象并不能如此。

第一种:先创建外部类对象,再使用和实例静态内部类的方式去创建。究其原因是实例内部类是属于对象的,创造实例内部类时,就需要通过外部类对象的引用;而静态内部类不属于外部类对象,进而不用创造外部类对象

第二种方式:这样的写法就是第一种写法的合并,但是看起来比较丑陋

(4)使用内部类对象引用访问成员

实例内部类对象的引用也只能访问内部类中的成员,而不能访问外部类的成员

(5)内部类中访问外部类

在实例内部类中,可以访问外部类的所有成员方法和成员变量,包括静态的和非静态的。

(6)注意事项

1)外部类中的任何成员都可以在实例内部类方法中直接访问
2)实例内部类所处的位置与外部类成员位置相同,因此也受 public private 等访问限定符的约束
3)在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this. 同名成员 来访问
4) 实例内部类对象必须在先有外部类对象前提下才能创建
5) 实例内部类的非静态方法中包含了一个指向外部类对象的引用
6) 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。

3.匿名内部类

匿名内部类的应用场景大多数在对于接口的实现或者类的继承中,主要场景还是应用于对接口的实现中。

(1)匿名内部类简介

1)为什么匿名?因为该类实现了接口之后,是没有名字,不同于普通类实现接口(例如:普通类A是实现了接口B,此时A就是类名,是有名字的)

2)为什么是内部类?其实匿名内部类是局部内部类的一种,而且创造此类是必须要存在于一个类的方法中(一般是main函数中,而main函数要存在于一个类中)

3)使用目的:为了使用接口中的方法,本质上会发生向上转型

(2)匿名内部类定义

第一种创造方式:不使用引用(一次性对象)

interface Inter {
    void func();
}
public class Test3 {

    public static void main(String[] args) {

        new Inter() {
            @Override
            public void func() {
                System.out.println("我是重写的方法");
            }
        }.func();
    }
}

匿名内部类实现了接口,并且重新了接口中的方法,然后使用该类调用了该接口中的方法

与普通类实现接口的对比:

两种方式的对比,很明显,第一种的方式很方便

 

第二种创建方式:给匿名内部类对象加上引用

这其实也是一种向上转型,并且发生了动态绑定。父类(接口)类型 引用了 子类(匿名内部类)对象,通过父类引用调用了子类重写的方法,最终会绑定到子类上。

与普通类的对比:

普通类可以使用两种类型的引用去接收该对象,原因是普通类的名字我是是可以知晓的,所以可以使用普通类的引用去接收,但是匿名内部类不行,他没有名字。

以上就是匿名内部类的定义和使用

(3)匿名内部类的注意事项

1)

2)匿名内部类中不能有构造方法

3)匿名内部类中不能有静态方法和变量

4.局部内部类

上面的匿名内部类就是局部内部类的代表,定义在方法中。局部内部类使用的场景非常的少。

(1)定义局部内部

(2)实例化局部内部类对象

由此可见,局部内部类是非常不方便的,典型的吃力不讨好,所以后续的使用场景非常的少。

以上就是本文的全部内容了

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码小娥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值