第8章 面向对象编程(中级部分)——3

一、IDEA的使用

二、包

三、访问修饰符

四、OOP三大特征(封装、继承和多态)

五、Super关键字

六、方法重写(overwrite)

七、Object类详解

1.equals方法

(1)==和equals的对比

==:
1.既可以判断基本类型,又可以判断引用类型。
2.如果判断基本类型,判断的是值是否相等。
3.如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象。

equals:
1.是Object类中的方法,只能判断引用类型。
2.Object类中判断的是地址是否相等,子类String/Integer中判断的是内容是否相等。

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

        //比较值
        int n1 = 10;
        double n2 = 10.0;
        System.out.println(n1 == n2);//true

        //比较地址
        B a = new B();
        B b = a;
        B c = b;
        System.out.println(a == c);//true
        A d = c;
        System.out.println(a == d);//true

        //equals:是Object类中的方法,只能判断引用类型

        "hello".equals("abc");

        /*
        (1)Object类中的equals方法
        public boolean equals(Object obj) {
            return (this == obj);//比较对象地址是否相同,也就是判断是否为同一个对象
        }
        */
        /*
        (2)Object的子类String中的equals方法
        public boolean equals(Object anObject) {
            if (this == anObject) {//判断比较对象地址是否相同,如果相同,返回true
                return true;
            }
            if (anObject instanceof String) {//判断传入的对象的运行类型是否为String
                String anotherString = (String)anObject;//向下转型,anObject的编译类型Object->String
                int n = value.length;//value:private final char value[],String类是通过该数组来存放字符串的
                if (n == anotherString.value.length) {
                    char v1[] = value;
                    char v2[] = anotherString.value;
                    int i = 0;
                    while (n-- != 0) {
                        if (v1[i] != v2[i])//判断内容是否相同,如果两个字符串的所有字符都相等,则返回true
                            return false;
                        i++;
                    }
                    return true;
                }
            }
            return false;//如果比较的不是字符串,则直接返回false
        }
         */
        /*
        (3)Object的子类Integer中的equals方法:判断两个值是否相等
        public boolean equals(Object obj) {
            if (obj instanceof Integer) {
                return value == ((Integer)obj).intValue();//比较值是否相同
            }
            return false;
        }
         */
        Integer integer1 = new Integer(1000);
        Integer integer2 = new Integer(1000);
        System.out.println(integer1 == integer2);//false 使用==比较引用数据类型,比较的是地址,即是否指向同一个对象
        System.out.println(integer1.equals(integer2));//true Integer中的equals方法,比较两个值是否相等

        String str1 = new String("hspedu");
        String str2 = new String("hspedu");
        System.out.println(str1 == str2);//false
        System.out.println(str1.equals(str2));//true


    }
}

class A {}
class B extends A {}

(2)如何重写equals方法?

  • 判断比较的两个对象是否为同一个对象,如果是则返回true。
  • 判断传入的比较对象类型是否与被比较对象类型一致,如果一致才继续比较属性是否相同。
public class EqualsExercise {
    public static void main(String[] args) {
        /*
        应用实例:判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回true,反之false.
         */
        Person person1 = new Person("王一博", 27, '男');
        Person person2 = new Person("王一博", 27, '男');
        System.out.println(person1.equals(person2));//true

    }
}

class Person {//是Object子类
    private String name;
    private int age;
    private char gender;

    public Person(String name, int age, char gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    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 char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    //重写Object的equals方法
    public boolean equals(Object obj) {
        //如果比较的两个对象是同一个对象,则直接返回true
        if (this == obj) {
            return true;
        }
        //判断obj的运行类型是否为Person,如果是才比较属性是否相同
        if (obj instanceof Person) {//向下转型,以访问到Person类所特有的属性
            Person p = (Person) obj;
            return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
        }
        //如果obj的运行类型不是Person,则直接返回false
        return false;
    }
}

(3)int与float、int和char

public class EqualsExercise03 {
    public static void main(String[] args) {
        int it = 65;
        //浮点数常量有两种表示方式:
        //(1)十进制数形式:5.12  512.0f  .512 (必须有小数点)
        //(2)科学计数法形式:5.12e2(5.12*10^2) 5.12e-2(5.12*10^(-2))
        float fl = 65.0f;
        System.out.println("65和65.0f是否相等?" + (it == fl));//true ==判断基本类型的值是否相等,it和fl虽然类型不同,但值相同

        //char字符类型表示单个字符,可以存放 'a' 'A' '\t' '韩' '97' 97
        char ch1 = 'A';
        char ch2 = 12;
        //在java中,char的本质是一个整数,在输出时,是unicode码对应的字符
        System.out.println("65和'A'是否相等?" + (it == 'A'));//true 'A'对应的是65
        System.out.println("12和ch2是否相等?" + (12 == ch2));//true

        String str1 = new String("hello");
        String str2 = new String("hello");
        System.out.println("str1和str2是否相等?" + (str1 == str2));//false
        System.out.println("str1是否equals str2?" + (str1.equals(str2)));//true
//        System.out.println("hello" == new java.sql.Date());//编译错误,两者分别是字符串类型和Date类型的对象,根本无法比较
        
    }
}

2.hashCode方法

(1)基本介绍

总结:

  • 提高具有哈希结构的容器的效率。
  • 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的。
  • 两个引用,如果指向的是不同对象,则哈希值是不一样的。
  • 哈希值主要是根据地址号来的,但不能完全将哈希值等价于地址。
  • 后面在集合中,hashCode如果有需要的话,会重写。
public class HashCode_ {
    public static void main(String[] args) {
        AA aa = new AA();
        AA aa1 = new AA();
        AA aa2 = aa;
        System.out.println("aa.hashCode=" + aa.hashCode());
        System.out.println("aa1.hashCode=" + aa1.hashCode());//aa和aa1指向不同的对象
        System.out.println("aa2.hashCode=" + aa2.hashCode());//aa和aa2指向同一个对象
    }
}

class AA {}

(2)子类String的hashCode方法

在进行hash计算时,希望尽量减少生成重复的哈希值。

String类中的hashCode源码:

//Sting类中的hashCode源码:    
    public int hashCode() {
        int h = hash;//hash:private int hash;用来存放String对象的hashCode,默认是0
        if (h == 0 && value.length > 0) {//value:private final char value[];String类是通过该数组来存放字符串的
        //如果没有进行过hash计算,并且字符串的长度>0,则进行哈希值计算
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];//推导出的h = val[0]*31^(n-1) + val[1]*31^(n-2) + ... + val[n-1]
            }
            hash = h;
        }
        return h;
    }

思考:
1.为什么 h==0 时可以认为没有进行过hash计算呢?
答:如果hash计算后不会产生0值,那么h==0时就可以认为没有进行过hash计算。
根据hash的计算逻辑,当字符串只有一个元素且val[0] = 0时,根据公式h = 31 * h + val[i]进行计算,h的值为0。查看ASCII表发现,null的ASCII值为0,显然val[0]中永远不可能存放null(因为val[i]是char类型数据),因此hash计算后不会产生0值。

2.为什么是31?
答:(1)因为31是一个素数。素数又称质数,指在大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。根据素数的特性,与素数相乘得到的结果比其他方式更容易产生唯一性,也就是哈希值重复的概率比较小。
(2)31的二进制:11111,占用5个二进制位,在进行乘法运算时,可以转换为(x<<5)-x。Java中如果相乘的数字太大会导致内存溢出的问题,从而导致数据丢失。 

3.toString方法

(1)默认返回:全类名(包名+类名)+@+哈希值的十六进制

(2)子类往往重写toString方法,用于返回对象的属性信息

public class ToString {
    public static void main(String[] args) {
        /*
        Object类中toString方法的源码
        (1)getClass().getName():该类的全类名(包名+类名)
        (2)Integer.toHexString(hashCode()):将对象的hashCode值转成16进制字符串
        public String toString() {
            return getClass().getName() + "@" + Integer.toHexString(hashCode());
        }
         */
        Monster monster = new Monster("小怪物", "巡山的", 1000);
        System.out.println(monster.toString());
        //(没有重写toString)调用toString方法:
        //(1)Monster类中没有toString方法,转而查找Moster的父类Object
        //(2)Object类中有toString方法,并且可以访问,因此调用Object类中的toString方法
        //(重写toString后)调用toString方法:
        //Monster类中有toString方法,并且可以访问,因此调用Moster类中的toString方法

        //当直接输出一个对象时,toString方法会被默认地调用
        System.out.println(monster);//等价 monster.toString()

    }
}

class Monster {
    private String name;
    private String job;
    private double sal;

    public Monster(String name, String job, double sal) {
        this.name = name;
        this.job = job;
        this.sal = sal;
    }

    //Monster类中重写toString方法:alter+insert,返回对象的属性信息,程序员可修改
    @Override
    public String toString() {
        return "Monster{" +
                "name='" + name + '\'' +
                ", job='" + job + '\'' +
                ", sal=" + sal +
                '}';
    }
}

4.finalize方法

(1)当对象被回收时,系统自动调用该对象的 finalize方法 。
         子类可以重写该方法,做一些释放资源的操作。

(2)对象什么时候被回收?
         当某个对象没有任何引用时,则jvm就会认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize方法。
         回收/销毁:将对象的堆空间释放出来,以便之后别的对象可以占用该空间。           

(3)垃圾回收机制的调用,是系统来决定的(即有自己的GC算法),也可以通过 System.gc() 主动触发垃圾回收机制。即,当某个对象没有任何引用的时候,不会立即启动垃圾回收机制,而是由系统的GC算法来决定是否回收。System.gc() 也不会一定生效,例如:下午1点叫保洁阿姨打扫卫生,但保洁阿姨在打扫其他办公室,不会立即过来。

public class Finalize_ {
    public static void main(String[] args) {
        Car bwm = new Car("宝马");
        //这时,Car对象就是一个垃圾,垃圾回收器就会回收(销毁)对象。在销毁对象前,会调用该对象的finalize方法。
        //(1)如果程序员不重写finalize方法,那么就会调用Objct类的finalize,即默认处理
        //(2)如果程序员重写finalize方法,在finalize中写自己的业务逻辑代码(比如释放资源:数据库连接或者打开文件),
        //    那么就会调用子类中的finalize方法,实现自己的逻辑。
        bwm = null;//bwm不再指向Car对象
        System.gc();//主动调用垃圾回收机制

        System.out.println("程序退出了...");

        //重写finalize方法后并没有输出Car类中finalize方法中的信息:
        //因为当某个对象没有任何引用时,不会立即启动垃圾回收机制,而是由系统的GC算法来决定。

        //主动调用垃圾回收机制后,finalize方法中的输出在"程序退出了..."后:
        //因为程序不会在System.gc();处阻塞,不等垃圾回收机制执行完,就会继续向下执行
    }
}

class Car {
    private String name;

    public Car(String name) {
        this.name = name;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("我们销毁汽车" + name);
        System.out.println("释放了某些资源...");
    }
}

八、项目

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值