导图社区 java基础学习路线
1.列出了java基础的学习顺序和大致的知识点 2.本导图只是粗略的整理,主要是提供大致学习方向 3.复习或者辅助记忆是比较好的用途
编辑于2021-09-21 02:48:41Java基础
1.基本概念
基本数据类型
byte,short,int,long,float,double,boolean,char
引用传递与值传递
值传递
对基本变量而言,传递的是对象的副本,不影响原变量。
引用传递
传递对象型变量而言,传递对象地址的一个副本,操作引用对象会改变原对象。
自动装箱拆箱
编译器在基本数据类型和对应的对象包装类型之间做一个转化, 比如int转化为Integer,反之是自动拆箱。
java特性
封装
隐藏内部代码
继承
复用现有代码
多态
改写对象行为
类和对象的区别
类是对某一类事物的描述,是抽象的。对象是一个实实在在的个体,是类的一个实例。
重写与重载
方法重载(overloading)
简介
让类以统一的方式处理不同类型的数据的一种手段,多态的体现。
在一个类中 存在两个或者两个以上的同名函数,称作为方法重载。
要求
函数名要一致。
形参列表不一致
与返回值类型无关。
方法重写(overriding)
简介
父类的功能无法满足子类的需求。
必须要存在继承的关系。
子父类出了同名的函数,这个我们就称作为方法的重写。
要求
两同
方法名与形参列表必须一致。
两小
子类返回值类型必须是父类返回值类型的子类(若返回值类型是引用时),若是基本数据类型,则必须完全一致。
子类抛出的异常类型要小于或者等于父类抛出的异常类型。
一大
子类的权限修饰符必须要大于或者等于父类的权限修饰符。
覆盖问题
子类不能覆盖父类中声明为final或者static的方法
子类必须覆盖父类中声明为abstract的方法,或者子类也必须声明为abstract
子类覆盖父类中的同名方法时,子类的方法声明也必须和父类中被覆盖的方法的声明相同
正则表达式
记录文本规则的代码
进行字符串匹配和处理的时候最为强大的工具
matches(),replaceAll(),replaceFirst(),split(),Pattern
2.基础语法
关键字
String
String
String 是final类型,数值不可改变
StringBuffer
线程安全,synchronized修饰
可对字符串进行修改,动态构造字符数据
StringBuilder
线程不安全
synchronized
修饰静态方法以及同步代码块synchronized(类.class)锁的是类,线程需要实行对应的同步代码,需要获得类锁
synchronized修饰成员方法,线程获取的是当前调用该方法的对象实例的对象锁
修饰一个方法或者一个代码块时,保证一个时刻只有一个线程执行该段代码
发生异常时,自动释放线程所占有的锁,不会导致死锁现象发生。
volatile
保证有序性和可见性
int和Integer
int
int是java的基本数据类型
Integer
Integer是java为int提供的封装类
final
修饰一个类,表明这个类不能被继承
final类的成员变量可以根据需要设为final
final类中所有的成员方法都会被隐式指定为final
final修饰基本数据类型的变量,数值一旦初始化之后便不能修改。
final修饰引用类型的变量,对其初始化后不能再让其指向另一个对象。
finally
异常处理语句的一部分,表示总是执行
finalize
在垃圾收集器执行的时候,会调用被回收对象的此方
static
表明一个成员变量或者成员方法可以在没有所属类的实例变量的情况下被访问
static方法不能被覆盖,方法覆盖基于运行时动态绑定,static方法是编译时静态绑定的,static方法和任何实例都不相关
==比较的是什么
比较对象
两个对象的内存引用,两个对象完全相同时,返回true。
基本类型
两边是基本类型,比较数值是否相等。
&与&&的区别
&
按位与
逻辑与
&&
短路运算
左边的表达式是false,右边的表达式会被短路掉,不会进行运算
u!=null&&u!="",二者顺序不能交换,也不能使用&,否则会产生空指针异常
||和|
||和|都是表示“或”,区别是||只要满足第一个条件,后面的条件就不再判断,而|要对所有的条件进行判断。
变量的使用
运算符
基础语法
==比较的是什么
基本类型
两边是基本类型,比较数值是否相等。
比较对象
两个对象的内存引用,两个对象完全相同时,返回true。
equal比较的是什么
引用比较
默认情况下是引用比较
值比较
很多类重写了该方法,编程了值比较 如Integer,String
&与&&的区别
&
按位与
逻辑与
&&
短路运算
左边的表达式是false,右边的表达式会被短路掉,不会进行运算
u!=null&&u!="",二者顺序不能交换,也不能使用&,否则会产生空指针异常
||和|
||和|都是表示“或”,区别是||只要满足第一个条件,后面的条件就不再判断,而|要对所有的条件进行判断。
Math. round(-1. 5)的值
等于 -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃
流程控制
3.数组
4.类和对象
包装类
简介
为了将基本类型作为对象进行处理,连接相关的方法,java为每个基本类型提供了包装类。
Integer
简介
包装了基本类型int的值。
该类的对象包含一个int类型的对象。
方法
构造方法
Integer(int number)
int 变量为参数获取Integer对象。
Integer(String str)
String 变量1为参数获取Integer对象。
常用方法
byteValue()
返回值
byte
作用
以byte类型返回Integer的值。
compareTo(Integer anotherInteger)
返回值
int
作用
相等返回0,小于ano..返回负值,否则返回正值。
equals(Object IntegerObj)
返回值
boolean
作用
比较指定对象是否相等。
intValue()
返回值
int
作用
以int类型返回Integer对象。
shortValue()
返回值
short
作用
以short类型返回Integer对象。
toString()
返回值
String
作用
返回Integer的String对象
valueOf(String str)
返回值
Integer
作用
返回String值的Integer对象
parseInt(String str)
返回值
int
作用
返回调用该方法的数值字符串相应的int值。
进制转换
十进制
toString(int num)
二进制
toBinaryString(int num)
十六进制
toHexString(int num)
八进制
toOctalString(int num)
Boolean
简介
一个Boolean类型的对象只包含一个类型为boolean的字段。
方法
构造方法
Boolean(boolean value)
创建一个表示value参数的Boolean对象。
Boolean(String str)
str为true则为true,否则为false。
常用方法
booleanValue()
返回值
boolean
作用
Boolean对象以boolean值返回。
equals(Object obj)
返回值
boolean
作用
boolean对象是否相等。
toString()
返回值
String
作用
返回boolean值的String对象。
parseBoolean(String str)
返回值
boolean
作用
将字符串参数解析为boolean值。
需要基本类型用此方法。
valueOf(String str)
返回值
boolean
作用
返回用指定字符串表示值的boolean值。
需要对象用此方法。
Byte
简介
一个Byte类型的对象只包含一个类型为byte的字段。
方法
构造方法
Byte(byte value)
Byte(String str)
常用方法
byteValue()
返回值
byte
作用
以byte值返回Byte对象。
doubleValue()
返回值
double
作用
以double值返回此Byte的值。
intValue()
返回值
int
作用
以int值返回byte的值。
toString()
返回值
String
作用
返回Byte值的String对象。
parseByte(String str)
返回值
byte
作用
将String值解析成等价byte形式。
valueOf(String str)
返回值
byte
作用
返回指定String值的Byte对象。
equals(Object obj)
返回值
boolean
作用
Byte对象进行比较。
compareTo(Byte anotherByte)
返回值
int
作用
在数字上比较两个Byte对象。
Character
简介
一个Character类型对象包含类型为char的单个字段。
提供大小写转换等方法。
方法
构造方法
Character(char value)
Character icahr = new Character('icahr');
常用方法
charvalue()
返回值
char
作用
返回Character对象的值。
charValue()
返回值
char
作用
返回Character对象的值。
toString()
返回值
String
作用
指定char值的String对象。
compareTo(Character anotherCharacter)
返回值
int
作用
比较两个Character的值。
equals(Object obj)
返回值
Boolean
作用
对象进行比较。
toUpperCase(char ch)
返回值
char
作用
将字符参数转化为大写。
toLowerCase(char ch)
返回值
char
作用
将字符串转化为小写。
isUpperCase(char ch)
返回值
boolean
作用
判断指定字符是否为大写字符。
isLowerCase(char ch)
返回值
boolean
作用
判断指定字符是否为小写字符。
Double
方法
构造方法
Double(double value)
Double(String str)
常用方法
byteValue()
返回值
byte
作用
byte形式返回Double的值。
longValue()
返回值
long
作用
以long的形式返回double的值。
intValue()
返回值
int
作用
以int的形式返回Double的值。
doubleValue()
返回值
double
作用
以double形式返回Double对象。
valueOf(String str)
返回值
Double
作用
返回str表示的double值的Double对象。
toString()
返回值
String
作用
返回Double对象的字符串表现形式。
compareTo(Double d)
返回值
int
作用
对象之间的比较。
equals(Object obj)
返回值
boolean
作用
对象的比较。
isNan()
返回值
boolean
作用
如果double的值是非数字,返回true,否则返回false.
Number
子类
BigDecimal
BigInteger
Byte
Double
Float
Integer
Long
Short
方法
byteValue(()
intValue()
floatValue()
shortValue()
longValue()
doubleValue()
抽象类与接口
接口
概念
接口是对行为的抽象,是抽象方法的集合,利用接口可以达到API定义和实现分离的目的
特点
不能实例化。
支持多重继承
没有构造方法,没有非静态方法实现,要么是抽象方法,要么是静态方法
只能包括public函数及public static final常量
抽象类
概念
抽象类是不能实例化的类,用abstract关键字修饰,期目的是代码重用。
特点
除了不能实例化,形式上和一般java类没有太大区别
不支持多重继承
有构造方法,不能实例化
方法
Object类方法
clone()
创建并返回一个对象的副本
finalize()
垃圾收集器确定不存在对该对象的更多引用时,由对象的垃圾收集器调用此方法
hashCode()
返回对象的哈希码
wait()
导致当前的线程等待,指导其他线程调用notify()或者notifyAll()或者超过指定的时间。
notify()
唤醒在此对象监视器上等待的单个线程
notifyAll()
唤醒在此对象监视器上等待的所有线程
接口
Map
HashMap
特点
根据键的HashCode值存储数据,访问速度快
只允许一条1记录的键值为Null,允许多条记录值为NuLL
遍历时,随机取得数据
不支持线程同步,任意时刻可有多个线程同时写HashMap
遍历速度与容量有关,容量大,数据少时,遍历慢
Hashtable
特点
不允许键或值为空
支持线程同步,任意时刻只有一个线程能写Hashtable,导致写入时比较慢
LinkedHashMap
特点
保存了记录的插入顺序,Iterator遍历时,得到的记录很定是先插入的
遍历数据只与实际数据有关,和容量无关
TreeMap
特点
实现SoreMap接口,把保存的记录根据键值排序,默认按键值升序排序,Iterator遍历得到的记录是排过序的。
Array
Array
可以包含基本类型和对象类型
大小固定
ArrayList
只能包含对象类型
大小可以动态变化
提供了更多的方法和特性
Lock
发生异常时,若没有主动通过unLock()释放锁,很可能会造成死锁现象。
使用Locck需要在finally块中释放锁
Lock可以让等待锁的线程响应中断,synchronized不行,使用synchronized,等待的线程会一直等待下去,不能响应中断;Lock可以知道有没有成功获得锁,synchronized无法办到。
对象
划分
静态部分
属性
动态部分
行为
概念
类是封装对象属性和行为的载体
对象是类抽象出来的一个实例
封装
简介
面向对象编程的核心思想
将对象的属性和行为封装起来,载体就是类
特点
封装性
继承性
多态性
继承
简介
如四边形和平行四边形
多态
简介
允许以统一的风格编写程序,处理种类繁多以存在的类及相关类。
依赖于
抽象类
父类通常定义为抽象类,给出一个方法的标准,不给出实现的具体流程。
接口
比抽象类更方便的是把抽象类定义为接口。
类
概念
具有相同特性和行为的一类事物称为类。
类是世间事物的抽象称呼,对象是这个事物相对应的实体。
变量
成员变量
简介
普通的变量。
可以设置初始值,也可以不设置初始值。
实例
private String name;
局部变量
简介
在成员方法内定义的一个变量。
在方法执行的创建,结束时销毁。
使用时必须进行赋值操作或者初始化,不然编译错误。
方法
成员方法
简介
成员方法对应于类对象的行为。
范围
局部变量分为称为称为作用域。
从生命开始到变量结束位置。
实例
public String getName(){ int id =0;//局部变量 return this.name; }
静态成员
简介
static声明的变量,常量,方法。
属于类所有,类名.可调用。
不能将方法体内的局部变量声明为static。
实例
静态常量
final static String name = hly;
静态变量
static String id =123;
静态方法
public static void test(){}
静态区域 执行类的初始化动作
public class User{ static{ } }
this
简介
代表本类对象的引用。
区分参数变量和成员变量。
实例
public User getUser(){ return this; }//将整个对象返回
构造方法
简介
没有返回值。
名称与本类名称相同。
没有定义任何构造方法时,编译器会自动创建不带参数构造方法。
可以为成员变量赋值,实例化对象,成员变量也被初始化。
实例
public class User{ public User(){ this("asd");//调用下边有参构造方法。 } public User(String str){ syso }
权限修饰符
private
本类:可见
同包的类:不可见
其他包的类:不可见
protected
本类:可见
同包的类:可见
其他包的类:不可见
public
本类:可见
同包的类:可见
其他包的类:可见
5.异常处理
final,finally,finalize
区别
final
final修饰的类不能被继承
final修饰的变量不能被修改
final修饰的方法不能被重写
finally
保证重点代码一定要被执行的一种机制
使用try-finally,try-catch-finally关闭JDBC连接等
finalize
Object的一个方法,java虚拟机在实现gc时候调用回收对象的这个方法。
使用要求
无法保证 finalize 什么时候执行,执行的是否符合预期,使用不当会影响性能,导致程序死锁,挂起
实现类
Throwable
Error
StackOverflowException
OutOfMemoryError
Exception
IOException(checked)
RuntimeException
NullpointerException
ArrayIndexOutOfBoundsException
throw & throws
throw
抛出一个异常
throws
声明可能会抛出一个异常
类型
Exception
概念
可以被抛出和捕获异常
分类
checked
可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的异常
unchecked
不检查异常就是所谓的运行时异常,类似空指针,通常可以编码避免错误,根据需要来判断是否需要捕获。
Error
概念
正常情况下,不大可能出现的情况,导致程序宕机,不便于也不需要捕获
int和Integer
int
概念
int是java8个原始数据类型之一,java号称一切都是对象,但原始数据类型是例外。
Integer
概念
Integer是int的包装类有一个int类型的字段存储数据,并提供了基本操作,比如数学运算与字符之间的转换。引入了自动装箱和拆箱功能。
自动装/拆箱
一种语法糖,java平台自动进行一些转换,保证了不同写法在运行时等价,发生在编译时期,生成的字节码是一致的。javac会自动把装箱转换为Integer.valueOf(),拆箱替换为Integer.intValue()。
源码
缓存
java5添加了静态工厂方法valueOf(),调用时会利用一个缓存机制,这个值默认为-128-127之间
处理要求
不要生吞异常
尽量不要捕获类似Exception这样的通用异常,而是应该捕获特定异常
java 每实例化一个 Exception,都会对当时的栈进行快照,比较重的操作。
try-catch会产生额外的性能开销,最好是包括需要检验异常的代码,不要包含过长代码,影响JVM对代码进行优化
ps
finally一定会执行,如果catch中return了,catch中的return会等finally中的代码执行完后,才会执行。
异常处理
简介
程序执行期间发生的事件,中断了正在执行的程序的正常指令流。
捕捉异常
捕获结构
try
catch
getMessage()
输出异常错误的性质。
toString()
给出异常的类型与性质。
printStackTrace()
指出异常类型,性质,栈层次以及出现在程序中的位置。
finally
代码退出,都将执行。
不执行情况
finally语句块发生异常。
使用了System.exit()退出程序。
程序所在的线程死亡。
关闭CPU。
常见异常
ClassCastException
类型转换异常。
ClassNotFoundException
未找到相应类异常。
ArithmeticException
算数异常。
ArrayIndexOutOfBoundsException
数组下标越界异常。
ArrayStoreException
数组中包含不兼容的值
SQLException
操作数据库异常
NullPointerException
空指针异常
NoSuchFieldException
字段未找到异常
NoSuchMethodException
方法未找到异常
NumberFormatException
字符转化为数字异常
NegativeArraySizeException
数组元素个数为负数异常。
StringIndexOutOfBoundsException
字符串索引超出范围异常。
IOEException
输入输出异常。
IllegaAccessException
不允许访问某类异常
InstantiationException
无法实例化newInstance()异常
EOFException
文件已结束异常
FileNotFoundException
文件未找到异常
抛出异常
简介
可能会发生异常,但不想在当前方法中处理这个异常。
方法
throws
用在声明方法时,指定方法可能抛出的异常,多个异常逗号分隔。
抛给上一级,若不想处理,可基础抛出,但最终需要处理该异常。
throw
用于方法体中,抛出异常对象,程序执行到thrwo语句时立即终止。
通常用来抛出用户自定义的异常。
运行时异常
Throwable
Error
LinkageError
IOError
其他
Exception
RuntimeException
ClassCastException
ArraySizeException
NullPointerException
空指针异常。
ArrayIndexOutOfBoundsException
数组下标越界异常。
ArithmeticException
算数异常。
ArrayStoreException
数组包含不兼容的值。
IllegalArgumentException
非法参数异常。
SecurityException
安全性异常。
NegativeArraySizeException
数组长度为负数异常。
子类
子类
6.多线程
并发编程
概念
锁
悲观锁
乐观锁
公平锁
简介:多个线程按照顺序排队获得锁 优点:所有线程都能获得锁,不会而死在队列中 缺点:吞吐量下降,除了第一个线程,其他的线程都会被阻塞,CPU唤醒线程开销大 开启公平锁,默认我非公平 ReentrantLock lock = new ReentrantLock(true);
非公平锁
简介:多个线程不用去排队,直接尝试获取锁 优点:减少CPu唤醒线程的开销,提高整体吞吐率 缺点:可能导致队列中的有些线程一直得不到锁
独占锁
共享锁
CAS
简介
基于乐观锁,比较和替换
原理
线程在读取数据的时候不加锁,在写数据之前,先比较和原来的值是否相同,如果相同则写回,如果不同,重新执行读取流程。
缺点
死循环CPU开销大
ABA
解决
添加版本号
添加时间戳
实现
原子类
AQS
简介
队列同步器
特点
state
标记位,值为1表示线程占用,其他线程进入同步队列等待,同步队列是双向链表
获得锁的线程需要等待某个条件时,就会进入condition等待队列,等待队列可以有多个
condition条件满足时,就会从等待队列进入同步队列竞争锁
实现
ReentrantLock
独占锁
Semaphore
共享锁
happen-before
八大原则
单线程happen-before原则
在同一个线程中,书写在前面的操作happen-before后面的操作。
锁的happen-before原则
同一个锁的unlock操作happen-before此锁的lock操作。
volatile的happen-before原则
对一个volatile变量的写操作happen-before对此变量的任意操作
happen-before的传递性原则
如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作
线程启动的happen-before原则
同一个线程的start方法happen-before此线程的其它方法。
线程中断的happen-before原则
对线程interrupt方法的调用happen-before被中断线程的检测到中断发送的代码。
线程终结的happen-before原则
线程中的所有操作都happen-before线程的终止检测。
对象创建的happen-before原则
一个对象的初始化完成先于他的finalize方法调用。
原理
sync
简介
基于悲观锁
锁的位置
对象
组成
对象头
Mark Word(标记字段)
HashCode
分代年龄
锁标志位
偏向线程ID
线程持有的
指向Monitor对象的地址
—>
Monitor对象
被某个线程持有后,就会处于锁定状态
EntryList
进入获取锁的线程 如果线程进入,则得到对象锁,别的线程在该类所有对象上的任何操作都不能执行
Ower
持有对象的线程
WaitSet
等待获取锁的线程
Klass Pointer(类型指针)
存放对象指向他的类元数据指针,虚拟机根据这个指针判断是哪个类的实例
实例数据
对齐填充
子主题
同步方法
flags: ACC_SYNCHRONIZED标志
有标志位代表别的线程拥有锁
实现
同步代码块
实现
monitorenter
monitorexit
原理
每个对象都与一个monitor关联,某个对象的monitor被拥有后就会被锁住
线程执行到monitorenter,就会尝试获得对应的monitor
每个monitor维护者一个记录被拥有次数的计数器
未被拥有的monitor计数器为0,一个线程执行monitorenter获得monitor后,计数器+1,再次获得则自增,不同线程想要获得该monitor就会阻塞
同一个线程执行monitorexit指令,释放monitor,计数器自减
计数器为0,monitor释放,其他线程获得
锁升级
过程
无锁
偏向锁
一个线程获取锁,会使用偏向锁优先同一线程再次获取锁
轻量级锁
获取锁失败升级为CAS轻量级锁,进行短暂自旋
重量级锁
重量级锁通过对象内部加湿器monitor实现,其中Monitor的实现是通过底层操作系统的Mutex Lock实现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。
如果以上都失败就会升级为重量级锁
资料
https://blog.csdn.net/qq_35190492/article/details/104691668
this,Object,.class
https://blog.csdn.net/weixin_39841821/article/details/79902810
happen
https://www.jianshu.com/p/1508eedba54d/
并发容器
集合
Vector
CopyOnWriteArrayList
Collections
简介
并发集合类,可以通过方法调用返回一个加了锁的集合类。
ArrayList<String> arrayList = new ArrayList<>(); List<String> synchronizedList = Collections.synchronizedList(arrayList);
哈希
Hashtable
简介
基于HashMap,对所有的方法都加了锁。
ConcurrentHashMap
简介
线程安全的 HashMap
原理
1.7 使用分段锁,每次插入数据,只会锁住一小段。
1.8 synchronized + CAS
ConcurrentSkipListMap
其他
ThreadLocal
简介
提供一个局部变量,每个线程拥有自己的的局部变量,只在当前的线程范围内有效。在线程A设置的值,在线程B是使用不了的。
方法
void set(Object value)
设置当前线程的线程局部变量的值
Object get()
返回当前线程所对应的线程局部变量
remove()
将当前线程局部变量的值删除
当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
initialValue()
返回该线程局部变量的初始值
场景
MyBatis整合Spring时保存了数据库连接
子主题
队列
ConcurrentLinkedQueue
简介
并发的链表队列。
线程里面调用了一个poll方法,poll底层实现是cas。
LinkedBlockingQueue
简介
阻塞队列
在阻塞式容器里面加了一个方法,put,也就是如果满了就会等待,对应的方法叫take,如果空了就会等待。
ArrayBlockingQueue
简介
有界队列,意思就是说这个队列能装的元素的个数是固定的
超过了可能会出问题可能会阻塞,这里看你调用什么方法
add会报异常
offer不会报异常,他只通过布尔类型的返回值来告诉你是加成功了还是没有加成功。
offer可以设置时间,如果这段时间加不进去就不加了也就是返回false。
put方法是满了会阻塞住。
DelayQueue
简介
容器里每一个元素都设置了一个时间,时间到了才能从中提取元素。
LinkedTransferQueue
简介
和普通的queue的方法差不多,多了一个transfer方法。
如果你用这种队列的话,往往是消费者先启动, 生产者生产一个东西的时候,他先是去找消费者,如果有消费者就直接丢给消费者。
SynchronizedQueue
简介
同步队列
同步队列是容量为0,也就是来的东西必须给消费掉。
只能调用put,意思就是阻塞等待消费者消费。put里面其实用的是transfer,任何东西必须消费,不能往容器里面扔。调用add方法,会报错了
多线程(基础)
简介
作用
多线程是为了提高程序的执行效率
进程与线程
进程是操作系统资源分配的基本单位,线程是任务调度和执行的基本单位
同一个进程的线程共享该进程的地址空间,而进程之间则是独立的地址空间
一个程序至少有一个进程,一个进程至少有一个线程
并行/并发
并行
多个处理器同时处理多个任务(两个队列在两个窗口打饭)
并发
多个任务在同一个CPU上按时间片轮流交替执行(两个队列争着在在1个窗口打饭)
同步/异步
同步
顺序执行
同步是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调用返回,才会进行下一步。
异步
同时执行
其他任务不需要等待当前调用返回,通常依靠事件,回调机制来实现任务间的次序关系。
场景
多线程下载,爬虫,ajax异步上传
多线程
分类
守护线程
简介
一种特殊的进程,独立于终端,周期性的执行任务
特点
进程不存在或者主线程停止,守护线程就会退出
场景
gc线程
后台下载
使用
setDaemon(true)设置为守护线程
非守护线程
特点
也叫用户线程,主线程创建的线程
和主线程互不影响
主线程结束,并不代表子线程会结束
状态
新建
new Thread()
就绪
执行之前,调用了 start()方法,运行状态调用了yield()方法
运行
获得CPU资源,执行了 run()方法,同一时刻只有一个线程处于运行状态
阻塞
调用了join(), sleep(),尝试获得一个占用的锁
死亡
抛出异常或者 stop()
创建线程的方式
继承Thread
实现Runnable
匿名内部类
Callable +FutureTask 方式
线程池
常用API
Thread
函数
Thread()
创建新的线程
Thread(String name)
创建指定名字的线程
Thread(Runable r)
通过Runnable创建线程
Thread(Runable r, String name)
指定名字
API
start()
启动
join()
主线程调用t1.join(),表示主线程把执行权让给 t1,t1执行完后,主线程才执行
yield()
让出CPU的执行权(无法保证一定成功),暂停当前线程,执行其他相同优先级的线程
setPriority(10)
设置优先级,10最高,默认为5
getID()
获得线程ID
currentThread()
获得当前线程对象
getName()
获得线程名字
sleep(long mill)
睡眠
Stop()
停止,不推荐
Object
API
wait()
暂定当前正在执行的线程,并释放锁资源,让其他线程运行
notify()
随机唤醒因锁池中的线程
notifyAll()
唤醒所有因锁池中的线程
区别
wait和sleep
1、sleep属于Thread,wait属于Object
2、sleep让出CPU给其他线程,不会释放对象锁,wait会释放对象锁
3、wait()、notify()、notifyAll()必须在synchronized里面进行使用
多线程进阶(二)
概念
重排序
重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。
指令重排之前 int i =0 ; int k = 1; i++;//CPU又去内存找i 指令重排之后 int i =0 ; i++; int k = 1;
as-if-serial 语义
简介
不管如何进行重排序,单线程程序执行的结果不能改变。为了准守该规则,编译器和处理器不会对存在依赖关系的操作进行重排序,因为重排序操作会改变执行结果。as-if-serial 语义保护单线程程序,无需担心重排序问题的干扰。
happens- before 8大原则
简介
如果一个操作的执行结果需要对另一个操作可见,那么两个操作之间必须穿在happen-before关系。这里的两个操作可以在一个线程内,也可以在多个线程内。
原则
程序顺序规则
一个线程内要按照代码的顺序执行,保证语义的串行性
监视器锁规则
解锁操作必须发生在对同一个锁的加锁操作之前
volatile变量规则
对volatile修饰的变量的写操作发生在volatile变量的读操作之前。访问volatile的变量时候强制从主内存中进行读取,volatile修饰的变量被修改之后会被强制的刷新到主内存中。所以说volatile修饰的变量保证了可见性
传递性
线程A生在线程B之前,线程B发生在线程C之前,则线程A发生在线程C之前
线程启动规则
线程的start()方法最先执行,线程A在线程B执行start()方法之前对共享变量的修改对线程B可见
线程终止规则
线程的所有操作优于线程的终结,Thread.join()方法的作用是等待当前线程终止,线程B在终止之前修改了共享变量,执行完成后,线程B对共享变量的修改将对线程A可见
线程中断规则
对线程interrupt中断方法的调用发生在被中断的线程检测到中断事件的发生之前
对象终结规则
对象的构造函数执行完成先于finalize()方法
资料
详见Java并发编程之美
关键字
volatile
1、修饰变量
2、不会造成线程阻塞
3、保证可见性,不保证原子性
4、禁止指令重排序
5、通过内存屏障实现,读无差别,写数据稍慢
synchronized
1、修饰方法,代码块
2、会造成线程阻塞
3、保证可见性和原子性
4、没有禁止重排序
线程池
创建方式
1、newCacheThreadPool()
处理大量短时间工作任务的线程池,试图缓存线程池并重用,如果没有线程池,就会创建线程池。如果闲置时间超过60秒,则被终止并移除缓存,长期闲置时,这种线程池不会消耗什么资源,内部使用SynchrousQueue作为工作队列。
2、newFixThreadPool(int n)
重用指定数目的线程,背后是无界的工作队列,任何时候都有n个线程活动。意味着,如果超过了活动线程数目,将在工作队列中等待空闲线程出现。如果工作线程退出,将有新的工作线程被创建,以补足指定数目的线程数。
4、newSingleThreadSchduledExecutor()
创建的是ScheduleExecutorService,可以进行定时或者周期性的工作调度,单一线程。
5、newScheduledThreadPool(int n)
创建的是ScheduleExecutorService,可以进行定时或者周期性的工作调度,多个线程。
6、newWorkStealingPool()
可以并行处理任务,不保证顺序处理。
3、newSingletonThreadExecutor()
工作线程数限制为1,操作一个无界的工作队列,保证了所有任务都是被顺序执行,最多有一个任务处于活动状态,并且不允许使用者改变线程池的实例,因此可以避免改变线程池的数目。
常用参数
corePoolSize
基本线程数量
maximumPoolSize
最大线程数量
keepAliveTime
空闲线程存活时间
timeUnit
keepAlibeTime的 时间单位
runnableTaskQueue
任务队列和大小
handler
实际线程达到 maximumPoolSize,并且队列已经满时,就会调用饱和策略。 1、AbortPolicy:默认直接抛出异常 2、CallerRunPolicy该任务被线程池拒绝,由调用 execute 方法的线程执行该任务; 3、DiscardOldestPolicy:丢弃最老的一个,也就是马上要执行的一个任务; 4、DiscardPolicy:丢弃当前任务。
饱和策略
多线程进阶(一)
简介
JAVA内存模型
概念
Java内存模型JMM(注意:JAVA内存结构是JVM)。
决定一个线程对共享变量的的写入时,能对另一个线程可见。
JMM定义了线程和主存之间的抽象关系,线程之间的共享变量存储在主存中,每个线程都有一个私有的本地内存,本地内存存储了该线程以读/写共享变量的副本。
类型
主内存
主要存放共享的全局变量。
私有本地内存
本地线程私有的变量。
本地内存是JMM的一个抽象概念,并不真实存在,他涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
线程三大特性
原子性
保证数据一致性,保证线程安全
可见性
一个线程对另一个线程是否可见
有序性
线程之间执行有顺序
线程安全
线程安全概念
当多个线程同时共享同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题,做读操作时不会发生数据冲突问题。
线程同步概念
概念
多个线程共享一个全局变量,不会受到其他线程的干扰
保证数据原子性,线程安全问题
前提
两个或两个以上的线程
多个线程使用同一把锁
一个线程在同步中执行
原理
一个线程拿到锁,其他线程需要排队,等待线程释放锁
代码执行完毕或者程序抛出异常后释放锁
线程死锁
同步中嵌套同步,导致锁无法释放
线程安全解决方法
synchronized
使用
同步代码块
用法
synchronized(this){}
优点
解决多线程安全问题
同一时刻只有一个线程执行该代码块
特点
使用自定义锁
弊端
需要判断锁,比较消耗资源,需要抢锁
非静态同步函数
用法
方法上添加synchronized
特点
使用this锁
静态同步函数
用法
方法上添加 static synchronized
特点
使用当前函数所属字节码文件.class 对象
特点
1、JVM 层面
2、重入锁,悲观锁
3、只能是非公平锁
4、不能尝试获得锁
5、synchronized 可以用于方法,代码块
6、在发生异常的时候,会自动释放线程占有的锁
7、要么随机唤醒一个线程,要么全部唤醒
Lock
特点
1、JDK 层面
2、重入锁,悲观锁
3、默认为非公平锁,可以实现公平锁
4、让线程尝试获得锁,在无法获得锁的时候就会进行等待或者立即返回
5、只能用于代码块
6、在发生异常的时候,如果没有使用 unlock 释放,就会造成死锁
7、提供了一个 Condition 条件,可以用来分组唤醒线程
7.java常用类
String,StringBuffer,StringBuilder
区别
String
String是对象,典型的Immutable类,被声明为final,所有属性不可变,保证了基础的线程安全,由于不可变性,拼接,裁剪等动作,都会产生新的String对象。 PS:String="123" 会被分配到常量池中,new String()会被分配到堆内存中。
StringBuffer
在各种修改数据的方法上添加了synchronized解决拼接产生的问题提供的一个类,线程安全的可修改字符序列,带来了额外开销。
StringBuilder
去掉了线程安全的部分,有效减小了开销
特点
JDK8中非静态的拼接会自动被javac转换为StringBuilder操作
StringBuilder和StringBuffer底层使用了可修改的char,JDK9改为了byte,值可以改变,对象引用不会改变,构造的过程中,先申请字符数组,超过默认大小后,会创建更大的数组,并将原先数组复制过来,丢弃原来数组。
String使用频繁,引入了字符串常量池,创建一个字符串时,首先检查池中是否有相同的字符串对象有则直接取出,没有则创建对象把对象放入池中,new方法创建的对象不检查字符串池,直接在堆区或者栈区创建新的对象。Object obj = new Object() obj是对象的引用,位于栈中,new Object()产生的数据才是对象,位于堆中。
new String()提供了intern()方法,如果池中有等于String对象的字符串,则返回池中的字符串,如果没有则在池中创建一个字符串并返回该字符串,如果堆中有该字符串池中没有,则将此对象指向堆中的引用放入池中,并返回堆中的对象引用。
语法特点
重写equal还要重写hashcode
hashcode()继承于Objec类,hashcode码默认为对象的内存地址, 两个相同含义的对象,比较也不相等。equal()比较的是对象的地址。
HashMap比较key是否相等,需要使用这两个函数,先求出key的hashcode(),若相等,再比较equal,equal相等则认为他们相等。
若只重写hashcode(),不重写equal(),比较equal时只是比较他们是否为同一对象,所以必须两个方法一起重写。
重载hashcode()为了同一个key能得到相同的HashCode,重载equal()为了向HashMap表名当前对象和key上所保存的对象是相同的。
8.枚举类和注解
java平台
编译运行
解释执行
解释器在运行时逐行将代码翻译成机器码,交给机器执行。
编译执行
存在JIT编译器(Just In Time Compile 即时编译器)以方法为单位把经常运行的代码作为"热点代码"编译成本地平台相关的机器码,并进行各种层次的优化。
AOT编译器
Java 9提供的直接将所有代码编译成机器码执行。
解释执行和编译执行有何区别
一个是同声传译,一个是放录音
类加载
类加载器
bootstrap、application、Extension Class-loader
类加载过程
加载、链接(验证,准备,解析)、初始化
运行机制
实例化顺序(重上往下)
父类静态代码块>子类静态代码块>父类成员>父类非静态代码块>父类构造方法>子类成员>子类非静态代码块>子类构造方法
执行顺序
执行顺序优先级:静态块>main()>构造块>构造方法
静态块:用static申明,JVM加载类时执行,仅执行一次
构造块:类中直接用{}定义,每一次创建对象时执行
值类型与引用类型
传递
只有基本数据类型和String s="hello";的传递是值传递,其他对象传递都是引用传递。 数组属于对象,所以是引用传递,引用传递只要不重新开辟内存空间,修改都是有效的。如果重新开辟空间,则引用的对象会被解析为同名的局部变量,原引用消失。
面向对象的基本要素
封装
隐藏内部代码,提高安全性和简化编程。
继承
复用现有的代码
多态
改写对象的行为
方法重载(overloading)
简介
让类以统一的方式处理不同类型的数据的一种手段,多态的体现。
在一个类中 存在两个或者两个以上的同名函数,称作为方法重载。
要求
函数名要一致。
形参列表不一致
与返回值类型无关。
方法重写(overriding)
简介
父类的功能无法满足子类的需求。
必须要存在继承的关系。
子父类出了同名的函数,这个我们就称作为方法的重写。
要求
两同
方法名与形参列表必须一致。
两小
子类返回值类型必须是父类返回值类型的子类(若返回值类型是引用时),若是基本数据类型,则必须完全一致。
子类抛出的异常类型要小于或者等于父类抛出的异常类型。
一大
子类的权限修饰符必须要大于或者等于父类的权限修饰符。
覆盖问题
子类不能覆盖父类中声明为final或者static的方法
子类必须覆盖父类中声明为abstract的方法,或者子类也必须声明为abstract
子类覆盖父类中的同名方法时,子类的方法声明也必须和父类中被覆盖的方法的声明相同
Java的引用
简介
java语言中,除了原始数据类型,其他类型都是引用类型,指向不同的对象
不同的引用,体现的是对象不同的可达性状态和对垃圾收集的影响
分类
强引用
Object o =new Object();
普通对象的引用,强引用指向一个对象,表明对象还活着,垃圾收集器不会碰这种对象。
普通对象如果没有其他引用关系,只要超过引用的作用域或者显示将引用赋值为null,就可以被垃圾收集了
软引用
SoftReference
相对强引用弱化一点的引用,让对象避免一些垃圾收集,JVm内存不足时,才会试图收集软引用指向的对象,JVM在抛出OutOfMemoryError之前,才会清理软引用指向的对象。
通常用来实现内存敏感的缓存,如果还有空闲内存,可以暂时保留缓存,内存不足时清理掉,保证了使用缓存的同时,不会耗尽内存,比如图片缓存。
弱引用
WearkReference
并不能是对象避免垃圾收集,仅仅提供一种访问弱引用状态下对象的途径。
可以用来构建一种没有特点约束的关系,维护一种非强制性的映射关系,如果获取时对象还在就使用,否则重新实例化,也是很多缓存实现的选择。
幻象引用
Object o =new Object(); o=null
也叫作虚引用,不能通过他访问对象,仅仅提供一种确保对象被finalize后,做某些事情的机制。
通常用来做Post-Mortem清理机制,Cleaner机制,监控对象的创建和销毁。
可达性级别
强可达
一个对象可以有一个或多个线程可以不通过各种引用访问到的情况,我们创建一个对象,创建他的线程对他就是强可达。
软可达
我们只能通过软引用才能访问到对象的状态
弱可达
无法通过强引用或者软引用访问,只能通过弱引用访问的对象,接近finalize状态,弱引用被清除,就是finalize条件。
幻象可达
没有强,软,若引用关联,且finalize过了,只有幻象引用指向这个对象的时候。
不可达
对象可以被清除了。
14.java8语法特性
java8新特性
Lamda表达式
优点
简介;容易并行计算;未来编程趋势。
缺点
没有并行计算,速度没有传统的for循环快;不容易调试。
13.反射
动态代理&反射
编程语言类型
动态类型
运行时检查,缺点是生成代理对象和调用代理方法都要额外花费时间。
静态类型(java)
编译时检查,事先写好代理类,可以手工编写,也可以用工具生成,缺点是每个业务都要对应一个代理类,非常不灵活。
反射
概念
反射最大的作用之一就在于我们可以不在编译时知道某个对象的类型,而在运行时通过提供完整的”包名+类名.class”得到。注意:不是在编译时,而是在运行时。
功能
1.在运行时能判断任意一个对象所属的类。2.在运行时能构造任意一个类的对象。3,在运行时判断任意一个类所具有的成员变量和方法。4.在运行时调用任意一个对象的属性和方法。
场景
反射技术常用在各类通用框架开发中。因为为了保证框架的通用性,需要根据配置文件加载不同的对象或类,并调用不同的方法,这个时候就会用到反射——运行时动态加载需要加载的对象。
实例
反射操作
简介
在运行,动态获取这个类的信息
三种方法获取类
1、
Class clazz1 = User.class;
2、
Class clazz2 = Class.forName("com.hly.java.reflect.User");
3、
User user = new User(); Class clazz3 = user.getClass();
实例
反射创建无参类
//创建反射对象,调用无参构造方法 Object newInstance1 = clazz1.newInstance(); //设置私有属性的值 Field fieldUsername = clazz1.getDeclaredField("username"); //允许操作私有成员 fieldUsername.setAccessible(true); //设置值 fieldUsername.set(newInstance1,"123"); //类型转换 User user1 = (User) newInstance1; //输出 System.out.println(user1.getUsername());
反射创建有参类
//实例化有参构造方法 Constructor<?> constructor = clazz3.getConstructor(String.class, String.class); User newInstance2 = (User) constructor.newInstance("123", "123"); //System.out.println(newInstance2.getUsername());
常用API
获取该类的所有方法
getDeclaredMethods []
子主获取该类的返回值题
getReturnType()
获取传入参数
getParameterTypes()
获取该类的所有字段
getDeclaredFields()
允许访问私有成员
setAccessible
动态代理
概念
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在两者之间起到中介的作用
所谓动态代理,就是实现阶段不用关心代理谁,而是在运行阶段才指定代理哪个一个对象,也就是运行时动态生成代理类(不确定性)。如果是自己写代理类的方式就是静态代理(确定性)。
组成要素
抽象类接口
被代理类(具体实现抽象类的接口)
动态代理类(实际调用被代理类的方法和属性的类)
场景
包装RPC调用,面向切面编程AOP,JDBC,Servlet
方式
JDK动态代理
基于接口实现
CGlIB
基于继承当前类的子类实现
简介
在运行,动态获取这个类的信息
三种方法获取类
1、
Class clazz1 = User.class;
2、
Class clazz2 = Class.forName("com.hly.java.reflect.User");
3、
User user = new User(); Class clazz3 = user.getClass();
实例
反射创建无参类
//创建反射对象,调用无参构造方法 Object newInstance1 = clazz1.newInstance(); //设置私有属性的值 Field fieldUsername = clazz1.getDeclaredField("username"); //允许操作私有成员 fieldUsername.setAccessible(true); //设置值 fieldUsername.set(newInstance1,"123"); //类型转换 User user1 = (User) newInstance1; //输出 System.out.println(user1.getUsername());
反射创建有参类
//实例化有参构造方法 Constructor<?> constructor = clazz3.getConstructor(String.class, String.class); User newInstance2 = (User) constructor.newInstance("123", "123"); //System.out.println(newInstance2.getUsername());
常用API
获取该类的所有方法
getDeclaredMethods []
子主获取该类的返回值题
getReturnType()
获取传入参数
getParameterTypes()
获取该类的所有字段
getDeclaredFields()
允许访问私有成员
setAccessible
12.网络编程
11.I/O
编码
.getBytes("ISO8859_1"),"GBK");
ISO8859_1进行解码为2进制,再使用GBK进行编码
File类
简介
java.io中唯一代表磁盘文件本身的对象。
定义了一些与平台无关的方法操作文件。
File对象用来获取文件本身的一些信息。
文件的创建于删除
1.new File(String path)
功能
指定路径创建file实例
实例
File file = new File("d:/dile.text")
2.new File(String parent,String child)
功能
根据指定的父路径和子路径创建新的File对象
3.new File(File f,String child)
功能
根据parent抽象路径名和child创建新的File实例
获取文件信息
方法
getName()
String
获取文件名
canRead()
boolean
是否可读
canWrite()
String
是否可写
exist()
boolean
判断是否为目录
length()
long
文件长度
getAbsolutePath()
String
获取绝对路径
getParent()
String
获取父路径
isFile()
String
判断文件是否存在
isDirectory()
String
判断是否为目录
isHidden()
String
判断是否隐藏文件
lastModified()
long
最后修改时间
I/O
术语
同步/异步
同步
同步是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调用返回,才会进行下一步。
异步
其他任务不需要等待当前调用返回,通常依靠事件,回调机制来实现任务间的次序关系。
阻塞/非阻塞
阻塞
进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如ServerSocket新建连接。
非阻塞
不管IO操作是否结束,直接返回,响应操作在后台继续处理。
种类
功能
输入流input
输出流output
类型
字节流
按 8 位传输以字节为单位输入输出数据
字符流
按 16 位传输以字符为单位输入输出数据
分类
File
特点
代码简单直观
效率和扩展性存在局限性,容易成为性能的瓶颈
分类
InputStream/OutputStream
用于读取和写入字节,例如操作图片文件。
Reader/Writer
用于操作字符的,增加了字符解码等功能。
BufferOutputStream
带缓冲区的实现,避免频繁的磁盘读写。
Socket
NIO
特点
可以构建多路复用,同步非阻塞IO程序,同时提供了更接近操作系统底层的高性能数据操作方式。
AIO
特点
引入了异步非阻塞IO方式
异步IO操作基于事件和回调机制,应用操作直接返回,不会阻塞在那里,当后台处理完成,操作系统会通知响应线程进行后续工作。
举例
BIO(同步阻塞)
快递员通知你有一份快递会在今天送到某某地方,你需要在某某地方一致等待快递员的到来。
NIO(同步非阻塞)
快递员通知你有一份快递会送到你公司的前台,你需要每隔一段时间去前台询问是否有你的快递。
AIO(异步非阻塞)
快递员通知你有一份快递会送到你公司的前台,并且前台收到后会给你打电话通知你过来取。
缓存输入/输出流
简介
缓存是I/O的一种性能优化。
为I/O流增加了内存缓存区。
文件—>InputStream—>BufferedInputStream—>目的地
字符数据—>BufferWriter—>OutputStreamWriter—>OutputStream—>文件
BufferedInputStream
功能
读取
构造方法
BufferedInputStream(InputStream in)
创建32字节大小的缓存流。
BufferedInputStream(InputStream in,int size)
创建指定大小的缓存区。
方法
flush()
将缓存区数据强制输出完。
BufferedOutputStream
功能
写入
构造方法
BufferedOutputStream(OutputStream in)
创建32字节大小的缓存流。
BufferedOutputStream(OutputStream in,int size)
创建指定大小的缓存区。
BufferedWriter
功能
写入
方法
write(String s,int off,int len)
写入字符串的某一部分。
flush()
立刻将缓存区写入输出流,write方法数据没有立刻写入输出流,而会进入缓存区。
newLine()
写入一个行分隔符。
BufferedReader
功能
写入
方法
read()
读取单个字符
readLine()
读取一个文本行,返回字符串,无数据可读,返回null
I/O(输入/输出流)
1.概述
类型
输入流
Input
输出流
Output
输入流
InputStream
简介
所有字节输入流的父类
用来处理字节
不适合处理字符文本
方法
read()
从输入流读取下一个字节,返回0-255,读到末尾返回-1.
read(byte[]b)
从输入流中读入一定长度字节,返回字节数。
mark(int readlimit)
在输入流当前位置放置标志,在该位置之前允许读取的字节数。
reset()
将输入指针返回到当前所做的标记处。
skip(long n)
跳过输入流n个字节,并返回实际跳过的字节数。
markSupported()
当前支持mark()/reset()操作就返回True.
close()
关闭输入流并释放与该流相关的系统资源。
层次结构
InputStream
AudioInputStream
ByteArrayInputStream
StringBufferInputStream
FileInputStream
FilterInputStream
BufferedInputStream
DataInputStream
PushbackInputStram
...
InputStream
ObjectInPutStream
SequenceInputStream
PipedInputStream
Reader
简介
字符输入流的抽象类
处理字符时简化了编程
所有字符输入流是它的子类
层次结构
CharArrayReader
BufferReader
LineNumberReader
FilterReader
PushbackReader
InputStreamReader
FileReader
PipedReader
StringReader
输出流
OutputStream
简介
所有字节输出流的抽象类
方法
write(int b)
将制定字节写入输出流
write(byte[] b)
将b各字节从指定的Byte数组写入输出流
write(byte[]b,int off,int len)
将byte数组从off开始的len个字节写入此输出流
flush()
彻底完成输出并清空缓存区
close()
关闭输出流
层次结构
ByteArrayOutputStream
FileOutputStream
FilterOutputStream
BufferedOutputStream
DataoutputStream
...
ObjectOutputStream
OutputStream
OutputStream
PipedOutputStream
Writer
简介
字符输出流的抽象类
所有字符输出流的父类
层次结构
BufferWriter
CharArrayWriter
FilterWriter
OutputStreamWriter
PipedWriter
PrintWriter
StringWriter
数据输入/输出流
简介
程序以与机器无关的方式从底层输入流读取基本Java数据类型。
读取一个数据,不必关心这个数值应当是那种字节。
DataInputStream
构造方法
DataInputStream(InputStream in)
方法
readUTF()
返回字符串
DataOutputStream
构造方法
DataOutputStream(OutputStream in)
方法
writeBytes(String s)
Java字符Unicode编码双字节。将每一个字符低字节写入设备。
writeChars(String s)
将每一个字符两个字节都写入目标设备。
writeUTF(String s)
向目标设备写入字符串的长度,能准确读回字符串。
将字符串按照utf编码后的字节长度写入目标设备然后再写入每一个字节的UTF编码。
文件输入/输出流
FileInputStream
功能
文件读取
读取字节或自己数组
方法
FileInputStream(String name)
FileInputStream(File file)
FileOutputStream
功能
文件写入
写入字节或字节数组
子主题
FileOutputStream(String name)
FileOutputStream(File file)
FileReader
功能
文件读取
读取字符流
FileWriter
功能
文件写入
写入字符流
压缩输入/输出流
简介
将文件内容写入zip文件内,必须写入对应于该文件的目录进入点。
ZipEntry类产生的对象,代表ZIP压缩文件内的进入点(entry)。
ZipOutputStream
功能
将文件压缩为zip文件
构造方法
ZipOutputStream(OutputStream out)
方法
putNextEntry(ZipEntry e)
void
写入新的ZipEntry,将流的位置移至此entry所指数据的开头。
write(byte []b,int off,int len)
void
将字节数组写入当前ZIP条目数据。
finish()
void
完成ZIP输出流的内容,无需关闭配合的OutputStream。
setComment()
void
设置此ZIp文件的注释文字。
ZipInputStream
功能
解压缩
构造方法
ZipInputStream(InputStream in)
方法
read(byte[]b,int off,int len)
int
读取b,off开始,长度为len
available()
int
判断是否读完当前entry数据,读完0,否则1
closeEntry()
void
关闭当前ZIP条目,并定位流以读取下一条目。
skip(long n)
long
跳过当前ZIP条目中指定的字节。
getNextEntry()
ZipEntry
读取下一个ZipEntry,将流内的位置移至该Entry所指数据的开头。
createZipEntry(String name)
ZipEntry
指定name参数新建一个ZipEntry对象。
10.泛型
泛型
简介
解决向下转型出现的问题。
省去了强制类型转化。
能够在编译期间发现错误。
子转父类,向上转型(自动转型);父转子类,向下转型(强制转换)
方法
声明多个类型
MutiOverClass<T1,T2>
MutiOverClass<Integer,Boolean> moc = new MutiOverClass<Integer,Boolean>();
声明数组类型
ArrayClass<T>
private T[]array;
被泛型化的集合
ArrayList<E>
HashMap<K,V>
HashSet<E>
Vector<E>
结论
泛型参数只能是类类型,不能是简单类型
泛型类型个数可以是多个
可以用extends关键字限制泛型类型
可以用通配符限制泛型的类型
泛型的高级用法
限制泛型可用类型
class 类名<T extends anyClass>
限制泛型的上界,可以实例化已经实现anyClass的类
使用通配符
class类名<? extends List> a = null;
限制这个泛型类的类型实现或继承某个接口或类的子类。
extends声明通配符的上界
可以实例化实现该类型的类。
super声明通配符的下界
可以实例化该类型实现的类。
继承泛型类与实现泛型接口。
定义
java中的泛型方法: 是否拥有泛型方法,与其所在的类是否泛型没有关系。 泛型的声明,必须在方法的修饰符(public,static,final,abstract等)之后,返回值声明之前。 和泛型类一样,可以声明多个泛型,用逗号隔开。 一个static方法,无法访问泛型类的类型参数,所以,若要static方法需要使用泛型能力,必须使其成为泛型方法。 (通俗点说,如果方法参数里面有泛型,方法又是static,要能识别该方法参数,必须将该方法改为泛型方法,也就是在方法前面将进行泛型声明。) eg: [java] view plain copy public <U> Class<? extends U> asSubclass(Class<U> clazz) {}
用法
其中<AnyType extends Comparable<? super AnyType>>就是泛型声明; 其中的extends表明AnyType只能是实现Comparable接口的类或子类; Comparable<T>本身就是一个泛型接口,实现的时候要指明泛型类型; Comparable<? super AnyType>中的<? super AnyType>表示具体的泛型类型是AnyType或者AnyType的上层父类类型。
9.集合
哈希
ConcurrentHashMap
简介
线程安全高效的HashMap
多线程环境使用
核心
1.7
保证线程安全
分段锁
分段锁,每个Segment有独立的锁 1、分为16个Segment(相当于HashTable) 2、对每一个小的Segment加锁,使用ReentrantLock上锁 3、根据index存放在哪个Segment 4、get时候计算存放到那个Segment里面
1.8
初始化
CAS
new 出一个新的节点的时候,使用CAS无锁机制创建Node保证线程安全
put
1、计算hash值
int hash = spread(key.hashCode());
h^(h>>>16)
hash = key.hashCode() hash = (h ^ (h >>> 16)) & HASH_BITS; index = (n - 1) & hash) ---- 1、key.hashCode = 12312313 2、左边高16位和低右边16位异或运算(两个值不同为1,相同为0),降低重复的可能性 因为如果hashCode不同,可能&(n-1)的结果相同,所以先异或一下 3、异或结果&(n-1)得到index --- https://blog.csdn.net/qq_42034205/article/details/90384772
2、如果没有节点,使用CAS创建一个节点(new Node),直接把节点放在当前位置
3、index冲突的时候,使用sync锁住Node节点
4、如果key相同,新值替换掉老值
5、如果key相同,值不相同,形成链表
6、大于8转化为红黑树
7、最后addCount
https://www.cnblogs.com/maratong/p/12345595.html
扩容
扩容2倍
扩容时复制到新的数组,重新计算index
在一个线程扩容的时候,如果有线程put,就会去帮忙搬运节点,在搬运的时候,头结点的hash值为-1
保证线程安全
CAS+sync
CAS
public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x); 读入对象内存偏移量为offset位置与期望值expected比较,相等就把x的值赋给offset位置的值,返回true, 不相等,就不赋值,返回true。 --- CAS 比较并替换,保证并发时无锁并发的安全性
sync锁住表头
计算index产生冲突,使用synchronized锁住一个Node节点,更加精细,效率更高
关键代码
HashMap
基础知识
位运算
https://blog.csdn.net/xiaopihaierletian/article/details/78162863
类型
与运算&
简介
两个操作数都为1,结果为1,否则为0
实例
0&0=0,0&1=0,1&1=1
异或^
简介
两个值不同为1,相同为0
实例
0^0=0,0^1=1,1^0=1
或运算|
简介
两个数只要有一个为1,结果就为1,否则为0
实例
0|1=1,0|0=0,1|1=1
取反运算~
简介
按二进制取反
实例
~1=0,~0=1
左位移<<
简介
num<<value,num转为2进制向左位移value位,乘上2^n
实例
<<
num<<1
1010
10100
右位移>>
简介
num>>value,num转为2进制向右位移value位,带符号右移,正数高位补零,负数补1
实例
>>
num>>1
1010
0101
>>>
简介
无符号右移,忽略符号位,右移后左边空出来的位用0填充,移出右边的丢弃
实例
-14 >>>2
11111111 11111111 1111111111110010
00111111 11111111 1111111111111100
哈希码
hashCode 默认为JDK根据对象生成的地址 hashCode 相同,对象的值不一定相同 equal 相同,对象的值一定相同 先比较hashCode,不相同肯定不相同,再比较equal ,相同则一定相同 a="a" ACI 为 97 Integer b = new Integer(97) a.hashCode b.hashCode
实现
1.7是数组+链表
1.8是数组+链表+红黑树
节点>=8转化为红黑树
节点<6转化为链表
红黑树
特征
比平衡二叉树多了颜色
每个节点不是红色就是黑色
根节点都是黑色
新增节点默认红色,如果产生红色相连,变色或者旋转
变换规则
为什么不用其他树
二叉搜索树
没有平衡值,第一次添加的节点作为根节点,如果接下来添加的节点都比根节点大,会形成链表
AVL缺点
查询快,插入删除慢
常见问题
1、初始容量 答:16 2、底层如何解决Hash冲突问题 答:链表,头插法 3、是否线程安全 答:不是线程安全 4、是否支持key为空对象 答:支持,放在数组为0的位置或者第一个链表 5、1.7扩容存在哪些问题 答: 在JDK7,链表使用头插法,在多线程情况下,死循环问题, 每次在数组扩容的时候,数组长度发生了变化,需要重新计算index(不是重新计算hash,而是重新计算index), 把原来的table复制到新的table里面,table是全局的, 多线程情况下,会产生两个new Table, 赋值的时候,修改了原来table的共享的next, T1线程扩容完后,改变原来table B.next =A,A.next =B, JDK1.8已经解决该问题。 6、是否存放键值对为自定义对象 可以,支持泛型 8、加载因子为什么为0.75不是其他 答: 因子越大,index下标冲突越大,但是空间利用很高 因子越小,index下标冲突越小,空间利用率不是很高 下标冲突高,查询成本高,反而非常低 所以必须在时间和空间之间均衡 9、put如何实现 答:根据key获取hash值,根据indexFor获取下标,下标为 h&(n-1);length为2的幂次方容易发生冲突,所以要-1。 10、index冲突和hash冲突区别 index:对象不同,底层二进制运算产生相同index,h&(length-1)必须是奇数 hash:对象不同,hashCode相同 11、链表是单向还是双向 答:单向 12、链表长度过长问题 效率降低,时间复杂度为O(N) 13、1.7 存在哪些问题 扩容死循环Bug 线程不安全 链表过长导致效率过低 JDK7计算hash非常均摊,减少hash冲突问题,因为底层是链表,效率低 14、扩容多少倍 2倍
实现
阈(yu)值
作用
扩容,节点数量>阈值(加载因子*数组长度)(12>=16*0.75)
数值
0.75
综合时间复杂度和空间复杂度
<0.75
内存充足,空间换时间
>1
空间利用率高,哈希碰撞多,时间换空间
数组初始化大小
必须是2^n
因为必须保证 2^n-1为01111 如果为14,在做&运算的时候,hashCode的最后一位,不管是0还是1,&0都为0,易造成冲突 所以必须让结果取决于hashCode的二进制,所以必须是2^n hashCode 01101 2^n-1 01110
put方法
1.对key求hash值,然后再计算下标
2.如果没有碰撞,直接放入数组中
3.如果碰撞了,放到链表中
4.如果节点已经存在,就替换旧值
5.如果链表长度超过阈值,转换成红黑树
6.如果同满了(容量*加载因子),就resize
扩容
容量扩为原来两倍,对每个节点重新计算index值
区别
JDK7
数组+链表
头插法
死循环Bug
JDK8
数组+链表+红黑树
尾插法
Node 封装键值对
长度大于8转化为红黑树,时间复杂度为logn
优化
哈希冲突
开放地址法
发生冲突时,如果hash表未被装满,所以还有坑位,那就可以把key放到冲突位置后面的空位上面,缺点是查找和扩容。
发生冲突,地址+1
再哈希法
冲突时,再计算另一个hash值,指导冲突不发生,增加了计算时间,如果不考虑时间成本,对查询元素的要求极高,就可以考虑。
链地址法
HashMap采取的方法,用一个链表存储相同hash值的数据。
获取元素
链表
红黑树
扩容优化
提前设置初始容量 (初始容量 = 预知数据量/加载因子)减少resize操作,提高效率。 扩容 16*0.75 = 12 初始容量 = 预知数据量/加载因子 16 = 12/0.75
技巧
(n-1)&num
n对num取模,n为2^n
1>>4=16
Map
接口
Map
特点
以键值对的形式存储和操作数据的容器类型。
接口
AbstractMap
接口
HashMap
特点
不是同步的
支持null键和值,只能有一个key为null,可多个value为null
原理
HashMap基于hash算法实现,我们通过put进行存储,get进行获取,当传入key时, 会根据key.hashCode()计算出hash值,根据hash值将value保存在bucket里。当计 算出的hash值相同时,我们成为hash冲突,HashMap的做法是使用链表和红黑树存储 hash值相同的value。
接口
LinkedHashMap
特点
提供的遍历顺序符合插入顺序
维护了一个双向链表
ConcurrentHashMap
特点
基于lock实现锁分段技术。首先将Map存放的数据分成一段一段的存储方式,然后给每一段数据分配一把锁,当一个线程占用锁访问其中一个段的数据时,其他段的数据也能被其他线程访问。
保证了多线程运行环境下的数据访问安全性,而且性能上有长足的提升。
TreeMap
特点
不允许对象为null
集合中的对象存在一定顺序,整体顺序由键的顺序决定,通过Comparator或Comparable来决定
Hashtable
特点
同步,线程安全,性能开销大,不支持null键和值,不建议使用
约定
equals相等,hashCode一定要相等,hashcode相等,equal不一定相等。
重写equal还要重写hashcode
hashcode()继承于Objec类,hashcode码默认为对象的内存地址, 两个相同含义的对象,比较也不相等。equal()比较的是对象的地址。
HashMap比较key是否相等,需要使用这两个函数,先求出key的hashcode(),若相等,再比较equal,equal相等则认为他们相等。
若只重写hashcode(),不重写equal(),比较equal时只是比较他们是否为同一对象,所以必须两个方法一起重写。
重载hashcode()为了同一个key能得到相同的HashCode,重载equal()为了向HashMap表名当前对象和key上所保存的对象是相同的。
List
接口
Collection
List
特点
List集合允许元素重复。
元素的顺序是对象插入的顺序。
类似于数组,用户可通过索引访问集合中的元素。
接口
Vector
特点
线程安全的动态数组,使用了Synchronized实现同步,扩容时会提高1倍。
内部元素以数组形式顺序存储,适合随机访问,插入和删除性能差。
ArrayList
特点
应用更加广泛的动态数组实现,不是线程安全的,扩容增加50%。
内部元素以数组形式顺序存储,适合随机访问,插入和删除性能差。
LinkList
特点
双向链表,不是线程安全的。
进行节点插入,删除比较高效,随机访问性能较差。
Set
特点
不按特定方式排序,简单把对象加入集合。
不能包含重发对象,只允许一个Null。
接口
HashSet
特点
不保证数据有序。
依靠hashCode(),equals()区分重复数据。
实现Set接口,由HashMap支持。
允许使用null元素。
原理
基于HashMap实现,底层使用HashMap保存所有元素。
TreeSet
特点
保存数据有序,支持自然顺序访问,添加,删除效率较低。
依靠Comparable区分重复数据。
实现了Set接口,SortedSet接口。
保存自定义类的对象,定义所在的类需要实现Comparable
遍历集合时按自然顺序递增排序,也可按比较器递增排序。
LinkedHashSet
特点
构建了一个记录插入顺序的双向链表,提供了按照插入顺序遍历的能力。
保证了常数时间添加,删除,包含等操作,需要维护链表开销,性能略低于HashSet。
Iterator
简介
可以遍历任何Collection接口,并允许在迭代的过程中删除元素。
用法
List list = new ArrayList(); Iterator iterator = list.iterator();
集合类
概念
数组长度固定,集合长度可变。
数组存放基本类型数据,集合用来存放对象。
关系
Object
Map
HashMap
TreeMap
Collection
Set
HashSet
TreeSet
List
ArrayList
LinkList
Vector
Stack
Collection
简介
层次结构中的根接口。
构成Collection的单位称为元素。
通常不能直接使用。
方法
add(E e)
将指定对象添加到集合中。
remove(Object o)
将指定对象从集合中移除。
isEmpty()
判断当前集合是否为空。
iterator()
返回此集合的迭代器,用于遍历集合中的对象。
size()
返回集合中的个数。
接口
List
简介
List集合允许元素重复。
元素的顺序是对象插入的顺序。
类似于数组,用户可通过索引访问集合中的元素。
方法
Collection中的所有方法
get(int index)
获得指定索引位置的元素。
set(int index,Object obj)
在索引指定位置修改对象。
实现类
ArrayList
简介
实现了可变的数组。
允许保存所有元素,包括null。
插入,删除对象慢。
根据索引位置快速随机访问。
线程不够默认扩展50%+1
List<E> list = new ArrayList<>();
LinkList
简介
采用链表结构保存对象。
便于插入和删除对象。
随机访问集合中的对象效率较低。
不是线程同步
List<E> list =new ArrayList<>();
Vector
简介
顺序存储结构
是线程安全的,即是线程同步的,花销大
指定个数扩大容量或翻倍扩大容量
内存不够默认扩展1配
构造方法
Vector()
Vector(Collection c)
Vector(int initialCapacity)
方法
添加
add(Obejct)
add(int index,Object o)
addElement(Object o)
删除
remove(int index)
remove(Object o)
removeElement(Object o)
读取
get(int index)
获得元素,容量
size()
capacity()
实现类
Stack
简介
专门用来实现栈的工具类。
方法
Set
简介
不按特定方式排序。
简单把对象加入集合。
不能包含重发对象,只允许一个Null。
包含Collection的所有方法。
实现类
HashSet
简介
保存数据无序。
依靠hashCode(),equals()区分重复数据。
实现Set接口,由HashMap支持。
允许使用null元素。
TreeSet
简介
保存数据有序
依靠Comparable区分重复数据。
实现了Set接口,SortedSet接口。
保存自定义类的对象,定义所在的类需要实现Comparable
遍历集合时按自然顺序递增排序,也可按比较器递增排序。
方法
first()
返回此Set当前第一个元素。
last()
返回Set当前最后一个元素。
comparator()
返回Set元素进行排序的比较器,Set使用自然顺序,返回null。
headSet(E toElement)
返回新的Set集合,新集合1toElement(不含)之前的对象。
subSet(E fromElement,E fromElement)
返回两者之间的对象,包含前者,不包含后者。
tailSet(E fromElement)
返回新的集合,fromElement(包含)之后的对象。
Map
简介
没有继承Collection接口。
提供key到value映射。
允许多个值为null,只允许一个键为null。
每个key映射一个value,key决定存储对象在映射中的位置。
方法
put(K key,V value)
向集合中添加映射关系。
containsKey(Object key)
此映射包含指定key映射关系,返回true。
containsValue(Object value)
如果此映射将一个或多个key映射到指定值,返回true.
get(Object key)
存在指定的key,返回对象的值,否则返回null。
keySet()
返回集合中所有key对象形成的Set集合。
values()
返回集合中所有值对象形成的Collection集合。
实现类
HashMap
简介
添加,删除映射关系效率更高。
允许使用null值和null键,必须保证键的唯一性。
不保证映射的顺序。
TreeMap
简介
映射关系存在一定的顺序,不允许对象为null。
希望集合中对象存在一定顺序,用TreeMap。
实现了Map,SortedMap,映射关系具有一定的顺序。
添加,删除,定位映射关系性能较差。
List 源码
ArrayList
实现
数组
内存连续
扩容
默认不会初始化,add之后才会初始化,扩容50%
操作
新增元素
会确认容量大小,容量够大,不用扩容,容量太小,扩容1.5倍,扩容后将数组复制到新分配的内存地址。 添加到任意位置,导致之后的元素重新排列 添加到末尾,没有扩容的前提下,不会有元素复制排列过程 如果我们知道数据存储大小,初始化指定容量,添加元素只添加到末尾,即使大量新增元素,性能也不会变差,反而比List好。
加到末尾
加到任意位置
删除元素
删除元素都要进行数组重组,删除元素越靠前,数组重组开销越大。
遍历元素
随机获取速度快
特点
transient
transient修饰elementData,表示该属性不会被序列化,文档中没说不能序列化。 基于动态扩容,并不是所有内存空间都存储数据,如果采用外部序列化法序列化,会序列化整个数组,避免没有存储的空间被序列化。 内部提供两个方法writeObject和readObject,自我完成序列化和反序列化,节省了空间和时间。 transient防止对象数组被其他外部方法序列化。
自动扩容
初始大小默认为10
实现List接口,继承AbstractList抽象类
实现Cloneable,Serializable接口
实现RandomAccess接口,随机访问
没有使用迭代器删除会抛出异常
LinkedList
实现
双向链表
内存不连续
操作
遍历
不支持随机访问。 使用迭代遍历速速更快。
新增
将元素加到队尾,将last放到临时变量中,新生成一个节点Node,然后将last引用指向新节点,之前last对象的前指针指向新节点。
添加到队尾
添加到中间
删除
如果元素处于前半段,就从前面找,如果在后半段,就从后面找 如果有大量元素,元素处在中间,效率就非常低了
特点
transient
transient修饰first/last,size 内部提供两个方法writeObject和readObject,自我完成序列化和反序列化,节省了空间和时间。 transient防止对象数组被其他外部方法序列化。
实现List,Deque,AbstractSequentialList
实现Cloneable,Serializable接口
有Queue类型的特点
没实现RandomAccess
Vector
简介
线程安全的ArrayList
特点
默认初始化大小为 10
可以自定义扩容大小,扩容100%
性能比较
新增/删除
头部
ArrayList>LinkedList
1、ArrayList基于数组,数组是连续的内存空间,添加到头部,会对头部以后的数据进行重排,效率低。 2、LinkList基于链表,如果在前半段,就从前面找,在后半段,就从后面找,所以高效。
中间
ArrayList<LinkedList
ArrayList添加到中间,有部分数据需要重排,效率不是很高, LinkedList 添加到中间,效率是最低的,因为靠近中间位置,添加元素之前的查找是遍历元素最多的操作。
尾部
ArrayList<LinkedList
添加到尾部,在没有扩容的情况下,ArrayList效率高于LinkedList,添加元素到尾部,不需要复制重排数据, LinkedList多了new对象以及变换指针的流程,效率低于ArrayList
遍历
for
ArrayList<LinkedList
LinkedList的for性能最差,ArrayList的for性能最好。 LinkedList基于链表实现,每一次for,都会遍历半个List ArrayList基于数组,并实现RandomAccess接口,支持快速随机访问,所以效率非常高。
迭代器
ArrayList=LinkedList
效率相当