继承类中的成员隐藏
成员隐藏:
如果名字相同,不管各自的类型或访问控制符是否相同,隐藏技术就将应用,即基类的成员将被派生类的成员隐藏起来。《参考IVOR的JAVA2入门经典》p188
隐含的数据域部分可以通过对象的强制类型转换的技术来进行访问。当然前提是数据域的访问控制符为public 或package 访问 时。在类内可以采用this. 或 super. 来访问;在类外可以先生成对象,然后通过对象直接挂点访问。《参考坚果JAVA技术手册》P125、P126
子类对象可以通过强制类型转换转化为相应的超类类型然后赋值于超类类型变量,但它的本质还是属于产生它的子类的类型, 只不过暂时将焦点集中在它所加载的超类区域。因此可以通过它访问继承下来的隐藏成员,当然是超类区域中的非私有成员。例如:访问控制符为public 或package 访问标志的实例变量(但不包括实例方法)和静态成员(包括静态方法和静态变量)。实例方法的调用要考虑到多态,还是要显示出子类特征的。《参考坚果JAVA技术手册》P125、P126
如果希望防止子类覆盖超类中的方法,可以简单地在超类中将这个方法声明为final就可以了。注意:防止覆盖并不意味着子类不能继承这个方法,即子类中确实有这个方法且可以通过对象调用。《参考IVOR的JAVA2入门经典》p222
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
子类对象的实例化过程中相应构造函数的调用顺序
构造函数的名称必须与类名相同,且在构造函数的签名中不定义返回类型(返回值类型和void类型)。其原因在于构造函数的目的是为了生成一个对象,并且构造函数的名称(与class的名称相同)实际上指明了所诞生对象的类型。《参考坚果JAVA手册》p112
如果在一个类中没有封装构造函数,JAVA编译器在编译时将为它创建一个默认构造函数(即无参构造函数)。见测试案例
在封装类时,构造函数的第一个任务就是隐式地或显示地(通过super())调用其直接超类(或父类)的构造函数。如果代码中没有包含对超类构造函数的显示调用,那么JAVA编译器在编译时将为它插入一条super()语句即隐式地调用超类的默认构造函数(即无参构造函数)。见测试案例。如果超类不包括无参构造函数,那么这个隐式的调用就会导致编译错误。见测试案例。
如果创建的public类,不希望被公开的初始化,那么我们应该至少声明一个非public类型的构造函数来防止JAVA插入一个默认的public构造函数。例如:永远不需要初始化的类(例如java.lang.Math或java.lang.System)应该定义一个private的构造函数。这样的构造函数保证不会从类的外部调用该构造函数,而且也同时禁止了JAVA自动插入默认构造函数[注意:JAVA编译器自动加入的无参构造函数是公有的]。《参考坚果JAVA手册》P124
一般情况下,如果一个类没有定义无参构造函数,那么它的所有子类定义的构造函数必须明确地调用包括相应参数的超类的构造函数。案例参照 P335 10.7节工资系统 《参考坚果JAVA手册》p124
因此建议:在定义超类时,最好包含一个无参的构造函数。
当程序创建子类的对象时,子类的构造函数将立即调用超类的构造函数(通过super显示调用或隐式调用),其目的是超类的构造函数初始化超类的实例变量,而子类的构造函数初始化子类的实例变量。注意:调用构造函数并不总是意味着生成对象,比如抽象类的构造函数被子类通过super语句调用并不能生成抽象类对象,但是它能够初始化抽象类的数据域(此点已测试)。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
JAVA的垃圾回收机制
由于每个对象都继承于Object类,因此每个对象都有finalize方法。JAVA保证在垃圾收集器收回这些对象所占用的空间之前,为每个对象调用finalize方法。注意:因为对象要回收,Java虚拟机才自动调用finalize方法,即给对象临终前说几句话的机会。而不是通过执行finalize方法来回收对象,所以没有必要显示调用finalize方法。因此《HowToJAVA》P315说明:通常不鼓励使用finalize方法。
将引用型变量设置为null, 表明该变量不再引用对象。这相当于将对象标记为垃圾收集。
通过使用System类的静态方法gc,显示地调用垃圾收集程序,只表示垃圾收集程序应立即尽量收回满足垃圾收集条件的对象。但是即使调用System.gc(),也不能保证垃圾收集程序立即执行,而且不能保证按特定的次序收集对象。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
接口:
接口不是类,与顶级超类Object无任何继承关系。但是我们可以理解为:接口相当于抽象的超类,常常位于继承层次中的顶端位置。
尽管一个JAVA类只能继承一个超类,但是它可以实现(implements)任意多个接口,借以达到C++中的多重继承的目的。《参考坚果JAVA手册》p139
创建接口就是定义一个新的引用类型,因此可以用接口声明变量。《参考坚果JAVA手册》p139
当一个类实现了某个接口,继承当中的“is-a”关系同样适用于类与接口。例如,Point类实现Shape接口。因此,Point是Shape。《参照HowToJava》P345
接口中的所有方法都默认是public的,即使它们省略了public。所以在接口中定义protected 或private类型的方法都是错误的。《参考坚果JAVA手册》p139
接口中只能包含方法的声明,不能有方法的实现,因此所有的方法本质上是抽象的,但不包含abstract标识。《参考坚果JAVA手册》p139
在接口中没有实例变量,因此在接口中不定义构造函数。也就是说接口中没有构造函数。在匿名类中以new 接口名称(){……}的形式出现, 只是说明了匿名类把所实现的接口当作自己的父类,目的是为了以调用父类无参构造函数的形式来声明自己的类型,这只是一种借用,并不意味着接口就有构造函数。《参考坚果JAVA手册》p139
接口中的方法一般被子类全部实现或者是部分实现,部分实现的子类要声明为抽象类。
接口应用的两种形式:
在继承层次中相当于超类使用。经典案例参照《HowToJava》p342 10.8案例
在接口中可以定义 static final 常量,供实现此接口的类直接使用。类甚至可以通过引入该接口,然后以接口名.常量名的形式来使用。说明参照《HowToJAVA》P345;案例参照P450例SwingConstants.CENTER P706、P707
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
抽象类:
包含抽象方法的类必然是抽象类。抽象类可以有构造函数,并且其构造函数可能通过子类使用super()方法加以调用。注意:在抽象类中可以包含构造函数,但它的构造函数只起到初始化抽象类的数据域,而不能产生本类对象。即不能用new 抽象类名()的形式来生成对象。也就是说抽象类是不能实例化的。《参考坚果JAVA手册》p137
即使类中实际上不包括任何抽象方法,我们也可以把它声明为抽象的。把这样的类声明为抽象类型意味着该类的实现是不完整的,而且它会作为一个或多个子类的超类,子类中会分别完成其超类不完整的实现。这种抽象类也是不能实例化的。比如Component是一个抽象class。《参考坚果JAVA手册》p137
抽象类一般作为超类使用。
抽象类的案例:《HowToJava》p328 10.5案例
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
匿名类:
1.匿名类的定义形式为:在定义匿名类主体时,同时创建匿名类对象,即类的封装与对象的创建合二为一。有两种情况:      
(1) new 父类构造函数{……}
                        (2) new 接口名称(){…… }
注意:理解匿名类定义形式的前提是要明确:构造函数的名称(与class的名称相同)实际上指明了所诞生对象的类型。
由于匿名类没有名称,匿名类对象只能借助于父类的构造函数来声明自己的类型(或者把匿名类所实现的接口当作自己的父类,然后以调用父类无参构造函数的形式来声明自己的类型)。构造函数的后面紧跟一对大括号,是匿名类的主体定义。匿名类定义的详细内容包括数据成员和成员方法,它们都可以定义在大括号里面。当然成员方法不包括构造函数,因为匿名类没有名称。2.匿名类的使用特点:
匿名类没有名称,常常借助于父类或接口来表明自己的对象类型。
匿名类总是有父类的,因为任何class至少继承于Object。
匿名类是内部类,常常用于成员方法或者是主方法中。
匿名类对象常常作为GUI组件(JFrame、JTextField、JButton等)的侦听器对象。
《HowToJava》p351 程序10.23说明了匿名类定义的两种形式。注意:借用了接口ActionListener、和使用了父类WindowAdapter。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
JAVA虚拟机:
注意:Java编译器和Java虚拟机(或叫JAVA解释器)是两个概念《参考坚果JAVA手册》P14:
Java编译器将Java源程序翻译为JVM可执行代码——字节码
JAVA虚拟机是java程序的执行环境,甚至可以理解为一个运行程序的容器。
虚拟机的解释执行过程分为三步:类加载(通过大名鼎鼎的类加载器class loader)、代码校验(校验器可发现操作数栈溢出,非法数据类型转化等多种错误)、代码执行。
Java字节码的执行有两种方式:(1)即时编译方式(JIT):解释器先将字节码编译成机器码,然后再执行该机器码。(2).边解释便执行方式:解释器通过每次解释并执行一小段代码来完成Java字节码程序的所有操作。通常采用的是第二种方法。由于JVM规格描述具有足够的灵活性,这使得将字节码翻译为机器代码的工作具有较高的效率。对于那些对运行速度要求较高的应用程序,解释器可将Java字节码即时编译为机器码,从而很好地保证了Java代码的可移植性和高性能。
因此,像类加载、类型转换校验、溢出检查、垃圾收集、JIT等重要操作都是在JAVA虚拟机中完成的。