1.Lambda表达式
1.1 什么是Lambda表达式
定义:一段带有输入参数的可执行语句块。也可以这样说,Lambda表达式就是接口实现的一种方法,Lambda表达式的参数列表就是接口中抽象方法的参数列表,Lambda表达式中所需要执行的功能就是接口中抽象方法具体实现要执行的功能。
1.2 Lambda表达式语法
在java8中引入了一个新的操作符“->”,该操作符称为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式拆分成两个部分
(参数列表) -> { 过程体 }
例如:
(int x, int y) -> x + y
() -> 42
(String s) -> { System.out.println(s); }
- 第一个表达式使用两个整数参数,分别命名为x和y,并使用表达式形式返回x + y。
- 第二个表达式不接受任何参数,并使用表达式形式返回整数42.
- 第三个表达式接受一个字符串并使用块形式将字符串输出到控制台,并且不返回任何内容
- 左侧:Lambda表达式的参数列表
- 右侧:Lambda表达式中所需执行的功能,即Lambda体
语法格式一:实现的接口无参 无返回值
左侧直接写(),右侧是是实现接口的功能
()->System.out.println("Hello Lambda");
【牛刀小试:以Runable接口为例】
public class Test {
public static void main(String[] args) {
new Test().test1();
}
public void test1(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
};
r1.run();
System.out.println("--------------------------------");
Runnable r2 = () -> System.out.println("Hello Lambda");
r2.run();
}
}
Runable接口
public interface Runnable {
public abstract void run();
}
运行结果:
Hello World
--------------------------------
Hello Lambda
语法格式二:实现的接口有一个参数 无返回值
(X) -> System.out.println(x);
以Consumer接口为例
@Test
public void test2(){
Consumer<String> con = (x) -> System.out.println(x);
con.accept("Hello Lambda");
}
运行结果:
Hello Lambda
语法格式三 :若实现的接口有且只有一个参数 左侧()可以省略
X -> System.out.println(x);
@Test
public void test3(){
Consumer<String> con = x -> System.out.println(x);
con.accept("Hello Lambda");
}
运行结果:
Hello Lambda
语法格式四 :有两个及以上的参数,有返回值,并且Lambda体中有多条语句
Comparator<Integer> com = (x, y) -> {
System.out.println("Hello Lambda");
return Integer.compare(x,y);
以Comparator接口为例
@Test
public void test4() {
Comparator<Integer> com = (x, y) -> {
System.out.println("Hello Lambda");
return Integer.compare(x,y);// Comparator类的抽象方法 int compare(T o1, T o2);
};
}
语法格式五:有两个以上的参数,有返回值,并且Lambda体中只有一条语句 {}和return 可以省略
(x, y) -> Integer.compare(x,y);
@Test
public void test5() {
Comparator<Integer> com = (x, y) -> Integer.compare(x,y);
}
语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写 ,因为jdk1.8 JVM编译器可以通过上下文推断出数据类型,即“类型推断”
( x, y) -> Integer.compare(x,y);
@Test
public void test6() {
//省略前
Comparator<Integer> com = (Integer x,Integer y) -> Integer.compare(x,y);
//省略后
Comparator<Integer> com1 = (x, y) -> Integer.compare(x,y);
//比如 以下都是通过上下文推断出类型
String[] strs = {"aaa","bbb","ccc"};
List<String> list = new ArrayList<>();
show(new HashMap<>());
}
public void show(Map<String,Integer> map){
}
总结:
上联:左右遇一括号省
下联:左侧推断类型省
横批: 能省则省
Lambda 表达式需要“函数式接口”的支持
函数式接口:接口中只有一个抽象方法的接口,称为函数式接口
可以使用注解@FunctionalInterface 修饰,声明只能有一个抽象方法,可以检查是否为函数式接口
使用Lambda表达式,创建一个函数式接口
@FunctionalInterface
public interface MyFun {
public Integer getValue(Integer num);
}
@Test
public void test7(){
Integer num = opration(100, x -> x + 200);//加法
System.out.println(num);
System.out.println(opration(100, x -> x*x));//乘法
}
/**
* @param num 对一个数进行运算
* @param myFun 进行什么运算
* @return
*/
public Integer opration(Integer num,MyFun myFun){
return myFun.getValue(num);
}
运行结果:
300
10000
2.基本数据类型包装类
2.1 包装类的概述(java.lang包中)
为了解决无需将基本数据类型装换为对象,java在设计类时为每个基本数据类型设计了一个对应的类进行代表。这样八个和基本数据类型对应的类统称为包装类。2
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
注意:
1)在这八个类名中,除了Integer和Character类以外,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写即可。
2)在这八个类中,除了Interger和Character类之外,其余的都是数值类型。
2.1.1 包装类的作用
1)作为和基本数据类型对应的类型存在,方便涉及到对象的操作,如Object[],集合等操作。
2)包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法(这些操作方法的作用是在基本数据类型、包装类对象、字符串之间提供相互之间的转化!)
2.2基本数据类型和包装类(自动装箱)
2.2.1 基本数据类型转包装类
语法:
基本数据类型 变量名1 = 任意数值;
包装类 变量名2 = 变量名1;
例如:
jdk 1.5 以后
int num = 10;
Integer number = num;
jdk 1.5之前
int num = 10;
Integer number = Integer.valueOf(num);// valueOf(num);将int类型转换为Integer类型
2.2.2 基本数据类型转成包装对象注意事项:
1)针对Character类型,字符串不能转化为Character类型的包装对象。
2)针对Boolean类型,只有字符串为“true”(不区分大小写)的时候,转化为包装对象的值才为true,否则都为false。
3)数值型的包装类中(不包含Character和Boolean),形参字符串的内容为必须为数值型,否则抛出NumberFormatException异常。
2.3 包装类转基本数据类型(自动拆箱)
2.3.1 数值型的包装类
数值型包装类是java.lang.Number的子类,Number类提供了抽象方法:byteValue()、shortValue()、intValue()、longValue()、floatValue()、doubleValue(),意味着所有的数值型包装类都可以互相转型。
语法:
包装类 变量1 = 包装类.valueOf(任意数值);
基本数据类型 变量名2 = 变量名1;
【示例】
jdk1.5 之后
Integer number = Integer.valueOf(12);
int num = number;
jdk 1.5之前
Integer number = Integer.valueOf(12);
int num = number.intValue();
2.3.2 自动装箱的蜜糖
1)"=="
- 基本数据类型:比较的是两个数的值
- 引用数据类型:比较两个数变量(对象)的地址是否相同 true相同 false不相同
【示例】
分析下面代码,为什么会这样??
/*
Integer num1 = Integer.valueOf(125);
Integer num2 = Integer.valueOf(125);
System.out.println(num1 == num2);// 输出true
*/
Integer num1 = Integer.valueOf(128);
Integer num2 = Integer.valueOf(128);
System.out.println(num1 == num2);// 输出false
原因:
通过点击valueOf源码我们发现,valueOf每次判断值的时候它会先去缓存中的数据-128到127这个范围去找。
原理:
如果数据在-128~127之间,那么在类加载时就已经为该区间的每个数值创建了对象,并且将它们存储在一个名字叫做cache的数组中。每当自动装箱的过程发生时,就会判断数据是否在该区间。如果在,就直接获取数组中对应的包装类的对象的引用,不会创建对象。如果不在,就会创建对象。
[外链图片转存失败(img-SWJOHHIH-1568125623716)(C:\Users\22109\Pictures\valueof源代码.png)]
* This method will always cache values in the range -128 to 127
* @since 1.5
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
2)缓存:牺牲空间换取时间
在CPU和内存之间的一个桥梁
通常将频繁读取并且不易改变的数据放入缓存中
缓存中的数据只会加载一次 ,因为缓存中的数据类型的修饰符都会使用 static修饰
Integer里面的静态内部类IntegerCache里面的缓存 static final Integer cache[] 在外部类Integer成功加载到JVM的时候就会初始化内部类(IntegerCache)的静态块,从而初始化缓存
3.事件处理相关类
3.1 Date
import java.util.Date;
3.3.1 Date类的概述及历史
1)在标准java类库中包含一个java.util.Date类。
2)它的对象表示一个特定的瞬间,精确到毫秒。
3)Date类从jdk1.0就开始了。
3.3.2 Date为我们提供的构造器
构造器 | 作用 |
---|---|
Date() | 创建一个代表当前日期时间的Date对象。 |
Date(long date) | 指定的long型整数来创建一个Date对象,以表示自从标准基准时间以来的指定毫秒数。 |
**标准基准时间:**又称“历年(epoch)”,即1970年1月1日00:00:00 GMT。
Date对象的大部分方法也已经弃用了,只剩下为数不多的几个方法:
boolean before(Date when):测试此日期是否在指定日期之后
boolean after(Date when):测试此日期是否在指定日期之前。
boolean equals(Object obj):比较两个日期的相等性。
long getTime():返回自1970年1月1日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
void setTime(long time):使用给定的毫秒时间值设置现有的 Date对象。
3.3.3 如何获取现在的“时刻数值”
long now = System.currentTimeMillis();
**时刻数值:**是所有时间类的核心值,年月日都是根据这个“数值”计算出来的。
3.3.4 如何使用Date类
Date date = new Date();
System.out.println(date);// 输出Sat Sep 07 19:10:39 CST 2019
// 从1970.01.01 00:00:00到现在的毫秒数
long time = System.currentTimeMillis();
System.out.println(time);// 输出1567854766682
Date dt = new Date(time);
System.out.println(dt);// 输出Sat Sep 07 19:13:40 CST 2019
3.2 SimpleDateFormat
import java.text.SimpleDateFormat;
由于Date对象打印出来的日期时间不够友好,我们想把日期时间变得更友好。例如:2019-01-03 21:34:42
3.2.1 SimpleDateFormat的作用
用于指定日期时间的格式,可以按照自己的需求来设置时间。
目的:将日期时间优化显示。
【牛刀小试】
Date date = new Date(System.currentTimeMillis());
/*
* YYYY yyyy 年份
* MM 月份
* dd 一个月中的日期
* HH 24小时
* hh 12小时
* mm 分钟
* ss 秒钟
* LL 月份
* DD 一年中的天数,今天是一年中的第250天
*/
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 将日期时间转换为指定的格式
String currentTime = sdf.format(date);
System.out.println(currentTime);// 输出2019-09-07 19:18:35
**优点:**能够按照指定的方式获取年月日时分秒
**缺点:**不能单独获取年月日时分秒
3.3 Calendar
import java.util.Calendar;
3.3.1 Calendar的作用
使用日历获取时间,可以获取年月日时分秒,也可以单独的获取年月日时分秒,还可以设置时间。
【牛刀小试】
// ca编译期是抽象类,运行期是java.util.GregorianCalendar(是Calendar的子类)
Calendar ca = Calendar.getInstance();
// 获取日历的年份
int year = ca.get(Calendar.YEAR);
System.out.println(year);// 输出2019
// 获取日历的月份 范围在0-11
int month = ca.get(Calendar.MONTH);
System.out.println(month);// 输出8
//一个月的第一周
int day = ca.get(Calendar.DAY_OF_WEEK_IN_MONTH);
System.out.println(day);// 输出1
//一个月中的第几天
int days = ca.get(Calendar.DAY_OF_MONTH);
System.out.println(days);// 输出7
//一天中的小时
int hour = ca.get(Calendar.HOUR_OF_DAY);
System.out.println(hour);// 输出19
//分钟
int minute = ca.get(Calendar.MINUTE);
System.out.println(minute);// 输出33
// 秒
int second = ca.get(Calendar.SECOND);
System.out.println(second);// 输出20
**注意:**静态属性和静态方法不要使用对象名称调用,必须使用类名称调用
设置年月日获取时间
【牛刀小试】
Calendar ca = Calendar.getInstance();
//将年份设置为2030年
ca.set(Calendar.YEAR,2030);
// set方法没有返回值,必须调用get方法才知道是否设置成功
int year = ca.get(Calendar.YEAR);
System.out.println(year); //输出2030
// 设置年月日(月份从0开始)
ca.set(2020,12,12);
// 获取2020 12 12
Date dt = ca.getTime();
System.out.println(dt); //输出Tue Jan 12 19:41:00 CST 2021
3.4 LocalDate
import java.time.LocalDate;
日期
【牛刀小试】
//JDK1.8新的日期类:在Date和Calendar上面做了优化,不需要转换就能够直接输出日期
LocalDate ld = LocalDate.now();
//将日期转换为字符串
String date = ld.toString();
System.out.println(date); // 输出2019-09-07
LocalDate ldt = LocalDate.of(2020, 12, 16);
System.out.println(ldt);// 输出2020-12-16
3.5 LocalTime
import java.time.LocalDateTime;
时间(时分秒)
【牛刀小试】
LocalTime lt = LocalTime.now();
//时间转换为字符串
String time = lt.toString();
System.out.println(time);// 输出19:51:05.478
去掉纳秒
//withNano(0) 将纳秒设置为0
LocalTime lt2 = LocalTime.now().withNano(0);
String time2 = lt2.toString();
System.out.println(time2);// 输出19:51:05
**注意:**去掉纳秒,可以用withNano(0) 将纳秒设置为0。
3.6 LocalDateTime
import java.time.LocalDateTime;
日期+时间
【牛刀小试】
LocalDateTime ldt = LocalDateTime.now().withNano(0);
String dateTime = ldt.toString();
System.out.println(dateTime);// 输出2019-09-07T19:53:56
如何将上面例子中的T去掉呢,我们需要设置一个日期和时间的格式,来转换日期时间
【牛刀小试】
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
LocalDateTime ldt =LocalDateTime.now().withNano(0);
// String dateTime = ldt.toString();
// System.out.println(dateTime);
//DateTimeFormatter:日期和时间按照指定的格式进行转换 SinceJDK1.8
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(dtf); //输出Value(YearOfEra,4,19,EXCEEDS_PAD)'-'Value(MonthOfYear,2)'-'Value(DayOfMonth,2)' 'Value(HourOfDay,2)':'Value(MinuteOfHour,2)':'Value(SecondOfMinute,2)
//format(TemporalAccessor temporal)型参是TemporalAccessor,传入的实际参数可以是LocalDateTime
String dateTime = dtf.format(ldt);
System.out.println(dateTime); // 输出2019-09-07 19:58:15;
4.File类
4.1 File的基本用法
**java.io.File类:**代表文件和目录。在开发中,读取文件、生成文件、删除文件、修改文件的属性时经常会用到本类。
**File类的常见构造方法:**public File(String pathname)
**pathname:**pathname代表文件路径,该路径可以为相对路径,也可以为绝对路径。如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
**【牛刀小试】**分别以相对路径和绝对路径创建File对象
public static void main(String[] args) throws IOException {
// 获取当前工作目录路径
System.out.println(System.getProperty("user.dir"));
// 1.相对路径创建File对象:默认放到user.dir目录下面
File file1 = new File("abc.txt");
// 创建文件
file1.createNewFile();
// 2.绝对路径创建File对象
File file2 = new File("D:/abc.txt");
// 创建文件
file2.createNewFile();
}
4.2 常见的File构造方法
构造方法 | 描述 |
---|---|
public File(String pathname) | 通过将给定路径名字符串转换为抽象路径名来创建一个新File实例 |
public File(File parent, String child) | 根据parent抽象路径名和child路径名字符串创建一个新File实例 |
public File(String parent, String child) | 根据parent路径名字符串和child路径名字符串创建一个新File实例 |
4.3 File类获取功能
方法名 | 描述 |
---|---|
public String getName() | 返回由此抽象路径名表示的文件或目录的名称。 |
public String getPath() | 返回此抽象路径名的绝对形式(绝对路径或相对路径)。 |
public String getAbsolutePath() | 返回此抽象路径名的绝对形式(绝对路径)。 |
public long lastModified() | 返回此抽象路径名表示的文件最后一次被修改的时间。 |
public long length() | 返回由此抽象路径名表示的文件的长度。 |
【牛刀小试】
// 1.相对路径创建File对象
File file = new File("abc.txt");
// 2.获取文件或目录名字
System.out.println(file.getName());
// 3.返回此抽象路径名的绝对形式(绝对路径或相对路径)。
System.out.println(file.getPath());
// 4.返回此抽象路径名的绝对形式(绝对路径)。
System.out.println(file.getAbsolutePath());
// 5.返回此抽象路径名表示的文件最后一次被修改的时间
System.out.println(file.lastModified());
// 6.返回由此抽象路径名表示的文件的长度。
Sstem.out.println(file.length());
4.5 File类判断功能
方法名 | 描述 |
---|---|
public boolean exists() | 测试此抽象路径名表示的文件或目录是否存在。 |
public boolean isDirectory() | 测试此抽象路径名表示的文件是否是一个目录。 |
public boolean isFile() | 测试此抽象路径名表示的文件是否是一个标准文件。 |
public boolean isHidden() | 测试此抽象路径名指定的文件是否是一个隐藏文件。 |
public boolean canRead() | 测试应用程序是否可以读取此抽象路径名表示的文件。 |
public boolean canWrite() | 测试应用程序是否可以修改此抽象路径名表示的文件。 |
【牛刀小试】
// 1.相对路径创建File对象
File file = new File("abc.txt");
// 2.测试此抽象路径名表示的文件或目录是否存在。
System.out.println(file.exists());
// 3.测试此抽象路径名表示的文件是否是一个目录。
System.out.println(file.isDirectory());
// 4.测试此抽象路径名表示的文件是否是一个标准文件。
System.out.println(file.isFile());
// 5.测试此抽象路径名指定的文件是否是一个隐藏文件。
System.out.println(file.isHidden());
// 6.测试应用程序是否可以读取此抽象路径名表示的文件。
System.out.println(file.canRead());
// 7.测试应用程序是否可以修改此抽象路径名表示的文件。
System.out.println(file.canWrite());
4.6 File类其它功能
方法名 | 描述 |
---|---|
public boolean mkdir() | 创建此抽象路径名指定的目录。 |
public boolean mkdirs() | 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。 |
public boolean createNewFile() | 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。 |
public boolean delete() | 删除此抽象路径名表示的文件或目录。 |
public boolean renameTo(File dest) | 重新命名此抽象路径名表示的文件。 |
public String[] list() | 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。 |
public File[] listFiles() | 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。 |
【牛刀小试】
public static void main(String[] args) throws IOException {
// 1.mkdir方法使用(G盘下没有demo文件夹)
File file1 = new File("G:/demo");
if(!file1.exists()) { // 判断文件夹是够存在
file1.mkdir(); // 不存在则创建一个文件夹
}
// 2.mkdirs方法使用(G盘下没有java和demo文件夹)
File file2 = new File("G:/java/demo");
if(!file2.exists()) { // 判断文件夹是够存在
file2.mkdirs(); // 不存在则创建多个文件夹
}
// 3.createNewFile方法使用(G盘下没有abc.txt文件)
File file3 = new File("G:/abc.txt");
if(!file3.exists()) { // 判断文件是够存在
file3.createNewFile(); // 不存在则创建该文件
}
// 4.delete方法,删除文件或文件夹
file1.delete(); // 删除文件文件夹
file3.delete(); // 删除文件
// 5.renameTo方法,文件重命名
file3.renameTo(new File("G:/haha.txt"));
// 6.list方法,遍历文件和文件夹
File file4 = new File("G:");
for(String path : file4.list()) {
System.out.println(path);
}
// 7.listFiles方法,遍历目录中的文件
for(File file : file4.listFiles()) {
System.out.println(file);
}
}
4.7 File类的过滤器
通过 listFiles()方法,我们可以获取到一个目录下的所有文件和文件夹,但能不能对其进行过滤呢?比如我们只想要一个目录下的指定扩展名的文件,或者包含某些关键字的文件夹呢?
查阅API,发现 File 类中重载的 listFiles 方法,并且接受指定的过滤器。
【牛刀小试】
//自定类继承 FilenameFilter过滤器接口
class MyFilenameFilter implements FilenameFilter {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".java");
}
}
// 测试类
public class FileDemo {
// 获取扩展名为.java所有文件
public static void main(String[] args) {
// 创建 File 对象
File file = new File("C:\\Users\\Administrator\\Desktop\\Java");
// 获取指定扩展名的文件
File[] files = file.listFiles(new MyFilenameFilter());
// 遍历获取到的所有符合条件的文件
for (File f : files) {
System.out.println(f.getName());
}
}
}
在查阅 API 时,我们发现,在 listFiles(FileFilter filter) 也可以接受一个 FileFilter 过滤器,它和我们讲的 FilenameFilter 有啥区别呢?
FilenameFilter 过滤器中的 accept 方法接受两个参数,一个当前文件或文件夹所在的路径,一个
是当前文件或文件夹对象的名称。
FileFilter 过滤器中的accept 方法接受一个参数,这个参数就当前文件或文件夹对象。
当我们需要过滤文件名称时就可以使用 FilenameFilter 这个过滤器,当我们想对当前文件或文件
夹进行过滤,就可以使用 FileFilter ,比如需要当前目录下的所有文件夹,就可以使用 FileFilter 过滤器。
**【牛刀小试】**使用Lambda表达式完成文件过滤
public class TestFileFilter {
public static void main(String[] args) {
File file = new File("test0812");
File list []=file.listFiles((dir,name)->name.endsWith(".java"));
for (File filePath : list) {
String path =filePath.getAbsolutePath();
System.out.println(path);
}
}
}
【牛刀小试】获取指定目录下的文件
//自定类继承 FileFilter过滤器接口
class MyFileFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
return pathname.isFile();
}
}
// 测试类
public class FileDemo {
// 获取指定目录下的文件(排除文件夹)
public static void main(String[] args) {
// 创建 File 对象
File file = new File("C:\\Users\\Administrator\\Desktop\\Java");
// 获取指定目录下的文件
File[] files = file.listFiles(new MyFileFilter());
// 遍历获取到的所有符合条件的文件
for (File f : files) {
System.out.println(f.getName());
}
}
}
4.8 磁盘的使用率
步骤:1创建File对象
2 获取当前磁盘的总空间
3 获取当前磁盘的自由空间
4 计算已使用空间
5 计算使用率
6 保留小数
public class TestFileDiskRage {
public static void main(String[] args) {
//.. 当前路径,当前工程的路径
File file = new File("..");
System.out.println(file.getAbsolutePath());
/*
*1 总空间
*2 自由空间
*3 计算已使用空间
*4 计算使用率
*5 使用BigDecimal精确小数
*6 将BigDecimal转换为double,并且打印结果
* */
long total =file.getTotalSpace();
long free =file.getFreeSpace();
long used = total-free;
//使用率
double usedRate = 1.0 * 100 * used/total;
BigDecimal big = new BigDecimal(usedRate, new MathContext(3,RoundingMode.HALF_UP));
double finalResult = big.doubleValue();
System.out.println(finalResult+"%");
}
}
Lambda
import java.io.File;
/**
* 获取后缀为java的文件
*/
public class TestFileList {
public static void main(String[] args) {
//.. 计算磁盘使用率使用..表示当前路径
File file = new File("..");
System.out.println(file.getAbsolutePath());
File fileList []= file.listFiles((ff,name)-> name.endsWith(".java"));
for(File filePath : fileList) {
System.out.println(filePath.getAbsolutePath());
}
}
}
4.9 删除目录
场景:删除某个文件夹下面所有的子文件夹和文件
会从最里面的文件夹开始删除,一直删除,最后删除自己
如何删除:需要使用递归(自己调用自己)
删除步骤:
1指定一个路径,该路径表示的文件或者目录就是你要删除的资源
2 判断该路径是否存在并且该路径是否是文件夹
3如果不是文件夹(目录),直接删除文件
4如果是文件夹(目录),获取目录下面所有的子目录和文件
5判断子目录下面有没有文件或者目录,如果有继续向下一层获取,如果没有就删除
【牛刀小试】
public class TestDeleteFile {
public static void main(String[] args) {
File file = new File("test123");
deleteFolderAndFile(file);
}
/**
* 删除目录和或者文件
* @param file 外界传入的路径
* @return true删除成功 false删除失败
*/
public static boolean deleteFolderAndFile(File file) {
//条件成立:表示该路径存在并且是目录,获取该路径下面的所有子目录和文件
if(file.exists() && file.isDirectory()) {
//获取路径下面所有的文件和目录列表
File fileList[] = file.listFiles();
for (File subFilepath : fileList) {
//参数1:父路径(test0812)
//参数2:子路径的文件名或者目录名 ,例如:test
File subFile = new File(file,subFilepath.getName());
// 做递归运算,自己调用自己
deleteFolderAndFile(subFile);
}
}
// 当路径是文件直接删除
return file.delete();
}
}
4.10 小结:
掌握的技能点:文件列表 (匿名内部类、Lambda),磁盘使用率,删除某个路径下面的所有文件和文件夹
了解的技能点: File对象的创建和常用的方法
5.String(核心)
5.1 慨念
有一个或者多个文字符号组成的字符序列,所以他的本质是一个字符数组 char[]
5.2 如何创建String对象
1)一种是使用双引号
2)一种是调用String类的构造函数
【牛刀小试】
String str1 = "we are student";
String str2 = new String ("we are student");
5.2.1 两种字符串创建方式区别
// 通过双引号创建字符串
String str1 = "hello world";// (在常量池中)
// 通过构造方法创建字符串
String str2 = new String("hello world");//(一个 new 的对象在堆中,一个字符串本身对象,在字符串
常量池中。)
// 比较地址
System.out.println(str1 == str2); // 输出:false
// equals方法比较字符串内容
System.out.println(str1.equals(str2)); // 输出:true
**注意:**java为String类型提供了缓冲池机制,当使用双引号定义对象时,Java环境首先去字符串缓冲池寻找内容相同的字符串,如果存在就拿出来使用,否则就创建一个新的字符串放在缓冲池中。
问题一:String str = new String(“abc”), 产生(或者创建)几个对象
答案是:产生一个或者两个对象。如果常量池中原来没有“abc",就产两个对象,如果字符串常量池中"abc",就产生一个对象。
问题二:String str = new String(“abc”)涉及到几个String对象
答案是:2个
5.3 String类的特性
1)String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。
2)字符串常量池: 我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性,因此我们可以十分肯定常量池中一定不存在两个相同的字符串
5.4 常量池分类(形态)
1)静态常量池:即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。
2)运行时常量池:则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
5.5 String类构造方法
构造方法 | 注解 |
---|---|
[String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#String–)() | 初始化新创建的String对象,使其表示空字符序列。 |
[String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#String-java.lang.String-)([String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) original) | 初始化新创建的String对象,新创建的字符串是参数字符串的副本。 |
[String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#String-byte:A-)(byte[] bytes) | 通过使用平台的默认字符集解码指定的字节数组来构造新的String。 |
[String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#String-char:A-)(char[] value) | 分配一个新的String,以便它表示当前包含在字符数组参数中的字符序列。 |
【牛刀小试】
// 1.创建一个空字符串
String str1 = new String(); // 存储内容:""
// 2.通过常量字符串来创建字符串
String str2 = new String("whsxt"); // 存储内容:"whsxt"
// 3.通过byte数组来创建字符串
byte[] bytes = new byte[] {65, 66, 67, 68, 69};
// 3.1把byte数组全部元素作为字符串的内容来创建字符串
String str3 = new String(bytes); // 存储内容:"ABCDE"
// 4.通过char数组来创建字符串
char[] chars = new char[] {'a', 'b', 'c', 'd', 'e'};
// 4.1把char数组全部元素作为字符串的内容来创建字符串
String str4 = new String(chars); // 存储内容:"abcde"
5.6 String 类查找的方法
5.6.1 获取字符串长度
通过String类提供的length()方法,可以获取到字符串中有多少个字符。注意区分获取数组长度和字符串长度的方式,数组是通过length属性来获取数组的长度 。
【牛刀小试】
String str = new String("whsxt");
int length = str.length();
System.out.println("length:" + length); // 输出:5
5.6.2 length 和 length()的区别
length:length不是方法,是属性,数组的属性;
length():length()是字符串String的一个方法
5.6.3 charAt方法
char charAt(int index)方法是一个能够用来检索特定索引下的字符的String实例的方法。
charAt()方法返回指定索引位置的char值,索引范围为0 ~ length() – 1,如果索引越界,则抛出StringIndexOutOfBoundsException异常。
【牛刀小试】
String str = new String("whsxt");
// 注意:索引范围为0~length()-1
char c1 = str.charAt(3);
System.out.println(c1); // 输出:'x'
// 如果索引越界,抛出StringIndexOutOfBoundsException异常
char c2 = str.charAt(8); // 运行抛出异常
5.6.4 indexOf方法
通过indexOf方法,获取字符&子字符串在指定字符串中第一次出现的位置(从前往后查找)。
indexOf方法返回一个整数值,该整数值就是字符或子字符串在指定字符串中所在的位置,如果没有找到则返回-1。如果fromIndex是负数,则fromIndex被当作零,如果它比最大的字符位置索引还大,则它被当作最大的可能索引。
方法区 | 注解 |
---|---|
int [indexOf](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#indexOf-int-)(int ch) | 返回指定字符第一次出现在字符串内的索引。 |
int [indexOf](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#indexOf-int-int-)(int ch, int fromIndex) | 返回指定字符第一次出现在字符串内的索引,以指定的索引开始搜索。 |
int [indexOf](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#indexOf-java.lang.String-)([String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) str) | 返回指定子字符串第一次出现在字符串内的索引。 |
int [indexOf](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#indexOf-java.lang.String-int-)([String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) str, int fromIndex) | 返回指定子串的第一次出现在字符串中的索引,从指定的索引开始搜索。 |
【牛刀小试】
String str = "hello-world-hello-world";
// 1. 获取指定字符在字符串中的第一次出现的位置,如果字符不存在返回-1
int index1 = str.indexOf('o');
System.out.println("index:" + index1); // 输出:4
// 2. 获取指定字符第一次出现在字符串中的位置,以指定的索引开始搜索,如果字符不存在返回-1
int index2 = str.indexOf('o', 5);
System.out.println("index:" + index2); // 输出:7
// 3.获取指定子字符串在字符串中第一次出现的位置,如果字符不存在返回-1
int index3 = str.indexOf("llo");
System.out.println("index:" + index3); // 输出:2
// 4.获取指定字符串第一次出现的字符串中的位置,以指定的索引开始搜索,如果字符不存在返回-1
int index4 = str.indexOf("llo", 9);
System.out.println("index:" + index4); // 输出:14
5.6.5 startsWith()
startsWith(String prefix)方法,判断字符串是否以指定字符串开头,返回值为boolean类型。
【牛刀小试】
String url = "http://www.whsxt.com";
// 判断url是否采用的http:协议,就是判断字符串是否以"http:"开头
boolean flag = url.startsWith("http:");
System.out.println("flag:" + flag); // 输出:true
5.6.6 endsWith方法
endsWith(String prefix)方法,判断字符串是否以指定字符串结尾,返回值为boolean类型。
【牛刀小试】
String url = "http://www.whsxt.com";
// 判断网站域名是否是.cn域名,也就是判断字符串是否以.cn结尾
boolean flag = url.endsWith(".cn");
System.out.println("flag:" + flag); // 输出:false
5.6.7 contains 方法
contains(CharSequence s)方法,判断字符串中是否包含指定字符串,返回值为boolean类型。
CharSequence是一个接口,String类是该接口的实现类,所以contains方法的参数可以为字符串。
【牛刀小试】
String url = "http://www.whsxt.com";
// 判断字符串中是否包含"whsxt"
boolean flag = url.contains("whsxt");
System.out.println("flag:" + flag); // 输出:true
5.6.8 intern() 方法(维护)
public class StringTestIntern {
public static void main(String[] args) {
String str1="Tom";
String str2=new String("Tom");
//检查对内存中的Tom是否在字符串常量池中存在,如果存在str2直接指向常量池中的Tom,如果不存在在常量池中创建一个Tom并返回
//此时常量池存在Tom,str2改变指向(由之前指向堆内存改变为指向堆内存中常量池的Tom)
str2.intern();
System.out.println(str1==str2);
}
}
5.7 String类型转换方法
5.7.1 字符串转数组
方法 | 注解 |
---|---|
[String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html)[] [split](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#split-java.lang.String-)([String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) regex) | 将一个字符串分割为子字符串,然后将结果作为字符串数组返回 |
char[] [toCharArray](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#toCharArray–)() | 将此字符串转换为新的字符数组。 |
byte[] [getBytes](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#getBytes–)() | 得到一个操作系统默认的编码格式的字节数组 |
【牛刀小试】
String str = "I Love WHSXT";
// 字符串转数组,通过空格来做分割
String[] arr1 = str.split(" ");
// 输出:[I, Love, WHSXT]
System.out.println(Arrays.toString(arr1));
// 字符串转字符数组
char[] arr2 = str.toCharArray();
// 输出:[I, , L, o, v, e, , Y, o, u]
System.out.println(Arrays.toString(arr2));
// 字符串转字节数组
byte[] arr3 = str.getBytes();
// 输出:[73, 32, 76, 111, 118, 101, 32, 87, 72, 83, 88, 84]
System.out.println(Arrays.toString(arr3));
5.7.2 字符串大小写转换
方法 | 注解 |
---|---|
[String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) [toUpperCase](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#toUpperCase–)() | 返回一个新的字符串,该字符串中所有英文字符转换为大写字母。 |
[String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) [toLowerCase](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#toLowerCase–)() | 返回一个新的字符串,该字符串中所有英文字符转换为小写字母。 |
5.7.3 忽略字符串前后空格
使用trim()方法去掉字符串中首尾的空格。
【牛刀小试】
String str = " he llo ";
// 忽略字符串前后空格
String newStr = str.trim();
// 输出:"--he llo--"
System.out.println("--" + newStr + "--");
5.7.4 字符串截取
方法 | 注解 |
---|---|
[String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) [substring](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#substring-int-)(int beginIndex) | 从beginIndex开始截取字符串,到字符串末尾结束。 |
[String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) [substring](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#substring-int-int-)(int beginIndex, int endIndex) | 从beginIndex开始截取字符串,到字符索引endIndex-1结束。 |
【牛刀小试】
String str = "hello world";
// 从索引startIndex(包含)开始截取,一直截取到字符串末尾
String subStr1 = str.substring(3);
System.out.println(subStr1); // 输出:"lo world"
// 从索引startIndex(包含)开始截取,到endIndex(不包含)结束
String subStr2 = str.substring(3, 8);
System.out.println(subStr2); // 输出:"lo wo"
注意事项:
1、 在substring(int beginIndex)方法中,如果 beginIndex的值为负或大于字符串长度,则抛出IndexOutOfBoundsException异常。
2、 在substring(int beginIndex, int endIndex)方法中,如果beginIndex为负,或endIndex大于字符串长度,或beginIndex大于endIndex,则抛出IndexOutOfBoundsException异常。
5.7.5 字符串替换
方法 | 注解 |
---|---|
[String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) [replace](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#replace-char-char-)(char oldChar, char newChar) | 通过用newChar字符替换字符串中出现的所有oldChar字符,并返回替换后的新字符串。 |
[String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) [replace](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#replace-java.lang.CharSequence-java.lang.CharSequence-)([CharSequence](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/CharSequence.html) target, [CharSequence](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/CharSequence.html) replacement) | 将与字面目标序列匹配的字符串的每个子字符串替换为指定的字面替换序列。 |
【牛刀小试】
String str = "BJSXT and WHSXT";
// 替换字符串中的符合条件的字符
String newStr1 = str.replace('X', '学');
// 输出:"BJS学T and WHS学T"
System.out.println(newStr1);
// 替换字符串中所有符合条件的子字符串
String newStr2 = str.replace("SXT", "尚学堂");
// 输出:"BJ尚学堂 and WH尚学堂"
System.out.println(newStr2);
5.7.6 字符串拼接
String concat(String str)方法,将指定的字符串连接到该字符串的末尾,该方法实现的功能和“+”连接符比较相似。
【牛刀小试】
// 字符串拼接,功能和“+”连接符相似
String str = "hello".concat(" world");
// 输出:"hello world"
System.out.println(str);
5.8 String 类的其他方法
5.8.1 isEmpty
boolean isEmpty()方法,判断字符串内容是否为空。当字符串长度为0,则返回true,否则返回false。
【牛刀小试】
// 当字符串内容为空时,返回true
String str1 = "";
boolean flag1 = str1.isEmpty();
System.out.println(flag1); // 输出:true
// 当字符串内容为不为空时,返回false
String str2 = "hello world";
boolean flag2 = str1.isEmpty();
System.out.println(flag2); // 输出:true
//在工作中这样判断
String str3 = null;
boolean result = (null == str3 || str.length() == 0);
System.out.println(result);// 输出true
5.8.2 equals 方法
方法 | 注解 |
---|---|
boolean [equals](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#equals-java.lang.Object-)([Object](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/Object.html) anObject) | 判断字符串内容是否相同 |
boolean [equalsIgnoreCase](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html#equalsIgnoreCase-java.lang.String-)([String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) anotherString) | 判断字符串内容是否相同,忽略字母大小写。 |
【牛刀小试】
String str = "hello world";
// 判断字符串内容是否相同
boolean flag1 = str.equals("hello WORLD");
System.out.println(flag1); // 输出:false
// 判断字符串内容是否相同,忽略大小写
boolean flag2 = str.equalsIgnoreCase("hello WORLD");
System.out.println(flag2); // 输出:true
5.8.3 compareTo 方法
int compareTo(String anotherString)方法按字典顺序比较两个字符串,返回参与比较的前后两个字符串的Unicode码的差值。
如果String对象按字典顺序排列在参数字符串之前,结果为负整数。如果String对象按字典顺序排列在参数字符串之后,结果为正整数。如果字符串相等,则结果为零。
【牛刀小试】
// 字符串相同,返回结果为0
int num1 = "abc".compareTo("abc");
System.out.println(num1);
// 字符串按字典顺序排列在参数字符串之前,结果为负整数
int num2 = "AB".compareTo("abc");
System.out.println(num2);
// 字符串按字典顺序排列在参数字符串之后,结果为正整数
int num3 = "abcd".compareTo("abc");
System.out.println(num3);
5.8.4 valueOf 方法
valueOf方法是一个静态方法,可以把基本数据类型转化为字符串类型,是类型转换中非常重要的一个方法。
【牛刀小试】
// 把int类中转化为字符串
System.out.println(String.valueOf(123) + 4); // 输出:"1234"
// 把double类型转化为字符串
System.out.println(String.valueOf(1.23) + 4); // 输出:"1.234"
// 把boolean类型转化为字符串
System.out.println(String.valueOf(true) + 4); // 输出:"true4"
// 把char类型转化为字符串
System.out.println(String.valueOf('A') + 4); // 输出:"A4"
6.StringBuffer类
6.1 StringBuffer 类概述
变长的字符串(可变的)
6.2 StringBuffter 类构造方法
构造方法 | 注解 |
---|---|
[StringBuffer](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/StringBuffer.html#StringBuffer–)() | 构造一个没有字符的字符串缓冲区,初始容量为16个字符。 |
[StringBuffer](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/StringBuffer.html#StringBuffer-java.lang.CharSequence-)([CharSequence](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/CharSequence.html) seq) | 构造一个包含与指定的相同字符的字符串缓冲区CharSequence 。 |
[StringBuffer](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/StringBuffer.html#StringBuffer-int-)(int capacity) | 构造一个没有字符的字符串缓冲区和指定的初始容量。 |
[StringBuffer](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/StringBuffer.html#StringBuffer-java.lang.String-)([String](mk:@MSITStore:C:\Users\Administrator\Desktop\jdk api 1.8_google.CHM::/java/lang/String.html) str) | 构造一个初始化为指定字符串内容的字符串缓冲区。 |
无参构造方法:new StringBuffer();
当你使用该构造方法创建对象,默认会分配16个长度到 char value[] =new char[16];
有一个int参数的构造方法: new StringBuffer(int);
注意:工作中不要频繁扩容,也不要永远不扩容,效率都不高
正确的做法:定义一个带有参数的构造方法 new StringBuffer(50);
【牛刀小试】
// 1.实例化一个初始容量为16个字符的可变字符串
StringBuffer sb1 = new StringBuffer();
// 2.实例化一个指定字符容量的可变字符串
StringBuffer sb2 = new StringBuffer(20);
// 3.实例化一个指定字符串内容的可变字符串
StringBuffer sb3 = new StringBuffer("hello world");
扩容方式: 数组原始长度*2+2
新数组 Arrays.copyOf(原始数组,新的容量);
6.2.1 链式编程
*【牛刀小试】
public class TestStringBuffer {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
//每次调用append()方法追加字符串,都会返回当前对象this(new StringBuffer())
sb.append("Tomson").append("Tom").append("Jerry");
System.out.println(sb.toString());
}
}
**小结:**StringBuffer最重要的成员方法:append()追加字符串 toString() 将StringBuffer转换为String()
7 StringBuilder类
StringBuilder类概述
StringBuffer类和StringBuilder类非常类似,都是继承自抽象类AbstractStringBuilder类,均代表可变的Unicode字符序列。
StringBuilder类和StringBuffer类方法几乎一模一样
8.String,StringBuilder和StringBuffer的区别
1)从是否可变的角度
string源代码
String类中使用字符数组保存字符串,因为有“final”修饰符,所以String对象是不可变的。
StringBuilder源代码
StringBuffer源代码
StringBuffer和StringBuilder都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,但没有“final”修饰符,所以两种对象都是可变的。
2)是否线程安全
String中的对象是不可变的,也就是可以理解为常量,所以是线程安全的.
AbstractStringBuilder是StringBuffer和StringBuilder的公共父类,定义了一些字符串的基本操作,如append、、indexOf等公共方法。
StringBuffer对方法加了同步锁(synchronized) ,所以是线程安全的。看如下源码:
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。如下源码:
StringBuffer和StringBuilder的共同点 StringBuffer和StringBuilder有公共父类AbstractStringBuilder(抽象类)。
StringBuffer、StringBuilder的方法都会调用AbstractStringBuilder中的公共方法,如上面的两段源码中都调用了super.append(str); 只是StringBuffer会在方法上加synchronized关键字,进行同步。
9.UnitTest
对独立的功能进行测试,测试你的业务逻辑
特征:可以在一个类中编写多个单元测试用例
单元测试必须依赖于第三方jar包
何为第三方:我的程序是一方,JDK提供的API是一方,除了我的程序和JDK提供的API以外任何代码都叫做第三方。
如何将第三方的jar文件(包),放到工程下,必须导入jar包到我的工程
jar包封装了一些类单元测试相关的class文件,也就是说这些class文件需要打成一个jar包才能够使用。
单元测试的步骤:1导入单元测试相关的jar包到工程下 junit-4.7.jar
步骤:1在工程下创建一个目录(文件夹) 右键工程—>New—>Folder lib
2将junit-4.7.jar拷贝到lib下面
3 右键junit-4.7.jar---->BuilderPath---->Add to BuilderPath
将jar包导入工程
2 定义单元测试类,编写单元测试的相关方法
注意:@Test表示我定义的方法是单元测试的方法,所有参与单元测试的方法必须加上@Test
3 选中方法(test()),右键---->Run As---->Junit Test 运行程序
import org.junit.Test;
public class TestStringBuffer {
@Test
public void test() {
StringBuffer sb = new StringBuffer(100000);
for(int i=0;i<5000000;i++) {
sb.append("i");
}
}
}
注意:测试成功绿色线条,测试失败红色线条.
参与单元测试的方法必须是public,返回类型必须是void 方法参数必须是无参.
10. 枚举
10.1 枚举的概述
枚举(enum)类型是jdk1.5新加的特性,它是一种新的类型,允许用常量来表示特定的数据片段,而且全部类型都以类型安全的形式来表示.
目的:代替常量
10.2 枚举语法
创建枚举类型要使用 enum 关键字,隐含了所创建的类型都是 java.lang.Enum 类的子类(java.lang.Enum 是一个抽象类)。
enum 枚举名 {
枚举体(常量列表)
//最后一个元素以;结束
元素1,元素2,元素3......元素n;
//定义成员方法
//定义静态方法
//定义构造方法
//定义抽象方法
//定义属性
}
10.3 使用枚举的好处
一组常量的集合(春夏秋冬),能够从多个维度(季节名称、季节描述、季节编号)描述常量
回顾:输入数字获取季节0123 春夏秋冬
步骤:定义接口,编写常量
从控制台数据数字,编写switch case语句,判断输入的数字是否匹配case,如果匹配打印季节信息
下面程序缺点:常量可读性不高,必须使用注释
swith case 出现脏代码,Java编译器在编译期无法检查脏代码
【牛刀小试】
/**
* @author caojie
* 季节相关的接口:春夏秋冬
*/
public interface Season {
/**
* 春天
*/
public final static int SPRING=0;
/**
* 夏天
*/
public final static int SUMMER=1;
/**
* 秋天
*/
public final static int AUTUMN=2;
/**
* 冬天
*/
public final static int WINTER=3;
}
import java.util.Scanner;
/**
* @author caojie
*输入数字获取季节0123 春夏秋冬
*/
public class TestSeason {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("输入数字获取季节0123 春夏秋冬");
int season=input.nextInt();
if(season<0 || season>=4) {
System.out.println("输入错误,程序退出");
System.exit(-1);
}
switch (season) {
case Season.SPRING:
System.out.println("春天");
break;
case Season.SUMMER:
System.out.println("夏天");
break;
case Season.AUTUMN:
System.out.println("秋天");
break;
case Season.WINTER:
System.out.println("冬天");
break;
case '王':
break;
case -23456:
break;
case 12345678:
break;
}
}
}
如何解决上面的bug呢,使用枚举
【牛刀小试】
/**
* @author caojie
*定义枚举规范:使用E开头
*/
public enum ESeason {
// 编译错误:元素定义的是无参,但是构造方法是带3个参数的所以编译错误
// SPRING,
// SUMMER,
// AUTUMN,
// WINTER;
SPRING((byte)0,"春天","春暖花开"),
SUMMER((byte)1,"夏天","烈日炎炎"),
AUTUMN((byte)2,"秋天","秋风扫落叶"),
WINTER((byte)3,"冬天","白雪皑皑");
private byte id;
private String seasonName;
private String seasonDesc;
//枚举不支持公有构造方法
//public ESeason(){}
// private ESeason() {
//
// }
private ESeason(byte id, String seasonName,String seasonDesc) {
this.id=id;
this.seasonName = seasonName;
this.seasonDesc= seasonDesc;
}
public byte getId() {
return id;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
/**
* @author caojie
* 枚举常用方法
* 1 获取枚举元素
* 2 调用常用的方法
*/
public class TestEnum {
public static void main(String[] args) {
//秋天是季节 获取枚举中的元素,并且返回枚举类型
ESeason season = ESeason.AUTUMN;
//返回每个枚举元素对应的下标:秋天AUTUMN对应的下标是2
int ordinal = season.ordinal();
System.out.println(ordinal);
//name()方法返回元素名称:AUTUMN
String name=season.name();
System.out.println(name);
//返回枚举中所有的元素
ESeason seasons []= ESeason.values();
for (ESeason eSeason : seasons) {
//获取每个元素的成员方法
System.out.println("季节描述"+eSeason.getSeasonDesc());
}
}
}
使用枚举,完成季节的打印:
/**
* @author caojie
*定义枚举规范:使用E开头
*枚举中定义的构造方法参数,必须跟元素匹配
*通常枚举中定义私有构造,隐藏枚举的元素(不让外界直接访问),提供公有方法让外界访问枚举的属性
*一旦ESeason被加载到JVM,JVM就会初始化枚举的元素,然后调用元素对应的构造方法
*枚举中的元素不要超过64个否则,程序运行效率低下(2^6次方)
*名义上ESeason是枚举,实际上是类 ,因为每个没有都有自己独立的class文件
*/
public enum ESeason {
// 编译错误:元素定义的是无参,但是构造方法是带3个参数的所以编译错误
// SPRING,
// SUMMER,
// AUTUMN,
// WINTER;
SPRING((byte)0,"春天","春暖花开"),
SUMMER((byte)1,"夏天","烈日炎炎"),
AUTUMN((byte)2,"秋天","秋风扫落叶"),
WINTER((byte)3,"冬天","白雪皑皑");
private byte id;
private String seasonName;
private String seasonDesc;
//枚举不支持公有构造方法
private ESeason(byte id, String seasonName,String seasonDesc) {
this.id=id;
this.seasonName = seasonName;
this.seasonDesc= seasonDesc;
}
public byte getId() {
return id;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
/**
* 根据外界传入的参数,获取对应的枚举元素
* 1调用枚举的静态方法values()获取所有的枚举元素
* 2逐个遍历枚举中的元素
* 3判断参数index是否等于枚举元素的id
* 4如果条件成立,将对应的枚举元素返回给调用者
* @param index 外界传入的参数
* @return 返回的枚举元素
*/
public static ESeason from(int index) {
ESeason [] seasons =ESeason.values();
for(ESeason season : seasons) {
if(season.getId() == index) {
return season;
}
}
return null;
}
}
import java.util.Scanner;
public class TestSeason2 {
public static void main(String[] args) {
//使用枚举,解决常量中的BUG
//ESeason season = ESeason.WINTER;
Scanner input = new Scanner(System.in);
System.out.println("请输入数字获取对应的季节信息0春天1夏天2秋天3冬天");
ESeason season = ESeason.from(input.nextInt());
if(null ==season) {
System.out.println("输入错误,程序退出");
System.exit(-1);
}
switch (season) {
case SPRING:
System.out.println(season.getSeasonName()+"\t"+season.getSeasonDesc());
break;
case SUMMER:
System.out.println(season.getSeasonName()+"\t"+season.getSeasonDesc());
break;
case AUTUMN:
System.out.println(season.getSeasonName()+"\t"+season.getSeasonDesc());
break;
case WINTER:
System.out.println(season.getSeasonName()+"\t"+season.getSeasonDesc());
break;
//编译错误:case 后面只能跟ESeason定义的元素
// case 123:
// break;
}
}
}
10.4 枚举的高级部分
枚举的高级部分:在枚举中定义抽象方法 abstract
场景:输入数字,获取Tree,调用方法
/**
* @author caojie
* 所有Tree的父类
*/
public abstract class Tree {
/**
* 每个树都有生长的行为
*/
public abstract void grow();
}
public class AppleTree extends Tree {
@Override
public void grow() {
System.out.println("AppleTree grow.....苹果树生长");
}
}
public class BananaTree extends Tree {
@Override
public void grow() {
System.out.println("BananaTree grow.....香蕉树生长");
}
}
public class LemonTree extends Tree {
@Override
public void grow() {
System.out.println("LemonTree 柠檬树生长");
}
}
/**
* @author caojie
* Tree工厂,唯一职责是创建产品
*/
public class TreeFactory {
/**
* 根据外界传入的参数创建对应的Tree产品
* 1创建AppleTree
* 2创建BananaTree
* 3创建LemonTree
* @param index 外界传入的参数
* @return Tree
* 多态:程序在执行期判断对象类型,调用恰当的方法
* 父类定义做什么(定义抽象方法),具体如何做下沉到子类(实现父类的抽象方法)
* 做什么和怎么做分离
* 父类定义共性行为,每个子类的实现方式不同
*/
public static Tree createTree(int index) {
Tree tree =null;
switch (index) {
case 1:
tree= new AppleTree();
break;
case 2:
tree = new BananaTree();
break;
case 3:
tree = new LemonTree();
break;
}
return tree;
}
}
package com.whsxt.day4.enum2;
import java.util.Scanner;
/**
* @author caojie
* 园丁负责Tree的创建和种植
* 1 从控制台输入数字
* 2 根据输入的数字创建对象
* 3 调用Tree的grow()方法
*/
public class YuanDing {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入要创建的Tree,1AppleTree2BananaTree3LemonTree");
int index = input.nextInt();
Tree tree = TreeFactory.createTree(index);
tree.grow();
}
}
/**
* @author caojie
* 此时APPLE_TREE、BANANA_TREE、LEMON_TREE编译期是枚举中的元素,运行期匿名内部类 ETree$1,因为枚举定义了抽象方法,所以每个元素都要去实现
* 一旦枚举中定义了{}运行期一定是一个匿名内部类
*/
public enum ETree {
APPLE_TREE((byte)1) {
@Override
public Tree createTree() {
return new AppleTree();
}
},BANANA_TREE((byte)2) {
@Override
public Tree createTree() {
return new BananaTree();
}
},LEMON_TREE((byte)3) {
@Override
public Tree createTree() {
return new LemonTree();
}
};
private byte id;
private ETree(byte id) {
this.id=id;
}
public byte getId() {
return id;
}
/**
* 在枚举中定义抽象方法,所有的元素必须实现该抽象方法
* @return Tree
*/
public abstract Tree createTree();
/**
* 根据外界传入的参数获取对应的枚举元素
* @param index
* @return 枚举元素
*/
public static ETree from(int index) {
for(ETree tree : ETree.values()) {
if(index==tree.getId()) {
return tree;
}
}
return null;
}
}
import java.util.Scanner;
import org.junit.Test;
/**
* @author caojie
*1从控制台输入整数
*2根据输入的整数,从枚举中获取对应的Tree元素
*3创建Tree对象
*4调用Tree的成员方法grow()
*/
public class TestYuanDing {
@Test
public void testTree() {
Scanner input = new Scanner(System.in);
System.out.println("请输入要创建的Tree,1AppleTree2BananaTree3LemonTree");
int num = input.nextInt();
//etree 运行期表示枚举的元素
ETree etree =ETree.from(num);
if(null == etree) {
System.out.println("输入错误,程序退出");
//程序出现非正常情况退出
System.exit(-1);
}
//调用枚举的createTree()方法创建Tree对象
Tree tree =etree.createTree();
tree.grow();
}
}
10.5 枚举(enum)常用方法介绍
方法 | 注解 |
---|---|
int compareTo(E o) | 比较此枚举与指定对象的顺序。 |
Class getDeclaringClass() | 返回与此枚举常量的枚举类型相对应的 Class 对象。 |
String name() | 返回此枚举常量的名称,在其枚举声明中对其进行声明。 |
int ordinal() | 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。 |
String toString() | 返回枚举常量的名称,它包含在声明中。 |
static <T extends Enum> T valueOf(Class enumType, String name) | 返回带指定名称的指定枚举类型的枚举常量。 |
: ETree.values()) {
if(index==tree.getId()) {
return tree;
}
}
return null;
}
}
```java
import java.util.Scanner;
import org.junit.Test;
/**
* @author caojie
*1从控制台输入整数
*2根据输入的整数,从枚举中获取对应的Tree元素
*3创建Tree对象
*4调用Tree的成员方法grow()
*/
public class TestYuanDing {
@Test
public void testTree() {
Scanner input = new Scanner(System.in);
System.out.println("请输入要创建的Tree,1AppleTree2BananaTree3LemonTree");
int num = input.nextInt();
//etree 运行期表示枚举的元素
ETree etree =ETree.from(num);
if(null == etree) {
System.out.println("输入错误,程序退出");
//程序出现非正常情况退出
System.exit(-1);
}
//调用枚举的createTree()方法创建Tree对象
Tree tree =etree.createTree();
tree.grow();
}
}
10.5 枚举(enum)常用方法介绍
方法 | 注解 |
---|---|
int compareTo(E o) | 比较此枚举与指定对象的顺序。 |
Class getDeclaringClass() | 返回与此枚举常量的枚举类型相对应的 Class 对象。 |
String name() | 返回此枚举常量的名称,在其枚举声明中对其进行声明。 |
int ordinal() | 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。 |
String toString() | 返回枚举常量的名称,它包含在声明中。 |
static <T extends Enum> T valueOf(Class enumType, String name) | 返回带指定名称的指定枚举类型的枚举常量。 |