Java编程思想重点整理笔记_第1页
Java编程思想重点整理笔记_第2页
Java编程思想重点整理笔记_第3页
Java编程思想重点整理笔记_第4页
Java编程思想重点整理笔记_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、目录1. Java中的多态性理解(注意与C+区分)12. is-a关系和is-like-a关系43. 运行时类型信息(RTTI + 反射)44. 代理模式与Java中的动态代理115. 即时编译器技术 JIT146. 访问控制权限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 oo public String dynamicGet() o return Base dynamicGet();o o oo class StaticSub extends StaticSuper

3、o public static String staticGet() o return Derived staticGet();o oo public String dynamicGet() o return Derived dynamicGet();o o oo public class StaticPolymorphism oo public static void main(String args) o StaticSuper sup = new StaticSub();o System.out.println(sup.staticGet();o System.out.println(s

4、up.dynamicGet();o oo 输出: Base staticGet()Derived dynamicGet() 构造函数并不具有多态性,它们实际上是static方法,只不过该static声明是隐式的。因此,构造函数不能够被override。 在父类构造函数内部调用具有多态行为的函数将导致无法预测的结果,因为此时子类对象还没初始化,此时调用子类方法不会得到我们想要的结果。 class Glyph void draw() System.out.println(Glyph.draw(); Glyph() System.out.println(Glyph() before draw();

5、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); public class PolyConstructors pu

6、blic 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)调用基类构造函数。从根开始递归下去,因为多态性此时调用子类覆盖后的draw()方法(要在调用RoundGlyph构造函数之前调用),由于步

7、骤1的缘故,我们此时会发现radius的值为0;(3)按声明顺序调用成员的初始化方法;(4)最后调用子类的构造函数。 只有非private方法才可以被覆盖,但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行,即覆盖private方法对子类来说是一个新的方法而非重载方法。因此,在子类中,新方法名最好不要与基类的private方法采取同一名字(虽然没关系,但容易误解,以为能够覆盖基类的private方法)。 Java类中属性域的访问操作都由编译器解析,因此不是多态的。父类和子类的同名属性都会分配不同的存储空间,如下: / Direct fiel

8、d 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 public static void main(String args)

9、 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.filed = 0, sup.getField() = 1sub.fil

10、ed = 1, sub.getField() = 1, sub.getSuperField() = 0Sub子类实际上包含了两个称为field的域,然而在引用Sub中的field时所产生的默认域并非Super版本的field域,因此为了得到Super.field,必须显式地指明super.field。2. is-a关系和is-like-a关系 is-a关系属于纯继承,即只有在基类中已经建立的方法才可以在子类中被覆盖,如下图所示:基类和子类有着完全相同的接口,这样向上转型时永远不需要知道正在处理的对象的确切类型,这通过多态来实现。 is-like-a关系:子类扩展了基类接口。它有着相同的基本接口

11、,但是他还具有由额外方法实现的其他特性。缺点就是子类中接口的扩展部分不能被基类访问,因此一旦向上转型,就不能调用那些新方法。3. 运行时类型信息(RTTI + 反射) 概念RTTI:运行时类型信息使得你可以在程序运行时发现和使用类型信息。 使用方式Java是如何让我们在运行时识别对象和类的信息的,主要有两种方式(还有辅助的第三种方式,见下描述):o 一种是“传统的”RTTI,它假定我们在编译时已经知道了所有的类型,比如Shape s = (Shape)s1;o 另一种是“反射”机制,它运行我们在运行时发现和使用类的信息,即使用Class.forName()。o 其实还有第三种形式,就是关键字i

12、nstanceof,它返回一个bool值,它保持了类型的概念,它指的是“你是这个类吗?或者你是这个类的派生类吗?”。而如果用=或equals比较实际的Class对象,就没有考虑继承它或者是这个确切的类型,或者不是。 工作原理要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息。Java送Class对象来执行其RTTI,使用类加载器的子系统实现。无论何时,只要你想在运行时使用类型信息,就必须首先获得对恰当的Class对象的引用,获取方式有三种:(1)如果你没有持有该类型的对象,则Class.forN

13、ame()就是实现此功能的便捷途,因为它不需要对象信息;(2)如果你已经拥有了一个感兴趣的类型的对象,那就可以通过调用getClass()方法来获取Class引用了,它将返回表示该对象的实际类型的Class引用。Class包含很有有用的方法,比如:package rtti;interface HasBatteriesinterface WaterProofinterface Shootsclass Toy Toy() Toy(int i) class FancyToy extends Toyimplements HasBatteries, WaterProof, Shoots FancyToy

14、() 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

15、args) Class c = null; try c = Class.forName(rtti.FancyToy); / 必须是全限定名(包名+类名) catch(ClassNotFoundException e) System.out.println(Cant find FancyToy); System.exit(1); printInfo(c); for(Class face : c.getInterfaces() printInfo(face); Class up = c.getSuperclass(); Object obj = null; try / Requires defau

16、lt constructor. obj = up.newInstance(); catch (InstantiationException e) System.out.println(Cant Instantiate); System.exit(1); catch (IllegalAccessException e) System.out.println(Cant access); System.exit(1); printInfo(obj.getClass(); 输出:Class name: rtti.FancyToy, is interface? falseSimple name: Fan

17、cyToyCanonical name: rtti.FancyToyClass name: rtti.HasBatteries, is interface? trueSimple name: HasBatteriesCanonical name: rtti.HasBatteriesClass name: rtti.WaterProof, is interface? trueSimple name: WaterProofCanonical name: rtti.WaterProofClass name: rtti.Shoots, is interface? trueSimple name: Sh

18、ootsCanonical name: rtti.ShootsClass name: rtti.Toy, is interface? falseSimple name: ToyCanonical name: rtti.Toy(3)Java还提供了另一种方法来生成对Class对象的引用,即使用类字面常量。比如上面的就像这样:FancyToy.class;来引用。这样做不仅更简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中),并且它根除了对forName方法的引用,所以也更高效。类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型。注意:当使用“.cl

19、ass”来创建对Class对象的引用时,不会自动地初始化该Class对象,初始化被延迟到了对静态方法(构造器隐式的是静态的)或者非final静态域(注意final静态域不会触发初始化操作)进行首次引用时才执行:。而使用Class.forName时会自动的初始化。为了使用类而做的准备工作实际包含三个步骤:- 加载:由类加载器执行。查找字节码,并从这些字节码中创建一个Class对象- 链接:验证类中的字节码,为静态域分配存储空间,并且如果必需的话,将解析这个类创建的对其他类的所有引用。- 初始化:如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。这一点非常重要,下面通过一个实例来说明

20、这两者的区别:package rtti;import java.util.Random;class Initable static final int staticFinal = 47; static final int staticFinal2 = ClassInitialization.rand.nextInt(1000); static System.out.println(Initializing Initable); class Initable2 static int staticNonFinal = 147; static System.out.println(Initializ

21、ing Initable2); class Initable3 static int staticNonFinal = 74; static System.out.println(Initializing Initable3); public class ClassInitialization public static Random rand = new Random(47); public static void main(String args) / Does not trigger initialization Class initable = Initable.class; Syst

22、em.out.println(After creating Initable ref); / Does not trigger initialization System.out.println(Initable.staticFinal); / Does trigger initialization(rand() is static method) System.out.println(Initable.staticFinal2); / Does trigger initialization(not final) System.out.println(Initable2.staticNonFi

23、nal); try Class initable3 = Class.forName(rtti.Initable3); catch (ClassNotFoundException e) System.out.println(Cant find Initable3); System.exit(1); System.out.println(After creating Initable3 ref); System.out.println(Initable3.staticNonFinal); 输出:After creating Initable ref47Initializing Initable25

24、8Initializing Initable2147Initializing Initable3After creating Initable3 ref74 RTTI的限制?如何突破? 反射机制如果不知道某个对象的确切类型,RTTI可以告诉你,但是有一个限制:这个类型在编译时必须已知,这样才能使用RTTI识别它,也就是在编译时,编译器必须知道所有要通过RTTI来处理的类。可以突破这个限制吗?是的,突破它的就是反射机制。Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含了Field、Method以及Constructor类(每个类都实现了Member接口)

25、。这些类型的对象是由JVM在运行时创建的,用以表示未知类里对应的成员。这样你就可以使用Constructor创建新的对象,用get()/set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields()、getMethods()和getConstructors()等很便利的方法,以返回表示字段、方法以及构造器的对象的数组。这样,匿名对象的类信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。#反射与RTTI的区别当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类(就

26、像RTTI那样),在用它做其他事情之前必须先加载那个类的Class对象,因此,那个类的.class文件对于JVM来说必须是可获取的:要么在本地机器上,要么可以通过网络取得。所以RTTI与反射之间真正的区别只在于:对RTTI来说,编译器在编译时打开和检查.class文件(也就是可以用普通方法调用对象的所有方法);而对于反射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件。下面的例子是用反射机制打印出一个类的所有方法(包括在基类中定义的方法):package typeinfo;import java.lang.reflect.Constructor;impo

27、rt java.lang.reflect.Method;import java.util.regex.Pattern;/ Using reflection to show all the methods of a class./ even if the methods are defined in the base class.public class ShowMethods private static String usage = usage: n + ShowMethods n + To show all methods in class or:

28、n + ShowMethods wordn + To search for methods involving word; private static Pattern p = Ppile(w+.); public static void main(String args) if(args.length 1) System.out.println(usage); System.exit(0); int lines = 0; try Class c = Class.forName(args0); Method methods = c.getMethods

29、(); Constructor ctors = c.getConstructors(); if(args.length = 1) for(Method method : methods) System.out.println(p.matcher(method.toString().replaceAll(); for(Constructor ctor : ctors) System.out.println(p.matcher(ctor.toString().replaceAll(); lines = methods.length + ctors.length; else for(Method m

30、ethod : methods) if(method.toString().indexOf(args1) != -1) System.out.println(p.matcher(method.toString().replaceAll(); lines+; for(Constructor ctor : ctors) if(ctor.toString().indexOf(args1) != -1) System.out.println(p.matcher(ctor.toString().replaceAll(); lines+; catch (ClassNotFoundException e)

31、System.out.println(No such Class: + e); 输出:public static void main(String)public final native void wait(long) throws InterruptedExceptionpublic final void wait() throws InterruptedExceptionpublic final void wait(long,int) throws InterruptedExceptionpublic boolean equals(Object)public String toString

32、()public native int hashCode()public final native Class getClass()public final native void notify()public final native void notifyAll()public ShowMethods()4. 代理模式与Java中的动态代理 代理模式在任何时刻,只要你想要将额外的操作从“实际”对象中分离到不同的地方,特别是当你希望能够很容易地做出修改,从没有使用额外操作转为使用这些操作,或者 反过来时,代理就显得很有用(设计模式的关键是封装修改)。例如,如果你希望跟踪对某个类中方法的调用,

33、或者希望度量这些调用的开销,那么你应该怎样做 呢?这些代码肯定是你不希望将其合并到应用中的代码,因此代理使得你可以很容易地添加或移除它们。 interface Interface void doSomething(); void somethingElse(String arg); class RealObject implements Interface Override public void doSomething() System.out.println(doSomething.); Override public void somethingElse(String arg) Syst

34、em.out.println(somethingElse + arg); class SimpleProxy implements Interface private Interface proxy; public SimpleProxy(Interface proxy) xy = proxy; Override public void doSomething() System.out.println(SimpleProxy doSomething.); proxy.doSomething(); Override public void somethingElse(String

35、 arg) System.out.println(SimpleProxy somethingElse + arg); proxy.somethingElse(arg); public class SimpleProxyDemo public static void consumer(Interface iface) iface.doSomething(); iface.somethingElse(bonobo); public static void main(String args) consumer(new RealObject(); consumer(new SimpleProxy(ne

36、w RealObject(); 输出:doSomething.somethingElse bonoboSimpleProxy doSomething.doSomething.SimpleProxy somethingElse bonobosomethingElse bonobo 动态代理Java的动态代理比代理的思想更向前迈进了一步,因为它可以动态地创建代理并动态地处理对所代理方法的调用。 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.P

37、roxy; class DynamicProxyHandler implements InvocationHandler private Object proxy; public DynamicProxyHandler(Object proxy) xy = proxy; Override public Object invoke(Object proxy, Method method, Object args) throws Throwable System.out.println(* proxy: + proxy.getClass() + . method: + method

38、 + . args: + args); if(args != null) for(Object arg : args) System.out.println( + arg); return method.invoke(xy, args); public class SimpleDynamicProxy public static void consumer(Interface iface) iface.doSomething(); iface.somethingElse(bonobo); public static void main(String args) RealObje

39、ct real = new RealObject(); consumer(real); / insert a proxy and call again: Interface proxy = (Interface)Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class Interface.class , new DynamicProxyHandler(real); consumer(proxy); 输出:doSomething.somethingElse bonobo* proxy: class typeinfo.$

40、Proxy0. method: public abstract void typeinfo.Interface.doSomething(). args: nulldoSomething.* proxy: class typeinfo.$Proxy0. method: public abstract void typeinfo.Interface.somethingElse(java.lang.String). args: Ljava.lang.Object;6a8814e9bonobosomethingElse bonobo5. 即时编译器技术 JITJava虚拟机中有许多附加技术用以提升速度

41、,尤其是与加载器操作相关的,被称为“即时”(Just-In-Time,JIT)编译器的技术。这种技术可以把程序全部或部分翻译成本地机器码(这本来是JVM的工作),程序运行速度因此得以提升。当需要装载某个类时,编译器会先找到其.class文件,然后将该类的字节码装入内存。此时,有两种方案可供选择:(1)一种就是让即时编译器编译所有代码。但这种做法有两个缺陷:这种加载动作散落在整个程序生命周期内,累加起来要花更多时间;并且会增加可执行代码的长度(字节码要比即时编译器展开后的本地机器码小很多),这将导致页面调度,从而降低程序速度。(2)另一种做法称为惰性评估(lazy evaluation),意思是

42、即时编译器只在必要的时候才编译代码,这样,从不会被执行的代码也许就压根不会被JIT所编译。新版JDK中的Java HotSpot技术就采用了类似方法,代码每次被执行的时候都会做一些优化,所以执行的次数越多,它的速度就越快。6. 访问控制权限 Java访问权限修饰词:public、protected、包访问权限(默认访问权限,有时也称friendly)和private。 包访问权限:当前包中的所有其他类对那个成员具有访问权限,但对于这个包之外的所有类,这个成员却是private。 protected:继承访问权限。有时基类的创建者会希望有某个特定成员,把对它的访问权限赋予派生类而不是所有类。这就

43、需要 protected来完成这一工作。protected也提供包访问权限,也就是说,相同包内的其他类都可以访问protected元素。 protected指明“就类用户而言,这是private的,但对于任何继承于此类的导出类或其他任何位于同一个包内的类来说,它却是可以访问的”。比如:基类: package access.cookie; public class Cookie public Cookie() System.out.println(Cookie Constructor); void bite() / 包访问权限,其它包即使是子类也不能访问它 System.out.println(

44、bite); 子类:package access.dessert;import access.cookie.Cookie;public class ChocolateChip extends Cookie public ChocolateChip() System.out.println(ChocolateChip constructor); public void chomp() bite(); / error, the method bite() from the type Cookie is not visible 可以发现子类并不能访问基类的包访问权限方法。此时可以将Cookie中的b

45、ite指定为public,但这样做所有的人就都有了访问权限,为了只允许子类访问,可以将bite指定为protected即可。7. 组合和继承之间的选择 组合和继承都允许在新的类中放置子对象,组合是显式的这样做,而继承则是隐式的做。 组合技术通常用于想在新类中使用现有类的功能而非它的接口这种情形。即在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口,而非所嵌入对象的接口。为取得此效果,需要在新类中嵌入一个现有类的private对象。但有时,允许类的用户直接访问新类中的组合成分是极具意义的,即将成员对象声明为public。如果成员对象自身都隐藏了具体实现,那么这种

46、做法是安全的。当用户能够了解到你正在组装一组部件时,会使得端口更加易于理解。比如Car对象可由public的Engine对象、Wheel对象、Window对象和Door对象组合。但务必要记得这仅仅是一个特例,一般情况下应该使域成为private。 在继承的时候,使用某个现有类,并开发一个它的特殊版本。通常,这意味着你在使用一个通用类,并为了某种特殊需要而将其特殊化。稍微思考一下就会发现,用一个“交通工具”对象来构成一部“车子”是毫无意义的,因为“车子”并不包含“交通工具”,它仅是一种交通工具(is-a关系)。 “is-a”(是一个)的关系是用继承来表达的,而“has-a”(有一个)的关系则是用

47、组合来表达的。 到底是该用组合还是继承,一个最清晰的判断方法就是问一问自己是否需要从新类向基类进行向上转型,需要的话就用继承,不需要的话就用组合方式。8. final关键字 对final关键字的误解当final修饰的是基本数据类型时,它指的是数值恒定不变(就是编译期常量,如果是static final修饰,则强调只有一份),而对对象引用而不是基本类型运用final时,其含义会有一点令人迷惑,因为用于对象引用时,final使引用恒定不变,一旦引用被初始化指向一个对象,就无法再把它指向另一个对象。然而,对象其自身却是可以被修改的,Java并未提供使任何对象恒定不变的途径(但可以自己编写类以取得使对象恒定不变的效果),这一限制同样适用数组,它也是对象。 使用final方法真的可以提高程序效率吗?将一个方法设成final后,编译器就可以把对那个方法的所有调用都置入“嵌入”调用里。只要编译器发现一个final方法调用,就会(根据它自己的判 断)忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。相 反,它会用方法主体内实际代码的一个副本来替换方法调用。这样做可避免方法调用时的系统开销。当然,若方法体积太大,那么程序也会变得雍肿,可能受到到不到嵌入代码所带来的任何性能提升。因为任何提

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论