目录
语法基础
1.jdk jre jvm区别
2.基本数据类型
3.引用数据类型
4.自动类型转换、强制类型转换
5.常见的运算符
6.& 和 &&区别
7.++ --在前和在后的区别
8.+=有什么作用
9.switch..case中switch支持哪些数据类型
10.break和continue区别
11.while 和 do while区别
12.如何生成一个取值范围在[min,max]之间的随机数
13.数组的长度如何获取?数组下标的取值范围是?
14.什么是不定参数方法?不定参数的形参本质上是什么?实参怎么传递给形参?不定参数方法有什么注意事项?
15.java循环结构有哪些
16.什么是形参,什么是实参,形参和实参什么关系
17.如果方法有返回值,应该通过哪个关键字返回数据,数据的类型有没有什么要求?如果方法没有返回值,能用这个关键字吗?为什么?
18.构造方法有什么特点
19.setter方法和getter方法区别
20.成员变量(属性)和局部变量区别
面向对象
21.什么是类,什么是对象
22.抽象类有什么特点
23.static关键字作用是什么
24.final关键字作用
25.抽象类和接口区别
26.什么是接口,接口的特点是什么
27.super this区别
28.继承使用什么关键字,子类能继承父类哪些内容
29.重载(Overload)和重写(Override)的区别
30.访问修饰符有哪些
31.内部类分为哪几种
常用类
32.Sting StringBuffer StringBuilder区别
33.try...catch...finally作用
34.列举java常见的异常
35.throw和throws区别
36.Java中的异常可以分为哪几类?区别是什么
37.String常用的方法
38.创建String对象有哪些方式
39.String类中的equals和java中的==有什么区别
40.final finally finalize区别
41.StringBuffer 的 append 方法在容量不足时会自动扩容。具体扩容机制?
42.8种基本数据类型和各自对应的包装类
集合
43.集合框架族谱图编辑
44. ArrayList和LinkedList区别是什么
45.Collection和Collections有什么区别?
46.List、Set、Map之间的区别是什么?
47.说一下HashMap非线程安全的原因 原因先不看
48.HashMap和HashTable有什么区别?
49.如何决定使用HashMap还是TreeMap?
50.说一下HashMap的实现原理?
51.说一下HashSet的实现原理?
52.如何实现数组和List之间的转换?
53.ArrayList和Vector的区别是什么?
54.Array和ArrayList有何区别?
55.Iterator和ListIterator区别?
多线程
56.并行和并发有什么区别?
57.线程和进程的区别?
58.守护线程是什么?
59.创建线程有哪几种方式?
60.说一下runnable和callable有什么区别?
61.线程有哪些状态?
62.sleep() 和 wait() 有什么区别?
63.notify()和 notifyAll()有什么区别?
64.线程的 run() 和 start() 有什么区别?
65。创建线程池有哪几种方式?
66.线程池有哪些状态?
67.线程池中 submit() 和 execute() 方法有什么区别?
68.在 Java 程序中怎么保证多线程的运行安全?
69.多线程中 synchronized 锁升级的原理是什么?
70.什么是死锁?
71.怎么防止死锁?
72.说一下ThreadLocal原理
73.ThreadLocal 是什么?有哪些使用场景?
74.说一下 synchronized 底层实现原理?
75.synchronized 和 volatile 的区别是什么?
76.synchronized 和 Lock 有什么区别?
77.synchronized 和 ReentrantLock 区别是什么?
78.四个拒绝策略:
79.自定义线程池
80.预定义线程池
81.如何实现多线程
1.继承thread类
2.实现Runnable接口
3.实现Callable接口
4.线程池
82.多线程优势
文件和IO
83.IO流分类
编辑84.IO中构造方法
缓冲流
转换流
对象流
85.File中常见方法
JVM是iava虚拟机,通过JVM可以实现跨平合开发 。JRE是iava运行时环境,它包含JVM以及iava核心类库,只要有JRE就能运行ava项目。JDK是iava开发工具包,包含JRE和各种开发相关的工具,安装了JDK不但能运行iava项日还能开发iava项日
byte short int long float double char boolean
类型 | 说明 | 示例 |
---|---|---|
类(class) | 用户定义的类 | String , ArrayList |
接口(interface) | 抽象类型 | List , Runnable |
数组(array) | 同类型数据集合 | int[] , String[][] |
枚举(enum) | 固定值集合 | enum Color { RED, GREEN } |
注解(annotation) | 元数据 | @Override |
自动转换(隐式)小类型转大类型
byte → short → int → long → float → double
↑
char
强制转换(显式)
double a = 9.78;
int b = (int)a; // b=9 (截断小数)
运算符类别 | 运算符 | 名称 | 示例 | 说明 |
---|---|---|---|---|
算术运算符 | + |
加法 | a + b |
也可用于字符串连接 |
- |
减法 | a - b |
||
* |
乘法 | a * b |
||
/ |
除法 | a / b |
整数相除会截断小数 | |
% |
取模 | a % b |
求余数 | |
++ |
自增 | a++ 或 ++a |
前缀/后缀区别 | |
-- |
自减 | a-- 或 --a |
前缀/后缀区别 | |
关系运算符 | > |
大于 | a > b |
返回boolean |
< |
小于 | a < b |
||
>= |
大于等于 | a >= b |
||
<= |
小于等于 | a <= b |
||
== |
等于 | a == b |
比较值(基本类型)或引用(对象) | |
!= |
不等于 | a != b |
||
逻辑运算符 | && |
逻辑与 | a && b |
短路与(前假不计算后) |
|| |
逻辑或 | a || b |
短路或(前真不计算后) | |
! |
逻辑非 | !a |
取反 | |
位运算符 | & |
位与 | a & b |
按位与 |
| |
位或 | a | b |
按位或 | |
^ |
位异或 | a ^ b |
相同为0,不同为1 | |
~ |
位非 | ~a |
按位取反 | |
<< |
左移 | a << 2 |
低位补0 | |
>> |
右移 | a >> 2 |
符号位填充 | |
>>> |
无符号右移 | a >>> 2 |
高位补0 | |
赋值运算符 | = |
赋值 | a = b |
|
+= |
加赋值 | a += b |
等价于 a = a + b |
|
-= |
减赋值 | a -= b |
||
*= |
乘赋值 | a *= b |
||
/= |
除赋值 | a /= b |
||
%= |
模赋值 | a %= b |
||
&= |
位与赋值 | a &= b |
||
|= |
位或赋值 | a |= b |
||
^= |
位异或赋值 | a ^= b |
||
<<= |
左移赋值 | a <<= b |
||
>>= |
右移赋值 | a >>= b |
||
>>>= |
无符号右移赋值 | a >>>= b |
||
条件运算符 | ?: |
三目运算符 | a ? b : c |
a为true取b,否则取c |
其他运算符 | instanceof |
类型比较 | a instanceof String |
检查对象是否是指定类实例 |
() |
强制类型转换 | (int)a |
||
new |
对象创建 | new Object() |
||
. |
成员访问 | obj.method() |
& 按位与运算符,&& 逻辑与运算符;两者都可以进行逻辑与运算;
& 不具备短路功能;&&具备短路功能,一旦表达式出现了false,则之后的表达式都不再运行,
++递增运算,在原有值的基础上+1
--递减运算,在原有值的基础上-1
如果+ +/--是一行单独的运算,那么在前和在后没有区别;如果++/--参与运算或调用,那么在前是先递增/递减运算再调用值,在后是先调用值再递增/递减运算
short a= 20;a = a + 10 short a = 20;a += 20区别
前者会出现编译错误,导致程序无法正确运行,主要是因为a是short类型,10是int类型。相加之后,类型变成int类型,再赋值给a的时候需要强制类型转换,后者不会报错,因为+=自带类型转换功能
byte short int char String 枚举
使用场合不同---break可用于switch结构和循环结构中,continue只能用于循环结构中
作用不同---break语句终止某个循环,程序跳转到循环块外的下一条语句。continue跳出本次循环,进入下一次循环
两者都是用来实现循环结构的;
while是先判断后执行; do while是先执行一次后判断;
如果初始条件成立,那么while和do while的执行结果一致:
如果初始条件不成立,那么while什么都不执行,do while至少执行一次:
Random r= new Random(); int num = r.nextInt(max - min + 1)+ min;
数组名.length 0—数组名.length-1
不定参数方法指的是方法的参数是动态的,不固定的。
不定参数的形参本质上是数组。
在方法调用时,实参会依次拷贝到形参数组中通过数组下标可以获取传入的参数。
注意事项:一个方法最多只能有一个不定参数,在方法定义时,不定参数应该作为方法的最后一个参数。
循环类型 |
语法结构 |
执行顺序 |
适用场景 |
最少执行次数 |
---|---|---|---|---|
while循环 |
|
先判断条件,再执行循环体 |
不确定循环次数 |
0次 |
do-while循环 |
|
先执行循环体,再判断条件 |
至少需要执行一次 |
1次 |
for循环 |
|
初始化→条件判断→循环体→更新 |
已知循环次数 |
0次 |
增强for循环 |
|
自动遍历集合/数组元素 |
遍历数组或集合 |
0次(集合为空时) |
在方法定义时,参数列表中的参数称为形参,也叫形式参数。在方法调用时,传入方法中的参数称为实参,也叫实际参数。在方法调用时实参会拷贝给形参,实参到形参是单向的值拷贝过程。
方法有返回值,使用return关键字返回数据。return返回的数据类型,必须和方法定义中的返回值类型一致。如果方法没有返回值,也可以使用return关键字,return的作用结束方法
执行。
构造方法名必须与类名相同
构造方法不能有返回值,不能有void
构造方法只有在创建对象的时候可以使用,不能单独使用
构造方法可以重载
当没有提供构造方法,系统会默认提供一个无参。若自己有构造方法,系统便不再提供
构造方法的作用是给属性赋初值
setter有参数,参数类型和要赋值的属性的类型相同,没有返回值,返回值类型是void
getter没有参数,有返回值,返回值类型是要返回的属性的类型
书写位置不同:属性定义在类内,方法外;局部变量定义在方法内
内存区域不同:堆区;栈区
默认值不同:不赋初值也可以使用,、int 为0 引用类型 null;必须赋初值才可以使用
生命周期不同:随着对象的创建而产生,当对象不再使用,会由垃圾回收机制回收堆区内存;随着方法的调用而产生,随着方法的结束或所在代码块的结束而销毁
作用域不同:所在的类中,非静态方法中使用,或者随着对象的创建而产生(private除外);所在代码块
类是对具有相同特征和行为的事物的抽象。对象是类的实例,是类的具体体现
抽象类中不能创建对象
抽象类中可以有抽象方法
普通类中有的,抽象类都可以拥有
static可以修饰属性,被修饰后的属性称为类属性,被所有对象共享。可以通过对象访问静态属性,可以通过类名访问静态属性。通常使用类名
static可以修饰方法,被修饰后的方法称为类方法,被所有对象共享,可以通过对象调用静态方法,可以通过类名调用静态方法。通常使用类名
static可以修饰代码块,随着类的加载而执行
final修饰类,类不能被继承
final修饰方法,方法不能被重写
final修饰变量,变量只能赋一次值,赋值之后,值不能修改
接口所有的方法都是public abstract的,抽象类不一定
类可以实现很多接口,但只能继承一个抽象类
接口属性默认public static final,抽象类不一定
接口关键字interface 抽象类是abstract class
————————————————————————
抽象类:用abstract修饰的类称为抽象类。
接口:用interface修饰,是一组规范和标准,往往是对行为的抽象。
### 相同点
1. 接口和抽象类都不能实例化对象。
2. 接口和抽象类都可以使用多态。
3. 接口和抽象类都可以包含抽象方法。
4. 一个类如果实现接口必须实现全部的抽象方法。一个类继承了抽象类,必须实现全部的抽象方法。
### 不同点
1. 接口中的属性只能包含静态常量(public static final)。抽象类中的属性可以包含:实例变量、静态变量、常量(静态、非静态),而且访问修饰符可以是(public、缺省、protected、private)
2. 接口中的方法通常是抽象方法(Java8开始,可以有默认方法和静态方法)。抽象类中的方法可以包含:实例方法(含getter、setter)、静态方法、抽象方法。
3. 接口不能有构造方法(它不是类),抽象类可以包含构造方法。
4. 接口中不能包含初始化块、静态代码块。抽象类中可以包含初始化块和静态代码块。
5. 接口可以继承别的接口,而且可以继承多个。抽象类可以继承别的类,但只能继承一个类。
6. 一个普通类,只能有一个父类(包括抽象类),但可以实现多个接口。
接口用于定义一种规范,只提供方法的声明,不提供方法的使用
接口中属性默认public static final
方法默认为public abstract
接口可以继承其他接口 用extends关键字
一个类实现接口用implements关键字,需要实现接口中全部的方法
this用于访问本类的属性 super用于访问父类继承过来的属性(尤其是父子类同名的属性)
访问本类方法 访问父类继承过来的 方法(尤其是父子类同名的方法)
调用本类的给构造方法 调用父类的构造方法
表示对象,是调用当前方法的对象 只是关键字
extends关键字 继承除父类构造方法外其他所有的属性和方法
重载发生在同一个类中,要求方法名称相同,参数列表不同,与返回值类型无关
重写发生在子类和父类之间,要求方法名称相同,参数列表相同,返回值类型要求<=父类要求的返回值类型
**Java中提供了4种权限访问修饰符(从小到大):private、缺省、protected、public**
4种修饰符可以修饰**类**以及**类的成员**(属性、方法、构造方法)。
1. 如果修饰类:只能使用public和缺省。
2. 如果修饰类的成员:private、缺省、protected、public都可以。
类的成员:指的是**类中定义的属性,方法**。
private、缺省、protected、public会影响所修饰的属性和方法的可见度(访问权限)。
修饰符 | 类内部 | 同包类 | 不同包子类 | 不同包其他类 |
---|---|---|---|---|
private | √ | |||
缺省 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
内部类类型 | 特点 | 示例 |
---|---|---|
成员内部类 | 定义在类中,方法外,可以访问外部类所有成员 | java class Outer { class Inner {} } |
静态内部类 | 使用static修饰,只能访问外部类静态成员 | java class Outer { static class Inner {} } |
局部内部类 | 定义在方法或作用域内,只能访问final局部变量 | java void method() { class Local {} } |
匿名内部类 | 没有类名,直接实现接口或继承类 | new Runnable() { public void run() {} } |
**String是不可变字符串。**
**StringBuffer是可变字符串。在多线程环境下访问是安全的(也称线程同步),不会出现紊乱。**
**StringBuilder是可变字符串。在多线程环境下访问是不安全的(也称线程不同步),数据可能会紊乱。**
————————————————————————————————————
它们的相同点是都用来封装字符串;都实现了CharSequence接口。
可变与不可变: String是不可变的字符序列,StringBuffer、StringBuilder都是可变的字符序列;String类的底层是使用final修饰的字符数组,不可变;但是StringBuffer、stringBuilder底层也是使用字符数组,但是没有使用final修饰,可变。
是否线程安全: String和StringBuffer都是线程安全的,效率低;StringBuilder是线程不安全的,效率高;执行效率: StringBuilder>StringBuffer >String
初始化方式:String可以通过字符串字面量和构造器来创建实例;但是StringBuffer、StringBuilder只能通过构造器来创建实例。
字符串修改方式: sting字符串修改方法是首先创建一个StringBuilder,其次调用StringBuilder的append方法,最后调用StringBuilder的tosting()方法把结果返回;StingBufer和StringBuilder在修改字符串方面比String的性能要高。
是否实现了equals和hashCode方法: String实现了equals0方法和hashcode0方法:而StringBufer没有实现equals0方法和hashcode0方法;
对比维度 | String | StringBuffer | StringBuilder |
---|---|---|---|
相同点 | 都用来封装字符串 都实现了CharSequence接口 |
都用来封装字符串 都实现了CharSequence接口 |
都用来封装字符串 都实现了CharSequence接口 |
可变性 | 不可变的字符序列 (final修饰的字符数组) |
可变的字符序列 (非final字符数组) |
可变的字符序列 (非final字符数组) |
线程安全 | 线程安全(不可变特性) | 线程安全(方法同步) | 线程不安全 |
执行效率 | 最低 | 中等 | 最高 |
初始化方式 | 1. 字符串字面量 2. 构造器创建 |
只能通过构造器创建 | 只能通过构造器创建 |
字符串修改方式 | 1. 创建StringBuilder 2. 调用append() 3. 调用toString()返回 |
直接修改原对象 | 直接修改原对象 |
方法实现 | 实现了equals()和hashCode() | 未实现equals()和hashCode() | 未实现equals()和hashCode() |
适用场景 | 少量字符串操作 | 多线程环境字符串操作 | 单线程环境字符串操作 |
try块中放可能发生异常的代码。
catch块用于捕获并处理一个特定的异常,catch块可以有多个:
finally块无论异常是否发生,异常是否匹配被处理,都会执行,主要做一些清理工作,比如释放资源;
异常类型 | 说明 |
---|---|
NullPointerException |
空指针异常 |
ArrayIndexOutOfBoundsException |
数组越界 |
ClassCastException |
类型转换异常 |
IllegalArgumentException |
非法参数异常 |
IOException |
输入输出异常 |
FileNotFoundException |
文件未找到 |
NumberFormatException |
数字格式异常 |
SQLException |
数据库操作异常 |
throw | ||
作用 | 主动抛出一个异常对象 | 声明方法可能抛出的异常类型 |
位置 | 方法体内 | 方法签名后 |
数量 | 一次只能抛出一个异常 | 可以声明多个异常 |
处理 | 必须处理或继续抛出 | 调用者需要处理或继续声明 |
异常类别 | 特点 | 继承关系 | 处理要求 |
---|---|---|---|
Error | 系统错误,程序无法处理 | Throwable子类 | 不需要捕获 |
RuntimeException | 运行时异常(非受检异常) | Exception子类 | 不强制捕获 |
Checked Exception | 受检异常 | Exception子类 | 必须捕获或声明 |
indexOf() 查找单个字符所在的位置的索引
Length() 判断字符串的长度
ToLowerCase() 将字符串中的英文字母全部改为小写
ToUpperCase() 将字符串中的英文字母全部改为大写
Trim() 将字符串中前后的空格符号去除
Equals() 比较两个字符串的值是否相等
Split() 将字符串按照特定的符号拆分成字符串数组
charAt() 返回指定索引处的字符
replace() 字符串替换
getBytes() 返回字符串的byte类型数组
substring() 截取字符串
String是一系列字符,所以我们无法转换成以单一的char但是可以调用toCharArray()方法将字符串转换成字符数组。
1.通过new关键字来创建,使用这种方式时,JVM创建字符串对象但不存储在字符串池中,我们可以调用intern()方法将该字符串对象存储在字符串池中,如果该字符串吃已经有了相同值的字符串,则返回引用
2.使用双引号直接创建,使用这种方式时。JVM去字符串找有没有相同值的字符串,如果有则返回找到的字符串引用。否则创建一个新的字符串对象,并存储在字符串池中
equals() | == | |
作用 | 内容比较 | 引用比较 |
重写 | 可重写 | 不可重写 |
String比较 | 比较字符串内容 | 比较内存地址 |
基本类型 | 不能使用 | 比较值 |
对象比较 | 通常比较内容 | 比较对象引用 |
关键字 | 作用 | 示例 |
---|---|---|
final | 修饰变量(常量)、方法(不可重写)、类(不可继承) | final int MAX = 100; |
finally | try-catch块中必定执行的代码块 | try {} finally { /*清理代码*/ } |
finalize | Object类方法,垃圾回收前调用(已废弃) | protected void finalize() throws Throwable {} |
在 Java 中,StringBuffer 的 append 方法在容量不足时会自动扩容。具体扩容机制如下: 初始容量:StringBuffer 的默认初始容量为 16 个字符。 如果创建时指定了容量,则以指定值为准。 容量不足时的扩容: 当 append 操作导致当前容量不足时, StringBuffer 会创建一个新的字符数组,大小为原容量的两倍加 2 (即 newCapacity = (oldCapacity << 1) + 2)。 如果新容量仍不足以容纳新增内容,则直接扩容到所需的最小容量。 数据复制:扩容后,原数组内容会被复制到新数组中,旧数组被丢弃。
包装类是Java针对8种基本数据类型提供的**类类型**,**对基本数据类型的封装,就是我们所谓的包装,对应的类就是包装类。**
相比基本数据类型,包装类更加强大,包含属性和方法,基本数据类型的功能显得相对单薄。
包装类弥补了基本数据的不足,比如,包装类的Integer既可以表示null空值,同时还可以进行数学运算。
基本数据类型都有默认值,比如int,默认值为0,所有的包装类默认值都为null
基本类型 | 包装类 | 父类 |
---|---|---|
byte | Byte | Number |
short | Short | Number |
int | Integer | Number |
long | Long | Number |
float | Float | Number |
double | Double | Number |
char | Character | Object |
boolean | Boolean | Object |
:ArrayList和LinkedList都实现了List接口,他们有以下的不同点:
1.ArrayList是基于索引的数据接口,它的底层是数组。它可以以0(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。(4分)
2.相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。(3分)
3. LinkedlisttArravlist更占内存,因为Linkedist为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。(3分
答:Collection是一个集合接口。
Collections是一个对集合类进行操作的工具类。
Collections是一个包装类,包含了很多静态方法。不能被实例化,就像一个工具类。
答:List和Set都是链表形式存储、而Map是以键值对的形式进行存储。
List是一个无序的可重复的队列,而Set是一个无序的不可重复的队列
1、List和Set是存储单列数据的集合,Map是存储键值对这样的双列数据的集合;
2、List中存储的数据是有顺序的,并且值允许重复;Map中存储的数据是无序的,它的键是不允许重复的,但是值是允许重复的;Set中存储的数据是无顺序的,并且不允许重复,但元素在集合中的位置是由元素的hashcode决定,即位置是固定的(Set集合是根据hashcode来进行数据存储的,所以位置是固定的,但是这个位置不是用户可以控制的,所以对于用户来说set中的元素还是无序的)
答:从数据插入这个方面来说吧,A和B两个线程时进入addEntry,然后计算出了相同的哈希值对应了相同的数组位置,因为此时该位置还没数据,然后对同一个数组位置调用createEntry,两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点,那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失。这就导致了HashMap在多线程上的不安全。
答:1、hashMap的键值都可以为Null,而hashTable不允许
2、hashTable是线程安全的、而hashMap不是
答:需要反复执行插入、取出操作时用TreeMap,因为TreeMap是双链表形式存储。
如果只需要进行查询操作的话,建议使用HashMap,因为hashMap的查询速度比较快。
对于在Map中,进行插入、删除、定位一个元素这类操作,HashMap是最好的选择,因为相对而言HashMap的插入会更快,但如果你要对key集合进行有序的遍历,那么TreeMap是一个更好的选择。
答:hashMap是存储键值对的一种集合,它是利用哈希算法转化键值对,然后将之存入,链表或者红黑树中。
HashMap是通过Hash算法实现的,通过put(key,value)存储,get(key)获取。当传入key时,HashMap会根据key.hashCode()计算出hash值,根据hash值将value保存在bucket中。当计算出的hash值相同的时候,称之为hash冲突,hashMap的做法是用链表和红黑树存储相同hash值的value。当hash冲突的个数比较少时使用链表否则使用红黑树。
答:HashSet是基于HashMap实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,唯一的区别是HashSet中的值无法重复
答:数组转换List:通过Arrays.asList(数组)的方法转换,转换后不支持新增和删除
List转换数组:
1、通过循环遍历的方法,把每个List中的对象一一放入数组中
2、使用 List 自带的 toArray() 方法。
答:1、ArrayList是非线程安全的,Vector是线程安全的
答:Array是一个接口,ArrayList是一个集合类
1、Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。
2、Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。
3、Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。
答:1、iterator可以对Set、List集合进行遍历,而ListIterator只能对List集合类进行遍历。可以对所有的集合类进行遍历
答:并行是多台服务器运行多个线程,每台服务器只运行单个线程。并行是一台服务器运行多个线程。
并行是多个处理器或多核处理器同时处理多个任务
并发是一个处理器(CPU)同时处理多个任务(多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。)
答:进程中包含有线程,一个进程可以有多个线程。
一个程序下至少有一个进程,一个进程下也至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度
答:守护线程是隐藏起来的线程,例如Java中的垃圾回收机制就是用守护线程来运行的
守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件,在Java中垃圾回收线程就是一种特殊的守护线程
答:1、继承Thread类
答:runnable没有返回值,callable无参有返回值
答:1、NEW 尚未启动准备状态
2、RUNNING 进行状态
2、RUNNABLE 正在进行中
3、WAITING 永久等候状态
4、TIME_WAITTING 定时等候状态
4、TIMED_WAITING 等待制定的时间重新被唤醒的状态
5、BLOCKED 阻塞状态
6、TERMINATED 执行完成状态
答:1、sleep()是继承自Thread类,而wait()是继承自Object
答:notify是唤醒单个线程,notifyAll是唤醒所有线程
会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而 notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。
答:1、run()可以执行多次(重复执行),start一个线程只能执行一次
2、run()方法中是用来执行线程的运行时代码,而start是线程的启动命令执行的代码块
答:创建线程的方式有7种
它的特点在于工作线程数目被限制为1,操作一个无界的工作队列,所以它包正了所有任务都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程池数目。
它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明的特点
①它会试图缓存线程并重用,当无缓存线程可以用时,它会创建新的工作线程。
②如果线程的闲置时间超过60秒,则会终止并移出缓存。
③长时间闲置时,这种线程池不会消耗什么资源。
它内部使用的是ScychronousQueue作为工作队列
它使用来创建指定数目的线程。其背后使用的是无界的工作队列,任何时候最多有指定数目的工作线程是活动的。这意味着如果任务数量超过了指定的活动队列数目,将在工作队列中等待空闲线程的出现;如果有工作线程退出将会有新的工作线程被创建,用来补足指定的线程数目
创建单线程池,返回ScheduledExectorService,可以进行定时或周期性的工作调度
和newSingleThreadScheduledExecutor()类似,创建的是一个ScheduledExectorService,可以进行定时或者周期性调度,区别在于单一工作线程还是多个工作线程
这是一个经常被忽略的线程池,Java8才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证任务的处理顺序
它是最原始的线程池创建,上面1-3创建方式,都是对ThreadExecutorPool()的封装
答:1、RUNNING 进行中
这是最正常的状态,接受新的任务,处理等待队列中的任务。
不接受新的任务提交,但是会处理等待队列中的任务
不接受新的任务,不在处理等待队列中的任务,并中断正在执行任务的线程
所有任务都销毁了,workCount为0,线程池状态在转换为TIDYING时,会执行钩子方法terminated()
terminated()方法结束后,线程池的状态就会变成这个
答:。。。。
Execute()只能执行Runnable()类型的任务
Submit()可以执行Runnable()和Callable()两种类型的任务
答:1、尽量使用java.util.concurrent中的集合,因为这里面的集合都是线程安全的
答:一开始该方法是无锁状态,然后当一个线程调用了该方法时,就会想threaid中添加标记,此时的锁是偏向锁,当第二个线程想调用该方法时,会判断threaid中是否含有该方法的标记,如果有则。。。。。。锁升级为轻量级索。当更多的线程涌入时会发生自旋,等待一段时间无果后,会升级为重量级锁
在锁对象的对象头里面有一个threadid字段,在第一次访问的时候threadid为空,JVM让其持有偏向锁,并将threadid设置为其线程的id,再次进入的时候会先判断threadid是否与其线程id一致,如果一致则可直接使用此对象,如果不一致,则升级偏量锁为轻量级锁,通过自旋一定次数来获取锁,执行一定次数后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级锁升级为重量级锁,此过程就构成了scychronized锁的升级。
拓展:锁升级的目的?
锁升级是为了降低锁带来的性能消耗。
答:A占用了B的解锁器,但是A被锁住了,而B又占用了A的解锁器,B也被锁住了,这样两者都无法掉用解锁器进行解锁的状态叫做死锁。
当线程A持有独占锁a,并尝试去获取独占锁b的同时,线程B持有独占锁b,并尝试获取独占锁a的情况下,就会发生AB两个线程由于互相持有对方需要的锁而发生的阻塞现象,我们成为死锁。
答:。。。。。。
尽量使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
尽量使用 Java. util. concurrent 并发类代替自己手写锁。
尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。
尽量减少同步的代码块。
答:首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。
初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
答:。。。。。
ThreadLocal为每个使用该变量的线程提供了独立的变量副本,所以每个线程都可以独立的改变自己的副本,而不会影响到其他线程所对应的副本。
ThreadLocal的经典使用场景是数据库的连接和session管理等
答:。。。。。。
synchronized 是由一对 monitorenter/monitorexit 指令实现的,monitor 对象是同步的基本实现单元。在 Java 6 之前,monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。但在 Java 6 的时候,Java 虚拟机 对此进行了大刀阔斧地改进,提供了三种不同的 monitor 实现,也就是常说的三种不同的锁:偏向锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。
答:。。。。。。
1、volatile 是变量修饰符;synchronized 是修饰类、方法、代码段。
2、volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。
3、volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
4、volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。
答:1、synchronized是自动锁,它会自动加锁自动解锁,而Lock是手动锁,需要手动进行加锁和解锁。
1、synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
2、synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。
3、通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
答:。。。。。。
1、ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作;
2、ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁;
3、ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。
1. 默认[`ThreadPoolExecutor.AbortPolicy`],线程池中的线程都在处理任务,等待队列里的任务已满,再来任务的时候抛出[`RejectedExecutionException`]异常,也就是打断当前执行流程。
2. [`ThreadPoolExecutor.CallerRunsPolicy`],线程池中的线程都在处理任务,等待队列里的任务已满,再来任务的时候,只要线程池没有关闭,就由提交任务的当前线程处理。
3. [`ThreadPoolExecutor.DiscardPolicy`中] ,线程池中的线程都在处理任务,等待队列里的任务已满,再来任务的时候直接丢弃任务。
4. [`ThreadPoolExecutor.DiscardOldestPolicy`中],线程池中的线程都在处理任务,等待队列里的任务已满,再来任务的时候丢弃等待队列中最老(早)的任务。
ThreadPoolExecutor executor = new ThreadPoolExecutor
(int corePoolsize核心线程数/初始化线程数,
int maximumPoolsize最大线程池,
long keepAliveTime非核心线程-空闲线程,
TimeUnit unit时间单位,
BlockingQueueworkQueue)工作队列(排队队列),
ThreadFactory threadFactory,
RejectedExecutionHandler handler拒绝策略
:使用Executors工具类封装好的方法,直接创建线程池
1.创建固定个数的线程池 ExecutorService service = Executors.newFixedThreadPool(5)
2.创建不限个数的线程池Executors.newCachedThreadPool()
3.创建单个个数的线程池(串行任务池)Executors.newSingleThreadExecutor()
4.支持定时任务的线程池Executors.newScheduledThreadPool(3)
5.添加任务 service.submit(()->{ })
1.创建类继承Thread,重写run方法 2.创建当前线程类对象,启动多线程,(调用start方法) start方法会自动调用run方法
1.创建类实现runnbale接口,重写Run方法 * 2.创建当前线程类对象,启动多线程,(调用start方法) start方法会自动调用run方法 3.run方法没有返回值
runnable没有返回值,callable无参有返回值
1.实现callable,重写call方法。 2.call方法无参有返回值 3.ExecutorService Executor Future
自定义线程池、预定义线程池(见上)
降低资源消耗:重复利用已创建的线程,降低线程创建和销毁造成的开销
提高响应速度:当任务到达时,可以直接使用线程池中空闲的线程执行任务
提高线程的可管理性:可以统一对线程进行分配、调优和分配
提供更多强大的功能:比如允许任务延期执行或定期执行
BufferedInputStream
BufferedInputStream是对InputStream的包装,为字节输入流增加缓冲功能。它的存在是为了减少读取文件的次数。BufferedInputStream会一次性从文件里读取若干字节(默认8KB)的数据存入缓存,以后每次读取是从缓存中读取,缓存中的数据读完之后,又会一次性从文件里读取若干字节。
缓存: 就是一个字节数组。文件中的数据会先读到这个数组里,以后从这个数组里读取数据。
构造方法 | 说明 |
---|---|
public BufferedInputStream(InputStream in) | 创建一个缓存大小为8KB的字节缓冲输入流, 需要一个字节输入流作为参数,为这个流提供缓冲 |
public BufferedInputStream(InputStream in, int size) | 创建一个缓存大小为size字节的字节缓冲输入流, 需要一个字节输入流作为参数,为这个流提供缓冲 |
BufferedOutputStream
BufferedOutputStream是对OutputStream的包装,为字节输出流增加缓冲功能。它的存在是为了减少写入文件的次数。BufferedOutputStream会把要写入文件的数据先写入缓存(默认8KB),当缓存满了之后,会一次性把缓存里的数据写入文件里。
构造方法 | 说明 |
---|---|
public BufferedOutputStream(OutputStream out) | 创建一个缓存大小为8KB的字节缓冲输出流, 需要一个字节输出流作为参数,为这个流提供缓冲 |
public BufferedOutputStream(OutputStream out, int size) | 创建一个缓存大小为size字节的字节缓冲输出流, 需要一个字节输出流作为参数,为这个流提供缓冲 |
BufferedReader
BufferedReader是对Reader的包装,为字符输入流增加缓冲功能。它的存在是为了减少读取文件的次数。BufferedReader会一次性从文件里读取若干字符(默认8KB)的数据存入缓存,以后每次读取是从缓存中读取,缓存中的数据读完之后,又会一次性从文件里读取若干字符。
构造方法 | 说明 |
---|---|
public BufferedReader(Reader in) | 创建一个缓存大小为8KB的字符缓冲输入流, 需要一个字符输入流作为参数,为这个流提供缓冲 |
public BufferedReader(Reader in, int size) | 创建一个缓存大小为size字节的字符缓冲输入流, 需要一个字符输入流作为参数,为这个流提供缓冲 |
BufferedWriter
BufferedWriter是对Writer的包装,为字符输出流增加缓冲功能。它的存在是为了减少写入文件的次数。BufferedWriter会把要写入文件的数据先写入缓存(默认8KB),当缓存满了之后,会一次性把缓存里的数据写入文件里。
构造方法 | 说明 |
---|---|
public BufferedWriter(Writer out) | 创建一个缓存大小为8KB的字符缓冲输出流, 需要一个字符输出流作为参数,为这个流提供缓冲 |
public BufferedWriter(Writer out, int size) | 创建一个缓存大小为size字节的字符缓冲输出流, 需要一个字符输出流作为参数,为这个流提供缓冲 |
InputStreamReader
InputStreamReader是输入转换流,它继承于Reader(即它是一个字符输入流),它能把一个字节输入流转换为字符输入流,而且可以在转换的时候,指定字符输入流的编码格式。
构造方法 | 说明 |
---|---|
public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException | 根据给定的字符集, 把字节输入流转换为字符输入流 |
OutputStreamWriter
OutputStreamWriter是输出转换流,它继承于Writer(即它是一个字符输出流),它能把一个字节输出流转换为字符输出流,而且可以在转换的时候,指定字符输出流的编码格式。
构造方法 | 说明 |
---|---|
public OutputStreamWriter(OutputStream out, String charsetName) throws UnsupportedEncodingException | 根据给定的字符集, 把字节输出流转换为字符输出流 |
ObjectInputStream
ObjectInputStream是对象输入流,它是字节输入流,继承于InputStream。它的主要作用是从文件中读取对象类型的数据。 ObjectInputStream不但能从文件中读取对象类型,还能读取基本数据类型数据。
构造方法 | 说明 |
---|---|
public ObjectInputStream(InputStream in) throws IOException | 创建一个对象输入流, 它需要一个字节输入流作为参数 |
ObjectOutputStream
ObjectOutputStream是对象输出流,继承于OutputStream,它的主要作用是把对象写入到文件中。 需要写入到文件的对象必须是可序列化的,即对象要实现Serializable接口。 ObjectOutputStream除了能把对象写入文件之外,还能把基本数据类型写入文件。
构造方法 | 说明 |
---|---|
public ObjectOutputStream(OutputStream out) throws IOException | 创建一个对象输出流, 它需要一个字节输出流作为参数 |
方法签名 | 方法说明 |
---|---|
public boolean createNewFile() throws IOException |
当file对象代表的文件不存在时,创建对应的空文件.如果文件存在,创建失败. |
public boolean delete() |
删除file对象表示的文件或目录。如果file代表的是目录,则目录必须为空才能删除。 |
public boolean exists() |
判断file对象表示的文件或者目录是否存在 |
public String getAbsolutePath() |
获取file对象的绝对路径 |
public String getName() |
获取文件或者目录的名称 |
public String getParent() |
获取文件或者目录的上级路径 |
public boolean isDirectory() |
判断file是不是目录 |
public boolean isFile() |
判断file是不是文件 |
public long lastModified() |
获取文件上次修改时间,得到的是距离1970年1月1日的毫秒数 |
public long length() |
获取文件大小,单位是字节 |
public File[] listFiles() |
获取当前文件夹中的子文件或子文件夹对象 |
public boolean mkdir() |
创建文件夹,只能创建一级 |
public boolean mkdirs() |
创建文件夹,可创建多级 |
public boolean renameTo(File dest) |
重命名,可换路径(类似于剪切) |