深浅拷贝

浅拷贝

public class User {
    String name;
    int id;
    public User() {
    }

    public User(String name,int id ) {
        this.id = id;
        this.name = name;
    }
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
public class Main {
    public static void main(String[] args) {
        User u = new User("tx",19);
        User a = new User();
        a = u;
        System.out.println(a);
    }
}

打印结果

User{id=19, name='tx'}

这就是浅拷贝 ,创建两个对象 , 第一个对象的引用指向第二个对象 , 第一个对象就被GC回收 ,这只是把user这个引用所保存的对象的内存首地址给到了u , 两个引用指向了同一个对象 ,这就是浅拷贝

深拷贝

如果想要在堆里面有两个一模一样的对象,他们的内存地址不一样
方法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 Main {
    public static void main(String[] args) {
        User u = new User("tx",19);
        User a = null;
        a = u;
        try {
            a = (User) u.clone();// clone()之后再强转
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        System.out.println(a);
    }
}


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

使用的时候,就不用强制类型转换了,因为在这里已经强转了。

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

内部类

类可以嵌套定义 ,即在一个类的类体中可以嵌套定义另一个类 。被嵌套的类称为内部类 , 它的上级被称为外部类 。 内部类中还可以再嵌套一个类 , 最外层的类被称为顶层类 。
除外部类外,其他类不能访问内部类 。当一个类只在某一个类中使用 ,并且不允许除外部类外的其他类访问时 ,可以考虑把该类设计成内部类

内部类的使用

public class Outer {
    private int x=10;
     class Inner{
        private int x = 20;
        void print(){
            int x = 30;
            System.out.println(x);               //输出内部类局部变量
            System.out.println(this.x);          //输出内部类成员变量
            System.out.println(Outer.this.x);    //输出外部类成员变量
        }
    }


}
public class Main {
    public static void main(String[] args) {
        Outer out = new Outer();
        Outer.Inner a = out.new Inner();
        a.print();
    }
}

运行结果

30
20
10

内部类的演变

public interface Work {
    void work();
}

public class Outer implements Work {
    private int x=10;

    @Override
    public void work() {
        System.out.println("工作");
    }
}

public class Main {
    public static void main(String[] args) {
        Outer out = new Outer();
        out.work();
    }
}

运行结果

工作

成员内部类:相当于一个成员属性

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

    @Override
    public void work() {
        System.out.println("工作");
    }
}

public class Good  {
    public Outer out;
    class In implements Work {

        @Override
        public void work() {
            System.out.println("继续工作");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Outer out = new Outer();
        Good good = new Good();
        Good.In tx = good.new In();
        tx.work();
        out.work();
    }
}

运行结果

继续工作
工作

静态内部类:相当于一个静态属性

public class Main {
    public static void main(String[] args) {
        Outer out = new Outer();
        Good good = new Good();
        //Good.In tx = good.new In();
        //tx.work();
        Good.In tx = new Good.In();
        tx.work();
    }
}

public class Good  {
    public Outer out;
   static class In implements Work {

        @Override
        public void work() {
            System.out.println("继续工作");
        }
    }
}

局部内部类:相当于一个局部变量

public class Good  {
    public void see()
    {
        class In implements Work {

            @Override
            public void work() {
                System.out.println("局部内部类运行");
            }
        }
        new In().work();
    }
}

public class Main {
    public static void main(String[] args) {
        Outer out = new Outer();
        Good good = new Good();
        good.see();
        //Good.In tx = good.new In();
        //tx.work();
    }
}

匿名内部类

重点(很重要) 匿名内部类
思考一个问题:匿名就是没有名字,匿名内部类就是没有名字的内部类,请问一个类没有名字如何实例化对象?
不能实例化对象。
所以,匿名内部类的定义方式为:
new 接口名[或者是父类名] {
这里是实现接口的抽象方法或者是父类中的抽象方法
};后面一定要加上分号。
这就好比是new Outer();
public class Good  {
    public void see()
    {
        new Work(){
            public void work(){
                System.out.println("匿名内部类运行");
            }
        }.work();
    }
}

public class Main {
    public static void main(String[] args) {
        Outer out = new Outer();
        Good good = new Good();
        good.see();
        //Good.In tx = good.new In();
        //tx.work();
    }
}

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

public interface Work {
    void work();
}

public class Good  {
    public void see(Work worker)
    {
        worker.work();
    }
}

public class Main {
    public static void main(String[] args) {
        Outer out = new Outer();
        Good good = new Good();
        good.see(new Work(){
            @Override
            public void work() {
                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 Work(){
            @Override
            public void work() {
                System.out.println("匿名内部类---run!!");
            }
        }.work();

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

匿名内部类的简化——lambda表达式

        good.work(()->{
            System.out.println("匿名内部类runrunrun");
        });


        good.work(()-> System.out.println("faefawefawefawefawefawe"));

        // 以上,两种叫做lambda表达式。这个一定要知道怎么来的,要看得懂。否则线程就没法学

ambda表达式的前提:这个非常重要。
函数式接口。什么是函数式接口?只含有一个抽象方法的接口叫做函数式接口
只有这样才能使用lambda表达式。
也就是说:接口中有且仅有一个方法才能使用lambda表达式。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值