Java学习笔记
1. 集合框架
重点:
- HashMap
- HashTable
- ConcurrentHashMap
1.1 集合框架设计要求
-
高性能,基本集合(动态数组、链表、树、哈希表)的实现也必须是高效的;
-
允许不同类型的集合,以类似方式工作,具有高度的互操作性;
-
对一个集合的扩展和适应必须是高效的;
1.2 Java集合框架的类型
- 集合(Collection):存储一个元素值
- 三种子类型:List、Set、Queue
- 图(Map):存储键/值对映射
1.3 集合框架内容
- 接口:是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象
- 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap
- 算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

1.4 集合接口
| 1 | Collection 接口 Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。Collection 接口存储一组不唯一,无序的对象。 |
|---|---|
| 2 | List 接口 List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。List 接口存储一组不唯一,有序(插入顺序)的对象。 |
| 3 | Set Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。Set 接口存储一组唯一,无序的对象。 |
| 4 | SortedSet 继承于Set保存有序的集合。 |
| 5 | Map Map 接口存储一组键值对象,提供key(键)到value(值)的映射。 |
| 6 | Map.Entry 描述在一个Map中的一个元素(键/值对)。是一个Map的内部类。 |
| 7 | SortedMap 继承于 Map,使 Key 保持在升序排列。 |
| 8 | Enumeration 这是一个传统的接口和定义的方法,通过它可以枚举(一次获得一个)对象集合中的元素。这个传统接口已被迭代器取代。 |
1.5 Set和List的区别
-
- Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
-
- Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。
-
- List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector> 。
1.6 LinkedList
Listlist=Collections.synchronizedList(newLinkedList(...));
LinkedList 查找效率低。
1.7 ArrayList
- 该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%,插入删除效率低。
1.8 Vector
- 该类和ArrayList非常相似,但是该类是同步的,可以用在多线程的情况,该类允许设置默认的增长长度,默认扩容方式为原来的2倍。
1.9 Stack
- 栈是Vector的一个子类,它实现了一个标准的后进先出的栈。
1.10 Properties
- Properties 继承于 Hashtable,表示一个持久的属性集,属性列表中每个键及其对应值都是一个字符串。
1.11 HashSet
- 该类实现了Set接口,不允许出现重复元素,不保证集合中元素的顺序,允许包含值为null的元素,但最多只能一个。
1.12 HashMap
- HashMap 是一个散列表,它存储的内容是键值对(key-value)映射
- 该类实现了Map接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许一条记录的键为null,不支持线程同步
1.13 WeakHashMap
- 继承AbstractMap类,使用弱密钥的哈希表。
1.14 LinkedHashMap
- 继承于HashMap,使用元素的自然顺序对元素进行排序.
1.15 LinkedHashSet
- 具有可预知迭代顺序的
Set接口的哈希表和链接列表实现。
2. 内存回收
- jvm自动回收无用内存;
- 不再引用的对象,可以把其引用赋为null;
- 也可以通过System.gc()来通知GC回收器回收,但只是建议,不能保证一定可以启动GC;
3. 编码和解码
-
String(byte[] bytes,String charsetName)
通过使用指定的charset解码指定的byte数组,构建一个新的String
-
String.getbytes(Charset charset)
按照指定的编码格式(charset)编码到byte,并存储到新的byte数组中
例:
//将GBK编码转化为UTF-8 byte[] a,b; a=new String(b,"GBK").getbytes("UTF-8");
4. 抽象类、接口
4.1 抽象类
抽象方法
- 不能用private 、static、synchronized、native修饰
- 抽象方法没有方法体,是用来被继承的,所以不能用 private 修饰
- static 修饰的方法可以通过类名访问,抽象方法用 static 修饰没有意义
- synchronized 是为方法加一个锁。而如果改关键字修饰的方法是 static 方法,则使用的锁就是 class 变量的锁;如果是修饰类方法,则使用 this 变量锁。但是抽象类不能实例化对象,因为该方法不是在该抽象类中实现的,而是在其子类实现的,所以锁因该归其子类所有
- native 本身就跟 abstract 冲突,都是方法的声明,一个是把方法的实现交给子类,另一个是移交给本地操作系统,同时出现就矛盾了
4.2 接口
- 特殊的抽象类,方法全部都是抽象方法(abstract 可以省略)
- 抽象类不能用的修饰符接口也不能用
- protected 修饰符也不能用。因为接口可以让所有的类去实现,不只是其子类,但是要 public 去修饰
- 如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错
4.3 比较
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 构造方法 | Yes | No |
| 普通成员变量 | Yes | No |
| 非抽象方法 | Yes | No |
| 访问类型 | public protected | public(默认为public abstract) |
| 静态方法 | Yes | No(Java8中可以有) |
| 静态成员变量访问类型 | 任意 | public static final(默认) |
| 方法访问权限 | 全都是public | |
| 设计层面 | 对类抽象 | 对行为抽象 |
5. 多线程
5.1 volatile和synchronize
volatile关键字:
本质:告诉JVM当前变量在寄存器中的值是不确定的,需要从主存中读取
- 并不能保证在并发下是安全的;因为Java中运算并非是原子操作,虽然只有一条Java代码,但是反编译成汇编之后会包含很多条指令,在这些指令执行过程中,可能另外的线程已经对volatile变量的值进行修改
- volatile变量禁止指令重排序优化,普通变量仅仅会保证在方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致
- 线程同步的轻量级实现,性能比synchronized要好
- 只能修饰变量
- 多线程访问volatile不会发生阻塞
- 能保证数据的可见性,不能保证原子性
- 解决变量在多线程之间的可见性
- 标记的变量不会被编译器优化
- (可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时必须先刷新,可以立即获取修改之后的值);在Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或是CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰的变量则是直接读写主存
synchronized:
本质:锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞
- 如果 synchronized 修饰的方法是 static 方法,则使用的锁就是 class 变量的锁
- 如果是 synchronized 修饰类方法,则使用 this 变量锁
- 可以修饰方法、代码块、变量
- 多线程访问synchronized会出现阻塞
- 可以保证数据的原子性,也可以间接保证可见性(因为它会将私有内存和公共内存中的数据做同步)
- 解决多线程之间资源同步问题
- 标记的变量可以被编译器优化
| 性能 | 修饰范围 | 多线程访问是否会出现阻塞 | 保证数据的性质 | 解决的问题 | |
|---|---|---|---|---|---|
| volatile | 线程同步的轻量级实现,性能好 | 只能修改变量 | 不会 | 原子性 | 解决变量在多线程之间的可见性 |
| synchronized | 性能差与volatile | 可以修饰方法、代码块 | 会出现阻塞 | 原子性和可见性 | 解决多线程之间资源同步问题 |
6. 序列化
6.1 What
6.1.1 序列化(Serialization)
-
将对象的状态信息转化为可以存储或者传输的形式的过程,一般将一个对象存储到一个储存媒介,例如档案或记忆体缓冲等;
-
在网络传输过程中,可以是字节或者XML等格式;而字节或者XML格式的可以还原成完全相等的对象,这个相反的过程又称为反序列化
6.1.2 Java对象的序列化和反序列化
-
在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用此对象。但是,我们创建出来的这些对象都存在于JVM中的堆(heap)内存中,只有JVM处于运行状态的时候,这些对象才可能存在。一旦JVM停止,这些对象也就随之消失
-
在真实的应用场景中,我们需要将这些对象持久化下来,并且在需要的时候将对象重新读取出来,Java的序列化可以帮助我们实现该功能
-
对象序列化机制(object serialization)是java语言内建的一种对象持久化方式,通过对象序列化,可以将对象的状态信息保存未字节数组,并且可以在有需要的时候将这个字节数组通过反序列化的方式转换成对象,对象的序列化可以很容易的在JVM中的活动对象和字节数组(流)之间进行转换
-
在JAVA中,对象的序列化和反序列化被广泛的应用到RMI(远程方法调用)及网络传输中
7. 参数传递
在Java中只有值传递
“传递引用” 本质上还是 “值传递”,只不过传递的是地址值
例如:
- 在方法中,修改一个基础类型的参数不会影响原始参数值
- 在方法中,改变一个对象参数的引用不会影响到原始引用,他只是会在堆中创建一个全新的对象
- 在方法中,修改一个对象的属性会影响到原始对象参数
- 在方法中,修改集合和Maps的元素会影响原始参数集合
8. 常用关键字
8.1 final
- final修饰类,则类不能被继承
- final修饰方法,则不可以被重写,但是可以被重载
- final修饰变量,则等同于常量,不允许被再次赋值
9. 重载和重写
9.1 重载(Overloading)
- 方法名相同,参数类型不同,包括但不限于一项:参数数目、参数类型、参数顺序
- 被重载的方法必须改变参数列表(参数个数或类型不一样)
- 被重载的方法可以改变返回类型
- 被重载的方法可以改变访问修饰符
- 被重载的方法可以声明新的或更广的检查异常
- 方法能够在同一个类中或者在一个子类中被重载
- 无法以返回值类型作为重载函数的区分标准
9.2 重写(Overriding)
要求:两同两小一大原则
- 方法名相同,参数类型相同,返回类型相同
- 参数列表必须完全与被重写方法的相同
- 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected
- 父类的成员方法只能被它的子类重写
- 声明为 final 的方法不能被重写
- 声明为 static 的方法不能被重写,但是能够被再次声明
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以
- 构造方法不能被重写
- 如果不能继承一个方法,则不能重写这个方法
10. 匿名类
创建格式
new father() | interface(){
//匿名内部类的类体实现
}
特性
- 使用匿名内部类时,我们必须是继承一个类或者实现一个接口(两者不可兼得)
- 匿名内部类中不能定义构造函数(因为匿名类没有类名)
- 不存在任何的静态成员变量和静态方法
- 匿名内部类为局部内部类,所以局部内部类的所有限制对匿名类生效
- 不能是抽象的(abstract),必须要实现继承的类或者实现的接口的所有抽象方法
11. 对象
11.1 创建对象的方式
-
使用new关键字(最常用)
Object obj = new Object(); -
使用反射的Class类的newInstance()方法
Object obj = Object.class.newInstance(); -
使用反射的Constructor类的newInstance()方法
Object obj = Object.class.getConstructor.newInstance(); -
使用对象克隆clone()方法
Object obj = obj.clone(); -
使用反序列化(ObjectInputStream)的 readObject() 方法
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE_NAME))){ Object obj = ois.readObject(); }
12. JDBC
声明
Statement
- 继承自 Wrapper
- Statement 接口提供执行语句和获取结果的基本方法
- 用于通用查询(普通的不带参的查询SQL)
- 支持批量更新,批量删除
- 每次执行sql语句,数据库都要执行sql语句的编译 ,最好用于仅执行一次查询并返回结果的情形,效率高于PreparedStatement
PreparedStatement
- 预编译的,使用PreparedStatement有几个好处
- 在执行可变参数的一条SQL时,PreparedStatement比Statement的效率高,因为DBMS预编译一条SQL当然会比多次编译一条SQL的效率要高
- 安全性好,有效防止Sql注入等问题
- 对于多次重复执行的语句,使用PreparedStament效率会更高一点,并且在这种情况下也比较适合使用batch
- 代码的可读性和可维护性
- 继承自 Statement
- 用于执行参数化查询
- PreparedStatement 接口添加了处理 IN 参数的方法
- 可变参数的SQL,编译一次,执行多次,效率高
- 安全性好,有效防止Sql注入等问题
- 支持批量更新,批量删除
PreparedStatement中,“?” 叫做占位符,一个占位符只能对应一个值
CallableStatement
- 继承自PreparedStatement
- 一般用于存储过程
- CallableStatement接口添加了处理 OUT 参数的方法
- 继承自PreparedStatement,支持带参数的SQL操作
- 支持调用存储过程,提供了对输出和输入 / 输出参数(INOUT)的支持
13. IO
BIO(Blocking I/O)
同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待完成
同步阻塞:发起一个调用或请求后,调用者必须等待被调用者处理完请求,才能得到请求结果,此时当前线程会被挂起,无事可做,唯有等待
同步与异步
- 同步:发起一个调用后,被调用者未处理完请求之前,调用不返回
- 异步:发起一个调用后,立刻得到被调用者的回应-表示已接受到请求,但并没有返回结果,此时可以处理其他请求.被调用者通常依靠事件,回调等机制来通知调用者返回结果
阻塞与非阻塞
- 阻塞:发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续
- 非阻塞:发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情
14. equals 与 hashCOde 与 ==
hashCode( )
- 获取哈希码(散列码);
- 实际上是返回一个int整数
- 哈希码的作用是确定该对象在哈希表中的索引位置
- 定义在JDK的Object类中,Object的hashCode 方法是本地方法(C/C++实现的)
- 该方法通常用来将对象的内存地址转换为整数后返回
- 减少equals的次数,提高执行速度,用来缩小查找成本
equals( )
它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
- 情况1:类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
- 情况2:类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。
当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象
= =
它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)
为什么两个对象有相同的hashcode值,它们也不一定是相等的?
因为hashCode() 所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode)。
我们刚刚也提到了 HashSet,如果 HashSet 在对比的时候,同样的 hashcode 有多个对象,它会使用 equals() 来判断是否真的相同。也就是说 hashcode 只是用来缩小查找成本。
重写 equals 时必须重写 hashCode方法?!
- 两个对象相等,则hashCode一定相等
- 两个对象相等,对两个对象分别调用equals方法都返回true
- 两个对象有相同的hashcode值,它们也不一定是相等的
- 因此,equals方法被覆盖过,则hashCode方法也必须被覆盖
- hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)


