百度脑图: http://naotu.baidu.com/file/c07a7be593acee73e72451ad1d393f5d?token=ce49cc74688fd5fd
虽然 java 基础看了不下 3 遍了,可是不怎么用 java 写代码
花了几天粗略的看了一下, 算是大致知道语法了, 至少写写 hello world 或者看代码没有压力啦.
高级语言: 汇编之上的语言, c / c++ / java ..
编译型(静态) / 解释型(动态)
jvm统一标准: 指令集 / 寄存器 / 类文件的格式 / 栈 / 垃圾回收堆 / 存储区
jre: jvm + 类加载器 / 字节码校验 / 基础类库
path: jdk/bin/
垃圾回收机制
- c/c++ 需要程序员自己负责已经分配的内存, 不及时回收会引起系统运行速度下降, 甚至系统瘫痪(内存泄漏)
- 只能回收内存资源(无用对象的内存空间), 不能回收物理资源(db连接, 磁盘I/O)
- 发生不可预知: 定时 / cpu 空闲 / 内存消耗过多
- 精确性: 精确标记活着的对象 / 精确定位对象之间的引用关系
- jvm 有多种垃圾回收实现
结构化程序设计: 设计不够直观, 与人类习惯思维不一样 / 适应性差, 可扩展性不强 / 强调实现某个功能的算法
任何算法都由 顺序结构 / 选择结构 / 循环结构 组合而成
面向对象程序设计: 类 / 对象 / 继承 / 封装 / 消息 等基本概念进行程序设计
类 = 成员变量(状态数据) + 方法(行为)
面向对象: 封装 / 继承 / 多态
UML(统一建模语言): OOA(分析) -> OOD(设计) -> OOP(编程); 用例图 / 类图(关联/泛华/依赖) / 部署图 / 顺序图(时序图) / 活动图 / 状态图
注释: 永远不要相信自己的理解能力 / 可读性第一, 效率第二 / 代码即文档
编程的本质, 就是对内存中数据的访问和修改
语法参考
java基本数据类型:
http://qiniu.daydaygo.top/java/basic-datatype.png
申明 long 类型:long num = 99999999999L;
二进制 / 八进制 / 十进制 / 十六进制
字符型: java 字符串使用 unicode 编码, char 使用单引号, string 使用双引号
计算机只能保存二进制, 字符集是用数字来表示字符, 如 ASCLL 中65 -> A
, 存储时存储64即可
浮点型: 需要精确表示一个浮点数, 可以考虑 BigDecimal 类 / 十进制 + 科学计数法 / 正无穷大 + 负无穷大 + 非数
数字使用下划线分隔:ob0000_0000_0000
大部分计算机允许分配的最小内存单位是字节, 所以 boolean 实际上占用 1 byte
自动类型转换: 小范围 -> 大范围
强制类型转换: 要小心 大范围 -> 小范围 的溢出问题
string 是一个典型的不可变类
常量池(constant pool): 类 / 方法 / 接口 中的常量, 字符串直接量
移位操作: 不会改变原数, 而是新生成数 / 相当于乘2或除2
你知道这条航线上的所有暗礁么? --不, 我只知道那条是深水航线
return 0;: 对的路只有一条, 错误的路有千万条
数组: 数据类型固定 / 长度固定 / 静态初始化 + 动态初始化 / foreach / 数组引用(比如指向对象) / 没有多维数组(其实就是用数组引用实现) / Arrays / 多核cpu支持
栈内存: 方法运行时, 使用的局部变量都保存在 栈内存 中, 当方法运行结束时, 清空 栈内存
堆内存: 程序运行时, 会有一个数据区用来保存运行所需的数据, 这个就是堆内存, 比如运行时创建的对象(创建对象通常开销都很大), 当一个对象没有任何应用变量引用它时, GC就会在适当的时候回收它
面向对象
Person p = new Person()
: p为对象的引用变量, 存放在栈内存中, 对象为类的实例, 属性是需要使用内存存储的, 存放在对内存中
Person p2 = p
: 对象可以有多个引用
this
: 对象引用自身, 本质其实是类自身 属性/方法 间的依赖; 返回 this 可以 链式调用; 还可以代码复用哦
static
: 本质上定义的属于类本身而非类实例(对象)的 属性/方法, 所以尽量分开2者的用法
变量: 类变量 / 实例变量 / 方法局部变量 / 代码块局部变量; setter / getter
方法的参数传递机制: 实际上是传递一个副本
形参个数可变: 被当作数组传入
递归: 方法调用自身 / 一定要向已知方向 / 比如遍历目录
方法重载: 同名 / 形参数量不同
包: 可以查考 php 命名空间来理解, 包名匹配路径名, 类名匹配文件名; package + import / import static(可以用来导入静态常量)
- java常用包
-封装
private / default / protected / public 访问控制; 隐藏成员变量和实现细节, 暴露方法; 本质是对客观世界的模拟
构造器
与类同名; 必须含有构造器; java自动生成, 结构体为空; 为变量设置默认值
对象在构造器执行前就产生了: 程序调用构造器 -> 系统为对象分配内存 -> 执行对象默认初始化 -> 执行构造器
通常为 public, 可以通过访问控制来实现来限制创建该类的对象
从 父类 -> 子类 的顺序执行构造器
初始化块: 特殊的成员变量 / 在构造器之前执行继承
extends, 继承实现了复用, 子类改变父类的变量形成多态, 单继承
override: 重写(方法覆盖) | super: 使用父类被 override 的方法
需要子类: 增加额外属性 / 增加独有行为方法
instanceof
: 判断继承关系;obj.getClass() == Person.Class()
, 判断是否为同一个类
继承会破坏封装多态
BaseClass cTest = new SubClass()
: 编译时为 BaseClass, 运行时为 SubClass
强制类型转换: 基本类型 -> 数值类型; 引用类型 -> 继承关系组合
将父类作为子类的属性, 从而避免使用继承
is-a 与 has-a 的关系来区分使用 继承 还是 组合基本数据类型与包装类
AutoBoxing <-> AutoUnboxing
字符串 -> 基本数据类型:parseXxx(str)
或者new Xxx(str)
基本数据类型 -> 字符串:String.valueOf(num)
Integer: 当数字在 -128~127时, java 会启用缓存机制cache[i] = num
toString(): 将类当字符串使用; 默认为 "类名 + @ + hashCode"; 重写来实现类的自我描述
equals(): 和==
比较; 重写来实现比较规则
singleton(单例)类: 单例模式
final: 成员变量必须现实指定初始值; 局部变量防止变量被重复赋值; 宏变量; 方法不可被 override; 不可有子类
缓存实例的不可变类: 简单使用 数组实现缓存(涉及到 查找 队列)
抽象类: 抽象方法; 有得有失 ->多了一个能力, 无法创建实例; 模板模式
接口: 契约精神; 成员变量自动为 public static
; 规范和实现分离的设计哲学; 简单工厂模式; 命令模式
内部类: 更好的封装, 可以类似 闭包/匿名函数
来理解
lambda表达式: 缩写, 感觉很赞
枚举类
- 对象与垃圾回收
只负责内存中的对象, 不负责回收任何物理资源(db连接, 网络io 等)
无法精确控制; 对象永久性失去应用 -> 系统在合适的时候回收
回收之前调用finalize()
, 此方法可以让对象 '复活', 取消回收
引用: 创建之后 -> 可达状态 -> 可回复状态 -> 不可达状态 -> 垃圾回收
强制垃圾回收:System.gc()
+Runtime.getRuntime().gc()
引用类型: 强引用(对象赋值); 软引用(内存足够时不会被回收, 常用于对内存敏感的程序); 弱引用(垃圾回收就会被清楚); 虚引用(必须和引用队列联合使用, 用于跟踪被垃圾回收的状态)
最好是经历过某种痛苦, 或者正在经历一种痛苦, 就会对设计模式有较深的感受
java 基础类库
工具类通常使用 s 作为后缀
- 运行java程序的参数: args
- Scanner: 获取键盘输入; 读取文件
- java 无法访问操作系统底层硬件设备, 需要借助C语言实现: java 申明 native 方法 -> 编译生成 .class 文件 -> javah 编译 .class 文件生成 .h 文件 -> 写一个 .cpp 方法实现 native 方法, 包含之前的 .h 文件 -> 将 .cpp 编译成动态链接库文件 -> 在java中调用生成的动态链接库文件
- 系统相关: System类代表当前java的运行平台; Runtime类代表java运行时环境
- Object 类: 所有 类/数组/枚举类 的父类; equals() / finalize() / getClass() / hashCode() / toString() / 控制线程 / clone(): 浅克隆, 不对引用类型成员变量进行克隆
- Objects 工具类: requireNotNull(obj), 用来对形参进行输入校验
- String(不可变类) / StringBuffer(字符序列可变, 线程安全) / StringBuilder(非线程安全, 性能略高)
- Math 类
- Random / ThreadLocalRandom / Math.random()
- BigDecimal(string val): java 在计算 float / double 时容易发生精度丢失(通病)
- 日期时间: Date(尽量不要使用) / Calendar(日期时间加减运算, 获取 年月日时分秒信息) / java.time.* / DateFormat / SimpleDateFormat / DateTimeFormatter
- 正则表达式: String / Pattern(不可变类, 可供多个并发线程安全使用 / Matcher
- 国际化&多语言: 属性资源文件 / 使用类替代 / Format 相关类
集合(Collection / Map)
hash: hash算法存在的意义就是快, 可以通过hash值快速定位数据存储在内存中的位置, 从而快速找到数据
所有内部基于数组的集合实现(如 ArrayList, ArrayDeque) 随机访问要比 Iterator 迭代访问性能要好
使用: 排序; 查找/替换; 同步控制 synchronizedXxx(), 包装成线程安全; 设置不可变集合
Set: 无序, 不可重复; 基本都是使用 HashSet / LinkedHashSet(使用链表维护元素次序, 遍历全部元素时将有很好的性能) / TreeSet(只能添加同一种类型的对象, 默认升序, 可以定制 Compare 实现降序) / EnumSet(以位向量形式存储) / HashSet + TreeSet + EnumSet 都是线程不安全的, 需要使用 Collections 的 syncchronizedSortedSet 来包装
List: 有序重复; ArrayList(线程不安全) / Vector(线程安全) -> Stack / ArrayDeque / LinkedList
Queue: PriorityQueue(优先级队列, 玩过算法应该都知道) / Deque(双端队列) / ArrayDeque
Map: 映射; java源码使用 value 全为 null 的 Map 实现 List; Hashtable(线程安全) / HashMap(线程不安全) / LinkedHashMap(双向链表维护) / Properties(用来读写属性文件, 包括 ini 文件) / SortedMap / TreeMap / WeakHashMap(弱引用) / IdentityHashMap(要求严格相等) / EnumMap
http://7xq89b.com1.z0.glb.clouddn.com/crazy-java3-8.1.png
http://7xq89b.com1.z0.glb.clouddn.com/crazy-java3-8.2.png
http://7xq89b.com1.z0.glb.clouddn.com/crazy-java3-8.3.png
Iterator: 迭代器, 用来遍历集合元素
Stream: 通用流接口, 支持串行和并行聚集操作
- 别告诉我你知道 Hash
对于 HashSet 及其子类而言, 它们采用 hash 算法来决定集合中的元素的存储位置, 并通过 hash 算法来h控制集合的大小; 对于 Hashtable / HashMap 及其子类, 使用 hash 算法决定其 key
属性: capacity(容量) / initial capacity / size(当前记录的数量) / load factor(负载) = 'size/capacity', 轻负载的 hash 表具有冲突少, 适宜插入, 但是 iterator 迭代比较慢
泛型 Generic
集合允许放置任何 Object 类型, 泛型就是为了防止类型滥用, 在使用前确定类型
可以为 任何类/接口 添加泛型声明
并不存在泛型类: 不会重新生成类, 不会改变类之间的关系, 而是限定类使用参数的数据类型
异常处理
传统的处理方式: 先使用 if 判断程序运行状态, 如果都正常, 再进入业务逻辑, 而 try{}catch{}
结构则是先业务逻辑, 然后根据现实再添加额外 catch
try -> 出现异常 -> throw, 抛出异常 -> java runtime 接收异常 -> 寻找处理该异常对象的 catch 块 -> catch 块处理, 即为 捕获异常, 如果无法捕获, runtime 终止, 程序直接退出
http://7xq89b.com1.z0.glb.clouddn.com/crazy-java3-10.2.png
捕获异常: 先小后大; 同时捕获多个异常
tyr: 自动关闭资源写法
finally: 垃圾回收只回收内存, 物理资源需要显式回收, 而将资源回收放在 try / catch 中都无法确定资源能被回收 / 除非调用了退出虚拟机的方法(System.exit(1)), 一定会被执行
Checked 异常: 没有完善错误处理的代码根本就不会被执行
catch-throw: 企业级应用经常使用, 后台记录异常的日志信息, 前台返回自定义提示信息给用户; 向用户隐藏原始异常信息
异常处理规则: 最小化代码混乱 / 捕获并保留诊断信息 / 通知合适的人员 / 采用合适的方式结束异常活动 / 不用使用异常来代替正常的业务逻辑判断
AWT && Swing
mysql 与 JDBC 编程
JDBC: 跨数据库
Annotation 注解
IO
random: 除了随机以外, 还有任意的意思, 所以 RAM (内存, random access memory) 可以理解了
File: 操作文件和目录
IO stream: 从 source 到 sink 的有序数据; 相对于 内存来确定输入输出
输入: InputStream / Reader
输出: OutStream / Writer
java IO 模型的优势: 使用 缓冲 提高效率; 操作便捷
如果不关闭输出流(close() 执行前会自动调用 flush()), 可能会导致缓冲区的内容没有 flush() 到输出源
http://7xq89b.com1.z0.glb.clouddn.com/crazy-java3-15.1.png
字节流 / 字符流: 如果使用 二进制数据, 使用 字节流; 字符流比字节流操作简单, 所以 java 提供了 字节流 -> 字符流 的方法
重定向
读写其他进程的数据: java 使用 runtime / exec() 可以执行其他程序, Process 类代表这些 子进程
RandomAccessFile: java io体系中功能最丰富的文件内容访问类; 只能读写文件
多线程网络下载工具(FlashGet)实现原来: 可以使用 RandomAccessFile 类来实现, 下载开始前创建2个文件, 一个和下载文件相同大小的空文件, 一个记录文件指针位置的文件
序列化: Serialize; 对象引用的序列化; 通过序列化算法获取对象的序列化编号; 第一次使用 writeObject() 获取序列化编号后就不会再次计算
- nio
Channel + Buffer(capacity / limit / position), 映射文件或文件的一段区域到内存中
Charset
fileLock: 高并发下还是推荐使用db来保存程序信息
Files / Paths / FileVistor / WatchService(监控文件变化)
多线程
线程在多cpu下自动并行
一个任务(程序)通常就是一个进程, 进程运行时, 内部可能包含多个顺序执行流, 每个顺序执行流就是一个线程
进程(Process): 当一个程序运行进入内存后, 就变成了一个进程; 独立性(独立实体, 独立资源, 独立地址空间) + 动态性(生命周期, 各种不同形态) + 并发性(单个cpu上可以执行多个进程, 互相不会影响)
并行(parallel): 同一时刻多条指令在多个处理器上同时执行
并发(concurrency): 同一个时刻只有一条指令执行, 但是多个进程指令在短时间内快速轮换执行, 使得宏观上具有多个进程同时执行的效果
线程(Thread): 进程的组成部分, 拥有自己的 堆栈/计数器/局部变量, 但是不拥有系统资源, 和父进程的其他进程共享该进程的全部资源
多线程优势: 共享内存 / 文件句柄 / 其他进程应有的状态; 线程很容易实现相互之间通信; 创建线程代价比进程小得多, 因此实现高并发比多进程效率高
Thread / Runnable(定 target, 给线程起名) / Callable -> call()
线程生命周期: new -> runnable -> running -> blocked -> dead
直接调用 Thread 的 run() 而不是 start() 会导致子进程抢占主线程
http://7xq89b.com1.z0.glb.clouddn.com/crazy-java3-16.4.png
join(): 必须等待另一个线程完成
setDaemon(true): 设置 守护线程
sleep() / yield() -> 相同或者更高优先级的进程才可以执行 / setPriority()
线程安全: 线程切换具有不确定性, 也许进程在程序体还没有完全执行就切换到了其他线程, 导致出现线程安全问题 / 使用 syncchronized 添加同步锁
线程安全解决思路: 监控资源, 使用时添加锁
DDD(domain driven design): 领域驱动设计
同步锁 / 死锁
线程通信: 传统方式 -> wait() + notify() + notifyAll(); condition -> await() + signal() + signalAll(); BlockingQueue
线程组
线程池 / ForkJoinPool -> 任务拆分充分利用多cpu
网络编程
计算机网络: 资源共享; 信息传输+集中处理; 均衡符合+分布处理; 综合信息服务
http://7xq89b.com1.z0.glb.clouddn.com/crazy-java3-17.2.png
ip被分为 A(10) B(172) C(192) D E
ip(32位)来确认网络上的通信实体, 端口来确认提供服务的应用程序
端口(16位): 一个端口只能一个程序使用; 公认端口(<1024, 绑定特定服务) + 注册端口(<49152, 应用程序通常应该绑定在这个范围内) + 动态/私有(不建议使用这个范围内的端口)
InetAddress: 处理 IP 地址
URLDecoder / URLEncoder / URL / URLPermission
TCP/IP -> Socket -> 加入多线程 -> 记录用户信息(Map) -> 半关闭Socket(适用于Http场景) -> NIO -> AIO(NIO2.0)
UDP -> DatagramPacket(定点) -> MulticastSocket(广播)
代理服务器: Proxy / ProxySelector
类加载机制与反射
所有程序都运行在 JVM 中
类的加载: 系统需要将类的 .class 文件加入到内存; 允许预先加载; 对象是类的实例, 其实系统要使用类, 也是需要 实例化 类的, 系统为之生成一个对应的 Class 对象
类加载 -> 系统为之实例化一个对应的 Class 对象 -> 连接阶段(验证 + 准备 + 解析) -> 初始化(类变量 + 静态初始化块)
类初始化的时机
类加载机制: 全盘负责 + 父类委托 + 缓存机制
- 通过反射来查看类信息
对象: 编译时类型 + 运行时类型, 如果可以确定对象的这2种类型, 则可以使用 instanceof 来判定; 如果不确定, 则需要使用运行时信息来发现对象的真实信息, 这就必须使用反射
通过反射查看类信息
通过反射生成并操作对象
使用反射生成 Proxy 类 和 InvocationHandler 接口
反射和泛型