类、接口的使用
类作为成员变量类型
例:类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)
- 注意:参数为Object,但在类Person中要比较对象是否跟参数相同,是Person类型。所以多态
为了比较Person对象中的属性是否相同,要对传入的obj进行向下转型
优化:
①增加一个判断,如果obj是this直接返回true
②增加一个判断,如果obj是null直接返回false - 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