重读《Java编程思想(第四版)》(10-17章、18章部分)

来源:互联网 发布:java课程设计 清华大学 编辑:程序博客网 时间:2024/06/14 02:20

  这些章节读起来就比前面的章节吃力多了,应该不能算重读了(因为大二第一遍读的时候这些章节就只是草草看的),但记录一下关键点。有些地方的记录可能过于粗略,因为只可意会,不可言传。
  1.使用“类名.this”来产生对外部类对象的引用。使用“外部类对象.new”来创建内部类,注意是对象而不是类名。
  2.protect具有包访问权限。
  3.(P194)内部类实现向上转型,可以限制客户端程序员了解或访问内部类成员。也不能实现向下转型。通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏实现的细节。
  4.(P197)匿名内部类是一个导出类,实现接口或父类,通过new表达式返回的引用被自动向上转型为对接口或父类的引用
  5.(P198)如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final。
  6.嵌套类:将内部类声明为static。意味着:1)要创建嵌套类的对象,并不需要其外围类的对象。2)不能从嵌套类的对象中访问非静态的外围类对象。
  7.嵌套类可以用于测试。(P203)
  8.内部类解决多重继承(P204-P205代码)
  9.http://www.importnew.com/19301.html这篇文章讲了回调机制(P205-207)讲了闭包和回调(有疑问)。
  10.内部类与控制框架(P207-P212)对于命令设计模式的理解。
  11.局部内部类不能有访问说明符,因为它不是外围类的一部分;但是它可以访问当前代码块的常量,以及此外围类的所有成员。
  12.Java Collection的总结。http://skyuck.iteye.com/blog/526358。从大的方面分容器可以分为Collection和Map。Collection又可分为List,Set。
  13.ListIterator可以双向移动。
  14.stack和queue都可以用LinkedList实现。stack可以用LinkedList实现“代理”(P230)。而queue LinkedList已经实现其接口,可以直接向上转型Queue queue=new LinkedList()。
  15.PriorityQueue是一个实现的类。也可以通过提供自己的comparator来修改这个顺序。
  16.Collection和Iterator。(P238-P241)利用Collection接口(可以继承AbstractCollection)和Iterator将display()方法与底层容器的特定实现解耦。
  17.foreach语法主要用于数组,但是它也可以应用于Collection对象。而之所以可以工作是因为实现了Iterable接口。该接口包含一个能够产生Iterator的iterator()方法。iterator()方法返回的是实现了Iterator<T>的匿名内部类的实例。
  18.Arrays.asList会直接操纵底层数组,也就是说返回的是视图而不是副本。
  19.容器分类图(P246)。
  20.所有的标准异常类都有两个构造器:一个默认构造器;另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器。
  21.(P257)例子显示如何使用Exception类型的方法。
  22.如果只是把当前异常对象重新抛出,那么printStackTrace()方法显示的将是原来异常抛出点的栈调用信息。因此如果“不知道该如何处,理这个异常信息,但是也不想把它吞了,或者打印一些无用的消息”,可以直接把“被检查的异常”包装进RuntimeException里面,变成不被检查的异常(P280)。
  23.异常的分类(P263-264)
  24.http://www.cnblogs.com/dolphin0520/p/3769804.html一篇很好的博文关于异常的总结。
  (转载)
  在Java中异常被当做对象来处理,根类是java.lang.Throwable类,在Java中定义了很多异常类(如OutOfMemoryError、NullPointerException、IndexOutOfBoundsException等),这些异常类分为两大类:Error和Exception。
  Error是无法处理的异常,比如OutOfMemoryError,一般发生这种异常,JVM会选择终止程序。因此我们编写程序时不需要关心这类异常。
  Exception,也就是我们经常见到的一些异常情况,比如NullPointerException、IndexOutOfBoundsException,这些异常是我们可以处理的异常。
  Exception类的异常包括checked exception和unchecked exception(unchecked exception也称运行时异RuntimeException,当然这里的运行时异常并不是前面我所说的运行期间的异常,只是Java中用运行时异常这个术语来表示,Exception类的异常都是在运行期间发生的)。
  unchecked exception(非检查异常),也称运行时异常(RuntimeException),比如常见的NullPointerException、IndexOutOfBoundsException。对于运行时异常,java编译器不要求必须进行异常捕获处理或者抛出声明,由程序员自行决定。
  checked exception(检查异常),也称非运行时异常(运行时异常以外的异常就是非运行时异常),java编译器强制程序员必须进行捕获处理,比如常见的IOExeption和SQLException。对于非运行时异常如果不进行捕获或者抛出声明处理,编译都不会通过。
在Java中,所有异常类的父类是Throwable类,Error类是error类型异常的父类,Exception类是exception类型异常的父类,RuntimeException类是所有运行时异常的父类,RuntimeException以外的并且继承Exception的类是非运行时异常。
  典型的RuntimeException包括NullPointerException、IndexOutOfBoundsException、IllegalArgumentException等。
  典型的非RuntimeException包括IOException、SQLException等。
  25.当要把除内存之外的资源恢复到它们的初始状态时,就要用到finally子句。这钟需要清理的资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至可以是外部世界的某个开关。(P265-P266)
  26.(异常的限制)子类重写父类方法的时候,如何确定异常抛出声明的类型。下面是三点原则:1)父类的方法没有声明异常,子类在重写该方法的时候不能声明异常;2)如果父类的方法声明一个异常exception1,则子类在重写该方法的时候声明的异常不能是exception1的父类;3)如果父类的方法声明的异常类型只有非运行时异常(运行时异常),则子类在重写该方法的时候声明的异常也只能有非运行时异常(运行时异常),不能含有运行时异常(非运行时异常)。换句话说,在继承和覆盖的过程中,某个特定方法的“异常说明的接口”不是变大了而是变小了——这恰好和类接口在继承时的情形相反。
  27.构造器的异常(P273),基本规则是:在创建需要清理的对象之后,立即进入一个try-finally语句块。
  28.关于异常的一些建议(转载):1)切忌使用空catch块。2)注意catch块的顺序,不要把基类的异常放在最前面的catch块。3) 异常处理尽量放在高层进行。尽量将异常统一抛给上层调用者,由上层调用者统一之时如何进行处理。如果在每个出现异常的地方都直接进行处理,会导致程序异常处理流程混乱,不利于后期维护和异常错误排查。由上层统一进行处理会使得整个程序的流程清晰易懂。4)在finally中释放资源,如果有使用文件读取、网络操作以及数据库操作等,记得在finally中释放资源。这样不仅会使得程序占用更少的资源,也会避免不必要的由于资源未释放而发生的异常情况。。
  29.String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。而最初的String对象丝毫未动。
  30.当你为一个类编写toString()方法时,如果字符串操作比较简单,那就可以信赖编译器,它会为你合理地构造最终的字符串结果。但是,如果你要在toString( )方法中使用循环,那么最好自己创建一个StringBuilder对象,用它来构造最终的结果。
  31.字符串连接时若有对象会自动调用toString()方法。因此如果在类中的toString方法中字符串连接this就会产生递归,正确的方法应该调用super.toString()方法。
  32.格式化输出用System.out.format()方法,用法与C++中的printf一样。或者Formatter类。
  33.正则表达式(P298-P299)
  34.Pattern和Matcher. Pattern p = Pattern.compile(arg); Matcher m=p.matcher(args[0]);
  35.RTTI:RTTI(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。Java使用Class对象来执行其RTTI。每一个类都有一个Class对象。
  36.static final为编译时常量,不需要初始化就可以读取。当使用“XXX.class”(类字面常量)来创建对象的引用时,不会自动地初始化该Class对象,但是为了产生Class引用,Class.forName()立即就进行了初始化。
  37.通过使用泛型语法,可以让编译器强制执行额外的类型检查,但这不是向上转型。(P320)
  38.ClassCastException(类型转换异常)。
  39.RTTI在Java中的第三种形式,instanceof。“对象 instanceof 类名”。它返回一个布尔值,告诉我们是不是某个特定类型的实例。“类字面值常量.isInstance(对象)”也可以判断是不是某个特定类型的实例。
  40.instanceof保持了类型的概念,它指的是“你是这个类吗,或者你是这个类的派生类吗?”而如果用==比较实际的Class对象,就没有考虑继承——它或者是这个确切的类型或者不是。
  41.如果不知道某个对象的确切类型,RTTI可以告诉你。但是有一个限制:这个类型在编译的时必须已知,这样RTTI才能识别它,并利用这些信息做一些有用的事。换句话说,在编译时,编译器必须知道所有要通过RTTI处理过的类。而反射则不用,当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类。在用它做其他事情之前必须先加载哪个类的Class对象。所以RTTI和反射之间真正的区别在于,对RTTI来说,编译器在编译时打开和检查.class文件。(换句话说,我们可以用“普通”方式调用对象的所有方法)而对于反射机制来说,.class文件在编译时是不可以获取的,所以是在运行时打开和检查.class文件。反射的例子(P336)
  42.代理设计模式与Java的动态代理(P337-P340)
  43.利用反射可以访问私有成员或函数。
  44.泛型接口。例如生成器(generator)(P358-P360)public interface Generator<T>{T next();}
  45.泛型方法:是否拥有泛型方法,与其所在的类是否是泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值之前。 如<T> void f(T x){},使用泛型方法时,通常不必指明参数类型,因为编译会为我们找出具体的类型。这称为类型参数推断。
  46.Java泛型:Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。
  47.擦除代价是显著的。泛型不能用于显式地引用运行时类型的操作之中,例如转型,instanceof操作和new表达式(一部分原因是因为编译器不能验证T,可以用工厂模式解决)。数组必须具有具体的类型信息,所以也不创建数组(成功创建数组的唯一方式是array = (T[])new Object[sz],需要@SuppressWarnings来抑制警告,最好的方法还是用ArrayList的吧)。
  48.因为擦除移除了类型信息,所以,可以用无界泛型参数调用的方法只是那些可以用Object调用的方法。使用边界<T extends HasF>可以在类型子集来调用方法。
  49.(P390-P395)Java泛型通配符extends与super http://sharewind.iteye.com/blog/1622164 1)List<? extends Frut> 表示 “具有任何从Fruit继承类型的列表”,编译器无法确定List所持有的类型,所以无法安全的向其中添加对象。可以接受现有的子类型List<Apple> 赋值,由于,其中放置是从Fruit中继承的类型,所以可以安全地取出Fruit类型。2)List<? super Fruit> 表示“具有任何Fruit超类型的列表”,列表的类型至少是一个 Fruit 类型,因此可以安全的向其中添加Fruit 及其子类型。由于List<? super Fruit>中的类型可能是任何Fruit 的超类型,无法赋值为Fruit的子类型Apple的List<Apple>.总结:extends 可用于的返回类型限定,不能用于参数类型限定。super 可用于参数类型限定,不能用于返回类型限定。
  50.为什么用边界而不用T?为了向上转型?
  51.不能实例化具有参数化类型的数组,List<Banana>[] peels = new List<Banana>[10]; 擦除会移除参数类型信息,而数组必须知道它们所持有的确切类型,以强制类型安全。编译器确实不让实例化泛型数组,但是它允许你创建对这种数组的引用List<String>[] ls因此你可以创建非泛型数组,然后将其转型:List<Banana>[] peels = (Banana [])new List[10]
  52.System.arraycopy()用于数组的拷贝。
  53.数组的比较和排序(Arrays.sort()):实现java.lang.Comparable接口,此接口很简单,只有compareTo()一个方法。此方法接受另一个Object为参数,如果当前对象小于参数则返回负值,如果相等则返回零,如果大于则返回正值。
  54.加入有人给你一个并没有实现Compareble的类,或者给你的类实现了Compareble。但是你并不喜欢它的实现方式,你需要另一种不同的比较方法。要解决这个问题,可以创建一个实现了Comparable接口的单独的类,然后实现其中的compare()方法(P453-P454)。
  54.使用Arrays.binarySearch()之前数组必须已经排序好了。
  55.在Java容器类库的简化图中,可以看到大量的类的名字都是以Abstract开头的,它们只是部分实现了特定接口的工具。例如,如果你在创建自己的set,那么并不用从set接口开始并实现其中的全部方法,只需从AbstractSet继承,然后执行一些创建新类必须的工作。
  56.(P464-P468)享元设计模式和对Map的理解。Map.Entry是Map声明中的一个内部接口,它表示Map中的一个实体(一个key-value对),接口中有getKey()和getValue()方法、
  57.Class<T>Class也具有泛型。注意Class是Java中一个特殊的类。
  58.对于良好的编程风格而言,你应该在覆盖equals()方法时,总是同时覆盖hashCode()的方法。
  59.Java标准类库中没有任何显式的用于双向队列的接口,但双向队列介意用LinkedList实现。
  60.默认的Object.equals()只是比较对象的地址,而String对象equals已经经过覆写,比较的是内容。
  61.对于自己编写的对象,如果不为你的键覆盖hashCode()和equals(),那么使用散列的数据结构(HashSet, HashMap, LinkedHashSet等)就无法正确处理你的键。
  62.对于hashCode计算的技巧(P496)。
  (下面是Java I/O部分,但不包括18.9进程控制,18.10新I/O,18.11压缩)
  63.File(文件)类这个名字具有一定的误导性,它既能代表一个特定文件的名称,又能代表一个目录下的一组文件的名称。(String[] list=new File(".").list()list中存储的当前目录下文件(目录)的名称数组,但这个不包括子目录下的文件,如果要显示所有文件则需要使用递归(P528-P531目录实用工具展示了如何做))。
  64.(P526-P527)三个程序展示了如何简化匿名内部类使用方法的过程(也说明了内部类如何做到回调)。
  65.在理解Java I/O类库之前,首先要了解装饰器模式和类之间关系(关系图),有两篇非常不错的文章:1)从装饰者模式的理解说JAVA的IO包 http://xubindehao.iteye.com/blog/474636 2)深入分析Java I/O 工作机制 http://blog.csdn.net/zhangerqing/article/details/8466532 。两篇文章很好的总结I/O之间的关系 ,在书中(P533-P539)讲诉了类之间的关系。
  66.基于字节操作的I/O接口:InputStream和OutputStream,基于字符操作的I/O接口:Writer和Reader,两者之间沟通桥梁是InputStreamReader和OutputStreamWriter(将字节流(Stream)传入构造器变成字符流(Reader、Writer))。
  67.(P539-P544)展示了I/O流典型的使用方式,总结:对于字符串或文件内容用BufferedReader和PrintWriter。用DataOutoutStream和DataInputoutStream得到Int Double之类的数据,一般用Buffer**具有缓冲作用的做装饰,至于如何构造这个类,还是要查手册或Internet。
  68.读写随机访问文件,使用RandomAccessFile,此类继承于Object,不依赖于其他I/O类库,是完全独立的(P544-P545)。
  69.确保文件能关闭的的写法:
  try{
    文件初始化
    try{
      要做的事
    }
    finally
    {
      关闭文件
    }
  }
  catch{
    初始化发生异常的处理
  }

  70.文件读写的实用工具(P545-P547)。
  71.System.out和System.err是PrintStream对象,而System.in是一个未经加工的InputStream。
  72.I/O重定向setIn(InputStream),setOut(PrintStream)、SetErr(PrintStream)。重定向以后System.out和System.in。不一定指向屏幕和键盘。
  73.(P570-P571)JAR包打包方法,常用:jar cf myJarFile.jar *.class。
  74.对象序列化,要序列化的对象实现Serializable接口,用ObjectInputStream和ObjectOutputStream读入和写入。
  75.通过实现Externalizable接口——代替实现Serializable接口,来对序列化过程进行控制。该接口实现两个方法writeExternal()和readExternal()。它与恢复一个Serializable对象不同。对于Serializable对象,对象完全以它的二进制位为基础来构造,而不调用构造器。而Externalizable所有普通的默认构造器都会被调用(包括在字段定义时的初始化),然后调用readExternal()。如果要像Serializable接口一样就要在writeExternal()写入必要的对象,在readExternal()读出初始化(P577-P578),如果从一个Externalizable对象继承,通常需要调用基类版本的writeExternal()和readExternal()来为基类组件提供恰当的存储和恢复功能。(还有一种Externalizable的代替办法)
  76.可以用transient(瞬时)关键字逐个字段的关闭序列化,特别是密码等重要数据。
  77.static值不会自动化序列化,必须自己去动手实现。(P583-P585)
  78.序列化成XML(P586-587),使用Preferences(P588-P589)
  暂时写到这里。

1 0
原创粉丝点击