week4 总结

通配符和上下界

需求:

  1. 有水果类,有苹果类,苹果继承水果,有一个装水果的盘子。
  2. 实现向盘子里放入水果,实现从盘子里取出水果。
  3. 实现向盘子里放入苹果,实现从盘子里取出苹果。

plate相当于一个容器,水果类与苹果类是父子关系可以适用里氏替换原则。但是用plate装起来的水果类与苹果类就不再适用。就算容器里装的东西之间有继承关系,但容器之间是没有继承关系的。

代码示例:

//父类:水果
class Fruit{

}
//子类:苹果
class Apple extends Fruit{

}
//定义一个容器,放水果的盘子,设计为泛型,所以可以放各种水果。通过get和set方法提供 “放” 和 “取” 的动作)
class Plate<T>{
    private T item;
    public Plate(T item){
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}
//测试
public class Test {
    public static void main(String[] args) {
        //定义一个水果盘子,放水果
        Plate<Fruit> p1 = new Plate<Fruit>(new Fruit());//正确
        //定义一个苹果盘子,放苹果
        Plate<Apple> p2 = new Plate<Apple>(new Apple());//正确
        //定义一个水果盘子,放苹果
        Plate<Fruit> p3 = new Plate<Apple>(new Apple());//错误
        //定义一个苹果盘子,放水果
        Plate<Apple> p4 = new Plate<Fruit>(new Apple());//错误
    }
}

程序第9和第11行报编译错误。为什么?

逻辑上,水果盘子当然可以放苹果,因为苹果是水果的子类,里氏替换原则是允许用子类对象代替父类对象的。

但实际上Java编译器不允许这个操作,

第 9行会报出 “ 装苹果的盘子无法转换为装水果的盘子 ”

第11行会报出 “ 装水果的盘子无法转换为装苹果的盘子 ”

实际上,编译器的逻辑是这样的

  • 苹果 is a 水果
  • 装苹果的盘子 not is a 装水果的盘子

所以,就算容器里装的东西之间有继承关系,但容器之间是没有继承关系的。

解决办法是使用上界通配符和下届通配符

什么是上界通配符?

把父类看作是继承关系中的上界。

上界通配符

Plate<? extend 父类>

表示限定了这个盘子可以放的东西上界,所以上界和上界以下的东西可以放进去,即父类和子类都能放进去。

修改第9行的代码为

Plate<? extends Fruit> p3 = new Plate<Apple>(new Apple());//正确

什么是下界通配符?

把子类看作是继承关系中的下界。

下界通配符

Plate<? super 子类>

表示限定了这个盘子可以放的东西下界,所以下界和下界以上的东西可以放进去,即子类和父类都能放进去。

修改第11行的代码为

Plate<? super Apple> p4 = new Plate<Fruit>(new Fruit());//正确

Lambda的限制条件

Lambda表达式在实现接口时,接口中只允许有且只有一个抽象方法。

这种限制可以使用函数式接口来定义

String类中涉及正则表达式的方法

String类中提供了matches()方法用于验证正则表达式,另外split()方法和replaceAll()方法中也可以使用正则表达式。

  • boolean matches(String regex)
  • String [] split(String regex)
  • String replaceAll(String regex,String replacement)

public boolean matches(String regex) :判断字符串是否匹配给定的规则

/*
 * 举例:校验qq号码
 * 1: 要求必须是5-15位数字
 * 2: 0不能开头
*/
String qq = "12882274";
String regex = "[1-9][0-9]{4,14}";
boolean result = qq.matches(regex);
System.out.println(result);

public boolean matches(String regex) :判断字符串是否匹配给定的规则

/*
 * 举例:校验手机号码
 * 1:要求为11位数字
 * 2:第1位为1,第2位为3、4、5、7、8中的一个,后面9位为0到9之间的任意数字
*/
String phone = "13579246810";
String regex = "1[34578][0-9]{9}";
boolean result = phone.matches(regex);
System.out.println(result);

public String[] split(String regex) 根据给定正则表达式的匹配规则,拆分此字符串

/*
 * 举例:分割出字符串中的的数字
*/
String s = "1-20-300-4000";
String regex = "-";
String[] result = s.split(regex);//根据给定正则表达式的匹配规则,拆分此字符串
System.out.println(Arrays.toString(result));//[1, 20, 300, 4000]

String s1 = "1/20/300/4000-50000-6000000";//根据给定正则表达式的匹配规则,拆分此字符串
String regex1 = "[/-]";//根据 / 或 - 拆分字符串
String[] result1 = s1.split(regex1);
System.out.println(Arrays.toString(result1));//[1, 20, 300, 4000, 50000, 6000000]

public String replaceAll(String regex,String replacement):将符合规则的字符串内容,全部替换为新字符串

/*
 * 举例:把文字中的数字替换成*
 */
String s = "Hello01234World56789";
String regex = "[0-9]";
String result = s.replaceAll(regex, "*");
System.out.println(result);//Hello*****World*****

hashCode()

猜猜这段代码输出什么

class person{
    public int age;
    public int num;

    public person(int age, int num) {
        this.age = age;
        this.num = num;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}
public class hashCodeDetail {
    public static void main(String[] args) {
        person p1 = new person(1,2);
        HashSet<Object> set = new HashSet<>();
        set.add(p1);
        p1.setAge(2);
        p1.setNum(1);
        set.add(p1);
        System.out.println(set.size());
    }
}

输出 1 因为默认hashCode方法是根据对象的地址值,而不是内容,如果要判断内容需要重写hashCode与equals方法

class person{
    public int age;
    public int num;
    public person(int age, int num) {
        this.age = age;
        this.num = num;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setNum(int num) {
        this.num = num;
    }

    @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 && num == person.num;
    }

    @Override
    public int hashCode() {
        return Objects.hash(age, num);
    }
}
public class hashCodeDetail {
    public static void main(String[] args) {
        person p1 = new person(1,2);
        HashSet<Object> set = new HashSet<>();
        set.add(p1);
        p1.setAge(2);
        p1.setNum(1);
        set.add(p1);
        System.out.println(set.size());
    }
}

此时这段代码输出的是2 重写后都是比较值的方法

点进objects这个工具类的hash方法可以看出他是根据内容改变hash值的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n4tkZ1cS-1659938698974)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20220805145651059.png)]
bject> set = new HashSet<>();
set.add(p1);
p1.setAge(2);
p1.setNum(1);
set.add(p1);
System.out.println(set.size());
}
}


此时这段代码输出的是2 重写后都是比较值的方法

点进objects这个工具类的hash方法可以看出他是根据内容改变hash值的

![在这里插入图片描述](https://img-blog.csdnimg.cn/959d4237da65492194145bdec5855240.png#pic_center)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值