当前位置:编程学习 > JAVA >>

Thinking in Java 4th chap7笔记-复用类

1.组合和继承,两种代码重用机制。
2.当创建一个类时,总是在继承,因此,除非已明确指出要从其他类中继承,否在就是在隐式的从Java的标准根类Object进行继承。
3.String:+ / +=
4.调用父类的方法,用super.f(),否在如果在子类覆写的f方法中调用f的话,则会产生递归;
5.继承并不只是复制基类的接口。当创建了一个导出类的对象时,该对象包含了一个基类的子对象。这个子对象与你用基类直接创建的对象是一样的。二者区别在于,后者来自外部,而基类的子对象被包装在导出类对象内部。
     ->对基类子对象的正确初始化至关重要,而且仅有一种方法保证这一点。在构造器中调用基类构造器来执行初始化,而基类构造器具有执行基类初始化所需要的所有知识和能力。Java会自动在导出类的构造器中插入对基类构造器的引用。
     ->如果没有默认的基类构造器,或者想调用一个带参数的基类构造器,就必须使用关键字super显示的编写调用基类构造器的语句,并且配以适当的参数列表。
     ->调用基类构造器必须是在你导出构造器中要做的第一件事情。[否在编译器报错]
6.第三种关系称为代理,Java并没有提供对它的直接支持。这是继承与组合之间的中庸之道,因为我们将一个成员对象置于所要构造的类中(就像组合),但与此同时我们在新类中暴露了该成员对象的所有方法(就像继承)。
7.有时候类可能在生命周期内执行一些必要的清理活动。因为你并不知道垃圾回收器何时被调用,或者它是否会调用。因此,如果你想要某个类清理一些东西,就必须显示的编写一个特殊方法来做这件事,并确保客户端程序员知晓他们必须调用这一方法。【必须将这一清理动作置于finaly子句中,以预防异常的出现】
8.清理方法(dispose)中,还必须注意对基类清理方法和成员对象清理方法的调用顺序,以防止某个子对象依赖于另一个子对象的情形。一般而言,所采用的形式应该与C++编译器在其析构函数上所施加的形式相同:首先,执行类的所有特定的清理动作,其顺序同生成顺序相反(通常这就要求基类元素仍然存活),然后调用基类的清理方法。
     注:C++构造函数和析构函数顺序
      1.构造函数:基类依次调用->类内部成员声明的顺序构造->派生类构造。
      2.析构函数:与析构函数顺序相反,编译器用栈实现。
     [个人认为因为类内部成员的声明顺序表明对象之间的依赖情形,如A先声明,表明A可能会被其他对象依赖,所以在析构的时候要最后析构]
     许多情况下,清理并不是问题,仅需让垃圾回收器完成该动作就行。但当必须亲自处理清理时,就的多做努力并多加小心。因为一旦涉及垃圾回收,能够信赖的事就不会很多了。垃圾回收器可能永远也无法调用,即使被调用,它也可能以任何它想要的清理顺序来回收对象。最好的办法除了内存以外,不能依赖垃圾回收器去做任何事情。如果需要清理,最好是编写自己的清理方法,不过不要使用finalize().
9.名称屏蔽:
 1.如果Java的基类拥有某个已被多次重载的方法名称,那么在导出类中重新定义该方法名称并不会屏蔽其在基类中的任何版本。(这一点与C++不同),因此无论是在该层还是它的基类对方法进行重新定义,重载机制都可以正常工作。
  注:Java这样做,看上去也令人迷惑不解,这也是C++为什么不允许这样做的原因所在一防止你可能会犯错误。
10.@Override注解,当你想要覆写某个方法时,可以选择添加这个注解,在你不留心重载而非覆写了该方法时,编译器会报错。
11.组合与继承之间选择:
     1.二者都允许在新的类中放置子对象,组合是显示的这样做,而继承是隐式的做。
     2.组合技术通常用于想在新类中使用现有类的功能而非它的接口的情形,即在新类中嵌入某个对象,让其实现所需要的功能。->新类中嵌入一个现有类的private对象。
     3.在继承的时候,是有某个现有类,并开发它的一个特殊版本。通常,这意味着你在使用一个通用类,并为了某种特殊需要而将其特殊化。
     4.is-a(是一个)的关系是用继承来描述的;而has-a(有一个)的关系是用组合来表达的。
12.protected:就类用户而言,这是private的,但对于任何继承于此类的导出类或者任何位于同一个包的类来说,它却是可以访问的;protected也提供了包内访问权限;
 注:尽管可以创建protected域,不过最好的方式还是将域保持为private,你应当一直保留“更改底层实现的权利”;然后通过protected方法来控制类的继承者的访问权限。
13.为新的类提供方法并不是继承技术最重要的一方面,其最重要的方面是用来表现新类和基类之间的关系。这种关系可以用“新类是现有类的一种类型”这句话加以概括。
     ->语言支撑,由于继承 可以确保基类中所有的方法在导出类中也同样有效,所以能够像基类发送的所有信息同样也可以向导出类发送->编译器支持->向上转型
     ->向上转型,因为传统的类继承图的绘制方法,根置于页面的顶端,然后逐渐向下。导出类转成基类,在继承图是向上移动的,所以一般称为向上转型。
     ->因为向上转型是一个从较专用类型向较通用类型转换,所以总是很安全的。也就是说,导出类是基类的一个超集。它可能比基类含有更多的方法,但它必须至少具备基类中所含有的方法。在向上转型的过程中,类接口中唯一可能发生的事情是丢失方法,而不是获取他们(不能访问他们/赢得这些方法)。这就是为什么编译器在“未曾明确表示转型”或“未曾指定特殊标记”的前提下,仍然允许向上转型的原因。
14.面向对象编程中,生成和使用程序代码最有可能采用的方法就是直接将数据和方法包装进一个类中,并使用该类的对象。也可以运用组合技术使用现有类来开发新的类,而继承技术其实并不是太常用。应当慎用这一技术,其使用场合仅限于你确信使用该技术有效的情况。到底是使用组合还是继承,一个最清晰的判断方法就是问一问是否需要从新类向基类进行向上转型。如果必须向上转型,则继承是必要的;但如果不需要,则应当好好考虑自己是否需要继承。
15.可能用到final的三种情况:数据,方法,和类
     final,通常是指无法改变的;不想改变可能出于两种理由,设计或效率。
     1.数据的恒定不变,如:一个永不改变的编译时常量(带有恒定初始值);一个在运行时被初始化的值,而你不希望它被改变;对于基本类型,final使数值恒定不变,而用于对象引用,final使引用恒定不变;一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象。然而对象自身确实可以被修改的。这一对象同样适用于数组,它也是对象(另一种引用)。
     2.定义为static,则强调只有一份;定义为final,则说明它是一个常量。
     3.不能因为某数据是final的就认为在编译时可以知道它的值,在运行时也可动态生成,如利用Random随机。
     4.空白final:所谓空白final是指被声明为final却又未给定初值的域。无论什么情况,编译器都确保空白final在使用前必须被初始化。但是空白final在关键字final的使用上提供了更大的灵活性。为此,一个类中final域就可以做到根据对象有所不同,却又保持其恒定不变的特性。[即在不同的构造函数来初始化final]
 必须在域的定义处或者每个构造器中用表达式对final进行赋值,这正是final域在使用前总是被初始化的原因。
     5.final参数:Java允许在参数列表中以生命的方式将参数声明为final,这意味着你无法在方法中更改参数引用所指向的对象。
     ->当基本类型的参数被指定为final的时候,你可以读参数,不过却无法修改参数.这一特性主要用来向匿名内部类传递数据;
     6.final方法:使用final方法的原因有两个:第一个原因是把方法锁定。以防止任何继承类修改它的含义。确保在继承中使方法行为保持不变,并且不会被覆盖。过去建议使用final方法的原因是效率。在java早期实现中,如果将一个方法指明为final的话,就是同意编译器将针对对该方法的所有调用都转为内嵌调用。当编译器发现一个final方法调用命令时,它会根据自己的谨慎判断,跳过插入程序代码这种正常方式而执行方法调用机制(将参数压入栈,跳至方法代码处并执行,然后跳回并清理栈中的参数,处理返回值),并且以方法体中的实际代码的副本来代替方法调用。这种将消除方法调用的开销。当然,如果一个方法很大, 你的程序代码就会膨胀,因为可能看不到内嵌带来的任何性能上的提高。因为所带来的性能的提高会因为花费于方法内的时间量而被缩减。在最近的Java版本中,特别是hotspot技术可以探测到这种情况,并优化去掉这些效率反而降低的额外的内嵌调用,因此不再需要使用final方法来进行优化了。事实上,这种做易做图在逐渐的受到劝阻。在使用javase5/se6的时候,应该让编译器和JVM去处理效率问题,只有在想明确禁止覆盖的时候,才将方法设为final的。
     7.final和private关键字,类中所有private方法都隐式的指定为final.由于无法取用private,所以也就无法覆盖它。可以对private方法添加final修饰词
补充:软件开发 , Java ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,