深 入JAVA虚拟机
1. java虚拟机体系结构
2. 运行时内存区域中的程序计数器:就是当前线程所执行的字节码的行号指示器。字节码解释器通过改变这个计数器的值来选取下一条语句。执行多线程其实是通过线程轮流切换并分配处理器执行时间的方式来实现。每个线程都有自己对应的计数器记录执行的位置,线程之间独立存储,这类内存存储区叫“线程私有”。
3. 若为java方法则记录正在执行的虚拟机字节码指令的地址。若果是native方法(非java代码的接口)则计数器为空。
4. Java虚拟机栈(线程私有):每个方法在执行时都会创建一个栈帧用于存储局部变量表(在编译期就确定的可知的基本类型对象引用(指向对象的地址)等,运行期间不会改变大小)、操作数栈、动态链接、方法出口等信息。方法的执行过程就是栈帧在虚拟机栈中入栈出栈的过程。
5. 本地方法栈:与虚拟机栈处理的作用相似,就是处理的Native方法。
6. Java堆是所有线程共享的内存区(最大),在虚拟机启动被创建,几乎所有的对象实例均在这里分配内存。是垃圾回收的主要区域。
7. Java方法区(“非堆”为了区别java堆):也是各个线程的共享区域,它用于存储虚拟机加载的类信息、常量、静态变量、即时,编译器编译的代码等。
8.
Thinking In JAVA
1.比较两个对象的值是否相等,用equals方法,但是如果两个对象的实例类是自定义的类,就需要自己覆写equals方法;
2.程序运行加载的起点是main函数。
3.==与!=只是比较基本类型的若比较引用其实是比较的地址。
4. 基本类型除外,创建对象、数组然后用一个量指向他,这个量在JAVA中就是引用,引用指向这个对象的地址,引用之间的赋值(做 = 运算符),其实是将另一个引用也指向了这个地址,注意通过任何一个引用都能改变这个对象内容。因为他们指向一个对象,后面的单例设计模式和共享对象都是这个原理,让许多引用都指向一个对象。
5. Java参数传递:java只有按值传递 1为基本类型时,传递为值的副本,所以在方法中改变形参值实参不变2当传递对象时,也是值传递,传递的为引用的副本,形参实参的引用不是一个,但都指向一个地址,所以改变形参(副本)引用的指向,是不改变地址内容和实参引用指向的地址的,形参引用和实参引用不是一个引用。
在 C++ 中当传递给函数的参数是引用时,您传递的就是这个引用,或者内存地址(按引用传递)。在 Java应用程序中,当对象引用是传递给方法的一个参数时,您传递的是该引用的一个副本(按值传递),而不是引用本身。
6.在方法中的成员变量系统不会用默认值初始化,而在类中的属性会被默认初始化,开发者为了不产生不必要的错误;
7.传递参数时:a++:先传递进去a然后再自加,++a: 先自加然后再传递进去参数。b=a++与b=++a在使用时一定要注意是要赋给b a的值还是自加之后的值;
8.if else的每个分支一次只执行一个,要是有返回值时候,每个分支中都要有返回值,否则会出错,特别是在递归当中。
深层次递归return 只能够退出当前层不能退出整个函数,所以有返回值的时候就要依次向外传,层层接力。
9.Java中不会将整数转化为boolean类型,在C++可以,所以while(1)是错的,3&&4也是错的因为&&两边必须是boolean类型的结果;
10. 强制类型转换,就是为了防止数据丢失(能容纳更多数据的类型向容纳更少数据的类型转换),boolean类型不能做任何的类型转换;
11. Float类型 因为java对小数都是默认为双精度double的,所以定义float a=0.5f 后面的f一定要加上,否则类型转换会出错;
12. 随机数生成器的Rand构造时若是加入 随机数种子则每次运行与上次运行结果(产生的随机数一样),但是与产生的随机数的范围没关系,但在一次运行中连续调用多次nextInt()方法得到的随机数数不一定一样。
13. Break只退出外层循环。如果想退出外层循环可以使用标签。标签是紧跟在循环语句之前的带有“:”的标示符。注意标签和循环语句之间不能有任何语句;break outer;可以退到outer标签处并且不会再进入循环;continue outer(使用这个时要注意迭代变量的取值防止出错);退到outer标签处再继续下一次循环,循环变量在原来的基础会自加,然后判断表达式。
14. 基本的类型转换,类型越高数据容量越大
15. 基本数据类型的优先级:
Char
int—long——float——double 从左到右依次递增
Byte—short
Char只能向括号右边的类型转换,至少是int
16. 重载方法可以通过参数列表和返回值类型区分;
17. 若自定义了构造器,则系统不再会提供默认的无参的构造器,若是用 new People()注意是否存在无参构造函数
18. This代表当前对象的引用在内部使用,可以返回本对象和作为对象参数,使用this(参数列表)可以调用构造器;注意在构造器中只能调用一个this()构造器,并且在构造器中不能使用类名+参数列表 的方法调用构造器,只能用this,在除了构造器的其他方法中不能调用任何构造器;
19. 任何类中的成员变量不管要调用构造器的初始化还是方法中的初始化,其在类的构造器之前会系统默认进行初始化成员域。有效地防止了因为构造器未对某个元素初始化而产生错误(相对于那种执行构造器但不默认初始化的情况)。总是会:先执行成员变量的初始化再执行构造器。
20. 静态方法或者静态成员只是在第一次加载.class文件的时候初始化一次,其他再次创建此类对象的时候将不会再次初始化。加载完.class并对静态初始化,然后再为要创建的 对象在堆上分配空间,然后对其他非静态初始化;(简单的说就是类(.class)刚加载的时候静态方法与静态域和静态块就加载了),先加载类再创建对象。静态的内容与单个对象的创建是两个过程。静态的内容属于整个类。
21. 静态方法中只能访问静态域,因为非静态的在加载类的时候还没有被加载,找不到此变量,自然出错。当使用类名调用静态时,静态均初始化,非静态的不会初始化,非静态当创建某个特定的对象时才会初始化。静态域不能接受非静态函数的返回值,还是上面的原因。如 static int a=f();f为非静态的。
22. 注意静态块与非静态块中声明的变量都是局部变量,若想用于全局可以再静态域声明。
23. 非静态块与非静态域是初始化的时间是一致的,有几个对象就分配几次空间,就初始化几次非静态块。此可以用于匿名内部类,当仅用new People()就可以执行输出一部分内容,成员域非静态块的初始化是在构造器之前的。
24. 非静态内容只有在为某个特定的对象分配内存空间时才会进行初始化(也就是有 new 分配时),只用类名时只是加载.class文件,加载静态,静态块和飞静态块可以以看成没有返回值的方法,当构造一个对象时某一个功能必须执行(例如数据库操作的加载数据库驱动,在一个类中加载驱动的方法,1自己加载2通过调用导入类的加载驱动方法(最好静态块因为不用调用)只要是在此类体重执行一次加载代码就行)也可以用方法每次建立对象就手动调用此方法(比较麻烦),也可以写在块中使其在对象初始化的时候就自动加载。(静态属于每一个对象)
25. 数组也是一种对象只有在初始化的时候才会分配内存空间。数组之间的复制其实是引用的复制,即地址。
26. 数组创建 int[]a={ };int []a=new int[lenth](lenth必须是大于等于0的数);int []a=new int[]{1,2,3}(注意:此时的[]不填数字)
27. 可变参数列表: f(Object...as)中间以三个点的形式,实质上是一个Object数组(与f(Object[]as)不同,这个的实参只能是数组),f()中实参可以是参数列表如(1,2,1)
此时系统会将此列表的元素自动包装成数组,也可以是一个数组,若只有一个数组则不会再将其转化为数组,只要除了一个数组参数外还有别的参数,就会将此列表转化为数组。
28. 可变参数列表中还可以加入,一个非可变参数,1.f(非可变参数,可变参数列表),与2.f(可变参数列表)如果他俩的可变参数列表的类型一样,注意如果有的调用这两个函数都可以匹配(可以通过自动转型),那么就要避免这种二义性错误,为一二个方法加上非可变参数,使之完全匹配。
29. Main方法也是静态方法,在本类中,其中不能直接使用非静态的内容,因为加载顺序的问题,会导致找不到声明,若想能加载到非静态方法就必须为对象分配空间,使用new创建对象,而静态内容可以直接使用,不用创建对象。
30. 枚举类型声明 enum 名字{}与类的声明差不多,{}中写此枚举中含有的常量,都用大写字母;
31. Import static 导入,静态导入只能导入指定类中的静态内容(只能为静态域或静态方法如import static java.lang.System.out;指定到静态方法(域)名否则编译出错),可以将此包中的静态内容当做自己的静态内容直接调用,而不是使用 <导入的类名>.<静态内容>的方式,
32. 当不指定访问权限时,就是包访问权限。具有包访问权限的类、成员,不管要访问他的类继承还是导入他,只要不在一个包下就不能访问。(<类名/对象名>.<方法>的形式也不能访问)本质就看在不在同一个包。
33. Protected访问权限即在包访问权限的基础上加上如果继承时不在一个包内也可以访问。当不在同一个包时,只有通过继承的方式(特别注意什么是体现继承的方式并不是简单地在子类中访问要排除通过创建父类对象访问的情况)才能访问父类的protected成员,若在子类中直接创建父类对象访问此成员(不在一个包中,若在一个包中肯定能访问)则不能访问,因为没有体现一点继承关系。
34. 在子类中访问父类Protected成员的方法: 在同一个包下 当不在相同包时,使用能体现继承关系的访问方式 若 父类是A子类是B 在B中这样 new A().f()访问则会出错因为直接创建父类对象没有体现继承,要是不加类名直接用方法名则可以体现继承关系,但注意静态方法中不能访问非静态方法。
35. 类的访问权限只能是:包和public。因为private把自己独立起来没有意义,protected的继承此类的时候不在一个包中也不能继承,所以和包一样了。
36. 当不想让外界通过new 创建对象时,可以将构造器设为private,这样外界不能通过new ,只有一个方法能创建对象,就是在静态方法中返回,因为这样可以直接用类名调用。(单例设计模式就是这样)
37. 注意;私有成员在类的外界不能访问,只能在类的声明中访问。
38. 单例设计模式:将构造器设为私有,不能由外界直接new创建,唯一可以通过静态方法返回一个此对象。这时此静态方法返回的对象是私有静态域声明创建的对象,因为静态域的初始化只在第一次加载类的时候初始化,其他的时候,就不初始化了,创建的对象还是以前的对象,所以返回的都是那个内存地址的对象。只是引用的名字变了。如果不是静态域创建对象,而是在静态方法中创建并返回此对象,因为加载.class的时候并不执行方法,每次调用都会执行new所以返回的并不是一个对象。上面的静态域中只会执行一次new初始化一次;
39. 若想为某一个对象赋值,可以将引用A复制给另一个引用B,通过改变引用B就能改变那个对象。
40. 继承总是会先初始化父类,在子类(导出类)的构造器中没有显示调用父类构造器时就自动调用父类无参的构造器,此时,要保证父类中含有无参的构造器,并且调用父类构造器必须写在子类构造器的第一行。调用父类的其他方法不用必须在首行。
41. 进行清理(c++中的析构),调用顺序按照初始化的相反顺序,先成员对象最后是基类(父类),因为父类先初始化。防止一个子类对象依赖于另一个子类对象?若先销毁父类的,若果子类中有父类的域或方法就会找不到声明和定义出错。
42. 垃圾清理的finalize函数一个对象只能调用一次。
43. 在C++中子类与父类的同名(只要名字相同就可以参数列表不管怎么样)的方法在子类中会把父类的此名字的方法覆盖,而在java中子类父类两个层次间依旧可以重载。若果想覆盖父类的方法 必须返回值与参数列表都必须一样,所以java中提供了@Override注解(表示此方法必须要覆写),如果你想覆写却重载了就会提示出错。
44. 继承其实是隐式的组合(隐式的在类中存放子类对象),“是一个”用继承,“有一个”用组合。组合一般用于想在新类中使用现有类的功能(在内部使用)而不使用其接口(public方法)。
45. 向上类型转换:子类转化为父类。这是可以的因为子类的方法比父类多,所以转化可能会发生方法丢失但是不影响方法的执行。而父类转化为子类就因为少方法变得不可行,多可以但少不可以。与基本类型的容量小的可以向容量大的转换 不造成数据丢失,相反。
46. 继承之后的子类中调用方法时包括重载的,想象成把父类中方法在子类中复制了一份一摸一样的,放在子类中分析简单。(多态的问题)注意:子类父类两个层次的方法是可以重载的父类的就是子类的。
47. 当继承中覆写子类方法时,只有使用父类名(对象)+方法名(而且这对象不能是由子类转型来的),才能调用到父类的版本。
48. 空白final:在定义的地方未初始化。(不能是static类型)。空白final是在构造器中初始化的,若为static则可以在调用构造器前使用,此时没有初始化值会报错(因为final不会系统默认初始化)。所以static类型的final必须在定义时初始化。Final类型的成员不会默认初始化,空白final一定要在构造器中赋值。非空白final不能再在构造中赋值。Final一经赋值就不能改变。
49. final其实也是成员变量的一安全机制,同private一样。
50. Static final的成员域:若是编译常量(不是要经过计算得到的),不进行初始化就可以读取。如 System.out.println(Initable.staticFinal);先输出47,然后对其他静态成员初始化。非常量的必须先初始化。(初始化包括系统默认的初始化和自定义的初始化,final类型不进行默认初始化)。
51. Final类型的成员域(系统不会默认初始化):常量。没有static修饰词的成员域要在定义时初始化或者在构造器中初始化,为了在使用前初始化。注意:final成员域一般为基本类型且在初始化或在第一次赋值后不能再改变值,若为final引用则表示引用指向的对象(地址)不能改变(注意是不能改变指向的对象)但是对象中的内容可以改变。所以与常量不相符,一般不用。
52. 总之final类型成员域只要保证在使用前初始化了就行。
43..final参数表示无法在方法中改变参数引用指向的对象。
45.覆写::只有在此方法是父类的接口一部分的时候才会出现。
覆写只是对于父类中非private的方法来说。Private修饰的方法不是接口(接口:能在外界访问)。
53. final类型的方法表示在继承中不能被覆写,更重要的是有效的关闭“动态绑定(后期绑定或运行时绑定)”。Private修饰方法时相当于没有定义此方法。所以用@Override覆写时会报错因为就没有这个方法怎么覆写。加上final与不加一样。
54. final的类不能被继承。
55. 判断是否需要使用继承:问自己是否需要向上转型?子类要在外界使用父类的接口吗?。
56. 含有继承的初始化顺序:1:加载.class文件(无论建造多少对象.class只加载一次)。Static的初始化就是在加载class的时候进行的。依次完成对基类和子类的static的加载 2:开始创建内存空间,先初始化根基类的成员变量,再调用根基类的构造器。 3载一次向下面的到处类执行2过程初始化本类成员在调用本类构造器。注意:在.class类加载的时候就把所有static加载完成
57. 方法被覆写用子类对象就调用到子类中的版本。
58. 子类的对象也是父类的一种实例
59. 工厂设计模式:每次调用next方法就会产生一个对象并返回
60. 动态绑定(多态)前提子类将父类的非private方法覆盖了(若父类中的方法为private则这就不是覆写也不会发生多态):当将子类的对象赋值给父类的引用时,编译器会自动绑定此引用到实质指向的对象,当通过这个父类的引用调用方法时,就会调用的相应的实际版本,因为是父类的引用所以先在基类找此方法,再看实际指向的对象中是否覆写此方法,若覆写了则调用实际指向对象中的此方法。
61. 不会发生多态的情况:1:没有覆写2:静态成员域成员方法与非静态成员域都不会发生多态。多态与对象关联。而静态方法与类关联。多态与个人有关。
62. 静态方法的使用时机:如果某个方法是用频率较高,或者方法本身通用性较强,无需使用类成员变量,则可以使用静态方法,那样方便,速度也快.
63. 在静态方法中不能使用非静态的东西。!!加载次序的原因。
64. 若有main方法.class的加载是在main执行之前,main中没有代码也会加载类,因为main是编译的起点。执行他会加载类。
65. 初始化的本质就是保证在使用之前初始化。因为子类可能用到基类的所有东西所以先初始化基类成员域执行构造方法。因为.class只加载一次所以父子类static首先按顺序加载完。勿忘:不管基类还是导出类都在调用构造器前初始化成员域。
66. 基类的清理要调用基类的清理函数,一般private的域子类也是访问不到的,由子类在最后一行代码处调用。先清理子类的,因为基类的东西可能在子类清理的过程中用到。所以使基类中的构件扔起作用不要过早的销毁。
67. 共享对象:一般为用共享对象作为多个被共享者的构造参数如newcomposed(shared) 用一个shared创建多个composed对象,shared为共享对象,含有引用计数,static count记录创建了多少个对象(创建一个count就加1),每个对象都有被共享的次数recCount,被共享者每共享一次(创建一次对象)就使recCount加1。在构造器中实现。
68. 清理时,注意共享对象的情况,此时要使用引用计数来跟踪被共享者(共享此对象的对象)的数量,先清理共享他的对象,数量到零时清理共享的对象。因为早早的清理了共用的东西会导致后面的对象无法使用。
69. 多态的一项错误使用:创建子类对象时,会先初始化父类,若此时在父类的构造器中调用动态绑定的方法(在子类中覆写的方法),由于覆写多态,可能会调用到子类中的方法,方法中若使用了子类的成员域就很会出现错误的值,因为此时成员域还没有被初始化,在初始化之前所有的都是二进制的零。
70. 用继承表达行为间的差异,用字段表达状态上的变化。
即:通过继承产生不同的子类,子类拥有不同的行为,运用组合对象作为字段改变状态。
71. 将生活中的实际物体,对象化,抽离出共性与异性,然后有机的联系起来,并将客观世界的物理表现,抽象到面向对象的程序设计中。——焦明亮
72. 向上转型后,只能调用基类中存在的接口,那些子类中扩展的接口就不能访问了。若访问写出错
73. Private修饰方法的时候不能在外界调用相当于没有此方法,所以写一个与之一模一样的方法只是修饰词不同就不叫覆写(顾名思义:把以前的覆盖了,以前根本就没有),private不是接口。若只有final 则写以上一模一样的方法会报错,不允许覆写,此时就叫复写了,但不允许。
74. 多态:向上转型并调用基类中的通用接口,加上不同版本的动态绑定方法,实现不同的形式。
75. 要初始化才能使用如一些分词系统和一些数据库的加载驱动最好初始化和操作部分和退出部分(释放资源)最好放在三个函数中,防止调用次数过多而导致内存不足。这样多次调用只执行一次初始化和一次释放资源。特别注意:放在static中的之初始化一次一旦释放资源,再创建对象也不能是已经初始化的状态了了。
76. 基类中若有抽象方法则父类不能向下转型;
77. 接口是完全抽象类,由abstract class——>interface,
接口中所有的方法都是抽象方法,并且可以有成员域,但这些域隐式的是public、static(因为接口不能创建对象若不是静态的则不能使用)和final(因为(不通过子类)若想修改接口的域,必须调用方法,而接口中的方法都是抽象的,这样就不能修改了,所以干脆就默认为final)的;
78. 抽象类与接口的区别:抽象类中的抽象方法要加abstract修饰词,而接口中的抽象方法不用加只声明出就表示为抽象方法,因为interface关键字
79. 打印本对象System.out.println(person/this);就是自动调用里面的toString方法
80. 一个字符串数组String a[]若是直接输出数组时,则打印地址,使用a.toString()也会打印出地址,若想打印出里面的内容则使用Arrays的静态方法 Arrays.toString(a),参数为数组引用,这是把数组转化为字符串的形式。
81. 接口中的抽象方法必须都被实现,若没有直接实现的接口,若是此时也继承了一个类,这个类有这个抽象方法的实现那也可以;
82. 类继承接口用implements,接口继承接口用extends
83. 策略的设计模式其实是接口上的多态
84. 适配的设计模式就是为了加入适配器(也叫中间人或代理)使一个没有继承通用接口的类,也实现接口上的多态。
85. 工厂的设计模式:就是接口多态的使用,用于一个棋盘实现下象棋和围棋。(同一个接口不同的实现)
86. 适配接口:一个接受接口类型的方法,而该接口的实现和向该方法传递的对象则取决于,实际方法的使用者。常见用法:策略设计模式(只要你的对象遵循我的接口(覆盖我的方法),你可以使用这些对象调用我的方法)其实质就是抽象接口上的多态。但更灵活:如果某个类想通过一个通用的方法实现自身方法,但又不继承某个这个通用接口,那么就可以创建适配器,创建一个代理,继承自这个类和那个通用接口。若这个接口是类,因为JAVA只能继承一个父类(可以多个接口),那么就只能继承一个类,就不能把这两个类(通过继承)联系起来(只有通过继承才能实现多态)。用于:要实现多态的类没有继承某个接口。(中间人:继承这个类实现这个类的方法的复制,继承接口的可以发生向上转型)
87. hasNext()与next()方式的迭代,里面有一个值个数的计数器。hasNext就是看是否大于零就是有值,next是返回值,并且计数器自减。
88. 接口中的域都是public static final的所以不能是private,特别是当接口中嵌套接口时,接口里面的东西都不能用private。接口中的域可以被非常量的表达式初始化。
89. 因为接口的域都是final static的所以可以用于制作常量组。也就是枚举类型。
90. 类中的嵌套接口若是私有类型的,类中若还有一个返回此接口的方法,那么在外界调用这个方法,只有把结果交给有权限使用这个接口的方法才不会出错,否则会出错。
91. 非静态的final成员域,一个对象只能初始化一次。final int a=count++;是可以的不同的对象可以初始化不同的值,动态初始化。
92. ArrayList容器都自动保存为Object对象,所以获得里面的元素要记得转型。转型其实是将引用转型,其指向的对象实质上还是原来的对象,只是存放引用的类型是Object。
93. 若是容器中使用了泛型List<String>括号中存放的就是泛型,则只能向容器中存入括号中类型(或能发生向上转型(泛型的子类))的对象。取出对象就不用类型转换
94. foreach(for循环的简化写法)语法可以不用索引进行循环。
95. List中能含有重复元素,Map中key唯一,若再次插入已有的键,以前的值会被覆盖。Linked类型的容器无论怎么修改都是初始插入的顺序,即使使用Map的put(key,value)方法,key为本身存在的key,执行之后顺序也不会变,对于LinkedHashMap还是以前插入的顺序,对于HashMap还是原来散列码的排序,只是value变了。
96. List和Map其实是一种通用接口。像LinkedList、ArrayList就是对List接口的实现,但是ArrayList中有List方法中未包含的方法,TreeMap也有Map中没有的方法。
97. Set不允许加入重复元素,调用add()若加入成功则返回true
98. Collection(的引用指向一种容器)是接口(Map容器没有实现他)而Collections是类二者不一样。Collection.addAll(Collection<>)为自己加上Collection导出类对象的所有元素;Collections.addAll(Collection a,可变参数列表)为参数a这个序列加上可变参数列表的内容,注意可变参数列表不能是Collection类型。注意:上面的Collection是指的容器(List Set等)
99. List a=Arrays.asList(1,2,3,4,5);虽然返回一个List类型但是也不能在原基础上直接进行添加删除元素,会报错,因为使用的是可变参数合成数组后的原地址,其底层是数组,可能引起改变数组尺寸变化,这是不允许的。若想增加元素,可以使用new ArrayList(Arrays.as(参数列表))),使用new就代表新生成地址。(实质: Arrays.asList是返回一个ArrayList()容器,其构造参数就是aslist的数组参数,使用数组构造容器是内部使用的一个构造方法,将数组直接赋给了他的成员域(他是final类型),所以不能改变长度)
(snow是根基类,light、heavy是他的孙子)List<Snow> snow2 =Arrays.asList( new Light(), new Heavy());编译出错因为右边只知道其父类是power所以直接产生power的链表,若左边有泛型说明则左右两侧必须一致,左边没有泛型标注,则不会出错,注意整个链表是不会发生泛型转换的,只能单个对象可以发生向上类型转换(List<Snow>snow4= Arrays.<Snow>asList(new Light(),new Heavy());这种就可以在右边加上“线索”)。但是 List<Snow> snow3 = newArrayList<Snow>();
Collections.addAll(snow3, new Light(),new Heavy());因为这个函数会先看第一个参数链表为snow的所以单个对象会转换。这两种方式前一种只看右边,后一种先看要添加元素的链表。
100. HashSet(HashMap)(取出的元素没有顺序):采用复杂的方式进行存储,是最快的获取元素的方式。LinkedHashSet(LinkedHashMap):采用插入的顺序存储,保留了HashMap的查询速度:TreeMap(TreeSet):采用升序的方式存储。
101. 在迭代器中可以移除容器中的元素。Iterator的remove移除的是next()产生的最后一个元素。移除元素前必须先调用next。迭代器保证里面的元素都与泛型标注的类型一致,或不加泛型标注。迭代器统一了容器的访问方式。
102. Iterator只是从前向后迭代元素。而ListIterator可以从后往前迭代用previous。并且在初始化的时候可以选择从索引为n的地方开始迭代(list.iterator(n)),并且使用set()可以替换访问了的最后一个元素前面要有next()和previous(),当调用这两个函数时可以认为指针移向了,当前与下一个(或前一个)元素之间,以此来判断previousIndex()与nextIndex()的值。分别为前一个元素和下一个元素下标。
103. LinkedList添加了可以使其用作栈、队列或双端队列的方法。
新增加的方法:
返回第一个元素:getFirst()与element(),list为空时 会出现异常。Peek()链表为空时返回null
移出列表的第一个元素并返回:removeFirsst()与remove(),
移出列表的最后一个元素并返回:removeLast()
为空时出现异常。Poll(),为空时返回null
向列表的头部插入元素 :addFirst()
向列表的尾部插入:add()和addLast() offer()
104. 注意迭代器中的add()是在ListIterator(只用于List容器类型,他可以双向访问,可以选定开始迭代的索引)迭代开始处插入。
Iterator(适合所有(除了map,因为键值对)容器)没有这个方法,他只有简单的next hasnextremove
105. 栈(stack)其实是利用LinkedList构造出来的,只是在pop、push中调用LinedList的方法。因为LinkedList中包含有栈队列和双端队列的方法。(Stack类用组合LinkedList的方法实现,因为他不需要用LinkedList的接口所以不用继承)
106. 队列(Queue)是一个接口,LinkedList是对Queue接口的实现,使用Queue a=new LinkedList<>(),构造一个普通队列。PriorityQueue(类)是优先级队列,添加进去元素会按优先级排序,默认是自然增,可以使用 new Priority(int initialCapacity, Comparator<?super E> comparator
)第二各参数为自定义比较器。(与普通队列多了排序)
107. Set容器关键用法使用其contains()方法,测试某字母或字符串是否包含在其中。
108. 创建能装任何类型的List容器,使用List list=Arrays.asList(参数列表),但是这样做没有意义只要都不指定泛型就可以任意创建。Arrays.asList()是生成list的一个重要方法,方便。
109. List list<? ExtendsPet>此泛型表示,只要继承自Pet的类就行。
110. Map容器中有ContainsKey()和containsValue()的方法,keySet()返回的是Set容器。
111. 使用split分割句子时,若单词之间有空格 换行标点符号等可以用\\W+ 正则表达式分组,表示以非字母数字下划线的一个或多个分割。(加号不要忘)
112. 不同类型的容器类型转换也会出错,用一个容器构造另一个容器,不能简单地做“=”,要用new 构造器构建。
Map容器保存键值对的形式,List和Set容器也可以保存为键值对,其实是把每一个元素变成了键值对元素,使用泛型为Map.Entry<Object,Object>其实这每一个元素都是一个键值对。 List<Map.Entry<Integer,String>> set=newArrayList<Map.Entry<Integer,String>>(map.entrySet());(map是一个Map子类的实例entrySet()作用将每一个键值对抽离出来形成一个Map.Entry类型的元素并把所有元素放在Set容器中)
能使用这种泛型的有 LinkedHashSet、HashSet、ArrayList和LinkedList,注意TreeSet不能使用,因为TreeSet中有比较器排序(递增),Map.Entry<>类型不能在比较器中使用,所以会发生类型转换错误。
113. Foreach Map的方法:
for(Map.Entrys:map.entrySet())
s.getKey();s.getValue();实质是转化为Set容器
114. Collections.sort()只对list容器排序
115. 若一个类继承自ArrayList<String>,便有了起的所有方法,别忘了在构造函数中,super()父类,初始化里面的内容。
116. 容器中实现对元素存储的数据结构为Entry<E>是一种数据结构?
117. Foreach语句可以用于数组和任何Iterable(自己检验若直接返回iterator出错,而返回Iterable则不出错所以是用于Iterable)。数组不是Iterable。能迭代或foreach的本质实质是实现Iterable接口,Iterable中的唯一方法返回一个Iterator类。
这里讨论费数组的foreach:
for(String s:list)list实际是list中Iterable接口的的隐式使用。若自定义书写逆向迭代,一定要实现Iterable接口,还不能覆盖以前的向前迭代方法,就要自己使用内部类的方法返回一个Iterable接口并实现其抽象方法。在一个方法中返回Iterable。如reversed();则for(String s:list.reversed())未显示调用则自动使用默认的迭代器。显示调用自定义的(返回Iterable的)迭代器。
118. 与foreach不同,使用迭代器 it=a.iterator()的方式。程序员只要覆写(返回)一个Iterator类就行。
119. a为数组,Listlist1=Arrays.asList(a)和List list2=new ArrayList(Arrays.asList(a)),list1直接使用Arrays.asList
的结果,使用的为原地址,修改list1会修改a,list2是创建了新的数组容器 将返回的结果包装,是使用的新地址。不会改变a。注意 a若为字符串,与a.split()的地址不一样
120. 字符串String具有不可变性,改变的只是引用,原来地址的内容不会变。
121. 当在toString()方法中打印本对象地址时,应该调用super.toString()方法(即Object的,调用者为本对象,本对象继承自Object,所以里面的toString也是自己的方法,里面的用类型信息调用getName肯定为子类的名字),而不是使用this,打印this是调用本身的toString方法,递归调用出现错误。Object的toString就是打印当前对象的地址。
122. 正则表达式:正则表达式输出一个“\”用,“\\”在java中表示出此正则表达式用,“\\\\”两个反斜扛表示转义出一个\。
123. 正则表达式去除标点符号:使用如\\pP,其中的小写 p 是 property 的意思,表示 Unicode 属性,用于 Unicode 正表达式的前缀。大写 P 表示 Unicode 字符集七个字符属性之一:标点字符。其他六个为:
L:字母;
M:标记符号(一般不会单独出现);
Z:分隔符(比如空格、换行等);
S:符号(比如数学符号、货币符号等);
N:数字(比如阿拉伯数字、罗马数字等);
C:其他字符
124. Matcher的find(),搜索完一个会接着向下搜索,而find(int i)会根据参数的i重新设定起始位置。不是接着来。
125. 在Pattern.MULTILINE(?m)的模式下^:表示每行的开头 $:表示每行的结尾。注意文本中的换行符。
126. 正则表达式的模式:
1.CASE_INSENSITIVE:启用不区分大小写的匹配。
默认情况下,不区分大小写的匹配假定仅匹配 US-ASCII 字符集中的字符。可以通过指定UNICODE_CASE标志连同此标志来启用 Unicode 感知的、不区分大小写的匹配。
通过嵌入式标志表达式 (?i) 也可以启用不区分大小写的匹配。
指定此标志可能对性能产生一些影响。
2.COMMENTS:模式中允许空白和注释。
此模式将忽略空白和在结束行之前以 # 开头的嵌入式注释。
通过嵌入式标志表达式 (?x) 也可以启用注释模式
3.MULTILINE:启用多行模式。
在多行模式中,表达式 ^ 和 $ 仅分别在行结束符前后匹配,或者在输入序列的结尾处匹配。默认情况下,这些表达式仅在整个输入序列的开头和结尾处匹配。
通过嵌入式标志表达式 (?m) 也可以启用多行模式。
4.DOTALL:启用 dotall 模式。
在 dotall 模式中,表达式 . 可以匹配任何字符,包括行结束符。默认情况下,此表达式不匹配行结束符。
通过嵌入式标志表达式 (?s) 也可以启用 dotall 模式(s 是 "single-line" 模式的助记符,在 Perl 中也使用它)。
127. Find()在字符串的任意位置匹配正则表达式,。Matches()(区分matcher(str))只有整个字符串与正则表达式匹配时才返回true,lookingAt()只要字符串的从开头开始的部分匹配成功就返回true;
128. 正则表达式中Groups组的用法,整个正则表达式为0组,以后的每个括号中的部分分别是一个组。巧妙使用括号(组)获取需要的字符串。我们写表达式时可以将对我们游泳的部分用()括起来,到时候通过group(int i)获得。如“/\\*(.*)\\*/”,我们只要注释中间的文本,find()之后,用group(1);(直接匹配括号中的有用文本的不好写相应的表达式,但它外面包着的东西容易写表达式,这时就可以把里面的有用部分加上括号)
129. Matcher中的appendReplacement(StringBuffersbuf,String replacement),与find()和group()结合使用,调用一次,只替换一个地方,能实现查找后处理再替换的功能。注意第一个参数是StringBuffer类型,第二个参数为要替换成的字符串(此方法为Matcher调用所以替换的为find()到的字符串)。另外此方法的添加功能,是把每次替换后的末位置之前的字符串加入到sbuf中,最后执行Macher的appendTail(sbuf)功能把原来字符串的其余部分也加上。
130. Matcher的reset()方法,没有参数表示将Matcher对象重新设置到当前字符序列的起始位置。当参数为字符串时表示,将此对象(就是原来地址的那个)应用与一个新的字符串。注意这种方式最节约内存空间,不用再词创建对象,若使用Pattern的matcher()则会返回新的对象,只是以前的那个引用指向了他。利用这个方法,可以实现动态查找,一边从文件中读取数据,一边对每一行用表达式匹配。reset(str)重置字符序列 。
131. Matcher的replaceAll(str)方法其实是在内部实现。appendReplacement方法。所以会打印出未匹配到的序列。Matcher方法只是记录从0到匹配结束的区域。
132. StringBuilder和 StringBuffer的区别:
1. 在执行速度方面的比较:StringBuilder > StringBuffer
2. StringBuffer与StringBuilder,他们是字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,不像String一样创建一些对象进行操作,所以速度就快了。
3. StringBuilder:线程非安全的
StringBuffer:线程安全的
当我们在字符串缓冲去被多个线程使用是,JVM不能保证StringBuilder的操作是安全的,虽然他的速度最快,但是可以保证StringBuffer是可以正确操作的。当然大多数情况下就是我们是在单线程下进行的操作,所以大多数情况下是建议用StringBuilder而不用StringBuffer的,就是速度的原因。
对于三者使用的总结:
1.如果要操作少量的数据用 = String
2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
133. 创建对象的操作最好放在循环体的外面,因为循环一次创建一次空间比较占用内存。
134. 文件I/O中,File类的list()方法得到文件目录中的文件名 .后缀名(与getName()结果一样)String类型,使用listFile()则获得File类型,其获得绝对路径就是真实的物理路径。getAbsolutePath(),若文件已经是绝对路径就得到实际绝对路径,若不是则得到逻辑路径。
135. 总结对字符串的操作:
1.使用正则表达式的Pattern和Matcher
可以在指定字符串序列中,匹配相应的正则表达式。并完成复杂的替代工作。
2.使用Scanner对象
Scanner构造器可以接受任何类型的输入对象,包括File、InputStream、String或者Readable(一个就接口,表示具有read()方法的某种东西如BufferedReader)对象。
原理:对传入的字符串,先(默认空白符为定界符)分词,然后调用里面的nextInt()、nextDouble() nextLine()等方法所有的基本类型(除了char,String不是基本类型)都有相应的此方法。还有相应的hasNextInt()等方法,判断下一个分词是否是所需要的类型。定界符可以自己定义使用useDelimiter(正则表达式)方法;(注意这些hasNext()方法,只是判断下一个分词是否有值,执行完后不会下标后移,后移发生在next中)
其也可以完成对正则表达式的匹配。使用hasNext(表达式)判断是否有。使用next(表达式)找到相应的输入部分。在使用MatchResult ma=scanner.match()得到匹配结果再使用ma.group()得到相应的组。与Pattern和Matcher类似。
这种匹配正则表达式的也只是看下一个分词是否匹配若第一个不匹配则不会向下移动下标,下标还是在这个位置。这也是弊端不能用循环查找出所有的Int等。
注意:Scanner的匹配,是先使用分界符分词后,再用正则表达式与每个词比较匹配。若正则表达式中有分界符就不能匹配成功与Pattern不同,Pattern直接在原始字符序列中查找。
136. 循环语句的执行:只要有一次判断为不满足条件,就会退出循环。不再执行。区分与continue语句。
137. 内部类可以访问外部类的所有元素。
138. 在内部类中使用,this指的是内部类的对象,若想其指向外部类对象则用 外部类名.this。super是指父类。
139. 非嵌套(非静态)内部类对象的创建 1.通过外部类的方法返回一个内部类 2.通过New关键字, 让外部类对象创建内部类(a.new),因为内部类隐式的含有一个外部类的引用所以要通过外部类对象创建,例如 外部类 Outter 其对象为outer,内部类Inner,则为Outter.Inner inner = outer.new Inner();这里就不用(outer.new Outter.Inner ())因为外部类对象的使用已经解决了作用域的问题,让一个外部类创建内部类肯定只能创建自己的内部类。
140. 嵌套类(静态内部类):没有指向外部类的引用,所以不能通过外部类对象创建。嵌套类可以包含static的域、方法和嵌套类,但是普通内部类不行。因为是static所以就相当于一个静态方法。直接调用。new Dotnew.Inner().
141. 接口内部类,自动的为public static。类中可以实现接口的抽象方法。
142. 为什么使用内部类?每个内部类都能独立的继承自一个(接口的)实现,无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。解决了多重继承的问题。
143. 一个方法方法体用{ }括起来,每个{}都是一个作用域,在作用域外面不能访问{}中的东西,方法也是一个作用域。作用域中声明的东西不能在作用域以外使用。
144. 匿名内部类:return new classname(初始化基类的参数){ };此语法表示创建一个继承自Contents匿名类的对象。若动态的初始化匿名内部类中的成员变量,只能通过方法的形参传入,若果这个参数在内部类中使用则必须是final类型,如果不在匿名内部类中使用,在基类中使用可以不是final。
145. 匿名内部类用于工厂模式(调用一次工厂域或方法就返回一个继承自通用接口的对象)。
146. Class类的对象其实是包含了一个类的所有相关信息的引用(就像存放的是上下文)。
147. 类型信息(类的信息):类加载方式,1当类创建一个对类的静态成员的引用时,就会加载这个类。(new创建对象,调用的构造器其实也是一种静态方法),所以java程序是动态加载的(用到才加载)。类只在第一次用到时加载,而且只加载一次。静态成员只初始化一次。2挂接额外的类加载器,Class.forName(String className);此方法返回一个要加载类的Class类型的引用。参数要带有包名。Class类型引用初始化只能用Class c=Class.forName()或者一个类调用getClass()方法或使用类字面常量(类名.class)。一个类若调用getClasses()方法,则生成所有嵌套类的引用(内部类)。
148. 使用类字面常量(.class)获得类的引用,不会自动初始化该类(不会初始化静态内容),而Class.forName()会执行初始化。使用(初始化)一个类的准备工作:1加载.class类加载器()2链接(为静态域分配空间)3初始化(父类和静态内容)。
149. 如果一个Staticfinal的值是一个编译期常量(不是表达式),不用初始化就能直接使用(不用初始化),所以使用这个静态内容时,先得到其值在初始化其他静态内容之前。若没有final只有static 必须链接(分配空间)和初始化(初始化空间)之后才能使用。不管是不是静态编译常量,都要初始化所有静态成员,只是先使用或先初始化的问题。
150. Class中含有:1.getInterfaces()在感兴趣的class中获得所包含的接口。2.getSuperclass()3.getName()产生全定限类名,getSimpleName()产生不含包名的类名。4 newInstance():实现虚拟构造器的途径,返回一个其真实的类的实例,一般用Object接受,“我不知道你的确切类型,但无论如何要创建你”,注意用此方法创建的类要有默认的构造器。4isAssignableForm(Class a)判断a是否为调用者的子类或与调用者类型一样。
151. Java性能优化 ,采用简单的数据结构,避免不必要的创建对象,尽量减少调用垃圾清理,对象空间的分配最好在循环体外进行。
152. 对象名/类型信息 instanceof 类名 你是这个类吗你是这个类的派生类吗 与“= =”的区别:= =表示的是这个类的确切类型吗 类型引用.isInstanceof(对象名/类型信息) 后面为前面的实例吗。
153. RTTI与反射的区别:对RTTI来说,编译器在编译时打开和检查.class文件(换句话说我们可以使用普通方式调用对象的所有方法。)而对于反射:.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件。只使用Class引用并不能知道它的确切类型方法等。
154. 默认构造器会被自动赋予与类一样的访问权限。
155. Java动态代理比代理更迈进了一步。动态代理实现InvocationHandler(代理实例的调用处理程序)接口,里面只有invoke抽象方法。调用Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h
)第一个参数:类加载器 第二个参数:你希望该代理实现的接口列表(不是类也不是抽象类),以及InvocationHandler接口的一个实现,当使用代理调用接口的时,本来应该发生多态的,因为这是代理,所以被重定向到对代理的调用(调用处理器)。
156. 通过反射,访问未知类的所有域或方法,通过类引用对象调用getDeclaredMethod(方法名),或getDeclaredField(域名)返回 Method或者Field对象,然后此对象调用setAccessible(true)使用代理的 方法对象.invoke(调用他的对象),调用。final的域遭遇修改时是安全的,反射可以违反访问权限。
157. 反射就相当于访问类中方法 和 域的后门。
158. 数组标识符其实是一种引用,数组是一种效率最高的存储和随机访问对象引用序列的方式。比ArrayList的效率要高很多。
159. 对象数组保存的是引用,引用在数组创建中被初始化为空,基类型的数组直接保存基本类型的值,在数组中自动被初始化为0。
160. 数组在初始化之前是不能做任何操作的(即调用任何方法),初始化方式:使用new关键字,数组初始化的形式:1.使用new int[5],2.聚集初始化;int[]a={ };这种其实也是隐式的使用new在堆中创建如1 。此方法创建数组,必须在定义数组的位置使用不能分开3.动态的聚集初始化, 使用int[]a=new int[]{ };此种定义和初始化可以分开。比较灵活所以为动态。
161. java中再函数体内返回一个引用,只要你使用它,即使作用域不是全局的,来及回收器也不会回收他。
162. 多维数组创建是可以一维一维的创建,每一维的长度不一样,成为粗糙数组。Arrays.deepToString(a)可以将多维数组转换为多个String输出。
163. int[][]a={{ },{ },{ }};a为2维数组,一维长度是红括号的数目3,每个红括号的长度为各自中元素的数目。
164. 数组 的Arrays.fill(数组名,填充元素),将所有的元素或者某个区域都填充为一个值。
165. System.arraycopy(src,srcPos, dest, destPos, length);从数组复制src数组到dest。注意两个数组类型必须一致,此方法不会自动包装和自动拆包。所以 int与Integer不同。
166. Arrays.sort(数组(,比较器comparator))第二个参数可有可无; 对数组进行排序,数组中的元素实现Comparabale<>接口,覆写其中的comparaTo(object)方法,里面自定义比较方法,当前对象大于参数返回正直,如sort(a)。Collections.reverseOrder()产生一个反转自然顺序的比较器。使用comparator(sort的第二个参数),自定义个比较器类实现Comparator接口,覆写compare(obj,obj)方法。(基本类型的数组不能够使用Comparator排序)
167. 数组排序,对任意的对象数组进行排序,只要该对象实现了Comparable接口或者具有相关联的Comparator。Arrays.binarySearch(数组a,查找的元素),查找成功返回在数组中位置,不成功返回负数(-(插入点)-1)这个-1为了确保返回插入点为0时,也是负数,所以此方法用于已经排好顺序的数组,如果数组中包含重复元素则不能找到是数组中的哪一个。
JAVAI/0系统
168. 流处理器所处理的流必定都有流源,而如果将流类所处理的流源分类的式话,基本可以分成两大类:第一 数组,String,File等,这一种叫原始流源。第二 同样类型的流用做链接流类的流源,叫链接流源。注意:File流源就是一些文件,String、数组流源构造流处理器时就用字符串和数组。如new StringReader(“字符串“)。
169. File类,的list()方法返回目录下所有文件的名字,参数:new DirFilter(正则表达式)类,此类是自定义的,实现了FilenameFilter接口,此类只有accept(File dir,String name)方法,返回name是否匹配整个正则表达式,是策略的设计模式,list回调accept方法判断哪个子文件名匹配此表达式,已达到过滤的作用。注意:“.”表示当前目录,“../”表示当前目录的上层目录
170.
171. 传入匿名内部类中的参数,必须是final类型的。
172. StringBulider不能直接添加的基本类型,不能输出会出现打印的为地址,需要自动包装为类.System.out,println(1);是出不来结果的
173. File对象mkdirs()创建目录路径。rnameTo(file)参数为file对象,将调用者重命名为参数的名字。lastModified()最后一次修改的时间返回long与1970年对比
174. 装饰器模式:使用分层动态透明的向单个对象中添加责任。装饰器指定包装在最初的对象周围的所有对象都具有相同的基本的接口。通过使用组合和形式化结构来实现的。装饰器必须具有和他所装饰的对象相同的接口,但它也可以扩展接口,也可以卡扩展接口。
175. 装饰器的接口FilterInputStream是抽象类(不是接口),作为装饰器的接口。I/O中的使用一个流去构造另一个流其实是添加装饰器的过程。
176. ReadLine()读取行时,将每行的\n删掉。
177. readByte()一次一个字节的读取字符,任何字节的值都是合法的,因此返回值不鞥用来检测输入是否结束。可以使用available()方法检查,返回可供存取的字节数,为零时结束读取。
178. PrintWriter写入流,BufferedWriter和PrintWriter都可以向文件中写入数据,但BufferedWriter没有相关的格式化的方法,而PrintWriter可以在写入同时对写入的数据进行格式化。像C语言的printf(“%d”,5)
179. RandomAccessFile,既能读又能写,写入时写入字节,如Double为8个字节,知道文本的排版,就可以使用seek(字节数)随机读取,存取访问让分别建立两个对象。因为若使用一个:写入内容必须使用close()。一旦关闭后这个对象就不能用了。
180. 标准I/O,按照标准I/O模型,java提供了System.in((从控制台)输入)、System.out、System.err。后两个都被包装成了printStream对象,可以直接使用。但是System.in是一个没有被包装过的未曾加工的InputStream(抽象类)。所以使用时,要对其进行包装。一般包装成BufferedReader。
181. JavaI/O字节流注意为各占几个字节:
1个字节是8位 只有8种基本类型可以算.其他引用类型都是由java虚拟机决定的自己不能操作 byte 1字节boolean 1字节short 2字节char 2字节int 4字节float 4字节long 8字节 double 8字节
182. 字节流采用一个字节一个字节读取,每8位二进制为一个字节,所以读取方式的不同可能会产生乱码或读取值得错误。
183. 高位优先存储,最低地址存放高位字节,可称为高位优先。内存从最低地址开始,顺序存放:(高位低位优先存储,只是高低字节顺序的变化,一个字节由8为二进制数组成,这8位2进制数顺序不变,8位为一个字节。宏观字节的顺序)
184. 新I/O在nio包中,使用跟接近与OS执行I/O的方式:通道和缓冲器,唯一与通道交互的是缓冲器。可以在一些字节流上通过getChannel()获得通道。
185. 通道channel只适用于字节流,FileInputStream、FileOutputStream、RandomAccessFile
186. 在ByteBuffer中,数据默认下是以大端形式存储,即高位优先(低地址存放高字节),也可以设置为使用小端方式存储数据,使用order(ByteOrder ord)方法修改存储方式,ord的值可取为:ByteOrder.BIG_ENDIAN或者ByteOrder.LITTLE_ENDIAN两者之一。
187. 视图缓冲器可以让我们通过某个特定的基本数据类型的视窗查看底层的ByteBuffer。ByteBuffer依然是实际存储数据的地方,“支持”在它上建立的其他视图,对这些视图的任何修改都会映射成对ByteBuffer中数据的修改。利用asCharBuffer、asShortBuffer等“asXXXBuffer”方法可以获取ByteBuffer上对应的视图(或者使用CharBuffer的静态方法wrap(char[])将字符数组包装到缓冲区返回一个视图)。当我们需要向ByteBuffer中写入数据时,可以使用ByteBuffer自带的put/get方法来操作,也可以建立一个对应的视图,通过视图的put/get方法来操作数据。如下图所示为ByteBuffer和其他视图的转换
188. 缓冲器细节:
189. Buffer的属性。每个Buffer都有以下的属性:
capacity:容量;表示这个Buffer最多能放多少数据。capacity一般在buffer被创建的时候指定。
limit:界限;在Buffer上进行的读写操作都不能越过这个下标。当写数据到buffer中时,limit一般和capacity相等,当读数据时,limit代表buffer中有效数据的长度。
position:位置;读/写操作的当前下标。当使用buffer的相对位置进行读/写操作时,读/写会从这个下标进行,并在操作完成后,buffer会更新下标的值。调用position()可返回当前位置。
mark:标记;一个临时存放的位置下标。调用mark()会将mark设为当前的position的值,以后调用reset()会将position属性设置为mark的值。mark的值总是小于等于position的值,如果将position的值设的比mark小,当前的mark值会被抛弃掉。这些属性总是满足以下条件:0 <= mark <=position <= limit <= capacity
Buffer的主要方法如下:
clear():清空缓冲区,将position设置为0,limit设置为容量。调用该方法为覆写缓冲区做好准备。
flip():将limit设置为position,position设置为0。该方法位于准备从缓冲区中读取已经写入的数据。
limit():返回limit的值;
limit(int lim): 设置limit的值
position():返回position的值
position(int pos):设置position的值
remaining():返回(limit- position)
hasRemaining():若有介于position和limit之间的元素,则返回true
rewind():将position设置为0,limit不变,一般在把数据重写入Buffer前调用
mark():将mark设置为position
reset():将position设置到mark
190. Java串行化技术可以使你将一个对象的状态写入一个Byte 流里,并且可以从其它地方把该Byte 流里的数据读出来,重新构造一个相同的对象。这种机制允许你将对象通过网络进行传播,并可以随时把对象持久化到数据库、文件等系统里。Java的串行化机制是RMI、EJB等技术的技术基础。用途:利用对象的串行化实现保存应用程序的当前工作状态,下次再启动的时候将自动地恢复到上次执行的状态。
191. 序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
192. 序列化的实现:将需要被序列化的类实现Serializable接口,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
193. 串行化的特点:
(1)如果某个类能够被串行化,其子类也可以被串行化。如果该类有父类,则分两种情况来考虑,如果该父类已经实现了可串行化接口。则其父类的相应字段及属性的处理和该类相同;如果该类的父类没有实现可串行化接口,则该类的父类所有的字段属性将不会串行化。
(2)声明为static和transient类型的成员数据不能被串行化。因为static代表类的状态, transient代表对象的临时数据;
(3)相关的类和接口:在java.io包中提供的涉及对象的串行化的类与接口有ObjectOutput接口、ObjectOutputStream类、ObjectInput接口、ObjectInputStream类。
(1)ObjectOutput接口:它继承DataOutput接口并且支持对象的串行化,其内的writeObject()方法实现存储一个对象。ObjectInput接口:它继承DataInput接口并且支持对象的串行化,其内的readObject()方法实现读取一个对象。
(2)ObjectOutputStream类:它继承OutputStream类并且实现ObjectOutput接口。利用该类来实现将对象存储(调用ObjectOutput接口中的writeObject()方法)。ObjectInputStream类:它继承InputStream类并且实现ObjectInput接口。利用该类来实现读取一个对象(调用ObjectInput接口中的readObject()方法)。
194. 对于父类的处理,如果父类没有实现串行化接口,则其必须有默认的构造函数(即没有参数的构造函数)。否则编译的时候就会报错。在反串行化的时候,默认构造函数会被调用。但是若把父类标记为可以串行化,则在反串行化的时候,其默认构造函数不会被调用。这是为什么呢?这是因为Java对串行化的对象进行反串行化的时候,直接从流里获取其对象数据来生成一个对象实例,而不是通过其构造函数来完成。
195. Externalizable控制序列化,可以控制对象中成员变量是否进行序列化。
196. Externalizable 接口定义了两个方法,writerExternal方法在序列化时被调用,可以再该方法中控制序列化内容,readExternal方法在反序列时被调用,可以在该方法中控制反序列的内容。这两个方法中使用writeObject()和readObject()方法,选择将哪些进行序列化。
并 发
思路转换:从以前的顺序执行,到多个线程一起执行()包括main线程。
197. 并发在多处理器下是可以同时执行的。在单处理器下是轮流切换,使看起来像并发执行,如果没有阻塞,但处理机上的并发就没有任何意义,也没有提升速度。在单处理机上,若一个任务阻塞还可以执行另一个任务从而提高性能。
198. 要并发的类实现Runnable接口,并重写run()方法,此方法通常总会有某种形式的循环,所以要有结束循环的条件。Run有一个静态方法,Thread.yield()的调用是对线程调度器(JAVA线程机制的一部分可以将CPU从一个线程转移到另一个线程)的一种建议。
199. 只实现Runnable,并没有创建线程,并不是由单独的线程驱动,若启动它需要在main(也是一种线程,在任何一个线程里都可启动另一个线程)中 ,直接调用方法。使用Thread类可以创建一个线程,构造参数为Runnable对象start()启动线程。
200. Executors可以创建线程,使用其静态方法newCachedThreadPool或FixedThreadPool、SingleThreadExecutor(所有任务均使用一个线程)
201.使用Callable代替Runnable接口可以在任务完成时返回值, Callable是具有类型参数(即< >中的东西)的泛型,他的类型参数表示的是从call中返回的值,并且使用ExecutorService.submit()调用他。submit()方法使用Callable的返回结果的特定类型进行参数化产生Future对象。
202. 异常不能跨线程传播。
203. volatile (可能随时发生意想不到的改变)告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。
而 优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样以来,如果 i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。可以保证不进行编译器优化(随时改变值随时读取)。
204. 必须在程序启动之前设置后台线程。后台线程在所有被后台线程运行完之后被kill
205. System.nanoTime()纳秒System.currentTimeMillis()毫秒是系统计时器。TimeUnit.MILLISECONDS.sleep()是休眠
206. 后台线程中创建的线程都被自动设置为后台线程。后台线程在没有执行finally子句的情况下就会终止run()方法。
207. 使用继承Thread也可以创建线程,可以再派生类的构造函数中调用start启动线程。 或者使用组合Thread,用this构造Thread线程。然后在构造函数中启动。这种方式启动,可能会在构造器结束之前开始执行,这意味着该任务能够访问处于不稳定状态的对象。这就是优先使用Executor,而不显示创建Thread的原因。
208. 多线程并发,并不是真正的多任务并行开始运行,其实CPU每次运行的任务只有一个,通过时间切片,多多个任务分配时间,一般10ms接近cpu替换线程(任务)的时间。共同访问临界资源时,并不是多个等待的线程一起去抢临界区,CPU会从等待队列依次调度,先调度的先获得使用权。
209. 处理多线程并发问题,满足不出现死锁(多个任务同时等待),互斥,等待时间有限。几个行为主体就写几个主体块,同一行为主体的互斥,只用一个线程块就可以,但是要设置共享变量,作为标识,以区分同一类主体的不同线程。(像哲学家进餐,多个哲学家就是同一类主体)
210. 原子操作一定可以在切换线程之前执行完毕 ,是不可中断的,所以能解决同步问题。可以应用于除long和double之外的所有基本类型之上的简单操作。由于切换线程而分裂的操作称为撕裂。Vilatile确保了应用中的可视性。 泛 型
211. 基本类型无法作为类型参数。
212. 对于静态方法而言,无法访问泛型类的类型参数,因为加载的时间不同,泛型参数要通过创建对象加载,而静态方法在加载.class的时候就加载了。因此,静态方法需要使用泛型能力,就必须使其成为泛型方法。只需将泛型参数列表放在返回值之前。如 public <T> void f(T t)红色表示的部分一定要写。
213. 使用publicstatic <V> List<V> map()这种方法为方法设置类型参数时,类型推断只对赋值操作有效即List<String>list=list();方法的参数传递时并不起作用,此时返回值为Object类型。此时应该使用f(类名.<String>map());显示类型说明。但是这种语法抵消了泛型带来的好处。注意与public <T> voidf(T t)这种区别,这种有形参的可以通过实参与形参的传递而给出类型推断。
214. 当使用泛型类时,必须在创建对象的时候指定类型参数的值,而是用泛型方法的时候通常不必指明参数类型。因为编译器会进行参数类型判断。
215. Java泛型使用擦除实现的,当你使用泛型时,任何具体的类型信息都将被擦除,你唯一知道的就是你在使用一个对象。所以List<String>和List<Integer>在运行上事实上是相同的类型。这两种形式都被擦除为他们的原生类型。