JavaSE学习笔记 6

        前面了解了接口、抽象类,下面就可以简单的了解一下几个常用的接口了,这里又涉及java的公共父类Object。

        (一)Cloneable接口

        关于Cloneable接口,我们其实可以通过IDE比较方便的了解到一些内容,我通过IDEA先编写了如下代码:

        我们实现了Cloneable接口的Test类,我们就具体看看Cloneable接口里面到底有什么,Ctrl按住点一下Cloneable,我们直接可以找到对应接口的代码,其代码如下:

        我们可以发现,这个Clonable接口里面什么都没有, 那这个Cloneable接口有什么意义呢?其实这个接口的意义就只是用来声明,我们这个类是可以被复制的,这个接口内部本身没有任何需要实现的抽象方法。

        那么我们接下来如何使用这个接口的功能呢?首先我们就需要了解公共父类Object,为什么叫他是公共父类呢?因为Object是Java内默认的,所有类的父类,那既然是父类,那我们自然可以调用父类的成员方法,我们可以看看Object内有什么父类方法:

        通过IDE,我们可以看到,Object类内,有一个方法叫clone, 这个clone是如何实现克隆的呢?clone方法是直接将引用这个方法的对象,其内存数据复制一份,所以这个方法就无视了具体内存的存储数据,对程序来说,我不管你是什么,我只需要把你内存复制一下就好了。

        那我们通过上面截图也看到了,Object虽然是我们的公共父类,但是克隆方法的权限是protected,换句话说只允许子类内去调用它,在子类内调用它,我们就只能通过在子类内写一个方法出来进行调用,一般会直接对对应的clone进行重写,然后通过super,再去调用Object的colne方法,这样就能实现克隆了,但是要注意Object的clone方法,其返回值是Object,所以我们需要在返回后进行重新造型。以前面的代码为例,修改后的全部代码如下:

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException{
        Test test1 = new Test("li san", 98);
        Test test2 = null;
        Test test3 = null;

        test2 = test1.clone1();
        test3 = test1.clone();

        test1.print();                // 输出:名字 = li san| 分数 = 98
        System.out.println(test1);    // 输出:Test@1b6d3586
        test2.print();                // 输出:名字 = li san| 分数 = 98
        System.out.println(test2);    // 输出:Test@4554617c
        test3.print();                // 输出:名字 = li san| 分数 = 98
        System.out.println(test3);    // 输出:Test@74a14482
    }
}

class Test implements Cloneable{
    int point = -1;
    String name = null;

    public Test clone1() throws CloneNotSupportedException {
        return (Test)this.clone();
    }

    @Override
    public Test clone() throws CloneNotSupportedException{
        return (Test)super.clone();
    }
    public Test() {
        this("zhangsan", 100);
    }

    public Test(String name, int point) {
        this.name = name;
        this.point = point;
    }

    public void print() {
        System.out.println("名字 = " + this.name + "| 分数 = " + this.point);
    }
}

        通过看上面代码的输出值,我们可以看到test2和test3成功克隆了test1的值,并且我们通过

System.out.println(),将对应Test引用类型的对象放入其中,将对应引用类型内存储的地址打印了出来,我们可以看到test1、test2和test3的地址均不相同,这说明我们打印的并不是原来的test1,打印的是克隆出来的test2和test3。而且test2和test3通过重写和继承去调用了父类的clone方法,结果都是成功的。

        那我们不妨深思一下,假如我们Test类内,内置有一个String变量,假如我们进行拷贝,并进行打印,String变量代表的字符串会被拷贝一份吗?

        其实不难想象,假如我们拷贝了一份Test,String变量也被拷贝一份,但拷贝以后,其内存储的值是不会变化的,而String是引用数据类型,所以String变量内存储的是对应字符串的地址,新拷贝出来的String变量,虽然新的String变量和原来那个String变量已经不是同一个变量了,但期内值一样,所以他们指向的仍然是同一个字符串。也就没有对我们期望的字符串实现拷贝功能。

        对于上面这种拷贝,我们称之为浅拷贝,如果对对象内的引用数据类型也进行了单独的拷贝,那么我们称这种拷贝为深拷贝,本身也不难实现,方法就是在拷贝出来一份Test后,再单独增加一行代码,对Test内的引用数据类型进行克隆即可。

        (二)抽象类和接口的区别

                抽象类和接口有相似之处,但总体来说还是差别更大。

                抽象类和接口都不能被实例化。

                抽象类和接口都需要被继承、实现,但抽象类本身跟普通类相比,除了多个抽象方法,其他都跟普通类是完全一样的,而接口则只能允许有公开静态常量和公开抽象方法。

                抽象类和接口的抽象方法都不实现,由继承、实现的子类进行实现。

                由于抽象类和接口的抽象方法都需要子类去重写,所以不能是private、static修饰的方法,并且抽象方法都不能是构造方法,接口还不允许存在静态代码块。

                从使用上来说,抽象方法相当于多了一道检查工序,而接口,更像是一种公共规范,规定了一个公共方法集合,一旦实现了这个接口,就必须要实现这个接口里的所有方法。

        (三)Object类

                前面也提到过Object类,这是公共父类,也就是所有类默认的父类,从另外的角度来看,Object类的成员变量、成员方法是被所有类都继承的。

                既然Object是所有类的父类,所有类都于它存在继承关系,那我们就可以利用Object来实现对所有类的引用,即向上转型,调用重写的方法。

                我们上面说了所有类都能重写父类Object的方法,那么我们首先要知道父类Object到底有哪些方法,不过由于后面内容也学的不多,我先把目前可能会用到的一些方法说一下,分别是toString方法、equals方法、clone方法、hascode方法。

                通过上面的四个方法,我们可以分别实现对对应类对象的字符串化、是否相等的对比、克隆以及获取其哈希值。最常见的情况就是我们自定义了一个类,由于是引用对象,引用对象之间如果直接引用equals进行是否相等的比对,只会对两个引用对象的地址进行比较,一般来说并不是我们想要的结果;再比如我们希望打印这个类对象的成员变量值,直接调用父类的toString方法,会导致打印出来的是一个地址。所以不管是toString方法还是equals方法,一般来说,我们自定义的类对象都需要对其进行重写。而clone方法,则如上面第二条的cloneable接口一样,想要调用,则需要实现这个接口才行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值