方法名 | 返回值 | 作用 |
---|---|---|
length() | int | 得到字符串长度 |
toLowerCase() | String | 转换为小写 |
toUpperCase() | String | 转换为大写 |
trim | String | 去除字符串首尾全部空格 |
isEmpty() | boolean | 判断字符串长度是否为0 |
getBytes() | byte[] | 转换为字节数组 |
toCharArray() | char[] | 转换为字符数组 |
equalsIgnoreCase(String str) | boolean | 忽略大小写比较字符串是否相等 |
equals(String str) | boolean | 判断两个字符串是否相等 |
charAt(int index) | char | 得到某个索引上的字符 |
indexOf(String str) | int | 得到某个字符串第一次出现的索引,不存在返回-1 |
lastIndexOf(String str) | int | 得到某个字符串最后一次出现的索引,不存在返回-1 |
contains(String str) | boolean | 判断是否存在某个字符串 |
startsWith(String str) | boolean | 判断是否以某个字符串开头 |
endsWith(String str) | boolean | 判断是否以某个字符串结尾 |
concat(String str) | String | 将指定字符串评级到原字符串末尾 |
substring(int index) | String | 从索引index开始截取字符串末尾 |
substring(int begin,int end) | String | 截取[begin,end)范围内的字符串 |
split(Sting regex) | String[] | 根据字符串或者正则表达式切分原字符串 |
replace(String oldStr,String newStr) | String | 将原字符串的oldStr替换为newStr |
String.valueOf(参数) | String | 将参数转换为字符串,参数可以是任何数据,通常用于原始类型转换为字符串 |
String.format(String式,Object…obj) | String | 根据指定格式转换参数。常用与将浮点数据保留指定小数位数。\n如String.format(“%4.2f”,2.345)表示将 2.345保留2位小数,整体占4位,输出为字符串格式。 如果实际数字总位数大于4,原样输出,如果实际数字 总位数小于4,会在最前补充空格。 |
用于表示可变字符串的一个类,是非线程安全的,在单线程环境下使用,效率更高。
用于表示可变字符串的一个类,是线程安全的,在多线程环境下使用。
StringBuilder和StringBuffer中的方法都一致,只不过StringBuffer中的方法使用了synchronized关键字修饰,表示是一个同步方法,在多线程环境下不会出现问题。
常用方法 | 作用 |
---|---|
append(Object obj) | 将任意数据添加带原可变字符串的末尾 |
delete(int start,int end) | 删除[start,end)范围内的字符 |
deleteCharAt(int index) | 删除index索引上的字符 |
insert(int index,Object obj) | 将obj添加到index上 |
repalce(int start,int end,String str) | 将[start,end)范围内的字符替换为str |
reverse() | 反转字符串 |
String类中的所有方法调用后,都会创建一个新的String对象,即原本的String字符串不会改变
StringBuilder类中的所有方法都是在操作同一个字符串对象,每次调用方法,都会让原字符串发生变化
StringBuilder类中没有重写equals方法,所以判断两个可变字符串对象是否相同时,如果调用equals方法,实际调用的是Object类中未重写的方法,即==判断。所以判断可变字符串是否相同时,需要将其转换为String对象再调用equals方法。
String.valueOf(Object obj)
toString()方法
拼接空字符串
这三个类都可以表示字符串。都提供了一些操作字符串的方法。
这三个类中有相同的方法,如charAt(),indexOf()等。
这三个类都是被final修饰的类,不能被继承
String定义的字符串是一个常量。可变字符串定义的字符串是一个变量。
String类中的方法调用后,不会改变原本字符串的值。可变字符串中的方法调用后,会改变原本字符串的值
StringBuilder是非线程安全的可变字符串类,StringBuffer是线程安全的可变字符串类,其中的方法被synchronized修饰。
常用方法与属性 | |
---|---|
System.ou | 获取标准输出流对象,用于打印信息 |
System.in | 获取标准输入流对象,用于获取输入的信息 |
System.err | 获取错误输出流对象,用于打印异常信息 |
System.exit(int statues) | 终止虚拟机运行,参数0表示正常终止 |
System.currentTimeMills() | 获取从1970/1/1 0:0:0至今经过了多少毫秒。中国是UTC(+8),所以实际是从1970/1/1 8:0:0至今经过了多少毫秒。返回值为long类型。通常称为时间戳。 |
System.arraycopy(原数组,原数组的起始位置,目标数组,目标数组的起始位置,要复制的元素数量) | 将原数组中指定数量的元素复制到新数组中 |
这个类不是一个抽象类,但不能创建对象,因为它的构造方法是私有的。
这个类提供了一个静态方法getRuntime(),通过该方法,可以获取一个Runtime类的对象。
这种方式可以保证该类只能创建一个对象,是Java中的一种设计模式:单例模式。
public class Runtime{
//定义了一个私有的静态成员,创建一个当前类的对象
private static Runtime currentRuntime = new Runtime();
//将构造方法私有,无法在外创建对象
private Runtime();
//定义了一个公开的静态方法,用于获取创建的唯一的当前类的对象
public static Runtime getRuntime(){
return currentRuntime;
}
}
常用方法 | 作用 |
---|---|
getTime() | 得到Date对应对象的毫秒数 |
after(Date when) | 判断参数是否是调用日期之后 |
before(Date when) | 判断参数是否再调用日期之前 |
特殊字符 | 作用 |
---|---|
yyyy | 年 |
MM | 月 |
dd | 日 |
hh | 12小时制 |
HH | 24小时制 |
mm | 分 |
ss | 秒 |
E | 星期 |
yyyy/MM/dd HH:mm:ss E | 2023/03/09 14:05:16 星期四 |
常用方法 | 返回值 | 作用 |
---|---|---|
format(Date date) | String | 将Date对象按日期模板转换为字符串 |
parse(String str) | Date | 将满足日期模板的字符串转换为Date对象 |
get(int field) | 根据日历字段获取对应的值 |
---|---|
getMaximum(int field) | 获取指定日历字段的最大值,如日期最大值为31 |
getActualMaximum(int field) | 获取指定日历字段的实际最大值,如11月的日期最大为30 |
getTime() | 将Calendar对象转换为Date对象 |
set(int field,int value) | 将指定的日历字段设置为指定值 |
set(int year,int month,int date) | 同时设置日历的年月日 |
setTime(Date date) | 将Date对象作为参数设置日历的信息 |
package com.hqyj.dateTest;
import java.util.Calendar;
import java.util.Scanner;
public class Test2 {
public static void main(String[] args) {
//实现"万年历"
//输入年份和月份,输出
Calendar c = Calendar.getInstance();
Scanner sc = new Scanner(System.in);
System.out.println("输入年份");
int year = sc.nextInt();
System.out.println("输入月份");
int month = sc.nextInt();
c.set(year, month - 1, 1);
//得到当月最大日期
int maxDate = c.getActualMaximum(Calendar.DATE);
//换行计数
int count = 0;
//输出空格
/*
* DAY_OF_WEEK 星期 空格数量
* 2 一 0
* 3 二 1
* 4 三 2
* 5 四 3
* 6 五 4
* 7 六 5
* 1 天 6
* //周天空6 其余空DAY_OF_WEEK-2
* */
System.out.println("一\t二\t三\t四\t五\t六\t日");
//获取当月1号是一周中的第几天
int week = c.get(Calendar.DAY_OF_WEEK);
//周天空6格
if (week == 1) {
System.out.print("\t\t\t\t\t\t");
//空格也需要计数
count += 6;
} else {
//其他情况空星期-2个格
for (int j = 1; j <= week - 2; j++) {
System.out.print("\t");
//空格也需要计数
count++;
}
}
//输出数字
for (int i = 1; i <= maxDate; i++) {
System.out.print(i + "\t");
//计数+1
count++;
//计数到7换行
if (count % 7 == 0) {
System.out.println();
}
}
}
}
Java是纯面向对象语言,宗旨是将一切事物视为对象处理。
但原始类型不属于对象,不满足面向对象的思想。但原始类型无需创建对象,保存在栈中,效率更高。
为了既保证效率又让原始类型也有对应的类类型,达到"万物皆对象"的理念,所以就有了包装类的概念。
包装类常用于字符串与原始类中之间的转换。
在web应用中,从浏览器页面中获取数据提交到服务器,全部都是String类型,所以一定要使用字符串转换为原始类型的方法。
包装类 | 原始类型 |
---|---|
Byte | byte |
Short | short |
Integer | integer |
Long | long |
Float | float |
Double | double |
Chartacter | char |
Boolean | boolean |
int对应Integer,char对应Character
包装类都是被final修饰的,不能被继承
除了Character类,其余包装类都有两个过时的构造方法,参数为对应的原始类型或字符串
Character只有一个参数为char类型的构造方法
构造方法的目的都是将原始类型的数据转换为包装类的对象
除了Character类,其余包装类都有静态方法"parse原始类型单词(String str)",用于将字符串转换为相应的原始类型
数值型的包装类parseXXX()方法,如果参数不是对应的数字,就会抛出NumberFormat异常,如"123a"或"123.4"都会报错
boolean的包装类Boolean的parseBoolean()方法,如果参数不是"true"这个单词的四个字母,转换结果都是false
除了Boolean类,其余包装类都有MAX_VALUE和MIN_VALUE这两个静态属性,用于获取对应类型支持的最大最小值
所有包装类都重写了toString(),用于将包装类对象转换为String对象
自动装箱缓冲区
//i1和i2保存的数字在byte范围[-127,127]内,这个值会共享,只会有一个"100"对象
Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1 == i2);//i1和i2引用同一个地址,结果为true
//i3和i4保存的数字不在byte范围[-127,127]内,会创建对象
Integer i3 = 128;//128对象
Integer i4 = 128;//128对象
System.out.println(i3 == i4);//i3和i4引用不同的地址,结果为false
System.out.println(i3.equals(i4));//包装类重写了equals,会拆箱后判断,结果为ture
使用自动装箱给包装类对象赋值,值的范围在byte范围[-127,127]内,这个值会保存在缓冲区中,如果多个对象都使用这个值,共享这一个数据,使用同一个地址,==判断结果true;值的范围不在byte范围[-127,127]内,就会创建新的包装类对象,会有不同的地址,==判断结果false
引用类型对象比较相同时,不要使用==,包括包装类的对象。比较相同时,使用包装类重写的equals方法.
当程序没有按开发人员的意愿正常执行,中途出现错误导致程序中断,这种情况,就称为异常。
学习异常就是认识异常的种类,如何处理异常和避免异常出现。
异常在程序中以对象的形式存在。当代码执行过程中出现异常,虚拟机会自动创建一个异常对象,如果没有对该异常对象进行处理,就会导致程序中断,不再执行后续内容。
异常在程序中以对象的形式存在,就有相应的类。
所有的异常类,组成了"异常家族"。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mPbZEMMA-1679503712167)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-10-15-26-11-image.png)]
如果出现xxxError,如StackOverFlowError,栈溢出,无法通过额外的代码解决,只能修改源代码。
RunTimeExcetpion运行时异常
如果一个异常类属于RunTimeExcetpion异常类的子类,称这个异常为运行时异常,可以通过编译,运行时可能抛出异常对象
常见运行时异常 | 说明 | 出现的情景 |
---|---|---|
NullPointerException | NullPointerException 空指针异 常 | 如用空对象null调用属性或方法 |
IndexOutOfBoundsException | n 索引越界异常 | 如当使用某个带有索引的对象超出范围 |
NumberFormatException | 数字格式异常 | 如调用包装类的parseXX()方法,如果参数不 能转换 |
InputMismatchException | 输入不匹配异常 | 如使用Scanner接收控制台输入时,如果输 入的数据不是对应的类型 |
ClassCastException | 对象转型异常 | 如Person p = (Person)Dog dog; |
ArithmeticException | 算术运算 异常 | 如0当分母 |
编译时异常
如果一个异常类属于RunTimeExcetpion异常类的子类,称这个异常为运行时异常,可以通过编译,运行时可能抛出异常对象
常见编译时异常 | 说明 | 出现的情景 |
---|---|---|
IOException | 输入输出流异常 | 使用流对象 |
FileNotFoundException | 文件未找到以 | 方法的参数为文件对象时 |
SQLException | 数据库相关异常 | 操作数据库时 |
通常所说的处理异常,指的是处理Exception类的子类异常。
处理异常的目的,就是保证程序正常执行。
这种方式处理异常,无论会不会抛出异常,都能让程序正常执行
try{
//可能出错的代码
}catch(异常类 异常对象){
//如果try中的代码抛出异常,异常对象属于catch中的异常类型,就会执行这里的代码
}catch(异常类 异常对象){
//如果try中的代码抛出异常,异常对象属于catch中的异常类型,就会执行这里的代码
}...{
}finally{
//无论程序是否会抛出异常,都要执行这里的代码
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KzJ8domQ-1679503712168)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-10-19-09-04-image.png)]
执行流程:先执行try中的内容,当出现异常,与后续每个catch中的异常类型进行匹配,如果匹配到对应的类型或异常父类时,执行后续大括号中的内容,最终一定执行finally中的内容。
如果代码会抛出多个异常,可以使用多个catch进行捕获。需要将子类异常放在最前,父类异常放在最后
try、catch、finally都不能单独使用,try必须配合catch或finally或一起使用
无论try中的代码是否会抛出异常,finally中的代码一定会执行
执行try中的代码是,如果出现异常,就不再执行try中剩余代码
try中定义的内容,无法在try之外的地方使用
try中如果有return,不影响finally的执行,finally优先于return执行
这种方法,可以让编译时异常通过编译。
在定义方法的时候,通过该关键字声明方法可能抛出的异常。
用法:方法的参数部分后,添加 throws ****异常类型1,****异常类型2.
public class Test{
//这时该方法就会有一个声明:该方法可能会抛出InterruptedException异常
public void fun() throws InterruptedException{
//sleep()方法在源码中声明了可能会抛出InterruptedException异常,
//InterruptedException异常不是RuntimeException的子类异常,必须要处理才能通过编译
//要么使用try-catch处理,要么继续声明有异常
Thread.sleep(500);
}
}
throws表示用于声明方法有可能出现的异常。使用时写在方法的小括号之后
public void fun() throws InterruptedException{
Thread.sleep(500);
}
throw用于手动抛出异常对象。使用时,写在方法体中,“throw 异常对象”。
常用于满足某种条件时,强制中断程序。
public void fun(){
throw
}
如果需要在某种情况下中断程序,可以自定义一个异常类。再通过throw关键字手动抛出自定义异常。
1.创建一个类,继承某个异常类
如果继承的是RuntimeException,表示自定义的异常类属于运行时异常,该异常对象可以不用处理
如果继承的是非RuntimeException,表示自定义的异常属于编译时异常,该异常对象必须要处理
2.可选操作。定义带参构造方法,参数为String类型的异常信息,调用父类中的构造方法
/*
* 自定义异常
* 只需继承某个异常类即可
* 是否定义构造方法根据实际情况决定
* */
public class MyException extends NullPointerException {
/*
* 带参构造,参数为异常信息
* */
public MyException(String msg){
super(msg);
}
/*
* 无参构造
* */
public MyException(){
super();
}
}
数组中保存的元素都是有序的,可以通过索引快速访问。
数组中保存的元素都是一种类型。
数组的长度在定义后,无法改变。
数组中无法获取其中实际保存的元素数量
能保存一组数据,元素可以有序和无序(存入的顺序和读取的顺序不一致)
集合中保存的元素的数据类型可以不同
集合的容量可以改变
可以获取集合中实际存在的元素数量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w5z2ldkO-1679503712169)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-13-10-08-29-image.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HlKT6gZ3-1679503712169)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-13-10-10-24-image.png)]
图上的所有实现类,都是非线程安全的,在多线程环境下使用以上任何集合,都会出现数据不准确的情况。
该接口有两个核心接口:List和Set。
这两个接口都可以保存一组元素,List接口保存原始有序可重复,Set接口保存元素无需不可重复。
Collection接口有一个父类接口Iterable,它不是一个集合,而是用于遍历集合的工具接口,包含forEach()和iterator()方法。
常用方法 | 返回值 | 作用 |
---|---|---|
add(Object obj) | boolean | 将元素添加到集合中 |
size() | int | 获取集合中的元素数量 |
isEmpty() | boolean | 判断集合是否为空 |
clear() | void | 清空集合 |
contains(Object obj) | boolean | 判断集合中是否包含指定元素 |
remove() | boolean | 移除集合中的指定元素 |
toArray() | Object[] | 将集合转换为数组 |
stream() | Streanm | 获取集合流对象 |
iterator() | Iterator | 得到集合的迭代器对象,用于遍历集合 |
有序集合,元素可以重复,允许保存null,可以通过索引获取对应的元素。
List接口在继承Colletion接口后,又拓展了一些操作元素的方法。
拓展方法 | 返回值 | 作用 |
---|---|---|
get(int index) | Object | 得到指定索引的元素 |
set(int index,Object obj) | Object | 使用obj替换index上的元素,返回被替换的元素 |
add(int index,Object obj) | void | 将obj添加到index上 |
remove(int index) | Object | 移除指定索引上的元素,返回被移除的元素 |
index(Object obj) | int | 得到obj第一次出现的索引 |
lastindex(Object ojb) | int | 得到obj最后一次出现的索引 |
subList(int from,int to) | List | 截取[from,to)区间的元素,返回子集合 |
采用数组实现集合。
可以通过索引访问元素,可以改变集合大小,可以要在其中插入和删除元素时,会影响后续元素。
该集合查询效率很高,在其中间删除或加入元素效率很低。
任何集合中保存的都是引用类,如集合中保存123,保存的不是int类型的123,二十integer类型的123.
构造方法 | 说明 |
---|---|
ArrayList() | 创建一个Object类型的空数组,在调用后添加方法,才会初始化数组大小为10. |
ArrayList(int initialCapacity) | 创建一个指定容量的Object类型数组,如果参数为负值,会抛出IllegalArgumentException异常 |
ArrayList(Collection c) | 根据指定集合创建Object类型数组 |
主要以List接口和Collection接口中的方法为主。
采用双向链表实现集合
集合中保存的每个元素称为节点,除首尾节点外,其他节点即保存了自己的数据,还保存了其前后节点的地址。
如果在双向链表的结构中进行插入和删除节点的操作时,不会影响其他节点现在的保存位置。
如果要查询某个节点的地址时,需要从头节点或则尾节点开始搜索目标节点的位置。
双向链表在中间插入和删除数据效率高,随机读取的效率低。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7td8qqj7-1679503712169)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-14-18-08-16-image.png)]
常用构造方法 | 说明 |
---|---|
LinkedList() |
主要以List接口和Collection接口中的方法为主,由于还实现了Deque接口,所以还有一些Deque接口中的方法。
常用来自于Deque接口中的方法 | 作用 |
---|---|
addFirst(Object obj) | 添加obj为头节点 |
addLast(Object obj) | 添加obj为尾节点 |
getFirst() | 得到头节点 |
gerLast() | 得到尾巴节点 |
removeFirst() | 删除头节点 |
removeLast() | 删除尾节点 |
这两个类都是List接口的实习类,保存的元素有序可重复,允许保存null。
ArrayList采用数组实现,随机读取效率高,插入和删除效率低,适用于查询。
LinkedList采用双向链表实现,插入和删除效率高,随机读取效率低,适用于频繁更新集合。
无序集合,元素不重复,允许保存null,没有索引。
Set接口中的方法都是继承于Collection接口。
哈希表,也成为散列表,是一种数据结构,能更快地访问数据.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h8ZbTgFq-1679503712170)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-14-18-21-54-image.png)]
采用哈希表实现
元素不重复,无序保存,允许保存一个null
本质是HashMap对象
使用HashSet集合时,通常要吃重写实体类中的equals和hashcode方法
常用构造方法 | 说明 |
---|---|
HashSet() | 创建一个空集合,实际是创建了一个HashMap对象 |
HashSet中没有定义属于自定的方法,都是父接口Set和Collection中的方法。
如果两个对象的equals方法结果为true,在没有重写equals方法的前提下,hashcode相同吗?
如果两个对象的hashcode不同,在没有重写equals方法的前提下,equals方法的结果为?
如果两个对象的hashcode相同,equals方法的比较结果为?
可能为true或false
String str1="hello";
String str2="hello";
//str1和str2使用同一个地址,hashcode相同,equals结果为true
String str3="通话";
String str4="重地";
//str3和str4不是同一个地址,但hashcode相同,这种情况称为哈希冲突,equals结果为
false
特殊的Set实现类,数据可以有序保存,可以重复,不能添加null
采用红黑树(自平衡二叉树)实现的集合
二叉树表示某个节点最多有两个节点
某个节点右侧节点值都大于左侧节点值
红黑树会经过"变色"和旋转达到二叉树的平衡
只能添加同一种类型的对象且该对象实现了Comparable接口
CompareTo()方法的返回值决定了能否添加新元素和新元素的位置
如果返回,视为每次添加的元素都是同一个,不再重复添加
如果为正数,将新的元素添加在现有元素之后
如果为负数,将新的元素添加在现有元素之前
添加的元素是自动排序
常用构造方法 | 说明 |
---|---|
TreeSet() | 常见一个空集合 |
独有方法 | 作用 |
---|---|
first() | 得到集合中第一个元素 |
last() | 得到集合中的最后一个元素 |
celliling(Object obj) | 得到集合中比参数大的元素中的最小元素 |
floor(Object obj) | 得到集合中比参数小的元素中的最大元素 |
Map成为映射,该集合中保存的数据时以键值对的形式保存,保存的键与值的映射关系。
键成为Key,值成为Value,键不能重复,允许出现一个null作为键,值没有限制
常用方法 | 作用 |
---|---|
size() | 得到键值对的数量 |
isEmty() | 判断是否为空集合 |
clear() | 清空所有键值对 |
put(Object key,Object Value) | 添加一组键值对 |
get(Object key) | 根据键得到对应的值 |
containsKey(Object key) | 判断是否存在某个键 |
containValue(Object value) | 判断是否存在某个值 |
keyset() | 得到键的集合 |
values() | 得到值的集合 |
entrySet() | 得到键值对的集合 |
remove(Object key) | 删除指定的键值对 |
常用构造方法 | 说明 |
---|---|
HashMap() | 创建一个大小为16,加载因子为0.75的空集合 |
使用Map接口中的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X90pCaep-1679503712170)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-14-19-16-12-image.png)]
JDK1.8之后,HashMap采用"数组+链表+红黑树"实现
当没有出现哈希表冲突时,元素保存在数组中
如果出现哈希冲突,在对应的位置上创建链表,元素保存在链表中
普通for循环
for(int i =0,i<集合.size(),i++){
s
}
增强for循环
for(数据类型 变量名 : 集合){
元素 变量 = 集合.get(i);
}
forEach()方法
使用该方法遍历集合时,不要使用add或remove操作,遍历会抛出异常。
集合.forEach(obj -> {
元素 变量 = 集合.get(i);
});
迭代器
//Collection接口有一个父接口Iterable,其中有一个iterator方法用于获取迭代器对象遍历集合
//所有Collection的子实现类都能调用该方法
Iterator it = Collection集合.iterator();
//hasNext()判断是否还有下一个元素
while(it.hasNext()){
//next()方法读取该元素
元素 变量 = it.next();
}
普通for循环无法遍历Set集合,因为元素没有索引
增强for循环
for(数据类型 变量名 : 集合){
元素 变量 = 集合.get(i);
}
forEach()方法
集合.forEach(obj -> {
元素 变量 = 集合.get(i);
});
迭代器
//Collection接口有一个父接口Iterable,其中有一个iterator方法用于获取迭代器对象遍历集合
//所有Collection的子实现类都能调用该方法
Iterator it = Collection集合.iterator();
//hasNext()判断是否还有下一个元素
while(it.hasNext()){
//next()方法读取该元素
元素 变量 = it.next();
}
Set keySet = 集合.keySets();
for(Object key :keySet){
Object value=集合.get(key);
}
一种规范,常用于限制集合中的元素类型,省去遍历集合时转换Object对象的过程。
//定义只能保存任意类型的元素,即Object类型
List list = new ArrayList();
list.add(123);
list.add("hello");
//遍历时只能使用Object类型获取
for(Object obj : list){
}
Collection是集合的跟接口,定义了操作集合中元素的方法。
Collection是集合的工具类,定义了操作集合中元素的静态方法。
Collections中的静态方法 | 说明 |
---|---|
Collections.shuffle(List list) | 打乱集合中元素的顺序 |
Collections.sort(List list) | 对集合中的元素进行排序。元素必须实现Comparable接 |
口。 | |
Collections.swap(List list,int | |
a,int b) |
将集合中索引a和b的元素交换位置 |
Collections.reverse(List list) | 反转集合中的元素 |
Collections.max(Collection c) | 得到集合中元素的最大值,元素必须实现Comparable接口 |
Collections.rotate(List list,int distance) | 将集合中第一个元素向后移动distance个元素 |
Collections.fill(List list,Object obj) | 使用obj填充集合 |
包含了一些操作数据的静态方法
常用静态方法 | 说明 |
---|---|
Arrays.sort() | 对数组的元素升序排序 |
Array.aList(T…obj) | 将可变参数转换为ArrayList集合 |
java中的File类,表示本地硬盘中的文件file或我呢见驾directory的一个类。
通过这个类创建对象,可以读取文件信息或操作对应文件。
常用构造方法 | 说明 |
---|---|
File(Strin pathName) | 根据文件的完整路径创建File对象 |
File(String parent,String child) | 根据文件的父目录的路径和自身创建File对象 |
File(File parent,String child) | 根据文件的父目录对应的Filed对象和自身的名称创建File对象 |
public class Demo {
public static void main(String[] args) {
//"C:\Users\23266\Desktop\test.txt"
File file = new File("C:\\Users\\23266\\Desktop\\test.txt");
System.out.println(file.exists());
File file1 = new File("C:\\Users\\23266\\Desktop","test.txt");
System.out.println(file.exists());
File parent = new File("C:\\Users\\23266\\Desktop ");
File file2 = new File(parent, "test.txt");
System.out.println(file.exists());
}
}
常用方法(File) | 作用 |
---|---|
exists() | 判断文件是否存在 |
isFile() | 判断是否为文件 |
isDirectory() | 判断是否为文件夹 |
getName() | 获取文件名 |
getPath() | 获取文件的相对路径 |
getAbsolutePath() | 获取文件的绝对路径 |
getParent() | 获取文件父目录路径 |
getParentFile() | 获取文件父目录对象 |
lastModfied() | 获取我呢见最后一次修改时间 |
length() | 获取文件字节大小 |
isHidden | 判断文件是否隐藏 |
delete() | 删除文件或空的目录 |
mkdir() | 新建指定文件夹 |
removeTo() | 移动且重命名文件 |
parent(文件夹).list() | 得到文件夹第一层文件名称的数组,返回String[] |
parent.listFile() | 得到文件夹中的第一层文件对象的数组,返回File[] |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pB2AZx8a-1679503712171)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-15-19-23-39-image.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GZWN3vk5-1679503712171)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-15-19-26-12-image.png)]
I:input输入
O:output输出
在java中,流表示计算机硬盘与内存之间传输数据的通道
将内存中的数据存入到硬盘中,称为写write,也称为输出Output。
将硬盘中的数据存入到内存中,称为读read,也称为输入Input
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OXRHEOx9-1679503712171)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-15-14-28-18-image.png)]
java中的流也是类,以对象的实现表示流,流有”四大家族“,是所有流的父类。
FileInputStream、ObjectInputStream
FileOutputStream、ObjectOutputStream
FileReader、BufferedReader、InputStreamReader
FileWriter、BufferedWriter、InputStreamWriter
输入流:InputStream、OutputStream
字符流:Reader、Writer
这四个父类都是java.io包下,都是抽象类,不能直接创建其对象,使用其子类对象
这四个父类都有close()方法,用于关闭流对象,释放资源
输入流(InputStream和Writer)都有flush()方法,用于将流中的数据冲刷到硬盘中
所有流中,以Stream结尾,都是字节流,数据以字节传输;以Reader或Writer结尾,都是字符流,数据以字符传输
读取硬盘中的数据时,读取的文件必须存在;写入数据到硬盘中时,写入的文件可以不存在,但父目录必须存在。
以字节的形式读取文件
常用构造方法 | 作用 |
---|---|
FileInputStream(String filePath) | 根据文件完整路径创建流对象 |
FileInputStream(File file) | 根据文件对象创建流对象 |
常用方法 | 作用 |
---|---|
read() | 读取一个字节,返回读取到的字节 |
read(byte[] bytes) | 读取指定数组大小的字节,返回读取到的字节数量 |
read(byte[] bytes,int off,int len) | 读取指定数组大小的字节,从off索引开始读取len个字节,返回读取到的字节数量 |
available | 文件可读取的最大字节数量 |
close() | 关闭流对象 |
以字节的形式读取文件
常用构造方法 | 说明 |
---|---|
FileOutputStream(String filePath) | 根据文件名创建流对象,覆盖写入 |
FileOutputStream(String filePath,boolean appen) | 根据文件名创建流对象,追加写入 |
FileOutputStream(File file) | 根据文件对象创建流对象,覆盖写入 |
FileOutputStream(File file,boolean appen) | 根据文件对象创建对象,追加写入 |
常用方法 | 作用 |
---|---|
write(int b) | 写入一个字节 |
write(byte[] bytes) | 写入一个字节数组 |
write(byte[] bytes,int off,int len) | 写入一个字节数组,从off开始到len个字节 |
flush() | 将流中的数据冲刷到硬盘中 |
close() | 关闭流对象 |
在通过FileInputStream对象使用ready(byte[] bytes)方法时,每次读取指定数组的字节,将读取到的字节保存在字节数组中。
该方法的返回值是读取到的字节数量,如果最后一次读取的字节数量不足字节数组的大小时,只会将读取到的内容覆盖数组中最前的几个元素。
在通过FileOutputStream对象使用write(byte[] bytes),会将字节数组中的所有内容写入到输出流中,在最后一次写入时,会写入多余的内容。
所以在写入时,使用write(byte[],bytes,int off,int len)方法,表示将字节数组中的内容,从off开始写入len个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GB9QFEEJ-1679503712172)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-15-20-05-48-image.png)]
public class Test2 {
public static void main(String[] args) throws IOException {//使用FileInputStream读取字节的同时使用FileOutputStream写入字节,实现单文件复制
//源文件
File source = new File("d:\\xxx.txt");
//读取
FileInputStream fis = new FileInputStream(source);
//写入到目标文件中
FileOutputStream fos = new FileOutputStream("f:\\copy.txt");
//定义字节数组,大小为8MB
byte[] bytes = new byte[1024*1024*8];
//读取指定数组大小的字节,返回读取到的字节数量
int count = fis.read(bytes);
while (count>0) {
//写入指定数组的字节
//fos.write(int b);写入一个字节
//fos.write(byte[] bytes);写入一个数组的字节
fos.write(bytes,0,count);//写入一个数组的字节,从0开始写入读取到的数量的字节
count = fis.read(bytes);
}
fis.close();
fos.close();
}
}
按字符读取文件
常用构造方法 | 说明 |
---|---|
FileReader(String fileName) | 根据文件名创建文件字符输入流对象 |
FileReader(File file) | 根据文件对象创建文件字符输入流对象 |
常用方法 | 作用 |
---|---|
ready() | 判断是否还有下一个字符 |
read() | 读取下一个字符,返回读取到的字符 |
read(char[] chars) | 按字符数组读取,返回读取到的字符数量,读取到的字符保存在字符数组中 |
close() | 关闭流对象 |
在读取纯文本文件(txt或md)时,首选该类。
常用构造方法 | 作用 |
---|---|
BufferedReader(Reader in) | 创建一个带有缓冲区(大小为8912的char数组)的字符流对象,参数为Reader类型对象,Reader是抽象类,所以实际参数为Reader的子类,如FileReader,在FileReader对象中定义要读取到的文件 |
BufferedReader(Reader in ,int size) | 创建一个指定缓冲区(字符数组)大小的字符输入流对象 |
常用方法 | 作用 |
---|---|
ready() | 判断是否还有字符 |
readLine() | 读取整行字符 |
close() | 关闭流对象 |
按字符写入文件,必须在调用flush()或close()方法后才会写入
常用构造方法 | 说明 |
---|---|
FileWriter(String filePath) | 根据文件路径创建流对象,覆盖写入 |
FileWriter(String filePath,boolean append) | 根据文件路径创建对象,append为true时追加写入 |
FileWriter(File file) | 根据文件对象创建流对象,覆盖写入 |
FileWriter(File file,boolean append) | 根据文件对象创建流对象,append为true时追加写入 |
常用方法 | 作用 |
---|---|
writer(String str) | 写入字符串 |
flush() | 冲刷数据到硬盘 |
close() | 关闭对象流 |
按字符写入,带有缓冲区的输出流。必须在调用flush()或flush()方法后才会写入。
常用构造方法 | 说明 |
---|---|
BufferedWriter(Writer writer) | 根据Writer对象创建字符缓冲输出流 |
常用方法 | 作用 |
---|---|
writer(String str) | 写入字符串 |
newLine() | 写入空白行 |
flush() | 冲刷数据到硬盘 |
close() | 关闭流对象 |
序列化:将对象转换为文件的过程。
被序列化的对象,其类必须要是实现Serializable接口。
这个接口是一个特殊的接口,其中没有定义任何方法,只是给类将上标记,表示该类可以被序列化。
构造方法 | 说明 |
---|---|
ObjectOutputStream(OutputStream os) | 根据字节输出流对象创建 |
常用方法 | 作用 |
---|---|
writerObject(Object obj) | 将一个对象写入本地文件 |
flush() | 冲刷数据到硬盘中 |
close() | 关闭流对象 |
反序列化:将文件转换为对象的过程。
构造方法 | 说明 |
---|---|
ObjectInputStream(InoutStream is) | 根据字节输入流对象创建 |
常用方法 | 作用 |
---|---|
readObject() | 对于序列化的文件,返回Object类型 |
close() | 关闭流对象 |
Person类,实现Serializable接口
/*
* 如果要序列化某个类的对象,该类必须要实现Serializable接口
* 该接口中没有定义任何方法,只是一个标记接口,表明该类可序列化
* */
public class Person implements Serializable {
private String name;
private int age;
private String sex;
//省略get/set/构造方法等
}
测试类
package com.hqyj.ObjectStreamTest;
import java.io.*;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
Person p1 = new Person("王海", 22, "男");
Person p2 = new Person("刘玉梅", 29, "女");
Person p3 = new Person("白伟强", 21, "男");
//将程序运行中的数据(对象)保存到本地,这个过程就称为序列化
ArrayList<Person> list = new ArrayList<>();
list.add(p1);
list.add(p3);
list.add(p2);
//创建 "对象输出字节流" 对象,参数为字节输出流对象,这里使用FileOutputStream子类
ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("person.p"));
oos.writeObject(list);
oos.close();
//创建一个对象,将其保存为一个文件,再读取该文件中保存的对象
//ObjectInputStream 反序列化
ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("person.p"));
//读取序列化文件中的对象
Object obj = ois.readObject();
//需要转换为相应的类型后使用
ArrayList<Person> pList = (ArrayList<Person> ) obj;
for (Person person : pList) {
System.out.println(person);
}
oos.close();
ois.close();
}
}
属于转换流(将字节流转换为字符流)
属于转换流(将字节转换为字符流)
进程就是操作系统中正在执行的程序.
一个程序就是一个执行的进程实体对象。
每个运行的进程,都有属于他独立的内存空间,各个进程之间互不影响。
线程是一个进程的执行单元,一个进程可以多个线程。
每个线程之间可以访问同一个进程中的资源。
每个线程都有一块独立的栈空间,这些线程所在的栈空间位于同一个进程空间中。
如果一个进程中,同时执行着多个线程,就称为多线程。
多线程可以提高程序执行效率。
其实每个执行的java程序都是多线程执行。所以main方法所在的线程称为主线程,还有GC线程(守护线程)在同时执行。
各个进程同时运行,称为并行。
多个线程同时执行,称为并发
所有任务排队执行,成为同步执行。
在执行任务A的同时,执行任务B,称为异步执行。
Java中,线程以对象的形式存在。
Thread类表示线程类。
获取当前正在运行的线程对象
Thread thread =Tread.cuttentThread();
创建一个线程对象
常用构造方法 | 说明 |
---|---|
Thread() | 创建一个线程对象 |
Thread(String name) | 创建一个指定线程名的线程对象 |
Thread(Runnable target) | 根据Runnable对象创建线程对象 |
Thread(Runnable target,String name) | 根据Runnable对象和指定线程名的线程对象 |
常用方法 | 作用 |
---|---|
getId() | 获取线程Id |
gatName() | 获取线程名 |
getPriority() | 获取线程优先级,默认为5 |
getState() | 获取线程状态 |
setName(String name) | 设置线程名 |
setPriority(int i ) | 设置线程优先级,范围在1~10,数字越大优先级越高,越先执行完 |
isDaemon() | 判断是否为守护线程 |
setDaemon(boolean on ) | 设置线程是否为守护线程 |
start() | 让线程进入就绪状态,等待执行 |
run() | 在线程获得执行权时要执行的方法(线程要做的事情) |
Thread.sleep(long m) | 让线程休眠m毫秒后继续执行 |
Thread.currentThread() | 获取当前执行的线程对象 |
1.创建一个类,继承Thread类
2.重写Thread类中run()方法,该方法
3.创建自定义的线程对象后,调用start(方法,让线程就绪)
自定义Thread类的子类(子线程)
/*
- 1.创建一个类,继承Thread类
- 2.重写Thread类中的run()方法,该方法表示当该线程执行时要做的事情
- 3.创建自定义的线程对象后,调用start()方法,让线程就绪
*/public class ThreadDemo extends Thread{
@Override
public void run(){
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
mian类(主线程)
public static void main(String[] args) {
new ThreadDemo().start();
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
由于Java中是单继承,如果某个类已经使用了extends继承了某个类,就无法在继承Thread类。
这时就可以自定义线程实现Runnable接口的方式实现多线程
1.自定义一个类,实现Runnable接口
2.重写run()方法,将多线程要执行的内容写在该方法中。
3.创建Runnable接口的实现类对象
4.使用Thread(Runnable)构造方法,将Runnable接口实现对象包装为Thread对象
自定义Runnable接口的实现类
/*
* 如果一个类已经是其他类的子类,无法再继承Thread
* 这时实现Runnable接口,重写Run方法
* 再将该类对象当做Thread(Runnable target)构造方法的参数,包装为线程类
* */
public class MyThread2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() +":"+ i);
}
}
}
mian类
public class Main{
public static void main(String[] args) {
//创建两个子线程对象
//使用new Thread(Runnable target)构造方法,将自定义线程子类包装为Thread
Thread t1 = new Thread(new MyThread2());
Thread t2 = new Thread(new MyThread2());
t1.start();
t2.start();
}
}
如果不想创建Thread的子类或Runnable的实现类,就可以将匿名内部类当做Runnable接口的实现类来使用,甚至可以配合lambda表达式来更进一步简化。
public class ThreadDemo2 {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
},"线程A");
t1.start();
new Thread(()->{
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
},"线程B").start();
}
}
线程的初始化到终止的过程,成为线程的生命周期。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lgRa7sQC-1679503712172)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-17-14-07-18-image.png)]
当线程对象创建成功后,进入新生状态
当某个线程对象调用了start()方法后,就进入就绪状态。
在这个状态下,线程对象不会做任何事情,只是等待CPU分配时间片。
当某个线程对象得到CPU时间片(CPU执行该线程时分配得时间),进入运行状态,开始执行run()方法。
不会等到run()方法执行完毕,只会在指定的时间内尽可能地执行run()方法。
如果多线程下,某个线程对象在调用完run()方法后,就会进入就绪状态。
如果某个线程遇到了sleep()方法或wait()方法时,就会进入阻塞状态。
sleep()方法在指定时间后,就会让线程重新就绪。
wait()方法只有在被调用notifyAll()方法将其唤醒后才能重新就绪。
当某个线程的run()方法中的所有内容执行完,就会进入终止状态。
如果将一个线程设置为setDaemon(true),表示该线程为守护线程。
public class DaemonDemo {
public static void main(String[] args) {
//将含有死循环的线程设置为守护线程
Thread t1 = new Thread(()->{
while (true) {
System.out.println("守护线程运行中......");
}
});
t1.setDaemon(true);
t1.start();
/*
* 普通线程,有限循环
* */
new Thread(()->{
for (int i = 1; i <= 100; i++) {
System.out.println("普通线程运行中......");
}
}).start();
//随着有限循环结束,死循环线程也会接受
//非守护线程结束,守护线程也会接受
}
}
如有10张票,三个窗口同时卖票
/*
* 模拟多窗口卖票
* */
public class TicketDemo implements Runnable{
private Integer ticket=10;
//如有10张票,三个窗口同时卖票
public void sell() throws InterruptedException {
while ((true)) {
if ((ticket>0)) {
Thread.sleep(1000);
ticket =ticket-1;
System.out.println(Thread.currentThread().getName()+"售出一张,剩余"+ticket+"张票");
}else {
System.out.println("已售罄");
break;
}
}
}
@Override
public void run(){
try {
sell();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
TicketDemo td = new TicketDemo();
Thread t1 = new Thread(td,"A");
Thread t2= new Thread(td,"B");
Thread t3 = new Thread(td,"C");
t1.start();
t2.start();
t3.start();
}
}
运行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N0cQKngY-1679503712172)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-17-15-03-44-image.png)]
由于线程调用start()方法后,就会进入就绪状态。当线程获得了CPU时间片,就开始执行run()方法
并不会等待run()方法执行完毕,所以很有可能在线程A执行run()方法过程中,线程B也开始执行run()方法,造成数据共享时出错。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOWlcTHd-1679503712173)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-17-14-57-26-image.png)]
如果要防止在线程A执行过程中B插队的情况,可以让所有线程排队同步执行。
这样一来,某个线程执行run()方法的时候,其他线程就会等待run()执行完毕。
这个关键字可以修饰方法或代码块
写在方法的返回值前,这是该方法就称为同步方法
public synchronized void sell(){}
synchornized(要同步的对象){
//该方法执行过程中,其他线程无法插队
}
每个对象默认都有一个"锁",当某个线程运行时被synchornized修饰方法时,
该对象就会拥有这把锁,在拥有锁的过程,其他线程不能同时访问该方法,只能等待其他执行结束后,才能释放这把锁。
使用synchornized修饰后的锁被称为"悲观锁"。
方法被synchornized修饰后,称为同步方法,会让原本多线程执行变成了单线程执行(异步变同步).
实现多线程的方式
继Thread类,创建对象
实现Runnable接口后,包装成Thread对象
匿名内部类
为什么说StringBuilder或ArrayList,HashMap是非线性安全的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ym2KMwYN-1679503712173)(C:\Users\23266\AppData\Roaming\marktext\images\2023-03-17-15-38-30-image.png)]
什么叫死锁?怎么产生?如何解决?
如两个人吃西餐,必须要有刀和叉,当前只有一副刀叉。
如果A拿到了刀,B拿到了叉,互相都在等待对象释放另一个工具,如果不释放,就会进入僵持局面,这个局面就叫做死锁,既不结束,也不继续。
定义两个线程类,线程A先获取资源A后,再获取资源B;线程B先获取资源B后,再获取资源A。
如果对资源A和资源B同步执行,线程A
PersonA类
public class PersonA implements Runnable{
private Object knife;
private Object fork;
public PersonA(Object knife, Object fork) {
this.knife = knife;
this.fork = fork;
}
@Override
public void run() {
synchronized (knife){
System.out.println("拿到了刀,三秒后拿到叉");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (fork){
System.out.println("拿到了叉,就可以吃饭啦");
}
}
}
}
personB类
public class PersonB implements Runnable{
private Object knife;
private Object fork;
public PersonB(Object knife, Object fork) {
this.knife = knife;
this.fork = fork;
}
@Override
public void run() {
synchronized (fork){
System.out.println("拿到了叉,三秒后拿到刀");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (knife){
System.out.println("拿到了刀,就可以吃饭啦");
}
}
}
}
Mian方法
public class Main {
public static void main(String[] args) {
Object knife = new Object();
Object fork = new Object();
new Thread(new PersonA( knife,fork)).start();
new Thread(new PersonB( knife,fork)).start();
}
}
如两个线程都先获取knife,再获取fork
如获取knife与fork之前,先获取paper
PersonA类
public class PersonA implements Runnable{
private Object knife;
private Object fork;
private Object paper;
public PersonA(Object knife, Object fork,Object paper) {
this.knife = knife;
this.fork = fork;
this.paper=paper;
}
@Override
public void run() {
synchronized (paper){
synchronized (knife){
System.out.println("拿到了刀,三秒后拿到叉");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (fork){
System.out.println("拿到了叉,就可以吃饭啦");
}
}
}
}
}
PersonB类
public class PersonB implements Runnable {
private Object knife;
private Object fork;
private Object paper;
public PersonB(Object knife, Object fork, Object paper) {
this.knife = knife;
this.fork = fork;
this.paper = paper;
}
@Override
public void run() {
synchronized (paper) {
synchronized (fork) {
System.out.println("拿到了叉,三秒后拿到刀");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (knife) {
System.out.println("拿到了刀,就可以吃饭啦");
}
}
}
}
}
Main
public class Main {
public static void main(String[] args) {
Object knife = new Object();
Object fork = new Object();
Object paper = new Object();
new Thread(new PersonA( knife,fork,paper)).start();
new Thread(new PersonB( knife,fork,paper)).start();
}
}
1.java容器有哪些?
答:List、ArrayList、Vector及map、HashTable、HashMap、Hashset
2.Collection 和 Collections 有什么区别?
答:Collection 是集合类的上级接口,继承它的主要有 List 和 Set; Collections 是针对集合类的一个工具类,它提供了一些列的静态方法实现,如 Collections.sort() 排序,Collections.reverse() 逆序等。
3.HashMap 和 Hashtable 有什么区别?
答:HashMap是非线程安全的,Hashtable是线程安全的。HashMap允许null作为键或值,Hashtable不允许,运行时会报NullPointerException。
4.如何决定使用HashMap还是TreeMap?
答:对于在Map中插入、删除、定位一个元素这类操作,HashMap是最好的选择。因为相对而言,HashMap插入更快。但如果对一个key集合进行有序的遍历,那TreeMap是更好的选择。
5.说一下HashMap的实现原理?
答:HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。
因为HashMap的好处非常多,我曾经在电子商务的应用中使用HashMap作为缓存。因为金融领域非常多的运用Java,也出于性能的考虑,我们会经常用到HashMap和ConcurrentHashMap。
6.说一下HashSet的实现原理?
答:HashSet 实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变;此类允许使用 null 元素;HashSet 中不允许有重复元素。HashSet 是基于 HashMap 实现的,HashSet 中的元素都存放在 HashMap 的 key 上面,而 value 中的值都是统一的一个 private static final Object PRESENT = new Object()。HashSet跟HashMap一样,都是一个存放链表的数组。
7.ArrayList和LinkedList 的区别是什么?
答:
1.默认情况下,比如调用ArrayList和LinkedList的add(e)方法,都是插入在最后,如果这种操作比较多,那么就用LinkedList,因为不涉及到扩容。
2.如果调用ArrayList和LinkedList的add(index, e)方法比较多,就要具体考虑了,因为ArrayList可能会扩容,LinkedList需要遍历链表,这两种到底哪种更快,是没有结论的,得具体情况具体分析。
3.还有,如果是插入场景比较少,但经常需要查询的话,查询分两种,第一种就是普通遍历,也就是经常需要对List中的元素进行遍历,那么这两种是区别不大的,遍历链表和遍历数组的区别,第二种就是经常需要按指定下标获取List中的元素,如果这种情况如果比较多,那么就用ArrayList
8.如何实现数组和List之间的转换?
答:对象数组转对象List使用Arrays.asList(),对象数组转对象List使用Arrays.asList()。
9.Array和 ArrayList有何区别?
答:区别1:存储类型不同,Array:只可存储基本数据类型和对象,ArrayList:只能存储对象
区别2:大小不同Array,被设置为固定大小,ArrayList:是一个可变数组,大小可自动调整
区别3:对象所包含的方法不同,Array:所包含的方法没有ArrayList多,ArrayList有很多操作方法:addAll、removeAll、iteration等
10.哪些集合类是线程安全的?
答:线程安全的集合有Vector、HashTable、Stack、ArrayBlockingQueue、ConcurrentHashMap、ConcurrentLinkedQueue等
11迭代器Iterator是什么?
答:迭代器iterator)有时又称光标(cursor)是程序设计的软件设计模式可在容器对象(container,例如链表或数组)上遍访的接口,设计人员无需关心容器对象的内存分配的实现细节。
12.Iterator 怎么使用?有什么特点?
答:Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
13.Iterator和ListIterator有什么区别?
答:1、Iterator是ListIterator的父接口。
2、Iterator是单列集合(Collection)公共取出容器中元素的方式。
对于List,Set都通用。而ListIterator是List集合的特有取出元素方式。
3、Iterator中具备的功能只有hashNext(),next(),remove();ListIterator中具备着对被遍历的元素进行增删改查的方法,可以对元素进行逆向遍历。之所以如此,是因为ListIterator遍历的元素所在的容器都有索引
14.怎么确保一个集合不能被修改?
答:可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。
15.线程和进程的区别?
答:1.根本区别:进程是操作系统进行资源分配的最小单元,线程是操作系统进行运算调度的最小单元。
2.从属关系不同:进程中包含了线程,线程属于进程。
3.开销不同:进程的创建、销毁和切换的开销都远大于线程。
4.拥有资源不同:每个进程有自己的内存和资源,一个进程中的线程会共享这些内存和资源。
5.控制和影响能力不同:子进程无法影响父进程,而子线程可以影响父线程,如果主线程发生异常会影响其所在进程和子线程。
6.CPU利用率不同:进程的CPU利用率较低,因为上下文切换开销较大,而线程的CPU的利用率较高,上下文的切换速度快。
7.操纵者不同:进程的操纵者一般是操作系统,线程的操纵者一般是编程人员。
答:1.继承Thead类创建线程
(1)继承Thread类并重写run方法
(2)创建线程对象
(3)调用该线程对象的start()方法来启动线程
2.实现Runnable接口创建线程
(1)定义一个类实现Runnable接口,并重写该接口的run()方法
(2)创建 Runnable实现类的对象,作为创建Thread对象的target参数,此Thread对象才是真正的线程对象
(3)调用线程对象的start()方法来启动线程
3.使用Callable和Future创建线程
和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大:call()方法可以有返回值,可以声明抛出异常。
17.说一下 runnable 和 callable 有什么区别?
答:1、最大的区别,runnable没有返回值,而实现callable接口的任务线程能返回执行结果
2、callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,try catch,但是runnable接口实现类中run方法的异常必须在内部处理,不能抛出。
18.在 Java 程序中怎么保证多线程的运行安全?
答:1.使用手动锁lock2.使用线程安全的类3.使用自动锁synchronized关键字4.使用volatile关键字。
19.什么是死锁?
答:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去;此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
20.多线程产生死锁的4个必要条件?
答:
21.怎么防止死锁?
答:1.不使用synchronized这个显式的锁,而采用信号量控制资源,可被多少线程访问
2.当设置资源只可被一个线程访问时,则此时为锁住状态,信号量中可设置获取的超时时间, 对无法成功获取,可进行重复尝试,或指定尝试次数后,也可立即退。
22.synchronized和 Lock有什么区别?
答:1、lock是一个接口,而synchronized是java的一个关键字。2、synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁;而lock发生异常时,不会主动释放占有的锁,必须手动来释放锁,可能引起死锁的发生。
23.sleep() 和 wait() 有什么区别?
答:所在类不同
sleep()是Thread类的静态方法。
wait()是Object类的方法。
锁释放不同
sleep()是不释放锁的。
wait()是释放锁的。
用途不同
sleep()常用于一定时间内暂停线程执行。
wait()常用于线程间交互和通信。
用法不同
sleep()方法睡眠指定时间之后,线程会自动苏醒。
wait()方法被调用后,可以通过notify()或notifyAll()来唤醒wait的线程。
24.引用的四种对象?
答:强引用 软引用 弱引用 虚引用
25.什么是 Java 序列化?什么情况下需要序列化?
答:Java序列化是将Java对象转换为字节流的过程。当Java 对象需要在网络上传输或者持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
和资源。
5.控制和影响能力不同:子进程无法影响父进程,而子线程可以影响父线程,如果主线程发生异常会影响其所在进程和子线程。
6.CPU利用率不同:进程的CPU利用率较低,因为上下文切换开销较大,而线程的CPU的利用率较高,上下文的切换速度快。
7.操纵者不同:进程的操纵者一般是操作系统,线程的操纵者一般是编程人员。
答:1.继承Thead类创建线程
(1)继承Thread类并重写run方法
(2)创建线程对象
(3)调用该线程对象的start()方法来启动线程
2.实现Runnable接口创建线程
(1)定义一个类实现Runnable接口,并重写该接口的run()方法
(2)创建 Runnable实现类的对象,作为创建Thread对象的target参数,此Thread对象才是真正的线程对象
(3)调用线程对象的start()方法来启动线程
3.使用Callable和Future创建线程
和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大:call()方法可以有返回值,可以声明抛出异常。
17.说一下 runnable 和 callable 有什么区别?
答:1、最大的区别,runnable没有返回值,而实现callable接口的任务线程能返回执行结果
2、callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,try catch,但是runnable接口实现类中run方法的异常必须在内部处理,不能抛出。
18.在 Java 程序中怎么保证多线程的运行安全?
答:1.使用手动锁lock2.使用线程安全的类3.使用自动锁synchronized关键字4.使用volatile关键字。
19.什么是死锁?
答:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去;此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
20.多线程产生死锁的4个必要条件?
答:
21.怎么防止死锁?
答:1.不使用synchronized这个显式的锁,而采用信号量控制资源,可被多少线程访问
2.当设置资源只可被一个线程访问时,则此时为锁住状态,信号量中可设置获取的超时时间, 对无法成功获取,可进行重复尝试,或指定尝试次数后,也可立即退。
22.synchronized和 Lock有什么区别?
答:1、lock是一个接口,而synchronized是java的一个关键字。2、synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁;而lock发生异常时,不会主动释放占有的锁,必须手动来释放锁,可能引起死锁的发生。
23.sleep() 和 wait() 有什么区别?
答:所在类不同
sleep()是Thread类的静态方法。
wait()是Object类的方法。
锁释放不同
sleep()是不释放锁的。
wait()是释放锁的。
用途不同
sleep()常用于一定时间内暂停线程执行。
wait()常用于线程间交互和通信。
用法不同
sleep()方法睡眠指定时间之后,线程会自动苏醒。
wait()方法被调用后,可以通过notify()或notifyAll()来唤醒wait的线程。
24.引用的四种对象?
答:强引用 软引用 弱引用 虚引用
25.什么是 Java 序列化?什么情况下需要序列化?
答:Java序列化是将Java对象转换为字节流的过程。当Java 对象需要在网络上传输或者持久化存储到文件中时,就需要对 Java 对象进行序列化处理。