《Java编程思想》第10章——内部类
- 在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到创建它的外部类对象上。
- 接口的所有成员自动被设置为public。
- 匿名类不可能有构造器。
《Java编程思想》第11章——持有对象
- 如果一个类没有显式地声明继承自哪个类,那么它自动地继承自Object。
- 通过使用泛型,就可以在编译期防止将错误类型的对象放置到容器中。
- 你可以将Apple的子类型添加到被指定为保存Apple对象的容器中。
- 你可以直接使用Arrays.asList()的输出,将其当作List,但是在这种情况下,其底层表示的是数组,因此不能调整尺寸。
- 基本的ArrayList,长于随机访问元素,但是在List的中间插入和移除元素时较慢。LinkedList在List中间进行插入和删除的操作代价较低,并提供了优化的顺序访问,但在随机访问方面相对比较慢。
- subList()所产生的列表的幕后就是初始化列表,因此,对所返回的列表进行修改,会直接反映到原初始列表中,反之亦然。
- 不能在foreach中直接删除容器的元素,但可以通过Iterator来实现。
- Iterator的真正威力:能够将遍历序列的操作与序列底层的结构分离。因此,我们有时会说:迭代器统一了对容器的访问方式。
- 我们通常都会选择一个HashSet的实现,它专门对快速查找进行了优化。
- 队列常被当作一种可靠的将对象从程序的某个区域传输到另一个区域的途径。
- 能够与foreach一起工作是所有Collection对象的特性。如果你创建了任何实现Iterable的类,都可以将它用于foreach语句中。
- 不存在任何从数组到Iterable的自动转换,你必须手动执行这种转换。
- 像数组一样,List也建立数字索引与对象的关联,因此数组和List都是排好序的容器,List能够自动扩充容量。
《Java编程思想》第12章——通过异常处理错误
- 当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。
《Java编程思想》第13章——字符串
- String对象是不可变。
- 当你为一个类编写toString()方法时,如果你要在toString()方法中使用循环,那么最好自己创建一个StringBuilder对象,用它来构造最终的结果。
- 一般来说,比起功能有限的String类,我们更愿意构造功能强大的正则表达式对象,只需要导入java.util.regex包,然后用static Pattern.compile()方法来编译你的正则表达式即可。它会根据你的String类型的正则表达式生成一个Pattern对象。接下来,把你想要检索的字符串传入Pattern对象的matcher()方法。matcher()方法会生成一个Matcher对象,它有很多功能可用,包括find等。
- 通过reset()方法,可以将现有的Matcher对象应用于一个新的字符序列。
- 在Java引入正则表达式和Scanner类之前,分割字符串的唯一方法是使用StringTokenizer来分词。现在基本上StringTokenizer已经可以废弃不用了。
《Java编程思想》第14章——类型信息
- 如果某个对象出现在字符串表达式中,toString()方法就会被自动调用,以生成表达该对象的String。
- 我们希望大部分代码尽可能少地了解对象的具体类型,而是与对象家族中的一个通用表示打交道。这样代码会更容易写,更容易读,且更便于维护,设计也更容易实现、理解和改变。所以“多态”是面向对象编程的基本目标。
- 要理解RTTI(Run-Time Type Information)在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息。
- 类是程序的一部分,每个类都有一个Class对象。
- 所有的类都是在对其第一次使用时,动态加载到JVM中的。
- 一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。
- 如果你已经拥有了一个感兴趣的类型的对象,那就可以通过调用getClass()方法来获取Class引用了。
- 进行向下转型前,如果没有其他信息可以告诉你这个对象是什么类型,那么使用instanceof是非常重要的,否则会得到一个ClassCastException异常。
- 对instanceof有比较严格的限制:只可将其与命名类型进行比较,而不能与Class对象做比较。
- instanceof保持了类型的概念,它指的是:“你是这个类吗?或者你是这个类的派生类吗?”,而如果用==比较实际的Class对象,就没有考虑继承——它或者是这个确切的类型,或者不是。
- RTTI和反射之间真正的区别在于,对RTTI来说,编译器在编译时打开和检查.class文件,而对于反射机制来说,.class文件在编译时是不可获取的,所以在运行时打开和检查.class文件。因此使用反射机制时,那个类的.class文件对于JVM来说必须是可获取的;要么在本地机器上,要么可以通过网络取得。
- 面向对象编程语言的目的是让我们在凡是可以使用的地方都使用多态机制,只在必需的时候使用RTTI。
- 不要太早地关注程序的效率问题,这是个诱人的陷阱。最好首先让程序运作起来,然后再考虑它速度。
《Java编程思想》第15章——泛型
- 与其使用Object,我们更喜欢暂时不指定类型,而是稍后再决定具体使用什么类型,要达到这个目的,需要使用类型参数,用尖括号括住,放在类名后面。然后在使用这个类的时候,再用实际的类型替换此类型参数。
- Java泛型的核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切细节。
- 是否拥有泛型方法,与其所在的类是否是泛型没有关系。
- 无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛型化,那么就应该使用泛型方法,因为它可以使事情更清楚明白。
《Java编程思想》第16章——数组
- Java标准类库Arrays有一个作用十分有限的fill()方法:只能用同一个值填充各个位置,而针对对象而言,就是复制同一个引用进行填充。
- Java标准类库提供有static方法System.arraycopy(),用它复制数组比用for循环复制要快很多。
- Java标准类库中的排序算法针对正排序的特殊类型进行了优化——针对基本类型设计的“快速排序”,以及针对对象设计的“稳定归并排序”。
- Java对尺寸固定的低级数组提供了适度的支持,这种数组强调的是性能而不是灵活性。
- 当你使用最近的Java版本编程时,应该“优先容器而不是数组”。只有在证明性能成为问题(并且切换到数组对性能提高有所帮助)时,你才应该将程序重构为使用数组。
《Java编程思想》第17章——容器深入研究
- 对于良好的编程风格而言,你应该在覆盖equals()方法时,总是同时覆盖hashCode()方法。
- 散列码是“相对唯一”的、用以代表对象的int值,它是通过将该对象的某些信息进行转换而生成的。hashCode()是根类Object中的方法,因此所有Java对象都能产生散列码。HashMap就是使用对象的hashCode()进行快速查询的,此方法能够显著提高性能。
- 默认的Object.equals()只是比较对象的地址。如果要使用自己的类作为HashMap的键,必须同时重载hashCode()和equals()。
- hashCode()并不需要总是能够返回唯一的标识码。
- 设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该生成同样的值。
- 如果你的hashCode()方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()就会生成一个不同的散列码,相当于产生了一个不同的键。
- 散列码不必是独一无二的(应该更关注生成速度,而不是唯一性),但是通过hashCode()和equals(),必须能够完全确定对象的身份。