【JavaSE ⑦】P209~P218类,接口作为成员变量,参数或返回值的情况;Object类API-①toString方法,②equals方法详解;❤特殊情况:拼接截取后字符串的常量池问题

类、接口的使用

类作为成员变量类型

例:类Personality作为成员变量

package DemoJava07;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: xuexuezi
 * @Date: 2024/06/10/22:26
 * @Description:
 */
public class Person {
    private String name;
    private int age;
    Personality nature;

    public Person(){

    }
    public Person(String name, int age, Personality nature){
        this.name = name;
        this.age = age;
        this.nature = nature;
    }
    public Person(String name){
        this.name = name;
    }

    public void show(){
        System.out.println(this.name + "年龄:" + this.age + ",性格特征" + this.nature);
    }

    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 Personality getNature() {
        return nature;
    }

    public void setNature(Personality nature) {
        this.nature = nature;
    }


}
package DemoJava07;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: xuexuezi
 * @Date: 2024/06/10/22:34
 * @Description:
 */
public class Personality {
    private String features1;//性格特征
    private String color;//性格颜色


    public Personality() {
    }

    public Personality(String features1, String color) {
        this.features1 = features1;
        this.color = color;
    }

    public String getFeatures1() {
        return features1;
    }

    @Override
    public String toString() {
        return "Personality{" +
                "features1='" + features1 + '\'' +
                ", color='" + color + '\'' +
                '}';
    }

    public void setFeatures1(String features1) {
        this.features1 = features1;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

public class Demo01 {
    public void demo01(){
        Person p1 = new Person("Barbie");
        p1.setAge(30);
        Personality nature1 = new Personality("坚韧", "粉色");
        p1.setNature(nature1);
        p1.show();
    }
}
Barbie年龄:30,性格特征Personality{features1='坚韧', color='粉色'}

接口作为成员变量类型

例:接口Clothes作为成员变量
注意,接口不可以new对象,所以需要一个接口的实现类实现接口的抽象方法。其中接口作为成员变量,接收的是实现类对象,相当于多态,向上转型。

package java07.demo02;

public interface Clothes {
    void color();//衣服颜色
    void coat();//上衣
}
package java07.demo02;

public class Person {
    private String name;
    private int age;
    Clothes dress;

    public Person(String name, int age, Clothes dress) {
        this.name = name;
        this.age = age;
        this.dress = dress;
    }

    public Person() {
    }

    public Person(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;
    }

    public Clothes getDress() {
        return dress;
    }

    public void setDress(Clothes dress) {
        this.dress = dress;
    }

    public void show(){
        System.out.println(this.name + "年龄:" + this.age +
                "着装:");
        dress.coat();
        dress.color();

    }
}
package java07.demo02;

public class ClotherImpl implements Clothes{

    @Override
    public void color() {
        System.out.println("红白");
    }

    @Override
    public void coat() {
        System.out.println("格子衫");
    }

}
package java07.demo02;

public class Demo02 {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.setName("朱莉");
        p1.setAge(14);
        Clothes dress1 = new ClotherImpl();
        p1.setDress(dress1);
        p1.show();
        p1.setDress(new ClotherImpl());//匿名对象
        p1.show();

        Person p2 = new Person("布莱斯", 13);
        Clothes dress2 = new Clothes(){//匿名内部类
            @Override
            public void color() {
                System.out.println("蓝色");
            }

            @Override
            public void coat() {
                System.out.println("牛仔夹克");
            }
        };
        p2.setDress(dress2);
        p2.show();

        p2.setDress(new Clothes(){//匿名内部类的匿名对象
            @Override
            public void color() {
                System.out.println("白色");
            }
            @Override
            public void coat() {
                System.out.println("蕾丝裙");
            }
        });
        p2.show();
    }
}

接口作为方法的参数或返回值

例:接口List作为方法的参数或返回值

package java07.demo03;

import java.util.ArrayList;
import java.util.List;

public class Demo03 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("sss@ddd@");
        list.add("@@");
        list.add("5424324");
        list.add("@@@@fdsaf@");


        list = replaceList(list);
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i)+ ",");
        }
    }
    //把集合中字符串中包含@的,替换为¥。注意是替换字符串中字符,不是替换集合元素
    public static List<String> replaceList(List<String> list){
        for (int i = 0; i < list.size(); i++) {
            if(list.get(i).indexOf("@") >= 0){//有索引下标说明“@”存在
                String tmp = list.get(i).replace("@", "¥");
                list.set(i, tmp);//替换掉新的字符串
            }
        }
        return list;
    }
}

sss¥ddd¥,¥¥,5424324,¥¥¥¥fdsaf¥,

● 练习

1.成员变量中使用自定义类对象并输出信息。(注意输出对象的名字等)

② 接口类型做为成员变量,注意其中的Setter(4种传参方式都可以)传入为实现类,匿名实现类,匿名内部类,或匿名内部类匿名对象都可以。
③ 接口做为方法的参数和返回值(复习多态的作用)
A:-----------------------------------------------------------------------------------------------------------
分析:Person类中,Business为自定义类作为成员变量,Fu为接口作为成员变量,方法method01为接口作为参数,method02为接口作为返回值
复习: 对象的向上转型,其实就是多态写法:
Animal animal = new Cat();含义:右侧创建一个子类对象,把它当做父类来看待使用。注意,此时,类对象不可以调用子类Cat特有的方法(因为对于成员方法,编译看左,运行看右。)
对象的向上转型,就是父类引用指向子类对象
对象的向下转型,其实是一个还原的动作。
Animal animal = new Cat();本来是猫,向上转型成为动物
Cat cat = (Cat) animal;本来是猫,已经被当做动物了,还原回来成为本来的猫
a. 必须保证对象本来创建的时候,就是猫,才能向下转型成为猫
b. 如果对象创建的时候本来不是猫(比如是狗Dog类,其他的子类,或父类本身Animal),现在非要向下转型成为猫,就会报错。 ClassCastException类转换异常

package code01;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/04/07/18:58
 * @Description:  // 1.成员变量中使用自定义类对象并输出信息。(注意输出对象的名字等)
 */
public class Person {//例如人有一份事业或学业
    String name;
    boolean sex;
    Business thing;
    Fu object;
    Person(){

    }
    Person(String name, boolean sex, Business thing, Fu object){
        this.name = name;
        this.sex = sex;
        this.thing = thing;
        this.object = object;
    }
    void show(){
        System.out.println("姓名:" + this.name + "性别:" + this.sex +
                "主业:" + this.thing);
        if(this.thing != null){
            this.thing.show();
        }
        System.out.println("接口类型的成员变量为:" + this.object);
        if(this.object != null){
            this.object.method();
        }
    }

    void method01(Fu obj){
        //多态写法,因为Fu为接口,所以obj一定是该接口的实现类
        System.out.println("method01 接口作为参数");
        obj.method();
    }
    Fu method02(){
        System.out.println("method02 接口作为返回值");
        //4 中return写法任选一种
        Fu obj1 = new Zi();
        return new Zi();
//        Fu obj2 = new Fu(){
//            @Override
//            public void method() {
//                System.out.println("匿名内部类实现接口");
//            }
//        };
//
//        return obj2;
//        return new Fu(){
//            @Override
//            public void method() {
//                System.out.println("匿名对象匿名内部类实现接口");
//            }
//        };
    }

    //getter setter
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isSex() {
        return sex;
    }

    public void setSex(boolean sex) {
        this.sex = sex;
    }

    public Business getThing() {
        return thing;
    }

    public void setThing(Business thing) {
        this.thing = thing;
    }

    public Fu getObject() {
        return object;
    }

    public void setObject(Fu object) {
        this.object = object;
    }
}
package code01;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/04/07/18:59
 * @Description: 学业或事业
 */
public class Business {
    String name;//学业或事业名称
    int revenue;//收入
    int time;//每日活动时长

    public Business(){

    }
    public Business(String name, int revenue, int time){
        this.name = name;
        this.revenue = revenue;
        this.time = time;
    }


    void show(){
        System.out.println("当前事业为:" + this.name + ",收入:" + this.revenue + ",每日活动hour:" + time);
    }

}

package code01;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/04/08/19:58
 * @Description:
 */
public interface Fu {
    void method();

}

package code01;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/04/08/19:58
 * @Description:
 */
public class Zi implements Fu{
    @Override
    public void method() {
        System.out.println("实现类实现接口方法");
    }

}

package code01;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/04/07/18:57
 * @Description:
 */
public class MainTest {
    public static void main(String[] args) {
        // 1.成员变量中使用自定义类对象并输出信息。(注意输出对象的名字等)
        //2. 接口类型做为成员变量,注意其中的Setter(4种传参方式都可以)
        // 传入为实现类,匿名实现类,匿名内部类,或匿名内部类匿名对象都可以。
        Business thing1 = new Business("无业游民", 0, 0);
        Business thing2 = new Business("程序员", 20000, 10);
        //第3个参数自定义类,第4个参数,匿名实现类
        Person p1 = new Person("ken", false, thing1, new Zi());
        Zi object1 = new Zi();
        //第4个参数,实现类
        Person p2 = new Person("barbie", true, thing2, object1);
        Fu object2 = new Fu(){
            @Override
            public void method() {
                System.out.println("p3匿名内部类实现接口方法");
            }
        };
        //第4个参数,匿名内部类(指接口的实现类匿名,但生成的对象有名)
        Person p3 = new Person("barbie", true, thing2, object2);
        //第4个参数,匿名对象匿名内部类
        Person p4 = new Person("barbie", true, thing2, new Fu(){
            @Override
            public void method() {
                System.out.println("p4匿名对象匿名内部类实现接口方法");
            }
        });


        p1.show();
        p2.show();
        p3.show();
        p4.show();

        //3. 接口做为方法的参数和返回值(复习多态的作用)
        p1.method01(new Zi());
        p2.method01(new Fu(){
            @Override
            public void method() {
                System.out.println("匿名内部类作为匿名实现类");
            }
        });

        Fu obj2 = p1.method02();//多态写法,相当于向上转型
        //Zi obj3 = p1.method02();错在方法返回值返回的是多态的,看似是Fu,其实是Zi。名称还是Fu的
        //对于变量,编译看左,运行还看左。相当于。Fu xx= new Zi() , Zi obj3 = xx。
        Zi obj3 = (Zi)p2.method02();//强转,相当于还原


    }
}

2.发红包案例,①普通红包平均分法,②手气红包

实现接口OpenMode中的方法ArralList divide(final int totalMoney, final int totalCount)来返回分配好的红包集合
设置群主类和群成员类,可以加一个该群的类,群主类中有发红包方法,群成员类有收红包方法。执行时让群主发红包,群成员抢红包即可。
A:-----------------------------------------------------------------------------------------------------------
分析:
① 因为double,float浮点数其实是不精确的。类似10/3,=3.33……无穷,所以关于钱的数都取到小数点后两位,多少多少分即可。把钱数提升,如1元用100分int表示。输出时记得转回
② 普通红包也有除不尽的情况,把剩余除不开的一分钱放到最后一个红包即可。如3.33, 3.33, 3.34
③ 不管是普通红包还是随机红包,都应该规范红包个数totalCount和总金额totalMoney,因为最小金额是1分,红包不可为空,需要totalMoney(单位分) >= totalCount,否则错,需要重新规定金额和红包份数
④ 在生成随机红包时,需要考虑万一开始包的红包太大,导致后续包红包金额不够最小数1分怎么办?需要限制随机金额的上限,先计算当前包红包时,通过剩余红包数,算出剩余金额如果平均包给剩下的红包(平均数计算包括当前要包的这份,因为是一起平分剩余的钱),平均数ave是多少。使当前红包最大值 < 平均数 * 2。(不可以=,因为假设极端情况,剩余平均数为最小的1分,说明剩下的红包必须全都为1分钱,当前的红包当然不能取2分,不然后续就有空红包。所以平均数*2不可以取到)
⑤ 发散:第二种随机分配方法。如果给随机红包最大值搞的高一点,例如以前手气王红包几乎占满总数额的极端情况。可以试着取随机数范围只要保证,剩余红包金额都(平均数计算不包含当前这份,因为当前红包要取减完剩下的金额) = 1即可。例如:总金额10元,转化为1000分,发为3份红包,每次范围为[1, lastMoney-(lastCount - 1)]。第一次包,最大可以到1000 - 2 = 998,即最大值取到,只给后面每个红包留1分的程度。考虑极端情况,如果总金额3分,分成3份。第一次包红包,lastMoney = 3, lastCount = 3。范围[1, 3 -(3 - 1)]->[1,1]。第二次,[1,2-(2-1)] ->[1,1]第三次[1,1-(1-1)]->[1,1]
⑥ 注意,手气红包塞最后一个时,直接塞剩余金额即可,不用再生成随机数

package code01;
import java.util.ArrayList;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/03/30/16:43
 * @Description: 接口
 */
public interface Code01_OpenMode {
    ArrayList<Integer> divide(final int totalMoney, final int totalCount);
}

package code01;

import java.util.ArrayList;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/03/30/14:36
 * @Description: 实现普通红包分配红包的方法
 */
public class Code02_Average implements Code01_OpenMode {

    //注意10块提升为1000分,遇到平均数除不尽的,把剩下的全放最后一个红包即可
    @Override
    public ArrayList<Integer> divide(final int totalMoney, final int totalCount) {
        if(totalMoney < totalCount){
            System.out.println("金额不足,请重新塞红包");
            return null;
        }
        ArrayList<Integer> list = new ArrayList<>();
        int lastMoney = totalMoney;
        int ave = totalMoney/totalCount;
        for (int i = 0; i < totalCount - 1; i++) {//最后一个红包单独塞
            list.add(ave);
            lastMoney -= ave;
        }
        list.add(lastMoney);

        return list;
    }
}

package code01;

import java.util.ArrayList;
import java.util.Random;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/03/30/16:49
 * @Description: 实现手气红包分配红包的方法
 */
public class Code03_Luck implements Code01_OpenMode{
    //普通手气红包,每个红包金额 【1,ave*2)
    @Override
    public ArrayList<Integer> divide(final int totalMoney, final int totalCount) {
        if(totalMoney < totalCount){
            System.out.println("金额不足,请重新塞红包");
            return null;
        }
        ArrayList<Integer> list = new ArrayList<>();
        int lastMoney = totalMoney;
        int lastCount = totalCount;

        for (int i = 0; i < totalCount - 1; i++,lastCount--) {//同样的最后一个红包单独塞
            int ave = lastMoney/lastCount;
            Random r = new Random();
            int num = r.nextInt(ave * 2 - 1) + 1;//[1,ave * 2)
            list.add(num);
            lastMoney -= num;
        }
        list.add(lastMoney);
        return list;
    }
}

package code01;

import java.util.ArrayList;
import java.util.Random;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/03/30/16:49
 * @Description: 实现超级手气红包分配红包的方法
 */
public class Code04_MaxLuck implements Code01_OpenMode{
    @Override
    public ArrayList<Integer> divide(final int totalMoney,final int totalCount) {
        if(totalMoney < totalCount){
            System.out.println("金额不足,请重新塞红包");
            return null;
        }
        ArrayList<Integer> list = new ArrayList<>();
        int lastMoney = totalMoney;
        int lastCount = totalCount;

        for (int i = 0; i < totalCount - 1; i++,lastCount--) {//同样的最后一个红包单独塞
            Random r = new Random();
            int money = r.nextInt(lastMoney - (lastCount - 1)) + 1;
            //[1,lastMoney - (lastCount - 1)]其中lastMoney - (lastCount - 1)一定是 >= 1的
            list.add(money);
            lastMoney -= money;
        }
        list.add(lastMoney);
        return list;
    }
}

package code01;

import java.util.ArrayList;
import java.util.Random;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/03/30/16:56
 * @Description: 群成员类,只有收红包方法,没有发红包方法。规定只有群主才可以发红包
 */
public class Code05_Memebers {
    private static int count = 0;
    private int id;
    private String name;
    protected double money;
    int groupId;
    Code05_Memebers(){
        count++;
        id = count;
    }
    Code05_Memebers(String name, double money){
        this.name = name;
        this.money = money;
        count++;
        id = count;
    }
    void receive(ArrayList<Integer> list){
        if(list == null){
            System.out.println("红包为空,抢不了");
            return;
        }
        if(list.size() == 0){
            System.out.println("id" + this.id + "的" + this.name + "没抢到");
            return;
        }
        Random r = new Random();
        int index = r.nextInt(list.size());
        double money = list.get(index);//这里单位还是分
        list.remove(index);
        /*变分为元*/
        money /= 100;
        this.money += money;//跟小数(默认double)做运算,结果是范围大的那种数据类型
        //如果是money/100会怎么样?会损失money的精度,并且自带.0 。
        System.out.println("id" + this.id + "的" + this.name + "抢到:" + money + "余额为:" + this.money);

    }
    //刚好用到内部类,因为想让创建群变成一个私有的操作,包括创建群属于普通成员
    // 以及换群主,给群命名等操作


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getMoney() {
        return money;
    }

    private void setMoney(double money) {
        this.money = money;
    }

    public static int getCount() {
        return count;
    }
}

package code01;

import java.util.ArrayList;
import java.util.Scanner;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/03/30/16:56
 * @Description: 群主类
 */
public class Code06_Leader extends Code05_Memebers {
    Code06_Leader(){

    }
    Code06_Leader(String name, double money){
        super(name,money);
    }

    public ArrayList<Integer> divide(final double money, final int count){
        if((money * 100) % 100 != 0){
            System.out.println("小数点超出两位,请重新输入");
            return null;
        }
        if(this.money < money){
            System.out.println("余额不足,请重新输入");
            return null;
        }
        this.money -= money;
        //规定参数必须最多精确到小数点后两位
        /*变元为分*/
        int totalMoney = (int)(money * 100);
        //使用三种不同的发红包方式
        System.out.println("请选择发红包的方式:普通红包请输1,手气红包请输2,超级手气红包请输3:");
        Scanner sc = new Scanner(System.in);
        int choose = sc.nextInt();
        int flag = 0;
        ArrayList<Integer> list = null;
        if(choose == 1){
            list = new Code02_Average().divide(totalMoney, count);
        }else if(choose == 2){
            list = new Code03_Luck().divide(totalMoney, count);
        }else if(choose == 3){
            list = new Code04_MaxLuck().divide(totalMoney, count);
        }else{
            if(flag >= 5){
                System.out.println("已经输错5次,请过1分钟再尝试");
                return null;
            }
            System.out.println("输入错误请重新输入!");
            flag++;
            divide(totalMoney, count);
        }
        return list;

    }
}

package code01;

import java.util.ArrayList;
import java.util.Scanner;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 雪雪子
 * @Date: 2024/03/30/16:43
 * @Description: 实现抢红包思路,分别为普通红包,手气红包和超级手气红包
 */
public class MainTest {
    public static void main(String[] args) {
//        int money = 1024;
//        double num = money/100;
//        System.out.println(num);

        ArrayList<Code05_Memebers> redList = new ArrayList<>();//这里不能用多态的Code06类型,
        // 因为调用divide方法时,编译看左。Code06里没这个方法

        Code06_Leader l1 = new Code06_Leader("群主", 100);
        redList.add(l1);
        redList.add(new Code06_Leader("一一",34));
        redList.add(new Code06_Leader("二二",0));
        redList.add(new Code06_Leader("三三",0));
        redList.add(new Code06_Leader("四四",0));
        redList.add(new Code06_Leader("五五",0));
        redList.add(new Code06_Leader("六六",0));
        System.out.println("请分别输入 发红包的金额和份数" + ",注意金额最高精度为小数点后两位,多出位数不算。\n(提示目前余额为:" + l1.getMoney() + "总人数为" + Code05_Memebers.getCount() + ")");
        Scanner sc = new Scanner(System.in);
        double money = sc.nextDouble();
        int count = sc.nextInt();
        ArrayList<Integer> list = l1.divide(money, count);
        for (int i = 0; i < redList.size(); i++) {
            redList.get(i).receive(list);
        }
    }
}

3. 自定义实现3种情况,①类作为成员变量,②接口作为成员变量,③接口作为参数和返回值

A:-----------------------------------------------------------------------------------------------------------
参照下文练习1.

Object类

java.lang.Object类 Object 是类层次结构的根(父)类
每个类(Person,Student……)都使用Object作为超(父)类,
所有对象(包括数组)都实现这个类的方法
如果一个类没有特别指定父类, 那么默认则继承自Object类。例如:

public class MyClass /*extends Object*/ {
  	// ...
}

toString方法和equals方法

  • public String toString()`:返回该对象的字符串表示。
  • public boolean equals(Object obj):指示其他某个对象是否与此对象“相等”。

toString

Person类默认继承了Object类,所以可以使用Object类中的toString方法
String toString() 返回该对象的字符串表示

public static void main(String[] args) {
        Person p = new Person("张三", 18);
        String s = p.toString();
        System.out.println(s);
    }

结果

code02_Java07.toString.Person@7cef4e59
System.out.println(p.toString());
System.out.println(p);

结果

code02_Java07.toString.Person@7cef4e59
code02_Java07.toString.Person@7cef4e59

注意:直接打印对象就相当于调用对象的toString方法,如果输出的字符串是包名.类名地址值说明没有重写
直接打印对象的名字,其实就是调用对象的toString p = p.toString();
快捷:在类中 alt + Insert 自动生成toString 方法

在这里插入图片描述

equals

源码:

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

参数:Object obj:可以传递任意的对象

对于 == 比较运算符,返回一个boolean值

  • 基本数据类型:比较的是值
  • 引用数据类型:比较的是两个对象的地址

其中this是:哪个对象调用的方法,方法中的this就是那个对象;p1调用的equals方法this就是p1,Object是传递过来的参数p2。
注意 equals不可以比较基本数据类型,因为不是Object的子类,但包装类可以

public static void main(String[] args) {
     Person p1 = new Person("Barbie", 18);
     Person p2 = new Person("Ken", 20);
     boolean b = p1.equals(p2);
     System.out.println(b);//false
     
	 p1 = p2;
     b = p2.equals(p1);
     System.out.println(b);//true
}

重写 boolean equals(Object obj)

  1. 注意:参数为Object,但在类Person中要比较对象是否跟参数相同,是Person类型。所以多态
    为了比较Person对象中的属性是否相同,要对传入的obj进行向下转型
    在这里插入图片描述
    优化:
    ①增加一个判断,如果obj是this直接返回true
    ②增加一个判断,如果obj是null直接返回false
  2. Alt + insert 快捷重写equals,一路next
    在这里插入图片描述

分析代码,getClass() != o.getClass() 使用反射技术,判断o是否是当前方法所在类即Person类型,等效于obj instanceof Person

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

重写equals为的是比较同类对象的属性是否相同,如果不重写比较的是对象的地址,必须是同一个对象才相等。没有意义

Objects类的public static boolean equals(Object a, Object b)方法

在这里插入图片描述
就是多一个判断,防止空指针异常
例如:

		String str1 = null;
        String str2 = "abc";
        //运行× System.out.println(str1.equals(str2));
        System.out.println(Objects.equals(str1,str2));

equals也可以比较集合,只要是引用类型就可以比较

 List<String> list = new ArrayList<>();
        list.add("sss@ddd@");
        list.add("@@");
        list.add("5424324");
        list.add("@@@@fdsaf@");


        list = replaceList(list);
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i)+ ",");
        }
        System.out.println();

        /*测试equals方法*/
        List<String> list2 = new ArrayList<>();
        list.remove(2);
        list.remove(2);
        list2.add("sss¥ddd¥");
        list2.add("¥¥");
        System.out.println("list");
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i)+ ",");
        }
        System.out.println();
        System.out.println("list2");
        for (int i = 0; i < list2.size(); i++) {
            System.out.print(list2.get(i)+ ",");
        }
        System.out.println();
        System.out.println("list和list2是否相同:" + list.equals(list2));

输出

sss¥ddd¥,¥¥,5424324,¥¥¥¥fdsaf¥,
list
sss¥ddd¥,¥¥,
list2
sss¥ddd¥,¥¥,
list和list2是否相同true

== 和 equals的区别

在这里插入图片描述

在这里插入图片描述

❤の思考:关于+链接后的字符串是否在常量池

测试字符串被拼接,截取操作后是否在常量池。(判断标准)

  • 常量池的字符串内容相同就是一个对象,用==比较是不是一个字符串对象
  • 用concat(String)拼接字符串;用+拼接字符串;
  • 从其他字符串中截取substring(2种);
  • 用replace替换字符串中字符得到返回值。
  • 用split分割得到字符串数组。
  • 假设得到的字符内容都是abcabc,检查最终得到的字符串是否在常量池中
public class MainTest {
    public static void main(String[] args) {
        //以下所有比较的字符串内容相同
        String str = "abcabc";
        String strCopy = "abcabc";
        String str1 = "ab".concat("cabc");
        String str2 = "a" + "bca" + "bc";
        String str22 = "bca";
        String str3 = "a" + str22 +"bc";
        String str4 = "aecaec".replace("e","b");
        String[] strArr1 = "abcabceartdeagfdseae".split("ea");
        String str5= strArr1[0];

        System.out.println(str == str1);//false//方法拼接的str1在堆
        System.out.println(str == str2);//true//字符串常量+拼接的字符串str2,在常量池
        System.out.println(str == "a" + "bca" + "bc");//true//字符串常量+拼接的匿名字符串结果,也在常量池
        System.out.println(str == str3);//false//其中有str22字符串引用拼接,str3在堆
        System.out.println(str == str4);//false//方法替换得到的str4在堆
        System.out.println(str == str5);//false//方法截取得到的str5在堆
        System.out.println(str == strCopy);//true//直接创建的在常量池

        String str11 = "aaa";
        String str88 = "aaa";
        System.out.println("str8:" + str11 == "str8:aaa");//false//+优先级高,最终笔试双方为 "str8:aaa" == "str8:aaa"但"str8:" + str1得到的字符串在堆,因为str8是字符串引用,“+”拼接在运行期间
        System.out.println(("str8:" + str11) == "str8:aaa");//false//同上
        System.out.println("str8:" + (str11 == "aaa"));//str8:true//直接创建在常量池

    }
}

重点对比str2、str3
都是用+拼接,怎么一个在常量池,一个不在?
注意区分字符串常量和字符串引用,即一个直接创建且匿名一个有名字

  • str2在常量池因为:
    字符串常量拼接在编译期间就已经完成,“+”号操作时处于编译时期。编译完后的“abcabc”放入常量池
  • str3在堆因为:
    字符串引用的拼接在运行时执行,“+”号操作时处于运行时期。执行完语句后创建一个新的字符串引用str3在堆来接收"a" + str22 +"bc"的结果。其中“a” ,str22和“bc”都在常量池,因为都是直接创建的,只有str3这个用引用有str22参与,“+”号拼接的在堆。

总结:

  • 对于String,属于引用类型,而==比较引用类型,必须得是同一个对象才true。
    其中直接创建的String对象在常量池,只要内容一样,就是同一个对象
    而使用空参,字符数组,字节数组这3种方式创建的String对象(包括使用字符串常用方法拼接、截取、替换得到的String)(和+拼接字符串引用得到的String对象),==内容相同也不是同一个对象,因为不在常量池中保存。==在堆中保存
  • 其中,直接创建的字符串,和+ 拼接字符串常量得到的String对象,都保存在堆中的字符串常量池中保存。特点:只要内容相同,就是同一个对象。即内容相同用==比较为true
  • 对于sout("str8:" + str1 == "str8:aaa")因为 + 优先于 == 实际比较的是 “str8:” + str1(即str8:aaa)和 后续的字符串比较,明明内容相同为啥false呢,因为自妇产引用经过+拼接得到的字符串也不在常量池。不是一个对象
    用equals比较就相同了

● 练习

1. 类Person有属性,String name, int age, Appearance looks(自定义类),Pets pet (自定义接口)。重写equals让Person对象之间比较属性

A:-----------------------------------------------------------------------------------------------------------
Person对象中成员变量包含appearance自定类,和接口Pets
因为接口不能new对象,所以都是多态写法,向上转型。左接口,右接口实现类。
因为接口中没有变量属性,只能定义常量。所以比较接口对象是否相同时无意义,因为比较的是实现类新增的变量。

package java07.demo01;

import java.util.Objects;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: xuexuezi
 * @Date: 2024/06/12/21:47
 * @Description:
 * 类Person有属性,String name, int age, Appearance looks(自定义类),
 * Pets pet (自定义接口)。重写equals让Person对象之间比较属性
 */
public class Person {
    private String name;
    private int age;
    Appearance looks;
    Pets pet;

    public Person(String name, int age, Appearance looks, Pets pet) {
        this.name = name;
        this.age = age;
        this.looks = looks;
        this.pet = pet;
    }

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

    public Person() {
    }

    public void show(){
        System.out.println("姓名:" + this.name);
        System.out.println("年龄:" + this.age);
        System.out.println("穿着:" + this.looks);//包含两属性,可以直接输出,重写toString即可
        System.out.println("宠物属性:" + this.pet);
        System.out.println("宠物习性:");//调用方法
        this.pet.eat();
        this.pet.play();
    }

    /**
     要比较内容而非地址,比较是否相同只能比较属性
     其中,接口中只有方法且无方法体,所以要比较也是比较实现类的属性内容。方法内容无法比较
     因为Person中看似是接口做成员变量,实际内容却是实现类,直接比较实现类
     接口的实现类中重写equals方法,比较实现类的属性

     缺点:匿名实现类也得重写equals方法才行
     问题:要比较也是比较实现类自己新添加的属性,没啥意义。
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name) &&
                Objects.equals(looks, person.looks) &&
                Objects.equals(pet, person.pet);
    }

    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 Appearance getLooks() {
        return looks;
    }

    public void setLooks(Appearance looks) {
        this.looks = looks;
    }

    public Pets getPet() {
        return pet;
    }

    public void setPet(Pets pet) {
        this.pet = pet;
    }
}
package java07.demo01;

import java.util.Objects;

public class Appearance {
    private String coat;
    private String pants;

    public Appearance() {
    }

    public Appearance(String coat, String pants) {
        this.coat = coat;
        this.pants = pants;
    }

    @Override
    public String toString() {
        return "Appearance{" +
                "coat='" + coat + '\'' +
                ", pants='" + pants + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Appearance that = (Appearance) o;
        return Objects.equals(coat, that.coat) &&
                Objects.equals(pants, that.pants);
    }

    public String getCoat() {
        return coat;
    }

    public void setCoat(String coat) {
        this.coat = coat;
    }

    public String getPants() {
        return pants;
    }

    public void setPants(String pants) {
        this.pants = pants;
    }
}

package java07.demo01;

public interface Pets {
    void eat();
    void play();
}
package java07.demo01;
import java.util.Objects;

public class PetsImpl implements Pets {
    private String name;
    private boolean healthy;

    public PetsImpl(String name, boolean healthy) {
        this.name = name;
        this.healthy = healthy;
    }

    public PetsImpl() {
    }

    @Override
    public void eat() {
        System.out.println("吃小鱼");
    }

    @Override
    public void play() {
        System.out.println("玩毛线球");
    }

    @Override
    public String toString() {
        return "PetsImpl{" +
                "name='" + name + '\'' +
                ", healthy=" + healthy +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PetsImpl pets = (PetsImpl) o;
        return Objects.equals(name, pets.name) &&
                Objects.equals(healthy, pets.healthy);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isHealthy() {
        return healthy;
    }

    public void setHealthy(boolean healthy) {
        this.healthy = healthy;
    }
}

2. 说明equals(参数)equals(参数,参数2)区别

一个字符串调用,一个Objects类调用。
一个this的字符串不可以为空,一个可以为空

String str1 = ""
String str2 = null;
//str2.equals(str1);//错在str2这里不可以为null,null不能调用什么字符串的方法
str1.equals(str2);//false
Objects.equals(null,str2);//true

3. 快捷生成类的toString和equals一参数方法

alt + insert
在这里插入图片描述
其中equals一直保持默认next生成即可
比如类PetsImpl属性是
private String name;
private boolean healthy;
的情况

@Override
    public String toString() {
        return "PetsImpl{" +
                "name='" + name + '\'' +
                ", healthy=" + healthy +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PetsImpl pets = (PetsImpl) o;
        return Objects.equals(name, pets.name) &&
                Objects.equals(healthy, pets.healthy);
    }

4. ★ 判断下列比较是否相等的结果,注意字符串常量池

用equals和==分别比较整数num1和其他所有num,比较str1和其他所有str,比较自定义类Person的两个对象,自定义类Appearance的两个对象
判断比较结果是true还是false

  • Person类重写equals方法,用来比较属性
public class Person {
    String name;
    int age;
    public Person(){

    }
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
     @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }
}
  • Appearance类三个属性,没重写equals方法
public class Appearance {
    boolean beauty;
    int height;
    double weight;

    public Appearance() {
    }

    public Appearance(boolean beauty, int height, double weight) {
        this.beauty = beauty;
        this.height = height;
        this.weight = weight;
    }
}
  • String类,基本数据类型,自定义类对象。分别进行比较
 	public static void main(String[] args) {
        //复习:String的3+1种创建方式,
        String str1 = "aaa";

        String str2 = new String();
        char[] chArr1 = new char[4];
        chArr1[1] = '4';
        chArr1[2] = '$';
      
        String str3 = new String(chArr1);
        byte[] byArr1 = {2, 1, '5', '#'};
        String str4 = new String(byArr1);
        char[] chArr2 = {'a', 'a', 'a'};
        byte[] byArr2 = {'a', 97, 97}; //因为每个字符有对应的Ascii码值,byte取值范围-2^7~2^7-1。只要字符的码在这个范围会默认为整数

        String str5 = new String(chArr2);
        String str6 = new String(byArr2);
        String str7 = str1;
        String str8 = "aaa";

        int num1 = 4;
        double num2 = 4.0;
        long num3 = 4L;
        float num4 = 4F;

        Person p1 = new Person("Barbie", 18);
        Person p2 = new Person("Barbie", 18);
        
		Appearance a1 = new Appearance(true, 170, 120.8);
        Appearance a2 = new Appearance(true, 170, 120.8);
    }

A:-----------------------------------------------------------------------------------------------------------

		System.out.println(str1 == str2);//false,对于引用对象只比较地址。只有是同一个对象才true
        System.out.println(str2.equals(str1));//false,只要负责调用的这个str2不为null,1参数和2参数的equals没区别
        System.out.println(Objects.equals(str2, null));//false,空字符串和null不同。null连地址和引用对象都没有
        System.out.println(Objects.equals(str2, ""));//true,都是空字符串
        System.out.println(str1 == str3);//false//不是一个对象
        System.out.println(str1.equals(str3));//false//内容不同
        System.out.println(str1 == str4);//false
        System.out.println(str1.equals(str4));//false
        System.out.println(str1 == str5);//false//★ 虽然内容相同,但是构造器构造的字符串不在常量池
        System.out.println(str1.equals(str5));//true//★ equals对于File,Data,String和包装类是比较内容,因为有重写equals方法
        System.out.println(str1 == str6);//false
        System.out.println(str1.equals(str6));//true
        System.out.println(str1 == str7);//true //★同一个对象
        System.out.println(str1.equals(str7));//true
        System.out.println("----------------------------------------");

        System.out.println("str8:" + str1 == str8);//♥false//why?"+"优先级高于"=="str8比较的对象是前两段字符串
        System.out.println("str8:" + str1 == "str8:aaa");//?♥false,牵扯到常量池问题,字符串引用相同通过+连接的字符串也并不在常量池,并不是一个对象
        System.out.println("str8:aaa".equals("str8:" + str1));//true//但内容相同
        System.out.println("str8:" + (str1 == str8));//str8:true//★直接构造的字符串在常量池,内容相同的话,都是同一个对象共享
        System.out.println(str1.equals(str8));//true
        System.out.println("-----------------------------------------");

        //基础数据类型只能用==比较,equals只能比较引用类型
        //值相同,数据类型不同是可以互相比较
        System.out.println(num1 == num2);//true
        System.out.println(num1 == num3);//true
        System.out.println(num1 == num4);//true

        System.out.println(p1 == p2);//false//不是一个对象
        System.out.println(p1.equals(p2));//true//equals有重写,属性相同,对象就相同

        System.out.println(a1 == a2);//false
        System.out.println(a1.equals(a2));//false//没重写,跟==没区别

5. (复习)思考字符串截取,替换操作后的常量池问题

A:-----------------------------------------------------------------------------------------------------------
总结以上:
直接创建的字符串在堆中的常量池
new String对象字符串的在堆
字符串拼接,替换,截取等操作得到的字符串在堆
"+"号拼接的字符串引用,也在堆
“+”号拼接的字符串常量,在常量池

常量池中字符串内容相同就是同一个对象
==比较基础数据类型的内容,比较引用对象的地址。必须是一个对象才true
equals只能比较引用对象,也是比较地址。String,Data,File和包装类是比较对象的内容,因为这几个类有重写equals方法

String 对象.equals(String obj)比较,如果对象为null,就会空指针异常
Object.equals(Object a, Object b)就不会,可以a,b都为null,返回true

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值