JavaSE 进阶 - 第18章 面向对象(Object和匿名内部类)

一、Object

1、JDK类库的根类:Object

   这个老祖宗类中的方法我们需要先研究一下,因为这些方法都是所有子类通用的。
   任何一个类默认继承Object。就算没有直接继承,最终也会间接继承。

2、Object类当中有哪些常用的方法?
  • 我们去哪里找这些方法呢?
    第一种方法:去源代码当中。(但是这种方式比较麻烦,源代码也比较难)
    第二种方法:去查阅java的类库的帮助文档

  • 什么是API?
    应用程序编程接口。(Application Program Interface)
    整个JDK的类库就是一个javase的API。
    每一个API都会配置一套API帮助文档。
    SUN公司提前写好的这套类库就是API。(一般每一份API都对应一份API帮助文档。)

  • 目前为止我们只需要知道这几个方法即可:
       String toString() // 将对象转换成字符串形式
       boolean equals(Object obj) // 判断两个对象是否相等
       protected void finalize() // 垃圾回收器负责调用的方法
       protected Object clone() // 负责对象克隆的
       int hashCode() // 获取对象哈希值的一个方法。

3、toString()方法

3.1、源代码:

public String toString() {
	return this.getClass().getName() + "@" + Integer.toHexString(hashCode());
}

   源代码上toString()方法的默认实现是:类名@对象的内存地址转换为十六进制的形式

3.2、SUN公司设计toString()方法的目的是什么?
   设计目的:通过调用这个方法可以将一个“java对象”转换成“字符串表示形式”

3.3、其实SUN公司开发java语言的时候,建议所有的子类都去重写toString()方法。
   toString()方法应该是一个简洁的、详实的、易阅读的.

3.4、总结:
   所有类的toString()方法是需要重写的。
   重写规则,越简单越明了就好。

   System.out.println(引用); 这里会自动调用“引用”的toString()方法。

   String类是SUN写的,toString方法已经重写了。

【实例】

public class Test01{
    public static void main(String[] args){
        MyTime t1 = new MyTime(1970, 1, 1);
        // 一个日期对象转换成字符串形式的话,我们还是希望能看到具体的日期信息。
        String s1 = t1.toString();

        //MyTime类重写toString()方法之前
        //System.out.println(s1); // MyTime@23fc625e
        //MyTime类重写toString()方法之后
        System.out.println(s1);            // 1970年1月1日

        System.out.println(t1.toString()); //1970年1月1日
        System.out.println(t1);           //1970年1月1日
        // 注意:输出引用的时候,会自动调用该引用的toString()方法。
    }
}
class MyTime{
    int year;
    int month;
    int day;
    public MyTime(){
    }
    public MyTime(int year, int month, int day){
        this.year = year;
        this.month = month;
        this.day = day;
    }

    // 重写toString()方法
    public String toString(){
        return this.year + "年" + this.month + "月" + this.day + "日";
    }
}
/*结果:
1970年1月1日
1970年1月1日
1970年1月1日
 */
4、equals()方法

4.1、源代码:

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

4.2、SUN公司设计equals方法的目的是什么?
   以后编程的过程当中,都要通过equals方法来判断两个对象是否相等。
   equals方法是判断两个对象是否相等的。

4.3、Object类给的这个默认的equals方法够用吗?
   在Object类中的equals方法当中,默认采用的是“== ”判断两个java对象是否相等。
   而“==”判断的是两个java对象的内存地址,我们应该判断两个java对象的内容是否相等。
   所以老祖宗的equals方法不够用,需要子类重写equals。

4.4、判断两个java对象是否相等,不能使用“== ”,因为“==”比较的是两个对象的内存地址。

4.5、总结:

  • 所有类的equals方法也需要重写
       因为Object中的equals方法比较的是两个对象的内存地址,我们应该比较内容,所以需要重写。

  • 重写规则:自己定,主要看是什么和什么相等时表示两个对象相等。

   基本数据类型比较时用:==
   对象(引用数据类型)和对象比较:调用equals方法

  • String类是SUN编写的,所以String类的equals方法重写了。

  • 以后判断两个字符串(String类型)是否相等,最好不要使用==,要调用字符串对象的equals方法。

  • 注意:重写equals方法的时候要彻底。

// 重写equals方法
// 需求:当一个学生的学号(int no;)相等,并且学校(String school;)相同时,表示同一个学生。
// 思考:这个equals该怎么重写呢?
// equals方法的编写模式都是固定的。架子差不多。
public boolean equals(Object obj){
	if(obj == null || !(obj instanceof Student)) return false;
	if(this == obj) return true;  //内存地址相同,即同一个对象
	Student s = (Student)obj;
	return this.no == s.no && this.school.equals(s.school);
}
5、finalize()方法(了解)

5.1、源代码:

protected void finalize() throws Throwable { }
  • finalize()方法只有一个方法体,里面没有代码,而且这个方法是protected修饰的。
  • 这个方法不需要程序员手动调用,JVM的垃圾回收器(GC)负责调用这个方法。
    不像equals toString,equals和toString()方法是需要你写代码调用的。
    finalize()只需要重写,重写完将来自动会有程序来调用。

4.2、finalize()方法的执行时机:

  • 当一个java对象即将被垃圾回收器回收的时候,垃圾回收器负责调用finalize()方法。

  • finalize()方法实际上是SUN公司为java程序员准备的一个时机,垃圾销毁时机
    如果希望在对象销毁时机执行一段代码的话,这段代码要写到finalize()方法当中。
    项目开发中有这样的业务需求:所有对象在JVM中被释放的时候,请记录一下释放时间!!!

  • 静态代码块的作用是什么?
    static {

    }
    静态代码块在类加载时刻执行,并且只执行一次。
    这是一个SUN准备的类加载时机。

  • finalize()方法同样也是SUN为程序员准备的一个时机。
    这个时机是垃圾回收时机。

  • 提示:
    java中的垃圾回收器不是轻易启动的,垃圾太少,或者时间没到,种种条件下,有可能启动,也有可能不启动。

【实例】

public class Test06{
    public static void main(String[] args){

        for(int i = 0; i < 1000; i++){
            Person p = new Person();
            p = null;

            // 有一段代码可以建议垃圾回收器启动。
            if(i % 2 == 0){
                System.gc(); // 建议启动垃圾回收器。(只是建议,可能不启动,也可能启动。启动的概率高了一些。)
            }
        }
    }
}

class Person{

    // 重写finalize()方法
    // Person类型的对象被垃圾回收器回收的时候,垃圾回收器负责调用:p.finalize();
    protected void finalize() throws Throwable {
        // this代表当前对象
        System.out.println(this + "即将被销毁!");
    }
}
/*结果:
Person@1f561dd0即将被销毁!
Person@33f40650即将被销毁!
Person@78a6d9d9即将被销毁!
Person@184ec5b4即将被销毁!
....
 */
6、hashCode方法

源代码:

public native int hashCode();
  • 这个方法不是抽象方法,带有native关键字,底层调用C++程序。

  • hashCode()方法返回的是哈希码:
    实际上就是一个java对象的内存地址,经过哈希算法,得出的一个值。
    所以hashCode()方法的执行结果可以等同看做一个java对象的内存地址。

【实例】

public class Test07{
    public static void main(String[] args){
        Object o = new Object();
        int hashCodeValue = o.hashCode();

        // 对象内存地址经过哈希算法转换的一个数字。可以等同看做内存地址。
        System.out.println(hashCodeValue); //258952499

        MyClass mc = new MyClass();
        int hashCodeValue2 = mc.hashCode();
        System.out.println(hashCodeValue2); //1067040082

        MyClass mc2 = new MyClass();
        System.out.println(mc2.hashCode()); // 1325547227
    }
}
class MyClass
{
}
/*结果:
258952499
1067040082
1325547227
 */

二、匿名内部类

  1. 什么是内部类?
       内部类:在类的内部又定义了一个新的类。被称为内部类。

  2. 内部类的分类:
       静态内部类:类似于静态变量
       实例内部类:类似于实例变量
       局部内部类:类似于局部变量

  3. 使用内部类编写的代码,可读性很差。能不用尽量不用。

  4. 匿名内部类是局部内部类的一种。因为这个类没有名字而得名,叫做匿名内部类。

  5. 学习匿名内部类主要是让大家以后在阅读别人代码的时候,能够理解。
    并不代表以后都要这样写。因为匿名内部类有两个缺点:
       缺点1:太复杂,太乱,可读性差。
       缺点2:类没有名字,以后想重复使用,不能用。

【示例】

public class Test01{

    // 静态变量
    static String country;

    // 该类在类的内部,所以称为内部类
    // 由于前面有static,所以称为“静态内部类”!!!!!!!!!!
    static class Inner1{
    }

    // 实例变量
    int age;

    // 该类在类的内部,所以称为内部类
    // 没有static叫做实例内部类。!!!!!!!!!!!!!!!!!
    class Inner2{
    }

    // 方法
    public void doSome(){
        // 局部变量
        int i = 100;

        // 该类在类的内部,所以称为内部类
        // 局部内部类。!!!!!!!!!!!!!!!!!!!!!!!
        class Inner3{
        }
    }

    public void doOther(){
        // doSome()方法中的局部内部类Inner3,在doOther()中不能用。
    }

    // main方法,入口
    public static void main(String[] args){
        // 调用MyMath中的mySum方法。
        MyMath mm = new MyMath();
		/*方法一、
		Compute c = new ComputeImpl();
		mm.mySum(c, 100, 200);

		//合并(这样写代码,表示这个类名是有的。类名是:ComputeImpl)
		//mm.mySum(new ComputeImpl(), 100, 200);
		*/

        // 方法二、使用匿名内部类,表示这个ComputeImpl这个类没名字了。 !!!!!!!!!!!!!!!!!!
        // 这里表面看上去好像是接口可以直接new了,实际上并不是接口可以new了。
        //             mm.mySum(new Compute(){实现}, 200, 300);
        // 后面的{} 代表了对接口的实现。
        // 不建议使用匿名内部类,为什么?
        // 因为一个类没有名字,没有办法重复使用。另外代码太乱,可读性太差。
        mm.mySum(new Compute(){
            public int sum(int a, int b){
                return a + b;
            }
        }, 200, 300);
    }
}

// 负责计算的接口
interface Compute{
    // 抽象方法
    int sum(int a, int b);
}

// 方法一、编写一个Compute接口的实现类
/*
class ComputeImpl implements Compute{

	// 对方法的实现
	public int sum(int a, int b){
		return a + b;
	}
}
*/

// 数学类
class MyMath{
    // 数学求和方法
    public void mySum(Compute c, int x, int y){
        int retValue = c.sum(x, y);
        System.out.println(x + "+" + y + "=" + retValue);
    }
}
/*结果:
200+300=500
*/

传送门

上一章:JavaSE 进阶 - 第17章 面向对象(包和访问控制权限)
下一章:JavaSE 进阶 - 第19章 数组(一)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值