Java笔记,
Java笔记,
前言
大二上的时候选了Java的课,一边学着,一边磕磕绊绊地做着项目。
最近,越来越感觉到自己对Java的掌握还不够深刻,并且,一些有关Java的笔试面试题也经常让我困惑。
因此,打算在近期对Java部分的知识进行一次系统的复习,主要关注Java类库源码,JVM,对Java一些特性的深入理解。
持续更新。
- 2016/4/8 Java集合框架
- 2016/4/10 封装、继承、多态;抽象类和接口。
- 2016/4/11 泛型
Java集合框架
Java提供了一些能够有效组织和操作数据的数据结构,这些数据结构通常称为Java集合框架。
Java集合框架支持两种容器:
- collection:存储元素集合
- map:存储键值对
1 collection
- Set:用于存储一组不重复的元素
- 具体实现:TreeSet,HashSet,LinkedHashSet
- List:用于存储一个有序的元素集合
- 具体实现:Vector,Stack,ArrayList,LinkedList
- Queue:用于存储先进先出方式处理的对象
- 具体实现:PriorityQueue
TreeSet
基于TreeMap实现,实质上是将TreeSet的元素作为键,一个空的Object作为值,存入TreeMap中。具体细节在TreeMap部分讨论。
HashSet
基于HashMap实现。
LinkedHashSet
基于LinkedHashMap实现
Vector
实现方式与ArrayList类似。
区别:
1. Vector包含了一些不属于集合框架的传统方法
2. Vector是同步的
Stack
基于Vector实现的栈,先进后出。
ArrayList
1.实现方式
ArrayList基于数组实现。
2.各操作复杂度
get/set方法直接访问对应下标即可,复杂度是O(1)的。
add/delete方法的复杂度是O(n)的,
其中,add过程如下:
插入元素E到index处:将(index,end)间的内容复制到(index+1,end+1),并将index处的内容置为E。其中数组复制的部分是由c/c++实现的,效率很高。
3.扩容
当数组容量不够时,newCapacity=1.5*oldCapacity
创建大小为newCapacity的数组,然后将旧数组复制进去。
扩容时,至少扩充至10。
LinkedList
1.实现方式
基于双向链表实现。
2.各操作复杂度
基于下标的访问复杂度是O(n)的。
在已知节点前后进行插入/删除复杂度是O(1)的。
PriorityQueue
优先队列,JDK1.5引入。
1.实现方式
最小堆
2.各操作复杂度
插入删除均为O(n)
2 map
存储键值对。
具体实现:HashMap,LinkedHashMap,TreeMap
HashMap
1.实现方式
基于key的hash表。
2.要点
- 无参构造方法初始容量为16。指定容量的构造方法,容量为最小的2^n,使得容量大于指定容量。
- 当元素个数达到容量*负载因子时,容量翻倍。因此,容量总为2^n。
- 散列函数以key的hashcode为基础,经过运算得到一个数字,取其低n位(哈希表容量为2^n)作为在数组中的位置。
- hash冲突:链接法(拉链法)
- 扩容:新建一个table,将旧table的元素添加到新table中,复杂度O(n)
- 性能:存取操作的平均复杂度是O(1)的,但最坏情况下是O(n)的。
LinkedHashMap
1.实现方式
在HashMap的基础上,将所有节点加入到一个双向链表中。
2.要点
- 默认按照插入顺序存储,可指定按照访问顺序存储
TreeMap
1.实现方式
红黑树
2.各操作复杂度
查找、插入、删除操作复杂度都是O(lg n)
3 关于集合类的同步
JDK1.1遗留的Vector,Stack,Hashtable是线程安全的。
后来引入的集合类本身是不支持并发的。
但Vector,Stack,Hashtable的线程安全是有一定缺陷的。
假设下面的情况:
Vector v;
int len=v.size();
v.get(len-1);
获取v的size,根据size获取最后一个元素。
在v.size()与v.get()之间,如果另一个线程删除了v中的一个元素,那么v.get()就越界了。
因此,为达到目的,我们往往需要再加一层synchronized。
另外,Vector,Stack,Hashtable为了实现同步,效率较低。
因此不推荐使用。
面向对象的基本特征
封装
将实现细节隐藏,只提供一些接口供外部使用,称为封装。
一个相关的概念是抽象,提取一些类的特征与行为而不关注细节,称为抽象。
继承
从已有的类派生出新类,称为继承。
继承是一种提高代码可重用性的手段。另一种常用的手段是组合,将在下面提到。
构造方法链
- Tips
- 只能用super调用父类的构造方法,这个调用必须是构造方法的第一条语句
- 父类的构造方法不被继承
构造方法可以调用重载的构造方法或它的父类的构造方法。如果它们都没有被显式地调用,编译器会自动地将super()
作为构造方法的第一条语句(此时,如果父类不存在无参构造方法,编译不通过)。
子类递归地调用父类的构造方法,直到继承体系架构的最后一个构造方法被调用为止。这就是构造方法链
覆盖
子类修改父类中定义的方法的实现,称为方法覆盖
- Tips
- 仅当实例方法是可访问时,它才能被覆盖。父类中的私有方法与子类无关。
- 静态方法能被继承但不能被覆盖。
继承与组合的比较
一个对象可以包含另一个对象,这样的关系称为组合
继承和组合都是提高代码可重用性的手段,很多时候可以相互替代。
通常更推荐组合而不是继承。
- 继承
- 优点
- 子类能自动继承父类的接口
- 缺点
- 破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性
- 扩展功能时往往以增加系统结构的复杂度为代价
- 脆弱的基类
- 优点
- 组合
- 优点
- 不破坏封装,整体类与局部类之间松耦合,彼此相对独立
- 具有较好的可扩展性
- 支持动态组合。在运行时,整体对象可以选择不同类型的局部对象
- 缺点
- 整体类不能自动获得和局部类同样的接口
- 创建整体类的对象时,需要创建所有局部类的对象
- 优点
多态
父类型的变量引用子类型的对象,称为多态
动态绑定
- 声明类型:一个变量被声明时定义的类型
- 实际类型:变量引用的对象的实际类
- 变量调用的非静态方法是由实际类型决定的,称为动态绑定
抽象类和接口
抽象类
抽象类用来表示抽象概念,由abstract关键字修饰。
* 抽象类不能被实例化
* 抽象方法必须包含在抽象类中。因此,抽象类的非抽象子类必须实现所有抽象方法。
* 包含抽象对象的类必须是抽象的
接口
接口是一种与类相似的结构,只包含常量与抽象方法。
- 实现接口的非抽象类必须要实现该接口的所有方法。抽象类可以不用实现。
- 接口的所有方法都是public abstract的
区别
- 语法上
- 抽象类可以拥有任意范围的成员数据,同时也可以拥有自己的非抽象方法
- 接口只能包含常量与抽象方法
- 概念上
- 接口指明了多个对象的共同行为,关注于局部特征
- 抽象类更关注本质特征,子类与父类间是“is-a”的关系
泛型
泛型是指参数化类型的能力。可以定义带泛型类型的类或方法,随后编译器会用具体的类型替换它。
泛型的优点
- 编译时进行类型安全检查,使程序更加可靠。
- 代码更加灵活
定义泛型类
public class Example<E>{
public void method(){
}
}
定义泛型方法
public class Example{
public <E> void method(){
}
}
通配泛型
- ?
- 非受限通配符,等价于? extends Object
- ? extends T
- 受限通配,表示T或T的一个子类型
- ? super T
- 下限通配,表示T或T的一个父类型
实现
Java的泛型是通过类型擦除的方法实现的。
编译器使用泛型类型信息来编译代码,但随后会消除它。因此,泛型信息在运行时是不可用的。
泛型存在于编译时,一旦编译器确认泛型类型是安全可用的,就会将它转换为原始类型。
Tips
- 不能使用
new E()
- 不能使用
new E[]
- 在静态环境下,不允许使用泛型
- 异常类不能是泛型的
总结
其实就是提供了写起来比较好看的语法,然后由编译器在编译阶段进行了强制类型转换。在运行时跟1.5以前用Object来实现泛型并无区别。
即,泛型这一概念,在运行时是不存在的。
这样的实现被称为伪泛型。
而有些语言,如C#,在运行层面实现了泛型容器,被称为真泛型。
比较起来,Java的伪泛型存在一些劣势。
Java泛型的劣势
语法上:
- 不支持new E()
- 不支持new E[]
List<String> list
与List<Integer> list
被认为是相同的类型
性能上:
C#的真泛型性能比Java的伪泛型高很多,差距大致在10倍以上。
Java泛型的优势
- 保证了向前兼容。新版本编译后的代码可以直接在旧版本JVM上运行。
- 通配泛型在可变性上更优越
可以看到,比起真泛型,Java的泛型主要还是处在劣势的。原因其实就是Java语言在早期没有支持泛型,也没有考虑到以后添加这种特性。因此,后来再想要实现真泛型难度太大,于是退而求其次实现了伪泛型。
多线程
待补充
sleep和wait的区别
网络
待补充
JVM
待补充
相关文章
- 暂无相关文章
用户点评