thinkinginjava
第一章
public,private,protected : 公共的(任何人都可用),私有的(类型创建者和内部方法使用),保护的(继承的类可访问,同包类访问)
默认访问权限(不加限定词):包访问权限
组合: 现有类合成新的类,动态的合成称为聚合;组合被视为 has-a(拥有关系),汽车拥有引擎
动态绑定:希望某个方法具备后期绑定属性的灵活性(C++通过virtual关键字来实现)。Java 动态绑定是默认行为,不需要额外添加关键字实现。
java: 单根继承,所有的类都继承自Object,单根继承结构用所有对象都具有一个共用接口,都是相同的基本类型。
单根继承保证所有对象都具备某些功能。单根继承结构使垃圾回收器实现变得容易。
java容器:java包含各种满足不同需要的各种类型的容器,例如 List(用于存储序列),Map(也被称为关联数组,用来简历对象之间的关联),Set(每种对象类型只持有一个);ArraList ,随机访问元素是一个花费固定时间的操作。LinkedList,访问越靠近表尾的元素,花费时间越长,如果想在序列中间插入一个元素,LinkedList开销比ArrayList要小。
通用容器存储对象抽象为Object:由于容器只存储Object,当对象应用置入容器时,必须被向上转型为Object,因此会丢失其身份,当取回时,获得了一个对Object对象的引用,而不是对置入时的那个类型对象的引用。
参数化类型容器:通过范型容器(定义容器参数类型)解决类自动识别类型。
内存分配方式:java使用动态内存分配方式,每当想创建对象时,使用new关键字来构建对象的动态实例。
创建对象的生命周期: 允许在堆栈上创建对象的语言,编译器可以确定对象存活时间,并可以自动销毁它。
如果是在堆上创建的对象,编译器就会对它的生命周期一无所知。
垃圾回收器能够实现的原因: 所有对象都是继承自单根基类Object以及只能以一种方式创建对象(在对上创建)这两个特性结合起来.
java异常处理:java内置异常处理,并强制使用它,如果没有编写正确的处理异常代码,就会得到一条编译时的出错信息。
并发编程:程序在逻辑上被划分为线程。
第二章
堆栈:位于通用RAM中,一部分Java数据存储在其中(必须知道存储数据的生命周期),比如对象引用,但是java对象并不存储于其中;
堆:一种通用的内存池,也位于RAM区,用于存放所有的java对像,编译器不需要知道存储在该处数据的存活时间,堆存储分配用时比堆栈长
常量存储:存储在ROM中
非RAM存储: 存活于程序之外的数据,比如流对象和持久化对象,java提供轻量级数据持久化支持,JDBC和Hibernate提供更加复杂的对数据库中存储和读取对象信息的支持
基本类型:存储在堆栈中,每种类型所占字节固定;
高精度数字:BigInteger和BigDecimal,属于包装容器类,运算速度较慢,速度换精度。
java对象:自动初始化,永远不需要销毁
java变量:较小作用域定义的变量不能与较大作用域定义的变量重名,这样会导致程序生命周期混乱
局部基本变量: 比如 int x ,不会被自动初始化,x的值可能为任意值
字段(数据成员)和方法(成员函数):字段是对某个对象的引用时,必须初始化。字段为基本类型时,会自动初始化;普通字段不能在对象间共享。
字符串中的字符:字符串中的每个字符都是16位或2个字节
static方法:可以创建或使用与其类型相同的被命名的对象?,因此该方法常被用来做“牧羊人”的角色,负责看护与其隶属同一类型的实例群。
java.lang: 会被自动导入到每一个java文件;
javadoc:提取注释的工具,所有javadoc命令都只能在“/**”注释中出现,注释结束于“*/”.使用javadoc的方式主要有两种:嵌入HTML,或使用“文档标签”。独立文档标签是一些以“@”字符开头的命令,且要置于注释行的最前面(但是不算前导“*”之后的最前面)。“行内文档标签”可以在javadoc注释中的任何地方,以“@”字符开头,弹药括在花括号内。注意:javadoc只能为Public和protected成员进行文档注释。private和包内可访问成员的注释会被忽略掉。
java doc标签:@see 引用其他类 ; @version ; @deprecated 指出一些旧特性已由改进的新特性所取代 ,建议不要再使用旧特性; “//:” 用一个“:”作为特殊记号说明这是包含源文件名的注释行,该行包含文件路径信息。“///:~”标志源代码清单的结束。
/*Output 标签表示输出的开始部分将由这个文件生成,通过这种形式,会自动测试以验证其准确性。
java编码风格:类名的首字母要大写,如果类名由几个单词构成,并在一起,每个内部单词首字母采用大写形式。标识符第一个字母小写,其他与类相同。驼峰风格;
直接常量后面后缀字符标志了它的类型,若为大写的L,代表Long,若为大写字母F,代表float,大写字母D,代表double
无符号 右移操作符:>>>,无论正负都在高位插入0
“移位”可与“等号” 组合使用,(<<=或>>=)操作符左边的值会移动由右边的值指定的位数,再将得到的结果赋给左边的变量。
类型转换操作符:cast,愿意是“模型铸造”。
java不需要sizeof()操纵符,因为所有数据类型在机器中的大小是相同的。
用于数组和容器的for语法:foreach ;for(float x: f) f为数组,把f数组中的每一个值赋值给x
range()的用法:for(int i : range(10)) //0..9 for(int i : range(5,10)) //5..9 for(i : range(5,20,3)) //5..20 step 3
switch:参数必须是Int或char那样的整数值
第五章 初始化与清理
java中采用构造器类似C++,并额外提供了“垃圾回收器”
java构造器:构造器选择与类相同的名称,初始化期间可自动调用构造器。
static: static方法内部不能调用非静态方法,反过来可以。java中禁止使用全局方法,但你在类中置入static方法就可以访问其他static方法和static域
finalize()方法:一旦垃圾回收器准备释放对象占用的内存空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。
本地方法调用(native)使用finalize()释放内存:本地方法目前只支持C和C++,finalize()调用释放C和C++ 内存的函数释放C和C++申请的内存。
垃圾回收: “自适应的、分代的、停止-复制、标记-清扫” 式垃圾回收
final的作用: 1.方法锁定,以防任何继承;2.提升效率(转为内嵌调用)
内部类:
类的定义置于外围类的里面;要从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须像在main()方法中那样,具体指明这个对象的类型。内部类自动拥有对其外围类所有成员的访问权。
内部类对外部类对象的引用:可以使用外部类的名字后面仅跟圆点和this。
匿名内部类与正规的继承相比有些受限,匿名内部类既可以扩展类,也可以实现接口,但不能两者兼备。而且如果实现接口,也只能实现一个接口。
使用匿名类实现工厂方法
嵌套类:如果不需要内部类对象与其外围对象之间的联系,可以将内部类声明为static,通常称为嵌套类.
创建嵌套类的对象,不需要外围类的对象;不能从嵌套类的对象中访问非静态的外围类对象。
嵌套类与普通内部类的区别:普通内部类的字段和方法只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段也不能包含嵌套类。
接口内部类:正常情况,不能再接口内部放置任何代码,但嵌套类可以作为接口的一部分。放到接口中的任何类都自动是public和static的。如果想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所公用,使用接口内部嵌套类会很方便。
(建议,每个类中都写一个main()方法,用来测试这个类,或者用嵌套类来放置测试代码)
需要内部类原因:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没影响。(内部类使得多重继承的解决方案变的完整,接口解决了部分问题,内部类有效地实现了“多重继承”,拥有的是抽象的类或具体的类而不是接口,则使用内部类才能实现多重继承)
(内部类与代理的区别)
闭包:是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域;通过内部类提供闭包的功能是优良的解决方案,比指针灵活,安全。
内部类的继承:只继承内部类而不继承外围类,要生成一个构造器时,不能只传递一个指向外围类对象的引用,必须在构造器内使用如下语法:enclosingClassReference.super();这样才提供了必要的引用,程序才能编译通过。
内部类可以被覆盖吗:当继承了某个外围类的时候,内部类并没有发生什么变化,两个内部类是完全独立的两个实体,各自在自己的命名空间内。
局部内部类:代码块里创建内部类,局部内部类的名字在方法外是不可见的,当我们需要一个已命名的构造器,或者需要重载构造器时,使用局部内部类,匿名内部类只能用于实例初始化。使用局部内部类另一个理由是需要不止一个该内部类的对象时。
接口和内部类深奥复杂,两者结合可以解决C++中的多重继承问题。
Collection容器:一个独立元素的序列,服从一条或多条规则。List必须按照插入顺序保存元素,Set不能有重复元素,Queue按照排队规则来确定对象产生的顺序。
Map:一组成对的“键值对”。ArrayList允许使用数字查找值,蒋数字与对象关联一起。映射表允许我们使用另一个对象来查找某个对象,称为“关联数组”,蒋某些对象与另外一些对象关联在一起;或被称为“字典”
list:ArrayList, LinkedList
Set: HashSet、TreeSet和LinkedHashSet
迭代器(也是一种设计模式):是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构。迭代器通常被成为轻量级对象,创建代价小。Java的Iterator只能单向移动。ListIteator是Iterator的子类型,可以双向移动。Iterator迭代器怎么实现的?
Stack 栈:通常指“后进先出”的容器。有时被成为叠加栈,因为最后“压入”栈的元素,第一个“弹出”栈。
Set: 不保存重复元素,最常用于测试归属性,所以查找是Set中最重要的操作,因此通常会选择HashSet的实现,它专门对快速查找进行了优化。
TreeSet:将元素存储在红-黑数数据结构中,可用于排序。HashSet使用的是散列函数。LinkedHashList因为查询速度的原因,也使用了散列,但是使用了链表来维护元素的插入顺序。
Map: 可以返回它的建的Set,它的值的Collection,或者它的键值对的Set。
Queue:队列是一个典型的先进先出(FIFO)的容器。LinkedList提供了方法以支持队列的行为,并且实现了Queue接口,因此LinkedList可以用作Queue的一种实现。
PriorityQueue:优先级队列,确保当你调用peek()、poll()和remove()方法时,获取的元素将是队列中优先级最高的元素。
JavaSE5中,大量的类都是Iterable类型,主要包括所有的Collection类。
Map是一种蒋对象与对象相关联的设计。HashMap设计用来快速访问;而TreeMap保持“键”始终处于排序状态,所以没有HashMap快。LinkedHashMap保持元素插入的顺序,但是也通过散列提供了快速访问能力。
Set不接受重复元素,HashSet提供最快的查询,TreeSet保持元素处于排序状态。LinkedHashSet以插入顺序保存元素。
异常处理两种基本模型:终止模型(异常发生,程序无法恢复执行);恢复模型(异常被处理之后可以继续执行),在该模型遇见错误时不能抛出异常,二十调用方法来修正错误。
异常日志记录:java.util.logging 工具将输出记录到日志中。
finally:使用的必要性主要是要把除内存之外的资源恢复到他们的初始状态。包括已经打开的文件或网络连接、屏幕上画的图形等
异常处理再研究...
String对象是不可变的,每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。
Pattern和Matcher正则表达式匹配类
接口中的方法必须是非静态的,因为静态的方法必须被实现,java8中可以有静态方法。接口中的变量都是 public static final类型的
RTTI (RunTime Type Information)运行时识别对象类型信息。
.class和Class.forName()获取对类的引用区别:前者不马上初始化,后者立即初始化
Class<ClassType> 限定为某种类型的类引用,?代替ClassType表示通配符
instanceof 和isInstance()判断结果相同,表示判断类型是否是这个类或这个类的派生类;==比较的是Class对象,没有考虑继承,判断的是确切的类型。
远程方法调用(RMI):希望提供在跨网络的远程平台上创建和运行对象的能力,允许一个Java程序将对象分布到多台机器上。
RTTI和反射之间的区别只在于,对RTTI来说,编译器在编译时打开和检查.class文件,而对于反射机制来说,.class文件在编译时是不可获取的,是运行时打开和检查.class文件。
动态代理:代理是基本的设计模式之一,它是为了提供额外的或不同的操作,而插入的用来代替”实际“对象的对象。(动态代理实现方法)
空对象标记接口: public interface Null{}///:~ 使得instanceof可以探测空对像。空对像通常都是单例,因此在具体实现类中可作为静态final实例创建。
模拟对象和桩都知识假扮可以传递实际信息的存活对象,而不是像空对象那样可以成为null的一种更加智能化的替代物,模拟对象和桩之间的差异在于程度不同,模拟对象往往是轻量级和自测试,桩是返回桩数据,通常是重量级的,并且经常在测试之间复用。
通过反射机制可以在包外通过外部调用访问类中private\protected和包访问权限的方法。
接口设计工具(?)
面向对象编程语言的目的是尽可能使用多态机制,只在必需的时候使用RTTI
多态机制方法调用要求我们拥有基类定义的控制权,因为在阔在程序时,可能会发现基类没有包含我们想要的方法,如果这时不能控制基类,则RTTI便是一种解决方法:继承一个新类,然后添加你需要的方法。
java泛型:出现最重要的原因 是可以创造容器类。类型参数,用尖括号括住放在类名后面, public class Holder<T>{private T a;...;}
元组(typle):是将一组对象直接打包存储于其中的一个单一对象。这个容器对象允许读取其中元素,但不允许向其中存放新对象,通过final声明属性变量。
15章 泛型
泛型接口:泛型也可应用于接口,例如生成器(generator)
java泛型的局限性:基本类型无法作为类型参数
普通static方法无法访问泛型类的类型参数,如果static方法需要泛型能力,就必需使其成为泛型方法。
泛型方法定义:将泛型参数列表置于方法返回值之前。
古怪的循环泛型(CRG):是指类相当古怪地出现在它自己的基类中。CRG的本质就是积累用导出类代替其参数。
混型与装饰器设计模式关系很近,装饰器模式使用分层对象来动态透明地向单个对象中添加责任。
使用泛型机制最吸引人的地方,就是使用容器类的地方,这些类包括注入List、各种set、各种Map等。
16章 数组
基本类型数组数值型,自动初始化为0;字符型(char)自动初始化为(char)O;布尔型自动初始化为false。
使用参数化方法而不使用参数化类的方便之处在于:你不必为需要应用的每种不同的类型都使用一个参数区实例化这个类,并且可以将其定义为静态的。
java标准库提供的static方法System.arraycopy(),用它复制数组比用for循环复制要快的多。
使用内置的排序方法,可以对任意的基本类型数组排序,也可以对任意的对象数组排序,只要该对象实现了Compareable接口或具有Comparator。
17章 容器的深入研究
java容器类简化图:

集合类型主要有3种:set(集)、list(列表)和map(映射)。
LinkedHashSet四种初始化方法
第一种构造函数初始化一个空的LinkedHashSet:
LinkedHashSet( );
第二种构造函数使用Collection元素集初始化LinkedHashSet:
LinkedHashSet(Collection c)
第三种构造函数用给定的容量初始化LinkedHashSet:
LinkedHashSet(int capacity)
第四种构造函数通过传入的容量和填充比初始化LinkedHashSet:
LinkedHashSet(int capacity, float fillRatio)
Iterable和Iterator

享元:运用共享技术,有效的支持大量细粒度的对象,避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类).。
在普通的解决方案需要太多对象或者产生普通的对象太占用空间时,可以使用享元的设计模式。
静态内部类: 如果一个类要被声明为static的,只有一种情况,就是静态内部类。如果在外部类声明为static,程序会编译都不会过。在一番调查后个人总结出了3点关于内部类和静态内部类(俗称:内嵌类)
(from https://www.cnblogs.com/Alex--Yang/p/3386863.html )
1.静态内部类跟静态方法一样,只能访问静态的成员变量和方法,不能访问非静态的方法和属性,但是普通内部类可以访问任意外部类的成员变量和方法
2.静态内部类可以声明普通成员变量和方法,而普通内部类不能声明static成员变量和方法。
3.静态内部类可以单独初始化:
Inner i = new Outer.Inner();
普通内部类初始化:
Outer o = new Outer(); Inner i = o.new Inner();
Map.entry<String,String>; EntrySet的实现逻辑?
实现定制Map和Collection,可以继承AbstractMap并实现entrySet,实现定制Entry,可以继承实现Map.entry;
为了创建只读的Set,可以继承AbstractSet,并实现iterator和size();
只读map实现:
https://github.com/xdengpao/MyJavaLearning/blob/master/thinkinjava/src/mindview/util/Countries.java
待总结:继承Abstract的容器来自定义容器,需要继承实现哪些必要方法,实现不同的方法获得的自定义容器的差异是什么?
可变自填充List实现:
https://github.com/xdengpao/MyJavaLearning/blob/master/thinkinjava/src/mindview/util/CountingIntegerList.java
Collection功能方法中不包括get()方法,因为Collection包括Set,Set是自己维护内部顺序的,因此要检查Collection中的元素必需使用迭代器。
Set:

Queue:队列,Java SE5中两种实现LinkedList和PriorityQyueue,链队列先进显出,优先级队列通过Comparable进行控制,实现优先级队列的Comparable的内部静态类。
散列(HashSet,HashMap,LinkedHashSet,LinkedHashMap)使用中:使用的仅是Object类默认的Object.equals()只是比较对象的地址,因为普通类仅是继承自Objbect,所以要使用自己的类作为HashMap的键时,必需重载hashCode()和equals(),根据实际需求实现该2方法。
使用散列的目的在于:想要使用一个对象来查找另一个对象。
持有引用:SoftReference,WeakReference和PhantomReference
SoftReference用以实现内存敏感的告诉缓存。Weakreference用以“规范映射”而设计,不妨碍来及回收期回收映射的“键”(或“值”)。“规范映射”中对象的实例可以在程序的多处被同时使用。Phantom reference 用以调度回收前的清理工作,比java终止机制灵活。
使用enum进行双路分发,最完美方式是采用EnumMap。https://github.com/xdengpao/MyJavaLearning/blob/master/thinkinjava/src/enumerated/RoShamBo5.java
18章 Java I/O系统
19章 枚举类型
20章 注解
21章 并发
线程可驱动任务,定义任务只需实现Runnable接口并编写run()方法。
从Runnable导出一个类时,必须具有run()方法,要实现线程行为,必须显式的将一个任务附着到线程上。
将Runnable 对象转变为工作任务的传统方式,是把它提交给一个Thread构造器。
SingleThreadExecutor,单线程池可用于序列化任务,添加到该线程池的任务会按照添加顺序执行。
从任务中获取返回值,可以实现Callable接口,submit()方法会产生Future对象,可用Callable返回结果的特定类型进行参数化。可以使用isDone()方法来查询Future是否已弯沉个,使用get()方法来获取该结果。
优先级:优先级较低的线程仅仅是执行的频率较低,而不是按顺序排在最后执行。
非后台线程结束时,程序终止,同时会杀死后台线程。
后台线程创建的线程同样是后台线程。
后台线程不能以优雅的方式关闭,非后台的Executor通常是一种更好的方式,因为非后台Executor控制的所有任务可以同时被关闭。
构造器中启动线程可能会有问题,因为另一个任务可能会在构造器结束之前开始执行,意味着该任务能够访问处于不稳定状态的对象。
一个线程可以在其他线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才继续执行,并可以在调用join时带上一个超时参数,如果目标线程在预定时间没有返回,则超时后能够返回,如果想中断join,可以在调用线程上调用interrupt()。(某个线程在另一个线程t上调用t.join,此线程将被挂起,直到目标线程t结束才恢复。)
由于线程的本质特性,使得不能捕获从线程中逃逸的一场,一旦一场逃出任务run()方法,就会向外传播到控制台,除非采取特殊的步骤捕获这种错误。JavaSE5可通过Executor来解决该问题。结局方式:修改Executor产生线程的方式,Thread.UncaughtExceptionHandler是Java SE5中新接口,允许在每个Thread独行上附着一个异常处理器。Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而临近死亡时被调用。实例如下:
要想并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。
当在对象上调用其任意synchronized方法的时候,此对象都被加锁,此时对象上的其他synchronized方法只有等到前一个方法调用完毕并释放了该锁之后才能被调用。对于同一个特定对象,其所有的synchronized方法共享同一个锁,可以被用来防止多个任务同时访问被编码为对象内存。
可通过显示互斥锁来控制资源共享(定义在java.util.concurrent.locks中),try块中首先调用lock(),unlock()的的调用要在finally块中,注意,return语句必须在try子句中出现,以确保unlock()不会过早发生,从而暴露数据给第二个任务,如下例:
临界区,通过synchronized对括号内的代码进行同步控制:Synchronized(syncObject){//this code can be accessed; //by only one task at a time},也被称为同步控制块。进入此段代码钱,必须得到syncObject对象的锁,如果其他线程得到这个锁,就得等到锁被释放后,才能进入临界区。
synchronized同步锁:一个任务如果已经持有锁,则可以调用在改对象上的其他synchronized方法
线程之间协作:
wait()与notifyAll()
sleep()与wait()的区别:(1)在wait()期间对象锁是释放的。(2)可以通过notify()、notifyAll()或者令时间到期,从wait()中恢复执行。
什么是乐观锁和乐观加锁
ReadWriteLock 读写锁
22章 图形化用户界面
Java网络发布协议(Java Network Launch Protocol,JNLP)
swing和swt




浙公网安备 33010602011771号