<[疯狂Java:突破程序员基本功的16课]>读书笔记

  1. Java的数组是静态的,即当数组被初始化之后,该数组的长度是不可变的,所谓初始化,就是为数组对象的元素分配内存空间,并为每个数组元素指定初始值。
  2. 静态初始化:初始化时由程序员显示指定每个数组元素的初始值,由系统决定数组长度。
  3. 动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值
  4. 一旦初始化完成,数组的长度就不可改变。Java语言允许通过数组的length属性来访问数组的长度
  5. 执行动态初始化时,程序员只需指定数组的长度,即为每个数组元素指定所需的内存空间,系统将负责为这些数组元素分配初始值。
  6. Java的数组变量是一种引用类型的变量,数组变量并不是数组本身,它只是指向堆内存中的数组对象。
  7. Java的数组变量是引用类型的变量,它不是数组对象本身,只要让数组变量指向有效的数组对象,程序中即可使用该数组变量
  8. 数组变量只是一个引用变量,通常存放在栈内存中(也可以放入堆内存中);而数组对象就是保存在堆内存中的连续内存空间,对数组执行初始化,其实并不是对数组变量执行初始化,而是要对数组对象执行初始化—也就是为该数组对象分配一块连续的内存空间,这块连续内存空间的长度就是数组的长度
  9. 所有局部变量都是放在栈内存里保存的,不管其是基本类型的变量,还是引用类型的变量,都是存储在各自的方法栈区中,但引用类型变量所引用的对象(包括数组、普通Java对象)则总是存储在堆内存中
  10. 堆内存中的对象(不管是数组对象,还是普通的Java对象)通常不允许直接访问,为了访问堆内存中的对象,通常只能通过引用变量
  11. NullPointerException.当通过引用变量来访问实例属性,或者调用非静态方法时,如果该引用变量还未引用一个有效的对象,程序就会引发NullPointerException。
  12. 引用类型数组的数组元素依然是引用类型的,因此数组元素里存储的还是引用,它指向另一块内存,这块内存里存储了该引用变量所引用的对象(包括数组和Java对象)
  13. 数组元素就是变量
  14. Main方法声明的变量都属于局部变量,因此它们都被保存在main方法栈中,但数组元素则作为数组对象的一部分,总是保存在堆内存中,不管它们是基本类型的数组元素,还是引用类型的数组元素。
  15. 一个Object[]类型的数组,每个数组元素都相当于一个Object类型的引用变量,因此可指向任何对象(包括数组对象和普通Java对象)
  16. Java内存管理分为两个方面:内存分配和内存回收。内存分配特指创建Java对象时JVM为该对象在堆内存中所分配的内存空间。内存回收指的是当该Java对象失去引用,变成垃圾时,JVM的垃圾回收机制自动清理该对象,并回收该对象所占用的内存。
  17. 局部变量分为三类:形参:在方法签名中定义的局部变量,由方法调用者为其赋值,随方法的结束而消亡。方法内的局部变量:在方法内定义的局部变量,必须在方法内对其进行显式初始化。这种类型的局部变量在初始化完成后开始生效,随方法的结束而消亡。代码块内的局部变量:在代码块内定义的局部变量,必须在代码块内对其进行显示初始化。这种类型的局部变量从初始化完成后开始生效,随代码块的结束而消亡。
  18. 局部变量都被存储在方法的栈内存中。
  19. 使用static修饰的成员变量是类变量,属于该类本身;没有使用static修饰的成员变量是实例变量,属于该类的实例。在同一个JVM中,每个类只对应一个Class对象,但每个类可以创建多个Java对象。同一个JVM内的一个类的类变量只需一块内存空间;但对于实例变量而言,该类每创建一次实例,就需要为实例变量分配一块内存空间。即程序中有几个实例,实例变量就需要几块内存空间。
  20. 类也是对象,所有类都是Class的实例。每个类初始化完成之后,系统都会为该类创建一个对应的Class实例,程序可以通过反射来获取某个类所对应的Class实例。Class.forName(“Person”)
  21. 对于实例变量而言,它属于Java对象本身,每次创建Java对象时都需要为实例变量分配内存空间,并执行初始化。
  22. 实例变量属于Java类本身,只有当程序初始化该Java类是才会该类的类变量分配内存空间,并执行初始化。
  23. 当创建任何Java对象时,程序总会先依次调用每个父类非静态初始化块、父类构造器(总是从Object开始)执行初始化,最后才调用本类的非静态初始化块、构造器执行初始化。
  24. Super调用用于显式调用父类的构造器,this调用用于显式调用本类中另一个重载的构造器。Super和this调用都只能在构造器中使用,而且super调用和this调用都必须作为构造器的第一行代码。
  25. 子类的方法可以访问父类的实例变量,这是因为子类继承父类就会获得父类的成员变量和方法,但父类的方法不能访问子类的实例变量,因为父类根本无从知道它将被哪个子类继承,它的子类将会增加怎样的成员变量。
  26. 构造器只是负责对Java对象实例变量执行初始化,在执行构造器代码之前,该对象所占的内存以及被分配下来,这些内存里值都默认是空值----对于基本类型的变量,默认的空值就是0或false;对于引用类型的变量,默认的空值就是null。
  27. This在构造器中时,this代表正在初始化的Java对象
  28. 当子类方法重写了父类方法之后,父类表面上只是调用属于自己的、被子类重写的方法,但随着执行context的改变,将会变成父类实际调用子类的方法。
  29. Java程序允许某个方法通过return this;返回调用该方法的Java对象,但不允许直接return super;甚至不允许直接将super当成一个引用变量使用。
  30. 类变量属于类本身,而实例变量则属于Java对象;类变量在类初始化阶段完成初始化,而实例变量则在对象初始化阶段完成初始化
  31. Final可以修饰变量,被final修饰的变量被赋初始值之后,不能重新对它重新赋值;final可以修饰方法,被final修饰的方法不能被重写;final可以修饰类,被final修饰的类不能派生子类
  32. 被final修饰的实例变量必须显式指定初始值,而且只能在三个位置指定初始值:1.定义final实例变量时指定初始值;在非静态初始化块中为final实例变量指定初始值;在构造器中为final实例变量指定初始值
  33. Java会缓存所用曾经用过的字符串直接量。例如执行String a = “java”;语句之后,系统的字符串池中就会缓存一个字符串”java”;如果程序再次执行String b = “java”;系统将会让b直接指向字符串池中的”java”字符串。因此a==b将会返回true。
  34. Set代表一种集合元素无序、集合元素不可重复的集合,Map则代表一种由多个key—value对组成的集合。Map集合是Set集合的扩展。
  35. Map集合的key具有一个特征,所有key不能重复,key之间没有顺序。Set(K) keyset()
  36. Map集合的所有key将具有Set集合的特征。对于Map而言,相当于key-value的Set集合。
  37. HashSet,系统采用Hash算法决定集合元素的存储位置,这样可以保证快速存取集合元素;HashMap,系统将value当成key的附属,系统根据Hash算法来决定key的存储位置,这样可以保证快速存取集合key。而value总是紧随key存储。
  38. Java集合实际上是多个引用变量所组成的集合,这些引用变量指向实际的Java对象。
  39. HashSet判断两个对象相等的标准除了要求通过equals()方法返回true之外,还要求两个对象的hashCode()返回值相等。
  40. TreeSet底层采用以后NavigableMap来保存TreeSet集合的元素。
  41. 对于TreeMap而言,采用一种被称为”红黑树”的排序二叉树来保存Map中每个Entry----每个Entry都被当成”红黑树”的一个节点对待。以后每向TreeMap中放入一个key-value对,系统都需哟啊将该Entry当成一个新节点,添加到已有红黑树中,通过这种方式就可保证TreeMap中所有key总是由小到大地排列。
  42. TreeMap添加元素、取出元素的性能都比HashMap低。TreeMap中的所有Entry总是按key根据指定排序规则保持有序状态,TreeSet中所有的元素总是根据指定排序规则保持有序状态。
  43. Map集合是一个关联数组,它包含两组值:一组是所有key组成的集合,因为Map集合的key不允许重复,而且Map不会保存key加入的顺序,因此这些key可以组成一个Set集合;另外一组是value组成的集合,因为Map集合的value完全可以重复,而且Map可以根据key来获取对应的value,所以这些value可以组成一个List集合。
  44. Map接口提供了get(K key)方法允许Map对象根据key来取得value;List接口提供了get(int index)方法允许List对象根据元素索引来取得value
  45. ArrayList使用了transient修饰了elementData数组。这保证系统序列化ArrayList对象时不会直接序列化elementData数组,而是通过ArrayList提供的writeObject、readObject方法来实现定制序列化;但对于Vector而言,它没有使用transient修饰elementData数组,而且Vector只提供了一个writeObject方法,并未完全实现定制序列化。从序列化机制的角度来看,ArrayList的实现比Vector的实现更安全。
  46. List代表一种线性表的数据结构,ArrayList则是一种顺序存储的线性表。ArrayList底层采用数组来保存每个集合元素,LinkedList则是一种链式存储的线性表。
  47. ArrayList性能总体优于LinkedList
  48. Iterator是一个迭代器接口,它专门用于迭代各种Collection集合,包括Set集合和List集合
  49. 对于JVM的垃圾回收机制来说,是否回收一个对象的标准在于:是否还有引用变量引用该对象?只要有引用变量引用该对象,垃圾回收机制就不会回收它。
  50. 程序创建一个对象,并把这个对象赋给一个引用变量,这个引用变量就是强引用。
  51. 程序在运行过程中会不断地分配内存空间,那些不再使用的内存空间应该即时回收它们,从而保证系统可以再次使用这些内存,如果存在无用的内存没有被回收回来,那就是内存泄露。
  52. 垃圾回收机制主要完成:跟踪并监控每个Java对象,当某个对象处于不可达状态时,回收该对象所占用的内存;清理内存分配、回收过程中产生的内存碎片。
  53. 当需要使用字符串,还有Byte、Short、Integer、Long、Float、Double、Boolean、Character包装类的实例时,程序不应该采用new的方式来创建对象,而应该直接采用直接量来创建它们。
  54. 使用StringBuilder和StringBuffer进行字符串连接,如果程序使用多个String对象进行字符串连接操作,在允许时将生产大量临时字符串。
  55. 尽量少用静态变量,如果某个对象呗static变量所引用,那么垃圾回收机制通常是不会回收这个对象所占的内存。
  56. String java  = new String(“crazyjava”);创建了两个字符串对象,一个是”crazyjava”直接量对应的字符串对象,一个是new String()构造器返回的字符串对象。
  57. Java程序整创建对象的常规方式:1.通过new调用构造器创建Java对象;2.通过Class对象的newInstance()方法调用构造器创建Java对象;3.通过Java的反序列化机制从IO流中恢复Java对象;4. 通过Java对象提供的clone()方法复制一个新的Java对象。
  58. 对于字符串以及Byte、Short、Integer、Long、Float、Double、Boolean、Character这些基本类型的包装类,Java还允许以直接量的方式来创建Java对象。
  59. 对于Java程序中的字符直接量,JVM会使用一个字符串来保存它们;当第一次使用某个字符串直接量时,JVM会将它们放入字符串池进行缓存。在一般情况下,字符串池中字符串对象不会被垃圾回收,当程序再次需要使用该字符串时,无需重新创建一个新的字符串,而是直接让引用变量指向字符串池中已有的字符串。
  60. 如果程序需要比较两个字符串是否相同,用==进行判断就可以了;但如果要判断两个字符串所包含的字符序列是否相同,则应该使用String重写过的equals()方法进行比较。
  61. 当一个算术表达式中包含多个基本类型的值时,整个算术表达式的数据类型将发生自动提升:所有byte型、short型和char型将被提升到int型。
  62. Java多线程:1.继承Thread类来创建线程类,重写run()方法作为线程执行体;2.实现Runnable接口来创建线程类,重写run()方法作为线程执行体;3.实现Callable接口来创建线程类,重写call()方法作为线程执行体
  63. 任何线程进入同步方法、同步代码块之前,必须先获取同步方法、同步代码块对于的同步监视器
  64. Switch语句的表达式而言, 只能人如下5种数据类型:byte、short、int、char、enum。绝不能是String类型。
  65. 构造器是Java每个类都会提供的一个”特殊方法”。构造器负责对Java对象执行初始化操作,不管是定义实例变量时指定的初始值,还是在非静态初始化块中所做的操作,实际都会被提取到构造器中被执行。
  66. 构造器并不创建Java对象,构造器只是负责执行初始化,在构造器执行之前,Java对象所需要的内存空间,应该说是由new关键字申请出来的。
  67. 对于使用private修饰符修饰的方法,只能在当前类中访问到该方法,子类无法访问父类定义的private方法。所以子类无法重写父类的该方法。如果子类中定义了一个与父类private方法具有相同方法名、相同形参列表、相同返回值类型的方法,依然不是重写,只是在子类中重新定义了一个新方法。
  68. 非静态内部类不能拥有静态成员
  69. 被static修饰的成员(Field、方法、内部类、初始化块、内部枚举类)属于类本身,而不是单个的Java对象。静态方法属于类,而不是属于Java对象。
  70. 静态内部类不能访问外部类的非静态成员。
  71. 当系统执行到程序中catch块代码里的return语句时,依然会去执行对应的finally快。
  72. 不论try块是正常结束,还是中途非正常地退出,finally块确实都会执行。但是如果System.exit(0)将停止当前线程和所有其他当场死亡的线程。Finally块不能让已经停止的线程继续执行。
  73. 只要Java虚拟机不退出,不管try块是否正常结束,还是遇到异常非正常退出,finally块总会获得执行的机会。
  74. 当Java程序执行完try块,catch块时遇到了return语句,return语句会导致该方法立即结束。系统执行完return语句之后,并不会立即结束该方法,而是去寻找该异常处理流程中是否包含finally块,如果没有finally块,方法终止,返回相应的返回值。如果有finally块,系统立即开始执行finally块----只有当finally块执行完成后,系统才会再次跳回来根据return语句结束方法。如果finally快里使用了return语句来导致方法结束,则finally块已经结束了方法,系统将不会跳回去执行try块,catch块里的任何代码。
  75. 在try块后使用catch块来捕捉多个异常时,程序应该小心多个catch块之间的顺序:捕捉父类异常的catch块都应该在捕捉子类异常的catch块之后。
  76. 子类重写父类方法时,子类方法只能声明跑出父类方法所声明抛出的异常的子类。
  77. 线性表是由n(n>=0)个数据元素(节点)a1, a2, a3….an组成的有限序列
  78. 遍历二叉树指的是按某种规律依次访问二叉树的每个节点,对二叉树的遍历过程就是讲非线性结构的二叉树中的节点排列在一个线性序列上的过程。
  79. 排序是对一组任意的数据元素(或记录)经过排序操作后,就可以把它们变成一组按关键字排序的有序序列。
  80.  
原文地址:https://www.cnblogs.com/bluescorpio/p/2623945.html