读书笔记 之《Thinking in Java》(对象、集合、异常)
一、前言? ? 本来想看完书再整理下自己的笔记的,可是书才看了一半发现笔记有点多,有点乱,就先整理一份吧,顺便复习下前面的知识,之后的再补上。 ? ? 真的感觉,看书是个好习惯啊,难怪人家说“书籍是人类进步的阶梯”。之前学知识,喜欢网上找份教程,看点视频,照着做呗,秉着”我做过的东西反正别人肯定玩过“的观念,一通乱学,学的又多又杂,现在细细想来,很多东西我只是学到了它的形,却没有学到它的神,只是在抄别人的代码。为什么这么做?这么写是出于什么考虑?我都一脸懵懂!而现在我喜欢看书,花时间来沉淀自己的知识,与大家共勉!另外,不推荐看第四版翻译的《Thinking in Java》,讲的太拗口了。或者说翻译的太拗口了。简直像谷歌的一键翻译成中文...... ?二、面向对象1、对象存储到什么地方?
Parcel11.Contents c = p.new Contents();
如果内部类是private或者protected的 时候又该怎么办呢?那就只能提供一个public的get()方法去得到这个内部类,尤其是向上转型或者实现接口的时候特别有用。
?
16、为什么需要定义内部类?
1、我们准备实现某种形式的接口,使自己能够创建和返回一个引用。
2、要解决一个复杂的问题,并希望创建一个类,用来辅助自己的程序方案。同时不愿意把它公开。
3、实现多重继承,由于可以实现多个接口,但只能继承一个类,如果想要同时继承两个类药怎么办呢?就可以使用内部类来实现。
?
? ? ? ?如果我只是需要一个对接口的引用,为什么不通过外围类实现那个接口呢?
如果这能满足你的需求,那么你就应该这么做。
?
? ? ? ?那么内部类实现一个接口与外围类实现一个接口有什么区别呢?
你不是总能享用到接口带来的方便,有时候你需要与接口的实现进行交互,每个内部类都能独立的继承一个接口的实现,所以无论外围类是否已经继承了某个接口。对于内部类都没有影响、
?
17、局部内部类:在方法的作用域中创建一个完整的类,一般表现为实现接口或向上转型,返回该接口或基类的引用。
? ? ? ? 嵌套类:如果你不需要内部类对象与其外围对象之间的联系,那你可以将内部类声明为static。
?
18、匿名内部类
1、如果是接口,相当于在内部返回了一个接口的实现类,并且实现方式是在类的内部进行的;
2、如果是普通类,匿名类相当于继承了父类,是一个子类,并可以重写父类的方法。
3、需要特别注意的是,匿名类没有名字,不能拥有一个构造器。如果想为匿名类初始化,让匿名类获得一个初始化值,或者说,想使用匿名内部类外部的一个对象,则编译器要求外部对象为final属性,否则在运行期间会报错。
public class Parcel8 { // Argument must be final to use inside anonymous inner class: public Destination dest(final String dest) {这个dest必须设置为final return new Destination() { private String label = dest; public String readLabel() { return label; } }; } static void main(String[] args) { Parcel8 p = Parcel8(); Destination d = p.dest("Tanzania"); } } ?? 19、接口可以嵌套子在类或其他接口中。并且接口可以在类中设置成private。 ? 20、一个内部类被嵌套多少层并不重要——他能透明的访问它所嵌入的外围类的所有成员。 ? 21、内部类的继承:如果你想继承一个内部类,由于内部类依赖于外部类的实例,所以你必须要调用外部类的构造器才能编译成功。 C { D{ } } class E extends C.D { public E(C c){ c.super(); } } 22、内部类的重载:如果子类中定义了一个类名和父类一样的内部类,实际上,内部类并没有发生什么特别的变化。两个内部类是完全独立的实体。 23、为什么普通内部类的的成员不能设置成 static ? ? 首先,尽管是内部类,他也是外部类的一个成员,是类实例的一部分。而静态的变量是类的一部分和实例无关 你若声明一个成员内部类 让他成为主类的实例一部分 然后又想在内部类声明和实例无关的静态的东西 你让JVM情何以堪啊 。 ? 24、局部内部类和匿名内部类有什么区别? ? 实际上,它们有一样的行为和能力。唯一的区别就是局部内部类具有一个已命名的构造器。而匿名类只能用于实例初始化。 ? 25、方法调用绑定 ? 前期绑定:面向过程的语言中不需要选择就默认的绑定方式。 ? 后期绑定:也叫动态绑定,编译器不知道对象的类型,但是方法调用机制能找到正确的方法体,并加以调用。Java中除了static 和 final的方法,其他所有的方法都是后期绑定。 ? 26、一个类继承了父类并实现了接口,如果接口中的方法名和父类的方法名相同则默认不实现接口中的方法,而采用父类的方法实现。 ? 27、实际上,内部类的一个实例初始化模块就是一个匿名内部类的构造器。初次之外,内部类拥有对封装类所有元素的访问权限。也就是说,我们可以在封装类中新建一个辅助类或实现一个工具类的接口帮助我们完成对封装类的一些增删查改的操作。 ? 28、设计构造器时一个特别有效的规则是:用尽可能简单的方法使对象进入就绪状态;如果可能,避免调用任何方法。在构造器内唯一能够安全调用的是在基础类中具有final 属性的那些方法(也适用于 private方法,它们自动具有 final 属性)。这里有一个特殊的情景就是,子类实例化必须先调用父类的构造器,如果父类的构造器中调用了子类的重写方法怎么办?程序不会报错,子类方法中的成员变量采用成员默认值,因为,子类还没有实例化。 ? 29、泛型类或者泛型方法中,不接受 8 种基本数据类型。? ? 30、不可变对象是指一经创建就不能改变的对象。"不可变性"一词有两种解释:观测不可变性和实现不可变性。观测不可变性是指在其他对象看来,该类是不可变的;实现不可变性是指对象本身不可变。实现不可变性意味着观测不可变性, 反之则不一定成立。 三、集合?1、为容纳一组对象,最适宜的选择应当是数组,但是数组也有他明显的缺点,即容量有限。而且假如容纳的是一系列基本数据类型,更是必须采用数组。集合实际容纳的类型为Object的引用,这当然包括一切的java对象,因为Object是一切对象的基类。当然并不包括基本数据类型,因为它们并不是从“任何东西”继承来的。 ? 2、Iterator接口的remove方法将会删除上次调用next方法时返回的元素。集合也有一个remove的方法,适用于List 和 Set ,boolean remove(Object)。? 3、Listiterator和Iterator 的区别? 一.相同点 都是迭代器,当需要对集合中元素进行遍历不需要干涉其遍历过程时,这两种迭代器都可以使用。 二.不同点 1.使用范围不同,Iterator可以应用于所有的集合,Set、List和Map和这些集合的子类型。而ListIterator只能用于List及其子类型。 2.ListIterator有add方法,可以向List中添加对象,而Iterator不能。 3.ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevIoUs()和prevIoUs()方法,可以实现逆向(顺序向前)遍历。Iterator不可以。 4.ListIterator可以定位当前索引的位置,nextIndex()和prevIoUsIndex()可以实现。Iterator没有此功能。 5.都可实现删除操作,但是ListIterator可以实现对象的修改,set()方法可以实现。Iterator仅能遍历,不能修改。 ? 4、Set集合都不能保存重复的数据,即使是TreeSet也只是对不重复的数据进行排序罢了。对于TreeSet或者HashSet来说,在进行add()以及contain()操作时,HashSet显然要比ArraySet出色的多,而且性能明显与元素的多寡关系不大。一般在元素比较少,而且要进来遍历查询的时候,TreeSet会稍微快一些。 ? 5、HashSet 与TreeSet和LinkedHashSet的区别? HaseSet: 1、不能保证元素的排列顺序,顺序有可能发生变化。 2、不是同步的。 3、集合元素可以为null,但只能放入一个null。 4、当向HashSet集合中存入一个元素时,会首先调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。如果该HashSet集合中已经有这个对象的hashCode值,则调用该对象的equal()方法,返回true,就不插入,返回false,就另外找一个hashCode存储。 ? TreeSet: TreeSet底层是一个红黑二叉树结构,其中元是不可重复的。TreeSet类型是J2SE中唯一可实现自动排序的类型。TreeSet支持两种排序方式,自然排序和定制排序。向treeSet中加入的应该是同一个类的对象。向TreeSet插入基本数据类型时,Java已经定义好了CompareTo(Object obj)方法,采用自然排序,默认升序。如果放入的是自定义的对象类需要实现Comparable接口并重写compareTo()方法,相等返回0,大于返回正数,小于返回负数。同时treeSet的插入比较也是通过compareTo()或者equal()方法来比较插入的。 ? LinkedHashSet: ???? LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺 序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素,也就是说它是按插入时的顺序排序的。 ? 6、Set的性能方面:HashSet的性能总是比TreeSet的好(特别是最常用的添加和查询元素操作)。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TreeSet存在的唯一原因是:他可以维护元素的排序状态。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好。 ? 7、HashMap、TreeMap 的区别? 1、HashMap是基于散列表实现的,时间复杂度能达到O(1),是无序的。 2、TreeMap基于红黑树(一种自平衡二叉查找书)实现的,时间复杂度平均能达到O(log n),是有序的。 ? 8、HashMap、HashTable的区别? 1、HashMap几乎可以等价于HashTable,都实现了Map接口。 2、HashMap是非同步的,线程不安全的。而HashTable是同步的,线程安全的。当然我们也可以手动进行HashMap同步(Collections.synchronizeMap(hashMap)) 3、HashTable中hash数组默认大小是11,增加的方式是?old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。 4、哈希值的使用不同,HashTable直接使用对象的hashCode。 ? ? 9、LinkedHashMap和hashMap的区别? ? ? HashMap 是无序的,HashMap 在 put 的时候是根据 key 的 hashcode 进行 hash 然后放入对应的地方。所以在按照一定顺序 put 进 HashMap 中,然后遍历出 HashMap 的顺序跟 put 的顺序不同(除非在 put 的时候 key 已经按照 hashcode 排序号了,这种几率非常小) ? ? ? LinkedHashMap 和hashMap最大的不同在于,LinkedHashMap维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。怎么理解呢?就是LinkedHashMap是实现有序的HashMap,他在根据hashcode进行存储的同时,维护者一张记录插入顺序的表。 10、LinkedHashMap:为了提高速度,LinkedHashMap散列化所以的元素,但是在遍历”键值对“时,却有以元素的插入顺序返回”键值对“,即迭代结果显示的是你插入的顺序。此外可以在构造器中设定LinkedHashMap,使之采用基于访问的LRU(最近最少使用)算法,于是没有被访问过的元素就会出现在队列的前面。LinkedHashMap linkedHashMap=new LinkedHashMap(16,0.75f,true); public LinkedHashMap(int initialCapacity,1)">float loadFactor,1)">boolean accessOrder) { (initialCapacity,loadFactor); this.accessOrder = accessOrder; } 参数说明: 1. initialCapacity 初始容量大小,使用无参构造方法时,此值默认是16 2. loadFactor 加载因子,使用无参构造方法时,此值默认是 0.75f 3. accessOrder false: 基于插入顺序 true: 基于访问顺序 ? 11、WeakHashMap ? ? ? ? WeakHashMap 用弱引用承载映射键,这使得应用程序不再使用键对象时它们可以被垃圾收集,get() 实现可以根据 WeakReference.get() 是否返回 null 来区分死的映射和活的映射。但是这只是防止 Map 的内存消耗在应用程序的生命周期中不断增加所需要做的工作的一半,还需要做一些工作以便在键对象被收集后从 Map 中删除死项。否则,Map 会充满对应于死键的项。虽然这对于应用程序是不可见的,但是它仍然会造成应用程序耗尽内存。 ? ? ? ? 引用队列是垃圾收集器向应用程序返回关于对象生命周期的信息的主要方法。弱引用有个构造函数取引用队列作为参数。如果用关联的引用队列创建弱引用,在弱引用对象成为 GC 候选对象时,这个引用对象就在引用清除后加入到引用队列中(具体参考上文软引用示例)。 ? ? ? ? WeakHashMap 有一个名为 expungeStaleEntries() 的私有方法,大多数 Map 操作中会调用它,它去掉引用队列中所有失效的引用,并删除关联的映射。 ? 12、Map的性能方面来:对于Map的put()和get(),hashMap和hashTable都要优于TreeMap,而hashTable要多花一些开销在线程同步上,所以hashMap的性能又稍优于hashTable。 ? ? ? ?而对于Map的遍历来说,TreeMap的性能要稍优于hashMap和hashTable。 ? ? ? ?如果需要创建大量Map,TreeMap、hashMap的性能要优于hashTable。 ? ? ? ?综合考虑,平常使用hashMap()就已经能够满足大部分需求了。 ? ? 13、对于Arrays和Collections来说,若在执行一次 binarySearch()之前不调用 sort(),便会发生不可预测的行为,其中甚至包括无限循环。排序遵守的是字典排序,亦即大写字母在字符集中位于小写字母的前面。 ? 14、Comparator和Comparable的区别? 集合或者数组要实现自动排序功能,比如TreeSet、TreeMap、Arrays.sort()、Collections.sort(),有两种方式。 第一种:比较的类型内部实现了Comparable接口重写了compareTo()方法。 第二种:自己新建了一个比较类实现了Comparator接口,重写了compare方法。 ? 15、使 Collection 或 Map 不可修改? Collections.unmodifiableCollection(c); Collections.unmodifiableList(a); Collections.unmodifiableSet(s); Collections.unmodifiableMap(m); ? 16、使Collection 或 Map 的同步? Collections.synchronizedCollection(new ArrayList()); Collections.synchronizedList(new ArrayList()); Collections.synchronizedSet(new HashSet()); Collections.synchronizedMap(new HashMap()); ? 17、 填充数组:Java中标准类库Arrays也有fill()的方法,但是他作用有限。只能用一个值填充各个位置,对于保存对象的数组,就是复制同一个引用进行填充。 复制数组:System.arraycopy(源数组,源数组下标开始,目标数组,目标数组下标开始,长度)。当然,对数组的任何越界操作都会导致异常。 填充集合:Collections.fill(list,"Hello")此方法的作用有限,只能替换已经在List中存在的元素,并不能增加新元素。 ? 18、方法keySet()方法由Map的“键”组成的Set。类似的方法还有values,他返回一个Collection,包含Map的所有值("键“必须是唯一的,而“值可以重复”),由于Collection背后是由Map支持的,所以对Collection的任何改动都会影响到Map。 ? 19、新程序中不应该使用过时的Vector、HashTable和Stack。四、异常?1、异常情形是指:阻止当前方法或作用域继续执行的问题。异常情形和普通问题不一样,普通问题指在编写代码的时候就已经提示的错误。当异常出现的时候,将使用new在堆上创建异常对象,当前的执行路径被终止,并且在当前环境中弹出异常对象的引用,由异常处理机制接管程序。 ? 2、异常处理的好处? 异常处理是Java中唯一正式的错误报告机制,并且通过编译器强制执行,它能使错误代码变得更有条理,而且把“描述做什么事的代码”和“出了问题怎么办的代码”相分离。异常处理是被设计用来处理一些烦人的运行期错误,这些错误是由你的代码控制能力之外的因素导致的。 ? 3、异常处理理论上有两种基本模型。一种是终止模型: 一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行。一种是恢复模型:异常处理程序的工作是修正错误,然后重新尝试调用出问题的方法,并认为第二次能成功。(现在基本都使用终止模型) ? 4、异常类的信息方法 String getMessage(); 获取详细信息 String getLocalizedMessage(); 获取本地语言表述的详细信息 String toString() ; 返回简单描述,要是有详细信息的话,也会包括在内 void printStackTrace(java.io.printWriter); ?打印Throwable 和 Throwable的调用栈轨迹。调用栈显示了"把你带到异常抛出地点"的方法调用序列、 Throwable fillInStackTrace(); 用于在Throwable对象的内部记录栈框架的当前状态 ? 5、异常链:你如果想要在捕获一个异常然后抛出另一个异常,并且希望把原始异常的信息保存下来,这就称为"异常链"。在Throwable的子类中,只有三种基本的异常类提供了带cause的构造器。他们是Error、Exception以及RuntimeException。如果你要把其他类型的异常链接起来,你应该用initCause()方法而不是构造器。public Throwable initCause(Throwable cause)
将此 throwable 的 cause 初始化为指定值。(该 Cause 是导致抛出此 throwable 的throwable。)
?
6、自定义异常类:需要继承Exception类。Exception继承自Throwable,Throwable可以分为两种类型:Error用来表示你不用关心的编译期和系统错误;Exception是可以被抛出的基本类型。
?
7、finally:对于一些代码,你可能希望无论try块中的异常是否抛出,他们都能得到执行。通常适用于内存回收之外的情况。
五、其他1、?Java包装类只能做一件事,就是将其初始化为某个值,然后读取这个值。也就是说,一旦创建了包装类的对象,就没有办法改变他的值。 ? 2、怎么理解TCP是可靠的协议,而HTTP基于IP/TCP协议,却是不可靠的? 1、首先IP 是网络层的协议,确认source和target的IP地址。 2、TCP是传输层的传输协议,传输基于三次握手过程提供可靠的传输协议。 3、HTTP是应用层协议,它只负责把服务器的资源反馈到客户端。 4、怎么理解三个协议之间的合作呢,IP提供源地址和目标地址,TCP建立可靠的通道,最后由HTTP来负责传输内容,如果一个会话需要多个层级的连接,会造成延迟大、资源浪费,所以如果一个层级已经提供了可靠连接,则其它层级完全没有必要连接,只需要交流信息即可,比如这里的HTTP。(编辑:北几岛) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |