java高级知识总结
string字符串
string字符串是一个常量,创建后值不可更改。
String str="hello";//这句话在执行时,创建一个hello字符串对象,将其地址保存在str变量中
str="abc";//这里看似是在改变字符串的值,实际是又创建了一个字符串对象abc,将其地址保存在str变量
string字符串的常用方法
常用构造方法 | 作用 |
---|---|
String() | 创建一个空白字符串对象,实际创建一个空字符数组 |
String(String str) | 创建一个指定的字符串对象,实际是创建一个形参字符串的副本 |
String(char[] list) | 创建一个指定字符数组的字符串对象。 |
String(byte[] list) | 按默认编码格式创建一个指定字节数组的字符串对象。 |
String(byte[] list,String charsetName) | 按指定的编码格式创建一个指定字节数组的字符串对象。 |
字符串相关测试题
//题目一
String str1="ab";
String str2=new String("ab");
String str3="a"+"b";//这个过程发生在编译阶段,实际保存的就是"ab"
String str4="你好"+str1;//这里的+,java会创建一个StringBuilder对象
String str5="你好"+str2;
String str6="你好ab";
System.out.println(str1==str2);//false
System.out.println(str1==str3);//true
System.out.println(str3==str2);//false
System.out.println(str4==str5);//false
System.out.println(str5==str6);//false
//题目二
//这两句话执行后,会创建几个对象
String str1="ab";
String str2="a"+"b";
//1个:存在于字符串常量池中的"ab"
//题目三
//这两句话执行后,会创建几个对象
String str1=new String("ab");//这句话执行后有两个new String()和"ab"
String str2=new String("ab");//这句话执行后有一个new String()
//3个:存在于字符串常量池中的“ab”和堆中的两个String对象
//题目四
//这两句话执行后,会创建几个对象
String str1 = new String("a");//2个,"a"和new String()
String str2 = str1 + "b";//2个,"b"和new StringBuilder()
//4个
字符串常用方法
方法名 | 返回值 | 作用 |
---|---|---|
length() | int | 获取字符串的长度 |
trim() | String | 去除字符串首尾的所有空格 |
toLowerCase() | String | 转换字符串为小写 |
toUpperCase() | String | 转换字符串为大写 |
isEmpty() | boolean | 判断是否为一个空字符串 |
getBytes() | byte[] | 按默认编码格式将字符串转换为字节数组 |
toCharArray() | char[] | 将字符串转换为字符数组 |
equalsIgnoreCase(String str) | boolean | 忽略大小写比较指定字符串是否相同 |
equals(String str) | boolean | 判断字符串是否相同 |
charAt(int index) | char | 获取index位置上的字符串 |
indexOf(String str) | int | 获取str第一次出现的位置,如果没有返回-1 |
lastIndexOf(String str) | int | 获取str最后一次出现的位置,如果没有返回-1 |
contains(字符序列) | boolean | 判断指定的字符序列(字符串)是否存在于原字符串中 |
concat(String str) | String | 将str拼接到原字符串末尾 |
startsWith(String str) | boolean | 判断是否以指定字符串开头 |
endsWith(String str) | boolean | 判断是否以指定字符串结尾 |
substring(int index) | String | 截取原字符串在[index,数组长度)区间内的字符。(从指定位置截取至末尾,包含指定位置) |
substring(int from,int to) | String | 截取原字符串在[from,to)区间内的字符。(从from截取至to,包含from不包含to) |
split(String reg) | String[] | 按指定字符串或正则表达式切分原字符串。如果指定内容不在末尾,n个指定字符能得到n+1个子串;如果指定内容在末尾,n个指定字符能得到n个子串(不包含末尾的无效字符)。 |
String.valueOf(参数) | String | 将一个参数转换为字符串,参数可以是原始类型,也可以是任意对象。 |
replace(char oldChar,char newChar) | String | 使用newChar替换oldChar |
可变字符串
StringBuilder类
用于表示可变字符串的一个类,是非线程安全的,建议在单线程环境下使用,效率略高于StringBuffer。
StringBuffer类
用于表示可变字符串的一个类,是线程安全的,建议在多线程环境下使用,效率略低于StringBuilder。
StringBuilder和StringBuffer中的方法作用都一致,只不过StringBuffer中的方法使用了synchronized关键字修饰,表示一个同步方法,在多线程环境下不会出现问题。
构造方法
常用构造方法 | 作用 |
---|---|
StringBuilder() | 创建一个大小为16的字符数组。类似于String str=“”; |
StringBuilder(int capacity) | 创建一个指定大小的字符数组 |
StringBuilder(String str) | 创建一个str长度+16的字符串数组后,将str添加到其中。类似于String str=“初始值”; |
普通方法
方法 | 作用 |
---|---|
append(Object obj) | 将指定内容添加到原可变字符串对象末尾 |
delete(int start,int end) | 删除[start,end)范围内的字符 |
deleteCharAt(int index) | 删除指定索引的字符 |
insert(int index,Object obj) | 将obj添加到index位置上 |
replace(int start,int end,String str) | 将[start,end)范围内的字符替换为str |
reverse() | 翻转原字符串 |
可变字符串与不可变字符串之间的转换
有些方法如indexOf()、charAt()等,在String和StringBuilder中都存在,可以不用转换。
但有些方法如getBytes()、contains()等,只能通过String调用,这时就需要进行转换。
不可变字符串转换为可变字符串
通过创建一个可变字符串对象,将不可变字符串作为参数实现转换
//定义一个不可变字符串对象
String str="hello";
//创建一个可变字符串对象,将不可变字符串对象作为参数
StringBuilder sb = new StringBuilder(str);
可变字符串转换为不可变字符串
通过调用可变字符串的toString()方法实现转换
//创建一个可变字符串对象
StringBuilder sb = new StringBuilder("hello");
//调用toString()转换为String类型
String str=sb.toString();
比较String、StringBuilder、StringBuffer这三个类的区别
相同点
- 这三个类都可以用于表示或操作一个字符串
- 这三个类中有公共的方法,如indexOf()、charAt()等、
- 这三个类都是被final修饰的类,不能被继承
不同点
- String定义的字符串是一个常量,不可变;StringBuilder和StringBuffer定义的字符串是一个变量,可以改变。
- String类中的方法调用后,会创建字符串副本,不会影响原字符串;可变字符串对象调用的方法,直接操作原字符串,会影响。
- StringBuilder是非线程安全的,StringBuffer是线程安全的,其中的方法使用synchronized修饰为同步方法。在多线程情景下,需要使用StringBuffer。StringBuilder效率略高于StringBuffer。
Date类(表示日期与时间)
构造方法
常用构造方法 | 说明 |
---|---|
Date() | 创建当前瞬间的日期对象 |
Date(long l) | 创建指定毫秒数对应的日期对象。(从1970.1.1起经过了的毫秒数) |
Date(int year,int month,int date) | 该构造方法已过时。创建指定年月日的日期对象。(年从1900年起经过的年数,月用0-11表示1-12月) |
常用方法
常用方法 | |
---|---|
getTime() | 得到对应Date对象指定的毫秒数。 |
setTime(long l) | 设置毫秒数 |
after(Date when) | 判断调用日期对象是否在when之后 |
before(Date when) | 判断调用日期对象是否在when之前 |
SimpleDateFormat类(格式化日期)
构造方法
//参数为一个日期模板
SimpleDateFormat(String pattern);
//如定义这样的一个模板对象
SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
//创建一个Date对象
Date now = new Date();
//调用格式化方法,参数为一个Date对象
String res=sdf.formart(now);
//输出的形式为2022/07/28
System.out.println(res);
常用方法
常用方法 | 返回值 | 作用 |
---|---|---|
format(Date date) | String | 将Date对象按日期模板转换为字符串 |
parse(String str) | Date | 将满足日期模板的字符串转换为Date对象 |
Calendar类
用于表示日历的类。包含了很多日历相关的信息。
是一个抽象类,无法直接创建对象,可以通过该类的静态方法getInstance()获取该类的实例。
//获取Calendar类的实例
Calendar c = Calendar.getInstance();
日历字段
该类中定义了很多被final和static修饰的日历字段,用于获取指定信息
值 | 说明 |
---|---|
Calendar.YEAR | 获取年份 |
Calendar.MONTH | 获取月份(0-11表示1-12月) |
Calendar.DATE | 获取日期 |
Calendar.DAY_OF_WEEK | 获取星期(1-7表示周天到周六) |
Calendar.HOUR_OF_DAY | 获取二十四小时制的小时 |
Calendar.MINUTE | 获取分钟 |
Calendar.SECOND | 获取秒 |
Calendar.DAY_OF_MONTH | 本月中的第几天 |
Calendar.DAY_OF_YEAR | 本年中的第几天 |
Calendar.WEEK_OF_MONTH | 本月中的第几周 |
Calendar.WEEK_OF_YEAR | 本年中的第几周 |
常用方法
常用方法 | 说明 |
---|---|
get(日历字段) | 获取指定日历字段实际的值 |
getTime() | 获取对应的Date对象(Calendar转换为Date) |
getMaximum(日历字段) | 获取指定日历字段支持的最大值。如Calendar.DATE支持最大值为31 |
getActualMaximum(日历字段) | 获取指定日历字段在实际时间下的最大值。如4月下Calendar.DATE实际最大值为30 |
getTimeZone() | 获取当前日历在实际时间下的时区信息 |
set(日历字段,值) | 将指定日历字段设置为指定值 |
set(年,月,日) | 设置指定的年月日,没有设置的部分沿用当前日期。 |
实现万年历练习
import java.util.Calendar;
import java.util.Scanner;
public class Test5 {
public static void main(String[] args) {
while (true){
Calendar c = Calendar.getInstance();
System.out.print("请输入年份");
Scanner sc=new Scanner(System.in);
c.set(Calendar.YEAR,sc.nextInt());
System.out.print("请输入月份");
c.set(Calendar.MONTH,(sc.nextInt()-1));
System.out.println("一\t二\t三\t四\t五\t六\t日\t");
int day = c.getActualMaximum(Calendar.DATE);
int count=0;
c.set(Calendar.DATE,1);
int num= c.get(Calendar.DAY_OF_WEEK);
for (int i = 1; i <= day; i++) {
if (i==1){
if (num==1){
for (int x = 0; x < 6; x++) {
System.out.println(" \t");
count++;
}
}else{
for (int j = 0; j < (num-2); j++) {
System.out.print(" \t");
count++;
}
}
}
System.out.print(i+"\t");
count++;
if (count%7==0){
System.out.println();
}
}
}
}
}
三天上班两天摸鱼练习
package homework;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Scanner;
public class Test6 {
/*
从1号开始,上三天,耍两天
/* 1,2,3, 4,5, 6,7,8, 9,10, 11,12,13, 14,15, 16,17,18, 19,20, 21,22,23, 24,25, 26,27,28,
29,30, 31,1,2, 3,4, 5,6,7, 8,9 10*/
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
SimpleDateFormat sd = new SimpleDateFormat("yyyy/MM/dd");
Scanner sc = new Scanner(System.in);
System.out.print("请输入起始年份");
int s_year = sc.nextInt();
System.out.print("请输入起始月份");
int s_month = sc.nextInt();
System.out.print("请输入起始日");
int s_d = sc.nextInt();
c.set(s_year, s_month - 1, s_d);
long start = c.getTime().getTime();
System.out.print("输入查询年份");
int year = sc.nextInt();
System.out.print("输入查询月份");
int month = sc.nextInt();
System.out.print("输入查询日期");
int day1 = sc.nextInt();
c.set(year, month - 1, day1);
long end = c.getTime().getTime();
long day = (end - start) / 1000 / 3600 / 24;
if (day >= 0) {
int x = (int) ((day + 1) % 5);
switch (x) {
case 1:
case 2:
case 3:
System.out.println(sd.format(c.getTime()) + "\t该工作哦");
break;
case 0:
case 4:
System.out.println(sd.format(c.getTime()) + "\t可以摸鱼了");
break;
}
}else{
System.out.println("这个时间已经过了哦");
}
}
}
异常
Error错误
如果出现XXXXXError,如StackOverflowError栈空间溢出时,无法通过额外的代码去解决,只能修改源码。
Exception异常
如果出现XXXXException,如NullPointerException空指针异常时,可以通过额外代码去避免。
运行时异常和非运行时异常
如果一个异常属于RuntimeException异常类的子类,称为运行时异常,可以通过编译,运行时可能抛出异常对象。
如果一个异常属于Exception异常类的子类,称为非运行时异常,无法通过编译,只有处理后才能编译运行。
- 运行时异常(RuntimeException)
可以通过编译,可以不用处理,但在运行时有可能会抛出异常。
常见运行时异常 | 说明 | 出现的情景 |
---|---|---|
NullPointerException | 空指针异常 | 用空对象null调用方法或属性 |
ArrayIndexOutOfBoundsException | 数组下标越界异常 | 使用数组时,下标超出范围 |
ClassCastException | 对象转型异常 | Dog dog=(Dog)new Person();(Person类和Dog类不是继承关系) |
InputMismatchException | 输入类型不匹配异常 | 如需要int类型的数据,输入String类型的数据。 |
ArithmeticException | 算术运算异常 | 0当分母 |
IllegalArgumentException | 非法参数异常 | 调用方法的参数不合理 |
非运行时异常(除RuntimeException)
无法通过编译,必须要处理后才能编译运行。
常见非运行时异常 | 说明 | 出现的情景 |
---|---|---|
IOException | 输入输出流异常 | 当使用流对象 |
SQLException | 数据库SQL相关异常 | 操作数据库时 |
FileNotFoundException | 文件未找到异常 | 方法的参数为文件 |
处理异常Exception
只要处理Exception异常类的子类时,都称为处理异常。处理异常的目的就是为了保证程序正常运行,不要中断。
try-catch-finally语句
try{
//可能出现异常的代码
}catch(异常类 异常对象){
//如果出现异常对象,且与catch中的异常类匹配,则执行
}catch(异常类 异常对象){//后续的catch中的异常类型只能与之前的异常类型同级或是父类
//如果出现异常对象,且与catch中的异常类匹配,则执行
}finally{
//无论程序是否会抛出异常,一定要执行的代码
}
先执行try中的内容,当出现异常,与后续的catch进行匹配,如果匹配对应的异常类型或异常父类型,则执行大括号中的内容,最终一定执行finally中的内容
注意
- 执行try中的内容时,当某行代码抛出异常后,不再执行try中该行代码后续的内容。
- 无论try中是否会抛出异常,finally中的内容一定执行。通常finally用于释放资源。
- 如果有多个catch,需要将异常子类放在最前,异常父类放在最后
- try、catch、finally都不能单独使用,try需要配合catch或finally或catch和finally一起使用
- try中定义的内容,无法在try之外的地方使用
- try中如果有return,不影响finally的执行,并且finally优先于return执行
throws关键字
这种方式,可以让非运行时异常通过编译,定义方法的时候,声明可能抛出的异常
public class Test{
public void fun() throws InterruptException{//在定义方法时,声明可能抛出的异常类型
//如果直接写这句话,会报错,因为sleep()方法会抛出一个非运行时异常,必须要处理
Thread.sleep(1000);
}
}
throw和throws
throws表示用于方法声明异常。使用时写在方法的小括号之后:
public void fun() throws Exception{
}
throw用于手动抛出异常对象。使用时写在{}中,通常用于满足某种条件时,强制中断程序:
public void fun(){
RuntimeException e = new RuntimeException();
throw e;
}
自定义异常步骤
- 定义一个类,继承某个异常类。
如果继承的是RuntimeException,表示自定义的异常类属于运行时异常,该异常对象可以不用处理。
如果继承的是非RuntimeException,表示自定义的异常类属于非运行时异常,该异常对象必须要处理。 - [可选操作]定义一个无参数的构造方法,调用父类中无参的构造方法,定义一个带字符串参数的构造方法,调用父类带字符串参数的构造方法。
集合
集合的特点
- 能保存一组数据,可以有序也可以无序
- 集合的容量可变
- 集合中可以保存不同类型的数据
- 可以获取集合中实际的元素数量
集合框架(集合家族)
Iterable接口并不算严格意义上的集合的"根",它称为迭代器,用于遍历集合元素的一个工具接口。
所以集合的根接口为:Collection接口和Map接口
Collection接口
核心的两个子接口:Set和List。
这两个接口都可以保存一组数据,Set接口保存数据时,是无序不重复的;List接口保存数据时,是有序可重复的。
List接口(有序可重复)
有序集合,元素可以重复,允许保存null,可以通过索引获取对应位置上的元素。
在接口中定义了一些操作元素的方法,如获取元素数量、添加、删除、替换、截取等。
ArrayList实现类
- 采用数组实现的集合
- 可以通过索引访问元素、可以改变集合大小。如果要在其中插入或删除元素时,会影响其余元素。该集合查询效率高、增删中间元素效率低。
- 该集合对象中保存的元素,都是引用类型(对象的内存地址)。即使保存了123,其实不是保存的int类型的123,而是Integer类型的123.
构造方法
常用构造方法 | 说明 |
---|---|
ArrayList() | 创建一个无参的ArrayList对象。实际是创建了一个空的Object数组。在调用添加元素方法时,才会初始化该数组大小为10。 |
ArrayList(int capacity) | 创建一个指定容量的ArrayList对象,实际是创建一个指定大小的Object数组,如果参数为负,会抛出IllegalArgumentException异常 |
常用方法
常用方法 | 作用 | 返回值 |
---|---|---|
add(Object obj) | 添加obj到集合的末尾 | boolean |
add(int index,Object obj) | 添加obj到index索引上 | void |
get(int index) | 得到指定index索引的元素 | Object |
set(int index,Object obj) | 使用obj覆盖index索引的元素,返回被覆盖的元素 | Object |
size() | 获取集合中的元素数量 | int |
isEmpty() | 判断集合是否为空 | boolean |
clear() | 清空集合 | void |
remove(Object obj) | 删除指定obj对象 | boolean |
remove(int index) | 删除指定Index索引的元素,返回被删除的元素 | Object |
contains(Object obj) | 判断是否存在obj元素 | boolean |
indexOf(Object obj) | 得到元素obj第一次出现的索引 | int |
lastIndexOf(Object obj) | 得到元素obj最后一次出现的位置 | int |
subList(int from,int to) | 得到[from,to)区间内的元素 | List |
toArray() | 将集合转换为数组 | Object[] |
LinkedList实现类
-
采用双向链表实现的集合
-
集合中保存的每个元素也称为节点,除首尾节点外,每个节点即保存了自己的数据,也保存了其前一个和后一个节点的地址
-
如果在其中进行插入和删除的操作时,不影响其他元素的位置,只需要重新定义新节点的前后节点位置即可。
-
如果要查询某个节点的索引,需要从头结点或尾结点开始一步步得到目标节点位置
-
所以中间进行插入和删除的效率高,随机读取的效率低
构造方法
常用构造方法 | 说明 |
---|---|
LinkedList() | 创建一个空链表 |
常用方法
由于LinkedList实现了List接口,所有这里省略List接口中常用的方法,如add(Object obj)、Remove(Object obj)等。参考ArrayList中的方法
常用方法 | |
---|---|
addFirst() | 添加头结点 |
addLast() | 添加尾结点 |
getFirst() | 得到头结点 |
getLast() | 得到尾结点 |
remove() | 删除头结点 |
removeFirst() | 删除头结点 |
removeLast() | 删除尾结点 |
push(Object obj) | 添加头结点(入栈) |
pop() | 得到并移除头结点(出栈) |
peek() | 得到头结点 |
poll() | 删除头结点 |
offer(Object) | 添加尾结点 |
ArrayList和LinkedList的区别
- 两者都是List接口的实现类,保存的元素有序可重复,允许保存null,拥有一些公共的方法,如size(),isEmpty(),subList(int from,int to)等
- ArrayList采用数组实现,对于随机读取效率更高,通常用于查询;LinkedList采用双向链表实现,插入删除不影响其他元素位置,通常用于中间插入删除。
Set接口(无序不重复)
无序集合,元素不可以重复,允许保存null,没有索引。
在接口中定义了一些操作元素的方法,如获取元素数量、添加、删除、替换、截取等。
哈希表hash table
哈希表,也称为散列表,是一种数据结构,能更快地访问数据。
要保存的数据称为原始值,这个原始值通过一个函数得到一个新的数据,这个函数称为哈希函数,这个新数据称为哈希码,哈希码和原始值之间有一个映射关系,这个关系称为哈希映射,可以构建一张映射表,称为哈希表。在哈希表中,可以通过哈希码快速访问对应的原始值。
哈希码的特点
- 如果两个对象的hashCode不同,这两个对象一定不同
- 如果两个对象的hashCode相同,这两个对象不一定相同
- 如**“通话"和"重地”**这两个字符串的hashCode相同,但是两个对象
- hashCode相同,对象不同,称为哈希冲突
HashSet实现类
- 采用哈希表实现
- 元素不能重复,无序保存,允许保存null
- 本质是一个HashMap对象,调用add()方法,实际调用的也是HashMap中的put()方法,参数作为put()方法的键,new Obejct()作为put()方法的值
HashSet添加数据的原理
if(两个对象hashCode相同&&两个对象equals相同){视为同一对象,不能添加}
每次向集合中添加元素时,先判断该元素的hashCode是否存在,
- 如果不存在,视为不同对象,直接添加
- 如果存在,再判断equals方法的结果
- 如果为false,视为不同对象,直接添加
- 如果为true,视为同一对象,不能添加
可见不能添加的条件是两个对象的hashCode相同并且equals结果为true。如果每次都只判断equals的话,过程可能会很久,效率不高,如果每次只判断hashCode的话,有可能会有哈希冲突,所以先判断hashCode,再判断equals,既能保证效率又能保证添加进去的元素都是不相同的元素。
equals方法和hashCode的关系
- 如果两个对象的equlas方法比较结果为true,在没有重写equals方法的前提下,hashcode相同吗?
- 相同
- 如果两个对象的hashCode不同,在没有重写equals方法的前提下,equals方法比较结果为?
- false
- 如果两个对象的hashCode相同,equals方法比较结果为?
- 可能为true也可能是false
构造方法
常用构造方法 | 说明 |
---|---|
HashSet() | 创建一个默认的集合对象,实际是创建了一个大小为16,加载因子为0.75的HashMap对象 |
HashSet(int capacity) | 创建一个指定容量的集合对象,实际是创建了一个指定大小,加载因子为0.75的HashMap对象 |
HashSet(int capacity,float loadFactor) | 创建一个指定容量和指定加载因子的集合对象。 |
常用方法
常用方法 | 说明 |
---|---|
add(Object obj) | 添加元素到集合中 |
size() | 得到元素数量 |
isEmpty() | 判断集合是否为空 |
remove(Object obj) | 根据对象删除元素 |
contains(Object obj) | 判断是否存在某个元素 |
toArray() | 将集合转换为Object数组 |
clear() | 清空集合 |
如果想要保存的对象保证不重复,且无关顺序时,可以使用HashSet。
TreeSet实现类
- 特殊的Set实现类,数据可以有序保存,可以重复,不能添加null元素
- 采用红黑树(自平衡二叉树)实现的集合
- 二叉树表示某个节点最多有两个子节点
- 某个节点右侧的节点值都大于左侧节点值
- 只能添加同一种类型的对象且实现了Comparable接口的对象
- 实现Comparable接口后必须要重写compareTo方法
- 每次调用添加时,参数会自动调用该方法
- 添加的元素可以自动排序
- compareTo方法的返回值决定了能否添加新元素和新元素的位置
- 如果返回0,视为每次添加的是同一个对象,不能重复添加
- 如果返回正数,将新元素添加到现有元素之后
- 如果返回负数,将新元素添加到现有元素之前
构造方法
常用构造方法 | 说明 |
---|---|
TreeSet() | 实际会创建一个TreeMap对象 |
常用方法
常用方法 | 作用 |
---|---|
add(Object obj) | 添加元素到集合中 |
size() | 得到元素数量 |
isEmpty() | 判断集合是否为空 |
clear() | 清空集合 |
ceiling(Object obj) | 得到比指定元素大的最小元素 |
floor(Object obj) | 得到比指定元素小的最大元素 |
first()/last() | 得到第一个/最后一个元素 |
HashMap实现类
- JDK1.8之后,HashMap的数据结构采用"数组+链表+红黑树"实现
- 当没有哈希冲突时,元素保存到数组中
- 如果哈希冲突,在对应的位置上创建链表,元素保存到链表中
- 当链表元素数量大于8,转换为红黑树
- 数据采用**"键值对"的形式保存,键称为key**,值称为value ,键不能重复,允许null,值没有限制,键和值都是引用类型
- 在哈希表中,哈希码就是键,保存的数据就是值,可以通过键得到相应的值。
构造方法
常用构造方法 | 说明 |
---|---|
HashMap() | 创建一个空的集合对象,默认大小为16,加载因子为0.75 |
常用方法
常用方法 | 作用 |
---|---|
put(Object key,Object Value) | 添加一组键值对 |
get(Object key) | 根据键得到值 |
size() | 得到键值对的数量 |
clear() | 清空所有键值对 |
keyset() | 得到所有键的集合 |
values() | 得到所有值的集合 |
containtsKey(Object key) | 判断是否包含某个键 |
containsValue(Object value) | 判断是否包含某个值 |
remove(Object key) | 根据键删除键值对 |
泛型
一种规范,常用于限制集合中元素的类型,省去遍历元素时转型的过程。
在定义集合时,在接收集合变量的数据类型后,写上<数据类型>。
集合类或接口<数据类型> 集合变量名 = new 集合实现类<>();
Collections集合工具类
- Collection是集合的根接口,定义了集合的方法
- Collections是集合的工具类,定义了很多静态方法,直接通过类名使用
常用方法
常用方法 | |
---|---|
Collections.shuffle(List list) | 打乱有序集合中的元素顺序 |
Collections.swap(List list,int i,int j) | 交换有序集合中i和j索引上的元素 |
Collections.replaceAll(List list,Object oldVal,Object newVal) | 将有序集合中的旧元素替换为新元素 |
Collections.fill(List list,Object obj) | 将有序集合使用obj填充 |
Collections.rotate(List list,int distance) | 将最后distance个元素放在最前 |
Collections.sort(List list) | 对有序集合中的元素进行排序,元素必须实现了Comparable接口 |
Collections.max(Collection list) | 得到有序集合中的最小元素,元素必须实现了Comparable接口 |
Collections.min(Collection list) | 得到有序集合中的最大元素,元素必须实现了Comparable接口 |
实现购物车练习
购物车类(Cart)
package homework1;
import java.util.HashMap;
import java.util.Scanner;
import java.util.Set;
public class Cart {
HashMap<Goods, Integer> hm = new HashMap();
public void add(Goods good, Integer num) {
if (!hm.containsKey(good)) {
hm.put(good, num);
System.out.println("添加成功");
} else {
hm.put(good, hm.get(good) + num);
System.out.println("添加成功");
}
}
public void show() {
double total_price = 0;
for (Goods goods : hm.keySet()) {
Integer value = hm.get(goods);
double price = goods.getPrice() * value;
total_price += price;
System.out.println("商品:" + goods + "\t数量:" + value + "\t价值:" + price);
}
System.out.println("总价:" + total_price);
}
public void delete(Goods good) {
if (hm.containsKey(good)) {
hm.remove(good);
System.out.println("删除成功");
return;
}
System.out.println("删除失败");
}
public void empty() {
System.out.println("确定清空购物车么?输入1确认,输入其他取消");
if (new Scanner(System.in).nextInt() == 1) {
hm.clear();
System.out.println("清空完成");
return;
}
System.out.println("取消成功");
}
}
商品类(Goods)
package homework1;
import java.util.Objects;
public class Goods implements Comparable {
private Integer id;
private String brand;
private String name;
private Double price;
public Goods() {
}
public Goods(Integer id, String brand, String name, Double price) {
this.id = id;
this.brand = brand;
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "编号:" + id +
", 品牌:'" + brand + '\'' +
", 款式:'" + name + '\'' +
", 单价:" + price;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Goods goods = (Goods) o;
return Objects.equals(id, goods.id) && Objects.equals(brand, goods.brand) && Objects.equals(name, goods.name) && Objects.equals(price, goods.price);
}
@Override
public int hashCode() {
return Objects.hash(id, brand, name, price);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public int compareTo(Object o) {
Goods good = (Goods) o;
return this.id - good.id;
}
}
商城类(Mall)
package homework1;
import java.util.TreeSet;
public class Mall {
TreeSet<Goods> goods = new TreeSet<>();
public void add(Goods good) {
goods.add(good);
}
public void show() {
for (Goods good : goods) {
System.out.println(good);
}
}
public Goods show_one_byId(int id) {
for (Goods good : goods) {
if (good.getId() == id) {
return good;
}
}
System.out.println("商品不存在");
return null;
}
}
Main
package homework1;
import java.util.Scanner;
public class Main {
Goods g1 = new Goods(3, "耐克", "空军一号", 568.0);
Goods g2 = new Goods(1, "匡威", "1970s", 348.0);
Goods g3 = new Goods(2, "耐克", "aj1", 1568.0);
Goods g4 = new Goods(1, "耐克", "空军一号", 568.0);
Mall mall = new Mall();
Cart cart = new Cart();
public void buy() {
mall.add(g1);
mall.add(g2);
mall.add(g3);
mall.add(g4);
System.out.println("请选择功能:\n1:购买商品\n2:查看购物车\n3:删除商品\n4:清空购物车\n0:退出");
Scanner sc = new Scanner(System.in);
switch (sc.nextInt()) {
case 1:
mall.show();
System.out.println("请输入购买的商品编号");
Integer input = sc.nextInt();
System.out.println("请输入购买数量");
Integer input1 = sc.nextInt();
cart.add(mall.show_one_byId(input), input1);
System.out.print("您当前购物车中:\n");
cart.show();
buy();
break;
case 2:
cart.show();
buy();
break;
case 3:
System.out.println("请输入删除的商品编号");
Integer id = sc.nextInt();
cart.delete(mall.show_one_byId(id));
buy();
break;
case 4:
cart.empty();
buy();
break;
case 0:
break;
}
}
public static void main(String[] args) {
new Main().buy();
}
}
file文件类
构造方法
常用构造方法 | 说明 |
---|---|
File(String pathName) | 根据文件的完整路径创建对象 |
File(String parent,String name) | 根据文件所在的父目录路径和自身文件名创建对象 |
File(File parent,String name) | 根据文件所在父目录文件对象和自身文件夹创建对象 |
常用方法
常用方法 | 说明 | 返回值 |
---|---|---|
exists() | 判断文件是否存在 | boolean |
isFile() | 判断是否为文件 | boolean |
isDirectory() | 判断是否为目录 | boolean |
getName() | 得到文件名 | String |
getPath() | 得到文件所在相对路径 | String |
getAbsolutePate() | 得到文件所在绝对路径 | String |
getParent() | 得到父目录的名称 | String |
getParentFile() | 得到父目录的对象 | File |
lastModified() | 得到文件最后一次修改时间对应的毫秒数 | long |
length() | 得到文件的所占字节大小 | long |
isHidden() | 判断文件是否为隐藏 | boolean |
list() | 得到目录下的第一层子文件名的字符串数组 | String[] |
listFiles() | 得到目录下的第一层子文件的文件数组 | File[] |
delete() | 删除某个文件或空目录 | boolean |
mkdir() | 创建一个空目录 | boolean |
renameTo(File newFile) | 将原文件命名为新文件(新文件可以指定新位置) |
递归函数
public class Test3 {
//递归
public static void main(String[] args) {
System.out.println(fun(20));
}
public static int fun(long n){
if (n>2){
return fun(n-1)+fun(n-2);
}
return 1;
}
}
IO
在Java中,流用于表示计算机硬盘与内存之间传输数据的通道。
将内存中的数据存入到硬盘中,称为写write,也称为输出
将硬盘中的数据存入到内存中,称为读read,也称为输入
流的分类
InputStream字节输入流
FileInputStream文件字节输入流 ObjectInputStream对象字节输入流(反序列化)
OutputStream字节输出流
FileOutputStream文件字节输出流 ObjectOutputStream对象字节输出流(序列化)
Writer字符输出流
FileWriter文件字符输出流 **BufferedWriter缓冲字符输出流(包装流) ** OutputStreamWriter字节输出流转换为字符输出流(转换流)
Reader字符输入流
FileReader文件字符输入流 BuffedredReader缓冲字符输入流(包装流) InputStreamReader字节输入流转换为字符输入流(转换流)
按方向分类
输入流:InputStream、Reader
作用:将硬盘中的数据读取到内存中
输出流:OutputStream、Wrtiter
作用:将内存中的数据写入到硬盘中
按类型分
字节流:InputStream、OutputStream
作用:适用于非文本类型,如图片、文件等的读写
字符流:Reader、Writer
作用:适用于文本类型,尤值txt格式文件的读写
如要将硬盘中某个txt文件中的字符读取到程序中,使用Reader
如要将硬盘中的某个图片读取到程序中,使用InputStream
流的四个父类的特点
- 以上四个类都在java.io包下,都是抽象类,不能直接创建其对象,要使用其子类
- 这四个类都有close()方法,用于关闭流对象释放资源
- 输出流(OutputStream和Write)都有flush()方法,用于将流中的数据到冲刷到硬盘中
- 在使用输出流对象时,一定要调用flush()或close()方法后,才能真正将数据写到硬盘中
- 输入流(InputStream和Reader)都有read()方法读取数据到内存中,输出流都有write()方法写入数据到硬盘中
- 所有的流类中,以Stream结尾的,都是字节流,以Reader或Writer结尾的都是字符流
- 读取硬盘中的数据时,使用输入流;将数据写入到硬盘中时,使用输出流
- 读取或写入文本文件时,使用字符流;读取或写入非文本文件时,使用字节流
FileInputStream文件字节输入流
构造方法
常用构造方法 | 说明 |
---|---|
FileInputStream(String pathName) | 根据文件名创建文件字节输入流对象 |
FileInputStream(File file) | 根据文件对象创建文件字节输入流对象 |
常用方法
常用方法 | 作用 |
---|---|
read() | 读取一个字节。返回读取到的字节本身。 |
read(byte[] b) | 按字节数组读取。返回读取到的字节数量。读取到的内容保存在了字节数组中 |
close() | 关闭流对象 |
FileOutputStream文件字节输出流
构造方法
常用构造方法 | 说明 |
---|---|
FileOutputStream(String pathName) | 根据文件名创建文件字节输出流对象,写入内容时覆盖原内容 |
FileOutputStream(String pathName,boolean append) | 根据文件名创建文件字节输出流对象,写入内容时追加在原内容之后 |
FileOutputStream(File file) | 根据文件对象创建文件字节输出流对象,写入内容时覆盖原内容 |
FileOutputStream(File file,boolean append) | 根据文件对象创建文件字节输出流对象,写入内容时追加在原内容之后 |
常用方法
常用方法 | 作用 |
---|---|
write(int i) | 写入一个字节 |
write(byte[] bytes) | 写入一个字节数组 |
write(byte[] bytes,int off,int len) | 写入字节数组中从off开始的len个字节 |
flush() | 将数据冲刷到本地文件中 |
**close( |
使用FileInputStream和FileOutputStream读写时的注意事项
- 在使用read(byte[] bytes)方法时,每次都会读取指定数组大小的字节,将读取到的字节保存到数组中,该方法返回读取到的字节数量。如果最后一次读取的字节数不足字节数组大小时,只会用读取到的内容覆盖最前的几个元素。所以会导致读取到的内容多于实际内容。
- 在使用write(byte[] bytes)方法时,会将字节数组中的所有内容写入到输出流中,在最后一次写入时,可能会写入多余的内容。所以在写入时,最好使用write(byte[] bytes,int off,int len)方法,表示将字节数组中的元素,从off开始,写入len个字节。
FileReader文件字符输入流
构造方法
常用构造方法 | 说明 |
---|---|
FileReader(String fileName) | 根据文件名创建文件字符输入流对象 |
FileReader(File file) | 根据文件对象创建文件字符输入流对象 |
常用方法
常用方法 | 作用 |
---|---|
ready() | 判断是否还有下一个字符 |
read() | 读取下一个字符,返回读取到的字符 |
read(char[] chars) | 按字符数组读取字符,返回读取到的字符数量,读取到的字符保存在了字符数组中 |
close() | 关闭流对象 |
FileWriter文件字符输出流
构造方法
常用构造方法 | 说明 |
---|---|
FileWriter(String fileName) | 按文件名创建字符输出流对象 |
FileWriter(String fileName,boolean append) | 按文件名创建字符输出流对象,允许追加写入 |
FileWriter(File file) | 按文件对象创建字符输出流对象 |
FileWriter(File file,boolean append) | 按文件对象创建字符输出流对象,允许追加写入 |
常用方法
常用方法 | 作用 |
---|---|
write(String str) | 按字符串写入 |
write(char[] c) | 按字符数组写入 |
write(String str,int off,int len) | 按字符串写入,从off开始写入len个字符 |
write(char[] c,int off,int len) | 按字符数组写入,从off开始写入len个字符 |
flush() | 冲刷流中的数据到硬盘中(必须调用该方法或close方法后,才能真正写入) |
close() | 关闭流对象 |
BufferedReader字符缓冲输入流
自带字符数组(缓冲区)的字符输入流。默认字符数组大小为8192,每次最多读取8192个字符,在读取纯文本文件(txt或md等)时,首选该类。
构造方法
常用构造方法 | 说明 |
---|---|
BufferedReader(Reader reader) | 创建一个带有缓冲区(字符数组)的字符输入流对象,默认缓冲区大小为8192。参数注意是一个Reader类型的对象,但Reader是一个抽象类,所以要使用其子类,如FileReader,然后在FileReader中定义要读取的文件信息 |
BufferedReader(Reader reader,int size) | 创建一个指定缓冲区(字符数组)大小的字符输入流对象 |
常用方法
常用方法 | 作用 |
---|---|
ready() | 判断是否还有字符 |
readLine() | 读取整行字符 |
close() | 关闭流对象 |
BufferedWriter字符缓冲输出流
构造方法
常用构造方法 | 说明 |
---|---|
BufferedWriter(Writer writer) | 创建一个带有缓冲区(字符数组)的字符输出流对象,参数为一个Writer对象,但Writer是一个抽象类,所以要使用其子类,如FileWriter对象,在FileWriter中定义要写入的文件 |
BufferedWriter(Writer writer,int size) | 创建一个指定缓冲区(字符数组)大小的字符输出流对象 |
常用方法
常用方法 | 作用 |
---|---|
write(String str) | 写入一个字符串 |
newLine() | 换行 |
flush() | 将输出流中的字符冲刷到文件中 |
close() | 关闭流对象 |
ObjectOutputStream对象字节输出流(序列化)
序列化:将对象转换为文件的过程
被序列化的对象,必须要实现Serializable接口。
这个接口是一个特殊的接口,没有定义方法,只是给这个类打上标记,表示该类可以被序列化。
构造方法
常用构造方法 | 说明 |
---|---|
ObjectOutputStream(OutputStream os) | 创建一个对象字节输出流对象,参数为一个字节输出流对象,由于是抽象类,所以要使用其子类,如FileOutputStream对象,在其中定义要写入的文件。 |
常用方法
常用方法 | 作用 |
---|---|
writeObject(Object obj) | 将一个对象写入到本地文件中 |
close() | 关闭流对象 |
ObjectInputStream对象字节输入流(反序列化)
反序列化:将文件转换为对象的过程
构造方法
常用构造方法 | 说明 |
---|---|
ObjectInputStream(InputStream is) | 创建一个对象字节输入流对象,参数为一个字节输入流对象,由于是抽象类,所以要使用其子类,如FileInputStream对象,在其中定义要读取的文件 |
常用方法
常用方法 | 作用 |
---|---|
readObject() | 读取序列化后的文件,返回对象Object |
close() | 关闭流对象 |
转换流
OutputStreamWriter
将字节输出流转换为字符输出流
InputStreamReader
将字节输入流转换为字符输入流
网络编程
InetAddress类
表示IP对象的一个类
Socket类和ServerSocket类
都属于Socket(套接字)对象,表示网络中的某个端点。
- ServerSocket指服务器端
- Socket指普通端点
进程和线程
进程Process
进程就是操作系统中执行的程序。一个进程就是一个执行的程序实体。
每个运行中的进程,都有属于它独立的内存空间。各个进程之间互不影响。
线程Thread
线程是一个进程中的执行单元,一个进程中可以有多个线程。
多线程之间,可以访问同一个进程中的资源。
每个线程都有一个独立的栈空间。这些线程所在的栈空间位于同一个进程空间中。
多线程
如果一个进程中,同时在执行着多个线程,就称为多线程。
其实每个执行的Java程序,都是多线程的,虽然看似只有main方法所在的主线程在执行,其实还有一个gc线程(守护线程)。
并行和并发
并行
多个进程各自执行,称为并行。
并发
多个线程同时执行,称为并发。
同步和异步
同步
所有的任务排队执行,称为同步执行。
异步
在执行任务A的同时,执行任务B,称为异步执行。
Java中的线程
Java中,线程以对象的形式存在。
Thread表示线程类。
获取线程对象
-
获取当前正在运行的线程对象
//获取当前运行的线程对象 Thread ct = Thread.currentThread();
-
创建一个线程对象
构造方法
常用构造方法 说明 Thread() 创建一个默认的线程对象 Thread(String name) 创建一个指定线程名的线程对象 Thread(Runnable target) 将一个Runnable对象包装为线程对象 Thread(Runnable target,String name) 将一个Runnable对象包装为线程对象同时命名
常用方法
常用方法 | 作用 |
---|---|
setName(String str) | 设置线程名称 |
setPriority(int i) | 设置线程优先级(1~10),数字越大优先级越高,线程越先执行完 |
setDaemon(boolean f) | 是否将该线程设置为守护线程 |
getId() | 获取线程ID |
getName() | 获取线程名,主线程名默认main,自定义线程名默认Thread-N |
getPriority() | 获取线程优先级 |
getState() | 获取线程状态 |
isDaemon() | 判断该线程是否属于守护线程 |
start() | 启动线程 |
run() | 线程启动后执行的方法 |
Thread.currentThread() | 获取当前运行的线程对象 |
Thread.sleep(long m) | 设置当前线程休眠m毫秒 |
Thread.yield() | 线程让步,让其他线程执行 |
实现多线程
方式一:继承Thread类
- 1.让某个类成为Thread类的子类
- 2.重写Thread类中的run方法,将要让该线程执行的内容写在该方法中
- 3.创建Thread类的对象后,调用start()方法,启动线程
方式二:实现Runnable接口(建议使用)
由于Java中是单继承,所以如果某个类已经使用了extends关键字去继承了另一个类,这时就不能再使用extends继承Thread类实现多线程,就需要使用实现Runnable接口的方法实现多线程。
- 1.自定义一个类,实现Runnable接口
- 2.重写run方法,将要多线程执行的内容写在该方法中
- 3.创建Thread线程对象,将自定义的Runnable接口实现类作为构造方法的参数
- 4.调用线程对象的start()方法启动线程
方式三:使用匿名内部类
如果不想创建一个Runnable接口的实现类,就可以使用匿名内部类充当Runnable接口的实现类
线程的生命周期
线程的初始化到终止的整个过程,称为线程的生命周期
新生状态
当线程对象被实例化后,就进入了新生状态。new Thread()
就绪状态
当某个线程对象调用了start()方法后,就进入了就绪状态。
在该状态下,线程对象不会做任何事情,只是在等待CPU调用。
运行状态
当某个线程对象得到运行的机会后,则进入运行状态,开始执行run()方法。
不会等待run()方法执行完毕,只要run()方法调用后,该线程就会再进入就绪状态。这时其他线程就有可能穿插其中。
阻塞状态
如果某个线程遇到了sleep()方法或wait()等方法时,就会进入阻塞状态。
sleep()方法会在一段时间后自动让线程重新就绪。
wait()方法只有在被调用notify()或notifyAll()方法唤醒后才能进入就绪状态。
终止状态
当某个线程的run()方法中所有内容都执行完,就会进入终止状态,意味着该线程的使命已经完成。
多线程访问同一资源
可能出现的问题
如多线程卖票,多线程取钱,类似的多个线程在操作同一个资源时,实际操作的结果和预想结果不符。
如票实际卖出的大于原本的,取出的钱大于本金等
出现问题的原因
由于线程调用start()方法后,就进入就绪状态,如果获得了CPU的使用权,开始调用run()方法,调用run方法后,马上就会重新回到就绪状态,所以不会等待run()方法执行结束,在执行run()方法的时候,可能其他线程也可能获取CPU的使用权,从而开始执行run()方法。
当前所有线程是在异步执行。
如何解决
让线程同步执行(排队)即可。就能在某个线程执行run方法的时候,让其他线程等待run()方法执行完毕
synchronized关键字
这个关键字可以修饰方法或代码块。
修饰方法
写在方法的返回值前。这样该方法就称为同步方法,在执行的时候,其他线程要排队等待该方法执行完毕后才执行。
修饰代码块
写在{}前,这样这段{}中的内容称为同步代码块,在执行的时候,其他线程要排队等待该方法执行完毕后才执行。
原理
每个对象都默认有一把"锁",当某个线程运行到被synchronized修饰的方法时,该对象就会拥有这把锁,在拥有锁的过程中,其他线程不能同时访问该方法,只有等待其执行结束后,才会释放这把锁。
使用synchronized修饰后拥有的锁称为"悲观锁",在拥有的时候,不允许其他线程访问。
方法被synchronized修饰后,成为了同步方法,就会让原本的多线程成为了单线程(异步变为同步),虽然效率变低,但是数据安全是首位。
多线程相关测试题
- 实现多线程的方式?
- 继承Thread类
- 实现Runnable接口后,包装为Thread对象
- 使用匿名内部类
- 为什么说StringBuilder是非线程安全的
- 什么叫死锁?怎么产生?如何解决?
如有两个人吃西餐,必须要有刀和叉才能吃饭,只有一副刀叉。
如果A拿到了刀,B拿到了叉,互相都在等待另一个工具,但都不释放自己的,这时就会造成死锁的局面,既不结束,也不继续。
模拟死锁出现的原因
定义两个线程类,线程A先获取资源A后,再获取资源B;线程B先获取资源B后,再获取资源A。
如果对A和B对使用了synchronized进行同步,就会在线程A获取资源A时候,线程B无法获取资源A,
相反线程B在获取资源B的时候,线程A无法获取资源B,所以两个线程都不会得到另一个资源。
死锁的解决方式一:
让线程A和线程B获取资源A和资源B的顺序保持一致。
死锁的解决方式二:
让线程A和线程B获取资源A和资源B之前,再获取第三个资源,并对其使用synchronized进行同步,
这样线程A在获取第三个资源后,将所有事情执行完后,线程B才能继续。
网络编程服务器端
public class Server {
public static void main(String[] args) throws IOException {
InetAddress ip = InetAddress.getByName("192.168.3.20");
System.out.println(ip);
ServerSocket server = new ServerSocket(99, 20, ip);
Socket client = server.accept();
FileOutputStream fos = new FileOutputStream(new File("D:\\信息12.zip"));
InputStream is=client.getInputStream();
byte[] bytes = new byte[1024 * 1024 * 16];
int count= is.read(bytes);
while (count!=-1){
fos.write(bytes);
count=is.read(bytes);
}
is.close();
fos.close();
}
}