0.回顾
①线程 vs 进程 并发 vs 并行
②实现线程的方式 3种,以及run vs start、call vs run,实现Runnable和继承Thread的区别
③线程的状态,一张图
④线程安全【为什么会出现,怎么解决】 补充:拔高,RenentrantLock
的原理,以及默认是公平锁还非公平锁…
⑤线程通讯【原理–等待唤醒机制】,以及如何实现,wait()、notify/notifyall
都是Object的方法;Condition
await();
和signal()
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock(); try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally { lock.unlock(); }
}
public Object take() throws InterruptedException {
lock.lock(); try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally { lock.unlock(); }
}
}
⑥线程池【池化的好处?常用的线程池有哪几种?如何使用?ThreadPoolExecutor
,4个核心参数怎么理解【coreSize、maxSize、queue、hander—拒绝策略】?】 掌握线程池的工作流程【拔高】
⑦常用方法【join、yield】
⑧如何让线程停止?
⑨线程的分类?用户线程,守护线程【启动前,setDaemon(true)
】
1.System
java.lang.System
类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作。
由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便的进行调用。
成员变量:System类内部包含in、out和err三个成员变量,分别代表标准输入流(键盘输入),标准输出流(显示器)和标准错误输出流(显示器)。
在System类的API文档中,常用的方法有:
-
public static long currentTimeMillis()
:返回以毫秒为单位的当前时间。时间的表达格式为当前计算机时
间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。 -
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
:将数组中指定的数据拷贝到另一个数组中。 -
void exit(int status)
:该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表异常退出。 -
void gc()
:请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中垃圾回收算法的实现以及系统执行时的情况。 -
String getProperty(String key)
:该方法的作用是获得系统中属性名为key的属性对应的值。系统中常见
的属性名以及属性的作用如下表所示:
1.1 currentTimeMillis方法
实际上,currentTimeMillis方法就是 获取当前系统时间与1970年01月01日00:00点之间的毫秒差值
练习:验证for循环打印数字1-9999所需要使用的时间(毫秒)
1.2 arraycopy方法
我们在前学习Arrays
工具类的时候,用过copyOf()
系列方法,实际上,该方法最终调用的还是System.arraycopy
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
:将 数组中指定的数据拷贝到另一个数组中。 数组的拷贝动作是系统级的,性能很高。
System.arraycopy方法具有5个参数,含义分别为:
参数序号 | 参数名称 | 参数类型 | 参数含义 |
---|---|---|---|
1 | src | Object | 源数组 |
2 | srcPos | int | 源数组索引起始位置 |
3 | dest | Object | 目标数组 |
4 | destPos | int | 目标数组索引起始位置 |
5 | length | int | 复制元素个数 |
练习:将src数组中前3个元素,复制到dest数组的前3个位置上复制元素前:src数组元素[1,2,3,4,5],dest数组元素 [6,7,8,9,10]复制元素后:src数组元素[1,2,3,4,5],dest数组元素[1,2,3,9,10]
1.3 Properties getProperties()
后面在读取jdbc.properties后缀名的配置文件时会用到Properties,实际上就可以看成是HashMap
public class Properties extends Hashtable<Object,Object>
//获取到一堆系统信息
Properties properties = System.getProperties();
System.out.println(properties);
/*
有一个配置文件:jdbc.properties,讲jdbc时在用
username=zs
pwd=111111
*/
Properties prop = new Properties();
prop.getProperty("username");
2.时间相关
2.1 JDK8之前
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z5PrJuW1-1618449408817)(assets/image-20200616212045372.png)]
2.1.1 Date
需要掌握的点:古老且经典版本,使用的还是比较多的
获取日期:
Date date=new Date()
; 注意:util包下的将日期类型格式化为字符串:
//2.创建一个格式化类 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss S"); //3.对date格式化 **** sdf.format(date) String time = sdf.format(date);
将字符串类型格式化为Date:
Date date2 = sdf.parse("2019-11-12 11:04:43 21");
注意:SimpleDateFormat是一个线程不安全的类,若在多线程下使用,官方建议,每个线程创建一个单独的实例【在方法内部创建即可】,而不是作为共享资源。
2.1.2 Calendar
2.2.1 概念
java.util.Calendar
是日历类,在Date后出现,替换掉了许多Date的方法。该类将所有可能用到的时间信息封装 为静态成员变量,方便获取。日历类就是方便获取各个时间属性的。
2.2.2 获取方式
Calendar为抽象类,由于语言敏感性,Calendar类在创建对象时并非直接创建,而是通过静态方法创建,返回子 类对象,如下:
Calendar静态方法
public static Calendar getInstance()
:使用默认时区和语言环境获得一个日历
2.2.3 常用方法
根据Calendar类的API文档,常用方法有:
-
public int get(int field)
:返回给定日历字段的值,参数使用静态变量传值。 -
public void set(int field, int value)
:将给定的日历字段设置为给定值。 -
public abstract void add(int field, int amount)
:根据日历的规则,为给定的日历字段添加或减去指 定的时间量。 -
public Date getTime()
:返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象。
Calendar类中提供很多成员常量,代表给定的日历字段:
需要知道的几个点:
- 获取某个属性值的时候,需要使用这种方式:
calendar.get(Calendar.DAY_OF_MONTH)
- calendar实例的获取方式:Calendar.getInstance();
//1.获取实例
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getTime());
//获取指定的值,需要通过静态变量作为get的参数
System.out.println(calendar.get(Calendar.YEAR)+"-"+
calendar.get(Calendar.MONTH));
//传入一个指定的值,设置日期
calendar.set(2019,11,23);
System.out.println(calendar.get(Calendar.DATE));
2.2 【重点】JDK8操作日期
LocalDate
:2019-11-09LocalTime
:09:34:23.024LocalDateTime
:2019-11-09 09:34:23.024DateTimeFormatter
:指定pattern进行日期格式化
注意:上述4个类均是不可变的和线程安全的
其他:
ZonedDateTime
:带时区的时间Duration
:持续时间
需要掌握的几个方法:
-
获取时间:
LocalDate.now()
,其余两个一样 -
设置时间:
LocalDate.of(年、月、日)
,其余两个一样 -
对时间进行加/减:
某个实例对象.plus年/月/日等
,比如加2天localDate.plusDays(2)
减时间:minusDays(2)
-
获取年/月/日:
localDate.getYear()
,依次类推 -
计算时间间隔
- Duration:用于计算两个“时间”间隔
- Period:用于计算两个“日期”间隔
-
DateTimeFormatter
LocalDateTime ldt = LocalDateTime.now(); DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); String strDate2 = dtf2.format(ldt);
public class TestJdk8Time {
public static void main(String[] args) {
//直接获取到2019-12-27
LocalDate now = LocalDate.now();
//FRIDAY
System.out.println(now.getDayOfWeek());
//361
System.out.println(now.getDayOfYear());
System.out.println(now.toString());
System.out.println(now.getMonth());
System.out.println(now.getMonthValue());
System.out.println(now.getYear());
System.out.println(now.plusDays(2).minusMonths(1));
//2019-12-27T09:19:42.058
LocalDateTime time = LocalDateTime.now();
System.out.println(time);
LocalTime localTime = LocalTime.now();
//09:20:32.094
System.out.println(localTime);
//“时间"类型的间隔
Duration between = Duration.between(LocalTime.now(), LocalTime.now().minusSeconds(10));
System.out.println(between);
//“日期”类型的间隔,以年、月、日衡量
Period period = Period.between(LocalDate.now(), LocalDate.now().plusMonths(1).minusDays(2));
System.out.println(period);
}
}
3.String/StringBuilder/StringBuffer[重重点]
3.1 String
3.1.1 概述
java.lang.String
类代表字符串。Java程序中所有的字符串文字(例如"abc"
)都可以被看作是实现此类的实例。- String是一个final类,代表不可变的字符序列,体现在值的存储。
- 类
String
中包括用于检查各个字符串的方法,比如用于比较字符串,搜索字符串,提取子字符串以及创建具有翻译为大写或小写的所有字符的字符串的副本。
3.1.2 特点
底层是通过字符数组存放的,且该数组final修饰的:
private final char value[];
String是不可变的字符串,原因还是因为第1点
String str = "abc"
,是将值放入到常量池中[方法区的一个区域]
String str = new String("abc");
会创建几个对象?1个或2个
str2.intern()
:提分点当调用字符串str 的intern方法时,如果常量池中已经包含了一个字符串equalsstr,则直接从池中返回这个字符串的引用,否则,把字符串str添加到池中并且返回其引用。
当且仅当s.equals(t)返回true时, t, s.intern() == t.intern()才返回true。(注意jdk7及以上常量池中才开始存的地址引用,之前的都是直接把对象copy一份存到常量池,这点《深入理解java虚拟机》中有特别说明)从1.5开始,String的构造方法,还可以传入两个SB:
StringBuilder
和StringBuffer
-
==字符串不变:==字符串的值在创建后不能被更改。
String s1 = "abc"; s1 += "d"; System.out.println(s1); // "abcd" // 内存中有"abc","abcd"两个对象,s1从指向"abc",改变指向,指向了"abcd"。
-
因为String对象是不可变的,所以它们可以被共享。
String s1 = "abc"; String s2 = "abc"; // 内存中只有一个"abc"对象被创建,同时被s1和s2共享。
-
"abc"
等效于char[] data={ 'a' , 'b' , 'c' }
例如: String str = "abc"; 相当于: char data[] = {'a', 'b', 'c'}; String str = new String(data); // String底层是靠字符数组实现的。
3.1.3 使用步骤
-
查看类
java.lang.String
:此类不需要导入。 -
查看构造方法
public String()
:初始化新创建的 String对象,以使其表示空字符序列。public String(char[] value)
:通过当前参数中的字符数组来构造新的String。public String(byte[] bytes)
:通过使用平台的默认字符集解码当前参数中的字节数组来构造新的 String。- 构造举例
String str = "hello"; // 本质上this.value = new char[0]; String s1 = new String(); //this.value = original.value; String s2 = new String(String original); //this.value = Arrays.copyOf(value, value.length); String s3 = new String(char[] a); String s4 = new String(char[] a,int startIndex,int count); char[] ch = {'q','b'}; String str1 = new String(ch); //直接取ch数组的所有 String str2 = new String(ch,0,1); //指定起始位置和长度 byte[] but = {1,3,4,5}; String str3 = new String(but); String str4 = new String(but,0,2);
3.1.4 常用方法
3.1.4.1 判断功能的方法
public boolean equals (Object anObject)
:将此字符串与指定对象进行比较。public boolean equalsIgnoreCase (String anotherString)
:将此字符串与指定对象进行比较,忽略大小写。
3.1.4.2 获取功能的方法
public int length ()
:返回此字符串的长度。 跟数组的length属性要分区开public String concat (String str)
:将指定的字符串连接到该字符串的末尾。public char charAt (int index)
:返回指定索引处的 char值。public int indexOf (String str)
:返回指定子字符串第一次出现在该字符串内的索引。public String substring (int beginIndex)
:返回一个子字符串,从beginIndex开始截取字符串到字符 串结尾。public String substring (int beginIndex, int endIndex)
:返回一个子字符串,从beginIndex到 endIndex截取字符串。含beginIndex,不含endIndex。
3.1.4.3 转换功能的方法
public char[] toCharArray ()
:将此字符串转换为新的字符数组。public byte[] getBytes ()
:使用平台的默认字符集将该 String编码转换为新的字节数组。public String replace (CharSequence target, CharSequence replacement)
:将与target匹配的字符串使用replacement字符串替换。
3.1.4.4 分割功能的方法
public String[] split(String regex)
:将此字符串按照给定的regex(规则)拆分为字符串数组。
String str = "a春眠不觉晓,处处蚊子咬,也来吧掌声,不知死多少 ";
System.out.println("长度:"+str.length());
System.out.println("查找:"+str.indexOf('a'));
System.out.println("查找:"+str.indexOf(97));
System.out.println("查找:"+str.lastIndexOf("处"));
System.out.println(""+str.lastIndexOf(100));
System.out.println("去除空格:"+str.trim());
System.out.println("取子串:"+str.substring(3));
System.out.println("取子串:"+str.substring(3,7));
System.out.println("替换:"+str.replace("处处","**"));
System.out.println("找某个字符:"+str.charAt(10));
System.out.println("变为小写:"+"Abc".toLowerCase());
System.out.println("变为大写:"+"Abc".toUpperCase());
System.out.println("判断以xxx结尾:"+str.endsWith("fsdfd"));
System.out.println("判断以xxx结尾:"+str.startsWith("a"));
System.out.println("将基本类型变为字符串:"+String.valueOf(1));
System.out.println("===将字符串变为字符数组====");
char[] chars = str.toCharArray();
Stream<char[]> stream = Stream.of(chars);
System.out.println(chars.length);
//正则表达式:做更精准的校验
//构建一个Pattern对象
Pattern pattern = Pattern.compile(".*d.*");
Matcher matcher = pattern.matcher(str);
System.out.println(matcher.matches());
String str = "中";
System.out.println(str.getBytes("ISO8859-1").length);// -128~127
System.out.println(str.getBytes("GBK").length);
System.out.println(str.getBytes("UTF-8").length);
System.out.println(new String(str.getBytes("ISO8859-1"),
"ISO8859-1"));// 乱码,表示不了中文
System.out.println(new String(str.getBytes("GBK"), "GBK"));
System.out.println(new String(str.getBytes("UTF-8"), "UTF-8"));
3.1.5 其他
String concat(String str):字符串的拼接,习惯使用+
boolean contains(CharSequence s) :判断是否包含某个字符或子串
boolean endsWith(String suffix) :测试此字符串是否以指定的后缀结尾。比如验证文件后缀是否符合要求
boolean startsWith(String prefix) :测试此字符串是否以指定的前缀开头
boolean isEmpty() :判断长度是否为0
toLowerCase() /toUpperCase():将所有在此字符 String使用默认语言环境的规则,以小写/大写
static String valueOf(任意类型):将任意类型变为字符串,比如会用到的将 基本类型变为字符串
3.1.5 练习
3.1.5.1 拼接字符串
需求:定义一个方法,把数组{1,2,3}按照指定个格式拼接成一个字符串。格式参照如下:String [word1#word2#word3]。concat()或+
3.1.5.2 统计字符个数
需求:键盘录入一个字符串,统计字符串中大小写字母及数字字符个数,若拿到其他字符则提示:“该字符”+除了上述之外其他的字符+“非法” ,toCharArray
3.1.5.3 处理身份证
输入一个身份证号,判断长度是否合法18位【搞个6位测试】,出生年份,必须在[1910,2028]范围之间,将出生的日期0923替换成****
,否则给出对应的提示信息 ,先打印出替换后的,再逆序输出原串
提示:replace、subString、Integer.parseInt(“1910”) —> 1910
3.1.6 内存分析
3.1.7 使用陷进
- String s1 = “a”:在字符串常量池中创建了一个字面量为"a"的字符串。
- s1 = s1 + “b”:实际上原来的“a”字符串对象已经丢弃了,现在在堆空间中产生了一个字符串s1+“b”(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。
- String s2 = “ab”:直接在字符串常量池中创建一个字面量为"ab"的字符串。
- String s3 = “a” + “b”:s3指向字符串常量池中已经创建的"ab"的字符串。
- String s4 = s1.intern():堆空间的s1对象在调用intern()之后,会将常量池中已经存在的"ab"字符串赋值给s4。
【面试题】下列程序的运行结果是什么
public class StringTest {
String str = new String("good");
char[] ch = { 't', 'e', 's', 't' };
public void change(String str, char ch[]) {
str = "test ok";
ch[0] = 'b';
}
public static void main(String[] args) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch);
System.out.print(ex.str + " and ");//
System.out.println(ex.ch);
}
}
3.2 StringBuilder
3.2.1 字符串拼接问题
- 由于String类的对象内容不可改变,所以每当进行字符串拼接时,总是会在内存中创建一个新的对象
- 在API中对String类有这样的描述:字符串是常量,它们的值在创建后不能被更改。
public class StringDemo {
public static void main(String[] args) {
String s = "Hello";
s += "World";
System.out.println(s);
}
}
思考?上述代码共创建了几个字符串
如果对字符串进行拼接操作,每次拼接,都会构建一个新的String对象,既耗时,又浪费空间。为了解决这一问题,可以使用
java.lang.StringBuilder
类
3.2.2 sb概述
查阅java.lang.StringBuilder
的API,StringBuilder又称为可变字符序列,它是一个类似于 String 的字符串缓冲 区,通过某些方法调用可以改变该序列的长度和内容。
它是JDK1.5之后出来的,底层也是用char[] value
,只不过没有加final
修饰
原来StringBuilder是个字符串的缓冲区,即它是一个容器,容器中可以装很多字符串。并且能够对其中的字符串进行各种操作。
它的内部拥有一个数组用来存放字符串内容,进行字符串拼接时,直接在数组中加入新内容。StringBuilder会自动 维护数组的扩容。原理如下图所示:(默认16字符空间,超过自动扩充) 参见源码
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
3.2.3 构造方法
根据StringBuilder的API文档,常用构造方法有2个:
public StringBuilder()
:构造一个空的StringBuilder容器。public StringBuilder(String str)
:构造一个StringBuilder容器,并将字符串添加进去。
3.2.4 常用方法
StringBuilder常用的方法有2个:
public StringBuilder append(...)
:添加任意类型数据的字符串形式,并返回当前对象自身。public String toString()
:将当前StringBuilder对象转换为String对象。
备注:
- StringBuilder已经覆盖重写了Object当中的toString方法
- StringBuilder支持链式调用【调用完append()返回值还是sb,所以支持链式调用】
3.2.5 代码演示
StringBuilder sb = new StringBuilder();
//sb的一些常用方法
//1.向后追加
sb.append("abc")
.append("bfsd");
System.out.println(sb.toString());
//2.反转
//System.out.println(sb.reverse());
//3.替换
//System.out.println(sb.replace(1, 3, "e"));
//4.insert
System.out.println(sb.insert(1, 100));
}
3.3 面试题【高频】
- String相关
String str1 = "abc";
String str3 = new String("abc");
//上述分别创建几个对象
- String、StringBuilder、StringBuffer的区别
①String是不可变字符串,内部实际存储为char数组
延伸:
- 为什么String类型要用final修饰
- ==和equals的区别
- String的intern()方法有什么含义
- String类型在JVM中是如何存储的?编译器对String做了哪些优化
1.==和equals的区别 略
2.final修饰的好处
①缓存结果【高效,字符串常量池】:当你在传参时不需要考虑谁会修改它的值;如果是可变类的话,则有可能需要重新拷贝出来一个新值进行传参,这样在性能上就会有一定的损失
②安全:当你在调用其他方法时,比如调用一些系统级操作指令之前,可能会有一系列校验,如果是可变类的话,可能在你校验过后,它的内部的值又被改变了,这样有可能会引起严重的系统奔溃问题,这是迫使String类设计成不可变类的一个重要原因
只有字符串是不可变时,我们才能实现字符串常量池,字符串常量池可以为我们缓存字符串,提高程序的运行效率
3.intern()
当调用字符串str 的intern方法时,如果常量池中已经包含了一个字符串equalsstr,则直接从池中返回这个字符串的引用,否则,把字符串str添加到池中并且返回其引用。
当且仅当s.equals(t)返回true时, t, s.intern() == t.intern()才返回true。(注意jdk7及以上常量池中才开始存的地址引用,之前的都是直接把对象copy一份存到常量池,这点《深入理解java虚拟机》中有特别说明)
4.String和JVM
String 常见的创建方式有两种,new String() 的方式和直接赋值的方式,直接赋值的方式会先去字符串常量池中查找是否已经有此值,如果有则把引用地址直接指向此值,否则会先在常量池中创建,然后再把引用指向此值;而 new String() 的方式一定会先在堆上创建一个字符串对象,然后再去常量池中查询此字符串的值是否已经存在,如果不存在会先在常量池中创建此字符串,然后把引用的值指向此字符串,如下代码所示:
String s1 = new String("Java");
String s2 = s1.intern();
String s3 = "Java";
System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true
小贴士:JDK 1.7 之后把永久代换成的元空间,把字符串常量池从方法区移到了 Java 堆上。
②两个sb都是可变字符串
③区别:StringBuffer线程安全,性能上略于StringBuilder,所以在非并发操作的环境下可使用StringBuilder来进行字符串拼接。
StringBuffer
不管方法是否涉及到写操作,都加了synchronized
https://blog.csdn.net/yrwan95/article/details/81212714
//初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));
【面试】程序输出
String str = null;
StringBuffer sb = new StringBuffer();
sb.append(str);
System.out.println(sb.length());//
System.out.println(sb);//
StringBuffer sb1 = new StringBuffer(str);
System.out.println(sb1);//
4.Math/Random
4.1 Math类
在JDK8中,提供了一些+1,-1,两数相加等方法,目的若超过范围,抛出一个数字over flow异常;而不是之前的返回一个错误的值。
System.out.println(Math.addExact(10, 20)); System.out.println(Math.incrementExact(10)); System.out.println(Math.decrementExact(12)); //Exception in thread "main" java.lang.ArithmeticException: integer overflow System.out.println(Math.toIntExact(Long.MAX_VALUE)); //System.out.println(Integer.MAX_VALUE + 1);
4.1.1 概述
java.lang.Math
类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。类似这样的工具 类,其所有方法均为静态方法,并且不会创建对象,调用起来非常简单。
4.1.2 基本运算的方法
public static double abs(double a)
:返回 double 值的绝对值。public static double ceil(double a)
:返回大于等于参数的最小的整数。public static double floor(double a)
:返回小于等于参数最大的整数。public static long/int round(double a)
:返回最接近参数的 long/int。(相当于四舍五入方法)
4.1.3 练习
需求:使用 Math
相关的API,计算在 -10.8 到 5.9 之间,绝对值大于 6 或者小于 2.1 的整数有多少个?
4.2 Random类
4.2.1 概述
此类的实例用于生成伪随机数。
4.2.2 使用步骤
查看类
java.util.Random
:该类需要 import导入使后使用。
查看构造方法
public Random()
:创建一个新的随机数生成器。
查看成员方法
public int nextInt(int n)
:返回一个伪随机数,范围在 0 (包括)和 指定值 n (不包括)之间的 int 值,且必须是一个正数,否则抛出异常:Exception in thread "main" java.lang.IllegalArgumentException: bound must be positive
。 使需求:用Random类,生成3个10以内的随机整数
Random r = new Random(种子),r.nextInt(4); 每次生成的都是同一个值,一般不传这个种子
r.nextInt(4),该随机数的范围[0,3),不包含上界
Random random = new Random();
//传一个上线 正数,不包括bound(10) [0,bound)
System.out.println(random.nextInt(10));
4.2.3 练习
-
获取随机数
需求:获取1-n之间的随机数,包含n(从键盘输入)
-
猜数字小游戏
游戏开始时,会随机生成一个1-100之间的整数 number 。玩家猜测一个数字 guessNumber ,会与 number 作比较,系统提示大了或者小了,直到玩家猜中,游戏结束。
5.BigInteger与BigDecimal
5.1 BigInteger
5.1.1 概述
- Integer类作为int的包装类,能存储的最大整型值为231 -1,Long类也是有限的,最大为263 -1。如果要表示再大的整数,不管是基本数据类型还是他们的包装类都无能为力,更不用说进行运算了。
- java.math包的BigInteger 可以表示不可变的任意精度的整数。BigInteger 提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、位操作以及一些其他操作。
- 构造器
BigInteger(String val):根据字符串构建BigInteger对象
5.1.2 常用方法
- public BigInteger abs():返回此 BigInteger 的绝对值的 BigInteger。
- BigInteger add(BigInteger val) :返回其值为 (this + val) 的 BigInteger
- BigInteger subtract(BigInteger val) :返回其值为 (this - val) 的 BigInteger
- BigInteger multiply(BigInteger val) :返回其值为 (this * val) 的 BigInteger
- BigInteger divide(BigInteger val) :返回其值为 (this / val) 的 BigInteger。整数相除只保留整数部分。
- BigInteger remainder(BigInteger val) :返回其值为 (this % val) 的 BigInteger。
- BigInteger[] divideAndRemainder(BigInteger val):返回包含 (this / val) 后跟(this % val) 的两个 BigInteger 的数组。
- BigInteger pow(int exponent) :返回其值为 (this exponent ) 的 BigInteger。
5.2 使用
5.2.1 概述
-
一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中,到要求数字精度比较高,故用到java.math.BigDecimal类 。
-
BigDecimal类支持不可变的、任意精度的有符号十进制定点数。
-
构造器
- public BigDecimal(double val)
- public BigDecimal(String val)
5.2.2 常用方法
- public BigDecimal add(BigDecimal augend)
- public BigDecimal subtract(BigDecimal subtrahend)
- public BigDecimal multiply(BigDecimal multiplicand)
- public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
public void testBigInteger() {
BigInteger bi = new BigInteger("12433241123");
BigDecimal bd = new BigDecimal("12435.351");
BigDecimal bd2 = new BigDecimal("11");
System.out.println(bi);
// System.out.println(bd.divide(bd2));
System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP));
System.out.println(bd.divide(bd2, 15, BigDecimal.ROUND_HALF_UP));
}
6.包装类
如何获取数值的最大值
Integer.MAX_VALUE
6.1 概述
Java提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而很多情况,会创建对象使用,因为 对象可以做更多的功能,如果想要我们的基本类型像对象一样操作,就可以使用基本类型对应的包装类,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xn3o1NcQ-1618449408861)(assets/1573091732894.png)]
6.2 装箱和拆箱
装箱:将基本类型直接复制给引用类型
拆箱:将引用类型变为基本类型
6.3 自动装箱与自动拆箱
由于我们经常要做基本类型与包装类之间的转换,从**Java 5(JDK 1.5)**开始,基本类型与包装类的装箱、拆箱动作 可以自动完成。
1.5的新特性:增强for、可变参数【必须放在参数的最后…】、自动拆装箱、枚举
6.4 基本类型与字符串之间的转换
-
字符串—>基本数据类型
以整数为例:
Integer.parseInt("123")
,其他依次类推,parseXxx()
String str = "123"; int abc = Integer.parseInt(str);
-
字符串—>包装类型
valueOf
Integer aa = Integer.valueOf("1123");
-
基本类型—>字符串
int age = 10; String ageStr = a.toString(); String ageStr2 = Integer.toString(age);
-
不同数字之间的类型转换:
xxxValue
Double i = 1.0; int i1 = i.intValue();
6.5 备注【了解】
-
Character在构建对象的时候,只有一种构造方法,而Integer有两种
Integer score = new Integer(10); Integer score2 = new Integer("10"); Character c = new Character('c');
-
Boolean在构建对象的时候,传字符串作为参数的时候,不区分大小写
Boolean bo = new Boolean(Boolean.TRUE); Boolean bo2 = new Boolean("True"); Boolean bo3 = new Boolean("true");
6.6 大数据类型
对于更精确的数据需求,使用BigDecimal进行处理
BigDecimal bd = new BigDecimal(10);
BigDecimal bd2 = new BigDecimal(3);
//scale:小数点后面几位
//Rounding mode:数字的处理方式,向上、向下取
System.out.println(bd.divide(bd2,10,BigDecimal.ROUND_CEILING));
System.out.println(new BigDecimal(Double.MAX_VALUE).multiply(new BigDecimal(Double.MAX_VALUE)));
7.枚举类
- 会获取枚举值
- 对于多参的枚举类型,会定义,能看懂
- 多个枚举值之间使用,分割
7.1 为什么用枚举
- 在使用有限的值的时候,对于值的合法性校验,能否将其在运行时提前到编译时呢?我们可以规定类型,只能取我们规定的类型。
- 在使用枚举的时候,枚举值不易过多
7.2 如何使用
通过关键字enum
进行定义,需要掌握的知识点如下:
通过javap -v Gender.class
查看字节码时发现,我们的枚举值,给我们加上了public static final test06.Gender MALE
-
会定义枚举类
public enum Gender { MALE,FEMALE,THIRDGENDER }
-
会使用枚举值
Gender.FEMALE; //该表达式的返回值类型依然是Gender
-
能够取出所有的枚举值
Gender[] values = Gender.values(); for (Gender value : values) { System.out.println(value.name()); }
-
根据名称取出枚举的值
Gender.valueOf("MALE").name()
7.3 扩展
枚举的高级用法,枚举中构造方法默认private
,若想使用带参数的枚举值,此时我们需要自定义构造方法。若系统参数很多,此时枚举就不太合适,用类去处理
//比如Boolean中获取值
public static final Boolean TRUE = new Boolean(true);
public enum Gender {
MALE("系统出错了。。。。。。",400),FEMALE,THIRDGENDER;
private String name;
private int code;
Gender(String name,int code){
this.name = name;
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
Gender(){}
}
如何使用:
System.out.println(Gender.MALE.getName());
System.out.println(Gender.MALE.getCode());
7.4 在switch中使用枚举
switch (Gender.FEMALE) {
case MALE:
break;
}
练习:定义一个枚举类,里面写3个枚举值,RED、YELLOW、GREEN,在switch使用枚举,打印一句话
7.5 枚举式单例
建议写,天生是线程安全的,可以避免反射的暴力破解
//构造方法私有化,枚举中已经默认私有化
//提供一个INSTANCE 就是 public static final 不能改变
enum EnumSingleTon{
INSTANCE;
public static EnumSingleTon getInstance() {
return INSTANCE;
}
}
8.File类
8.1 概述
java.io.File
类是文件和目录路径名的抽象表示,与平台无关,主要用于文件和目录的创建、查找和删除等操作。
8.2 构造方法
public File(String pathname)
:通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。一般使用这个较多public File(String parent, String child)
:从父路径名字符串和子路径名字符串创建新的 File实例。public File(File parent, String child)
:从父抽象路径名和子路径名字符串创建新的 File实例。
小贴士:
- 一个File对象代表硬盘中实际存在的一个文件或者目录。
- 无论该路径下是否存在文件或者目录,都不影响File对象的创建。
8.3 常用方法
8.3.1 获取功能的方法
public String getAbsolutePath()
:返回此File的绝对路径名字符串。public String getPath()
:将此File转换为路径名字符串。public String getName()
:返回由此File表示的文件或目录的名称。public long length()
:返回由此File表示的文件的长度。
API中说明:length(),表示文件的长度。但是File对象表示目录,则返回值未指定。
8.3.2 绝对路径和相对路径
- 绝对路径:从盘符开始的路径,这是一个完整的路径。
- 相对路径:相对于项目目录的路径,这是一个便捷的路径,开发中经常使用。
-
若使用的绝对路径,那么file.getAbsolutePath()和file.getPath()返回的都是绝对路径;
若使用的相对路径(相对当前项目),那么file.getAbsolutePath()和file.getPath()返回的有区别
8.3.3 判断功能的方法
public boolean exists()
:此File表示的文件或目录是否实际存在。
public boolean isDirectory()
:此File表示的是否为目录。
public boolean isFile()
:此File表示的是否为文件。
8.3.4 创建删除功能的方法
public boolean createNewFile()
:当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。public boolean delete()
:删除由此File表示的文件或目录。public boolean mkdir()
:创建由此File表示的目录。public boolean mkdirs()
:创建由此File表示的目录,包括任何必需但不存在的父目录。
API中说明:
- delete方法,如果此File表示目录,则目录必须为空才能删除。
- 如果你创建文件或者 文件 目录没有 写 盘符路径 , 那么 , 默认在项目路径下 。
8.4 目录的遍历
-
public String[] list()
:返回一个String数组,表示该File目录中的所有子文件或目录。 -
public File[] listFiles()
:返回一个File数组,表示该File目录中的所有的子文件或目录。
小贴士: 调用listFiles方法的File对象,表示的必须是实际存在的目录,否则返回null,无法进行遍历。
8.5 示例代码
public class Demo01 {
@Test
public void testGetFileInfo() {
File file = new File("aa");
//getFileInfo(file);
//rmFile(file);
listFiles(file);
}
@Test
public void testCreateFile() throws IOException {
File file = new File("d:\\aa\\bb\\cc");
createFile(file);
}
/**
* 创建目录或文件
* @param file
*/
public void createFile(File file) throws IOException {
//判断文件或目录是否存在
if(!file.exists()){
//file.mkdir(); //创建单级目录
file.mkdirs(); //创建多级目录
//file.createNewFile(); //创建文件
System.out.println("目录/文件创建成功...");
}else {
System.out.println("目录已存在");
}
}
public void getFileInfo(File file) {
if(file.exists()) {
System.out.println("绝对路径:"+file.getAbsolutePath());
System.out.println("文件名称:"+file.getName());
System.out.println("文件大小:"+file.length());
System.out.println("最后修改时间:"+file.lastModified());
System.out.println("是否是目录:"+file.isDirectory());
System.out.println("获取父路径"+file.getParent());
}else {
System.out.println("绝对路径:"+file.getAbsolutePath());
System.out.println("文件名称:"+file.getName());
System.out.println("文件大小:"+file.length());
System.out.println("最后修改时间:"+file.lastModified());
System.out.println("是否是目录:"+file.isDirectory());
System.out.println("获取父路径"+file.getParent());
System.out.println("文件不存在");
}
}
/**
* 删除文件或目录 linux中如何删除 rm -rf
* @param file
*/
public void rmFile(File file) {
if(file.exists()) {
//对于目录而言,这个方法只能删除空目录
file.delete();
}
}
public void listFiles(File file) {
if(file.exists()) {
//获取当前目录下的所有,包括文件和目录
File[] files = file.listFiles();
for (File file1 : files) {
if(file1.isDirectory()) {
System.out.println("目录名称:"+file1.getName());
//若是目录再去遍历,递归
listFiles(file1.getAbsoluteFile());
// System.out.println(file1);
} else {
//文件
System.out.println(file1.getName()+"\t"+file1.getParent());
}
}
} else {
System.out.println("文件或目录不存在....");
}
}
}
9.单元测试
便于我们对某个方法的单独测试,功效相当于main方法,不过在一个类中main方法只能有一个,而使用@Test
标识的方法,可以有多个。单元测试方法的要求:
@Test
public void test02() {
System.out.println(2222);
}
注意:
- 修饰符必须为public
- 只能使用void
- 方法名可以自己起,但是不能有参数
如何导入单元测试是包:
写完方法之后,加入@Test注解,然后alt+enter
,两次回车,搞定
10.练习
- 过滤敏感字符
输入一个身份证号,判断长度是否合法18位【搞个6位测试】,出生年份,必须在[1910,2028]范围之间,将出生的日期0923替换成****
,否则给出对应的提示信息 ,先打印出替换后的,再逆序输出原串
提示:replace、subString、Integer.parseInt(“1910”) —> 1910
- 拼接字符串
需求:定义一个方法,把数组{1,2,3}按照指定个格式拼接成一个字符串。格式参照如下:String [word1#word2#word3]。concat()或+
-
统计字符个数
需求:键盘录入一个字符串,统计字符串中大小写字母及数字字符个数,若拿到其他字符则提示:“该字符”+除了上述之外其他的字符+“非法” ,toCharArray
-
模拟一个trim 方法,去除字符串两端的空格
-
将一个字符串进行反转。将字符串中指定部分进行 反转如 。比如“abcdefg”反转为”abfedcg”
-
获取一个字符串在另一个字符串中出现的次数。比如:获取“ ab” 在 “abkkcadkabkebfkabkskab” 中出现的次数
-
获取两个字符串中最大相同子串。比如:str1 = "abcwerthelloyuiodef“;str2 = “cvhellobnm”
提示:将短的那个串进行长度依次递减的子串与较长的串比较。 -
使用StringBuffer/StringBuilder实现,
99999999999 =====> 999,999,999,99
-
整个File的方法练习
-
数据库要保证可用,MySQL自己安装
-
搜索 D:\aaa 目录中的 .java 文件。
分析:- 目录搜索,无法判断多少级目录,所以使用递归,遍历所有目录。
- 遍历目录时,获取的子文件,通过文件名称,判断是否符合条件。