java—08 equals + 浅拷贝与深拷贝 + 内部类 + lambda

一、Object 中的 equals方法,重要!!!

public class Hello {
    public static void main(String[] args) {
        User u = new User(10,"小牛");
        System.out.println(u);
        User p = new User(10,"小牛");
        System.out.println(p);
        System.out.println(p.equals(u));
    }
}
class User {
    private int id;
    private String name;

    public User() {
    }
    public User(int id, String name){
        this.id = id;
        this.name = name;
    }
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
    
    //这种情况输出false
    @Override
    public boolean equals(Object obj) {
        return (this == obj);
    }
// equals 比较两个对象是否是一个对象,在这里也就是说 u 和 p
// Object类中原生的方法实现只能比较两个对象的“引用”是否相等,所以这里返回false


    //这种情况输出 true
    @Override
    public boolean equals(Object o) {//形参是Object o,这里传入的实参是User类型的对象引用,Objcet o = new User();
//         在调用equals(Object o)时,实际上传入的是指向了子类型User对象的父类型引用Object o
//         该equals(Object o)是成员方法,该方法中还有一个隐藏的参数this,是第一个形参,equals(Object this, Object o);
//         方法的调用者给这个this传实际值,然后同Object o所指向的实参类型进行比较


        if (this == o) return true; //Object类中原生的equals()方法
        if (o == null || getClass() != o.getClass()) return false;//如果传入的实参为空,没有指向堆内存中的对象;
//       如果不为null,那么堆中已经new了对象,如果this所代表的对象的类型和传入的实参对象的类型不一样,也就是getClass和o.getClass类型不一样;

        User user = (User) o;//由Object o = new User();知道o所指向的是子类型User的对象,接着便是向下转型
//      问:这里问什么没有ClassCastException异常?  答:第三条语句,如果不是一个类型,直接返回false。

        return id == user.id &&  //比较两个整型的数据是否相等??
            Objects.equals(name, user.name); //比较两个String类型的name属性是否相等,调用了Objects类中的equals方法,见下 ↓
    }

// 这种方式是两个不同引用指向的对象,拥有相同的属性值我们也认定为是相同的对象

    public static boolean equals(Object a, Object b) { //Object a = new String(name);
//		   首先,上面的 name 和 user.name是String类型的,注意:String类型也是一个引用类型!!!String的父类是Object
//		   也就是父类型的引用指向子类型对象
        
        
        return (a == b) || (a != null && a.equals(b));//a、b同时为null,如果同时为空,id又相等,那两个User类型的对象当然是同一个
//		   a.equals(b)方法本质上调用的是String中重写的equals方法。 见下 ↓   
    
    }
    
//		补充String中重写的equals方法如下:
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length; //数组有length用法
            if (n == anotherString.value.length) {
                char v1[] = value; //char[] = value; 
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
}

二、浅深拷贝

2.1 浅拷贝

public class Hello {
    public static void main(String[] args) {
            User u = new User(1, "小牛");

            User user = new User(2, "小美");
            u = user;
          //这里只是 在栈中的 u 和 user 指向了堆内存中同一个首地址对象User(2, "小美") 
            System.out.println(u);// User{id=2, name='小美'}
        }
}

在这里插入图片描述

2.2 深拷贝clone

深拷贝就是重写clone()方法
方法的重写:
 @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

这里注意一下:因为Object的clone()protected的,它在java.lang包下,跨包无法访问Object的clone()方法,要导包。
    
方法一:
      先在main里面调用,再强转
    也就是,重写的方法的返回值类型还是Object的
    
public class Hello {
    public static void main(String[] args) {
        User u = new User(2, "小美");
        User user = new User();
        try {
            user = (User) u.clone();// clone()之后再强转
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        System.out.println(user);
    }
}


方法二:
    重写的时候可以改变Object类中的clone()方法的返回值类型。
    也就是将Object类型变为User类型
    (方法的返回值类型可以改变的,但一定要是父类型或者是父类型派生的子类型。)
    
    @Override
    public User clone() throws CloneNotSupportedException{
        return (User) super.clone();
    }   
  
public class Hello {
    public static void main(String[] args) {
        User u = new User(2, "小美");
        User user = new User();
        user = u; //就不用强制类型转换了。
        System.out.println(user);//User{id=2, name='小美'}
    }
}

在这里插入图片描述

三、内部类

3.1 成员内部类

内部类编译之后得到的.class文件,两个类名之间带有 符 号 : A n i m a l 符号:Animal AnimalAnimalRun.class
注意语法上与正常的类的定义没有区别,只是相当于成员属性。我们以前学过类中有引用类型的成员属性,这种类型的成员属性是不是一定要new出来?内部类也一样,只是语法上有个包含关系。
内部类表达调用方法:
OuterClass.InnerClass 标识符 = new OuterClass().new InnerClass();

// 定义一个接口
public interface IRun {
    void run();
}

// 写一个接口的实现类
public class Person implements IRun {
    @Override
    public void run() {
        System.out.println("人跑步!");
    }
}

// 测试
public class Test {
    public static void main(String[] args) {
        Person p= new Person();
        p.run();// 人跑步
    }
}
public class Animal {
    public Person p;
    class AnimalRun implements IRun {

        @Override
        public void run() {
            System.out.println("动物跑!");
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Person p= new Person();
        p.run();
        
        第一种:        
      //  Animal animal = new Animal();
      //  Animal.AnimalRun ar = animal.new AnimalRun();
      //  ar.run();
        
        第二种:
        // OuterClass.InnerClass 标识符 = new OuterClass().new InnerClass();
        Animal.AnimalRun ar1 = new Animal().new AnimalRun();

     
    }
}

3.2 静态内部类

相当于一个静态属性

public class Animal {
    public Person p;
    static class AnimalRun implements IRun {
        @Override
        public void run() {
            System.out.println("动物跑!");
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Person p= new Person();
        p.run();
            
       相当于一个静态static属性
       Animal.AnimalRun ar2 = new Animal.AnimalRun();
        ar2.run();

     
    }
}

3.3 局部内部类

相当于一个局部变量

public class Animal {
    public void walk() {
       
        相当于一个局部变量
        class LocalClass implements IRun {
            @Override
            public void run() {
                System.out.println("局部内部类---run!");
            }
        }
       // new LocalClass().run();
        LocalClass lc = new LocalClass();
        lc.run();

    }
}

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

        Animal ar3= new Animal();
        ar3.walk();
    }
}

3.4 匿名内部类

匿名内部类就是没有名字的内部类,不能实例化对象。

new 接口名 [或者是父类名] {
实现接口的抽象方法或者是父类中的抽象方法
}; 后面一定要加上分号。
相当于new Person();

public class Animal {
    public void walk() {
        new IRun(){
            @Override
            public void run() {
                System.out.println("匿名内部类---run!!");
            }
        };
    }
}

调用匿名内部类中的方法,采用new 接口名(){}.方法名();的形式,相当于new Person().run();
new IRun(){
            @Override
            public void run() {
                System.out.println("匿名内部类---run!!");
            }
        }.run();

匿名内部类的另外一种调用方式:在调用的方法的时候,以参数的形式写匿名内部类

public interface IRun {
    void run();
}

public class Animal {
    public void walk(IRun iRun) {
        iRun.run();
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.walk(new IRun(){
            @Override
            public void run() {
                System.out.println("匿名内部类的另外一种调用方式");
            }
        });
    }
}

new IRun这里并不是指new了一个接口类型的对象!!!

意思是:new一个IRun接口的实现类,它的实现类是没有名字的,new这个实现类的依据是{}中的代码,这个{}所包裹的代码就是匿名内部类的代码。
   new IRun(){
            @Override
            public void run() {
                System.out.println("匿名内部类---run!!");
            }
        }
	这是一个完整的匿名内部类。他一定要配合new 接口名()来使用,否则无法给它创建对象。

3.5 匿名内部类的简化——lambda

lambda表达式的前提————要是函数式接口
函数式接口: 只含有一个抽象方法的接口叫做函数式接口。

public class Test {
    public static void main(String[] args) {
        Animal animal = new Animal();
----------------------------------------------------------------------------           
没简化的时候  ↓
----------------------------------------------------------------------------        
        animal.walk(new IRun(){
            @Override
            public void run() {
                System.out.println("匿名内部类的另外一种调用方式");
            }
        });
----------------------------------------------------------------------------          
对比一下,简化之后的表达式样式,叫做lambda表达式,该省的省 ↓ 
----------------------------------------------------------------------------
        animal.walk(()->{
            System.out.println("匿名内部类runrunrun");
        });


        animal.walk(()-> System.out.println("faefawefawefawefawefawe"));
----------------------------------------------------------------------------   
    }
}

带参数的lambda表达式

interface IEat {
    // @param str 表示吃早饭还是中饭、晚饭的描述
    void eat(String str);
}

public class Person {
    public void dinner(IEat iEat,String str) {
        iEat.eat(str);
    }

public static void main(String[] args) {
        Person p= new Person();
        p.dinner(str-> System.out.println(str),"晚餐");
    }
}

总结:千万注意(提醒我自个儿),匿名内部类的特殊用法,不是不是new了一个接口对象哈

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值