IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> java个人整理知识点 -> 正文阅读

[网络协议]java个人整理知识点

发展:

1.SE?标准版。
2.EE?企业版,基于SE。
3.ME?已经淘汰。

JDK、JRE和JVM:

JDK:

称为Java开发工具包(?Java?Development?Kit)。Java开发人士需要下载和安装JDK,目前的主流版本为JDK11。

JRE:

称之为Java?SE运行时环境(Java?SE?Runtime?Environment),提供了运行Java应用程序所必须的软件环境等。无论是开发还是运行Java应用都必须安装。

区别:

JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。
JRE包含JVM和类库
JVM多个系统上都有实现,使得跨平台能够实现。

javac和java:

javac.exe编译器,主要用于将高级Java源代码翻译成字节码文件。生成对应的xxx.class,即可通过java?xxx执行
java.exe解释器,主要用于启动JVM对字节码文件进行解释并执行。新特性:java?xxx.java可以直接在版本11运行,如果当前路径存在编译后的class,会报错。

常见操作:

1.注释

单行用//,多行用/*?*/,文档注释/**?*/可以提取

2.规范

xxx.java开头用注释声明项目名称、功能、作者、版本、备注

3.xxx.java里面要有这个类xxx

public?class?xxx?{
????public?static?void?main(String[]?args){
????}
}

4.变量的声明方式

数据类型?变量名?=?初始值;????//其中=初始值可以省略,但;不可以省略。
变量使用之前需要声明和初始化。
不能重复声明

5.标识符的命名法则

??由数字、字母、下划线以及$等组成,其中数字不能开头(不能区分字面值)。
??不能使用Java语言的关键字,所谓关键字就是Java语言用于表示特殊含义
的单词。
??区分大小写,长度没有限制但不宜过长。
??尽量做到见名知意,支持中文但不推荐使用。
??标识符可以给类/变量/属性/方法/包?起名字。

6.变量的输入

Scanner?scanner?=?new?Scanner(System.in);
api的使用可以参考手册index.html

基本数据类型:

概述:

Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。

1. byte(Byte)

byte?数据类型是8位、有符号的,以二进制补码表示的整数;
最小值是?-128(-2^7),最大值是?127(2^7-1,最高位为0表示非负),默认值是?0;用在大型数组中节约空间。

2. short(Short)

short?数据类型是?16?位、有符号的以二进制补码表示的整数
最小值是?-32768(-2^15);最大值是?32767(2^15?-?1);默认值是?0;

3. int(Integer)

int?数据类型是32位、有符号的以二进制补码表示的整数;
最小值是?-2,147,483,648(-2^31);最大值是?2,147,483,647(2^31?-?1);一般地整型变量默认为?int?类型;默认值是?0?;
正无穷为∞(字节'\u221e'),负无穷为-∞。print输出为Infinity。

4. long(Long)

long?数据类型是?64?位、有符号的以二进制补码表示的整数
默认值是?0L;long?a?=?100000L,"L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。

默认的数字字面值解释器声明为int类型,然后转换为定义的byte、short,int。
????但int?a=25;byte?b=a;解释器会报错,因为a是变量,存储的值还不固定,和数字字面值不同,所以解释器直接报错了。
如果超过int范围,会报错整数太大。需要显式加上l或L。
更大的数可以用java.math.BigInteger
????BigInteger?a?=?new?BigInteger("25025050060000000000000000");

5. float(Float)

float?数据类型是单精度、4个字节32位、符合IEEE?754标准的浮点数,可以表示7位有效数字;范围:-3.403E38~3.403E38。(因为有些位数用来表示指数,所以范围更大了)
float?在储存大型浮点数组的时候可节省内存空间;默认值是?0.0f;浮点数不能用来表示精确的值,如货币;
例子:float?f1?=?234.5f/F(不加f的话double转float,报错)。会自动忽略超过位数。

6. double(Double)(推荐,有效位数更多)

double?数据类型是双精度、8个字节64?位、符合IEEE?754标准的浮点数,可以表示15位有效数字;范围:-1.798E308~1.798E308。
浮点数的默认类型为double类型;double类型同样不能表示精确的值,如货币;默认值是?0.0d;运算不精确。比如0.1+0.2=0.3000004。
精确可以用java.math.BigDecimal。System.out.println(new?BigDecimal("0.1").add(new?BigDecimal("0.2")));
例子:double?d1?=?123.4。

7. boolean

boolean数据类型表示一位的信息;只有两个取值:true?和?false;默认值是?false;内存空间中所占大小没有明确的规定,可以认为是1个字节。

8. char(Character)

char类型是一个单一的?2个字节?16?位(没有符号位)?Unicode?字符;
单引号。
最小值是?\u0000(即为0);最大值是?\uffff(即为65,535,UCS-2统一码版本,其中汉字一共2w多个,基本满足各种语言的使用,最新版本UCS-4是31位字符集);char?数据类型可以储存任何字符;
????扩展:有两个实现utf-8采用变长字节,1-4位字节变化,由于额外的标志信息,4个字节一共64029个(3个字节一共52156个,汉字常用);utf-16两个字节
例子:
????char?letter?=?'A'。?
????char?d=1会自动转换为ascii码对应的字符,计算机的底层只识别0和1组成的二进制序列。(int)d即可打印对应的数字。

引用数据类型:

数组、类、接口、枚举、标注

进制转换:

10转2:

二进制(ob/oB开头)中的最高位(最左边)用于代表符号位,若该位是0则表示非负数,?若该位是1则表示负数。
1.除2取余法,使用十进制整数不断地除以2取出余数,直到商为0时将余数逆序排序。
2.拆分法,将十进制整数拆分为若干个二进制权重(1,2,4,8)的和,有该权重下面?写1,否则写0。

2转10:

1.加权法,使用二进制中的每个数字乘以当前位的权重再累加起来。

负十进制转换为二进制的方式:

先将十进制的绝对值转换为二进制,然后进行按位取反再加1。(涉及到补码的概念,正负相加为0,溢出丢弃)

负二进制转换为十进制的方式:

先减1再按位取反,合并为十进制整数后添加负号。

16进制:

0x开头

8进制:

0开头

字节与bit:

1B(byte,字节)= 8 bit(位)

为什么?

一个字节定义为可容纳单个字符的最小bits
它并不总是8,有时是7或9。这取决于平台。
一个说法是使用8个字符(方便的2乘幂)?),有时他们将第8位用于奇偶校验,有时又将其用于ASCII标准的扩展。有时他们只是将第8位设置为零。
26个英文字母大小写52个,?加上10个数字,还有一些特殊符号超过了64个,所以ascll128个,?共计需要连续七组信号值,?但是早期的传输不可靠,?于是预留了一位作为奇偶校验,?共八组信号值,?一字节8位

类型转换:

自动类型转换:

从小类型到大类型之间的转换
整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。
????byte,short,char—>?int?—>?long—>?float?—>?double?

强制类型转换:

概述:

从大类型到小类型之间的转换

格式:

变量名?=?(目标类型)源类型变量名;

数据类型转换必须满足如下规则:

//?子类也可以通过这种强制类型转换?变为父类
1.?不能对boolean类型进行类型转换。
2.?不能把对象类型转换成不相关类的对象。
3.?在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
4.?转换过程中可能导致溢出或损失精度,例如:
????int?i?=128;???
????byte?b?=?(byte)i;
????因为?byte?类型是?8?位,最大值为127,所以当?int?强制转换为?byte?类型时,值?128?时候就会导致溢出。
5.?浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:
????(int)23.7?==?23;????????
????(int)-45.89f?==?-45

运算符:

算术运算符:

+?表示加法运算符,同时可以实现字符串(至少一边即可)与其他数据类型“相连”。(表明java为弱类型语言)
-?表示减法运算符
*?表示乘法运算符
/?表示除法运算符,只保留整数部分,要想保留小数需要其中一个为double,或者乘以0.1。不能除以0,除以0.0得到infinity,0/0得到NaN
%?表示取模/取余运算符

自增减运算符:

只能用于变量,常数不可以
a++:是一个表达式,先让a的数值作为整个表达式的最终结果,然后再让a变量加1
++:a让a变量加1,再让a的数值作为整个表达式的最终结果。少了一步声明赋值,效率更高。
a--
--a

逻辑运算符:

&&?表示逻辑与运算符,相当于"并且",同真为真,一假为假。?
||?表示逻辑或运算符,相当于"或者",一真为真,同假为假。
!?表示逻辑非运算符,相当于"取反",真为假,假为真。
逻辑运算符的操作数均为boolean表达式。

短路特性
对于逻辑与运算符来说,若第一个表达式为假则结果为假,此时跳过第?二个表达式;
对于逻辑或运算符来说,若第一个表达式为真则结果为真,此时跳过第?二个表达式;

条件/三目运算符:

条件表达式??表达式1:?表达式2
判断条件表达式是否成立,若成立则执行表达式1,否则执行表达式2?。

赋值运算符:

=?表示赋值运算符,用于将=右边的数据赋值给=左边的变量,覆盖变量?原来的数值。
赋值表达式本身也有值,其本身之值即为所赋之值。
+=、?-=、?*=、?/=、?...

注意:
????byte?a=10;
????a?=?a+10;会报错,编译器做了优化,将a和10都变为int类型然后相加,结果为int类型。需要强转换。
????而a+=10等价于a=(byte)(a+10),所以没有报错。

规范:
????a==2
????2==a两者的区别是少写一个等号的时候,能在编译阶段报错。

移位运算符:

<<?左移运算符,用于将数据的二进制位向左移动,右边使用0补充
????左移1位通常结果*2,在没超出范围的情况下。
>>?右移运算符,用于将数据的二进制位向右移动,左边使用符号位补充
????右移1位相当于除2
>>>?表示逻辑右移运算符,用于将数据的二进制位向右移动,左边使用0?补充。
????非负时和右移一样

位运算符:

&?表示按位与运算符,按照二进制位进行与运算,同1为1,一0为0.
|?表示按位或运算符,按照二进制位进行或运算,一1为1,同0为0.
~?表示按位取反运算符,按照二进制位进行取反,1为0,0为1.
^?表示按位异或运算符,按照二进制位进行异或运算,同为0,不同为1.

优先级:

[]()(方法调用)????????????????从左到右
!~++--+(一元运算)-(一元运算)????从右到左
*/%????????????????????????????从左到右
+-????????????????????????????从左到右
<<?>>?>>>????????????????????从左到右
==?!=????????????????????????从左到右
&????????????????????????????从左到右
^????????????????????????????从左到右
|????????????????????????????从左到右
&&????????????????????????????从左到右
||????????????????????????????从左到右
?:????????????????????????????从右到左
=????????????????????????????从右到左

流程控制:

if语句:

if(){}
if?else?
if?else?if?else

switch case分支结构:

从上到下执行
switch(变量/表达式)?{
????case?字面值1:?语句块1;?break;?
????case?字面值2:?语句块2;?break;?...
????default:语句块n;
}
switch()中支持的数据类型有:byte、short、char以及int类型,从jdk1.5?开始支持枚举类型,从jdk1.7开始支持String类型。
case穿透:如果执行了一个case没有break,会执行下一行直到break;
default和位置顺序无关,总是在匹配不到的时候才执行。

for循环:

for(初始化表达式;?条件表达式;?修改初始值表达式)?{?循环体;
}
for(;;)?-?这种没有循环条件的循环叫做?无限循环,俗称“死循环”。
如果要退出外层循环体,需要使用标号的方式。
????outer:?for?(...)?{
????????for(...)?{
????????????break?outer;
????????}
????}

while循环:

while(条件表达式)?{?
????循环体;
}
注意:
????while(条件表达式);{}相当于
????while(条件表达式){:}{},这会导致i不会增加,?空语句,用于延时。

do while循环(熟悉):

do?{?
????循环体;
}?while(条件表达式);????????//这里有个分号

数组:

概述:

Java?语言中提供的数组是用来存储固定大小的同类型元素。

声明:

首先必须声明数组变量,才能在程序中使用数组。
dataType[]?arrayRefVar;???//?首选的方法
dataType?arrayRefVar[];??//?效果相同,但不是首选方法,dataType?arrayRefVar[]?风格是来自?C/C++?语言?,在Java中采用是为了让?C/C++?程序员能够快速理解java语言。

创建:

dataType[]?arrayRefVar?=?new?dataType[arraySize];
dataType[]?arrayRefVar?=?{value0,?value1,?...,?valuek};
dataType[]?arrayRefVar?=?new?dataType[]{value0,?value1,?...,?valuek};
示例:
????int[]?arr?=?new?int[2];默认值填充

索引:

数组的元素是通过索引访问的。数组索引从?0?开始,所以索引值从?0?到?arrayRefVar.length-1。

遍历:

for?(int?i?=?1;?i?<?myList.length;?i++)
for(type?element:?array)

内存结构分析:

栈区
????栈用于存放程序运行过程当中所有的局部变量。一个运行的Java程序从开?始到结束会有多次变量的声明。
堆区
????JVM会在其内存空间中开辟一个称为“堆”的存储空间,这部分空间用于存?储使用new关键字创建的数组和对象。
????示例:
????????int[]?a?=?new?int[2];这句话,先在栈区声明一个变量,然后在堆区初始化数组,最后赋值操作是在栈区存储堆区的内存地址(引用类型)

增删改查:

数组赋值arr=brr,会把brr的内存地址赋值给arr,arr的长度元素都变为brr。
拷贝:System.arraycopy(Object?src,?int?srcPos,?Object?dest,?int?destPos,?int?length)

优缺点:

可以直接通过下标(或索引)的方式访问指定位置的元素,速度很快。?
数组要求所有元素的类型相同。
数组要求内存空间连续,并且长度一旦确定就不能修改。
增加和删除元素时可能移动大量元素,效率低。

Arrays 类:

java.util.Arrays?类能方便地操作数组(遍历、查找、排序),它提供的所有方法都是静态的。
方法:
????static?String?toString(int[]?a)
????public?static?void?fill(int[]?a,?int?val)???//将指定的?int?值分配给指定?int?型数组指定范围中的每个元素。
????????示例:
????????????Arrays.fill(a,?1,3,10);?将1到3不含3的位置的元素填充为10,注意不能超索引
????public?static?void?sort(Object[]?a)?????//对指定对象数组根据其元素的自然顺序进行升序排列。
????public?static?boolean?equals(long[]?a,?long[]?a2)???//如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。返回true
????public?static?int?binarySearch(Object[]?a,?Object?key)??//用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。
????????????//数组在调用前必须排序好的(二分查找)。如果查找值包含在数组中,则返回搜索键的索引;否则返回?(-(插入点)?-?1)。
????copyOfRange(li,start,end)切片复制,生成另一个数组

二维数组:

声明和初始化:

数据类型[][]?数组名称?=?new?数据类型[行数][列数];?????????其中列数可以忽略。表示不定长。
数据类型[][]?数组名称?=?{{元素1,?元素2,...},?...};

面向对象编程:

概念:

面向对象指以属性和行为的观点去分析现实生活中的事物。
面向对象编程指先以面向对象的思想进行分析,然后使用面向对象的编程语言进行表达的过程。
面向过程指侧重具体的步骤,面向对象则是针对能实现步骤的对象。

理解面向对象的思想精髓(封装、继承、多态),至少掌握一种编程语言。

类和对象的概念:

类简单来就是“分类”,是对具有相同特征和行为的多个对象共性的抽象描述,在Java语言中体现为一种引用数据类型,里面包含了描述特征/属性的成员变量以及描述行为的成员方法。
类是用于构建对象的模板,对象的数据结构由定义它的类来决定。

类和成员变量的定义:

class?类名{????????通常情况下,当类名由多个单词组成时,要求每个单词首字母都要大写。
????类体;
????数据类型成员变量名=?初始值;?????当成员变量由多个单词组成时,通常要求从第二个单词起每个单词的首字母大写。?初始值一般不写
}

对象的创建:

a.当一个类定义完毕后,可以使用new关键字来创建该类的对象,这个过程叫做类的实例化。
b.创建对象的本质就是在内存空间的堆区申请一块存储区域,用于存放该对象独有特征信息。
成员变量的默认值:?
????当变量作为作为类成员使用时,java才确保给定其初始值,防止程序运行时错误。
????byte?short?int?long?float?double?char??0/'/uoooo'
????boolean?false?
????引用类型?null

引用的定义:

基本概念:

a.使用引用数据类型定义的变量叫做引用型变量,简称为"引用"。
b.引用变量主要用于记录对象在堆区中的内存地址信息,便于下次访问。

语法格式:

类名引用变量名;
引用变量名.成员变量名;

方法:

定义:

class?类名{
????返回值类型?成员方法名(形参列表)?{?????当成员方法名由多个单词组成时,要求从第二个单词起每个单词的首字母大写
????????成员方法体;
????}
}????
方法内访问成员可以直接访问。
数据类型形参变量名1,?数据类型形参变量名2,?...

调用:

引用变量名.成员方法名(实参列表);

可变长参数:

返回值类型方法名(参数的类型...?参数名)?类型固定,看作数组即可。
方法参数部分指定类型的参数个数是可以改变的,也就是0~n个。
一个方法的形参列表中最多只能声明一个可变长形参,并且需要放到参数列表的末尾。
基于数组的实现的语法糖

参数传递:

值传递。把实参传递给形参,方法内部其实是在使用形参。
基本数据类型的变量作为方法的参数传递时,形参变量数值的改变通常不会影响到实参变量的数值,因为两个变量有各自独立的内存空间;
引用数据类型的变量作为方法的参数传递时,形参变量指向内容的改变会影响到实参变量指向内容的数值,因为两个变量指向同一块内存空间
????当引用数据类型的变量作为方法的参数传递时,若形参变量改变指向后再改变指定的内容,则通常不会影响到实参变量指向内容的改变,因为两个变量指向不同的内存空间。
形参和实参不能同名,否则当作形参了。

内存结构:

栈用于存放程序运行过程当中所有的局部变量。一个运行的Java程序从开始到结束会有多次方法的调用。
JVM会为每一个方法的调用在栈中分配一个对应的空间,这个空间称为该方法的栈帧。一个栈帧对应一个正在调用中的方法,栈帧中存储了该方法的参数、局部变量等数据。
当某一个方法调用完成后,其对应的栈帧将被清除。

构造方法:

class?类名{
????类名(形参列表)?{
????????构造方法体;
????}
}
构造方法名与类名完全相同并且没有返回值类型,连void都不许有。
支持重载。
使用new关键字创建对象时会自动调用构造方法实现成员变量初始化工作。
this可以在构造方法里调用其他构造方法
默认构造方法:
????当一个类中没有定义任何构造方法时,编译器会自动添加一个无参空构造构造方法,叫做默认/缺省构造方法,如:Person(){}
????若类中出现了构造方法,则编译器不再提供任何形式的构造方法。

方法引用:

方法引用通过方法的名字来指向一个方法,可以使语言的构造更紧凑简洁,减少冗余代码。
使用一对冒号?::?

1.?构造器引用:它的语法是Class::new,或者更一般的Class<?T?>::new实例如下:
????final?Car?car?=?Car.create(?Car::new?);
????final?List<?Car?>?cars?=?Arrays.asList(?car?);

2.?静态方法引用:它的语法是Class::static_method,实例如下:
????cars.forEach(?Car::collide?);

3.?特定类的任意对象的方法引用:它的语法是Class::method实例如下:
????cars.forEach(?Car::repair?);

4.?特定对象的方法引用:它的语法是instance::method实例如下:
????final?Car?police?=?Car.create(?Car::new?);
????cars.forEach(?police::follow?);

方法重载:

若方法名称相同,参数列表(类型、个数、顺序)不同,这样的方法之间构成重载关系(Overload)。
和返回类型无关。
实际意义
????调用者只需要记住一个方法名就可以调用各种不同的版本,来实现各种不同的功能。

this关键字:

若在构造方法中出现了this关键字,则代表当前正在构造的对象。可以print验证。
若在成员方法中出现了this关键字,则代表当前正在调用的对象。
this关键字本质上就是当前类类型的引用变量。
工作原理:
????在构造方法中和成员方法中访问成员变量时,编译器会加上this.的前缀,而this.相当于汉语中"我的",
????当不同的对象调用同一个方法时,由于调用方法的对象不同导致this关键字不同,从而this.方式访问的结果也就随之不同。
使用方式:
????1.当局部变量名与成员变量名相同时,在方法体中会优先使用局部变量(就近原则),若希望使用成员变量,则需要在成员变量的前面加上this.的前缀,明确要求该变量是成员变量(重中之重)。
????2.this关键字除了可以通过this.的方式调用成员变量和成员方法外,还可以作为方法的返回值(重点)。
????3.在构造方法的第一行可以使用this()的方式来调用本类中的其它构造方法(了解)。

递归:

使用递归必须有递归的规律以及退出条件。
使用递归必须使得问题简单化而不是复杂化。
若递归影响到程序的执行性能,则使用递推取代之。比如费氏数列

代码拆分:

默认导入同级的class,main方法单独放到xxxTest.java当中。

封装:

私有化成员变量,使用private关键字修饰。
提供公有的get和set方法,并在方法体中进行合理值的判断。
在构造方法中调用set方法进行合理值的判断。
JavaBean
????一种Java语言写成的可重用组件,其它Java?类可以通过反射机制发现和操作这些JavaBean?的属性。
????JavaBean本质上就是符合以下标准的Java类:
????????类是公共的
????????有一个无参的公共的构造器
????????有属性,且有对应的get、set方法

static关键字:

基本概念:

使用static关键字修饰成员变量表示静态的含义,此时成员变量由对象层级提升为类层级,也就是整个类只有一份并被所有对象共享,该成员变量随着类的加载准备就绪,与是否创建对象无关。
static关键字修饰的成员可以使用引用.的方式访问,但推荐类名.的方式。????

使用方式:

在非静态成员方法中既能访问非静态的成员又能访问静态的成员。(成员:成员变量+?成员方法,静态成员被所有对象共享)
在静态成员方法中只能访问静态成员不能访问非静态成员。(成员:成员变量+?成员方法,因为此时可能还没有创建对象)
????静态变量可以通过静态方法修改访问
在以后的开发中只有隶属于类层级并被所有对象共享的内容才可以使用static关键字修饰。(不能滥用static关键字)

内存结构:

静态变量位于方法区(代码区)。指向可能是堆区(单例模式的对象)。

单例设计模式:

流程:
1.私有化构造方法,使用private关键字修饰。
2.声明本类类型的引用指向本类类型的对象,并使用private?static关键字共同修饰。
3.提供公有的get方法负责将对象返回出去,并使用public?static关键字共同修饰。

实现方式:
饿汉式和懒汉式(if?null==xxx再创建),在以后的开发中推荐饿汉式(避免多线程问题)。
双重检查锁
????问题:
????????并发场景下,初始化Singleton?和?将对象地址写到instance字段?的顺序是不确定的。
????????在某个线程new?Singleton()时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了。
????????然而该对象可能还没有初始化(初始化赋值的业务逻辑)。此时若另外一个线程来调用getInstance,取到的就是状态不正确的对象。(比如一些变量还没赋值完成。))
双重检查锁?+?volatile:
????禁止未初始化完成时,就将内存地址赋值给instance字段。

构造块和静态代码块:

构造块:

在类体中直接使用{}括起来的代码块。每创建一个对象都会执行一次构造块。

静态代码块:

使用static关键字修饰的构造块。静态代码块随着类加载时执行一次。先于构造块执行(加载阶段),比如加载数据库的驱动包等。

子类构造时执行顺序:

先加载父类,再加载子类,在执行父类的无参构造方法(会先执行构造块),再执行子类的构造方法

继承:

概念:

当多个类之间有相同的特征和行为时,可以将相同的内容提取出来组成一个公共类,让多个类吸收公共类中已有特征和行为而在多个类型只需要编写自己独有特征和行为的机制,叫做继承。

格式:

在Java语言中使用extends(扩展)关键字来表示继承关系。
public?class?Worker?extends?Person{}?-表示Worker类继承自Person类
????其中Person类叫做超类、父类、基类。
????其中Worker类叫做派生类、子类、孩子类。
使用继承提高了代码的复用性,可维护性及扩展性,是多态的前提条件。

特点:

子类不能继承父类的构造方法和私有方法,但私有成员变量可以被继承只是不能直接访问(可以通过public方法来访问)。
无论使用何种方式构造子类的对象时,都会首先自动调用父类的无参构造方法(没有super的情况下),来初始化从父类中继承的成员变量以及自己的单独成员变量(super()方法做的事情)。
相当于在构造方法的第一行增加代码super()的效果。
????可以手动加super()则解释器不再加(不是第一行会报错)。
????也可以super(实参列表),这样就不再调用父类的无参构造方法
????如果子类不定义任何构造方法,如果父类定义有参,需要父类定义无参构造方法以便super调用。
????如果子类定义了无参构造方法,也需要父类定义无参构造方法以便super隐式或者显式调用。
????如果子类定义了有参构造方法,可以不定义无参,需要父类定义有参或者无参构造方法,以便super隐式或者显式调用。
????总结:一般都定义比较好。
Java语言中只支持单继承不支持多继承,也就是说一个子类只能有一个父类,但一个父类可以有多个子类。

方法重写:

从父类中继承下来的方法不满足子类的需求时,就需要在子类中重新写一个和父类一样的方法来覆盖从父类中继承下来的版本,该方式就叫做方法的重写(Override)
调用父类方法:
????super.function()
示例:?
????@override???????????#?说明重写,若没有重写则编译报错。
????public?void?show(){
????????super.function()
????????xxx
????}
原则:
????要求方法名相同、参数列表相同以及返回值类型相同,从Java5开始允许返回类型为子类类型。
????要求方法的访问权限不能变小,可以相同或者变大。(继承本身是扩展,不能变小)
????要求方法不能抛出更大的异常(异常机制)。
静态方法:?
????不能标注@override,但可以起同名的。

访问控制符:

public?:?对所有类可见。使用对象:类、接口、变量、方法
protected?:?对同一包内的类和所有子类可见。使用对象:变量、方法。?注意:不能修饰类(外部类)。
default?(即默认):?在同一包内可见(不同包内的子类不行),不使用任何修饰符。使用对象:类、接口、变量、方法。
private?:?在同一类内可见。使用对象:变量、方法。?注意:不能修饰类(外部类)

package包:

格式:

package?包名;
package?包名1.包名2.包名3...包名n;

规范:

org.apache.commons.lang.StringUtil
其中StringUtils是类名而org.apache.commons.lang是多层包名,其含义如下:
org.apache表示公司或组织的信息(是这个公司(或组织)域名的反写);common?表示项目的名称信息;lang?表示模块的名称信息。????

导入:

使用import关键字导入包。
使用import关键字导入一个包的静态成员,从Java5.0开始支持。

final关键字:

final关键字修饰类体现在该类不能被继承。
????-主要用于防止滥用继承,如:java.lang.String类等。
final关键字修饰成员方法体现在该方法不能被重写但可以被继承(即只能调用)。
????-主要用于防止不经意间造成重写,如:java.text.Dateformat类中format方法等。
final关键字修饰成员变量体现在该变量必须初始化(一开始显式初始化或者延迟在构造块/方法中初始化)且不能改变。
????-主要用于防止不经意间造成改变,如:java.lang.Thread类中MAX_PRIORITY等。

常量:

在以后的开发中很少单独使用final关键字来修饰成员变量
通常使用public?static?final关键字共同修饰成员变量来表达常量的含义,常量的命名规范要求是所有字母都要大写,不同的单词之间采用下划线连。
????public?static?修饰的常量作用域是全局的,不需要创建对象就可以访问它

多态:

格式:

父类类型引用变量名=?new?子类类型();
如Shape?sr=?new?Rect();

特点:

当父类类型的引用指向子类类型的对象时,父类类型的引用可以直接调用父类独有的方法,不可以直接调用子类独有的方法(编译会报错,需要强转)。????????
对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段调用子类重写的版本(动态绑定)(如果子类没有则调用父类)。
对于父子类都有的静态方法来说,编译和运行阶段都调用父类版本。

引用数据类型之间的转换:

引用数据类型之间的转换方式有两种:自动类型转换和强制类型转换。
????自动类型转换主要指小类型向大类型的转换,也就是子类转为父类,也叫做向上转型。(多态调用)
????强制类型转换主要指大类型向小类型的转换,也就是父类转为子类,也叫做向下转型或显式类型转换。
条件:
????1.引用数据类型之间的转换必须发生在父子类之间,否则编译报错。
????2.如果转换发生在父级和兄弟子类或者孙类这种不是父子关系时,编译阶段不报错,但运行阶段会报ClassCastException。
????????若强转的目标类型并不是该引用真正指向的数据类型时则编译通过,运行阶段发生类型转换异常。
使用:
????为了避免上述错误的发生,应该在强转之前进行判断,格式如下:
????????if(引用变量instanceof数据类型)
????判断引用变量指向的对象是否为后面的数据类型

实际意义:

屏蔽不同子类的差异性实现通用的编程带来不同的效果。

多态存在的三个必要条件:

1.继承
2.重写
3.父类引用指向子类对象 //Parent p = new Child();

多态的实现方式:

方式一:重写
方式二:接口
方式三:抽象类和抽象方法????

多态的使用场景:

1.通过方法的参数传递形成多态
2.抽象类或接口类的引用指向子类,在方法体中直接使用多态的语法格式
3.通过方法的返回值类型形成多态

虚函数:

虚函数的存在是为了多态。
Java?中其实没有虚函数的概念,它的普通函数就相当于?C++?的虚函数,动态绑定是Java的默认行为。
如果?Java?中不希望某个函数具有虚函数特性,可以加上?final?关键字变成非虚函数。
重写:?
????当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。要想调用父类中被重写的方法,则必须使用关键字?super。
示例:????
????Salary?s?=?new?Salary("员工?A",?"北京",?3,?3600.00);
????Employee?e?=?new?Salary("员工?B",?"上海",?2,?2400.00);
????在编译的时候,编译器使用?Employee?类中的?mailCheck()?方法验证该语句,?
????但是在运行的时候,Java虚拟机(JVM)调用的是?Salary?类中的?mailCheck()?方法
????以上整个过程被称为虚拟方法调用,该方法被称为虚拟方法。
????Java中所有的方法都能以这种方式表现,因此,重写的方法能在运行时调用,不管编译的时候源代码中引用变量是什么数据类型。?

抽象方法:

概述:

主要指不能具体实现的方法并且使用abstract关键字修饰,也就是没有方法体。

格式:

访问权限?abstract?返回值类型?方法名(形参列表);

兼容的访问权限:

不能private,私有方法不能继承
不能final,需要修改。
不能static,抽象方法不能提升为类层级
default需要同一包内。

抽象类:

概念:

主要指不能具体实例化的类并且使用abstract关键字修饰,也就是不能创建对象。
和普通类的区别:
????不能具体实例化
????声明了抽象方法。

特点:

抽象类中可以有成员变量、构造方法、成员方法(供子类super调用)
抽象类中可以没有抽象方法,也可以有抽象方法(抽象类不能实例化,防止误调用抽象方法);
拥有抽象方法的类必须是抽象类
因此真正意义上的抽象类应该是具有抽象方法(不能实例化的意义所在)并且使用abstract关键字修饰的类。

实际意义:

抽象类的实际意义不在于创建对象而在于被继承。
当一个类继承抽象类后必须重写抽象方法,否则该类也变成抽象类,也就是抽象类对子类具有强制性和规范性,因此叫做模板设计模式。

格式:

public?abstract?class?Employee
{
???public?abstract?double?computePay();
???//其余代码
}

接口:

概述:

一种比抽象类还抽象的类,体现在所有方法都为抽象方法。

定义:

定义类的关键字是class,而定义接口的关键字是interface。
里面只能声明常量,可以忽略public?static?final?
从jdk1.9开始允许接口出现私有方法。
方法可以忽略public?abstract

支持的修饰符:

public?
private

实际意义:

类可以实现多个接口,达到多继承的效果

格式:

Interface关键字用来声明一个接口。
[可见度]?interface?接口名称?[extends?其他的接口名]?{
????????//?声明变量
????????//?抽象方法
}

使用:

implements?A,B?支持多实现,接口之间可以继承。
然后
????A?xxx?=?new?yyy();可以调用A接口的方法
????B?xxx?=?new?yyy();可以调用B接口的方法

标记接口:

最常用的继承接口是没有包含任何方法的接口。
标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
????public?interface?EventListener
????{}
没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:
????1.?建立一个公共的父接口:
????????正如EventListener接口,这是由几十个其他接口扩展的Java?API,你可以使用一个标记接口来建立一组接口的父接口。
????????例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
????2.?向一个类添加数据类型:
????????这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。????????

类和接口之间的关系:

名称????????????????????????????关键字?????????????????????????关系
类和类之间的关系??????????使用extends关键字表达继承关系??????????支持单继承
类和接口之间的关系?????????使用implements关键字表达实现关系???支持多实现
接口和接口之间的关系?????使用extends关键字表达继承关系??????支持多继承

抽象类和接口的主要区别:

定义抽象类的关键字是abstract?class,而定义接口的关键字是interface。
继承抽象类的关键字是extends,而实现接口的关键字是implements。
继承抽象类支持单继承,而实现接口支持多实现。(区别3)
抽象类中可以有构造方法,而接口中不可以有构造方法。(区别1)
抽象类中可以有成员变量,而接口中只可以有常量。(区别2)
抽象类中可以有成员方法,而接口中只可以有抽象方法。
抽象类中增加方法时子类可以不用重写,而接口中增加方法时实现类需要重写(Java8以前的版本)。
从Java8开始增加新特性,接口中允许出现非抽象方法和静态方法(工具类功能),但非抽象方法需要使用default关键字修饰(表示接口的默认功能方法,子类可以选择性重写)。
从Java9开始增加新特性,接口中允许出现私有方法。(一般用于定义一些基础方法,供default和static方法调用)

函数式接口:

一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为?lambda?表达式。
如:
????@FunctionalInterface
????interface?GreetingService?
????{
????????void?sayMessage(String?message);
????}
那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA?8?之前一般是用匿名类实现的):
????GreetingService?greetService1?=?message?->?System.out.println("Hello?"?+?message);

内部类:

基本概念:

当一个类的定义出现在另外一个类的类体中时,那么这个类叫做内部类(Inner),而这个内部类所在的类叫做外部类(Outer)。
#?类中的内容:成员变量、成员方法、构造方法、静态成员、构造块和静态代码块、内部类。

实际作用:

当一个类存在的价值仅仅是为某一个类单独服务时,那么就可以将这个类定义为所服务类中的内部类,这样可以隐藏该类的实现细节并且可以方便的访问外部类的私有成员而不再需要提供公有的get和set方法。

分类:

普通内部类-直接将一个类的定义放在另外一个类的类体中。
静态内部类-使用static关键字修饰的内部类,隶属于类层级。
局部内部类-直接将一个类的定义放在方法体的内部时。
匿名内部类-就是指没有名字的内部类。

普通(成员)内部类:

定义:
属于外部类的一个成员,能直接调用外部类的其他成员

特点:
普通内部类和普通类一样可以定义成员变量、成员方法以及构造方法等。
普通内部类和普通类一样可以使用final(限制内部类的继承)或者abstract关键字修饰。
普通内部类还可以使用private(不能用于实例化)或protected关键字进行修饰。
普通内部类需要使用外部类对象来创建对象。Outer.Inner?in?=?ot.new?Inner();
如果内部类访问外部类中与本类内部同名的成员变量或方法时,需要使用this关键字。(遵循局部优先原则,this/Outer.this分别访问内部和外部对象)

静态内部类:

格式:
访问修饰符staticclass?内部类的类名

使用方式:
静态内部类不能直接访问外部类的非静态成员,可以直接访问静态成员。
静态内部类可以直接创建对象。Outer.Inner?in?=?new?Outer.Inner();
如果静态内部类访问外部类中与本类内同名的成员变量或方法时,需要使用类名.的方式访问。

局部(方法)内部类:

格式:
class?内部类的类名
没有修饰符(类似局部变量)

使用方式:
局部内部类只能在该方法的内部可以使用。
局部内部类可以在方法体内部直接创建对象。
局部内部类不能使用访问控制符和static关键字修饰符。
局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致。
????默认的局部变量从java8开始默认理解为final关键字修饰的变量,如果修改了,会报错。
????原因是防止局部变量拷贝到内部类中修改了,造成内外不一致。(防止修改)

回调模式:

如果一个方法的参数是接口类型,则在调用该方法时,需要创建并传递一个实现此接口类型的对象;而该方法在运行时会调用到参数对象中所实现的方法(接口中定义的)。
使用:?
????自定义类实现接口/继承类并重写方法,然后创建该类对象作为实参传递;
????使用上述匿名内部类的语法格式得到接口/类类型的引用即可;

匿名内部类:

格式:
接口/父类类型?引用变量名=?new?接口/父类类型()?{?方法的重写};
#?lambda表达式更简单()->{}

lambda表达式:

注意:

在lambda表达式中对变量的操作都是基于原变量的副本,不会影响到原变量的值。
只能引用标记了?final?的外层局部变量,否则会误以为外部变量的值能够在lambda表达式中被改变。

枚举:

概述:

在日常生活中这些事物的取值只有明确的几个固定值,此时描述这些事物的所有值都可以一一列举出来,而这个列举出来的类型就叫做枚举类型。

之前的实现方法:

在class中定义多个常量,给外部访问。
缺点:
????重复声明比较多

定义:

?使用public?static?final表示的常量描述较为繁琐,使用enum关键字来定义枚举类型取代常量,枚举类型是从Java5开始增加的一种引用数据类型。
?枚举值就是当前类的类型,也就是指向本类的对象,默认使用public?static?final关键字共同修饰,因此采用枚举类型.的方式调用。
?枚举类可以自定义构造方法,但是构造方法的修饰符必须是private,默认也是私有的。

示例:

public?enum?DirectionEnum?{
????UP("up"),DOWN("down");

????private?final?String?desc;

????private?DirectionEnum(String?desc)?{
????????this.desc?=?desc;
????}
}

switch使用:

function(Enum?de){
????switch(de){
????????case?UP:
????????case?DOWN:
????}
}
更简洁,对输入类型更限制

Enum类:

概念:
所有的枚举类都继承自java.lang.Enum类

方法:
static?T[]?values()返回当前枚举类中的所有对象
String?toString()返回当前枚举类对象的名称
intordinal()获取枚举对象在枚举类中的索引位置
static?T?valueOf(String?str)将参数指定的字符串名转为当前枚举类的对象,要求字符串必须对应存在
intcompareTo?(E?o)比较两个枚举对象在定义时的顺序,结果为1,0,-1,-2等,当调用对象在参数对象之后时,获取到的比较结果为正数。

实现接口:

不能继承,可以实现。
枚举类实现接口后需要重写抽象方法,而重写方法的方式有两种:重写一个,或者每个对象都重写。
每个对象都重写:
????UP("up"){重写方法}
????本质上是匿名内部类

注解:

基本概念:

注解(Annotation)又叫标注,是从Java5开始增加的一种引用数据类型。
注解本质上就是代码中的特殊标记,通过这些标记可以在编译、类加载、以及运行时执行指定的处理。

语法格式:

访问修饰符@interface?注解名称{
????注解成员;
}
自定义注解自动继承java.lang.annotation.Annotation接口。
若一个注解中没有任何的成员,则这样的注解叫做标注注解/标识注解。
注解体中只有成员变量没有成员方法,而注解的成员变量以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
如果注解只有一个参数成员,建议使用参数名为value,而类型只能是八种基本数据类型、String类型、Class类型、enum类型及Annotation类型。
成员变量可以有默认值:private?String?value()?default?"";
如:?
????public?String?value();

使用:

通过@注解名称的方式可以修饰包、类、成员方法、成员变量、构造方法、参数、局部变量的声明等。
@MyAnnotation(value=xxx,value2=yyy)

元注解:

概述:
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。

分类:
元注解主要有@Retention、@Documented、@Target、@Inherited、@Repeatable。

元注解@Retention:

?@Retention 应用到一个注解上用于说明该注解的的生命周期,取值如下:

?RetentionPolicy.SOURCE?注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
?RetentionPolicy.CLASS?注解只被保留到编译进行的时候,它并不会被加载到JVM?中,默认方式。
?RetentionPolicy.RUNTIME?注解可以保留到程序运行的时候,它会被加载进入到JVM?中,所以在程序运行时可以获取到它们。
元注解@Documented
?使用javadoc工具可以从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的API帮助文档,而该工具抽取时默认不包括注解内容。
?@Documented用于指定被该注解将被javadoc工具提取成文档。
?定义为@Documented的注解必须设置Retention值为RUNTIME。
元注解@Target
用于指定被修饰的注解能用于哪些元素的修饰
ElementType.ANNOTATION_TYPE可以给一个注解进行注解
ElementType.CONSTRUCTOR可以给构造方法进行注解
ElementType.FIELD可以给属性进行注解
ElementType.LOCAL_VARIABLE可以给局部变量进行注解
ElementType.METHOD可以给方法进行注解
ElementType.PACKAGE可以给一个包进行注解
ElementType.PARAMETER可以给一个方法内的参数进行注解
ElementType.TYPE可以给类型进行注解,比如类、接口、枚举

从Java8开始对元注解@Target的参数类型ElementType枚举值增加了两个:

ElementType.TYPE_PARAMETER?表示该注解能写在类型变量的声明语句中,如:泛型。
ElementType.TYPE_USE?表示该注解能写在使用类型的任何语句中。场景更广泛。

元注解@Inherited:
@Inherited并不是说注解本身可以继承,而是说如果一个超类被该注解标记过的注解进行注解时,如果子类没有被任何注解应用时,则子类就继承超类的注解。

元注解@Repeatable:
表示自然可重复的含义,从Java8开始增加的新特性。
示例:?
????@Repeatable(value=ManTypes.class)
默认不能重复。java8之前可以通过注解数组的方式@manTypes({@ManType(value=""),@ManType(value="")})

常见的预制注解:

@author标明开发该类模块的作者,多个作者之间使用,分割
@version标明该类模块的版本
@see参考转向,也就是相关主题
@since从哪个版本开始增加的
@param对方法中某参数的说明,如果没有参数就不能写
@return对方法返回值的说明,如果方法的返回值类型是void就不能写
@exception对方法可能抛出的异常进行说明????
@Override限定重写父类方法,?该注解只能用于方法
@Deprecated用于表示所修饰的元素(类,?方法等)已过时
@SuppressWarnings抑制编译器警告

Object类:

基本概念:

java.lang.Object类是Java语言中类层次结构的根类,也就是说任何一个类都是该类的直接或者间接子类。
如果定义一个Java类时没有使用extends关键字声明其父类,则其父类为?java.lang.Object?类。
Object类定义了“对象”的基本行为,?被子类默认继承。

常用的方法:

Object()?使用无参方式构造对象
boolean?equals(Object?obj)用于判断调用对象是否与参数对象相等。该方法默认比较两个对象的地址是否相等,与?==?运算符的结果一致若希望比较两个对象的内容,则需要重写该方法。
????若该方法被重写后,则应该重写hashCode方法来保证结果的一致性(不然放到字典中时,两个相等的对象存的位置不同)。
????需要强转来调用子类的独有方法。
????自反性、对称的、传递性、一致的、对于任何非空的参考值x?,?x.equals(null)应该返回false。?
????优化:需要判断是否地址相同,是否为null
int?hashCode()用于获取调用对象的哈希码值(内存地址的编号)。
????若两个对象调用equals方法相等,则各自调用该方法的结果必须相同。
????若两个调用对象equals方法不相等,则各自调用该方法的结果应该不相同。
????为了使得该方法与equals方法保持一致,需要重写该方法。一般加个固定值。不然很可能会因为存储了两个equals相等的数据而导致存储数据的不唯一性,导致内存泄露。
????非基本类型可以用对应的引用类型的hashCode方法
String?toString()用于获取调用对象的字符串形式。该方法默认返回的字符串为:包名.类名@哈希码值的十六进制。
????为了返回更有意义的数据,需要重写该方法。使用print或println打印引用或字符串拼接+引用都会自动调用该方法
Class<?>?getClass()?用于返回调用对象执行时的Class实例,反射机制使用

包装类:

概念:

通常情况下基本数据类型的变量不是对象,为了满足万物皆对象的理念就需要对基本数据类型的变量进行打包封装处理变成对象,而负责将这些变量声明为成员变量进行对象化处理的相关类,叫做包装类。????

内置:

java.lang.Byte?????????????byte
java.lang.Short?????????short
java.lang.Integer?????????int
java.lang.Long?????????????long
java.lang.Float?????????float
java.lang.Double?????????double??????//?java.lang.Number类是个抽象类,是上述类的父类来描述所有类共有的成员。
java.lang.Boolean?????????boolean
java.lang.Character?????char

Integer类:

基本概念:

java.lang.Integer类内部包装了一个int类型的变量作为成员变量,主要用于实现对int类型的包装并提供int类型到String类之间的转换等方法。

常用的常量:

public?static?final?int?MAX_VALUE?表示int类型可以描述的最大值,即2^31-1
public?static?final?int?MIN_VALUE?表示int类型可以描述的最小值,即-2^31
public?static?final?int?SIZE?表示int类型采用二进制补码形式的位数
public?static?final?int?BYTES?表示int类型所占的字节个数
public?static?final?Class?TYPE?表示int类型的Class实例

常用的方法:

Integer(int?value)?根据参数指定的整数来构造对象(已过时)
Integer(String?s)?根据参数指定的字符串来构造对象?(已过时)
int?intValue()?获取调用对象中的整数值并返回
static?Integer?valueOf(int?i)?根据参数指定整数值得到Integer类型对象,也可以传string
boolean?equals(Object?obj)?比较调用对象与参数指定的对象是否相等
String?toString()?返回描述调用对象数值的字符串形式
static?int?parseInt(String?s)?将字符串类型转换为int类型并返回
static?String?toString(int?i)?获取参数指定整数的十进制字符串形式
static?String?toBinaryString(int?i)?获取参数指定整数的二进制字符串形式
static?String?toHexString(int?i)?获取参数指定整数的十六进制字符串形式
static?String?toOctalString(int?i)?获取参数指定整数的八进制字符串形式

装箱和拆箱的概念:

在Java5发布之前使用包装类对象进行运算时,需要较为繁琐的“拆箱”和“装箱”操作;即运算前先将包装类对象拆分为基本类型数据,运算后再将结果封装成包装类对象。
从Java5开始增加了自动拆箱和自动装箱的功能。

自动装箱池:

在Integer类的内部提供了自动装箱池技术(vm?cache,可以调优),将-128到127之间的整数已经装箱完毕,当程序中使用该范围之间的整数时,无需装箱直接取用自动装箱池中的对象即可,从而提高效率。
????Interger?it?=?100;??注意会自动装箱,超过128的会导致it1==it2为false,但数值相等。小于128的内存地址会一致。
????int?ib?=?it;

Double类:

概述:

java.lang.Double类型内部包装了一个double类型的变量作为成员变量,主要用于实现对double类型的包装并提供double类型到String类之间的转换等方法。

常用的方法:

double?doubleValue()?获取调用对象中的浮点数据并返回
static?Double?valueOf(double?d)?根据参数指定浮点数据得到Double类型对象
static?double?parseDouble(String?s)?将字符串类型转换为double类型并返回
boolean?isNaN()?判断调用对象的数值是否为非数字。0/0.0为nan

装箱和拆箱:

Double?d?=?3.14;
double?d2?=?d;
没有装箱池

Boolean类:

概述;
????java.lang.Boolean类型内部包装了一个boolean类型的变量作为成员变量,主要用于实现对boolean类型的包装并提供boolean类型到String类之间的转换等方法。
常用的方法:
????boolean?booleanValue()?获取调用对象中的布尔数值并返回
????static?Boolean?valueOf(boolean?b)?根据参数指定布尔数值得到Boolean类型对象,内部调用parseBoolean方法
????static?boolean?parseBoolean(String?s)?将字符串类型转换为boolean类型并返回,内部用的是"true".equalsIgnoreCase(s),其他情况都返回false
装箱和拆箱:
????类似

Character类:

概述:

主要用于实现对char类型的包装并提供字符类别的判断和转换等方法。

常用的方法:

char?charValue()?获取调用对象中的字符数据并返回
static?Character?valueOf(char?c)?根据参数指定字符数据得到Character类型对象
boolean?equals(Object?obj)?比较调用对象与参数指定的对象是否相等
String?toString()?返回描述调用对象数值的字符串形式
static?boolean?isUpperCase(char?ch)?判断参数指定字符是否为大写字符
static?boolean?isLowerCase(char?ch)?判断参数指定字符是否为小写字符
static?boolean?isDigit(char?ch)?判断参数指定字符是否为数字字符
static?char?toUpperCase(char?ch)?将参数指定的字符转换为大写字符
static?char?toLowerCase(char?ch)?将参数指定的字符转换为小写字符

数学处理类:

math类:

java.lang.Math类主要用于提供执行数学运算的方法,如:对数,平方根。
常用的方法:
????static?int?max(int?a,?int?b)?返回两个参数中的最大值
????static?int?min(int?a,?int?b)?返回两个参数中的最小值
????static?double?pow(double?a,?double?b)?返回第一个参数的幂
????static?int?abs(int?a)?返回参数指定数值的绝对值
????static?long?round(double?a)?返回参数四舍五入的结果
????static?double?sqrt(double?a)?返回参数的平方根
????static?double?random()?返回0.0到1.0的随机数

BigDecimal类:

概念:

由于float类型和double类型在运算时可能会有误差,若希望实现精确运算则借助java.math.BigDecimal类型加以描述。
常用的方法
BigDecimal(String?val)?根据参数指定的字符串来构造对象
BigDecimal?add(BigDecimal?augend)?用于实现加法运算
BigDecimal?subtract(BigDecimal?subtrahend)?用于实现减法运算
BigDecimal?multiply(BigDecimal?multiplicand)?用于实现乘法运算
BigDecimal?divide(BigDecimal?divisor)?用于实现除法运算,注意:必须要除尽。否则抛出ArithmeticException。
BigDecimal?divide?(BigDecimal?divisor,?int?roundingMode)常用RoundingMode.HALF_UP,需要import

BigInteger类:

概念:

若希望表示比long类型范围还大的整数数据,则需要借助java.math.BigInteger类型描述。

常用的方法:

BigInteger(String?val)?根据参数指定的字符串来构造对象
BigInteger?add(BigInteger?val)?用于实现加法运算
BigInteger?subtract(BigInteger?val)?用于实现减法运算
BigInteger?multiply(BigInteger?val)?用于实现乘法运算
BigInteger?divide(BigInteger?val)?用于实现除法运算
BigInteger?remainder(BigInteger?val)?用于实现取余运算
BigInteger[]?divideAndRemainder(BigInteger?val)?用于实现取商和余数的运算

String类:

概念(重点):

1.java.lang.String类用于描述字符串,Java程序中所有的字符串字面值都可以使用该类的对象加以描述,如:"abc"。
????只有String可以new?+?字面值
2.该类由final关键字修饰,表示该类不能被继承。
3.从jdk1.9开始该类的底层不使用char[]来存储数据,而是改成?byte[](1个字节)加上编码标记,从而节约了一些空间。
4.该类描述的字符串内容是个常量不可更改,因此可以被共享使用。
????如:
????????String?str1?=?“abc”;?-?其中"abc"这个字符串是个常量不可改变。在方法区(常量区)声明这个常量后,将地址返回给变量。
????????str1?=?“123”;?-?将“123”字符串的地址赋值给变量str1。
??????????????????????-?改变str1的指向并没有改变指向的内容

常量池的概念(原理):

由于String类型描述的字符串内容是常量不可改变,因此Java虚拟机将首次出现的字符串放入常量池中,若后续代码中出现了相同字符串内容则直接使用池中已有的字符串对象而无需申请内存及创建对象,
从而提高了性能。
特殊情况:
????"ab"和"a"+"b"是同一个,常量优化机制。常量是固定的。
????如果是其中一个是变量,则不是同一个地址。
????即String?s1?=?"abc";?????String?s2?=?"a"?+?"bc";?????s1==s2;
????而String?s11?=?"a";??????String?s22?=?"bc";??????String?s6?=?s11?+?s22;??s1!=s6;
验证:?
????声明两个相同的字符串,比较两个变量的内存地址即可。
intern()方法:
????首先检查字符串池中是否有”abc”这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用。

常用的构造方法:

String()?使用无参方式构造对象得到空字符序列
String(byte[]?bytes,?int?offset,?int?length)使用bytes数组(ascii码)中下标从offset位置开始的length个字节来构造对象。原理:逐个翻译字节数字。
String(byte[]?bytes)?使用bytes数组中的所有内容构造对象
String(char[]?value,?int?offset,?int?count)使用value数组中下标从offset位置开始的count个字符来构造对象
String(char[]?value)?使用value数组中的所有内容构造对象
String(String?original)根据参数指定的字符串内容来构造对象,新创建对象为参数对象的副本。两个对象,一个在常量池,一个在堆区(new)。

常用的成员方法:

String?toString()?返回字符串本身
byte[]?getBytes()?将当前字符串内容转换为byte数组并返回????????思路:拆分字符串为多个字符,然后转换为数字
char[]?toCharArray()?用于将当前字符串内容转换为char数组并返回????

char?charAt(int?index)?方法charAt用于返回字符串指定位置的字符。
int?length()?返回字符串字符序列的长度。
boolean?isEmpty()?判断字符串是否为空,底层判断长度。

int?compareTo(String?anotherString)?用于比较调用对象和参数对象的大小关系。逐字符比较,返回的一个不同字符的ascii码数值相差。如果是长度不同,返回的一个长度减去第二个长度。
int?compareToIgnoreCase(String?str)?不考虑大小写,也就是'a'和'A'是相等的关系

String?concat(String?str)?用于实现字符串的拼接。更简单用+
boolean?contains(CharSequence?s)?用于判断当前字符串是否包含参数指定的内容,CharSequence是接口,String实现了该接口
String?toLowerCase()?返回字符串的小写形式
String?toUpperCase()?返回字符串的大写形式
String?trim()?返回去掉前导和后继空白的字符串
boolean?startsWith(String?prefix)?判断字符串是否以参数字符串开头
boolean?startsWith(String?prefix,?int?toffset)?从指定位置开始是否以参数字符串开头
boolean?endsWith(String?suffix)?判断字符串是否以参数字符串结尾

boolean?equals(Object?anObject)?用于比较字符串内容是否相等并返回,逐字符比较。
int?hashCode()?获取调用对象的哈希码值
boolean?equalsIgnoreCase(String?anotherString)用于比较字符串内容是否相等并返回,不考虑大小写,如:'A'和'a'是相等

int?indexOf(int?ch)用于返回当前字符串中参数ch指定的字符第一次出现的下标,失败返回-1
int?indexOf(int?ch,?int?fromIndex)?用于从fromIndex位置开始查找ch指定的字符
int?indexOf(String?str)在字符串中检索str返回其第一次出现的位置(第一个字符的下标),若找不到返回-1
int?indexOf(String?str,?int?fromIndex)表示从字符串的fromIndex位置开始检索str第一次出现的位置
int?lastIndexOf(int?ch)?用于返回参数ch指定的字符最后一次出现的下标
int?lastIndexOf(int?ch,?int?fromIndex)用于从fromIndex位置开始反向查找ch指定字符出现的下标
int?lastIndexOf(String?str)?返回str指定字符串最后一次出现的下标
int?lastIndexOf(String?str,?int?fromIndex)用于从fromIndex位置开始反向搜索的第一次出现的下标。

String?substring(int?beginIndex,?int?endIndex)返回字符串中从下标beginIndex(包括)开始到endIndex(不包括)结束的子字符串
String?substring(int?beginIndex)返回字符串中从下标beginIndex(包括)开始到字符串结尾的子字符串

boolean?matches(String?regex)判断当前正在调用的字符串是否匹配参数指定的正则表达式规则

String[]?split(String?regex)参数regex为正则表达式,以regex所表示的字符串为分隔符,将字符串拆分成字符串数组
String?replace(char?oldChar,?char?newChar)使用参数newChar替换此字符串中出现的所有参数oldChar
String?replaceFirst(String?regex,String?replacement)替换此字符串匹配给定的正则表达式的第一个子字符串
String?replaceAll(String?regex,String?replacement)将字符串中匹配正则表达式regex的字符串替换成replacement

字符串数字转整数:

int?ib=0;
for(int?i=0;i<str.length();i++){
????ib?=?ib*10+str2.charAt(i)-'0';
}
Interger.parseInt(string)也可以

整数转字符串:

String.valueof()
""+123

可变字符串类:

基本概念:

由于String类描述的字符串内容是个常量不可改变,当需要在Java代码中描述大量类似的字符串时,只能单独申请和存储,此时会造成内存空间的浪费。
为了解决上述问题,可以使用java.lang.StringBuilder类和java.lang.StringBuffer类来描述字符序列可以改变的字符串,如:"ab"。
StringBuffer类是从jdk1.0开始存在,属于线程安全的类,因此效率比较低。
StringBuilder类是从jdk1.5开始存在,属于非线程安全的类,效率比较高。
作为参数传递的话,方法内部String不会改变其值,StringBuffer和StringBuilder会改变其值。
为什么修改本身,还有返回本身?
????为了连续调用。
如何在StringBuilder和String之间互转?
????toString方法
????构造方法
性能排行?
????string?<?stringBuffer?<?stringBuilder

常用的构造方法:

StringBuilder()?使用无参方式构造对象,容量为16
StringBuilder(int?capacity)?根据参数指定的容量来构造对象,容量为参数指定大小
StringBuilder(String?str)?根据参数指定的字符串来构造对象,容量为:16+字符串长度

常用的成员方法:

int?capacity()?用于返回调用对象的容量
int?length()?用于返回字符串的长度,也就是字符的个数
StringBuilder?insert(int?offset,?String?str)插入字符串并返回调用对象的引用,就是自己。原值也改变了(不同于String)。
StringBuilder?append(String?str)?追加字符串,还可以是CharSequence、StringBuffer等形参。
StringBuilder?deleteCharAt(int?index)将当前字符串中下标为index位置的单个字符删除
StringBuilder?delete(int?start,int?end)?删除字符串
StringBuilder?replace(int?start,int?end,String?str)替换字符串
StringBuilder?reverse()?字符串反转
还有charAt查找、indexOf、LastIndexOf、subString、setCharAt等。

容量的扩容算法:

底层采用byte[]来存放
ensureCapacityInternal(count?+?len);????确保容量往后满足。
????????putStringAt(count,?str);????????放到count的位置。coutn表示当前char用了多少

private?void?ensureCapacityInternal(int?minimumCapacity)?{
????int?oldCapacity?=?value.length?>>?coder;????????#?value是底层byte数组。coder是编码相关,在utf16是1,在LATIN1是0
????if?(minimumCapacity?-?oldCapacity?>?0)?{????????#?容量不满足时,发生扩容
????????value?=?Arrays.copyOf(value,
????????????????newCapacity(minimumCapacity)?<<?coder);?????#?翻倍
????}
}
newCapacity方法具体算出新容量,utf16为最低长度的2倍或者2*旧容量+4,LATIN1是2*旧容量+2或者最低长度,具体配置通过COMPACT_STRINGS

时间:

System类:

基本概念:

Java.lang.System类中提供了一些有用的类字段和方法。

常用的方法:

static?long?currentTimeMillis()????返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差

Date类的概述:

基本概念:

java.util.Date类主要用于描述特定的瞬间,也就是年月日时分秒,可以精确到毫秒。

常用的方法:

Date()?使用无参的方式构造对象,也就是当前系统时间
Date(long?date)根据参数指定毫秒数构造对象,?参数为距离1970年1月1日0时0分0秒的毫秒数
long?getTime()?获取调用对象距离1970年1月1日0时0分0秒的毫秒数
void?setTime(long?time)设置调用对象为距离基准时间time毫秒的时间点

SimpleDateFormat类:

基本概念:

java.text.SimpleDateFormat类主要用于实现日期和文本之间的转换。

常用的方法:

SimpleDateFormat()?使用无参方式构造对象
SimpleDateFormat(String?pattern)根据参数指定的模式来构造对象,模式主要有:?y-年?M-月?d-日?H-时?m-分?s-秒
final?String?format(Date?date)用于将日期类型转换为文本类型
Date?parse(String?source)?用于将文本类型转换为日期类型

Calendar类:

基本概念:

java.util.Calender类主要用于描述特定的瞬间,取代Date类中的过时方法实现全球化。
该类是个抽象类,因此不能实例化对象,其具体子类针对不同国家的日历系统,其中应用最广泛的是GregorianCalendar(格里高利历),对应世界上绝大多数国家/地区使用的标准日历系统。

常用的方法:

static?Calendar?getInstance()用于获取Calendar类型的引用
void?set(int?year,?int?month,?int?date,?int?hourOfDay,?int?minute,?int?second)用于设置年月日时分秒信息,month从0开始,需要减1
void?setTime(Date?date)
Date?getTime()用于将Calendar类型转换为Date类型
void?set(int?field,?int?value)?设置指定字段的数值,Calendar.YEAR
void?add(int?field,?int?amount)?向指定字段增加数值

注意:

Calendar本身是abstract(有构造方法但只能super调用),为啥可以返回类型的对象?
该静态方法返回的是子类。
多态的场景之三:返回类型形成多态

Java8日期类:

由来:

JDK?1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK?1.1引入Calendar类之后被弃用了。而Calendar并不比Date好多少。
它们面临的问题是:
????Date类中的年份是从1900开始的,而月份都从0开始。
????格式化只对Date类有用,对Calendar类则不能使用。
????非线程安全等。

概述:

Java?8通过发布新的Date-Time?API来进一步加强对?日期与时间的处理。
java.time包:该包日期/时间API的基础包。
java.time.chrono包:该包提供对不同日历系统的访问。
java.time.format包:该包能够格式化和解析日期时间对象。
java.time.temporal包:该包包含底层框架和扩展特性。
java.time.zone包:该包支持不同时区以及相关规则的类。

LocalDate类:

基本概念:

java.time.LocalDate类主要用于描述年-月-日格式的日期信息,该类不表示时间和时区信息。

常用的方法:

static?LocalDate?now()?在默认时区中从系统时钟获取当前日期

LocalTime类:

基本概念:

java.time.LocalTime?类主要用于描述时间信息,可以描述时分秒以及纳秒。

常用的方法:

static?LocalTime?now()?从默认时区的系统时间中获取当前时间
static?LocalTime?now(ZoneId?zone)?获取指定时区的当前时间

LocalDateTime类:

基本概念:

java.time.LocalDateTime类主要用于描述ISO-8601日历系统中没有时区的日期时间,如2007-12-03T10:15:30。

常用的方法:

static?LocalDateTime?now()从默认时区的系统时间中获取当前日期时间
static?LocalDateTime?of(int?year,?int?month,?int?dayOfMonth,?int?hour,?int?minute,?int?second)根据参数指定的年月日时分秒信息来设置日期时间
static?LocalDateTime?LocalDateTime.ofInstant(Instant?instant,?ZoneId?zone)根据Instant来设置日期时间
int?getYear()?获取年份字段的数值
int?getMonthValue()?获取1到12之间的月份字段
int?getDayOfMonth()?获取日期字段
int?getHour()?获取小时数
int?getMinute()?获取分钟数
int?getSecond()?获取秒数
LocalDateTime?withYear(int?year)?设置为参数指定的年,返回的是同类型新对象,和String类型相似
LocalDateTime?withSecond(int?second)?设置为参数指定的秒
LocalDateTime?plusYears(long?years)?加上参数指定的年
LocalDateTime?plusSeconds(long?seconds)?加上参数指定的秒
LocalDateTime?minusYears(long?years)?减去参数指定的年
LocalDateTime?minusSeconds(long?seconds)?减去参数指定的秒

Instant类:

基本概念:

java.time.Instant类主要用于描述瞬间的时间点信息。

常用的方法:

static?Instant?now()?从系统时钟上获取当前时间,utc时区
OffsetDateTime?atOffset(ZoneOffset?offset)将此瞬间与偏移量组合以创建偏移日期时间,ZoneOffset.ofHours(8)
static?Instant?ofEpochMilli(long?epochMilli)根据参数指定的毫秒数来构造对象,参数为距离1970年1月1日0时0分0秒的毫秒数
long?toEpochMilli()?获取距离1970年1月1日0时0分0秒的毫秒数

DateTimeFormatter类:

基本概念:

java.time.format.DateTimeFormatter类主要用于格式化和解析日期。

常用的方法:

static?DateTimeFormatter?ofPattern(String?pattern)?根据参数指定的模式来获取对象
String?format(TemporalAccessor?temporal)?将参数指定日期时间转换为字符串,TemporalAccessor是接口,可以传Instant、LocalDateTime
TemporalAccessor?parse(CharSequence?text)?将参数指定字符串转换为日期时间

集合类库:

集合:

由来:

当需要在Java程序中记录单个数据内容时,则声明一个变量。
当需要在Java程序中记录多个类型相同的数据内容时,声明一个一维数组。
当需要在Java程序中记录多个类型不同的数据内容时,则创建一个对象。
当需要在Java程序中记录多个类型相同的对象数据时,创建一个对象数组。
当需要在Java程序中记录多个类型不同的对象数据时,则准备一个集合。

集合的框架结构:

Java中集合框架顶层框架是:java.util.Collection集合?和?java.util.Map集合。
其中Collection集合中存取元素的基本单位是:单个元素。
其中Map集合中存取元素的基本单位是:单对元素。

关系图:

?????????????????????collection?????????????????????????????????????????????????????????????Map
???set?????????????????????????????????List??????????queue??????????????????????????????????????????????????????SortedMap
??????????????SortedSet???
HashSet?LinkedSet?TreeSet????ArrayList?Vector?LinkedList??PriorityQueue?????????HashMap??LinkedHashMap??HashMap??TreeMap

Collection集合:

基本概念:

java.util.Collection接口是List接口、Queue?接口以及Set接口的父接口,因此该接口里定义的方法
既可用于操作List集合,也可用于操作Queue集合和Set集合。

常用的方法:

boolean?add(E?e);?向集合中添加对象
boolean?addAll(Collection<??extends?E>?c)用于将参数指定集合c中的所有元素添加到当前集合中
boolean?contains(Object?o);?判断是否包含指定对象,原理是Objects.equals(o,?e),这句话相当于1.判断null,2.判断自身,3.该类型的方法e.equals(o)
boolean?containsAll(Collection<?>?c)?判断是否包含参数指定的所有对象
boolean?retainAll(Collection<?>?c)?保留当前集合中存在且参数集合中存在的所有对象,交集。如果此集合因调用而更改就返回true。取代原集合。
boolean?remove(Object?o);?从集合中删除对象,成功返回true。多个只删除一个,原理是删除元素e?,使其为Objects.equals(o,?e)
boolean?removeAll(Collection<?>?c)?从集合中删除参数指定的所有对象,改变了就返回true
void?clear();?清空集合
int?size();?返回包含对象的个数
boolean?isEmpty();?判断是否为空,底层是判断size==0
boolean?equals(Object?o)?判断是否相等
int?hashCode()?获取当前集合的哈希码值
Object[]?toArray()?将集合转换为数组,Arrays.asList(object数组)可以逆转
Iterator?iterator()?获取当前集合的迭代器

Iterator接口:

基本概念:

java.util.Iterator接口主要用于描述迭代器对象,可以遍历Collection集合中的所有元素。
java.util.Collection接口继承Iterator接口,因此所有实现Collection接口的实现类都可以使用该迭代器对象。

常用的方法:

boolean?hasNext()?判断集合中是否有可以迭代/访问的元素,
E?next()?用于取出一个元素并指向下一个元素,位置会变化,不能多次遍历,可以再次调用iterator方法
void?remove()?用于删除最后一个访问到的元素,这样可以防止超删。collection.remove(next获取到的obj)方法会发生并发修改异常。

应用:

官方的toString方法就是迭代拼接print

for each循环:

基本概念:

Java5推出了增强型for循环语句,可以应用数组和集合的遍历。是经典迭代的“简化版”。

底层原理:

迭代器

语法格式:

for(元素类型?变量名?:?数组/集合名称)?{
????循环体;
}????

执行流程:

不断地从数组/集合中取出一个元素赋值给变量名并执行循环体,直到取完所有元素为止。

List集合:

基本概念:

java.util.List集合是Collection集合的子集合,该集合中允许有重复的元素并且有先后放入次序。
该集合的主要实现类有:ArrayList类、LinkedList类、Stack类、Vector类。
ArrayList类的底层是采用动态数组进行数据管理的,支持下标访问,增删元素不方便。
LinkedList类的底层是采用双向链表进行数据管理的,访问不方便(内存不连续),增删元素方便。
????可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定的差别,ArrayList更适合于随机访问而LinkedList更适合于插入和删除;在性能要求不是特别苛刻的情形下可以忽略这个差别。
Stack类的底层是采用动态数组进行数据管理的,该类主要用于描述一种具有后进先出特征的数据结构,叫做栈(last?in?first?out?LIFO)。
Vector类的底层是采用动态数组进行数据管理的,该类与ArrayList类相比属于线程安全的类,效率比较低,以后开发中基本不用。扩容一般为2倍
????类似StringBuilder和StringBuffer的区别。

ArrayList类:

概念:
底层是数组,但可以动态修改(有个临时变量)

遍历中删除:
1.使用for?index++的方式,list.remove(e)会更新索引下标。
????解决:index要不增。或者倒序遍历。
2.使用高级for循环(增强for循环)遍历删除/增加操作
????for?(type?e?:?es?){
????????//?如果break不会报错
????}???????????????????}
????可能会报ConcurrentModificationException异常。

源码分析扩容:

1.声明new ArrayList();构造方法里

this.elementData?=?DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
其中transient?Object[]?elementData;?
????private?static?final?Object[]?DEFAULTCAPACITY_EMPTY_ELEMENTDATA?=?{};?是一个长度为0的数组

2.add方法添加元素时,

首先判断长度是否满了s?==?elementData.length。
满了的情况下发生扩容:
????判断是否是DEFAULTCAPACITY_EMPTY_ELEMENTDATA,如果是,先申请大小为10的object数组
????否则,取1.5倍*旧容量或旧容量+1的最大值。//?addAll的情况下就不是1了,而是参数集合的size。

LinkedList类:

常用方法:
void?addFirst(E?e)
void?addLast(E?e)
E?removeFirst()
E?removeLast()

源码分析add过程:

1.new时声明first和last变量

transient?Node<E>?first;

2.add方法会判断修改first和last,将旧的node.next指向新的last

常用的方法
????void?add(int?index,?E?element)?向集合中指定位置添加元素,与collect不同。
????boolean?addAll(int?index,?Collection<??extends?E>?c)?向集合中添加所有元素
????E?get(int?index)?从集合中获取指定位置元素,返回类型为object,需要强转(但可能不是父子类型,发生类型转换异常)
????int?indexOf(Object?o)?查找参数指定的对象
????int?lastIndexOf(Object?o)?反向查找参数指定的对象
????E?set(int?index,?E?element)?修改指定位置的元素,返回原有元素
????E?remove(int?index)?删除指定位置的元素。注意for循环,size每次会减小(倒着删也可以);元素自动填充,后面的元素会补位。
????????注意,本身还有remove(Object?e)的方法,重载了。
????List?subList(int?fromIndex,?int?toIndex)?用于获取子List,共享内存空间。

Stack类:

常用方法:
boolean?empty()?测试此堆栈是否为空。??
E?peek()?查看此堆栈顶部的对象,而不将其从堆栈中删除。??
E?pop()?移除此堆栈顶部的对象,并将该对象作为此函数的值返回。??
E?push?(E?item)?将项目推到此堆栈的顶部。??
int?search?(Object?o)?返回对象在此堆栈上的从1开始的位置。?

Queue集合:

基本概念:

java.util.Queue集合是Collection集合的子集合,与List集合属于平级关系。
该集合的主要用于描述具有先进先出特征的数据结构,叫做队列(first?in?first?out?FIFO)。
该集合的主要实现类是LinkedList类,因为该类在增删方面比较有优势。

常用的方法:

boolean?offer(E?e)?将一个对象添加至队尾,若添加成功则返回true
E?poll()?从队首删除并返回一个元素
E?peek()?返回队首的元素(但并不删除)

Set集合:

基本概念:

java.util.Set集合是Collection集合的子集合,与List集合平级。
该集合中元素没有先后放入次序,且不允许重复。
该集合的主要实现类是:HashSet类?和?TreeSet类以及LinkedHashSet类。
其中HashSet类的底层是采用哈希表进行数据管理的。
其中TreeSet类的底层是采用红黑树进行数据管理的。
其中LinkedHashSet类与HashSet类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代。

常用的方法:

参考Collection集合中的方法即可!

元素放入HashSet集合的原理:

使用元素调用hashCode方法获取对应的哈希码值,再由某种哈希算法计算出该元素在数组中的索引位置。
若该位置没有元素,则将该元素直接放入即可。
若该位置有元素,则使用新元素与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放入。
????若新元素与已有元素的哈希值相同,则使用新元素调用equals方法与已有元素依次比较。
????若相等则添加元素失败,否则将元素直接放入即可。

TreeSet集合:

概念:
由于TreeSet集合的底层采用红黑树进行数据的管理,当有新元素插入到TreeSet集合时,需要使用新元素与集合中已有的元素依次比较来确定新元素的合理位置。
元素默认从小到大排序了。
比较元素大小的规则有两种方式:
????使用元素的自然(自身)排序规则进行比较并排序,让元素类型实现java.lang.Comparable接口;(至少实现这个,不然报错ClassCastException)
????使用比较器规则进行比较并排序,构造TreeSet集合时传入java.util.Comparator接口;
自然排序的规则比较单一,而比较器的规则比较多元化,而且比较器优先于自然排序;

自然排序:
负数表示新对象小于参数对象。0表示不放入。
implements?Comparable<Person>{}

@Override
public?int?compareTo(Person?o)?{
????return?this.getName().compareTo(o.getName());
}

比较器规则:
实现接口,选择匿名类(lambda表达式也可以)、实现接口的子类都可以。负数表示o1小于o2
Comparator<Person>?comparator?=?new?Comparator<Person>()?{
????@Override
????public?int?compare(Person?o1,?Person?o2)?{
????????return?0;
????}
};????

Map集合:

基本概念:

java.util.Map<k,v>集合中存取元素的基本单位是:单对元素,其中类型参数如下:
K?-?此映射所维护的键(Key)的类型,相当于目录。
V?-?映射值(Value)的类型,相当于内容。
该集合中key是不允许重复的,而且一个key只能对应一个value。
该集合的主要实现类有:HashMap类、TreeMap类、LinkedHashMap类、Hashtable类、Properties类。
其中HashMap类的底层是采用哈希表进行数据管理的。
其中TreeMap类的底层是采用红黑树进行数据管理的。
其中LinkedHashMap类与HashMap类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代。
其中Hashtable类是古老的Map实现类,与HashMap类相比属于线程安全的类,且不允许null作为key或者value的数值。
其中Properties类是Hashtable类的子类,该对象用于处理属性文件,key和value都是String类型的。
Map集合是面向查询优化的数据结构,?在大数据量情况下有着优良的查询性能。经常用于根据key检索value的业务场景。

常用的方法:

V?put(K?key,?V?value)将Key-Value对存入Map,若集合中已经包含该Key,则替换该Key所对应的Value,返回值为该Key原来所对应的Value,若没有则返回null
V?get(Object?key)?返回与参数Key所对应的Value对象,如果不存在则返回null
boolean?containsKey(Object?key);判断集合中是否包含指定的Key
boolean?containsValue(Object?value);判断集合中是否包含指定的Value
V?remove(Object?key)?根据参数指定的key进行删除
Set?keySet()?返回此映射中包含的键的Set视图
Collection?values()?返回此映射中包含的值的Set视图
Set<Map.Entry<K,V>>?entrySet()?返回此映射中包含的映射的Set视图,每个元素是Map.Entry<K,V>

HashMap集合:

java7和java8的变化:
链表数组?->?链表超过一定长度后变为红黑树????TREEIFY_THRESHOLD

源码分析:
1.new声明,只是this.loadFactor?=?DEFAULT_LOAD_FACTOR。
2.put过程:
????先hash键,通过(h?=?key.hashCode())?^?(h?>>>?16)和(n?-?1)?&?hash得到数组的索引位置i
????判断数组容量是否为0或者null,发生扩容。
????根据索引位置i找到元素:
????????若该位置没有元素,则将该键值对直接放入即可。然后++modCount,超过thr时调用resize。
????????若该位置有元素p,则使用key与已有元素依次比较key的哈希值
????????????若p.key调用equals方法与p相同,则将该元素的value直接赋值给该Node。
????????????若元素是TreeNode,调用putTreeVal方法
????????????若key不相同
????????????????判断p.next是否为空,如果为空直接放到next,然后分析是否超过TREEIFY_THRESHOLD(默认是8),再决定是否建树treeifyBin
????????????????????????????????????????????????????(里面会先判断链表长度是否超过MIN_TREEIFY_CAPACITY,64,如果没超过就resize)。
????????????????判断当前链表的Node的key是否相等,相等就更新值。
3.扩容过程:
????一开始容量和thr都为0,赋值为DEFAULT_INITIAL_CAPACITY=16,thr为16*0.75=12。初始化一个数组Node<K,V>[]?newTab?=?(Node<K,V>[])new?Node[newCap];
????容量和thr不为0的情况下,newCap?=?oldCap?<<?1和newThr?=?oldThr?<<?1都翻倍了。
????????遍历旧数组得到e,先置空旧位置
????????????看e是否有next,没有通过e.hash?&?(newCap?-?1)放到新位置
????????????如果是TreeNode,((TreeNode<K,V>)e).split(this,?newTab,?j,?oldCap)
????????????否则,e有next,桶的情况,顺序放到j或者j?+?oldCap的位置
????????一次转移完旧数据。

遍历:
EntrySet,只需要取一次。

缺点:
并发情况下扩容会出现死循环,因为要遍历链表,将next指向下一个。并发情况下有可能指向自身。1.8修复了。

ConcurrentHashMap:
1.7采用了分段锁技术,其中?Segment?继承于?ReentrantLock。能支持?N?个?Segment?这么多次数的并发.
1.8使用????如果为空CAS?+?不然synchronized。因为?jdk对?synchronized?优化是很到位的

Collections类:

基本概念:

java.util.Collections类主要提供了对集合操作或者返回集合的静态方法。

常用的方法:

static?<T?extends?Object?&?Comparable<??super?T>>?T
max(Collection<??extends?T>?coll)根据元素的自然顺序返回给定集合的最大元素
static?T?max(Collection<??extends?T>?coll,?Comparator<??super?T>?comp)根据指定比较器引发的顺序返回给定集合的最大元素
static?<T?extends?Object?&?Comparable<?super?T>>?T?min(Collection<??extends?T>?coll)根据元素的自然顺序返回给定集合的最小元素
static?T?min(Collection<??extends?T>?coll,?Comparator<??super?T>?comp)根据指定比较器引发的顺序返回给定集合的最小元素
static?void?copy(List<??super?T>?dest,?List<??extends?T>?src)将一个列表中的所有元素复制到另一个列表中,需要dest的size大于src的。
static?void?reverse(List<?>?list)?反转指定列表中元素的顺序
static?void?shuffle(List<?>?list)?使用默认的随机源随机置换指定的列表
static?<T?extends?Comparable<??super?T>>?void?sort(List?list)根据其元素的自然顺序将指定列表按升序排序
static?void?sort(List?list,?Comparator<??super?T>?c)根据指定比较器指定的顺序对指定列表进行排序
static?void?swap(List<?>?list,?int?i,?int?j)?交换指定列表中指定位置的元素

泛型机制:

基本概念:

只能引用类型
通常情况下集合中可以存放不同类型的对象,是因为将所有对象都看做Object类型放入的,因此从集合中取出元素时也是Object类型,
????为了表达该元素真实的数据类型,则需要强制类型转换,而强制类型转换可能会引发类型转换异常。
为了避免上述错误的发生,从Java5开始增加泛型机制,也就是在集合名称的右侧使用<数据类型>的方式来明确要求该集合中可以存放的元素类型,
????若放入其它类型的元素则编译报错。泛型只在编译时期有效,在运行时期不区分是什么类型。

本质:

参数化类型。
泛型参数T只存在于编译时,在编译后会被擦除。
泛型类型在逻辑上看以看成是多个不同的类型,实际上运行时,都是相同的底层基本类型。
在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。

底层原理:

泛型的本质就是参数化类型,也就是让数据类型作为参数传递,其中E相当于形式参数负责占位,而使用集合时<>中的数据类型相当于实际参数,用于给形式参数E进行初始化,
从而使得集合中所有的E被实际参数替换,由于实际参数可以传递各种各样广泛的数据类型,因此得名为泛型。

示例:

List<String>?l?=?new?LinkedList<String>();????????
List<String>?l?=?new?LinkedList<>();????????????从java7开始的新特性:菱形特性,不用再写两遍

自定义泛型接口:

泛型接口和普通接口的区别就是后面添加了类型参数列表,可以有多个类型参数,如:<E,?T,?..?>等。????

自定义泛型类:

泛型类和普通类的区别就是类名后面添加了类型参数列表,可以有多个类型参数,如:<E,?T,?..?>等。
实例化时没有指定类型时,默认是Object类型。
实例化泛型类时应该指定具体的数据类型,并且是引用数据类型而不是基本数据类型(基本类型可以用包装类)。
继承:
????父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型。
????????如果不保留泛型和没有指定泛型,则父类的T为默认Object。
????????????A?extends?B
????????如果不保留泛型但指定了泛型,则父类的T为指定类型
????????????A?extends?B<String>
????????可以保留泛型
????????????A<T>?extends?B<T>???
????子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型。
????????????A<T,E>?extends?B<T>
示例:?
????public?class?Person<T>?{
????????private?T?gender;
????}

自定义泛型方法:

介绍:

泛型方法就是我们输入参数的时候,输入的是泛型参数,而不是具体的参数。我们在调用这个泛型方法的时需要对泛型参数进行实例化或者static。

泛型方法的格式:

[访问权限]?<泛型>?返回值类型?方法名([泛型标识?参数名称])?{?方法体;?}
在静态方法中使用泛型参数的时候,需要我们把静态方法定义为泛型方法。
用到类的泛型参数的方法不能static

泛型方法的实现:

注意泛型方法只能实现,不能重写(接口没有这个概念),但可以重载。
必须实现<T?extends?father>?void?xxx的方法。
重载可以<T?extends?son>?void?xxx(T?a),使用不用的泛型。(多态接口的变量不能使用重载的方法,因为接口没有定义,解决:泛型接口的继承,实现类可以指定具体子类)
触发:
????传入father对象时,触发第一个方法。
????传入son对象以及son的子类对象时,触发第二个方法,而且对象a的成员变量为son的值,而不是son的子类的值。
????传入otherSon对象时,仍然触发father的方法。

泛型方法的继承:

子类中可以对方法重写override,也可以重载overload。
重载示例:
????@Override
??????????????????<T?extends?father>?void?print(T?a)??//?重写,只能保持泛形一模一样。
????<T?extends?son>?void?print(T?a)?????//?重载,如果没有用到参数T,那么会提示错误,因为两个方法擦除泛型后都是同样的方法,both?methods?have?same?erasure
重载后触发:
????与普通方法类似,根据传参来确定。

泛型在继承上的体现:

如果B是A的一个子类或子接口,而G是具有泛型声明的类或接口,则G<B>并不是G<A>的子类型!
比如:String是Object的子类,但是List<String>并不是List<Object>的子类。但是是List的子类。

通配符的使用:

概述:

有时候我们希望传入的类型在一个指定的范围内,此时就可以使用泛型通配符了。
如:之前传入的类型要求为Integer类型,但是后来业务需要Integer的父类Number类也可以传入。

原理:

理解可以看泛型的本质

泛型中有三种通配符形式:

<?>?无限制通配符:表示我们可以传入任意类型的参数。????????
????作为函数参数的多态传递,允许多个具体泛型传进来。
????因为既没有extends,也没有super,所以:
????????不允许调用set(T)方法并传入引用(null除外);
????????不允许调用T?get()方法并获取T引用(只能获取Object引用)。
????????????不支持元素的添加操作。对比List不加泛型这个父类,可以添加元素,但相当于去掉了泛型。
????????????可以取元素,返回Object类型
????特点:
????????Pair<?>是所有Pair<T>的超类,可以安全地向上转型。

<??extends?E>?表示类型的上界是E,引用只能是E或者是E的子类。不支持添加操作(不确定具体子类型),支持获取,返回的是E类型
????List<??extends?Fruit>?flist?=?new?ArrayList<Apple>();
????Fruit?fruit?=?flist.get(0);
????用途:
????????用于遍历list,里面包含不同类型的对象,返回接收的类型都是E。
????????遍历的对象可以用于super存放

<??super?E>?表示类型的下界是E,引用只能是E或者是E的父类。可以添加父类最多到E的类型(因为这些类型可以向上强转),只能获取返回Object类型。
????List<??super?Fruit>?flist?=?new?ArrayList<Fruit>();
????flist.add(new?Fruit());
????flist.add(new?Apple());
????用途:
????????能够存放父类最多到E的不同类型的对象。

运行如何判断泛型参数E为某个类型:

如果入参类型不为E,目前没办法,可以先根据re的类型,用instanceof来判断
????//?通过反射获得泛型签名中的类型,貌似也只能获取继承于泛型类的当前类已确定的泛型
????interface?A<T,?ID>?{??
????}??
????class?B?implements?A<String,?Integer>?{??
????}?
如果输入参数类型为E,那么可以根据
????方法一:传目标的类型Type
????????public?<E>?E?selectOneValue(String?sql,Class<E>?myClass)?throws?SQLException?{}?????
????????int?rs?=?conn.selectOneValue("select?id?from?t_json",Integer.class)
????????然后就可以用myClass.isAssignableFrom(String.class)来判断?????//?myClass为class?java.lang.Integer
????????//也可以将myClass通过new存入类变量中
????????????public?class?MyGenericClass<T>?{
????????????????Class<T>?t;
????????????????public?static?<E>?MyGenericClass<E>?createMyGeneric(Class<E>?t){????//通过该方法返回的instance包含类变量t为输入类型
????????????????????return?new?MyGenericClass<E>(t);
????????????????}
????????????????public?MyGenericClass(Class<T>?t)?{
????????????????????this.t=t;
????????????????}
????????????????public?void?out()?{
????????????????????System.out.println(t);??????????//instance的其他方法即可使用变量t
????????????????}
????????????}
????方法二:传目标的类型变量
????????public?<E>?void?doSomething(E?a)
????????a?instanceof?Integer[]
????????a.getClass().isAssignableFrom(Integer[].class)

异常机制:

基本概念:

异常就是"不正常"的含义,在Java语言中主要指程序执行中发生的不正常情况。
java.lang.Throwable类是Java语言中错误(Error)和异常(Exception)的超类。
其中Error类主要用于描述Java虚拟机无法解决的严重错误,通常无法编码解决,如:JVM挂掉了等。
其中Exception类主要用于描述因编程错误或偶然外在因素导致的轻微错误,通常可以编码解决,如:0作为除数等。

异常的分类:

java.lang.Exception类是所有异常的超类(不能抛出),主要分为以下两种:

RuntimeException?-?运行时异常,也叫作非检测性异常,运行后才可能抛出
IOException和其它异常?-?其它异常,也叫作检测性异常,所谓检测性异常就是指在编译阶段都能被编译器检测出来的异常。

其中RuntimeException类的主要子类:

ArithmeticException类?-?算术异常
ArrayIndexOutOfBoundsException类?-?数组下标越界异常
NullPointerException?-?空指针异常
ClassCastException?-?类型转换异常
NumberFormatException?-?数字格式异常

注意:

当程序执行过程中发生异常但又没有手动处理时,则由Java虚拟机采用默认方式处理异常,而默认处理方式就是:打印异常的名称、异常发生的原因、异常发生的位置以及终止程序。

异常的捕获:

语法格式:

try?{
????编写可能发生异常的代码;
}
catch(异常类型?引用变量名)?{
????编写针对该类异常的处理代码;
}
...
finally?{
????编写无论是否发生异常都要执行的代码;如果catch发生异常,也会执行。try或者catch的模块return前需要执行finally!使得finally可能修改返回值。
}

注意:

当需要编写多个catch分支时,切记小类型应该放在大类型的前面;
懒人的写法:
????catch(Exception?e)?{}
finally通常用于进行善后处理,如:关闭已经打开的文件等。????

异常的抛出:

基本概念:

在某些特殊情况下有些异常不能处理或者不便于处理时,就可以将该异常转移给该方法的调用者,这种方法就叫异常的抛出。
当方法执行时出现异常,则底层生成一个异常类对象抛出,此时异常代码后续的代码就不再执行。

语法格式:

访问权限?返回值类型?方法名称(形参列表)?throws?异常类型1,异常类型2,...{?方法体;?}
如:
????public?void?show()?throws?IOException?{}????

方法继承:

子类重写的方法不能抛出更大的异常、不能抛出平级不一样的异常,但可以抛出一样的异常、更小的异常以及不抛出异常。

规范:

若父类中被重写的方法没有抛出异常时,则子类中重写的方法只能进行异常的捕获处理。
若一个方法内部又以递进方式分别调用了好几个其它方法,则建议这些方法内可以使用抛出的方法处理到最后一层进行捕获方式处理。

自定义异常:

基本概念:

当需要在程序中表达年龄不合理的情况时,而Java官方又没有提供这种针对性的异常,此时就需要程序员自定义异常加以描述。

实现流程:

a.自定义xxxException异常类继承Exception类或者其子类。
b.提供两个版本的构造方法,一个是无参构造方法,另外一个是字符串作为参数的构造方法(字符串用于Exception父类)。

异常的产生:

throw?new?异常类型(实参);
如:
????throw?new?AgeException("年龄不合理!!!");

异常的处理:

一个是向上throws,一个是就地处理(适合继承重写的方法,它的父类没有抛出异常)。

规范:

Java采用的异常处理机制是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。

File类:

基本概念:

java.io.File类主要用于描述文件或目录路径的抽象表示信息,可以获取文件或目录的特征信息,如:大小等。

常用的方法:

File(String?pathname)?根据参数指定的路径名来构造对象
File(String?parent,?String?child)?根据参数指定的父路径和子路径信息构造对象
File(File?parent,?String?child)?根据参数指定的父抽象路径和子路径信息构造对象
boolean?exists()?测试此抽象路径名表示的文件或目录是否存在
String?getName()?用于获取文件的名称
long?length()?返回由此抽象路径名表示的文件的长度
long?lastModified()?用于获取文件的最后一次修改时间,毫秒
String?getAbsolutePath()?用于获取绝对路径信息
boolean?delete()?用于删除文件,当删除目录时要求是空目录,不然返回true但还是删除失败
boolean?createNewFile()?用于创建新的空文件
boolean?mkdir()?用于创建目录
boolean?mkdirs()?用于创建多级目录
File[]?listFiles()?获取该目录下的所有内容,单层
boolean?isFile()?判断是否为文件
boolean?isDirectory()?判断是否为目录
File[]?listFiles(FileFilter?filter)?获取目录下满足筛选器的所有内容。new?一个匿名类,然后实现accept方法即可。

IO流:

概念:

IO就是Input和Output的简写,也就是输入和输出的含义。
IO流就是指读写数据时像流水一样从一端流到另外一端,因此得名为“流"。????????

基本分类:

按照读写数据的基本单位不同,分为?字节流?和?字符流。
????其中字节流主要指以字节为单位进行数据读写的流,可以读写任意类型的文件。
????其中字符流主要指以字符(2个字节)为单位进行数据读写的流,只能读写文本文件。
按照读写数据的方向不同,分为?输入流?和?输出流(站在程序的角度)。
????其中输入流主要指从文件中读取数据内容输入到程序中,也就是读文件。
????其中输出流主要指将程序中的数据内容输出到文件中,也就是写文件。
按照流的角色不同分为节点流和处理流。
????其中节点流主要指直接和输入输出源对接的流。
????其中处理流主要指需要建立在节点流的基础之上的流。????

体系结构:

???????????????????IO流?
?????????????字节流?????????????????????????????????????????字符流?
??InputStream????????????????OutputStream???????????????????????Reader????Writer???????????????-->??????抽象类
FileInputStream????????????FileOutputStream????????????????FileReader?????????FileWriter
BufferedInputStream?????BufferedOutputStream???????????BufferedReader?????BufferedWriter?
DataInputStream?????????DataOutputStream??????????????InputStreamReader??OutputStreamReader?????????
ObjectInputStream????????ObjectOutputStream??????????????????????????????????PrintWriter?????????????
???????????????????????????PrintStream????????????????????????

FileWriter类:

基本概念:

java.io.FileWriter类主要用于将文本内容写入到文本文件。

常用的方法:

FileWriter(String?fileName)?根据参数指定的文件名构造对象,文件不存在会新建文件。会清空原内容。
FileWriter(String?fileName,?boolean?append)以追加的方式根据参数指定的文件名来构造对象。文件不存在会新建文件。
void?write(int?c)?写入单个字符
void?write(char[]?cbuf,?int?off,?int?len)将指定字符数组中从数组偏移量off开始的len个字符写入此文件输出流
void?write(char[]?cbuf)将cbuf.length个字符从指定字符数组写入此文件输出流中
void?flush()?刷新流
void?close()?关闭流对象并释放有关的资源

FileReader类:

基本概念:

java.io.FileReader类主要用于从文本文件读取文本数据内容。

常用的方法:

FileReader(String?fileName)根据参数指定的文件名构造对象
int?read()?读取单个字符的数据并返回,返回-1表示读取到末尾
int?read(char[]?cbuf,?int?offset,?int?length)从输入流中将最多len个字符的数据读入一个字符数组下标offset开始的位置中,返回读取到的字符个数,返回-1表示读取到末尾
int?read(char[]?cbuf)从此输入流中将最多?cbuf.length?个字符的数据读入字符数组中,返回读取到的字符个数,返回-1表示读取到末尾
void?close()?关闭流对象并释放有关的资源

FileOutputStream类:

基本概念:

java.io.FileOutputStream类主要用于将图像数据之类的原始字节流写入到输出流中。

常用的方法:

FileOutputStream(String?name)?根据参数指定的文件名来构造对象
FileOutputStream(String?name,boolean?append)以追加的方式根据参数指定的文件名来构造对象
void?write(int?b)?将指定字节写入此文件输出流
void?write(byte[]?b,?int?off,?int?len)将指定字节数组中从偏移量off开始的len个字节写入此文件输出流
void?write(byte[]?b)将?b.length?个字节从指定字节数组写入此文件输出流中
void?flush()?刷新此输出流并强制写出任何缓冲的输出字节
void?close()?关闭流对象并释放有关的资源

FileInputStream类(重点):

基本概念:

java.io.FileInputStream类主要用于从输入流中以字节流的方式读取图像数据等。

常用的方法:

FileInputStream(String?name)根据参数指定的文件路径名来构造对象
int?read()?从输入流中读取单个字节的数据并返回,返回-1表示读取到末尾
int?read(byte[]?b,?int?off,?int?len)从此输入流中将最多len个字节的数据读入字节数组中,返回读取到的字节个数,返回-1表示读取到末尾
int?read(byte[]?b)从此输入流中将最多?b.length?个字节的数据读入字节数组中,返回读取到的字节个数,返回-1表示读取到末尾
void?close()?关闭流对象并释放有关的资源
int?available()?获取输入流所关联文件的大小

写文件:

write(int?b)太慢,write(byte[]?b)场景不通用,write(arr,0,res=fis.read(barr))适合将读出的长度写入。

BufferedOutputStream类(重点):

基本概念:

java.io.BufferedOutputStream类主要用于描述缓冲输出流,此时不用为写入的每个字节调用底层系统(减小io)。
处理流。
多了一层缓冲区,自己可以write单个字节(仍然慢),或者字节数组write(快很多)都可以。

常用的方法:

BufferedOutputStream(OutputStream?out)?根据参数指定的引用来构造对象,默认8192
BufferedOutputStream(OutputStream?out,?int?size)根据参数指定的引用和缓冲区大小来构造对象
void?write(int?b)?写入单个字节
void?write(byte[]?b,?int?off,?int?len)?写入字节数组中的一部分数据
void?write(byte[]?b)?写入参数指定的整个字节数组
void?flush()?刷新流
void?close()?关闭流对象并释放有关的资源

BufferedInputStream类(重点):

基本概念:

java.io.BufferedInputStream类主要用于描述缓冲输入流。

常用的方法:

BufferedInputStream(InputStream?in)?根据参数指定的引用构造对象,默认8192
BufferedInputStream(InputStream?in,?int?size)?根据参数指定的引用和缓冲区大小构造对象
int?read()?读取单个字节
int?read(byte[]?b,?int?off,?int?len)?读取len个字节到数组中
int?read(byte[]?b)?读取b.length个字节
void?close()?关闭流对象并释放有关的资源

BufferedWriter类(重点):

基本概念:

java.io.BufferedWriter类主要用于写入单个字符、字符数组以及字符串到输出流中。

常用的方法:

BufferedWriter(Writer?out)?根据参数指定的引用来构造对象
BufferedWriter(Writer?out,?int?sz)?根据参数指定的引用和缓冲区大小来构造对象
void?write(int?c)?写入单个字符到输出流中
void?write(char[]?cbuf,?int?off,?int?len)将字符数组cbuf中从下标off开始的len个字符写入输出流中
void?write(char[]?cbuf)?将字符串数组cbuf中所有内容写入输出流中
void?write(String?s,?int?off,?int?len)?将参数s中下标从off开始的len个字符写入输出流中
void?write(String?str)?将参数指定的字符串内容写入输出流中
void?newLine()?用于写入行分隔符到输出流中
void?flush()?刷新流
void?close()?关闭流对象并释放有关的资源

BufferedReader类(重点):

基本概念:

java.io.BufferedReader类用于从输入流中读取单个字符、字符数组以及字符串。

常用的方法:

BufferedReader(Reader?in)根据参数指定的引用来构造对象。比如new?BufferedReader(new?InputStreamReader(System.in))输入。
BufferedReader(Reader?in,?int?sz)根据参数指定的引用和缓冲区大小来构造对象
int?read()从输入流读取单个字符,读取到末尾则返回-1,否则返回实际读取到的字符内容
int?read(char[]?cbuf,?int?off,?int?len)从输入流中读取len个字符放入数组cbuf中下标从off开始的位置上,若读取到末尾则返回-1,否则返回实际读取到的字符个数
int?read(char[]?cbuf)?从输入流中读满整个数组cbuf
String?readLine()?读取一行字符串并返回,返回null表示读取到末尾
void?close()?关闭流对象并释放有关的资源

示例:

new?BufferedReader(new?InputStreamReader(new?FileInputStream(filename),"iso-8859-1"));
new?BufferedReader(new?InputStreamReader(System.in))输入。
new?BufferedReader(new?FileReader(file.getName()))

PrintStream类:

基本概念:

java.io.PrintStream类主要用于更加方便地打印各种数据内容。

常用的方法:

PrintStream(OutputStream?out)?根据参数指定的引用来构造对象
void?print(String?s)?用于将参数指定的字符串内容打印出来
void?println(String?x)?用于打印字符串后并终止该行
void?flush()?刷新流
void?close()?用于关闭输出流并释放有关的资源

PrintWriter类:

基本概念:

java.io.PrintWriter类主要用于将对象的格式化形式打印到文本输出流。

常用的方法:

PrintWriter(Writer?out)?根据参数指定的引用来构造对象
void?print(String?s)?将参数指定的字符串内容打印出来
void?println(String?x)?打印字符串后并终止该行
void?flush()?刷新流
void?close()?关闭流对象并释放有关的资源

OutputStreamWriter类:

基本概念:

java.io.OutputStreamWriter类主要用于实现从字节流到字符流的转换。

常用的方法:

OutputStreamWriter(OutputStream?out)?根据参数指定的引用来构造对象
OutputStreamWriter(OutputStream?out,?String?charsetName)根据参数指定的引用和编码构造对象
void?write(String?str)?将参数指定的字符串写入
void?flush()?刷新流
void?close()用于关闭输出流并释放有关的资源

InputStreamReader类:

基本概念:

java.io.InputStreamReader类主要用于实现从字节流到字符流的转换。

常用的方法:

InputStreamReader(InputStream?in)?根据参数指定的引用来构造对象
InputStreamReader(InputStream?in,?String?charsetName)根据参数指定的引用和编码来构造对象
int?read(char[]?cbuf)?读取字符数据到参数指定的数组
void?close()?用于关闭输出流并释放有关的资源

字符编码:

编码表的由来:

计算机只能识别二进制数据,早期就是电信号。为了方便计算机可以识别各个国家的文字,就需要
将各个国家的文字采用数字编号的方式进行描述并建立对应的关系表,该表就叫做编码表。

常见的编码表:

ASCII:美国标准信息交换码,?使用一个字节的低7位二位进制进行表示。
ISO8859-1:拉丁码表,欧洲码表,使用一个字节的8位二进制进行表示。
GB2312:中国的中文编码表,最多使用两个字节16位二进制为进行表示。
GBK:中国的中文编码表升级,融合了更多的中文文字符号,最多使用两个字节16位二进制位表示。(最高位1表示两个字节,0表示一个字节)
Unicode:国际标准码,融合了目前人类使用的所有字符,为每个字符分配唯一的字符码。所有的文字都用两个字节16位二进制位来表示。

编码的发展:

面向传输的众多?UTF(UCS?Transfer?Format)标准出现了,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。这是为传输而设计的编码并使编码无国界,这样就可以显示全世界上所有文化的字符了。
Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流,取决于字符编码方案。推荐的Unicode编码是UTF-8和UTF-16。
UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。(0表示一个字节,110表示两个,1110表示三个)

DataOutputStream类:

基本概念:

java.io.DataOutputStream类主要用于以适当的方式将8种基本数据类型写入输出流中。

常用的方法:

DataOutputStream(OutputStream?out)根据参数指定的引用构造对象?OutputStream类是个抽象类,实参需要传递子类对象
void?writeInt(int?v)用于将参数指定的整数一次性写入输出流,优先写入高字节,4个字节全写入,int写入后转换为char
void?write(int?v)两者不同,只写入一个字节。
void?close()?用于关闭文件输出流并释放有关的资源

DataInputStream类:

基本概念:

java.io.DataInputStream类主要用于从输入流中读取基本数据类型的数据

常用的方法:

DataInputStream(InputStream?in)根据参数指定的引用来构造对象?InputStream类是抽象类,实参需要传递子类对象
int?readInt()?用于从输入流中一次性读取一个整数数据并返回,没有或者不对的话返回EOFException。
void?close()?用于关闭文件输出流并释放有关的资源

ObjectOutputStream类(重点):

基本概念:

java.io.ObjectOutputStream类主要用于将一个对象的所有内容整体写入到输出流中。
只能将支持?java.io.Serializable?接口的对象写入流中。
类通过实现?java.io.Serializable?接口以启用其序列化功能。
所谓序列化主要指将一个对象需要存储的相关信息有效组织成字节序列的转化过程。

常用的方法:

ObjectOutputStream(OutputStream?out)?根据参数指定的引用来构造对象
void?writeObject(Object?obj)?用于将参数指定的对象整体写入到输出流中
void?close()?用于关闭输出流并释放有关的资源

示例:

public?class?User?implements?java.io.Serializable?{
????@Serial
????private?static?final?long?serialVersionUID?=?-6302068608321621724L;
????private?String?name;
}

ObjectInputStream类(重点):

基本概念:

java.io.ObjectInputStream类主要用于从输入流中一次性将对象整体读取出来。
所谓反序列化主要指将有效组织的字节序列恢复为一个对象及相关信息的转化过程。

常用的方法:

ObjectInputStream(InputStream?in)根据参数指定的引用来构造对象
Object?readObject()主要用于从输入流中读取一个对象并返回?无法通过返回值来判断是否读取到文件的末尾
void?close()?用于关闭输入流并释放有关的资源

序列化版本号:

序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,
如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。

transient关键字:

transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。

经验的分享:

当希望将多个对象写入文件时,通常建议将多个对象放入一个集合中,然后将集合这个整体看做一个对象写入输出流中,此时只需要调用一次readObject方法就可以将整个集合的数据读取出来,
从而避免了通过返回值进行是否达到文件末尾的判断。

RandomAccessFile类:

基本概念:

java.io.RandomAccessFile类主要支持对随机访问文件的读写操作。

常用的方法:

RandomAccessFile(String?name,?String?mode)根据参数指定的名称和模式构造对象
????r:?以只读方式打开
????rw:打开以便读取和写入
????rwd:打开以便读取和写入,同步文件内容的更新
????rws:打开以便读取和写入,同步文件内容和元数据的更新
int?read()?读取单个字节的数据
void?seek(long?pos)用于设置从此文件的开头开始测量的文件指针偏移量,字节单位
void?write(int?b)?将参数指定的单个字节写入,如果存在会覆盖。
void?close()?用于关闭流并释放有关的资源

多线程:

线程的创建:

Thread类的概念:

java.lang.Thread类代表线程,任何线程对象都是Thread类(子类)的实例。
Thread类是线程的模板,封装了复杂的线程开启等操作,封装了操作系统的差异性。

创建方式:

自定义类继承Thread类并重写run方法,然后创建该类的对象调用start方法(run方法调用相当于普通成员方法调用,start才是新线程)。
????继承Thread类的方式代码简单,但是若该类继承Thread类后则无法继承其它类。
????匿名内部类的方式
自定义类实现Runnable接口并重写run方法,创建该类的对象作为实参来构造Thread类型的对象,然后使用Thread类型的对象调用start方法(target执行run方法)。
????Runnable接口的方式代码复杂,但不影响该类继承其它类以及实现其它接口。
????匿名内部类的方式
????????new?Thread(()->{
????????????System.out.println();
????????}).start();
实现Callable接口:
????从Java5开始新增加创建线程的第三种方式为实现java.util.concurrent.Callable接口。
????常用的方法
????????V?call()?计算结果并返回
????FutureTask类
????????概念:
????????????java.util.concurrent.FutureTask类用于描述可取消的异步计算,该类提供了Future接口的基本实现,包括启动和取消计算、查询计算是否完成以及检索计算结果的方法,
????????????也可以用于获取方法调用后的返回结果。
????????常用的方法
????????????FutureTask(Callable?callable)?根据参数指定的引用来创建一个未来任务
????????????V?get()?获取call方法计算的结果
????????使用:?
????????????FutureTask实现了Runnable接口,用Thread封装一下,然后start()

相关的方法:

Thread()?使用无参的方式构造对象,成员变量target为null,run方法会判断跳过执行。所以需要继承并重写run方法自定义功能。
Thread(String?name)?根据参数指定的名称来构造对象
Thread(Runnable?target)根据参数指定的引用来构造对象,其中Runnable是个接口类型
Thread(Runnable?target,String?name)根据参数指定引用和名称来构造对象
void?run()若使用Runnable引用构造了线程对象,调用该方法时最终调用接口中的版本,若没有使用Runnable引用构造线程对象,调用该方法时则啥也不做
void?start()?用于启动线程,Java虚拟机会自动调用该线程的run方法

线程的编号和名称:

long?getId()?获取调用对象所表示线程的编号
String?getName()?获取调用对象所表示线程的名称
void?setName(String?name)?设置/修改线程的名称为参数指定的数值
static?Thread?currentThread()?获取当前正在执行线程的引用,可以用于main方法和接口,获取当前主线程。

常用的方法:

static?void?yield()当前线程让出处理器(离开Running状态),使当前线程进入Runnable状态等待
static?void?sleep(times)使当前线程从?Running?放弃处理器进入Block状态,?休眠times毫秒,?再返回到Runnable。如果其他线程打断当前线程的Block(sleep),?就会发生InterruptedException。
void?stop()停止线程,该方法已过时。
int?getPriority()?获取线程的优先级
void?setPriority(int?newPriority)修改线程的优先级。优先级越高(数字越大)的线程不一定先执行,但该线程获取到时间片的机会会更多一些
void?join()?主线程等待该线程start终止。默认主线程不会等待子线程start()执行完才执行下一步。
void?join(long?millis)?等待参数指定的毫秒数
boolean?isDaemon()?用于判断是否为守护线程,默认不是
void?setDaemon(boolean?on)用于设置线程为守护线程

线程同步机制:

基本概念:

当多个线程同时访问同一种共享资源时,可能会造成数据的覆盖等不一致性问题,此时就需要对线程之间进行通信和协调,该机制就叫做线程的同步机制。
多个线程并发读写同一个临界资源时会发生线程并发安全问题。
异步操作:多线程并发的操作,各自独立运行。
同步操作:多线程串行的操作,先后执行的顺序。????

synchronized加锁:

概述:
在Java语言中使用synchronized关键字来实现同步/对象锁机制从而保证线程执行的原子性,具体

同步代码块的方式:

格式如下:

synchronized(类类型的引用)?{
????编写所有需要锁定的代码;
}
其中类类型的引用要求唯一,一般创建一个class?demo{},然后实例化放到成员变量,传递给synchronized。

注意:

对于继承Thread,如果实例化了两个子类对象,那么锁的只是本对象,因为成员变量独立,解决是加static关键字。
对于实现接口Runnable,传给Thread的对象如果是一个,那么可以实现锁

同步方法的方式:
直接使用synchronized关键字来修饰整个方法即可,注意不同实例对象之间独立。
该方式等价于:
????synchronized(this)?{????????????//this是当前调用对象。要注意是否this一致。
????????整个方法体的代码?
????}
可以加static来锁类对象。对于重写方法比如run方法不方便加static,可以分离方法,然后调用传参。
????synchronized(类名.class)

静态方法的锁定:

1.当我们对一个静态方法加锁,如:

public?synchronized?static?void?xxx(){….}
那么该方法锁的对象是类对象。每个类都有唯一的一个类对象。获取类对象的方式:类名.class。
2.静态方法与非静态方法同时使用了synchronized后它们之间是非互斥关系的。原因在于:静态方法锁的是类对象而非静态方法锁的是当前方法所属对象。

注意事项:

1.使用synchronized保证线程同步应当注意:

多个需要同步的线程在访问同步块时,看到的应该是同一个锁对象引用。
2.在使用同步块时应当尽量减少同步范围以提高并发的执行效率。

底层实现:
Java虚拟机中的同步是通过显式(通过使用监视器输入和监视器输出指令)或隐式(通过方法调用和返回指令)的监视器输入和退出来实现的。?
显示就是使用monitorenter和monitorexit来控制同步代码块;隐式是修饰方法,在运行时常量池中通过ACC_SYNCHRONIZED来标志。

线程安全类和不安全类:

StringBuffer类是线程安全的类,但StringBuilder类不是线程安全的类。
Vector类和?Hashtable类是线程安全的类,但ArrayList类和HashMap类不是线程安全的类。(通过synchronized)
Collections.synchronizedList()?和?Collections.synchronizedMap()等方法实现安全。????

Lock(锁):

基本概念:
从Java5开始提供了更强大的线程同步机制—使用显式定义的同步锁对象来实现。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
该接口的主要实现类是ReentrantLock类,该类拥有与synchronized相同的并发性,在以后的线程安全控制中,经常使用ReentrantLock类显式加锁和释放锁。

常用的方法:
ReentrantLock()?使用无参方式构造对象
void?lock()?获取锁
void?unlock()?释放锁

使用方式:
lock.lock()????????????//?确保lock方法和try之间没有可能的异常发生
try?{
????...
}?finally?{
????lock.unlock()???//?这样unlock的时候不会对未加锁的对象解锁
}

底层原理:
底层是CAS乐观锁,依赖AbstractQueuedSynchronizer类,把所有的请求线程构成一个CLH队列。而对该队列的操作均通过Lock-Free(CAS)操作。功能更强大。

与synchronized方式的比较:
Lock是显式锁,需要手动实现开启和关闭操作,而synchronized是隐式锁,执行锁定代码后自动释放。
Lock只有同步代码块方式的锁,而synchronized有同步代码块方式和同步方法两种锁。
使用Lock锁方式时,Java虚拟机将花费较少的时间来调度线程,因此性能更好。

线程间通信:

Object类常用的方法:

void?wait()用于使得线程进入等待状态,直到其它线程调用notify()或notifyAll()方法。自动释放synchronized对象锁。
void?wait(long?timeout)用于进入等待状态,直到其它线程调用方法或参数指定的毫秒数已经过去为止
void?notify()?用于唤醒在此对象监视器上等待的单个线程。如果所有的线程都在此对象上等待,那么只会选择一个线程,选择是任意性的,并在对实现做出决定时发生。
????只能被作为此对象监视器的所有者的线程来调用。一次只能有一个线程拥有对象的监视器。
void?notifyAll()?用于唤醒等待的所有线程

线程操作的wait()、notify()、notifyAll()方法只能在同步控制方法或同步控制块内调用。
如果在非同步控制方法或控制块里调用,程序能通过编译,但运行的时候,将得到?IllegalMonitorStateException?异常。

轮询间隔执行:

synchronized?+?wait实现自动释放,这样另一方可以获取到锁。
synchronized?+?notify可以让另一方获取到锁后,唤醒自己。

场景:

生产者消费者模型,控制wait和notify的执行速率即可。

线程池:

概念:

线程池的概念:首先创建一些线程,它们的集合称为线程池,当服务器接受到一个客户请求后,就从线程池中取出一个空闲的线程为之服务,服务完后不关闭该线程,而是将该线程还回到线程池中。
解决了创建线程的消耗。

相关类和方法:

从Java5开始提供了线程池的相关类和接口:java.util.concurrent.Executors类和java.util.concurrent.ExecutorService接口。

Executors类:

概念:
工具类和线程池的工厂类,可以创建并返回不同类型的线程池,

常用方法:
static?ExecutorService?newCachedThreadPool()创建一个可根据需要创建新线程的线程池,可缓存线程池,此线程池不会对线程池大小做限制
????new?ThreadPoolExecutor(0,?Integer.MAX_VALUE,60L,?TimeUnit.SECONDS,new?SynchronousQueue<Runnable>(),threadFactory)无缓冲队列,线程缓存一段时间
static?ExecutorService?newFixedThreadPool(int?nThreads)创建一个可重用固定线程数的线程池,不会释放工作线程
????new?ThreadPoolExecutor(nThreads,?nThreads,0L,?TimeUnit.MILLISECONDS,new?LinkedBlockingQueue<Runnable>())
static?ExecutorService?newSingleThreadExecutor()?创建一个只有一个线程的线程池,不用每次重新创建
????new?ThreadPoolExecutor(1,?1,0L,?TimeUnit.MILLISECONDS,new?LinkedBlockingQueue<Runnable>(),threadFactory)

不成文强制规定:

Executors返回的线程池对象弊端如下:

FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
CacheThreadPool和ScheduledThreadPool:允许创建线程数量为Integer.MAX_VALUE,可能会创建大量线程,从而导致CPU占用满,OOM。
调用ThreadPoolExecutor的构造函数来自己根据业务场景创建线程池。
提交优先级:先往core线程提交,再往Array队列提交,最后往maxnum线程提交,非核心线程满了后reject
执行优先级:先执行core线程,再执行非核心线程,再执行队列的任务,代码(task?!=?null?||?(task?=?getTask())?!=?null)
作用:
????提交尽量避免创建非核心线程
????执行尽量先释放非核心线程

源码:
addWorker底层调用task.run()方法
worker执行完后再次调用addWorker进行复用

ExecutorService接口:

概念:
真正的线程池接口,主要实现类是ThreadPoolExecutor

构造方法:
ThreadPoolExecutor(int?corePoolSize,
??????????????????????????????int?maximumPoolSize,
??????????????????????????????long?keepAliveTime,
??????????????????????????????TimeUnit?unit,
??????????????????????????????BlockingQueue<Runnable>?workQueue,
??????????????????????????????ThreadFactory?threadFactory,
??????????????????????????????RejectedExecutionHandler?handler)

主要参数:
corePoolSize:核心线程数
????线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。
maximumPoolSize:最大线程数,包含非核心线程,非核心线程只会缓存一段时间。
keepAliveTime:非核心线程闲置超时时间
queue:任务队列,常见SynchronousQueue无缓冲等待队列,
????LinkedBlockingQueue无界缓存等待队列,当前执行的线程数量达到corePoolSize的数量时,剩余的元素会在阻塞队列里等待
????ArrayBlockingQueue有界缓存等待队列,防止资源耗尽,但是可能较难调整和控制。配合压测一般生产环境没有问题。
RejectedExecutionHandler当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。
????AbortPolicy:直接抛出异常。这个策略默认情况下是,表示无法处理新任务时抛出异常,可以捕获异常之后来进行相应的处理。
????CallerRunsPolicy:只用调用者所在线程来运行任务。
????DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
????DiscardPolicy:不处理,丢弃掉。
????也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务,存到数据库里,负载降低后启动线程取出到队列里。

示例:
ExecutorService?executorService?=?new?ThreadPoolExecutor(1,1,60L,TimeUnit.SECONDS,?new?ArrayBlockingQueue<Runnable>(2));

常用方法:
void?execute(Runnable?command)?执行任务和命令,通常用于执行Runnable。对于异常信息会直接抛出去。并且线程退出。
Future?submit(Callable?task或Runnable?task)?执行任务和命令,通常用于执行Callable,返回future对象,可以获取结果或者异常信息
void?shutdown()?启动有序关闭

异常的捕捉:

普通线程:

1.Thread.setDefaultUncaughtExceptionHandler,start启动后,可以捕捉到异常。
2.如果没有任何处理,会直接抛出并结束程序。

线程池:

知识点:
submit方法默认不显示异常
execute方法默认显示

如何捕捉:
1.线程内try catch包起来,输出信息
2.线程池execute?+?Thread.setDefaultUncaughtExceptionHandler可以捕捉到异常。
3.线程池submit?+?try?catch捕捉打印?+?future.get阻塞获取返回值,如果是异常则catch能捕捉到栈信息。
????注意submit?+?thread.setDefaultUncaughtExceptionHandler不能捕捉到异常,因为run方法将异常放到了?setException(ex),并赋值给future对象。
????get方法本身是阻塞的
????线程池需要shutdown()才会退出。

4.实现Thread.UncaughtExceptionHandler接口,实现void uncaughtException(Thread t, Throwable e);方法,并将该handler传递给线程池的ThreadFactory
Executors.newFixedThreadPool(8,factory);
5.类似的,继承ThreadGroup,覆盖其uncaughtException方法。

线程的状态:

分类:

NEW?新建?
Runnable可运行
Waiting无限等待
TimeWaiting计时等待
Blocked阻塞
Terminaled被终止

网络编程:

TCP:

编程模型:

服务器:
(1)创建ServerSocket类型的对象并提供端口号;
(2)等待客户端的连接请求,调用accept()方法;
(3)使用输入输出流进行通信;
(4)关闭Socket;

客户端:
(1)创建Socket类型的对象并提供服务器的IP地址和端口号;
(2)使用输入输出流进行通信;
(3)关闭Socket;

ServerSocket类:

概念:
java.net.ServerSocket类主要用于描述服务器套接字信息

常用方法:
ServerSocket(int?port)?根据参数指定的端口号来构造对象
Socket?accept()?侦听并接收到此套接字的连接请求
void?close()?用于关闭套接字

Socket类:

概念:
java.net.Socket类主要用于描述客户端套接字,是两台机器间通信的端点

常用方法:
Socket()?创建一个未连接的套接字,系统默认类型为SocketImpl。?可以用bind方法
Socket(String?host,?int?port)?根据指定主机名和端口来构造对象
InputStream?getInputStream()?用于获取当前套接字的输入流
OutputStream?getOutputStream()?用于获取当前套接字的输出流
void?close()?用于关闭套接字

输入输出:

输入:
PrintStream?ps?=?new?PrintStream(socket.getOutputStream());
ps.println("hello");
不限定PrintStream,可以Object类型和Data类型的stream都可以。

输出:
BufferedReader?br?=?new?BufferedReader(new?InputStreamReader(accept.getInputStream()));

多线程:

封装类,继承Thread,重写run方法,构造函数接受socket套接字即可。
合成复用模型。

UDP:

编程模型:

接收方:
(1)创建DatagramSocket类型的对象并提供端口号;
(2)创建DatagramPacket类型的对象并提供缓冲区;
(3)通过Socket接收数据内容存放到Packet中,调用receive方法;
(4)关闭Socket;

发送方:
(1)创建DatagramSocket类型的对象;
(2)创建DatagramPacket类型的对象并提供接收方的通信地址;
(3)通过Socket将Packet中的数据内容发送出去,调用send方法;
(4)关闭Socket;

DatagramSocket类:

概念:
java.net.DatagramSocket类主要用于描述发送和接收数据报的套接字(邮局)。

常用方法:
DatagramSocket()?使用无参的方式构造对象,发送方
DatagramSocket(int?port)?根据参数指定的端口号来构造对象,接收方
void?receive(DatagramPacket?p)?用于接收数据报存放到参数指定的位置
void?send(DatagramPacket?p)?用于将参数指定的数据报发送出去
void?close()?关闭Socket并释放相关资源

DatagramPacket类:

概念:
java.net.DatagramPacket类主要用于描述数据报,数据报用来实现无连接包裹投递服务。

常用方法:
DatagramPacket(byte[]?buf,?int?length)根据参数指定的数组来构造对象,用于接收长度为length的数据报,接收方
DatagramPacket(byte[]?buf,?int?length,InetAddress?address,?int?port)根据参数指定数组来构造对象,将数据报发送到指定地址和端口,发送方
InetAddress?getAddress()?用于获取发送方或接收方的通信地址
int?getPort()?用于获取发送方或接收方的端口号
int?getLength()?用于获取发送数据或接收数据的长度,避免接受的数组长度超过

InetAddress类:

概念:
java.net.InetAddress类主要用于描述互联网通信地址信息。

常用方法:
static?InetAddress?getLocalHost()?用于获取当前主机的通信地址
static?InetAddress?getByName(String?host)?根据参数指定的主机名获取通信地址

URL类:

基本概念:

java.net.URL(Uniform?Resource?Identifier)类主要用于表示统一的资源定位器

常用的方法:

URL(String?spec)?根据参数指定的字符串信息构造对象
String?getProtocol()?获取协议名称
String?getHost()?获取主机名称
int?getPort()?获取端口号
String?getPath()?获取路径信息
String?getFile()?获取文件名
URLConnection?openConnection()?获取URLConnection类的实例

URLConnection类:

基本概念:

java.net.URLConnection类是个抽象类,该类表示应用程序和URL之间的通信链接的所有类的超类,主要实现类有支持HTTP特有功能的HttpURLConnection类。
HttpURLConnection类的常用方法
InputStream?getInputStream()?获取输入流
void?disconnect()?断开连接

JVM介绍:

组成:

类装载器子系统、运行时数据区、执行引擎、本地方法接口。
执行引擎包含:解释器、及时编译器、垃圾回收器

生命周期:

虚拟机启动:

Java虚拟机的启动是通过引导类加载器(bootstrap?class?loader)创建一个初始类(initial?class)来完成的,这个类是由虚拟机的具体实现指定的。

虚拟机执行:

执行一个所谓的Java程序的时候,真真正正在执行的是一个叫做Java虚拟机的进程

虚拟机退出:

1.程序正常执行结束。
2.程序在执行过程中遇到了异常或错误而异常终止。
3.由于操作系统用现错误而导致Java虚拟机进程终止。
4.某线程调用Runtime类或system类的exit方法,或Runtime类的halt方法,并且ava安全管理器也允许这次exit或halt操作。
5.除此之外,JNI(Java?Native?Interface)规范描述了用JNI?Invocation?API来加载或卸载?Java虚拟机时,Java虚拟机的退出情况????。

jvm启动流程:

1.JVM运行环境的设置和检查。
2.通过CreateExecutionEnvironment函数查找JAVA_DLL动态库是否存在,能不能访问。并设置一些变量。
3.加载动态库,将动态库中的一些函数链接至本地变量。
4.解析[options]和[args]参数。
5.新建线程初始化虚拟机。
6.加载并执行主类。

类加载:

类加载器:

类加载器(class?loader)用来加载?Java?类到?Java?虚拟机中。
第一个:启动类/引导类:Bootstrap?ClassLoader
第二个:扩展类加载器:Extension?ClassLoader
第三个:应用程序类加载器:Application?Classloader,程序中默认的类加载器,我们Java程序中的类,都是由它加载完成的。
第四个:自定义加载器,继承java.lang.ClassLoader类,重写findClass()方法

类装载方式:

类的加载时机:
加载时机:new、对该类型静态变量的读写、对该类型静态方法的调用,还有反射
程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的。

隐式加载(非传入类全限定名的加载)、静态加载(编译时类不可以缺席的加载):
A?b?=?new?A();????如果程序运行到这段代码时还没有A类,那么JVM会请求装载当前类的类装器来装载类。没有用到的类不会加载(可以代码块测试)。(但import的类都会编译)

显式加载(传入类全限定名的加载)、动态加载(编译时类可以缺席的加载):
ClassLoader.loadClass(className)?:只执行装载过程。
Class.forName(className):执行类的装载、链接、初始化过程。
使用装载当前类的装载器
????Class.forName("test.A");
指定装载器
????Class.forName("test.A",true,this.getClass().getClassLoader());
使用类路径类装载装载.
????ClassLoader.getSystemClassLoader().loadClass("test.A");
使用当前进程上下文的使用的类装载器进行装载,这种装载类的方法常常被有着复杂类装载体系结构的系统所使用。
????Thread.currentThread().getContextClassLoader().loadClass("test.A")
使用自定义的类装载器装载类

过程:

加载:加载指的是把class字节码文件从各个来源通过类加载器装载入内存中。
????字节码来源。一般的加载来源包括从本地路径下编译生成的.class文件,从jar包中的.class文件,从远程网络,以及动态代理实时编译
????类加载器。一般包括启动类加载器,扩展类加载器,应用类加载器,以及用户的自定义类加载器。
链接
????验证:
????????主要是为了保证加载进来的字节流符合虚拟机规范,不会造成安全错误。
????????包括对于文件格式的验证,比如常量中是否有不被支持的常量?文件中是否有不规范的或者附加的其他信息?
????????对于元数据的验证,比如该类是否继承了被final修饰的类?类中的字段,方法是否与父类冲突?是否出现了不合理的重载?
????????对于字节码的验证,保证程序语义的合理性,比如要保证类型转换的合理性。
????????对于符号引用的验证,比如校验符号引用中通过全限定名是否能够找到对应的类?校验符号引用中的访问性(private,public等)是否可被当前类访问?
????准备:
????????主要是为类变量(注意,不是实例变量)分配内存,并且赋予初值。
????????特别需要注意,初值,不是代码中具体写的初始化的值,而是Java虚拟机根据不同变量类型的默认初始值。
????解析:
????????将常量池内的符号引用替换为直接引用的过程。
初始化:
????这个阶段主要是对类变量初始化,是执行类构造器的过程。
????换句话说,只对static修饰的变量或语句进行初始化。
????如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
????如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。

双亲委托模型:

概述:
某一个类加载器加载一个特定的类,他并不是立即由自己加载了,而是将这个动作委托给父亲来完成,如果父亲还有父亲,就继续将这个动作向上传递,一直到没有父亲的根类加载器也就是启动类加载器。由根类加载来尝试着进行加载我们所要加载的class文件,如果不成功,就返回给拓展类加载器,拓展类加载器尝试着加载,如果不成功就继续向下传递,一直到加载成功,然后将整个加载动作返回给第一次尝试加载的加载器中。
1.??当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。
????每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。
2.??当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp?ClassLoader.
3.??当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。

原因:
JVM中要确定某一个类,需要类的?全限定名?+?加载此类的ClassLoader来共同确定。
也就是说即使两个类的全限定名是相同的,但是因为不同的ClassLoader加载了此类,那么在JVM中它是不同的类。
采用了委托模型以后加大了不同的ClassLoader的交互能力,无论你程序中有多少个类加载器,那么这些类其实都是可以共享的,这样就避免了不同的类加载器加载了同样名字的不同类以后造成混乱。
通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。

优势:

1.避免类的重复加载
2.保护程序安全,防止核心API被随意篡改

类冲突:
多个版本的同一个类,默认情况下只会加载一次(根类加载器)。

类隔离:

前提:

JVM?在触发类加载时调用的是?ClassLoader.loadClass?方法。委托给父加载器查询,如果父加载器查询不到,就调用?findClass?方法进行加载。

类加载传导机制:

JVM?会选择当前类的类加载器来加载所有该类的引用的类。

概述:

利用类加载传导规则实现了不同模块的类隔离,这样不同模块之间的依赖就不会互相影响。

实现1:

自定义类加载器继承ClassLoader,通过自定义findClass方法来通过path读取byte来加载,但由于双亲委托,引用的类会被委托给根类加载器加载。

实现2:

自定义类加载器,自定义loadclass方法,破坏双亲委派机制。所有类包括?java.lang?包里面的类都会通过该方法加载。
先使用Thread.currentThread().getContextClassLoader().getParent()尝试loadClass,可以加载所有JDK自带的类,而自定义的类会报异常java.lang.ClassNotFoundException:?test.TestA
然后通过path获取bytes再defineClass,加载自定义的类。
注意:
????path必须是class文件的路径,不能是java文件

自定义String类:
由于双亲委托,会返回jdk自带的。

反射机制:

基本概念:

通常情况下编写代码都是固定的,无论运行多少次执行的结果也是固定的,在某些特殊场合中编写代码时不确定要创建什么类型的对象,也不确定要调用什么样的方法,
????这些都希望通过运行时传递的参数来决定,该机制叫做动态编程技术,也就是反射机制。
通俗来说,反射机制就是用于动态创建对象并且动态调用方法的机制。
目前主流的框架底层都是采用反射机制实现的。????

Class类:

基本概念:

java.lang.Class类的实例可以用于描述Java应用程序中的类和接口,也就是一种数据类型。
该类没有公共构造方法,该类的实例由Java虚拟机和类加载器自动构造完成,本质上就是加载到内存中的运行时类。

获取Class对象的方式:

1.使用数据类型.class的方式可以获取对应类型的Class对象(掌握)。
????字符串表示形式是字符串“class”或“interface”,后跟一个空格,然后是getName返回的格式的类的完全限定名。如Class?java.lang.String
????如果此类对象表示基本类型,则此方法返回基本类型的名称。?
????如果此类对象表示void,则此方法返回“void”。?
????如果此类对象表示数组类型,则此方法返回“class”,后跟getName?。
2.使用引用/对象.getClass()的方式可以获取对应类型的Class对象。注意基本数据类型不是对象,而方法只有类的对象才能调用。
3.使用包装类.TYPE的方式可以获取对应基本数据类型的Class对象。不是自身xxx.class。
4.使用Class.forName(String?xx)的方式来获取参数指定类型的Class对象(掌握)。如java.lang.String完整路径,不能获取基本数据类型
5.使用类加载器ClassLoader.loadClass(String)的方式获取指定类型的Class对象。可以通过getClassLoader获取。

常用的方法:

static?Class<?>?forName(String?className)?用于获取参数指定类型对应的Class对象并返回。String可以动态输入(键盘输入,读取配置)。
T?newInstance()?用于创建该Class对象所表示类的新实例,无参构造的方式。已过时。
Class<?>[]?getInterfaces()获取该类实现的接口类

Constructor类:

基本概念:

java.lang.reflect.Constructor类主要用于描述获取到的构造方法信息

Class类的常用方法:

Constructor?getConstructor(Class<?>...?parameterTypes)用于获取此Class对象所表示类型中参数指定的公共构造方法,不传参时获取无参构造。传参String.class
Constructor<?>[]?getConstructors()用于获取此Class对象所表示类型中所有的公共构造方法????
?????????????????getDeclaredConstructors获取所有构造方法,包括公有、受保护、默认、私有

onstructor类的常用方法:

newInstance(Object...?initargs)使用此Constructor对象描述的构造方法来构造Class对象代表类型的新实例
int?getModifiers(String?name)?获取方法的访问修饰符,整数类型
String?getName(String?name)?获取方法的名称
Class<?>[]?getParameterTypes(String?name)获取方法所有参数的类型

Field类:

基本概念:

java.lang.reflect.Field类主要用于描述获取到的单个成员变量信息。

Class类的常用方法:

Field?getDeclaredField(String?name)用于获取此Class对象所表示类中参数名字对应的单个成员变量信息
Field[]?getDeclaredFields()?用于获取此Class对象所表示类中所有成员变量信息

Field类的常用方法:

Object?get(Object?obj)?获取参数对象obj中此Field对象所表示成员变量的数值
void?set(Object?obj,?Object?value)将参数对象obj中此Field对象表示成员变量的数值修改为参数value的数值
void?setAccessible(boolean?flag)当实参传递true时,则反射对象在使用时应该取消?Java?语言访问检查。
int?getModifiers()?获取成员变量的访问修饰符
Class<?>?getType()?获取成员变量的数据类型
String?getName()?获取成员变量的名称

Method类:

基本概念:

java.lang.reflect.Method类主要用于描述获取到的单个成员方法信息。

Class类的常用方法:

Method?getMethod(String?name,?Class<?>...?parameterTypes)用于获取该Class对象表示类中名字为name参数为parameterTypes的指定公共成员方法
Method[]?getMethods()?用于获取该Class对象表示类中所有公共成员方法

Method类的常用方法:

Object?invoke(Object?obj,Object...?args)使用对象obj来调用此Method对象所表示的成员方法,实参传递args
int?getModifiers()?获取方法的访问修饰符
Class<?>?getReturnType()?获取方法的返回值类型
String?getName()?获取方法的名称
Class<?>[]?getParameterTypes()?获取方法所有参数的类型
Class<?>[]?getExceptionTypes()?获取方法的异常信息

获取其它结构信息:

Package?getPackage()?获取所在的包信息
Class<??super?T>?getSuperclass()?获取继承的父类信息
Class<?>[]?getInterfaces()?获取实现的所有接口
Annotation[]?getAnnotations()?获取注解信息
Type[]?getGenericInterfaces()?获取泛型信息,接口的泛型。

修饰符:

Java语言提供了很多修饰符,主要分为以下两类:

-?访问修饰符
-?非访问修饰符

访问控制修饰符:

Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java?支持?4?种不同的访问权限。
????1.?default?(即默认,什么也不写):?在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
????2.?private?:?在同一类内可见。使用对象:变量、方法。?注意:不能修饰类(外部类)
????3.?public?:?对所有类可见。使用对象:类、接口、变量、方法?//类变量要想通过实例访问,得声明为public
????4.?protected?:?对同一包内的类和所有子类可见。使用对象:变量、方法。?注意:不能修饰类(外部类)。

默认情况:
????接口里的变量都隐式声明为?public?static?final,而接口里的方法默认情况下访问权限为?public。

请注意以下方法继承的规则:
????父类中声明为?public?的方法在子类中也必须为?public。
????父类中声明为?protected?的方法在子类中要么声明为?protected,要么声明为?public,不能声明为?private。
????父类中声明为?private?的方法,不能够被继承。

非访问修饰符:

为了实现一些其他的功能,Java?也提供了许多非访问修饰符。
????1.?static?修饰符,用来修饰类方法和类变量。
????????static变量:
????????????static?关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。?静态变量也被称为类变量。局部变量不能被声明为?static?变量。
????????static方法:
????????????static?关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
????????????对象可以访问静态方法。
????2.?final?修饰符,用来修饰类、方法和变量,final?修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
????????final变量:
????????????表示"最后的、最终的"含义,变量一旦赋值后,不能被重新赋值。被?final?修饰的实例变量必须显式指定初始值。
????????????final?修饰符通常和?static?修饰符一起使用来创建类常量。
????????final方法:
????????????可以被子类继承,但是不能被子类修改。声明?final?方法的主要目的是防止该方法的内容被修改。
????3.?abstract?修饰符,用来创建抽象类和抽象方法。
????????抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
????????抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。
????4.?synchronized?和?volatile?修饰符,主要用于线程的编程。
????????synchronized?关键字声明的方法同一时间只能被一个线程访问。synchronized?修饰符可以应用于四个访问修饰符。
????????transient?序列化的对象包含被?transient?修饰的实例变量时,java?虚拟机(JVM)跳过该特定的变量。
????????volatile?修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

????????在并发编程中,我们通常会遇到以下三个问题:原子性问题,可见性问题,有序性问题。
????????原子性:
????????????Atomic包,原理是CAS+version,比如可以借助硬件的多核Cache一致性协议MESI
????????????synchronized和Lock
????????可见性:
????????????线程1对变量i修改了之后(仍处于CPU高速缓存中),线程2没有立即看到线程1修改的值。volatile、synchronized和Lock
????????有序性:
????????????CPU会对指令重排序,确保单个线程的重排序不会影响执行的结果。但多线程仍然可能会影响。
????????????解决:
????????????????volatile可以保证一定的有序性。在进行指令优化时,不能将在对volatile变量访问(赋值)前面的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
????????????????????系统将不允许对?写入一个volatile变量的操作与其之前的任何读写操作?重新排序,也不允许将?读取一个volatile变量的操作与其之后的任何读写操作?重新排序。
????????????????synchronized和Lock。

import static静态导入:

一般我们导入一个类都用?import?com.....ClassName;
而静态导入是这样:import?static?com.....ClassName.*;这里的多了个static,还有就是类名ClassName后面多了个?.*?,意思是导入这个类里的静态方法。
当然,也可以只导入某个静态方法,只要把?.*?换成静态方法名就行了。
然后在这个类中,就可以直接用方法名调用静态方法,而不必用ClassName.方法名?的方式来调用。?

如:?
????System.out.println(Integer.MAX_VALUE);?
变为:
????import?static?java.lang.System.out;
????import?static?java.lang.Integer.*;?
????out.println(MAX_VALUE);

Optional类:

Optional?类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional?是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional?类的引入很好的解决空指针异常。
实例:?
????Optional<Integer>?a?=?Optional.ofNullable(value1);
????Optional<Integer>?b?=?Optional.of(value2);??????????//null会抛出一个?空指针异常
????Integer?value1?=?a.orElse(new?Integer(0));??????????//可能为空,如果存在值,则返回该值,如果不存在值,则返回它收到的参数:
????Integer?value2?=?b.get();???????????????????????????//值为?null?时也会抛出异常。为避免出现异常,您可选择首先检验其中是否存在值ifPresent()。
????return?value1?+?value2;?
方法:
????orElse()
????orElseGet()?????//如果值存在,不会创建括号内对象
????orElseThrow()???//对象为空时,直接抛出一个异常
????flatMap
????filter
????or()????????????//如果对象包含一个值,则λ表达式不会执行
????ifPresentOrElse()???//Consumer??和?Runnable。如果对象包含一个值,则会执行?Consumer??动作;否则,会执行?Runnable??动作。

JDBC:

概念:

JDBC(Java?Data?Base?Connectivity)?是?Java?访问数据库的标准规范.是一种用于执行SQL语句的Java?API,可以为?多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范.

原理:

JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库!每个数据库厂商都需?要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。
JDBC就是由sun公司定义的一套操作所有关系型数据库的规则(接口),而数据库厂商需要实现这套接口,提供数据库驱动jar包,?我们可以使用这套接口编程,真正执行的代码是对应驱动包中的实现类。

API使用:

1.注册驱动

JDBC规范定义驱动接口:?java.sql.Driver
MySql驱动包提供了实现类:?com.mysql.jdbc.Driver

Class.forName(数据库驱动实现类)加载和注册数据库驱动,数据库驱动由数据库厂商MySql提供?"com.mysql.jdbc.Driver"
源码里是执行static代码块,DriverManager.registerDriver(new?Driver());
从?JDBC3?开始,目前已经普遍使用的版本。可以不用注册驱动而直接使用。?Class.forName?这句话可以省?略。

2.获得连接

Connection?接口,代表一个连接对象?,具体的实现类由数据库的厂商实现?
使用?DriverManager类的静态方法,getConnection可以获取数据库的连接
Connection?getConnection(String?url,?String?user,?String?password)?通过连接字符串和用户名,密码来获取数据?库连接对象

3.获取语句执行平台

通过Connection?的?createStatement方法?获取sql语句执行对象
????Statement?createStatement()?创建?SQL语句执行对象
Statement?:?代表一条语句对象,用于发送?SQL?语句给服务器,用于执行静态?SQL?语句并返回它所生成结?果的对象。
????int?executeUpdate(String?sql);?执行insert?update?delete语句.返回int类型,代表受影响的行?数
????ResultSet?executeQuery(String?sql);?执行select语句,?返回ResultSet结果集对象

4.处理结果集

ResultSet接口?作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。
????boolean?next()??可以循环获取
????????1)?游标向下一行?2)?返回?boolean?类型,如果还有下一条记录,返回?true,否则返回?false
????xxx?getxxx(String?or?int)??返回指定的结果
????????1)?通过列名,参数是?String?类型。返回不同的类型?2)?通过列号,参数是整数,从?1?开始。返回不同的类型

5.释放资源

1) 需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接
2) 释放原则:先开的后关,后开的先关。ResultSet ==> Statement ==> Connection
3) 放在哪个代码块中:finally 块,与IO流一样,使用后的东西都需要关闭!关闭的顺序是先开后关, 先得到的后关闭,后得到的先关闭

封装工具类:

JDBCUtils
1.?定义字符串常量,?记录获取连接所需要的信息
2.?静态代码块,?随着类的加载而加载,Class.forName(DRIVERNAME);
3.获取连接的静态方法
????关闭资源的静态方法

预处理对象PreparedStatement:

接口介绍:

PreparedStatement?是?Statement?接口的子接口,继承于父接口中所有的方法。它是一个预编译的?SQL?语?句对象.
预编译:?是指SQL?语句被发送给db预编译,并存储在?PreparedStatement?对象中。然后可以使用此对象多次高效地执行?该语句。

特点:

因为有预先编译的功能,提高?SQL?的执行效率。
可以有效的防止?SQL?注入的问题,安全性更高

常用方法:

reparedStatement?prepareStatement(String?sql)?指定预编译的?SQL?语句,SQL?语句中使用占位符???创建一个语句对象
int?executeUpdate();????????执行insert?update?delete语句.
ResultSet?executeQuery();????执行select语句.?返回结果集对象?Resulet
void?setDouble(int?parameterIndex,?double?x)????将指定参数设置为给定?Java?double?值。
void?setObject(int?parameterIndex,?Object?x)????使用给定对象设置指定参数的值。

事务相关API:

void?setAutoCommit(boolean?autoCommit)????参数是?true?或?false?如果设置为?false,表示关闭自动提交,相?当于开启事务
void?commit()????????提交事务
void?rollback()????????回滚事务

连接池:

介绍:

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
常见的连接池有?DBCP连接池,?C3P0连接池,?Druid连接池

DBCP连接池:

介绍:
DBCP也是一个开源的连接池,是Apache成员之一,在企业开发中也比较常见,tomcat内置的连接池。
在DBCP包中提供了DataSource接口的实现类,我们要用的具体的连接池?BasicDataSource?类

编写工具类:

1.定义常量 保存数据库连接的相关信息

2.创建连接池对象 (有DBCP提供的实现类)
public?static?BasicDataSource?dataSource?=?new?BasicDataSource();
3.使用静态代码块进行配置
static{
????????dataSource.setDriverClassName(DRIVERNAME);
????????dataSource.setUrl(URL);
????????dataSource.setUsername(USERNAME);
????????dataSource.setPassword(PASSWORD);
}
4.获取连接的方法
connection?connection?=?dataSource.getConnection();
接下来获取statement。
5.释放资源方法
con.close();归还连接

常见配置项:
driverClassName?数据库驱动名称
url数据库地址
username
password
maxActive最大连接数量
maxIdle最大空闲连接
minIdle最小空闲连接
initialSize初始化连接

C3P0连接池:

介绍:
C3P0是一个开源的JDBC连接池,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。

配置:
导入配置文件?c3p0-config.xml
c3p0-config.xml?文件名不可更改?
直接放到src下,也可以放到到资源文件夹中,在项目下创建一个resource文件夹(专门存放资源文件),将文件放在resource目录下即可,创建连接池对象的时候会去加载这个配置文件

编写C3P0工具类:

1.创建连接池对象 C3P0对DataSource接口的实现类

C3P0提供的核心工具类,?ComboPooledDataSource?,?如果想使用连接池,就必须创建该类的对象
new?ComboPooledDataSource();?使用?默认配置
new?ComboPooledDataSource("mysql");?使用命名配置

2.获取连接的方法

dataSource.getConnection();

常见配置:
user?用户名?
password?
driverClass?
jdbcUrl
initialPoolSize?
maxPoolSize?
minPoolSize?
maxIdleTime

Druid连接池:

介绍:
Druid(德鲁伊)是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行?情况。

导入配置文件:
是properties形式的
可以叫任意名称,可以放在任意目录下,我们统一放到?resources资源目录

编写Druid工具类:

1.创建属性集对象

Properties?p?=?new?Properties();

2.加载配置文件 Druid 连接池不能够主动加载配置文件 ,需要指定文件

InputStream?inputStream?=DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Druid?连接池不能够主动加载配置文件?,需要指定文件

3.使用Properties对象的 load方法 从字节流中读取配置信息

p.load(inputStream);

4.通过工厂类获取连接池对象

dataSource?=?DruidDataSourceFactory.createDataSource(p);

5.获取连接

dataSource.getConnection();

DBUtils工具类:

简介:
使用JDBC我们发现冗余的代码太多了,为了简化开发?我们选择使用?DbUtils
Commons?DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。

使用方式:
DBUtils就是JDBC的简化开发工具包。需要项目导入?commons-dbutils-1.6.jar。

核心功能介绍:
1.?QueryRunner?中提供对sql语句操作的API.
2.?ResultSetHandler接口,用于定义select操作后,怎样封装结果集.?
3.?DbUtils类,他就是一个工具类,定义了关闭资源与事务处理相关方法.

表和类之间的关系:
整个表可以看做是一个类
表中的一行记录,对应一个类的实例(对象)
表中的一列,对应类中的一个成员属性

JavaBean组件:
JavaBean?就是一个类,?开发中通常用于封装数据,有以下特点
????1.?需要实现?序列化接口,?Serializable?(暂时可以省略)?
????2.?提供私有字段:?private?类型?变量名;
????3.?提供?getter?和?setter
????4.?提供?空参构造
我们可以创建一个entity包,专门用来存放?JavaBean类

QueryRunner核心类:

构造方法:

QueryRunner()????DbUtils.closeQuietly(con);con关闭可以用这个方法,因为没有statement了,所以不用关闭。
QueryRunner(DataSource?ds)?,提供数据源(连接池),DBUtils底层自动维护连接connection(创建关闭),如QueryRunner?qr2?=?new?QueryRunner(DruidUtils.getDataSource());

常用方法:

update([Connection?conn,]?String?sql,?Object...?params)?,用来完成表数据的增加、删除、更新操?作
query([Connection?conn,]?String?sql,?ResultSetHandler<T>?rsh,?Object...?params)?,用来完成表?数据的查询操作

ResultSetHandler接口:

简介:

ResultSetHandler可以对查询出来的ResultSet结果集进行处理,达到一些业务上的需求。

常见实现类:

ArrayHandler将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这?条记录中的每一个字段的值。
ArrayListHandler将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集?合中。
BeanHandler将结果集中第一条记录封装到一个指定的javaBean中。如参数new?BeanHandler<Employee>(Employee.class)通过反射机制一一对应属性创建对象
BeanListHandler将结果集中每一条记录封装到指定的javaBean中,再将这些javaBean在封装到List?集合中
ColumnListHandler将结果集中指定的列的字段值,封装到一个List集合中
KeyedHandler将结果集中每一条记录封装到Map<String,Object>,在将这个map集合做为另一个?Map的value,另一个Map集合的key是指定的字段的值。
MapHandler将结果集中第一条记录封装到了Map<String,Object>集合中,key就是字段名称,?value就是字段值
MapListHandler将结果集中每一条记录封装到了Map<String,Object>集合中,key就是字段名称,?value就是字段值,在将这些Map封装到List集合中。
ScalarHandler它是用于封装单个数据。例如?select?count(*)?from?表操作。

数据库批处理:

配置:

mysql?批处理是默认关闭的,所以需要加一个参数才打开mysql?数据库批处理,在url中添加?rewriteBatchedStatements=true

实现:

Statement和PreparedStatement都支持批处理操作

PreparedStatement的批处理方式:

void?addBatch()将给定的?SQL?命令添加到此?Statement?对象的当前命令列表中。?通过调用方法?executeBatch?可以批量执行此列表中的命令。
int[]?executeBatch()每次提交一批命令到数据库中执行,如果所有的命令都成功执行了,那么返回一个数组,这个数组是说明每条命令所影响的行数
示例:
????ps.setString(1,"小强"+i);
????ps.addBatch();
????ps.executeBatch();

MySql元数据:

介绍:

除了表之外的数据都是元数据,可以分为三类
查询结果信息:?UPDATE?或?DELETE语句?受影响的记录数。?
数据库和数据表的信息:?包含了数据库及数据表的结构信息。?
MySQL服务器信息:?包含了数据库服务器的当前状态,版本号等。

常用命令:

select?version();?获取mysql服务器的版本信息
show?status;?查看服务器的状态信息
show?columns?from?table_name;?显示表的字段信息等,和desc?table_name一样
show?index?from?table_name;?显示数据表的详细索引信息,包括PRIMARY?KEY(主键)
show?databases:列出所有数据库?
show?tables?:?显示当前数据库的所有表?
select?database():?获取当前的数据库名

使用JDBC 获取元数据:

通过JDBC?也可以获取到元数据,比如数据库的相关信息,或者当我们使用程序查询一个不熟悉的表时,?我们可以通过获取元素据信息,了解表中有多少个字段,字段的名称?和?字段的类型.

常用类介绍:

DatabaseMetaData描述数据库的元数据对象
ResultSetMetaData描述结果集的元数据对象

获取元数据对象的方法:

xxx.getMetaData?()
????connection?连接对象,?调用?getMetaData?()?方法,获取的是DatabaseMetaData?数据库元数据对象
????PrepareStatement?预处理对象调用?getMetaData?()?,?获取的是ResultSetMetaData?,?结果集元数据对象

DatabaseMetaData的常用方法:

getURL()?:?获取数据库的URL
getUserName():?获取当前数据库的用户名?
getDatabaseProductName():?获取数据库的产品名称?
getDatabaseProductVersion():?获取数据的版本号?
getDriverName():?返回驱动程序的名称
isReadOnly():?判断数据库是否只允许只读?true?代表只读

ResultSetMetaData的常用方法:

getColumnCount()?:?当前结果集共有多少列
getColumnName(int?i)?:?获取指定列号的列名,?参数是整数?从1开始?
getColumnTypeName(int?i):?获取指定列号列的类型,?参数是整数?从1开始

XML:

作用:

1.存储数据。通常,我们在数据库中存储数据。不过,如果希望数据的可移植性更强,我们可以 把数据存储 XML 文件中

2.配置文件。作为各种技术框架的配置文件使用 (最多)

3.传输格式。客户端可以使用XML格式向服务器端发送数据,服务器接收到xml格式数据,进行解析

语法:

文档声明必须为结束;
文档声明必写在第一行;

XML约束:

在XML技术里,可以编写一个文档来约束一个XML文档的书写规范,这称之为XML约束。
常见的xml约束:
????DTD
????Schema
DTD约束
????介绍:
????????TD(Document?Type?Definition),文档类型定义,用来约束XML文档。规定XML文档中元素的名称,子元素的名称及顺序,元素的属性等。
????????常情况我们都是通过框架提供的DTD约束文档,编写对应的XML文档。常见框架使用DTD约束有:?Struts2、hibernate等。
????编写DTD
????????创建约束文件?student.dtd
????引入DTD
????????引入dtd文档到xml文档中,两种方式?
????????????内部dtd:将约束规则定义在xml文档中?
????????????外部dtd:将约束的规则定义在外部的dtd文件中
Schema约束
????介绍:
????????1.?Schema是新的XML文档约束,?比DTD强大很多,是DTD?替代者;
????????2.?Schema本身也是XML文档,但Schema文档的扩展名为xsd,而不是xml。?
????????3.?Schema?功能更强大,内置多种简单和复杂的数据类型
????????4.?Schema?支持命名空间?(一个XML中可以引入多个约束文档)
????XML引入Schema约束

XML 解析:

开发中比较常见的解析方式有两种,如下:

DOM:
要求解析器把整个XML文档装载到内存,并解析成一个Document对象。
?????????????????优点:元素与元素之间保留结构关系,故可以进行增删改查操作。
缺点:XML文档过大,可能出现内存溢出显现。?
SAX:是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。并以事件驱动的方?式进行具体解析,每执行一行,都将触发对应的事件。(了解)
优点:占用内存少?处理速度快,可以处理大文件?
缺点:只能读,逐行后将释放资源。

常见的解析器:

就是根据不同的解析方式提供的具体实现。有的解析器操作过于繁琐,为了方便开发人员,有提供易于操作的解析开发包
JAXP:sun公司提供的解析器,支持DOM和SAX两种思想
DOM4J:一款非常优秀的解析器?,?Dom4j是一个易用的、开源的库,用于XML,XPath和XSLT。?它应用于Java平台,采用了Java集合框架并完全支持DOM,SAX和JAXP。
Jsoup:jsoup?是一款Java?的HTML解析器?,也可以解析XML?PULL:Android内置的XML解析方式,类似SAX。

dom4j:

介绍
????使用核心类SaxReader加载xml文档获得Document,通过Document?对象获得文档的根元素,然后就可以操作了
常用API:
????SaxReader对象
????????read(...)?加载执行xml文档
????Document对象
????????getRootElement()?获得根元素
????Element对象
????????elements(...)?获得指定名称的所有子元素。可以不指定名称?
????????element(...)?获得指定名称的第一个子元素。可以不指定名称?
????????getName()?获得当前元素的元素名
????????attributeValue(...)?获得指定属性名的属性值?
????????elementText(...)?获得指定名称子元素的文本值
????????getText()?获得当前元素的文本内容
xpath方式读取xml
介绍
????XPath?是一门在?XML?文档中查找信息的语言。?可以是使用xpath查找xml中的内容。
????由于DOM4J在解析XML时只能一层一层解析,所以当XML文件层数过多时使用会很不方便,结合?XPATH就可以直接获取到某个元素
XPath基本语法介绍
????使用dom4j支持xpath的操作的几种主要形式
????????/AAA/DDD/BBB
????????BBB[1]?,?BBB[last()]
????????//BBB[@id='b1']
常用方法:
????selectSingleNode(query):?查找和?XPath?查询匹配的一个节点。参数是Xpath?查询串。
????selectNodes(query):?得到的是xml根节点下的所有满足?xpath?的节点;参数是Xpath?查询串。
????Node:?节点对象

JDBC自定义XML:

定义配置文件
????创建自定义xml?文件,?保存?数据库连接信息
使用:
????使用xpath?读取数据库信息
????SAXReader?sr?=?new?SAXReader();
????Document?document?=?sr.read
????document.selectSingleNode

Properties类:

Properties?继承于?Hashtable。表示一个持久的属性集,属性列表以key-value的形式存在,key和value都是字符串。
读取:
????1.?使用java.util.Properties类的load(InputStream?in)方法加载properties文件
????????new?Properties().load(new?BufferedInputStream(new?FileInputStream(new?File(basePath))));
????????path?=?prop.getProperty("path");
????????//需要添加全路径:src/main/resources/system.conf

????2.?使用java.util.ResourceBundle类的getBundle()方法,注意:这个getBundle()方法的参数只能写成包路径+properties文件名,否则将抛异常
????????ResourceBundle?rb?=?ResourceBundle.getBundle("cn/habitdiary/prop");
????????path?=?rb.getString("path");

????3.?使用java.util.PropertyResourceBundle类的构造函数
????????ResourceBundle?rb?=?new?PropertyResourceBundle(new?BufferedInputStream(new?FileInputStream(basePath)));
????????path?=?rb.getString("path");

????4.?使用class变量的getResourceAsStream()方法,注意:getResourceAsStream()方法的参数按格式写到包路径+properties文件名+.后缀
????????new?Properties().load(Myclass.class.getResourceAsStream("cn/habitdiary/prop.properties"));
????????path?=?p.getProperty("path");

????5.?使用class.getClassLoader()所得到的java.lang.ClassLoader的getResourceAsStream()方法,getResourceAsStream(name)方法的参数必须是包路径+文件名+.后缀,否则会报空指针异常
????????new?Properties().load(Myclass.class.getClassLoader().getResourceAsStream("cn/habitdiary/prop.properties"));
????????//能识别/src/main/resources路径下的文件

????6.?使用java.lang.ClassLoader类的getSystemResourceAsStream()静态方法,getSystemResourceAsStream()方法的参数格式也是有固定要求的
????????new?Properties().load(ClassLoader.getSystemResourceAsStream("cn/habitdiary/prop.properties"));
遍历:
????1.?stringPropertyNames方法
????????for?(String?key?:?properties.stringPropertyNames())?{
????????????System.out.println(key?+?"="?+?properties.getProperty(key));
????????}
????2.?keySet方法
????????Set<Object>?keys?=?properties.keySet();//返回属性key的集合
????????for?(Object?key?:?keys)?{
????????????System.out.println(key.toString()?+?"="?+?properties.get(key));
????????}
????3.?entrySet方法
????????Set<Map.Entry<Object,?Object>>?entrySet?=?properties.entrySet();
????????for?(Map.Entry<Object,?Object>?entry?:?entrySet)?{
????????????System.out.println(entry.getKey()?+?"="?+?entry.getValue());
????????}
????4.?propertyNames方法
????????Enumeration<?>?e?=?properties.propertyNames();
????????while?(e.hasMoreElements())?{
????????????String?key?=?(String)?e.nextElement();
????????????String?value?=?properties.getProperty(key);
????????????System.out.println(key?+?"="?+?value);
????????}

内存管理:

内存结构:

共享:

堆(Heap):线程共享。所有的对象实例以及数组都要在堆上分配。回收器主要管理的对象。
方法区(Method?Area):线程共享。存储类信息、方法信息、域信息、常量、静态变量、即时编译器编译后的代码。(JDK?1.8后,元空间存放在堆外内存中)

线程私有:

虚拟机栈(JVM?Stack):线程私有。存储局部变量表、操作栈、动态链接、方法出口,对象指针。
????大小可以是动态(OutOfMemoryError)或者固定的(在线程创建的时候独立设置,超出会StackoverflowError)。
本地方法栈(Native?Method?Stack):线程私有。为虚拟机使用到的Native?方法服务。如Java使用c或者c++编写的接口服务时,代码在此区运行。
程序计数器(Program?Counter?Register):线程私有。有些文章也翻译成PC寄存器(PC?Register),同一个东西。它可以看作是当前线程所执行的字节码的行号指示器。指向下一条要执行的指令。
????它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

JVM堆内存:

结构:

JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young?Generation)、老年代(Old?Generation),非堆内存就一个MetaSpace(java8之前为永久代)。????
年轻代又分为Eden生成区和Survivor区。Survivor区由FromSpace(s0)和ToSpace(s1)组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
堆内存用途:
????存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。
非堆内存用途:
????永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。
????几乎不进行垃圾回收。
????JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存(所以-XX指定的内存不包含)。
????配置:
????????-XX:MetaspaceSize?和?-XX:MaxMetaspaceSize
????k8s内存分配:
????????考虑-Xmx,metaspace,程序本身运行消耗。

对象分代:

1.新生成的对象首先放到年轻代Eden区,
2.当Eden空间满了,触发Minor GC(清理年轻代),存活下来的对象移动到Survivor0区
3.Survivor0区满后触发执行Minor?GC,Survivor0区存活对象移动到Suvivor1区,这样保证了一段时间内总有一个survivor区为空。
????经过多次Minor?GC(默认15次)仍然存活的对象移动到老年代。
4.老年代存储长期存活的对象,占满时会触发Major?GC(Full?GC),GC期间会停止所有线程等待GC完成,所以对响应要求高的应用尽量减少发生Major?GC,避免响应超时。

Jstat:

jstat?-gc?-t?58563?#显示pid是58563的垃圾回收堆的行为统计

线上问题排查:

jstat实时查看分析
堆溢出时自动堆转存-XX:+HeapDumpOnOutOfMemoryError,通过工具jhat分析

如何判断对象是否可回收?

1.引用计数器算法:

每当对象被引用一次计数器加?1,对象失去引用计数器减?1,计数器为?0?是就可以判断对象死亡了。
这种算法简单高效,但是对于循环引用或其他复杂情况,需要更多额外的开销,因此?Java?几乎不使用该算法。

2.根搜索算法-可达性分析算法:

所谓可达性分析是指,顺着?GCRoots?根一直向下搜索,整个搜索的过程就构成了一条“引用链”,只要在引用链上的对象叫做可达,
在引用链之外的(说明跟?GCRoots?没有任何关系)叫不可达,不可达的对象就可以判断为可回收的对象。
哪些对象可作为?GCRoots?对象呢??
????虚拟机栈帧上本地变量表中的引用对象(方法参数、局部变量、临时变量)
????方法区中的静态属性引用类型对象、常量引用对象
????本地方法栈中的引用对象(Native?方法的引用对象)
????Java?虚拟机内部的引用对象,如异常对象、系统类加载器等
????所以被同步锁(synchronize)持有的对象
????Java?虚拟机内部情况的注册回调、本地缓存等

垃圾回收算法:

1.标记-清除算法

根据名称就可以理解改算法分为两个阶段:首先标记出所有需要被回收的对象,然后对标记的对象进行统一清除,清空对象所占用的内存区域
下图展示了回收前与回收后内存区域的对比,红色的表示可回收对象,橙色表示不可回收对象,白色表示内存空白区域。
缺点:
????第一个:是执行效率不可控,试想一下如果堆中大部分的对象都可回收的,收集器要执行大量的标记、收集操作。
????第二个:产生了许多内存碎片,通过回收后的内存状态图可以知道,被回收后的区域内存并不是连续的,当有大对象要分配而找不到满足大小的空间时,要触发下一次垃圾收集。

2.标记-复制算法

标记-复制算法将内存分为大小相同的两个区域,运行区域,预留区域,所有创建的新对象都分配到运行区域,当运行区域内存不够时,将运作区域中存活对象全部复制到预留区域,
然后再清空整个运行区域内存,这时两块区域的角色也发生了变化,每次存活的对象就像皮球一下在运行区域与预留区域踢来踢出,而垃圾对象会随着整个区域内存的清空而释放掉
优点:
????标记-复制算法在大量垃圾对象的情况下,只需复制少量的存活对象,并且不会产生内存碎片问题,新内存的分配只需要移动堆顶指针顺序分配即可,很好的兼顾了效率与内存碎片的问题。
缺点:
????预留一半的内存区域未免有些浪费了,并且如果内存中大量的是存活状态,只有少量的垃圾对象,收集器要执行更多次的复制操作才能释放少量的内存空间,得不偿失。

3.标记-整理算法

标记-复制算法要浪费一半内存空间,且在大多数状态为存活状态时使用效率会很低,针对这一情况计算机科学家又提出了一种新的算法“标记-整理算法”,
标记整理算法的标记阶段与其他算法一样,但是在整理阶段,算法将存活的对象向内存空间的一端移动,然后将存活对象边界以外的空间全部清空。
优点:
????不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价
缺点:
????标记整理算法解决了内存碎片问题,也不存在空间的浪费问题,看上去挺美好的。
????但是,当内存中存活对象多,并且都是一些微小对象,而垃圾对象少时,要移动大量的存活对象才能换取少量的内存空间。效率不高。
????不同的垃圾回收算法都有各自的优缺点,适应于不同的垃圾回收场景

4.分代收集算法

新生代:复制算法(CG后只有少量的对象存活)
老年代:标记-整理算法?或者?标记-清理算法(GC后对象存活率高)

垃圾收集器:

Serial收集器(复制算法):

新生代单线程收集器,标记和清理都是单线程,优点是简单高效;

Serial Old收集器 (标记-整理算法):

老年代单线程收集器,Serial收集器的老年代版本;

ParNew收集器 (复制算法):

新生代收并行集器,实际上是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现;
除了Serial收集器之外,只有它能与CMS收集器配合工作。

Parallel Scavenge收集器 (复制算法):

新生代并行收集器,追求高吞吐量,高效利用?CPU。吞吐量?=?用户线程时间/(用户线程时间+GC线程时间)
适合场景:
????高吞吐量可以高效率的利用CPU时间,尽快完成程序的运算任务,适合后台应用等对交互相应要求不高的场景;

Parallel Old收集器 (标记-整理算法):

老年代并行收集器,吞吐量优先,Parallel?Scavenge收集器的老年代版本;

CMS(Concurrent Mark Sweep)收集器(标记-清除算法):

老年代并行收集器,以获取最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短GC回收停顿时间。
流程:
????初始标记(CMS?initial?mark)?-stop?the?world
????并发标记(CMS?concurrent?mark)
????重新标记(CMS?remark)?-stop?the?world
????并发清除(CMS?concurrent?sweep)
优点:
????并发收集、低停顿。
????适合重视服务的响应速度,希望系统停顿时间最短的互联网站。
缺点:
????CMS收集器对CPU资源非常敏感。(占用多)
????CMS收集器无法处理浮动垃圾(Floating?Garbage)。由于CMS并发清理时,用户线程还在运行,伴随产生新垃圾,而这一部分出现在标记之后,只能下次GC时再清理。这一部分垃圾就称为”浮动垃圾“。
????????需要预留空间给并发收集时用户程序产生的新对象。
????CMS收集器是基于标记-清除算法,该算法的缺点都有。
????????比如内存碎片,当大对象没有空间时,需要FullGC时开启内存碎片合并整理过程,这个过程无法并发,需要停顿。
????发生promotion?failed和concurrent?mode?failure时,会触发Full?GC,切换到Serial?Old(从未移植parallel?old,而g1在java10中已经实现)
????????promotionfailed是在进行Minor?GC时,survivor?space放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;
????????concurrent?mode?failure是在执行CMS?GC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。
配置:
????-XX:+UseConcMarkSweepGC?:?手动指定老年代使用CMS收集器
????-XX:+UseCMSCompactAtFullCollection?:?在进行Full?GC时对内存进行压缩,JDK1.6以前不需要配置,默认开启

G1(Garbage First)收集器 (标记-整理算法):

概述:
Java堆并行收集器,G1收集器是JDK1.7提供的一个新收集器,G1收集器基于“标记-整理”算法实现,也就是说不会产生内存碎片。
此外,G1收集器不同于之前的收集器的一个重要特点是:G1回收的范围是整个Java堆(包括新生代,老年代),而前六种收集器回收的范围仅限于新生代或老年代。
G1将堆分成许多相同大小的区域单元,每个单元称为Region。
虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。

优点:
G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。分区收集。
避免cms内存碎片问题的存在。
G1?Full?gc在java10后使用了Parallel技术

适合场景:
对?GC?的停顿时间很敏感,比如?WEB?服务器(替代CMS)
小内存应用上CMS的表现大概率会优于G1,而G1在大内存应用上则发挥其优势。平衡点在6-8GB之间。

配置:
-XX:+UseG1GC
XX:MaxGCPauseMillis=200????????如果MaxGCPauseMillis设置的过小,那么GC就会频繁,吞吐量就会下降。如果MaxGCPauseMillis设置的过大,应用程序暂停时间就会变长。

查看命令:

java?-XX:+PrintCommandLineFlags?-version
????java8使用的是UseParallelGC?即?Parallel?Scavenge?+?Parallel?Old
????java11使用的是UseG1GC
jmap?-heap?pid
????查看线上服务

GC:

分类:

部分收集(Partial?GC)。不是完整收集整个Java堆的垃圾收集
????新生代收集(Minor?GC/Young?GC):只是新生代的垃圾收集
????老年代收集(Major?GC/Old?GC):只是老年代的圾收集
????????目前,只有CMS?GC会有单独收集老年代的行为
????????备注:很多时候?Major?GC?会与Full?GC?混用,需要具体分辨是老年代回收还是整堆回收
????混合收集(Mixed?GC):收集整个新生代以及部分老年代的垃圾收集。目前,只有?G1?GC?会有这种行为
整堆收集(Full?GC)。收集整个?java?堆和方法区的垃圾收集

触发机制和特点:

Minor GC触发条件和特点:
当Eden区满时,触发Minor?GC。
Java对象大多都具备?朝生夕灭?的特性,所以Minor?GC非常频繁,一般回收速度也比较快
Minor?GC会引发STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行

老年代GC(Major GC)触发机制及特点:
指发生在老年代的GC
出现Major?GC,经常会伴随至少一次的Minor?GC
Major?GC的速度一般会比Minor?GC慢10倍以上,STW的时间更长
如果Major?GC后,内存还不足,就报OOM

Full GC触发条件:
(1)调用System.gc时,系统建议执行Full?GC,但是JVM不必然执行
(2)老年代空间不足
(3)方法区空间不足
(4)通过Minor?GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、From?Space区向To?Space区复制时,对象大小大于To?Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

查看gc情况:

jstat?-gc?12538?1s

逃逸分析:

逃逸现象:

当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他方法或者线程所引用,这种现象称作指针(或者引用)的逃逸。
通俗的说,如果一个对象的指针被多个方法或者线程引用时,那么就称这个对象的指针(或对象)的逃逸(Escape)。

概述:

一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。
通过逃逸分析,Java?Hotspot编译器能够分析出一个新的对象的引用的使用范围,从而决定是否要将这个对象分配到堆上。

编译器使用分析来优化:

栈上分配:将堆分配转化为栈分配,无需垃圾回收
同步省略:如果一个对象被发现只有一个线程被访问到,那么对于这个对象的操作可以不考虑同步
分离对象或标量替换:有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在CPU寄存器中(实际是存储在栈上)

逃逸分析的不足:

无法保证逃逸分析的收益大于进行此操作带来的性能消耗。

线上问题排查:

如果线程还存在,通过jstat查看gcutl的垃圾回收状态,分年轻代minor?gc不用管,关注老年代full?gc,内存占用满在启动参数配置OOM?dump内存堆栈文件,用根据MAT或者esay.gc.io分析。
java启动参数?-Xmx10m?-?XX:HeapDumpOnOutOfMemoryError,借助工具IBM?HeapAnalyzer看object?list排行

某个线程CPU占用过高:

top?-H?-p?<pid>??查看进程内各个线程的资源占用,通过jstack命令导出线程dump信息。把线程pid(对应线程dump内容中的nid,nid是16进制表示的)转换成十六进制,然后在线程dump文件中搜索即可找到对应的线程栈方法执行情况。
阿里开源Arthas

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-12-16 18:03:49  更:2021-12-16 18:04:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年10日历 -2024/10/5 7:52:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码