当前位置: 首页 > 后端技术 > Java

cs61bweek3--介绍和接口

时间:2023-04-01 20:59:32 Java

1.InterfaceInheritanceInterfaceInheritance回忆一下我们上周实现的SLList类和AList类,里面有很多类似的方法,比如addLast()addFirst()removeLast().....假设我们在某个类中调用SLList,当我们需要的时候调用AList,要么把代码中的SLList改成AList,要么把AList的所有类都搬过来。实际上,这两个方法包含很多重复的方法。可以看出它们来自同一个父类:List,两者都是继承自List。我们只需要定义一个父类,然后让AList和SLList分别继承父类的特性即可。是的,从而避免了很多重复冗长的代码。如何定义父类和子类的继承关系?在Java中,为了表达这种层次结构,我们需要做两件事:第1步:定义泛型列表上位词的类型——我们将选择名称List61B。第2步:指定SLList和AList是该类型的下位词。我们使用一种称为接口(interface)的新类型来表示上位词。不像以前,我们经常定义publicclass类名。接口被定义为公共接口接口名称。在接口中,我们只需要编写其中包含的函数声明,不需要编写具体的实现代码:publicinterfaceList61B{publicvoidaddFirst(Itemx);publicvoidaddLast(Itemy);公共项目getFirst();公共项目getLast();公共项目removeLast();公共项目获取(inti);publicvoidinsert(Itemx,intposition);IntellijIDEA中的publicintsize();}提示:在方法声明前加上public对于接口来说是多余的,也就是说可以删掉上面在方法声明前的第二步public,我们需要指定AList和SLList是List61B类的下位词,并在publicclassAList{...}publicclassAListimplementsList61B{中使用关键字implements。..}implementsList61B本质上是一个承诺。这意味着AList“将拥有并定义List61B接口中指定的所有属性和方法”。至此我们完成了父类List61B和子类AList的继承关系2.@Override标签我们在上面的List61B接口中定义了一些常用的方法,不能是私有类型,也就是说所有的List61B子类都有这些方法。AList继承后,可以根据需求具体实现这些方法。对于List61B的所有方法,都必须在AList中用代码实现,可以按照来实现,其实是一种Override(覆盖),在子类中重写从父类继承的方法,比如addFirst(),在List61B中:publicinterfaceList61B{publicvoidaddFirst(Itemx);}在子类中编写方法的具体实现代码时,在方法的右上方添加@Override:@OverridepublicvoidaddFirst(Itemx){insert(x,0);}这表示子类会重写父类的addFirst()方法,使用@Override有以下好处:检查错别字,假设你把addFirst()写成addFrist(),那么@Override会报红色,说明你没有重写这个方法,也就是更有利于Debug提醒程序员这个方法继承自父类,已经被重写了。即使不加@Override标签,编译器也会自动覆盖子类中addFirst()的实现代码到父类中的addFirst()。GoldRuleOfEqualRecallus第一周学习的黄金判断法则假设inta=5;整数b=a;当我们进行赋值操作时,会将b中的位复制给a,并且要求b和a的类型相同。您不能分配Dogb=1或Dogb=newCat()因为这三者的类型不同。所以假设有一个方法,它的参数是publicstaticStringlongest(List61Blist)oftypeList61B{intmaxDex=0;for(inti=0;i的实例a1并将其作为参数传递给longest()?publicstaticvoidmain(String[]args){ALista1=newAList();a1.addLast("马");WordUtils.longest(a1);}答案是如果X是YX的父类,那么X的内存框可以放Y,也就是可以传入两者参与赋值:List61BsomeList=newSLList();someList.addFirst("麋鹿");3.实现继承Implementation除了继承(InterfaceInheritance),还有一种继承方式就是实现继承(ImplementationInheritance)。在接口继承中,父类中的所有方法只有声明,没有具体的代码实现。不同的是,在实现继承中,我们可以为method写具体的实现代码,在声明前加上关键字default,defaultpublicvoidmethod(){...}比如为61BList写一个print()函数:defaultpublicvoidprint(){for(inti=0;isomeList=newSLList();然后调用someList.print();someList将执行哪个print()?是在父类中还是在子类中?在解决这个问题之前,你需要了解一些关于静态类型和动态类型的知识在Java中,每个变量都有两种类型,即编译时类型(静态类型)和运行时类型(动态类型):statictype定义在变量声明的时间永远不会在编译时改变动态类型的类型。当用new实例化一个对象时,动态类型等同于对象的类型。想象一下我们用一个变量来调用对象的某个方法,这个变量有静态类型X和动态类型Y。如果Y重写了方法,那么Y中被重写的方法就是替换原来的方法这在X中被称为“动态方法选择”。因此,print()实际上是调用SLList中的方法,而不是61BList中的OverrideVSOverload。如果子类中有一个方法与父类完全重名(包括参数和参数类型),那么我们将子类的这个方法Override给父类。如果子类有一个与父类同名的方法,但是参数或参数类型不同,则称为Overload方法选择算法动态选择方法算法原理:考虑一个函数调用foo.bar(x1),其中foo有一个静态类型TPrime,x1有一个静态类型T1在编译时,编译器会验证TPrime是否有可以处理T1的方法。记录此方法的签名(如果存在)。注意:如果有多个方法可以处理T1,编译器会记录“最具体”的那个。例如,如果T1=Dog,如果TPrime有bar(Dog)和bar(Animal),则bar(Dog)将被记录。在运行时,如果foo的动态类型覆盖记录的签名,则使用Override的方法。否则,使用该方法的TPrime版本。为了验证你是否理解Override和Overload的区别,有一个小测验:红框的答案是什么?首先,a的静态类型是Animal,动态类型是Dogd。静态类型和动态类型都是Dog,根据上面的算法:a.greet(d);步骤:验证Animal有greet()方法-->有,下一步验证greet()是否被子类Dog重写-->没有,调用Animal的greet()输出hellowanimala.sniff(d);step:验证Animal是否有sniff()方法-->是,下一步验证sniff()是否被子类重写-->是,调用Dog的sniff()方法输出dogsniffanimald.flatter(d);d静态类型和动态类型都是Dog,直接调用Dog的flatter输出dogsniffanimala.flatter(d);step:验证Animal是否有flatter()方法-->有,下一步验证flatter()是否被子类重写-->没有,Dog类的flatter()和Dog类的flatter()Animal类同名,但参数类型不同,属于Overload,不属于Override!调用Animal的flatter()方法而不是Dog的flatter(),因为它不是Override输出“你很酷的动物”关于重载的练习:假设以下方法都在同一个类中:publicstaticvoidpeek(List61Blist){System.out.println(list.getLast());}publicstaticvoidpeek(SLListlist){System.out.println(list.getFirst());}当你运行以下代码时:SLListSP=newSLList();List61BLP=SP;SP.addLast("elk");SP.addLast("are");SP.addLast("酷");偷看(SP);偷看(LP);依次输出什么?答案是elkcool不执行动态选择算法,因为peek()是Overload,所以编译器会选择静态类型对应peek()括号内的参数类型,传入执行5.接口继承VS实现继承。实现继承的缺点:当子类层次很多时,很难跟踪子类的Override方法在哪里,或者在哪里定义。复杂的代码,当两个接口之间有相同的默认方法时,可能会导致冲突封装(encapsulation)crash