字符串String类中的常用方法
方法名 | 返回值 | 作用 |
---|---|---|
length() | int | 得到字符串的长度 |
toLowerCase() | String | 转换为小写 |
toUpperCase() | String | 转换为大写 |
trim() | String | 去除字符串首尾的所有空格 |
isEmpty() | boolean | 判断字符串是否为空白字符串"" |
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(字符序列) | boolean | 判断某个子字符串是否在原字符串中出现 |
concat(String str) | String | 将参数字符串拼接到原字符串末尾 |
startsWith(String str) | boolean | 判断是否以指定字符串开头 |
endsWith(String str) | boolean | 判断是否以指定字符串结尾 |
substring(int begin) | String | 从指定索引开始截取字符串至末尾 |
substring(int being,int end) | String | 截取[begin,end)区间内的字符串 |
split(String regex) | String[] | 按执行字符串或正则表达式切分原字符串。如果指定内容不再末尾,n个指定字符能得到n+1个子串;如果指定内容在末尾,n个指定字符能得到n个子串(不包含末尾的无效字符) |
replace(char oldChar,char newChar) | String | 将原字符串中的所有指定字符替换为新字符 |
String.valueOf(参数) | String | 将任意参数转换为字符串。通常用于原始类型转换为字符串。 |
String.formart(String 格式,Object… obj) | String | 根据指定格式转换参数。常用于将浮点数保留小数。如String.format(“%4.2f”,10.0/3)表示将计算的结果四舍五入保留2位小数转换为字符串;如果最终数据所占位置小于4,原样输出,大于4在最前补充空格。 |
StringBuilder类
用于表示可变字符串的一个类,是非线程安全的,建议在单线程环境下使用。
StringBuffer类
用于表示可变字符串的一个类,是线程安全的,建议在多线程环境下使用。
StringBuilder和StringBuffer中的方法都一致,只不过StringBuffer中的方法使用了synchoronized关键字修饰,表示是一个同步方法,在多线程环境下不会出现问题。
这里以StringBuilder为例
构造方法
常用构造方法 | 作用 |
---|---|
StringBuilder() | 创建一个大小为16的字符串数组,表示一个空白字符。类似于String str=“”; |
StringBuilder(String str) | 创建一个str长度+16的字符数组后,将str添加到其中。类似于String str=“初始值”; |
普通方法
常用方法 | 作用 |
---|---|
append(Object obj) | 将任意类型的参数添加到原可变字符串末尾 |
delete(int start,int end) | 删除[start,end)区间内的字符 |
deleteCharAt(int index) | 删除index索引上的字符 |
insert(int index,Object obj) | 在索引index上插入obj |
replace(int start,int end,String str) | 将[start,end)区间内的字符替换为str |
reverse() | 反转字符串 |
注意
- 以上表格中的方法都是在直接操作同一个字符串对象,每次调用方法后,原字符串都会发生变化
- StringBuffer和StringBuilder并没有重写equals方法,所以可变字符串的值是否相同时,调用的是equals中原始的==判断。如果要判断两个可变字符串的值是否相同时,需要将其转换为String后调用equals判断
System类
这个类中包含了一些系统相关的信息和一些方法。其中的属性和方法都是静态的。
该类不能创建对象,不是因为它是一个抽象类,而是因为它的构造方法是私有的。
常用属性和方法 | |
---|---|
System.out | 获取打印输出流PrintStream对象,用于控制台打印信息。 |
System.in | 获取输入流InputStream对象,用于获取输入的信息 |
System.err | 获取打印输出流PrintStream对象,用于控制台打印异常信息。 |
System.exit(int statues) | 终止虚拟机运行,参数0表示正常终止。 |
System.currentTimeMillis() | 获取从1970.1.1 0:0:0至今进过了多少毫秒。中国是UTC(+8),所以是从1970.1.1 8:0:0至今经过了多少毫秒。返回long类型。 |
System.arraycopy(原数组,原数组起始位置,目标数组,目标数组起始位置,原数组要复制的元素数量) | 将原数组中指定长度的元素复制到新数组中 |
RunTime类
Runtime类的对象,表示程序运行时对象(程序运行环境对象)。
包含了程序运行环境相关的信息。常用于获取运行环境信息(如虚拟机内存)或执行某个命令。
特点
这个类不是一个抽象类,但不能创建对象,因为它的构造方法是私有的。
这个类提供了一个静态方法getRuntime(),通过这个方法,可以获取一个Runtime类的对象。
这是Java中的一种设计模式–单例模式(一个类只能有一个创建对象)。
Date类
用于表示日期时间的类,位于java.util包下
构造方法
常用构造方法 | 说明 |
---|---|
Date() | 创建当前瞬间对应的日期对象 |
Date(long l) | 创建指定瞬间对应的日期对象 |
Date(int year,int month,int day) | 该构造方法已过时。创建指定年月日的日期对象(年是1900年起经过的年数,月用0-11表示1到12月) |
常用方法
常用方法 | 作用 |
---|---|
getTime() | 得到对应Date对象表示的毫秒数 |
setTime(long l) | 设置Date对象的毫秒数 |
after(Date when) | 判断调用日期对象是否在when之后 |
before(Date when) | 判断调用日期对象是否在when之前 |
SimpleDateFormat类
用于格式化日期的类。
构造方法
常用构造方法 | 作用 |
---|---|
SimpleDateFormat(String pattern); | 创建一个指定日期模板的格式化日期对象 |
日期模板
特殊字符 | 作用 |
---|---|
yyyy | 年份 |
MM | 月份 |
dd | 日期 |
HH | 小时 |
mm | 分钟 |
ss | 秒 |
E | 星期 |
以上两个字母都可以写成一个,如月份5 | M:5,MM:05 |
yyyy/MM/dd HH:mm:ss E | 2022/11/24 16:24:09 星期四 |
常用方法
常用方法 | 返回值 | 作用 |
---|---|---|
format(Date date) | String | 将Date对象按日期模板转换为字符串 |
parse(String str) | Date | 将满足日期模板的字符串转换为Date对象 |
package com.hqyj.test3;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatTest {
public static void main(String[] args) throws ParseException {
//定义格式化日期类所需的时间模板
/*
* yyyy 年
* MM 月份
* dd 日期
* HH 24小时制
* hh 12小时制
* mm 分钟
* ss 秒
* E 星期
*
* 两个字母都可以写成一个,如月份MM和M
* MM 5月实际为05
* M 5月实际为5
* */
String patten = "yyyy/MM/dd HH:mm:ss E";//年/月/日 时:分:秒 星期
//创建格式化日期类对象,参数为日期模板
SimpleDateFormat sdf = new SimpleDateFormat(patten);
//创建当前日期对象
Date now = new Date();
//调用格式化日期对象的format(Date date),将Date对象转换为指定日期格式的字符串
String format = sdf.format(now);
//输出
System.out.println(format);
//parse(String str)将指定日期模板的字符串转换为Date对象
Date date = sdf.parse("2000/5/3 2:1:3 星期一");
System.out.println(date);
}
}
Calendar类
表示日历的类,包含了很多日历相关的信息。
是一个抽象类,无法创建对象。可以通过静态方法getInstance()获取该类的一个实例。
//获取Calendar类的对象
Calendar cal = Calendar.getInstance();
日历字段
在Calendar类中,定义了很多被final和static修饰的常量,称为日历字段,实际一个数字,用于获取指定信息
值 | 作用 |
---|---|
Calendar.YEAR | 年份 |
Calendar.MONTH | 月份(0-11表示1-12月) |
Calendar.DATE | 日期 |
Calendar.DAY_OF_WEEK | 星期(1-7表示周天到周六) |
Calendar.HOUR | 12进制小时 |
Calendar.HOUR_OF_DAY | 24进制小时 |
Calendar.MINUTE | 分钟 |
Calendar.SECOND | 秒 |
Calendar.DAY_OF_MONTH | 本月第几天 |
Calendar.DAY_OF_YEAR | 本年第几天 |
Calendar.WEEK_OF_MONTH | 本月第几周 |
Calendar.WEEK_OF_YEAR | 本年第几周 |
常用方法
常用方法 | 作用 |
---|---|
get(int field) | 根据日历字段获取对应的值 |
getTime() | 获取对应的Date对象(Calendar对象转换为Date对象) |
getMaximum(int field) | 获取指定日历字段支持的最大值,如Calendar.DATE最大31 |
getActualMaximum(int field) | 获取指定日历字段在当前日期下的实际最大值,如11月,Calendar.DATE最大30 |
set(int field,int value) | 将指定的日历字段设置为指定值 |
set(int year,int month,int date) | 同时设置日历对象的年月日 |
setTime(Date date) | 将Date对象作为参数设置日历对象的信息 |
实现万年历
package com.hqyj.homework;
import java.util.Calendar;
import java.util.Scanner;
public class Test2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入年月");
Calendar cal = Calendar.getInstance();
int year = sc.nextInt();
int month = sc.nextInt();
//设置指定年月,默认为1号
cal.set(year, month - 1, 1);
//用于换行的计数,每行7个,包括空格
int count = 0;
System.out.println("========"+year+"年"+month+"月========");
System.out.println("一\t二\t三\t四\t五\t六\t日");
//打印1号前的空格
//DAY_OF_WEEK 星期 空格数量
//2 一 0
//3 二 1
//4 三 2
//5 四 3
//6 五 4
//7 六 5
//1 日 6
//获取本月1号是所在周的第几天
int week = cal.get(Calendar.DAY_OF_WEEK);
//根据规律判断,打印空格的同时也要计数
if (week == 1) {
System.out.print("\t\t\t\t\t\t");
count += 6;
} else {
for (int i = 1; i <= week - 2; i++) {
System.out.print("\t");
count++;
}
}
//遍历当月每一天
for (int i = 1; i <= cal.getActualMaximum(Calendar.DATE); i++) {
System.out.print(i + "\t");
count++;
//隔7换行
if (count % 7 == 0) {
System.out.println();
}
}
}
}
Collection接口
该接口有两个核心子接口:List和Set。
这两个接口都可以保存一组元素,List接口保存元素时,是有序可重复的;Set接口保存元素时,是无序不重复的。
常用方法 | 返回值 | 作用 |
---|---|---|
add(Object obj) | boolean | 将元素添加到集合中 |
size() | int | 获取集合中的元素数量 |
isEmpty() | boolean | 判断集合是否为空 |
clear() | void | 清空集合 |
contains(Object obj) | boolean | 判断集合中是否存在指定元素 |
remove(Object obj) | boolean | 移除集合中的指定元素 |
toArray() | Object[] | 将集合转换为数组 |
iterator() | Iterator | 获取集合的迭代器对象,用于遍历集合 |
List接口(有序可重复)
有序集合,元素可以重复,允许保存null,可以通过索引获取对应位置上的元素。
在该接口继承Collection接口的同时,又拓展了一些操作元素的方法,如添加到指定索引、根据索引删除、获取指定索引的元素、截取子集合的方法等。
常用方法 | 返回值 | 作用 |
---|---|---|
get(int index) | Object | 根据指定索引获取对应的元素 |
set(int index,Object obj) | Object | 使用obj替换index上的元素,返回被替换的元素 |
add(int index,Object obj) | void | 将obj添加到index上 |
remove(int index) | Object | 移除指定索引的元素 |
indexOf(Object obj) | int | 得到某元素第一次出现的索引,没有返回-1 |
lastIndexOf(Object obj) | int | 得到某元素最后一次出现的索引,没有返回-1 |
subList(int from,int to) | List | 截取[from,to)区间内的元素,返回子集合 |
ArrayList实现类(掌握)
- 采用数组实现的集合
- 可以通过索引访问元素,可以改变集合大小。如果要在其中插入或删除元素时,会影响后续元素
- 该集合中保存的都是引用类型,即便保存了数组123,也保存的是Integer类型的123,而不是int类型的123
- 该集合查询效率高,中途增加和删除元素效率低
构造方法
常用构造方法 | 说明 |
---|---|
ArrayList() | 创建一个Object类型的空数组。在调用添加方法后,才会更改该数组大小为10 |
ArrayList(int initialCapacity) | 创建一个指定容量的Object数组,如果参数为负,会抛出IllegalArgumentException异常 |
常用方法
ArrayList中的常用方法,就是Collection接口和List接口中定义的方法。
LinkedList实现类
- 采用双向链表实现的集合
- 集合中保存的每个元素也称为节点,除首尾节点外,其余节点都保存了自己的信息外,还保存了其前一个和后一个节点的地址
- 如果在双向链表的数据结构中插入和删除操作节点时,不会影响其他节点的位置。如添加时新节点时,只需要重写定义新节点的前后节点位置即可
- 如果要查询某个节点时,需要从头结点或尾结点开始一步步得到目标节点的位置
- 双向链表在中间插入和删除的效率高,随机读取的效率低
构造方法
常用构造方法 | 说明 |
---|---|
LinkedList() | 创建一个空链表 |
常用方法
由于LinkedList既实现了List接口,又实现了Deque接口,所以还有Deque接口中的一些方法
实现Deque接口的方法 | 说明 |
---|---|
addFirst(Object obj) | 添加头元素 |
addLast(Object obj) | 添加尾元素 |
removeFirst() | 移除头元素 |
removeLast() | 移除尾元素 |
getFirst() | 得到头元素 |
getLast() | 得到尾元素 |
remove() | 移除头元素 |
pop() | 移除头元素 |
push(Object obj) | 添加头元素 |
peek() | 得到头元素 |
poll() | 移除头元素 |
offer(Object obj) | 添加尾元素 |
ArrayList和LinkedList的区别
- 这两个类都是List接口的实现类,保存的元素有序可重复,允许保存null
- ArrayList采用数组实现,随机读取效率高,插入删除效率低,适合用于查询
- LinkedList采用双向链表实现,插入删除时不影响其他元素,效率高,随机读取效率低,适合用于频繁更新集合
Set接口(无序不重复)
无序集合,元素不可以重复,允许保存null,没有索引。
Set接口中没有自己定义的方法,都是继承于Collection接口中的方法
哈希表hash table
哈希表,也称为散列表,是一种数据结构,能更快地访问数据。
要保存的数据称为原始值,这个原始值通过一个函数得到一个新的数据,这个函数称为哈希函数,这个新数据称为哈希码,哈希码和原始值之间有一个映射关系,这个关系称为哈希映射,可以构造一张映射表,这个表称为哈希表。在哈希表中,可以通过哈希码快速地访问对应的原始值。
假设原本的数据为左侧的数组。
如果要查询10,需要遍历数组,效率不高。
通过一个特定的函数"原始值%5",得到一组新数据,让新数据重新对应元素,保存到“新数组”中,这个“新数组”称为哈希表。
这时如果要查询10,由于哈希函数是通过%5得到了0,所以直接查询哈希表中0对应的元素即可。
整个过程中,这个函数称为哈希函数,得到的新数据称为哈希码,新数组称为哈希表,对应关系称为哈希映射。
这个哈希函数,有一定的几率让多个原始值得到相同的哈希码,这种情况称为哈希冲突(哈希码一致,实际值不同),
为了解决哈希冲突,可以使用"拉链法",将2这个哈希码所在的位置向链表一样进行延伸。
哈希码的特点
- 如果两个对象的hashCode不同,这两个对象一定不同
- 如果两个对象的hashCode相同,这两个对象不一定相同
- hashCode相同,对象不同,这种现象称为哈希冲突
- **“通话"和"重地”**这两个字符串的hashCode相同,但是两个不同的对象
HashSet实现类
- 采用哈希表实现
- 元素不能重复,无序保存,允许保存一个null
- 本质是一个HashMap对象
- 使用HashSet集合时,通常要重写实体类中的equals和hashcode方法
构造方法
常用构造方法 | 说明 |
---|---|
HashSet() | 创建一个空集合,实际是创建一个HashMap对象。 |
常用方法
HashSet中没有属于自定义的方法,都是重写了父接口Set和Collection中的方法。这里参考Collection中的方法即可。
没有与索引相关的方法。
HashSet添加数据的原理
如果两个元素的hashCode相同且equals结果为true,视为同一个对象,不能添加。
每次向集合中添加元素时,先判断该元素的hashCode是否存在
- 如果不存在,视为不同对象,直接添加
- 如果存在,再判断equals方法的结果
- 如果false,视为不同对象,可以添加
- 如果true,视为同一对象,不能添加
由此可见,不能添加的条件是两个对象的hashCode相同且equals的结果为true。
如果每次只判断equals的话,由于equals方法通常重写时会判断很多属性,效率不高。
如果每次只判断hashCode的话,效率高,但有可能会有哈希冲突,
所以先判断hashCode,再判断equals,技能保证效率,又能保证不添加重复元素。
equals方法和hashCode的关系
-
如果两个对象的equals方法结果为true,在没有重写equals方法的前提下,hashCode相同吗
- 如果没有重写equals,默认是Object中使用==判断,如果结果为true,说明是同一个对象,hashCode一定相同
-
如果两个对象的hashCode不同,在没有重写equals方法的前提下,equals方法的结果为?
- hashCode不同,说明不是同一个对象,没有重写equals,说明使用Object中equals的==判断,结果为false
-
如果两个对象的hashCode相同,equals方法的比较结果为?
-
可能为true也可能为false
String str1="hello"; String str2="hello"; //以上两个字符串使用同一个地址,hashCode相同,equals方法为true String str3="通话"; String str4="重地"; //以上连个字符串是不同地址,但hashCode相同,因为哈希冲突,equals方法为false
-
HashSet的应用
如果想要保存的对象保证不重复,且无关顺序,可以使用HashSet。如学生管理
Goods类
package com.hqyj.hashsetTest;
import java.util.Objects;
/*
* 定义商品类
* 品牌、名称、价格
* */
public class Goods {
private String brand;
private String name;
private int 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 price == goods.price &&
Objects.equals(brand, goods.brand) &&
Objects.equals(name, goods.name);
}
/*
* 根据所有属性生成哈希码
* 如果两个对象的所有属性都一致,生成的哈希码就一致
* */
@Override
public int hashCode() {
return Objects.hash(brand, name, price);
}
@Override
public String toString() {
return "Goods{" +
"brand='" + brand + '\'' +
", name='" + name + '\'' +
", price=" + price +
'}';
}
public Goods(String brand, String name, int price) {
this.brand = brand;
this.name = name;
this.price = price;
}
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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
Main类
package com.hqyj.hashsetTest;
import java.util.HashSet;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
//创建一个HashSet集合
HashSet<Goods> hs = new HashSet<>();
//创建几个Goods对象
//g1、g2、g3的属性不同,生成的hashcode不同,都能添加
Goods g1 = new Goods("康师傅", "冰红茶", 3);
Goods g2 = new Goods("康师傅", "红烧牛肉面", 5);
Goods g3 = new Goods("农夫山泉", "矿物质水", 2);
//g3与g4的属性相同,生成的hashcode相同,继续判断equals
Goods g4 = new Goods("农夫山泉", "矿物质水", 2);
//第一次添加,一定可以添加
hs.add(g1);
//第二次添加,对象的属性不同,hashcode不同,可以添加
hs.add(g2);
//第三次添加,对象的属性不同,hashcode不同,可以添加
hs.add(g3);
//第四次添加,对象的属性相同,hashcode相同,再判断equals结果,true,视为已存在,无法添加
hs.add(g4);
/*
* HashSet没有可以通过索引获取对象的方法,所以无法使用普通for循环遍历
* 这里可以使用增强for循环
* */
for (Goods g : hs) {
System.out.println(g);
}
/*
* 可以使用迭代器遍历HashSet集合中的所有元素
* */
/*Iterator<Goods> it = hs.iterator();
while (it.hasNext()) {
Goods goods = it.next();
System.out.println(goods);
}*/
}
}
TreeSet实现类
- 特殊的Set实现类,数据可以有序保存,可以重复,不能添加null
- 采用红黑树(自平衡二叉树)实现的集合
- 二叉树表示某个节点最多有两个子节点
- 某个节点右侧节点值都大于左侧节点值
- 红黑树会经过不停的"变色"、"旋转"达到二叉树的平衡
- 只能添加同一种类型的对象且该类实现了Comparable接口
- 实现Comparable接口后必须要重写compareTo()方法
- 每次调用添加add(Object obj)方法时,就会自动调用参数的compareTo()方法
- compareTo()方法的返回值决定了能否添加新元素和新元素的位置
- 如果返回0,视为每次添加的是同一个元素,不能重复添加
- 如果返回正数,将新元素添加到现有元素之后
- 如果返回负数,将新元素添加到现有元素之前
- 添加的元素可以自动排序
构造方法
常用构造方法 | 说明 |
---|---|
TreeSet() | 创建一个空集合,实际是创建了一个TreeMap对象 |
常用方法
属于Set的实现类,所以能使用Collection和Set中的方法,除此之外,还有独有的方法
常用方法 | 作用 |
---|---|
fisrt() | 得到集合中的第一个元素 |
last() | 得到集合中的最后一个元素 |
ceil(Object obj) | 得到比指定元素obj大的元素中的最小元素 |
floor(Object obj) | 得到比指定元素obj小的元素中的最大元素 |
TreeSet的应用
如果要保存的元素需要对其排序,使用该集合。
保存在其中的元素必须要实现Comparable接口,且重写compareTo()方法,自定义排序规则
流的分类
Java中将流定义为类,以对象的形式表现流。流有"四大家族",是所有流的父类。
字节输入流InputStream
FileInpuStream、ObjectInputStream
字节输出流OutputStream
FileOutputStream、ObjectOutputStream
字符输入流Reader
FileReader、BufferedReader、OutputStreamWriter
字符输出流Writer
FileWriter、BufferedWriter、InputStreamReader
按方向分类
-
输入流:InputStream、Reader
- 将硬盘中的数据读取到内存中
-
输出流:OutputStream、Writer
- 将内存中的数据写入到硬盘中
按类型分
- 字节流:InputStream、OutputStream
- 读写非文本类型文件。如图片、音视频、其他文件等。
- 字符流:Reader、Writer
- 读写纯文本类型文件。如txt、md等
如要将硬盘中某个txt文件中的内容读取到程序中,使用Reader
如要将硬盘中的某个图片读取到程序中,使用InputStream
如要将程序中的文本写入到硬盘中为txt类型文件时,使用Writer
如要将程序中的数据写入到硬盘中为非文本文件时,使用OutputStream
流的四个父类的特点
- 这四个父类都是在java.io包下,都是抽象类,不能直接创建其对象,使用其子类创建对象
- 这四个父类中都定义了close()方法,用于关闭流对象,释放资源
- 输入流(InputStream和Reader)都有read()方法读取数据到内存中,输出流都有write()方法写入数据到硬盘中
- 输出流(OutputStream和Writer)都有flush()方法,用于将流中的数据冲刷到硬盘中
- 在使用输出流对象时,一定要调用flush()或close()方法后,才能真正将数据写入到硬盘中
- 所有的流中,以Stream结尾,都是字节流,数据以字节传输;以Reader或Writer结尾的,都是字符流,数据以字符传输
- 读取硬盘中的数据,使用输入流,读取的文件必须存在;将数据写入到硬盘中,使用输出流,文件可以不存在,但父目录必须存在。
- 读入或写入文本时,使用字符流;读取或写入非文本时,使用字节流
FileInputStream文件字节输入流(掌握)
按字节读取硬盘中的文件。
构造方法
常用构造方法 | 说明 |
---|---|
FileInputStream(String pathName) | 根据文件名创建流对象 |
FileInputStream(File file) | 根据文件对象创建流对象 |
常用方法
常用方法 | 说明 |
---|---|
read() | 读取一个字节,返回读取到的字节 |
read(byte[] bytes) | 按字节数组读取,返回读取到的字节数量,读取到的内容保存在字节数组中 |
close() | 关闭流对象 |
FileOutputStream文件字节输出流(掌握)
按字节将内存中的数据写入到硬盘中。
构造方法
常用构造方法 | 说明 |
---|---|
FileOutputStream(String pathname) | 根据文件名创建输出流对象,写入时覆盖原内容 |
FileOutputStream(String pathname,boolean append) | 根据文件名创建输出流对象,第二个参数为true,写入时追加在原内容之后 |
FileOutputStream(File file) | 根据文件对象创建输出流对象,写入时覆盖原内容 |
FileOutputStream(File file,boolean append) | 根据文件对象创建输出流对象,第二个参数为true,写入时追加在原内容之后 |
常用方法
常用方法 | 作用 |
---|---|
write(int i) | 写入一个指定字节 |
write(byte[] bytes) | 写入一个字节数组 |
write(byte[] bytes,int off,int len) | 写入字节数组中从off开始的len个字节 |
flush() | 将流中的数据冲刷到硬盘中 |
close() | 关闭流对象 |
面试题
字符串相关面试题
//题目一
String str1 = "ab";//常量池中创建"ab"
String str2 = new String("ab");//堆中new String()保存常量池中已有的"ab"
String str3 = "a" + "b";//用常量池已有的"ab"
String str4 = "a" + new String("b");//常量池中创建"a"和"b",堆中new String()和new StringBuilder()
String str5 = "ab";//用常量池已有的"ab"
System.out.println(str1 == str2);//false
System.out.println(str1 == str3);//true
System.out.println(str1 == str4);//false
System.out.println(str1 == str5);//true
//题目二
//这两句话执行后,会创建几个对象
String s1 = "abc";
String s2 = "a" + "b" + "c";
//在字符串常量池中创建一个对象"abc"
//题目三
//这两句话执行后,会创建几个对象
String s3 = new String("你好");//常量池:"你好",堆中:new String()
String s4 = new String("你好");//堆中:new String()
//3个对象:堆中两个new String(),常量池中"你好"
//题目四
//这两句话执行后,会创建几个对象
String s5 = "hello";//常量池:"hello"
String s6 = "hel" + new String("lo");//常量池:"hel"和"lo" 堆:new String()和new StringBuilder
//5个对象:常量池:"hello"、"hel"和"lo",堆:new String()和new StringBuilder
//题目五
String s7 = new String("wor");//常量池:"wor",堆:new String()
String s8 = s7 + "ld";//常量池:"ld" 堆:new StringBuilder()
//4个对象:常量池:”wor"和"ld",堆:new String()和new StringBuilder
可变字符串面试题
比较String、StringBuilder和StringBuffer的区别
相同点:
- 这三个类都可以表示字符串。都提供了一些操作字符串的方法。
- 这三个类中有相同的方法,如charAt()、indexOf()等
- 这三个类都是被final修饰的类,不能被继承
不同点:
- String定义的字符串是一个常量。可变字符串定义的字符串是一个变量
- String类中的方法,调用后,不会改变原本字符串的值;可变字符串类中的方法,调用后,会改变原本字符串的值
- StringBuilder是非线程安全的可变字符串类,StringBuffer是线程安全的可变字符串类,其中的方法被synchronized修饰
总结
在频繁操作同一个字符串时,一定要使用可变字符串StringBuidler或StringBuffer类的对象,不能使用String类的对象
-
什么叫死锁?怎么产生?如何解决?
如果有两个人吃西餐,必须有刀和叉,此时只有一副刀叉。
如果A拿到了刀,B拿到了叉,互相都在等待另一个工具,但都不释放自己拥有的,这时就会造成僵持的局面,这个局面就称为死锁,既不结束,也不继续。
模拟死锁出现的情况
定义两个线程类,线程A先获取资源A后,在获取资源B;线程B先获取资源B后,再获取资源A。
如果对资源A和资源B使用了synchronized进行同步,就会在线程A获取资源A的时候,线程B无法获取资源A,相反线程B在获取资源B的时候,线程A无法获取资源B,所以两个线程都不会得到另一个资源。
死锁的解决方式
方式一
让两个线程获取资源的顺序保持一致。
如两个线程都先获取knife,再获取fork
@Override
public void run() {
synchronized (knife) {
System.out.println(Thread.currentThread().getName() + "获取了knife,3s后获取fork");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (fork) {
System.out.println(Thread.currentThread().getName() + "获取了fork,可以吃饭了");
}
}
}
方式二
让两个线程在获取资源A和B之前,再获取第三个资源,对第三个资源使用synchronized进行同步,这样某个线程在获取第三个资源后,将后续内容执行完毕,其他线程才能开始执行。
如在获取knife和fork之前,先获取paper对象
@Override
public void run() {
//先获取paper,再进行后续操作
synchronized (paper) {
synchronized (knife) {
System.out.println(Thread.currentThread().getName() + "获取了knife,3s后获取fork");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (fork) {
System.out.println(Thread.currentThread().getName() + "获取了fork,可以吃饭了");
}
}
}
以上大部分,都是老师上课的笔记,我摘抄了各种类的方法的使用,以及一些关键的面试题案例;
总结,这一章的学的效果并不是特别理想,有一部分是因为学的内容的的原因,还有一部分是因为自我产生了懈怠,下个星期把状态补回来,加油O.o!
n(Thread.currentThread().getName() + “获取了fork,可以吃饭了”);
}
}
}
### 方式二
让两个线程在获取资源A和B之前,再获取第三个资源,对第三个资源使用synchronized进行同步,这样某个线程在获取第三个资源后,将后续内容执行完毕,其他线程才能开始执行。
如在获取knife和fork之前,先获取paper对象
```java
@Override
public void run() {
//先获取paper,再进行后续操作
synchronized (paper) {
synchronized (knife) {
System.out.println(Thread.currentThread().getName() + "获取了knife,3s后获取fork");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (fork) {
System.out.println(Thread.currentThread().getName() + "获取了fork,可以吃饭了");
}
}
}
以上大部分,都是老师上课的笔记,我摘抄了各种类的方法的使用,以及一些关键的面试题案例;
总结,这一章的学的效果并不是特别理想,有一部分是因为学的内容的的原因,还有一部分是因为自我产生了懈怠,下个星期把状态补回来,加油O.o!