java进阶-第一讲 浅拷贝与深拷贝 内部类与lambda表达式

第一讲: 浅拷贝与深拷贝、内部类

import com.demo01.pojo.User;

public class Demo01 {
    public static void main(String[] args) {
        User u = new User(100,"张三");

        User user = new User(100, "张三" );
        u = user;

        System.out.println(u);
        // 以上代码,我们首先创建了一个只有初始值的对象
        // 然后我们创建了一个已经赋值的对象
        // 我现在的想法是将user赋值给u,这样的话u所指向的对象就有值了?
        // 这显然不行。这仅仅只是把user这个引用所保存的对象的内存首地址给到了u
        // u原来所指向的对象被GC回收,u这时候和user指向了同一个对象User(100,"张三")
        // 这叫做浅拷贝
        // 我现在就是想要在堆里面有两个一模一样的对象,他们的内存地址不一样
        // 1. 构造方法
        // 2. clone
    }
}

1. 深拷贝

  • 方法1:构造方法,构造两个一模一样的对象
  • 方法2:clone()
 @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

重写clone()方法,因为Object的clone()protected的,它在java.lang包下,我们不可能在lang包下创建应用程序,所以跨包无法访问Object的clone()方法。我们思考一下,为什么要用protected修饰?克隆的本意是复制一个全新的对象出来,脱离了对象,一直调用Object的clone方法是不是不够用啊。所以一定要重写。    
第一种方案:先将其做成父类型的引用的指向子类型的对象,然后向下转型。
    也就是,重写的方法的返回值类型是Object的。(在本例中)
public class Demo01 {
    public static void main(String[] args) {
        User u = new User(100, "张三");
        User user = null;
        try {
            user = (User) u.clone();// clone()之后再强转
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        System.out.println(user);
    }
}


第二种方案:
    重写的时候可以改变Object类中的clone()方法的返回值类型。从这里我们补充一下重写的规则:方法的重写一定要有继承关系,这是大前提,没有这个前提没有重写机制。然后,private修饰的不能重写。重写的时候方法名、参数列表不能变;访问权限不能更低,只能相等或更高。这里可以看到,方法的返回值类型是可以改变的,但一定要是父类型或者是父类型派生的子类型。
    
    @Override
    public User clone() throws CloneNotSupportedException{
        return (User) super.clone();
    }   

使用的时候,就不用强制类型转换了,因为在这里已经强转了。
  • 第三种方案:用得最多的方式,序列化与反序列化,以后再说。
    • 大概的意思:一个对象是一个实实在在的东西,比如一台电脑是一个对象,它放在你家的书桌上,我们可以把这台电脑一个部件一个部件的拆开,邮寄到学校,然后一个部件一个部件的装上。在内存中,我们发送出去的是一份拷贝。

2.内部类

  • 内部类在项目中使用不多,但一定要会,尤其是匿名内部类和lambda表达式,我们用得不多,但其他人用得多,一定要看得懂这种奇怪的语法。
  • 内部类的用法及演变过程
// 定义一个接口
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 person = new Person();
        person.run();// 人跑步
    }
}

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

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

public class Test {
    public static void main(String[] args) {
        Person person = new Person();
        person.run();

        Animal animal = new Animal();
        Animal.AnimalRun ar = animal.new AnimalRun();
        ar.run();

        Animal.AnimalRun ar1 = new Animal().new AnimalRun();
    }
}
  • 静态内部类:相当于一个静态属性
public class Animal {
    public Person person;
//    class AnimalRun implements IRun {
//
//        @Override
//        public void run() {
//            System.out.println("动物跑!");
//        }
//    }

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


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

    static class AnimalRun implements IRun {
        @Override
        public void run() {
            System.out.println("动物跑!");
        }
    }
}
  • 局部内部类:相当于一个局部变量
public class Animal {
//    class AnimalRun implements IRun {
//
//        @Override
//        public void run() {
//            System.out.println("动物跑!");
//        }
//    }

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

    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) {
//        Person person = new Person();
//        person.run();

        Animal animal = new Animal();
//        Animal.AnimalRun ar = animal.new AnimalRun();
//        ar.run();
//
//        Animal.AnimalRun ar1 = new Animal().new AnimalRun();

//        Animal.AnimalRun animalRun = new Animal.AnimalRun();
//        animalRun.run();
        animal.walk();
    }
}
  • 重点(很重要) 匿名内部类
    • 思考一个问题:匿名就是没有名字,匿名内部类就是没有名字的内部类,请问一个类没有名字如何实例化对象?不能实例化对象。
    • 所以,匿名内部类的定义方式为:
      • new 接口名[或者是父类名] {
        • 这里是实现接口的抽象方法或者是父类中的抽象方法
        • };后面一定要加上分号。
        • 这就好比是new Person();
public class Animal {
//    class AnimalRun implements IRun {
//
//        @Override
//        public void run() {
//            System.out.println("动物跑!");
//        }
//    }

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

    public void walk() {
//        class LocalClass implements IRun {
//            @Override
//            public void run() {
//                System.out.println("局部内部类---run!");
//            }
//        }
//       // new LocalClass().run();
//        LocalClass lc = new LocalClass();
//        lc.run();

        new IRun(){
            @Override
            public void run() {
                System.out.println("匿名内部类---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("匿名内部类的另外一种调用方式");
            }
        });
    }
}
写在方法体中的匿名内部类:
public void test(){
    new xxx() {
        m1(){
            ....
        }
    }.m1();
}


第二种形式:
public void test(Interface i){
    i.m1();
}
可以在方法被调用时,直接写在参数列表中。
    test(new Interface() {
        public void m1() {
            ....
        }
    });
这样就不需要再次调用m1方法了。    
new IRun(){
            @Override
            public void run() {
                System.out.println("匿名内部类---run!!");
            }
        }.run();

解读一下这种语法:
	new 是为了产生一个对象,new IRun()这是不成立的,因为IRun是接口,接口不能被实例化,所以不能将new IRun()理解为new一个接口类型的对象,这是错的!!!!
    
    new IRun()在匿名内部类中才能这么写,其他地方不能这么写,写了就错了。这是匿名内部类特有的写法,只属于匿名内部类。意思是:new一个IRun接口的实现类,它的实现类是没有名字的,new这个实现类的依据是{}中的代码,这个{}所包裹的代码就是匿名内部类的代码。
    {
            @Override
            public void run() {
                System.out.println("匿名内部类---run!!");
            }
        }
	这是一个完整的匿名内部类。他一定要配合new 接口名()来使用,否则无法给它创建对象,因为它连个名字都没有,所以我们只好用它父类的名字来指代它。
    并不是new了一个接口类型的对象,没有这种玩法,编译都通不过。
  • 匿名内部类的简化:lambda表达式(λ)
        animal.walk(()->{
            System.out.println("匿名内部类runrunrun");
        });


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

        // 以上,两种叫做lambda表达式。这个一定要知道怎么来的,要看得懂。否则线程就没法学
  • 带参数的lambda表达式
public 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 person = new Person();
        person.dinner(str-> System.out.println(str),"晚餐");
    }
}
  • lambda表达式的前提:这个非常重要。
    • 函数式接口。什么是函数式接口?只含有一个抽象方法的接口叫做函数式接口
    • 只有这样才能使用lambda表达式。
    • 也就是说:接口中有且仅有一个方法才能使用lambda表达式。
    • 记住它,记死它!!!!!!
    • lambda表达式是 java1.8 出来的新特性。这是一个很重要的特性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值