1、Java编程思想重点整理笔记分析目录1. Java中的多态性理解(注意与C+区分) 12. is-a关系和is-like-a关系 43. 运行时类型信息(RTTI + 反射) 44. 代理模式与Java中的动态代理 115. 即时编译器技术 JIT 146. 访问控制权限 157. 组合和继承之间的选择 168. final关键字 169. 策略设计模式与适配器模式的区别 1710. 内部类 1711. String类型 不可变 1812. 序列化控制 221. Java中的多态性理解(注意与C+区分) Java中除了static方法和final方法(private方法本质上属于final方法
2、,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意味着通常情况下,我们不必判定是否应该进行动态绑定它会自动发生。o final方法会使编译器生成更有效的代码,这也是为什么说声明为final方法能在一定程度上提高性能(效果不明显)。o 如果某个方法是静态的,它的行为就不具有多态性:o class StaticSuper o public static String staticGet() o return Base staticGet();o o o public String dynamicGet() o return Base dynamicGet();o o o o class
3、StaticSub extends StaticSuper o public static String staticGet() o return Derived staticGet();o o o public String dynamicGet() o return Derived dynamicGet();o o o o public class StaticPolymorphism o o public static void main(String args) o StaticSuper sup = new StaticSub();o System.out.println(sup.s
4、taticGet();o System.out.println(sup.dynamicGet();o o o 输出: Base staticGet()Derived dynamicGet() 构造函数并不具有多态性,它们实际上是static方法,只不过该static声明是隐式的。因此,构造函数不能够被override。 在父类构造函数内部调用具有多态行为的函数将导致无法预测的结果,因为此时子类对象还没初始化,此时调用子类方法不会得到我们想要的结果。 class Glyph void draw() System.out.println(Glyph.draw(); Glyph() System.o
5、ut.println(Glyph() before draw(); draw(); System.out.println(Glyph() after draw(); class RoundGlyph extends Glyph private int radius = 1; RoundGlyph(int r) radius = r; System.out.println(RoundGlyph.RoundGlyph(). radius = + radius); void draw() System.out.println(RoundGlyph.draw(). radius = + radius)
6、; public class PolyConstructors public static void main(String args) new RoundGlyph(5); 输出:Glyph() before draw()RoundGlyph.draw(). radius = 0Glyph() after draw()RoundGlyph.RoundGlyph(). radius = 5为什么会这样输出?这就要明确掌握Java中构造函数的调用顺序:(1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制0;(2)调用基类构造函数。从根开始递归下去,因为多态性此时调用子类覆盖后的dr
7、aw()方法(要在调用RoundGlyph构造函数之前调用),由于步骤1的缘故,我们此时会发现radius的值为0;(3)按声明顺序调用成员的初始化方法;(4)最后调用子类的构造函数。 只有非private方法才可以被覆盖,但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行,即覆盖private方法对子类来说是一个新的方法而非重载方法。因此,在子类中,新方法名最好不要与基类的private方法采取同一名字(虽然没关系,但容易误解,以为能够覆盖基类的private方法)。 Java类中属性域的访问操作都由编译器解析,因此不是多态的。父类和子类
8、的同名属性都会分配不同的存储空间,如下: / Direct field access is determined at compile time. class Super public int field = 0; public int getField() return field; class Sub extends Super public int field = 1; public int getField() return field; public int getSuperField() return super.field; public class FieldAccess pu
9、blic static void main(String args) Super sup = new Sub(); System.out.println(sup.filed = + sup.field + , sup.getField() = + sup.getField(); Sub sub = new Sub(); System.out.println(sub.filed = + sub.field + , sub.getField() = + sub.getField() + , sub.getSuperField() = + sub.getSuperField(); 输出:sup.fi
10、led = 0, sup.getField() = 1sub.filed = 1, sub.getField() = 1, sub.getSuperField() = 0Sub子类实际上包含了两个称为field的域,然而在引用Sub中的field时所产生的默认域并非Super版本的field域,因此为了得到Super.field,必须显式地指明super.field。2. is-a关系和is-like-a关系 is-a关系属于纯继承,即只有在基类中已经建立的方法才可以在子类中被覆盖,如下图所示:基类和子类有着完全相同的接口,这样向上转型时永远不需要知道正在处理的对象的确切类型,这通过多态来实现
11、。 is-like-a关系:子类扩展了基类接口。它有着相同的基本接口,但是他还具有由额外方法实现的其他特性。缺点就是子类中接口的扩展部分不能被基类访问,因此一旦向上转型,就不能调用那些新方法。3. 运行时类型信息(RTTI + 反射) 概念RTTI:运行时类型信息使得你可以在程序运行时发现和使用类型信息。 使用方式Java是如何让我们在运行时识别对象和类的信息的,主要有两种方式(还有辅助的第三种方式,见下描述):o 一种是“传统的”RTTI,它假定我们在编译时已经知道了所有的类型,比如Shape s = (Shape)s1;o 另一种是“反射”机制,它运行我们在运行时发现和使用类的信息,即使用
12、Class.forName()。o 其实还有第三种形式,就是关键字instanceof,它返回一个bool值,它保持了类型的概念,它指的是“你是这个类吗?或者你是这个类的派生类吗?”。而如果用=或equals比较实际的Class对象,就没有考虑继承它或者是这个确切的类型,或者不是。 工作原理要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息。Java送Class对象来执行其RTTI,使用类加载器的子系统实现。无论何时,只要你想在运行时使用类型信息,就必须首先获得对恰当的Class对象的引用,获取
13、方式有三种:(1)如果你没有持有该类型的对象,则Class.forName()就是实现此功能的便捷途,因为它不需要对象信息;(2)如果你已经拥有了一个感兴趣的类型的对象,那就可以通过调用getClass()方法来获取Class引用了,它将返回表示该对象的实际类型的Class引用。Class包含很有有用的方法,比如:package rtti;interface HasBatteriesinterface WaterProofinterface Shootsclass Toy Toy() Toy(int i) class FancyToy extends Toyimplements HasBatt
14、eries, WaterProof, Shoots FancyToy() super(1); public class RTTITest static void printInfo(Class cc) System.out.println(Class name: + cc.getName() + , is interface? + cc.isInterface() + ); System.out.println(Simple name: + cc.getSimpleName(); System.out.println(Canonical name: + cc.getCanonicalName(); public static void main(String args) Class c = null; try c = Class.forName(rtti.FancyToy); / 必须是全限定名(包名+类名)
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1