计算机分为硬件系统和软件系统

我们是面向互联网架构开发的后端开发工程师,负责软件或者网站的开发

人机交互的方式:图形化界面 命令行的方式

jdk8的环境变量配置:

  1. JAVA_HOME配置的jdk的根、C:\Program Files\Java\jdk1.8.0_241

  2. .path:不需要新建,配置的是jdk的bin目录

3.CLASSPATH:需要新建,配置的jdk的lib目录

验证:win+R输入命令:java-version

对Java平台的理解主要包括以下三个方面

面向对象和核心类库方面,跨平台方面和虚拟机和垃圾收集

面向对象和核心类库方面

  1. java是一门面向对象编程语言,极好的实现类面向对象理论

  2. java核心类库提供了包含合集容器,线程相关类,IO/NIO,J.U.C并发包,异常和安全等类库,极大的方便了程序员的开发

  3. JDK提供的工具包含:基本的编译工具,虚拟机性能检测相关工具等

跨平台方面

java语言是跨平台,一行代码处处运行,而不同系统安装不同的jvm,是java语言跨平台的前提

  1. 运行过程:我们编写的源码是。java为后缀的,通过编译生成的是。class字节码文件,交给JVM来执行

  2. 跨平台:只要在不同的操作系统上安装对应的JVM,就可以实现跨平台:一份代码 处处运行

  3. java平台通过虚拟机屏蔽了操作系统的底层细节,使得开发者无需过多的关心不同操作系统之间的差异性

  4. 虚拟机和操作系统都是通过增加一个间接的中间层来进行”解耦“

虚拟机和垃圾收集

java通过垃圾收集器分配内存,大部分情况下,程序员不需要之间操心内存的分配和回收

同时,围绕着虚拟机的效率问题展开,将涉及一些优化技术,,如果虚拟机加载字节码后,一行一行的解释执行,,着势必影响执行效率。所以,对于这个运行环节,虚拟机会进行一些优化处理,例如JIT技术,会将热点代码编译成机器码。而AOT技术,是在运行前,通过工具直接将字节码转换为机器码。

对于“Java 是解释执行”这句话,这个说法不太准确。我们开发的 Java 的源代码,首先通过 Javac 编译成为字节码(bytecode),然后,在运行时,通过 Java 虚拟机(JVM)内嵌的解释器将字节码转换成为最终的机器码。但是常见的 JVM,比如我们大多数情况使用的 Oracle JDK 提供的 Hotspot JVM,都提供了 JIT(Just-In-Time)编译器,也就是通常所说的动态编译器,JIT 能够在运行时将热点代码编译成机器码,这种情况下部分热点代码就属于编译执行,而不是解释执行了。

JDK

=java development kit :Java 语言的软件开发工具包 最大-开发必须安装,包含了编译工具,打包工具等

JRE

=java runtime environment :运行环境,包括java虚拟机和java程序所需的核心类库等,核心类库主要是java。long包:包含了允许了java程序不可少的系统类,如包装类型,基本 数学函数,字符串处理,线程,异常处理等,系统缺省加载这个包

Jvm

=java virtual machine:java虚拟机,负责加载并运行.class字节吗文件,不同的平台有自己的虚拟机,因此java语言可以实现快平台

os操作系统 jdk包含jre包含jvm

JVM总体分为4个大模块

Jvm总体分为四大模块,分别是:类的加载机制、jvm内存结构、GC垃圾回收、调优(从性能和命令两方面进行调优,不做研究)

1,类的加载机制

通过ClassLoader及其子类的。class文件中的二进制数据读取到内存中,将其放在方法去内,然后在堆区创建一个Java。long。Class对象,用来封装类在方法取内的数据结构,类的生命周期是创建,加载,初始化,使用,销毁

2.JVM内存结构

jvm内存分配结构

堆:是java虚拟机所管理的内存中最大的一块,用来存放对象实例

方法区:用于存储已被虚拟机加载的类信息,常量,静态变量等数据

栈:用于存放局部变量等信息

堆对象的分配规则

虚拟机将堆内存划分为新生代和老生代以及持久代,新生代有划分为伊甸园区和幸村去,对象刚创建的时候是放到伊甸园区,在伊甸园区经过一次扫描,如果对象依然存在,则移到幸存区,如果在幸存区经过多次扫描依然存在,测移到老生代,老生代扫描频率远低于新生代,老生代的对象一旦产生回收可能会导致程序的卡顿甚至崩溃,持久代用于存放有些应用动态生成或者调用一些class过程中新增的一些类,比如hibernate应用等,持久代对垃圾回收没有显著的影响

发生在新生代的回收--minor gc 初代回收

发生在老生代的回收---full gc 完全回收

3.GC算法垃圾回收

常用的GC算法

标记 --清除算法

首先标记处所有的需要回收的对象,在标记完成后统一回收所有的被标记的对象

标记--压缩算法

首先标记处所有的需要回收的对象,让所有存活的对象都想一端移动,然后直接清理掉端边界意外的内存

复制算法

将可用内存把容量划分大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉

分代收集算法

把java堆分为新生代和老年代,这样就可以根据各个年代的特色采用最适当的收集算法

4.调优

(从性能和命令两方面进行调优,不做研究)

跨平台性?原理是什么

所谓跨平台性,是指java语言编写的程序,一次编译后,可以在多个系统平台上运行。

实现原理:Java程序是通过虚拟机在系统平台上运行的,只要该系统安装相应的java虚拟机,该系统就可以运行java程序。

字节码?采用字节码的最大好处是什么

字节码:Java源代码经过虚拟机编译器编译后产生的文件(即扩展为.class的文件),它不面向任何特定的处理器或操作系统,只面向虚拟机。

采用字节码的好处:

Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不面向任何特定的处理器或操作系统,因此,Java程序无须重新编译便可在多种不同的计算机上运行。

先看下java中的编译器和解释器:

Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行

Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。

权限修饰符符速查表

定义:java中可以使用访问修饰符来保护类,变量,方法的访问,jaba支持4中不同的访问权限

private:在同一类内可见,使用对象:变量,方法,注意:不能修饰类(外部类)

default默认:不使用任何关键字,在同一包内可见,不适用任何修饰符,,使用对象:类。接口。变量,方法。

protected:对同一包内的类和所有子类可见,使用对象:变量。方法。不能修饰类{外边类)

pubilc:对所有类可见,使用对象:类,接口,变量,方法

关键字必须背过

特点关键字中的所有字母都为小写 ,一共有50个 const和go to目前还没有确定意义

用于点定义数据类型的关键字

class类 interface接口 byte字节(1字节) short短整型 (2字节)

int整数型(Interger) (4字节) long长整型 (8字节) float单精度(4字节) double双精度 (8字节)

char字符 (2字节)(Character) 【0-65535】 Boolean布尔型(1字节)

void 无返回值

用于定义数据类型中的关键字(不做标识符)

true 真,非0 false假0 null空

用于定义流程控制的关键字

if 单分支判断 else 或者 switch多分支 case或者 default默认

while 循环 do循环 for循环 break跳出 continue跳过

return返回方法

用于定于访问权限修饰符的关键字

private私有的 protected保护的 public公有的

用于定义类,函数,变量修饰符的关键字

abstract抽象的 final最终,常量 static静态 synchronized同步

用于定于类与类之间关系的关键

extends继承 implements实现

用于定义建立实例及引用实例,判断实例的关键字

try试图 catch 捕获 finally最终 throw 抛出 throws接盘

用于包的关键字

package包 import导包

其他修饰符关键字

native本地的 strictfp小数 transient 序列化 volatile可见性 assert 断言,调试

标识符

标识符是开发者可以自行定义的名称,类名,方法名,变量名称

1.英文

2.数字,支持数字,数字必须放在字母后面

3.特殊符号,支持个别特殊符号字符$——等但是大多数不支持

4,中文,虽然支持,但是不适用

变量

:变化的量,能修改

定义变量

1.定义的时候并且赋值:变量的类型 变量名 =变量值 比如 int a = 19;

2。先定义,后面在赋值 int a ; a =99

3.注意:=是赋值符号,等号右边的值交给等号左边的变量来保存

常量:不变化的量,固定的值,不能修改final

4.变量的就近原则,离谁近,就使用谁

局部变量:

位置:定义在方法里或者局部代码块中

注意:必须受到初始化来分配内存,如:int i = 5或者int i; i=5

作用域:也就是方法里或者局部代码块中,方法运行完内存就释放了

成员变量

位置:定义在类里方法外

注意:不要初始化也会被初始化成默认值

作用域:整个类中,类消失了,变量才会释放

注意:基本类型保存的是值,引用类型保存的是地址值

  1. 如果想指定本类的成员变量,使用this.变量名来指定

  2. 如果想指定父类的成员变量,使用super.变量名来指定

各变量联系与区别

  • 成员变量:作用范围是整个类,相当于C中的全局变量,定义在方法体和语句块之外,一般定义在类的声明之下;成员变量包括实例变量和静态变量(类变量);

  • 实例变量:独立于与方法之外的变量,无static修饰,声明在一个类中,但在方法、构造方法和语句块之外,数值型变量默认值为0,布尔型默认值为false,引用类型默认值为null;

  • 静态变量(类变量):独立于方法之外的变量,用static修饰,默认值与实例变量相似,一个类中只有一份,属于对象共有,存储在静态存储区,经常被声明为常量,调用一般是类名.静态变量名,也可以用对象名.静态变量名调用;

  • 局部变量:类的方法中的变量,访问修饰符不能用于局部变量,声明在方法、构造方法或语句块中,在栈上分配,无默认值,必须初始化后才能使用;

运算符:

计算机做大的作用就是实现数学计算

算术运算符:+ - * / % ++ -- 自增自减 符号在前先运算在使用,符号在后先使用在运算

链接运算符: +

连接运算符: = += -= *= /= a *=2 等价于a =a*2

关系运算符: == != > >= < < 比较结果都是布尔型

逻辑运算符:& && | || ^ !

不同点:&&和||有短路现象

相同点:都是可以作为逻辑运算符使用 , &和|可以作为位运算符

位 运算符:只要参与的是二进制的运算

&:全真才真

|:全假才假

^:相同为0 ,不同为1

~:非0 为1,非1为0

三元三目运算符:c?x:y

优先执行:()

二八十六进制转化

十进制向二进制转化:不断除以2,然后取余数,将余数倒排

二进制向十进制转化:从低位次开始,按位乘以2的次幂在求和

十进制向其他进制转化,除以对应的进制,然后取余数倒排

其他进制向十进制:从地次位开始,按为次诚意进制的位次次幂,然后求和

二进制向八进制,三变一

八进制向二进制:一变三

二进制向十六进制,四变一

十六进制向二进制:一变四

字面值前缀: 0b = 2进制 0= 8进制 0x = 16进制

java的数据类型

1.基本类型

2.引用类型

类型转换

1.Boolean类型不参与转换

2.小转大,直接转,隐式转化

3.大专小,强行转 -显示转化 格式: byte b = (byte) a

4.浮变整 小数没 不会四舍五入

类型是否转化,取决与类型的取值范围,而不是字节数,字节数只能做大概的参考,是取之范围

引用类型之间转换取决于是否有继承关系

5条字面值规则

1.整数默认int类型

2.小数默认为double类型

3.byte short char三种比int小的类型,可以使用范围内的值直接赋值

4.字面值后缀: L F D

5.字面值前缀: 0b = 2进制 0= 8进制 0x = 16进制

==:

基本类型看值,相等就是true 不想等就是false

引用类型就是false ,数组也是引用类型

5条运算规则

1.运算结果的数据类型与最大类型保持一致

2.3种比int小的类型,运算时会自动提升成int在运算

3.整数运算溢出的问题,一旦溢出,数据就错误

4.浮点数运算不精确

5.浮点数的特殊值 infinity NaN

注意:不能使用double类型参数的构造方法,需要使用String的类型构造方法,否则还会出现不精确问题

注意:除法运算时,如果出现不尽的现象还会报错,所以需要指定保留文书与舍入方法

流程控制

1.流程结构

顺序结构从头到尾所有代码依次都会执行到

可以解决输入输出计算等问题 但是不可以先做判断,在选择行的执行代码

2.分支结构

if(){}

if(){}else{}

3.镶嵌分支结构

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

4选择结构

swich(a){

case : break;

case :break;

dafault 保底选项

1.a支持byte short char int String (Integer,Byte,Short,Character)

2.a的数据类型与case后的value数据类型要一致

3.如果没有增添break 又被case匹配到 会有穿透现象 包括default

4.case个数break default是否添加有业务决定

5.default是没有一个case匹配到才执行

6.一般我么习惯在每个case后加break

5循环结构

break,continue,return

break:直接结束当前循环,跳出循环体,简单粗暴

break以后的循环体中的语句不会继续执行, 循环体外的会执行

注意如果时嵌套for循环,在内层循环遇到了break,只会跳出当前这一层内循环

continue:跳出本轮循环,继续下一轮循环

continue后本轮循环体中的不会继续执行,但是会继续执行下轮循环,循环体外的也会执行

retrun:结束当前的方法,直接返回

  1. 外层循环控制的是执行的轮数,内层循环控制的是这一轮中执行的次数

  2. 外层循环控制的是图形的行数,内层循环控制的是这一行的列数

  3. 案例中的经验:

如果把内层循环循环变量的最大值设置为一个固定值,打印出来的是矩形

while循环

while(判断条件){循环体 更改条件}

do while循环

do{ 更改条件} while(执行条件)

高效for循环

foreach(遍历的元素类型 遍历到的元素名字 : 要遍历的数组/集合名){循环体}

优点:写法简单,效率高

缺点:只能从头到尾的遍历数据,不能控制轮数

三种循环的区别

  1. for:明确指定循环的次数/需要设置虚幻变量的变化情况

  2. while和do while 当循环次数不确定时

  3. while:先判断,不符合规定,不会执行代码。死循环 while(true)

  4. do while:代码至少被执行一次,在去判断,符合规则,再次执行代码

  5. 循环之间都可以相互替代,带式一般最好选择合适的循环结构来完成代码

方法

被命名的代码块,方法可以含参数可以不含参数,可以提高代码的复用性。

重载,重写

重载Overload

方法的重载是指在一个类中定义多个同名的方法,但是每个方法的参数列表不同(也就是指参数的个数和类型不同),程序在调用方法时,可以通过传递给他们的不同个数和类型的参数来决定具体调用哪个方法.

1.在同一个类中

2.多个方法的方法名相同

3.同名方法的参数列表不同

重写Override

方法重写

  1. 继承以后,子类就拥有了父类的功能

  2. 在子类中,可以添加子类特有的功能,也可以修改父类的原有功能

  3. 子类中方法的签名与父类完全一样时,会发生覆盖/复写的现象

  4. 注意:父类的私有方法不能被重写

  5. 重写的要求:两同两小一大

两同方法名 参数列表要完全一致

两小:

子类返回值类型小于等于父类返回之类型

子类抛出异常小于等于父类方法抛出异常

一大

子类方法的修饰符权限要大于等于父类被重写方法的修饰符类型

重载overload与重写override的区别

重载:在一个类中的现象:同一个类中,存在方法名相同。参数列表不同的方法,与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分

重写:是指建立了继承关系以后,子类对父类的方法不满意,可以重写,遵循两同两小一大的原则

重载的意义,时为了外界调用方法是方便,不管传入什么样的参数,都可以匹配到对应的同名方法

重写的意义:在不修改源码的情况下,进行功能的修改与拓展

方法的传值:如果方法的参数类型是基本类型,传入的

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

数组

数组Array概念,

表示时[],用于储存多个相同类型数据的集合

想要获取数组中的元素值,可以通过下标来获取

数组下标从0 开始的,下标的最大值时数组的长度-1

创建数组

1动态初始化

int【】a = new int 【5】;

2.静态初始化

int【】b = new int【】{1,2,3,4,5}

int【】c = {1,2,3,4,5}

创建数组过程分析

  1. 在内存中开辟连续的空间,用来存放数据,

  2. .给数组完成初始化过程,给每个元素赋予默认值,int类型默认值时0

  3. .数组完成初始化会分配一个唯一的地址值

  4. 把唯一的地址值交给引用类型的变量啊去保存

  5. 数组名是个引用类型的变量,他保存着的时数组的地址,不是数组中的数据

数组的长度

数组的长度用length属性来表示,数组一旦创建,长度不可改变

数组的长度允许为0

数组的遍历

遍历:从头到尾,依次访问数组 每一个位置,获取每一个位置的元素,形式如下:

我们通过数组的下标操作数组,所以for循环变量操作的也是数组的下标

开始下标0 结束下标length-1 变化 + -

for(从下标为0的位置开始;下标的取值<=数组的长度-1;下标++)

创建随机数组

int a = new Random().nextInt(数组的长度);

Math.random()可以产生[0,1)的随机浮点数

数组工具类Arrays

  1. Arrays.toString:在打印语句输出, 用逗号连接成一个字符串

  2. Arrays.sort: 对数组进行排序,基本类型快速排序 效率高 对引用类型,是优化后的合并排序算法

  3. Arrays.copyof:把数据赋值给一个指定长度的新数组

数组的复制和扩容

System.arraycopy(要复制的数组, 要复制的起始下标, 要存放的数组, 要存放的起始下标, 要复制的元素个数);

Arrays.copyOf(原数组, 改变之后的长度); ---对数组进行扩容---本质上是在做数组的复制的过程,复制完成之后一定是产生了一个新的数组

copyofRange():用于完成时数组的截取

int[] to4 = Arrays.copyOfRange(from, 2, 4);

对象

面向对象和面向过程的区别

面向过程:

优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源,

缺点:没有面向对象易维护,易复用,易扩展

面向对象:

优点:易维护,易复用,易扩展,由于面向对象有封装,继承,多态性的特性,可以设计出地耦合的系统,使系统更加灵活,更加易于维护

缺点:性能比面向过程低

面向过程是具体化的,流程化的,解决一个问题,,需要一步步的分析,一步一步的实现

面向对象是抽象化的,模型化的,只需要抽象出一个类,就是一个封闭的盒子,在这里拥有数据也拥有解决问题的方法,需要什么功能直接使用就可以了,不必去一步步实现,至于这个功能是如何实现的,我们可以不用太关心。

面向对象的底层还是面向过程,把面向过程抽象成类,然后分装,方便我们使用的就是面向对象了

万物皆对象

java提供万能抽象的方式,把生活万物都可以抽象出来,就是Object对象

对象的结构非常复制,它就运行单个值支持,数组支持,不同的类型支持

对象过程:是一种思想,意思是我们要做看人我们要做任何事,都要亲历亲为强调是过程

面向对象是一种编程思想,相对于面向过程,我们由执行者变成指挥者,进而把生活中很多复杂的问题变得简单化

面对对象的编程思想(OOP Object Oriented Progrmming)

面向对象的三大特征

  1. 封装:把相关的数据封装成一个类的祖件

  2. 继承:是子类自动共享父类属性和方法,就是类之间的一种关系

  3. 多态:增强软件的灵活性和重用性

  1. java语言最基本的单位就是类,

  2. 类是一类事物的抽象

  3. 可以理解为模板或者设计图纸

对象

对象:三个特点,对象的状态,对象的行为,对象的标志

  1. 对象的状体用来描述对象的基本特征

  2. 对象的行为用来描述对象的功能

  3. 对象的标识是值对象在内存中都有一个唯一的地址值用来和其他对象区分开来

  4. 类是一类事物的抽象,对象是集体的实现

类和对象的关系

计算机语言通过属性+行为来显示世界中的事物

通过类来描述一类事物,用成员变量来描述事物的属性,用方法来描述事物的行为

对象在内存中的存储

java吧内存分为5个区域,我们重点关注栈和堆

  1. 局部变量存在栈中,方法执行完毕内存被释放

  2. 对象(new出来的东西)存在堆中,对象不在被使用时,内存才会被释放

  3. 每个堆内存的元素都有地址值

  4. 对象的属性都是有默认值的

1).TIPS:栈与队列指的是一种数据的结构

2).栈:先进后出(FILO -FIRST In Last Out)

3).队列:先进先出(FIFO - First In First Out)

创建对象时,内存究竟经历了什么

1,在栈内存中开辟一块空间,存放引用类型变量p,并把p压入栈底

2.在堆内存中开辟一块空间,存放phone对象

3,完成对象的初始化,并赋予默认值

4,给初始化完毕的对象赋予唯一的地址值

  1. 把地址值交给引用类型变量p来保存

访问控制符

;来控制一个类,或者类中的成员的访问范围

多态

同一个类,表现出不同的结果

概念:是面对对象程序设施的一个重要特征,只用一个实体同时具有多种形式,及同一个对象,在不用时刻,代表的对象不一样,值得是对象的多种形态

可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准呢

java实现多态有三个必要条件:继承、重写、向上转型。

多态特点

  1. 前提1:是继承+重写

  2. 前提2:为了忽略子类型之间的差异,统一看作父类类型,写出更加通用的海马

  3. 父类引用指向子类对象 :如 Animal a -= new cat(),夫类型的引用类型爆粗你的是子类对象的地址值

  4. 多态中,编译看左边,运行看右边

  5. 重写看实例对象,否则看引用

多态的好处

  1. 多态可以让我们不用关系莫格对象到底具体是什么类型,就可以使用该对象的某些方法

  2. 提高程序的可扩展性和可维护性

多态的使用

前提:多态对象把自己看作是父类类型

  1. 成员变量:使用的是父类的

  2. 成员方法:由于存在重写,所有使用的子类的

  3. 静态成员:随着类的加载而加载,税调用就返回谁的

向上造型和向下造型

1。这两种都属于多态,只不过是多态的两种不同的表现形式

向上造型(最常用)

可以把不同的子类型都看作是父类型,比如Parent p = new Child();

比如:花木兰替父从军,被看作是父类型,并且花木兰在从军的时候,不能使用自己的特有功能,比如化妆

也就是说不能有子类的属性

向下造型

前提:必须得先向上造型,才能向下造型

子类的引用指向子类的对象,但是这个子类对象之前被看作是父类类型,所以需要强制类型转换

Parent p = new Child(); 然后:Child c = (Child) p;

比如:花木兰已经替她爸打完仗了,想回家织布,那么这个时候,一直被看作是父类型的花木兰必须经历“解甲归田”【强制类型转换】这个过程,才能重新被看作成子类类型,使用子类的特有功能

为什么有向下造型:之前被看作是父类类型的子类对象,想使用子类的特有功能,那就需要向下造型

构造方法

概念

构造方法是一种特殊的方法,他是一个与类同名且与类同名没有返回值类型的方法

勾着方法的主要功能是完成对象创建和初始化

当类创建对象时,就是自动调用构造方法

构造方法与普通方法一样也可以重载

封装 encapsulation

时隐藏对象的属性和实现细节,仅仅对外提供公共的访问方式,比如类和方法

作用:包装,不能看到他的内部结构,也不能了解其内部的运行机制,封装后,好处使用起来简单,不需要指定他运行机制,照样使用。隐藏起来,让使用者更加方便简易,同时提供一些可以被外界访问属性的方法,通过封装可以使程序便于使用

  1. 提高安全性

  2. 提高重用性

private关键字

是一个权限修饰符,可以用来修饰成员变量和成员方法,被私有化的成员只能在本类中访问

具体实现

  1. 创建实体类entity

  2. 一堆的私有属性,外部不能直接访问private String name;

  3. 一堆get和set方法调用

  4. 一堆的业务方法

前提:为了保证数据的安全,也为了程序的使用者能够按照我们预先设计好的方式来使用资源

封装属性:用private修饰我们的属性

然后为属性提供对应的getXxx()【获取属性值】与setXxx()【设置属性值】

封装方法:用private修饰方法,被修饰的方法只能在本类中使用,所以我们在本类的公共方法里调用这个私有方法

外界如果想要使用这个私有方法的功能,只需要调用这个公共方法就可以了

继承 ingheritance

继承是使用已存在的类的定义作为基础,建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过继承可以提高代码复用性。继承是多态的前提。

关于继承如下 3 点请记住

  1. 子类拥有父类非 private 的属性和方法。

  2. 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

  3. 子类可以用自己的方式实现父类的方法。

java中继承和其他语言不同的,他提供两个方式

  1. 继承extends只能单继承,安全性高不易出错

  2. 实现implements可以实现多个接口,她的类也称为实现类

好处是可以减少相同的代码,但需要变化时只需要修改一处,所以调用的地方就够更新了

  1. 前提:继承可以实现程序的复用性,减少代码的冗余

  2. 我们通过extends关键字建立子类与父类的继承关系:格式:子类extends父类

  3. 继承相当于子类把父类的功能复制了一分,包括私有资源

注意

记忆方法

  1. 特点:方法名与类名相同,且没有返回值类型

  2. 构造方法作用:用于创建对象,每次new对象时,都会触发对应的构造函数,new几次,触发几次

  3. 一个类中默认存在无参构造,如果这个构造不被覆盖的话,我们可以不传参数,直接创建这个类的对象

  4. 如果这个类中提供了其他的构造函数,默认的无参构造会被覆盖,所以记得手动添加无参构

1.无参构造:默认存在,如果添加了其他构造,默认的构造函数会被覆盖

2.含参构造:对于参数没有任何要求,有参数就行

3.全参构造,全参构造的参数必须与本类属性一致

全参构造不仅可以创建对象

还可以给对象的所有属性赋值

构造代码块

{代码。。。}

特点

  1. 位置:在类的内部,在方法的外部

  2. 作用:用于抽取构造方法中的共性代码

  3. 执行实际:每次调用构造方法都用调用构造代码块

  4. 注意实行:构造代码块优先于构造方法加载

局部代码块

  1. 位置,在方法里面的代码块

  2. 作用:通常用于控制变量的作用范围,处理花括号就失效

  3. 注意事项:变量的作用范围越小越好,成员变量会存在线程的安全的文艺

this

this代表本类对象的一个引用对象

形式

super

我们可以把super看作时父类对象: Father super = new Father();

  1. 当父类的成员变量与子类的成员变量同名时,使用super是父类的成员变量

  2. 使用super在子类构造方法的第一行调用父类构造方法的功能

super():调用的时父类的无参构造

super(参数):调用的时父类对象的参数的构造方法

注意:在构造方法里,出现的调用位置必须时第一行

this和super的区别

this代表的是本类对象的引用,我们可以把this看作是Cat this = new Cat();

this可以实现调用本类构造方法的功能,不能互相调用,需要写在构造方法首行

当本类的成员变量与局部变量同名时,需要使用this.变量名指定本类的成员变量

this();表示调用本类的无参构造 this(参数);表示调用本类的对应参数的构造

super代表的是父类对象的引用,我们可以把super看作是Father super = new Father();

当本类的成员变量与父类的成员变量同名时,需要使用super.变量名指定父类的成员变量

super也可以实现调用父类构造方法的功能

super();表示调用父类的无参构造 super(参数);表示调用父类的对应参数的构造

注意:super的使用前提是继承,没有父子类关系,就没有super

注意:this调用构造方法或者super调用构造方法,都必须出现在构造方法的第一行

注意:如果父类没有无参构造,需要手动在子类构造方法的第一行调用其他的含参构造

拓展:如果子类重写了父类的方法以后,可以使用super.方法名(参数列表)来调用

对象创建的过程

前提:对象是根据类的设定来创建的,目前我们可以在类中添加很多的元素:

属性 方法 静态方法 构造代码块 静态代码块 局部代码块 构造方法…所以不限制类里具体写什么,取决于业务

对象创建的过程:Phone p = new Phone();

需要在堆内存中开辟一块空间,用来存放对象

对象需要完成初始化,比如对应的属性都有自己的对应类型的默认值

对象创建完毕后,会生成一个唯一的地址值用于区分不同的对象

将这个地址值交给引用类型变量来保存

后续如果想要使用这个类的功能,可以从引用类型变量中保存的地址值找到对应的对象做进一步的操作

匿名对象:new Phone();

匿名对象是没有名字的对象,所以创建过程:

需要在堆内存中开辟一块空间,用来存放对象

对象需要完成初始化,比如对应的属性都有自己的对应类型的默认值

对象创建完毕后,会生成一个唯一的地址值用于区分不同的对象

那么我们使用匿名对象只能使用一次,并且一次只能使用一个功能

new Phone().video();//创建匿名对象1,调用看直播的方法

new Phone().message();//创建匿名对象2,调用看发短信的方法

静态static

时java中的一个关键字

用于修饰成语变量和成员方法,即使没有创建对象,也能使用属性和调用方法

static关键字还有一个比较关键的作用就是用来形成静态代码块来优化程序性能,static快可以在类中的任何地方,类中可以有多个static快,在类初次被加载的时候,会按照static快的顺序来执行每一个static快,并且只会执行一次,因此,很多时候将一些只需要进行一次初始化的操作放在static代码块中

特点:

  1. static可以修饰属性和方法

  2. 被static修饰的资源称为静态资源

  3. 静态资源随着类的加载而加载,最先加载,优先于对象进行加载

  4. 静态资源可以通过类名直接调用

  5. 静态被全局所有对象共享,值只有一份

  6. 静态资源只能调用静态资源

  7. 静态区域内不允许使用this与super关键字

普通资源可以调用普通资源,也可以调用静态资源

静态资源只能调用静态资源

静态代码块格式

三种代码块的比较

静态代码块:在类加载时就加载,并且只被加载一次,最先加载到内存,优先于对象进行加载,直到类小消失,它才会消失

作用:一般用来加载那些只需要加载一次并且第一时间就需要加载资源,称作:初始化

构造代码块:在创建对象时会自动调用,每次创建对象都会被调用,提取构造共性

作用:用于提取所有构造方法的共性功能

局部代码块:方法里的代码块,限制变量的范围

作用:用于限制变量的作用范围,出了局部代码块就失效

代码块的执行顺序

静态代码块->构造代码块->构造方法->局部代码块

final

是java提供的一个关键字

final是最终的意思

final可以修饰类,方法,字段,属性

初衷:

java出现继承后,子了可以更变父类的功能。当父类功能不许子类海边是,可以利用fanal修饰父类

特点

  1. 修饰类:最终类,不可以被继承

  2. 修饰方法:这个方法的最终实现,不可以被重写

  3. 修饰常量:值不可以被更改,并且常量定义时必须赋值

  4. 常量的定义形式:fanal数据类型 常量名 = 值

  • final修饰的String类,代表了String类的不可被继承,final修饰的char[]代表了被存储的数据不可更改。String类一旦在常量池(节省资源,提高效率,因为如果已经存在这个常量便不会再创建,直接拿来用)被创建,是无法修改的,即便你在后面拼接一些其他字符,也会把新生成的字符串存到另外一个地址。但是,虽然final代表了不可变,但仅仅是引用地址不可变,并不代表了数组本身不会变

  • 线程安全,多线程下对资源进行写操作是有风险的,不可变对象不能被写,所以线程安全

  • 因为字符串是不可变的,所以在它创建的时候HashCode就被缓存了,不需要重新计算,这就是HashMap中的键往往都使用字符串的原因之一

a抽象类

java中可以定义被avstract关键字修饰的方法,这种方法只有生命,没有方法体,叫做抽象方法

java中可以定义被abstract关键字修饰的类,就做抽象类

如果一个类含有抽象方法,那么他一定是抽象类

抽象类中的方法实现交给子类来完成

格式

特点

抽象的关键字是abstract

被abstract修饰的方法是抽象方法,抽象方法没有方法体

如果一个类中出现了一个抽象方法,那么这个类必须被abstract修饰

关于抽象类的特点:

1)抽象类中的方法不做限制 : 全普 / 全抽 / 半普半抽

2)如果一个类中的方法都是普通方法,还要声明成抽象类,为什么?

为了不让外界创建本类的对象

3)抽象类不可以创建对象,所以常用于多态

4)抽象类中包含构造方法,但是不是为了自己创建对象时使用,而是为了子类的super()

5)抽象类中也是可以定义成员变量的

如果一个子类继承了一个抽象父类,有两种解决方案:

1)作为抽象子类:不实现/实现部分 抽象父类中的抽象方法 : ”躺平”

2)作为普通子类:实现抽象父类中的所有的抽象方法 : “父债子偿”

面向抽象进行编程:后天重构的结果

以下关键字,在抽象类中。用是可以用的,只是没有意义了。

1.private:被私有化后,子类无法重写,与abstract相违背。

2.static:静态优先于对象存在,存在加载顺序问题。

3.final:被final修饰后,无法重写,与abstract相违背。

接口

接口也是一种抽象类型,接口中的内容是抽象形成的需要实现的功能,接口更像是一种规则和一套标准

接口编写顺序

  1. 创建接口Inter--父类接口

  2. 创建接口的实现类InterImpl--子实现类

  3. 创建测试类进行测试

  1. 定义一个接口,描述人这一类实物以及共有功能

  2. 设计出共同的功能

  3. 创建接口实现类

  4. 按照角色的特点实现接口中的方法

  5. 创建实现类实现接口

  6. 创建测试类测试

总结

:接口里是没有构造方法的

在创建实现类的对象时默认的super(),是调用的默认Object的无参构造

接口里没有成员变量,都是常量。所以,你定义一个变量没有写修饰符时,默认会加上:public static final

特点

  1. 我们使用interface关键字定义接口

  2. 我们使用implements关键字建立接口实现类与接口的实现关系,接口父类,接口实现类是子类

  3. 接口实现如果实现部分/不实现接口的抽象方法,那么实现类是一个抽象类接口类,如果实现了接口的所有抽象方法,那么这个实现类是一个普通类

  4. 抽象类与接口都不可以实例化/创建对象

  5. 接口没有构造函数,实现类使用的super()是父类的无参构造。如果没有明确指定父类,super()代表的才是Object的无参构造

  6. 接口中都是静态常量,没有成员变量,因为会默认拼接,public static final

  7. 接口中都是抽象类,默认会拼接public abstract,但是有默认default方法和static方法有方法体

  8. 接口不是类!!!

  9. 接口是用来指定规则【有哪些功能?方法有参数吗?有返回值吗】方法具体的实现交给接口的实现类去完成

类与接口总结

相同点

  • 接口和抽象类都不能实例化

  • 都位于继承的顶端,用于被其他类实现或继承

  • 都包含抽象方法,其子类都必须重写这些抽象方法

区别

  1. 抽象类使用class关键字定义,是类,接口使用interface关键字定义,是接口

  2. 抽象类里可以定义成语变量,接口里没有成员变量,有的是静态常量

  3. 抽象类的方法不做限制,全普,全抽,半普半抽

接口中的方法都是抽象方法,默认会拼接,public abstract

4.抽象类有构造方法,不是为了自己使用,而是为了子类创建对象时使用

接口里没有构造方法,接口实现类调用的构造是父类的构造方法,与接口无关

5.接口可以多继承,也就是说,一个接口可以继承一个接口或者多个接口库

抽象类只能单继承,也就是说,一个子类只能有一个父类

6.抽象是后天重构的结果,接口时是先天设计的结果

7.抽象类中的方法可以使任意访问修饰符。接口默认的是pubilc,并且不允许定义为private或者protected

备注:Java8中接口中引入默认方法和静态方法,以此来减少抽象类和接口之间的差异。现在,我们可以为接口提供默认实现的方法了,并且不用强制子类来实现它。

接口和抽象类各有优缺点,在接口和抽象类的选择上,必须遵守下面的几个原则:

  • 抽象类用来定义某个领域的固有属性,即抽象类表示它是什么,接口用来定义某个领域的扩展功能,即接口表示它能做什么。

  • 当需要为子类提供公共的实现代码时,应优先考虑抽象类。因为抽象类中的非抽象方法可以被子类继承,使实现功能的代码更简洁。

  • 当注重代码的扩展性和可维护性时,应当优先采用接口。①接口与实现类之间可以不存在任何层次关系,接口可以实现毫不相关类的行为,比抽象类的使用更加方便灵活;②接口只关心对象之间的交互方法,而不关心对象所对应的具体类。接口是程序之间的一个协议,比抽象类的使用更安全、清晰。一般使用接口的情况更多。

内部类

特点

  1. 创建内部类对象的格式:外部类名 内部类型 对象名= 外部类对象。内部类对象

  2. 根据内部类所在位置的不同,分为:成员内部类(类里方法外)与局部内部类(方法里)

  3. 内部类可以直接使用外部类的资源,包括私有资源

  4. 外部类如果想要使用内部类的资源,必须先创建内部类的对象,然后通过内部类对象来调用内部类的资源

  5. 成员内部类被private修饰后,无法被外界直接创建对象使用,所有可以创建外部类对象, 通过外部类对象间接访问内部类的资源

  6. 静态资源访问时不需要创建对象,可以通过直接访问,访问静态类中的静态资源可以通过。。。。链式加载的方式访问

  7. 匿名内部类属于局部内部类,而且是没有名字的局部内部类,通常和匿名对象一起使用

成员内部类

位置:雷里方法外

被private修饰

被私有化的内部类在main()中是没有办法直接创建器对象的

可以在私有内部类所处的外部类中,创建一个公共的方法供外界调用,这个方法用来返回创建号的私有内部类对象

被static修饰

竟然内部类可以不创建外部类对象,直接创建竟然内部类对象,格式:OUTER3.iNNER3 OI = NEW OUTER3.INNER3

如果静态内部类中海油静态方法,那么我们可以不创建对象直接通过链式加载的方式调用:OUTER3.INNER3.SHOU()

局部内部类

位置:方法里

直接创建外部类对象,调用局部内部类所处的方法,并不会出发局部内部类的功能

需要在外部类中创建局部内部类的对象并且进行调用局部内部类的功能,才能触发内部类的功能

匿名内部类

位置L可运行代码中,例如:main()中

匿名内部类通常与匿名对象一起使用

格式: newINter(){这个大括号其实是一个匿名内部类,我来实现方法}。eat()

如果只是想使用一次接口或者抽象类的摸个功能,可以使用匿名内部类

匿名内部类+匿名对象的功能:创建实现类+实现方法+方法功能的一次调用(功能三合一)

异常

ArithmeticException :算数异常

InputMismatchException:输入不匹配

NullPointerException空指针异常

ArrayIndexOutBoundException数组下标越界异常

ClassCastException类型转换异常

ClassNotFoundException:类未找到异常,没导入jar

IOException(IO流异常

SQLException:数据库异常

MySqlSyntaxErrorRxception:数据库可能未找到异常

Unknown database mydb;数据库名称错误

NumberFormatException 数字转换异常

Access denied for user ‘root123’@‘localhost’ (using password: YES)数据库用户名和密码错误

Table ‘py-school-db.mydb’ doesn’t exist表不存在或者表名错误

NotSerizlizableException 没有实例化接口

StreamCorruptedException

InvalidOperationException 栈是空的异常

NoSuchElementException队列元素为空的情况下,remove()方法抛出的异常

java.lang.UnsupportedOperationException:合集不可以被修改

异常是一些用来封装错误信息的对象

它由异常的类型。提示信息。报错的行号提示三部分组成

处理方法

当程序遇到了异常,通常有两种处理方法,捕获或者向上抛出

当一个方法抛出异常,调用位置可以不做处理继续向上抛出,也可以捕获处理异常

捕获方式

抛出方式

对于不想现在处理或处理不了的异常可以选择向上抛出

方式:在方法上这是异常的抛出管道

子啊可能会发生异常的方式上添加代码“

throws异常类型

TIPS方法方法上有默认的异常管道RuntimeException

throw 和 throws 的区别?

throw是语句抛出一个异常

throws是方法可能抛出异常的声明

throw语句在方法体,表示抛出异常,有方法体内的语句处理

throws语句用在方法声明后面,表示在抛出异常,有该方法的调用者来处理

throw是具体向外抛出异常的动作,所以他是抛出一个异常实例

throw就是你吧这个倾向变成真实

throws说明你有那个可能,倾向

同时

  1. throws出现在方法函数头,而throw出现在函数体

  2. throws表示出现异常的一种可能性,并不一定发生这些异常

throw则是抛出了异常,执行throw则一定抛出了某种异常

  1. 两者都是消极处理异常的方式,只是抛出或者可能抛出异常,但是不会有函数去处理异常,真正的处理异常有函数的上层调用处理

Error 和 Exception 区别是什么?

Error 是程序正常运行中,不大可能出现的错误,通常为虚拟机相关错误,如系统崩溃,内存不足,堆栈溢出等。编译器不会对这类错误进行检测,应用程序也不应对这类错误进行捕获,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身是无法恢复的;

Exception 是程序正常运行中,可以预料的意外情况,通常遇到这种异常,应对其进行处理,使应用程序可以继续正常运行。

程序本身可以捕获并且可以处理的异常。Exception 这种异常又分为两类:运行时异常和编译时异常。

编译时异常

定义: Exception 中除 RuntimeException 及其子类之外的异常。

特点: Java 编译器会检查它。如果程序中出现此类异常,比如 ClassNotFoundException(没有找到指定的类异常),IOException(IO流异常),要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。在程序中,通常不会自定义该类异常,而是直接使用系统提供的异常类。

Java 的所有异常可以分为受检异常(checked exception)和非受检异常(unchecked exception)。

受检异常

编译器要求必须处理的异常。Exception 中除 RuntimeException 及其子类之外的异常都属于受检异常。编译器会检查此类异常,也就是说当编译器检查到应用中的某处可能会此类异常时,将会提示你处理本异常——要么使用try-catch捕获,要么使用方法签名中用 throws 关键字抛出,否则编译不通过。

非受检异常

编译器不会进行检查并且不要求必须处理的异常,也就说当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws抛出该异常,编译也会正常通过。该类异常包括运行时异常(RuntimeException及其子类)和错误(Error)。

JVM 是如何处理异常的?

在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。创建异常对象并转交给 JVM 的过程称为抛出异常。可能有一系列的方法调用,最终才进入抛出异常的方法,这一系列方法调用的有序列表叫做调用栈。

JVM 会顺着调用栈去查找看是否有可以处理异常的代码,如果有,则调用异常处理代码。如果 JVM 没有找到可以处理该异常的代码块,JVM 就会将该异常转交给默认的异常处理器(默认处理器为 JVM 的一部分),默认异常处理器打印出异常信息并终止应用程序。

从性能角度来审视一下 Java 的异常处理机制

  • try-catch 代码段会产生额外的性能开销,或者换个角度说,它往往会影响 JVM 对代码进行优化,所以建议仅捕获有必要的代码段,尽量不要一个大的 try 包住整段的代码;

  • 利用异常控制代码流程,也不是一个好主意,远比我们通常意义上的条件语句(if/else、switch)要低效;

  • Java 每实例化一个 Exception,都会对当时的堆栈进行快照,这是一个相对比较重的操作。如果发生的非常频繁,这个开销可就不能被忽略了

API

API(Application Programming Interface,应用程序接口)是一些预先定义的函数。目的是提供应用程序与开发人员基于某软件可以访问的一些功能集,但又无需访问源码或理解内部工作机制的细节.

API是一种通用功能集,有时公司会将API作为其公共开放系统,也就是公司制定自己的系统接口标准,当需要进行系统整合,自定义和程序应用等操作时,公司所有成员都可以通过该接口标准调用源代码.

Java.util包是java中的工具包,包含各种实用工具类/集合类/日期时间工具等各种常用工具包

import java.util.Scanner;

import java.util.Arrays;

java.lang包是java的核心,包含了java基础类

包括基本Object类/Class类/String类/基本数学类等最基本的类,这个包无需导入,默认会自动导入

import java.lang.Object;

import java.lang.String;

import java.lang.StringBuilder/StringBuffer;

正则表达式

包装类等等 –

Object

  1. Object是java中所有类的超类,java中的类都直接或者间接基础子Object

  2. 如果一个类没有明确的指定父类,那么这个类默认基础Object

  3. java。lang包是java的核心包,无需导包,会自动导入

hashCode()

作用是获取哈希吗,也称为散列码,他实际是返回int类型的整数,这个哈希吗的作用是确定该对象在哈希表的 索引,hashcode()定义在JDK的object类中,这就是意味java中的任何类都包含有hashcode()函数

散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)

默认实现:根据对象的地址值生成一个唯一的哈希码值

返回对象对应的哈希码值,,对象不同,哈希码值不同

默认实现:根据对象的地址值生成一个唯一的哈希码值

重写后:根据传入的属性值生成哈希吗

toSring()

:默认实现:打印对象的【类型@十六进制的哈希码值】

注意:我们要牢记Object中的默认实现方式,只要与默认实现不同,说明当前类就重写了Object中的实现

重写后打印字符串的具体内容(java自带的类)

equals()

:默认实现:比较两个对象的地址值,默认使用==比较

重写后是比较两个字符串的具体内容

equals()和hashCode的重写要至

综上:如果执行的效果与Object的默认效果不同,说明子类重写了该方法

int hashCode() 返回此字符串的哈希码。

boolean equals(Object anObject) 将此字符串与指定的对象比较,比较的是重写后的串的具体内容

String toString() 返回此对象本身(它已经是一个字符串!)。

int length() 返回此字符串的长度。

String toUpperCase() 所有字符都转换为大写。

String toLowerCase() 所有字符都转换为小写

boolean startsWith(String prefix) 测试此字符串是否以指定的元素开头。

boolean endsWith(String suffix) 测试此字符串是否以指定的字符串结束。

char charAt(int index) 返回指定索引/下标处的 char 值/字符

int indexOf(String str) 返回指定字符在此字符串中第一次出现处的索引。

int lastIndexOf(String str) 返回指定字符在此字符串中最后一次出现处的索引。

String concat(String str) 将指定字符串连接/拼接到此字符串的结尾,注意:不会改变原串

String[] split(String regex) 根据给定元素来分隔此字符串。

String trim() 返回去除首尾空格的字符串

byte[] getBytes() 把字符串存储到一个新的 byte 数组中

String substring(int beginIndex) 返回一个新子串,从指定下标处开始,包含指定下标

String substring(int beginIndex, int endIndex) 返回一个新子串,从执定下标开始,到结束下标为止,但不包含结束下标

static String valueOf(int i) 把int转成String

String

底层的结构是字符数组char【】

String的常用方法

int hashCode()返回此字符的哈希吗值

boolean equals(Object anObject)将字符串与指定的对象比较没比较的是重写后的串的具体内容

String toString()返回此对象本身

String s = ”abc“

注意存在堆中的常量池中,有高效的效果,如果是第二次创建,不会新建

注意:==比较的如果是引用类型,那么是地址值

注意:String重写了Object中的toString(),所以可以直接打印字符串的具体内容

String重写了Object中的equals(),所以比较的也是两个字符串的具体内容

char[ ] values ={"a","b","c"};

String s1 = new String对象,存在堆中

char[] values = {'a','b','c'};

String s1 = new String(values);

注意:每new一次,创建一个String对象,存在堆

String的创建机理是什么?什么是字符串常量池?

创建机理:由于String在Java世界中使用过于频繁,为了提高内存的使用率,避免开辟多块空间存储相同的字符串,引入了字符串常量池(字符串常量池位于堆内存中)。

其运行机制是:在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用。

StringBuilder与Stringbuffer

  1. 前提:String提供了丰富的操作字符串的方法,所有操作字符串是还使用String

  2. String拼接字符串使用+进行拼接,效果比较低,所有需要使用工具类

  3. 创建方法:StringBuffer sb = newStringBuffer();

  4. 常用方法:append()拼接效率高

StringBulider

特点:StringBulilder 是一个长度可变的字符串序列,在创建的时候,会有个长度为16的默认空间,当拼接字符串的时候,是在原对象的基础上进行拼接,如果长度不够就扩容

所有在StringBuilder 在创建之后,对应的操作一直是用一个对象

创建方式

StringBuilder sb = new StringBuilser() 创建一个长度为16的StringBuilder对象

StringBuilder sb = new StringBuilder(“abc”) 以制定字符串内容为“abc”的方式创建一个StringBuilder对象

优缺点:

有点:在拼接的时候,不会产生新的对象,就避免了因为拼接频繁生成对象的问题,提高了程序的效率,使用的append()

缺点:对应字符串的操作,不太方便

StringBuilder转String:

StringBuilder sb = new StringBuilder();

sb.append(“abc”);

String s = sb.toString();

总结一句话,拼接多用StringBuilder,用完转回String用String丰富的方法

在线段安全上

String中的对象是不可变的,也就可以理解为常量,线程安全。,private final char value[]

StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。

AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

在执行效率上,StringBuilder>StringBuffer>String

使用场景

在字符串内容不经常发生变化的业务场景,优先使用String类,例如常量声明、少量的字符串拼接操作等。

在单线程环境下,频繁地进行字符串的操作,建议使用StringBuilder,例如SQL语句拼装、JSON封装等。

在多线程环境下,频繁地进行字符串的操作,建议使用StringBuffer,例如XML解析、HTTP参数解析与封装。

正则表达式

作用: 拿着我们指定好的规则,去判断数据是否符合这个规则

  1. 指定规则:String regex = “【0-9】{17}{0-9X}”

  2. 拿出数据与规则做比较:input。matches(regex)-->如果匹配,matches方法返回true

注意:单个斜杠表示转义字符,所有在正则中如果想要表示单个斜杠,需要写双斜杠,至于正则表达式的对照关系。

IO流

IO阶段的学习方法:学习父类的公共方法,学习子类对象的创建方式

总结:

流总共分为四种流:字节输入流(InputStream),字节输出流(OutputStream),字符输入流(Reader)和字符输出流(Writer),具体延伸如下:

FileReader:文件输入流

FileWriter:文件输出流

FileInputStream:文件字节输入流

FileOutputStream: 文件字节输出流

BufferedInputStream:字节输入缓冲流

BufferedOutputStream: 字节输出缓冲流

BufferedReader: 字符输入缓冲流

BufferedWriter: 字符输出缓冲流

InputStreamReader:字节字符输入转换流

OutputStreamWriter:字节字符输出转换流

流的分类

按照方向:输入流 输出流(从程序写数据到文件中是输出)

按照操作单位;字节流,字符流

按照流的角色划分,可以分为节点流和处理流

字节输入流

InputStream抽象父类类,不能new,可以作为超类,学习其所提供的共性方法

FileInputStream--操作文件的字节输入流,子类,普通类

构造方法参数:FileInputSteam:File file/String pathname

FileInputSteam in = new FileInputSteam(Pathname);

BufferedInputSteam==高效字节输入流,子类,普通类

构造方法:InputStream,但无法创建抽象父类对象,所有转的是FileInputStream

BufferedInputStream IN = new BufferedInputSteam(FileInputSream(ptanname))

FIS in = new FIS(new File(路径));

FIS in = new FIS(路径);

BIS in = new BIS( new FIS(new File(路径)));

BIS in = new BIS(new FIS(路径));

字节输出流

OutputStream抽象类父类不能new可以作为超类,学习其所提供的共性方法

FileOutputStream--操作文件的字节输入流,子类,普通类

构造方法参数:FileOutputStream:File file/String pathname

FileOutputStream in = newFileOutputStreamPathname);

BufferedOutputSteam==高效字节输入流,子类,普通类

构造方法:OutpurStream,但无法创建抽象父类对象,所有转的是FileOutputStream

BufferedOutputSteam IN = newBufferedOutputSteam(FileOutputStream(ptanname))

注意:默认存在一个参数Boolean append,默认值是false,也就是覆盖输出

如果将FileOutStream构造结构函数的第2个参数appned设置为true,就会实现追加输出的效果

FOS out = new FOS(new File(路径));

FOS out = new FOS(路径);

BOS out = new BOS(new FOS(new File(路径)));

BOS out = new BOS(new FOS(路径));

字符输入流

Reader抽象类父类不能new,可以作为超类,学习其所提供的共性方法

FileIReader--操作文件的字符输入流,子类,普通类

构造方法参数:FileIReader:File file/String pathname

FileIReader in = newFileIReader(Pathname);

BufferedReader==高效字符输入流,子类,普通类

构造方法:Reader,但无法创建抽象父类对象,所有转的是FileIReader

BufferedReader IN = newBufferedReader(FileIReader(ptanname))

FR in = new FR(new File(路径));

FR in = new FR(路径);

BR in = new BR(new FR(new File(路径)))

BR in = new BR(new FR(路径));

字符输出流

Writer抽象类父类不能new,可以作为超类,学习其所提供的共性方法

FileWriter--操作文件的字节输入流,子类,普通类

构造方法参数:FileWriter:File file/String pathname

FileWriter in = newFileWriter(Pathname);

BufferedWriter==高效字节输入流,子类,普通类

构造方法:Writer,但无法创建抽象父类对象,所有转的是FileWriter

BufferedWriter IN = new BufferedWriter(FileWriter(ptanname))

注意:默认存在一个参数Boolean append,默认值是false,也就是覆盖输出

如果将FileWriter构造结构函数的第2个参数appned设置为true,就会实现追加输出的效果

FW out = new FW(File/File,append/String pathname/String pathname,append);

BW out = new BW(Writer--所以传的是子类FW(上面那4种));

注意:这里的append参数表示流向文件输出数据的时候是追加还是覆盖,如果不写,默认false是覆盖,如果改为true,表示追加

序列化和反序列化

序列化:ObjectOutputStream

序列化是值将对象的状态信息转化为可以存储或者传输形式的过程,在序列化期间,对象将其当前状态写入到临时或者持久性存储取,以后可以通过从存储区中的企业或者反序列化对象的状态,重新创建该对象

序列化:利用ObjectOutputStream,把对象的i信息,按照固定的格式转成一串字节值输出并持久保存到磁盘

  1. 将程序中对象的对象信息,序列化输出到文件中保存

  2. 方向是Out,使用的流是ObjectOutputStream

  3. 使用的方法撒是out。writeObject(目标对象)

  4. 注意:如果一个类的对象想要被初始化,那么这个类必须实现Serizlizable接口

对用对象的输出writeObject(),保存对象的各项信息

反序列化:ObjectInputStream

反序列化:利用ObjectInputStream,读取磁盘中之前序列化好的数据,重新恢复成对象、

  1. 将之前输出到文件中的数据,读取会程序中,并把督导的数据重新恢复成对象

  2. 方向是in使用的流是ObjectInputStream

  3. 使用的方法是in。readObject()

  4. 注意L反序列化指定的文件路径,必须与序列化输出的文件路径一样

  5. 注意:一次序列化操作对应反序列化操作,或者UID必须保持一致,如果不一致,会报错

用于对象的独缺readObje(),把之前序列化输出的数据,在程序中重新恢复对象

注意:

  1. 如果一个类的对象想要被序列化,那么这个类必须实现可序列化接口

  2. 必须先序列化,在反序列化,并且一次序列化对应一次反序列化

  3. 反序列化时的文件路径必须和序列化时的文件路径一致

  4. 反序列化时持有的UID必须与序列化目标类Student中的UID一样,否则报错

特点:

  1. 需要序列化的文件必须实现Serializable接口,用来启用序列化功能

  2. 不需要序列化的数据可以修饰成static,原因:static资源属于类资源,不随着对象被序列化输出

  3. 每一个被序列化的文件都有一个唯一id,如果没有增添id,编译器会自动根据类的定义信息计算产生一个

  4. 在饭序列化是,如果有序列化的版本号不一样,无法完成反序列化

  5. 常用于服务器之间的数据传输,序列化成文件,反序列化读取树蕨

  6. 常用使用套接子列在主机之间传递对象

  7. 不需要序列化的数据也可以被修饰成transient(临时的),旨在程序运行期间在内存中存在,不会被序列化持久保持

集合

什么是集合,集合和数组的区别

集合:用于存储数据的容器。

集合和数组的区别

  • 数组是固定长度的;集合是可变长度的。

  • 数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。

  • 数组是Java语言中内置的数据类型,是线性排列的,执行效率和类型检查都比集合快,集合提供了众多的属性和方法,方便操作。

联系:通过集合的toArray()方法可以将集合转换为数组,通过Arrays.asList()方法可以将数组转换为集合

@$List,Set,Map三者的区别

Java 集合容器分为 Collection 和 Map 两大类,Collection集合的子接口有Set、List、Queue,Map接口不是collection的子接口。

Collection集合主要有List、Set和Queue接口

Map

集合的迭代:

Iterator iterator() 返回本集合的迭代器

常有的集合类有List集合,Set集合。Map集合,其中List集合与Set集合基础了Collection接口,各个接口还提供了不同的实现类-

Collection

java语言的java。utiil包中提供了一些集合类,这些集合类有陈志伟容器

集合类与数组最主要的不同处是,数组的长度是固定的集合的长度是可变的

而数组的访问方式比较单子,插入,删除等操作比较繁琐,二集合的访问方式比较灵活

集合的英文名称Collection是用来存放对象的数据结构,而且长度可变,可以存放不同类型的对象,并且还提供了一组操作成批的对象,Collection接口层次结构中的跟借口,接口不能直接使用,但是该接口提供了增添元素。删除元素。管理元素的父类接口公共方法

由于List接口Set接口都继承了Collection接口,因此这些方法对于List集合和Set集合是通用的

1,Collection是集合层次中的跟接口

2.集合的继承关系

3。是集合 层次的跟接口,学习抽象父类的公共方法

1。泛型

泛型通常与集合一起使用,用来约束集合中元素的类型

泛型<type>必须是引用类型而不是基本类型

泛型方法: public static == <E> == void get(E[] e){},两处位置都出现泛型,缺一不可

2.集合被collection, 是一个可以存放对个数据的容器,而且集合中提供了丰富的方法来操作集合中的元素

是集合层次的根接口,学习抽象父类的公共方法

单个集合的操作

boolean add(E e) 将指定元素添加到集合中

void clear() 清空集合

boolean contains(Object o) 判断本集合是否包含指定的元素

boolean equals(Object o) 比较集合对象与参数对象o是否相等

int hashCode() 返回本集合的哈希码值。

boolean isEmpty() 判断本集合是否为空

boolean remove(Object o) 从本集合中移除指定元素o

int size() 返回本集合中元素的个数

Object[] toArray() 将本集合转为数组

集合间的操作:

boolean addAll(Collection<> c) 将c集合中的所有元素添加到本集合中

boolean containsAll(Collection<> c) 判断本集合是否包含c集合的所有元素

boolean removeAll(Collection<> c) 移除本集合中属于参数集合c的所有元素

boolean retainAll(Collection<> c) 保留本集合与参数集合c的公共元素

  • List:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。

  • Set:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。常用实现类有 HashSet、LinkedHashSet 以及 TreeSet。

  • Queue/Deque,则是 Java 提供的标准队列结构的实现,除了集合的基本功能,它还支持类似先入先出(FIFO, First-in-First-Out)或者后入先出(LIFO,Last-In-First-Out)等特定行为。常用实现类有ArrayDeque、ArrayBlockingQueue、LinkedBlockingDeque

    序,允许重复。Map没有继承Collection接口,从Map集合中检索元素时,只要给出键对象,就能返回对应的值对象。

    常用实现类有HashMap、LinkedHashMap、ConcurrentHashMap、TreeMap、HashTable

    集合框架底层数据结构

    Collection

  • List

  • Arraylist:Object数组

  • LinkedList:双向循环链表

  • Vector:Object数组

  • Set

  • HashSet(无序,唯一):基于 HashMap 实现,底层采用 HashMap 的key来保存元素

  • LinkedHashSet:LinkedHashSet 继承于 HashSet,并且其内部是通过 LinkedHashMap 来实现的。

  • TreeSet(有序,唯一):红黑树(自平衡的排序二叉树)

  • HashMap:JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8),但是数组长度小于64时会首先进行扩容,否则会将链表转化为红黑树,以减少搜索时间

  • LinkedHashMap:LinkedHashMap 继承自 HashMap,它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得LinkedHashMap可以保持键值对的插入顺序。

  • HashTable:数组+链表组成的,数组是 HashTable 的主体,链表则是主要为了解决哈希冲突而存在的

  • TreeMap:红黑树(自平衡的排序二叉树)

List接口

特点:

list集合是由下标的

list集合是有顺序的

list集合可以存放重复的数

list的集合方法总结

单个集合间的操作

void add(int index, E element) 在集合的指定下标index处插入指定元素element

E get(int index) 返回本集合中指定下标index处的元素

E remove(int index) 移除本集合中指定下标index处的元素

E set(int index, E element) 用参数元素element替换集合中指定下标index处的元素

int indexOf(Object o) 判断指定元素o在本集合中第一次出现的下标,如果不存在,返回-1

int lastIndexOf(Object o) 判断指定元素o在本集合中最后一次出现的下标,如果不存在,返回-1

List subList(int fromIndex, int toIndex) 截取子集合,包含formidex处的元素,不包含toIndex处的元素

集合间的操作与集合的迭代

boolean addAll(int index, Collection<> c) 将参数集合c中的所有元素,插入到本集合中指定的下标index处

ListIterator listIterator() 返回此列表元素的迭代器,这个是List自己的,不太常用,可以逆序迭

【数据有下标,有序,可重复】

ArrayList子类

LinkedList子类

1.List接口的实现类

2.底层的结构是链表,内存空间是不连续的

3.元素有下标,有序,允许重复的数据

4.通常进行的是首位节点相关的操作

5.增删操作快,查询操作比较慢(数据量比较大的时候)

ArrayList底层是数组结构,查询快,增删慢,适合查询多的场景

LinkedList底层是链式表结构,查询慢,增删快,适合增删操作较多的场景

注意:LinkedList查询慢是指数据量大的时候,查询中要慢,首位操作还是比较快的

boolean offer(E e) 添加尾元素

boolean offerFirst(E e) 添加首元素

boolean offerLast(E e) 添加尾元素

E peek() 获取首元素

E peekFirst() 获取首元素

E peekLast() 获取尾元素

E poll() 返回并移除头元素

E pollFirst() 返回并移除头元素

E pollLast() 返回并移除尾元素

set接口

特点

1,数据无序且数据不允许重复

2.HashSet:底层是哈希表,包装了HashMap,相当向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中,当然K仍然不许重复

3.TreeSet:底层是TreeMap,也是红黑树的形式,便于查找数据

HashSet

底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K存入内部的HashMap中,其中K不允许重复,允许使用null.

map接口

步骤

java.util接口Map<K,V>

类型参数:k-表示此映射所维护的键V-表示此映射所维护的对应的值

也叫哈希表,散列表,常用于键值对结构的数据,其中键不能重复,值可以重复

特点:

TreeSet

有序(根据comparato来比较大小进行排序,所以里面的元素要实现comparable接口)

利用TreeSet保存自定义类对象的时候,自定义所在的类一定要实现Comparable接口,如果没有实现这个接口那么就无法区分大小关系,而且在TreeSet中如果要进行排序,那么就要将所有的字段都进行比较,就是说在TreeSet中是依靠comparato()方法返回的是不是0来判断是不是重复元素的。

  • List接口的实现类

  • 底层的结构是数组,内存空间是连续的

  • 元素有下标,有序,允许重复的数据

  • 通常进行的是根据下标进行操作

  • 增删操作慢,查询操作比较快(数据量比较大的时候)

  • Set是一个不包含重读数据的Collection

  • Set结合的数据是无序的(因为Set结合没有下标

  • Set集合中的元素不可以重复,常用来给数据去重

  • 创建对应的集合对象

  • 存入数据

  • 常用的方法测试

  • 创建set2集合,并向集合中存入数据

  • 集合的迭代

  • 创建map对象

  • 向map存入数据

  • 进行方法测试

  • 迭代器:map本身没有迭代器,要转化成set集合。Set<key>:把map中的所有key值存入到set集合当中---keySet()

  • 将map集合中的key值去除存入set集合中,集合的泛型就是key的类型

  • 想要遍历就需要获取集合的迭代器

  • 循环迭代中的所有元素

  • 遍历map集合,需要把map集合先转成set集合

  • 是把map中的一堆键值对key&valur作为一个Entry《k,y》整体放入set

  • 一堆k,v就是一个entry

  • map可以根据键来提取对应的值

  • map的键不允许重复,如果重复,对应的值会被覆盖

  • map存放的都是无序的数据

  • map的初始容量是16,默认的加载因子是 0.75

void clear() 从此映射中移除所有映射关系(可选操作)

boolean containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true

boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true

Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图

boolean equals(Object o) 比较指定的对象与此映射是否相等

V get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null

int hashCode() 返回此映射的哈希码值

boolean isEmpty() 如果此映射未包含键-值映射关系,则返回 true

Set keySet() 返回此映射中包含的键的 Set 视图

V put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)

void putAll(Map<? extends K,? extends V> m)从指定映射中将所有映射关系复制到此映射中(可选操作)

V remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)

int size() 返回此映射中的键-值映射关系数

Collection values() 返回此映射中包含的值的 Collection 视图

hashMap

HashMap的键要同时重现hashCode()和equlas()

hashCode()用来判定二者的hash值是否相同,重写后根据属性生成

equlas()用来判断属性的值是都相同,重写后,根据属性判断

–equlas()判断数据如果相等,hashCode()必须相同

–equlas()判断数据如果不等,hashCode()尽量不同

HashMap 内部实现是通过 key 的 hashcode 来确定 value 的存储位置,因为字符串是不可变的,所以当创建字符串时,它的 hashcode 被缓存下来,不需要再次计算,所以相比于其他对象更快

存储过程:

hashmap的结构是数组+链表或者数组加红黑树的形式

hashmap底层的Entry【】数组,初始容量为16,加载因子是0.75f,扩容按约为2倍的扩容

解决的方法就是采用链表的结构,在数组照片那个指定个位置处以后元素之后插入新的元素也就是说数组中的元素都是最早加入的节点

阻塞的情况分三种:

线程调度算法

线程调度是指按照特定机制为多个线程分配 CPU 的使用权。Java 虚拟机的一项任务就是负责线程的调度。

有两种调度模型:分时调度模型和抢占式调度模型。Java虚拟机采用抢占式调度模型。

分时调度模型:让所有的线程轮流获得 cpu 的使用权,平均分配每个线程占用的 CPU 的时间片。

抢占式调度模型:根据线程优先级、线程饥饿情况等数据算出一个总的优先级,优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。

线程同步以及线程调度相关

wait()和sleep()的区别

两者都可以暂停线程的执行,调用方法时都会抛出 InterruptedException 异常

进程和线程的区别

线程具有许多传统进行锁具有的特征,有称为轻型进程或进程元,而把传统的进程称为重型进程,在引入线程的操作系统中,通常一个进程都有若干个线程,至少包括一个线程

根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位

资源开销和内存分配:每个进程都有独立的代码和数据空间,进程之间的切换回有较大的开销,线程可以开左轻量级的进程,同一线程共享代码和数据空间,每个线程都有之间独立的运行栈和程序计数器,线程之间切换的开销小

包含关系:线程是进程的一部分,一个进程至少有一个线程,一个进程可以运行多个线程

影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉,所有多进程要比多线程健壮

执行过程:每个独立的继承有程序入口,顺序执行序列和程序出口,必须依存在进程中,有进程提供多个线程执行空值,两者均可并发执行

上下文切换

当前任务在执行完CPU时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务是,可以加载这个任务的状态,任务从保存到再加载的过程就是一次上下文切换

用户线程

运行在前台,执行具体的任务,如程序的主线程,连接网络的子线程等都是用户线程

守护线程

:运行在后台,为其他前台线程服务,也可以说守护线程是JVM中非线程的“用人”一旦所有用户线程都结束运行,守护线程随JVM一起结束工作

main函数所在的线程就是用户线程,main函数启动的同时在JVM内部同时还启动了好多守护线程,比如垃圾回收线程

区别

用户线程结束后,JVM退出,不管这个时候也没有守护线程运行,而守护线程不会影响JVM的退出

注意事项:

多线程的安全问题

利用synchronized同步代码块解决多线程并发安全问题

锁对象 --- 要求锁对象要被所有的线程都认识:共享对象,类的字节码(方法区是被线程所共享的),this(必须是同一个对象开启了多个线程)

出现安全问题的原因

解决办法:

原子性:一个或多个操作要么全部执行成功要么全部执行失败

可见性:一个线程对共享变量的修改,另一个线程能够立即看到

有序性:程序执行的顺序按照代码的先后顺序执行,避免指令重拍

串行:并行:并发

做一个形象的比喻:

串行 = 一个队列和一台咖啡机。

并发 = 两个队列和一台咖啡机。

并行 = 两个队列和两台咖啡机

串行与并行

串行是指同一时刻一个CPU只能处理一件事,类似于单车道

并行是指同一时刻多个CPU可以处理多件事,类型于多车道

并发

优点:充分利用多核cpu的计算能力:通过并发编程的形式可以将多核cpu的计算能力发挥到极致,性能得到提升

方便进行业务拆分,提升系统并发能力和性能

缺点:

并发编程的目的就是为了能提高程序的执行效率,提高程序运行速度,但是并发编程并不总是能提高程序运行速度的,而且并发编程可能会遇到很多问题,比如:内存泄漏、上下文切换、线程安全、死锁等问题。

并发三要素

  • 如果链表的长度>8时,链表会转为红黑树,当链表的长度<6时,会重新恢复成链表

    • 当存放数据是,会根基hash(key)%n算法来计算数据的存放位置,n就是数组的长度,其实也就是集合的容量

    • 会计算到的位置没有存过数据的时候,会直接存放数据

    • 当计算的位置,有数据时,会发生hach冲突和碰撞

    • 线程与进程的关系

      进程

      进程 --- 计算机中在执行的任务 --- 在CPU上执行和计算。

      一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。

      线程

      线程 --- 进程中的小任务 --- 多线程

      进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据

      线程是操作系统OS能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.

      一个进程可以开启多个线程,其中有一个主线程来调用本进程中的其他线程。

      我们看到的进程的切换,切换的也是不同进程的主线程

      多线程可以让同一个进程同时并发处理多个任务,相当于扩展了进程的功能。

    • 新建(new):新创建了一个线程对象。

    • 可运行(runnable):线程对象创建后,当调用线程对象的 start()方法,该线程处于就绪状态,等待被线程调度选中,获取cpu的使用权。

    • 运行(running):可运行状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

    • 阻塞(block):处于运行状态中的线程由于某种原因,暂时放弃对 CPU的使用权,停止执行,此时进入阻塞状态,直到其再次进入到就绪状态,才有机会再次被 CPU 调用以进入到运行状态。

  • . 等待阻塞:运行状态中的线程执行 wait()方法,JVM会把该线程放入等待队列(waitting queue)中,使本线程进入到等待阻塞状态;

  • 同步阻塞:线程在获取 synchronized 同步锁失败(因为锁被其它线程所占用),则JVM会把该线程放入锁池(lock pool)中,线程会进入同步阻塞状态;

  • 其他阻塞:通过调用线程的 sleep()或 join()或发出了 I/O 请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。

  • 死亡(dead):线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期,死亡的线程不可再次复生。

  • 类的不同:sleep() 是 Thread线程类的静态方法,wait() 是 Object类的方法。

  • 是否释放锁:sleep() 不释放锁;wait() 释放锁。

  • 用途不同:wait() 方法通常被用于线程间交互/通信,sleep() 通常被用于暂停线程执行。

  • 用法不同:wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。或者可以使用wait(long timeout)超时后线程会自动苏醒。sleep() 方法执行完成后,线程会自动苏醒。

  • setDaemon(true)必须在start()方法前执行,否则会抛出iiiefalThreadStateException异常

  • 在守护线程中产生的新线程也是守护线程

  • 不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑

  • 守护线程中不能依靠finally快来确保执行关闭或清理资源的逻辑,因为一旦所有用户线程都结束运行,守护线程就随着JVM一起结束工作,所以守护线程中的finally语句可以无法被执行

  • 线程切换带来的原子性问题

  • 缓存导致的可见性

  • 编译优化带来的有序性问题

  • JDK Atomic开头的元子类,synchronized,lock可以解决原子性问题

  • volatile,synchronized,lock,可以解决可见性问题

  • volatile,Happens-Before规则可以解决有序性问题

  • 串行:多个任务在一个线程上按顺序执行。由于任务都在一个线程执行所以不存在线程不安全情况,也就不存在临界区的问题。

  • 并发:多个任务在一个 CPU 核上按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。

  • 并行:单位时间内,多个 CPU 同时处理多个任务,是真正意义上的“同时进行”。

线程生命周期,主要有五种状态:

1.新建状态(New) : 当线程对象创建后就进入了新建状态.如:Thread t = new MyThread();

2.就绪状态(Runnable):当调用线程对象的start()方法,线程即为进入就绪状态.

处于就绪(可运行)状态的线程,只是说明线程已经做好准备,随时等待CPU调度执行,并不是执行了t.start()此线程立即就会执行

3.运行状态(Running):当CPU调度了处于就绪状态的线程时,此线程才是真正的执行,即进入到运行状态

就绪状态是进入运行状态的唯一入口,也就是线程想要进入运行状态状态执行,先得处于就绪状态

4.阻塞状态(Blocked):处于运状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会被CPU选中再次执行.

根据

继承Thread

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例

启动线程的唯一方法就是通过Thread类的start()实例方法

start()方法是一native方法,它将通知底层操作系统,.最终由操作系统启动一个新线程,操作系统将执行run()

这种方式实现的多线程很简单,通过自己的类直接extends Thread,并重写run()方法,就可以自动启动新线程并执行自己定义的run()方法

模拟开启多个线程,每个线程调用run()方法.

多线程实现方案1:继承Thread类

  1. 自定义一个类,继承Thread类

  2. 在自定义类中重写父类Thread中的run(),这个run()放着的是我们自定义业务

  3. 创建自定义线程类对象--对应的是线程的新建状态

  4. 调用start()一多线程的方式多个线程对象加入就绪队列中,等待os选中

我们每通过class关键字创建一个类,就会在工作空间中生成唯一对应的类名。class字节码

这个类名。class对应的对象,我们称之为这个类的字节码对象

字节码对象极其重要,是反射技术的基石,字节码对象中包含了当前类所有的关键信息

所有,用这样一盒唯一且明确的对象作为同步代码块的锁对象,再合适不过了

构造方法

Thread() 分配新的Thread对象

Thread(String name) 分配新的Thread对象

Thread(Runnable target) 分配新的Thread对象

Thread(Runnable target,String name) 分配新的Thread对象

普通方法

static Thread currentThread( )

返回对当前正在执行的线程对象的引用

long getId()

返回该线程的标识

String getName()

返回该线程的名称

void run()

如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法

static void sleep(long millions)

在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)

void start()

实现Runnable接口

5.1 概述

如果自己的类已经extends另一个类,就无法多继承,此时,可以实现一个Runnable接口

5.2 常用方法

void run()使用实现接口Runnable的对象创建线程时,启动该线程将导致在独立执行的线程中调用对象的run()方法

多线程实现方案2:实现Runnable接口

  1. 自定义多线程类,并实现Runnable接口

  2. 添加父接口中未实现的run(),run()里放着的是我们的业务

  3. 创建自定义类的对象,只创建一次,作为业务的对象存在

  4. 创建多个Thread线程类对象,并将业务对象交给线程对象来完成

  5. 以多线程的方式启动多个线程对象

自定义类与父类Runnable中都没有获取名字的方法

所以还需要从Thread类里找

currentThread():静态方法,获取当前正在执行的线程对象

getname():获取当前正在执行的线程对象的名称

Callable

返回结果并且可能抛出异常的任务

优点

1.可以获得任务执行返回值

2.通过与Future的结合,可以实现利用Future来跟踪异步计算的结果

运行Callable任务可以拿到一个Future对象,表示异步计算的结果,他提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果,通过Future对象可以了解任务执行情况,可以取消任务的执行,还可获取执行结果

callable两种执行方式

  • 借助FutureTask执行FutureTask类同时实现了两个接口,Future和Runnable接口,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

  • 借助线程池来运行线程池中执行Callable任务原型

runnable和callable

相同点:

  1. 都是接口

  2. 都可以编写多线程程序

  3. 都采用Tread。start()启动线程

区别:

runnable的run()没有返回值

callable的call()有返回值,是个泛型,和Future。FutureTask配合可以用来获取异步执行的结果

runnable接口run()只能抛出异常,且无法捕获处理,

callable接口call()允许抛出异常,可以获取异常信息

Future接口

是一个接口,代表一个异步计算的结果,接口中的方法用来检查计算是否完成,等待完成和得到计算的结果

当计算完成后,只能通过get()方法得到结果,get方法会阻塞直到结果准备好了

如果想取消,那么调用cancel()方法,其他方法用于确定任务是征程完成还是取消了

一旦计算完成了,那么这个计算就不能被取消;

Future Task类

  • FutureTask类实现了RunnableFuture接口,而RunnnableFuture接口继承了Runnable和Future接口,所以说FutureTask是一个提供异步计算的结果的任务。

  • FutureTask可以用来包装Callable或者Runnbale对象。因为FutureTask实现了Runnable接口,所以FutureTask也可以被提交给Executor(如上面例子那样)

什么是 Future 和 FutureTask?

Future 接口表示异步计算的任务,他提供了判断任务是否完成,中断任务,并可以通过get方法获取任务执行结果,该方法会阻塞直到任务返回结果。因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。

FutureTask 类间接实现了 Future 接口,可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

线程池ExecutorService/Executors

是用来辅助创建线程池的工具类

常用方法:new FixedThreadPool(int)这个方法可以创建指定数目的线程的线程池对象

创建出来的线程池对象就是ExecuorServise,负责:新建/启动/销毁 线程

execute()让线程池中的线程来执行业务,每次调用都会将一个线程加入就绪对

pool.execute(targer);本方法的参数就是你要执行的业务,也就是目标业务类对象

newThreadPool:创建线程池

newSingleTreadExcutor:创建一个单例线程池,而且里面最多只能有一个线程

newFixedThreadPool:创建一个固定长度的线程池

newCachedThreadPool:创建一个带有缓存的线程池

newScheduledThreadPool:创建一个固定长度的线程池,可以延迟或定时执行线程任务

ExecutorService/Executors

ExecutorService:用来存储线程的池子,把新建线程/启动线程/关闭线程的任务都交给池来管理

execute(Runnable任务对象)把任务丢到线程池

Executors辅助穿件线程池的工具类

线程池的实现原理

1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。

2、线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。

3、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

线程池的参数

从源码中可以看出,线程池的构造函数有7个参数,分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。下面会对这7个参数一一解释。

一、corePoolSize 线程池核心线程大小

线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。

二、maximumPoolSize 线程池最大线程数量

一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。

三、keepAliveTime 空闲线程存活时间

一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定

四、unit 空闲线程存活时间单位

keepAliveTime的计量单位

五、workQueue 工作队列

新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:

①ArrayBlockingQueue

基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。

②LinkedBlockingQuene

基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。

③SynchronousQuene

一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。

④PriorityBlockingQueue

具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

六、threadFactory 线程工厂

创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等

七、handler 拒绝策略

当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:

①CallerRunsPolicy

该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。

②AbortPolicy

该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。

③DiscardPolicy

该策略下,直接丢弃任务,什么都不做。

④DiscardOldestPolicy

该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

线程池的作用,为什么要使用线程池?

线层的每次创建和销毁都要消耗内存资源,使用了线程池,每次使用线程只需要从池中获取,不需要在单独创建,使用完之后重新放回池中,可以减少内存的消耗

线程池都有哪些状态?

1.RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。线程池的初始化状态是RUNNING。线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。

2.SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。调用线程池的shutdown()方法时,线程池由RUNNING -> SHUTDOWN。

3.STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。调用线程池的shutdownNow()方法时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

4.TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。因为terminated()在ThreadPoolExecutor类中是空的,所以用户想在线程池变为TIDYING时进行相应的处理;可以通过重载terminated()函数来实现。

当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。

当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

5.TERMINATED:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED

饱和策略

当队列和线程池都满了,说明线程池处于饱和状态,那么必须对新提交的任务采用一种特殊的策略来进行处理。这个策略默认配置是AbortPolicy,表示无法处理新的任务而抛出异常。JAVA提供了4中策略:

1、AbortPolicy:直接抛出异常

2、CallerRunsPolicy:只用调用所在的线程运行任务

3、DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。

4、DiscardPolicy:不处理,丢弃掉。

run()和start()的区别

run():主要用来封装我们定位的业务代码,直接调用本方法,相当于普通方法的调用

start():主要用来以多线程的方式启动线程,然后又JVM调用本线程的run()执行业务

注意:这里的启动指的是将线程对象加入到就绪队列中,具体什么时候执行还得看os

每个线程都是通过某个特定Thread对象对应的run()来完成其操作的,run()称为线程体通过调用Thread类的start()方法来启动一个线程,也就是说start()方法用于启动线程,run()方法用于执行线程任务,run()可以重复调用,而start只能调用一次

stat()来启动页一个线程,真正的实现了多个线程运行,调用start()方法无序等待run()代码执行完毕,可以直接继续执行其他的代码,此时线程是处于就绪状态,并没有运行,然后通过Thread类调用run()来完成运行状态,run()运行结束,线程终止,然后cpu在调度其他线程

run()方法是本线程里的,只是线程里的一个函数,而不是多线程的,直接调用fun(),其实就是调用了一个普通的函数而已,直接调用run()必须等待run()执行完毕才能执行下面的代码,所有执行路径还是 只有一条,根本就没有线程的特征,所以在多线程执行时要是用start()方法而不是run()方法

Concurrent包

  1. 概述

Concurrent包是jdk5中开始提供的一套并发编程包,其中包含了大量和

多线程开发相关的工具类,大大的简化了java的多线程开发,在高并发

分布式场景下应用广泛.

  1. BlockingQueue阻塞式队列

本身是一种队列数据结构,和其他队列比起来,多了阻塞机制,从而可以在

多个线程之间进行存取队列的操作,而不会有线程并发安全问题.所以称之

为阻塞式队列.

可以简单的理解为,阻塞式队列是专门设计用来在多个线程间通过队列共

享数据

状态产生的原因不同,阻塞状态又可以细分成三种:

1)等待阻塞:运行状态中的线程执行wait()方法,本线程进入到等待阻塞状态

2)同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态

3)其他阻塞:调用线程的sleep()或者join()或发出了I/O请求时,线程会进入到阻塞状态.当sleep()状态超时.join()等待线程终止或者超时或者I/O处理完毕时线程重新转入就绪状态

  • 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期

java基础(环境,java平台,jvm,异常,面向对象,数组,IO流,容器等等)相关推荐

  1. Java基础、多线程、JVM、集合八股文自述(持续更新)

    Java基础.多线程.JVM.集合八股文自述 一.Java基础 1.1 object类有哪些方法? getClass().hashCode().equals().clone().toString(). ...

  2. Java基础:Java程序设计环境

    按应用范围,Java 可分为 3 个体系,即 Java SE.Java EE 和 Java ME.Java 语言的开发运行,也离不开 Java 语言的运行环境 JRE.没有 JRE 的支持,Java ...

  3. 初学JAVA 基础(二)JVM、JRE、JDK之间的关系

    JDK: java开发工具 (java Development Kit) JDK的 下载与安装 JDK是整个JAVA的核心,包括了Java运行环境(JRE),一堆Java工具(javac/java/j ...

  4. java基础之----java常见异常及代码示例

    java基础之----java常见异常及代码示例 参考文章: (1)java基础之----java常见异常及代码示例 (2)https://www.cnblogs.com/gunduzi/p/1203 ...

  5. java基础总结-java技术栈快速复习

    java基础 java基础概念 java概述和语言背景 java语言是没有sun公司(Stanford University Network:斯坦福大学网络)在1995年推出的计算机语言 java之父 ...

  6. Java 基础-01 Java语言入门

    文章目录 Java 基础-01 Java语言入门 1.计算机基本概念 1.1 计算机概述 1.2 计算机组成 1.3 CPU.内存与硬盘 2.软件基本概念 2.1 软件概述 2.2 人机交互方式 2. ...

  7. Java 基础学习-Java语言概述

    Java 基础学习 第一章 Java语言概述 回顾java基础知识,进行整理记录. 文章目录 Java 基础学习 前言 一. Java语言发展史(了解) 二.Java语言跨平台原理(理解) 三.JRE ...

  8. Java基础:Java抽象接口

    在Java中,一个没有方法体的方法应该定义为抽象方法,而如果一个类中含有抽象方法,则该类必须定义为一个抽象类.接口是功能的集合,同样可看做是一种特殊的数据类型,是比抽象类更为抽象的类,接口只描述所应该 ...

  9. 黑马程序员:Java基础总结----Java语言编程规范

       黑马程序员:Java基础总结        Java语言编程规范:参考自SUN公司文档  ASP.Net+Android+IO开发..Net培训.期待与您交流!  I.   排版规范 A.  规 ...

  10. java基础之java中的基本数据类型

    java基础之java中的基本数据类型 学习java一段时间了,使用java也差不多一年多了,可是对于后续的java的学习真的是后劲不足,或者是说懒惰吧,回想一下这一年多,用java最多的就是Andr ...

最新文章

  1. 『TensorFlow』卷积层、池化层详解
  2. System类的几个常用方法
  3. 【数据可视化应用】绘制极坐标(附Python代码)
  4. 别光看世界杯 7月还有一场音视频技术盛宴等着你
  5. Spark RDD使用详解2--RDD创建方式
  6. java 翻转句子_Java编程-句子反转
  7. pe常用软件_验证几款U盘PE系统,找出来纯净的几个请大家参考
  8. net2.0中使用Cookie保存中文出现乱码的解决方法
  9. 95-10-180-启动-Mx4jLoader
  10. javascript--Math相关
  11. 关于.Net WebProxy【转】
  12. catia逆向建模步骤_catia逆向建模步骤_什么是3D扫描逆向设计?
  13. 计算机computer英语划分音节,computer是什么意思
  14. Golang 字符串
  15. 脑瘫患儿家庭的森森林林双包胎赴京演出圆满成功!
  16. 大数据为湖湘互联网发展加码
  17. WIN10更改不了锁屏壁纸
  18. 通过路由器的三台PC机实现网络互通-Cisco
  19. vercel.app无法访问
  20. java 获取微信头像和昵称 生成图片

热门文章

  1. 智慧档案馆智慧库房建设方案
  2. 城市运行一网统管_一网统管:超大城市运行的“绣花功夫”
  3. 2021年安全员-C证(山东省-2020版)考试报名及安全员-C证(山东省-2020版)考试APP
  4. windows服务器充当网关实现nat转发代理上网
  5. C++ Primer (暂时完结)
  6. C/C++ (const关键字详解)
  7. WebGL实现HTML5的3D贪吃蛇游戏
  8. 0.2、Vue 开发工具 HbuilderX
  9. 树莓派3b安装usb无线网卡要点大全
  10. 记一次kafka无法生产发送消息排查经历