自学内容网 自学内容网

Java初阶--抽象类+抽象模版模式

1.对象的克隆方法

我们的这个clone也是像这个hashcode,equals等等,是这个object这个类下面的一个方法,主要就是进行的拷贝,对象的拷贝;

他的这个接口就是clonable,和这个comparable很相似,就是这个形容词,接口表示的就是这样的功能;

在我们的这个person类里面,需要对于这个克隆的方法进行重写的操作,这个throws就是抛出异常,implements clonable表示这个接口是可以被克隆的,clone方法的这个返回值是这个object类型的,因此这个test里面,我们对于这个返回的对象进行强制类型转换;

在这里插入图片描述
clone()得到的对象进行强制类型转换,赋值给我们的这个person2对象;
在这里插入图片描述

2.浅拷贝

浅拷贝就是我们的这个重写的clone方法,return值就是我们的这个super.clone()这个是我们的自动填充上去的,如果对于这个自动填充的代码不进行任何的修改,这个时候就是执行的浅拷贝;
在这里插入图片描述
我们的这个成员变量的初识数值大小是9.9,当我们的任意一个对象里面的引用对于这个成员变量的值进行修改,两个打印的结果都会进行修改,这个就是浅拷贝的问题;
在这里插入图片描述
下面的这个就是浅拷贝的堆栈图,可以解释为什么这个浅拷贝的时候,我们对于这个person1里面的这个成员变量进行修改,也会让这个person2对象里面的这个成员变量随之改变;

浅拷贝就是直接把这个person1对象的这个内容拷贝给了这个person2对象,这个时候,对于这个money引用(这个里面的成员变量和对象都是叫做money,不要误会了)我们的这个money对象就是一个引用,指向的就是我们的这个money成员变量(person类里面的这个成员变量,图示有问题,应该是money成员变量,但是这个是属于peron类里面的这个person1对象的);

这个时候,拷贝之后,这个person2对象里面的这个money引用也是指向的这个成员变量,因此两个对象里面的这个引用,只要有一个对于这个数值进行修改,这个money就会同步发生改变,但是我们想要的不是这样的情况,我们想要让两者互不影响;

在这里插入图片描述

3.深拷贝

想要解决这个浅拷贝的问题,我们需要对于这个重写的clone的方法,因为这个时候深拷贝就是让这个拷贝之后的这个person2对象的引用指向自己的这个成员变量,不要和这个person1指向相同的位置,这个时候就可以让两者的改变互不影响;

我们的做法就是重写这个clone方法,这个时候,我们定义一个对象temp接受这个clone的返回值,我们想要克隆的是这个person1对象里面的这个money引用指向的这个成员变量,this就是我们的这个被克隆的对象,具体到这个问题里面就是person1,也就是把这个person1对象的money引用clone到我们的这个temp里面去,这个实际上就是把这个引用对象里面的这个成员变量money给了这个temp(这个时候因为我们的这个上面的money类也需要对于这个里面的方法重写我们的clone方法,并且抛出异常),然后我们的这个tem作为返回值,这个时候,我们的这个person2接受的对象就是temp,这个时候的temp的成员变量就给了我们的person2,这个时候person1和person2对象里面的这个money引用里面对应的成员变量的数值就会不一样,地址也是不一样,实现各自对于成员变量的控制,这个就是深拷贝的过程;

在这里插入图片描述
在这里插入图片描述

4.抽象类和接口的再谈(一)

1)抽象类和接口都是我们的java里面多态的具体的体现形式;

2)抽象类需要被继承,一个子类只能继承一个抽象类,但是可以实现多个接口;

3)接口是使用implement关键字进行实现,抽象类是使用这个extends关键字被继承实现;

4)一个抽象类可以实现若干个接口,但是一个接口只能继承父亲接口,不可以继承抽象类;

看似总结了,但是我的内心对于这个抽象类和接口并没有一个系统的认识,就是两者之间到底有什么异同之处,其实并不是很清楚,这个时候,我准备去把这个hsp老师的课再学习一下;

下面的这个就是我们在一个包里面定义的一个类Animal,这个类里面有一个成员变量和成员方法,还有一个构造方法,在这个成员方法里面,我们就是简单的实现了一个eat方法,但是动物的种类有很多,我们其实并不知道这个动物具体会吃什么,因此我们的这个打印其实是没有任何的实际意义的;
在这里插入图片描述
这个时候,我们可以把这个eat方法定义为抽象的方法,这个时候如果这个类里面的方法是抽象的方法,这个时候我们的这个类就必须是抽象类了,我们的这个加上abstract的抽象类里面的这个抽象方法是不需要实现的,因为我们的这个类就是因为没有什么值得打印的内容才被定义为抽象的类的,这个时候我们定义抽象类就是为了不实现的这个方法,因为具体到某一个动物,他吃的东西不一样;

这个时候,如果我们加上方法体{},就会报错,报错信息:this abstract methods can’t have a body就是这个抽象的方法不可以拥有一个方法体;
在这里插入图片描述
简单总结:
1)abstract修饰的类,就是抽象类;
2)abstract修饰方法的时候,这个方法就是抽象的方法,public/private abstract 返回类型 方法名()这样的方式去实现这个方法;
3)抽象类更多的价值在于设计,便于这个类的子类去实现具体的功能;
4)抽象类不可以实例化:就是不可以new对象;
在这里插入图片描述
5)抽象类可以没有抽象方法,像上面的这个抽象类A里面没有抽象方法也是可以的;而且这个抽象类可以有自己实现的方法;
6)一个类有抽象的方法,这个类一定是抽象类,但是没有抽象方法,也可以是抽象类;
7)abstract只能修饰这个类和方法,但是不可以修饰属性(例如这个anstract int a=10这样是不被允许的);
8)抽象类也是类,除了这个里面的抽象方法是不可以实现的,其他的和这个普通类没有区别,就是普通的类有的我们也可以有:像下面的这个抽象类里面的这个成员变量,静态变量,普通的方法,我们都是可以有的;
9)继承抽象类的子类需要对于我们的这个父类里面的抽象方法进行重写,否则会报错,除非我们的这个子类也是抽象类;
在这里插入图片描述

10)抽象方法不可以使用private final static关键字修饰,否则我们的抽象方法无法被子类重写方法;

5.抽象模版模式–抽象类的价值

在这里插入图片描述
其实这个抽象模版模式:就是为了体现我们上面学习的这个抽象类的价值,通过这个案例,我们逐渐从传统的类和对象过渡到我们的抽象类的写法;

5.1初级版的解读

这个因为是进行一个任务的完成,并且计算完成这个任务花费的时间,因此这个时间的计算上面我们使用的是这个currentTimeMillis方法,这个方法的作用就是进行这个时刻的统计,我们在计算之前统计一次,在计算之后统计一次,在这个计算的过程中花费的时间,就是我们的这个最后时间减去初始时刻的时间;

我们的AA类里面是实现了一个累加的计算,BB里面实现的是一个累乘的计算,两个类就是按照相同的思路写的,就是这个计算的内容不一样,这个就是我们完成这个问题的初级版本思路;
在这里插入图片描述

5.2进阶版的解读

对于上面的这个情况,有些同学可能会想到这个封装一个函数,就是下面的这个calculateTime函数,这个函数里面就是我们的这个不变的量,例如这个初始时刻的时间的统计,以及这个终止时刻的时间的统计,以及这个两者之间的插值,就是把这个job函数放到我们的这个calculate这个方法里面去,这样的话,如果我们的这个AA类里面想要实现job2()的时候,这个方法依然是可以使用的,这个就是函数的思想;
在这里插入图片描述
这个时候,我们的job就不用背调用了,在我们的test里面,需要去使用实例化的对象去调用这个calcalateTime这个方法,因为这个时候job是被我们的这个calcalateTime方法调用的;

5.3高级版的解读

下面的这个就是我们把计算这个开始时间和结束时间,以及这个时间差的内容封装成为一个抽象类,在这个类里面实现这个计算时间的calculatetime方法,但是不实现这个job方法,而是把这个job方法定义为一个抽象的方法,这样无论是我们的这个AA还是BB,都会去对于这个方法进行重写,这样的就是充分利用了我们的抽象类的特性,如果我们的这个aa调用calculate方法,就动态绑定到我们的这个AA类里面的这个计算,这个就实现了我们的多态的效果以及抽象类的使用;

在这里插入图片描述
相信通过这个抽象模版模式,你对于抽象类一定有了更加深入的理解~~


原文地址:https://blog.csdn.net/binhyun/article/details/142744644

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!