你的位置:首页 > Java教程

[Java教程]读书笔记 之《Thinking in Java》(对象、集合)

一、前言:

    本来想看完书再整理下自己的笔记的,可是书才看了一半发现笔记有点多,有点乱,就先整理一份吧,顺便复习下前面的知识,之后的再补上。

    真的感觉,看书是个好习惯啊,难怪人家说“书籍是人类进步的阶梯”。之前学知识,喜欢网上找份教程,看点视频,照着做呗,秉着”我做过的东西反正别人肯定玩过“的观念,一通乱学,学的又多又杂,现在细细想来,很多东西我只是学到了它的形,却没有学到它的神,只是在抄别人的代码。为什么这么做?这么写是出于什么考虑?我都一脸懵懂!而现在我喜欢看书,花时间来沉淀自己的知识,与大家共勉!

 

 二、面向对象

1、对象存储到什么地方?
  • 寄存器。这是最快的存储区,寄存器的数量及其有限,由编译器根据需求进行分配。你不能直接控制,也不能在程序中感觉到寄存器存在的任何现象。
  • 栈。栈指针向下移动则分配新的内存,向上移动,则释放那些内存。这是一种快速有效的分配存储方法,仅次于寄存器。基本数据类型和对象引用存储在其中。
  • 堆。一种通用性的内存池,用于存放所有的java对象,堆不同于栈的好处在于:编译器不需要知道从堆里分配多少存储区域,也不必知道存储的数据在堆里存活多少时间。
  • 静态存储。静态存储里存放 程序运行时一直存放的数据。Static 关键字的对象存放在其中。static局部变量在所处模块在初次运行时进行初始化工作,且只操作一次。
  • 常量存储。常量值通常直接存放在程序代码内部,这样做是安全的,因为他们永远不会被 改变。
  • 非RAM存储。比如流对象和持久化对象,发送给另一台机器或者存放在磁盘上。
2、基本成员默认值如果类的某个成员是基本数据类型,即使没有进行初始化,Java也会确保他获得一个默认值。boolean  false
char  null
byte  0
short  0
int  0
long  0
float  0.0
double  0.0 3、在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零。在一个类里,初始化的顺序是由变量在类内的定义顺序决定的。即使变量定义大量遍布于方法定义的中间,那些变量仍会在调用任何方法之前得到初始化—— 甚至在构建器调用之前。      static 初始化只有在必要的时候才会进行。如果不创建一个 Table 对象,而且永远都不引用 Table.b1 或Table.b2,那么 static Bowl b1 和 b2 永远都不会创建。然而,只有在创建了第一个 Table 对象之后(或者发生了第一次 static 访问),它们才会创建。在那以后, static 对象不会重新初始化。初始化的顺序是首先 static(如果它们尚未由前一次对象创建过程初始化),接着是非static 对象 4、类的设计中为什么要控制对成员的访问?1、防止用户接触那些他们不应碰的工具。对于数据类型的内部机制,那些工具是必需的。但它们并不属于用户接口的一部分,用户不必用它来解决自己的特定问题。比如private的方法,有些是不希望被用户使用的。2、允许类设计者改变类的内部工作机制,同时不必担心它会对客户程序员产生什么影响。3、防止类的成员被用户随意修改,比如用户通过 类名.成员名 就可以改变类的属性值。 5、为什么优先使用组合而非继承?1、继承破坏封装性。基类的很多内部细节都是对派生类可见的,因此这种复用是“白箱复用”;2、如果基类的实现发生改变,那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性;3、组成不会强迫我们的程序设计进入继承的分级结构中。同时,组成显得更加灵活,因为可以动态选择一种类型(以及行为),而继承要求在编译期间准确地知道一种类型。因此降低了应用的灵活性。 6、一条常规的设计准则是:用继承表达行为间的差异,并用成员变量表达状态的变化。 7、为什么要向上转型?好处:向上转型主要是为了用变量来接收不同的子类对象,调用方法的时候传参父类对象,可以调用子类中不同的重写方法,实现不同的效果。坏处:屏蔽了子类中新增的变量和方法。 8、static关键字 强调他们只有一个,而final 关键字强调他们是一个常数。不能由于某样东西的属性是final,就认定他的值在编译的时候已经知道,其实final的值是进行类对象初始化的时候确定的。 9、final 关键字 修饰方法 的好处?1、为方法“上锁”,防止任何继承类改变它的本来含义。设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖或改写,就可以采取这种做法。2、将一个方法设成 final 后,编译器就可以把对那个方法的所有调用都置入“嵌入”调用里。只要编译器发现一个 final 方法调用,就会(根据它自己的判断)忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。相反,它会用方法主体内实际代码的一个副本来替换方法调用。这样做可避免方法调用时的系统开销。当然,若方法体积太大,那么程序也会变得雍肿,可能受到到不到嵌入代码所带来的任何性能提升。因为任何提升都被花在方法内部的时间抵消了。 Java 编译器能自动侦测这些情况,并颇为“明智”地决定是否嵌入一个 final 方法。然而,最好还是不要完全相信编译器能正确地作出所有判断。通常,只有在方法的代码量非常少,或者想明确禁止方法被覆盖的时候,才应考虑将一个方法设为final。 10、为什么要把方法定义成抽象或者定义接口?1、它能为不同的子类型或者实现类作出不同的表示。它为我们建立了一种基本形式,使我们能定义在所有衍生类里“通用”的一些东西。比如 子类继承了父类,虽然如果方法名与父类相同,但自变量或参数不同,就会出现重载现象,那或许并非我们所愿意的。所以好一点的方法,就是在父类或者接口中建立一种标准。2、还有就是父类中的方法实现其实很冗余,基本用不到11、接口也包含了基本数据类型的数据成员,但他们都默认是static 和 final,必须获得初始化(所以可以借助此性质定义需要的“枚举值”,通过接口名.变量名来访问)。接口只提供一种形式,并不提供实施的细节。 12、接口当作方法的形参的时候,传入的不仅可以是接口的实现类,也可以是实现类的子类。 13、利用继承技术,可方便地为一个接口添加新的方法声明,也可以将几个接口合并成一个新接口。在这两种情况下,最终都得到一个新接口。接口的继承实际上是一个接口功能增加的过程,有些应用只需要简单的接口。通过继承,可以在简单的接口上得到适合自己需要的复杂接口。 14、可以在一个类的内部定义新的类,称为内部类,如果想生成内部类的一个对象,必须将那个对象的类型设为"外部类名.内部类名" 15、为什么需要定义内部类?1、我们准备实现某种形式的接口,使自己能够创建和返回一个引用。2、要解决一个复杂的问题,并希望创建一个类,用来辅助自己的程序方案。同时不愿意把它公开。 16、匿名内部类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; }  }; }public static void main(String[] args) {Parcel8 p = new Parcel8();Destination d = p.dest("Tanzania");}}  
 17、实际上,一个实例初始化模块就是一个匿名内部类的构造器。初次之外,内部类拥有对封装类所有元素的访问权限。 18、设计构造器时一个特别有效的规则是:用尽可能简单的方法使对象进入就绪状态;如果可能,避免调用任何方法。在构造器内唯一能够安全调用的是在基础类中具有final 属性的那些方法(也适用于 private方法,它们自动具有 final 属性) 19、泛型类或者泛型方法中,不接受 8 种基本数据类型。 三、集合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将会以元素的添加顺序访问集合的元素。     LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet6、如果两个对象相同,那么他们的hashCode值一定相同。如果两个对象的hashCode值相同,他们不一定相同。 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、从性能方面来说,对于Map的put()和get(),hashMap和hashTable都要优于TreeMap,而hashTable要多花一些开销在线程同步上,所以hashMap的性能又稍优于hashTable。而对于Map的遍历来说,treeSet的性能要稍优于hashMap和hashTable。 10、如果需要创建大量Map,TreeMap、hashMap。的性能要优于hashTable。 11、对于Arrays和Collections来说,若在执行一次 binarySearch()之前不调用 sort(),便会发生不可预测的行为,其中甚至包括无限循环。排序遵守的是字典排序,亦即大写字母在字符集中位于小写字母的前面。 12、Comparator和Comparable的区别?集合或者数组要实现自动排序功能,比如TreeSet、TreeMap、Arrays.sort()、Collections.sort(),有两种方式。第一种:比较的类型内部实现了Comparable接口重写了compareTo()方法。第二种:自己新建了一个比较类实现了Comparator接口,重写了compare方法。13、使 Collection 或 Map 不可修改?Collections.unmodifiableCollection(c);Collections.unmodifiableList(a);Collections.unmodifiableSet(s);Collections.unmodifiableMap(m);14、使Collection 或 Map 的同步?Collections.synchronizedCollection(new ArrayList());Collections.synchronizedList(new ArrayList());Collections.synchronizedSet(new HashSet());Collections.synchronizedMap(new HashMap()); 四、其他 1、怎么理解TCP是可靠的协议,而HTTP基于IP/TCP协议,却是不可靠的?1、首先IP 是网络层的协议,确认source和target的IP地址。2、TCP是传输层的传输协议,传输基于三次握手过程提供可靠的传输协议。3、HTTP是应用层协议,它只负责把服务器的资源反馈到客户端。4、怎么理解三个协议之间的合作呢,IP提供源地址和目标地址,TCP建立可靠的通道,最后由HTTP来负责传输内容,如果一个会话需要多个层级的连接,会造成延迟大、资源浪费,所以如果一个层级已经提供了可靠连接,则其它层级完全没有必要连接,只需要交流信息即可,比如这里的HTTP。