一、String
1. String 封装字符串数据
1)直接给对象赋值字符串,例:
String name = "蔡徐坤"
2)利用 String 类的构造器
public String( ) ---- 创建一个空白字符串对象,不含有任何内容
public String(String original) ---- 根据传入的字符串内容,来创建字符串对象
public String(char[] chars) ---- 根据字符数组的内容,来创建字符串对象
public String(byte[] bytes) ---- 根据字节数组的内容(ASCII转换),来创建字符串对象
2. String 的常用方法
public class Test {
public static void main(String[] args) {
String s1 = "abcdefg";
// 1.获取字符串的长度
System.out.println(s1.length());
// 2.提取对应索引位置的字符(0开始)
System.out.println(s1.charAt(2));
// 字符串的遍历
for (int i = 0; i < s1.length(); i++)
{
char ch = s1.charAt(i);
System.out.println(ch);
}
// 3.把字符串的所有字符提取到一个char数组中
char[] chars = s1.toCharArray();
for (int i = 0; i < s1.length(); i++)
{
System.out.println(chars[i]);
}
// 4.比较字符串内容
String s2 = new String("abcdefg");
System.out.println(s1 == s2); // false,因为字符串直接比较地址
System.out.println(s1.equals(s2)); // true
// 5.忽略大小写比较字符串内容(常用于验证码比较)
String s3 = "ABCDEFG";
System.out.println(s1.equalsIgnoreCase(s3));
// 6.截取字符串的部分内容
String c1 = s1.substring(2);
String c2 = s1.substring(1,4);
System.out.println(c1);
System.out.println(c2);
// 7.替换字符串的部分内容
String s4 = "fzc是一个臭傻逼";
System.out.println(s4.replace("臭傻逼", "***"));
// 8.判断字符串是否包含某个内容
System.out.println(s1.contains("abc")); // true
System.out.println(s1.contains("ABC")); // false,精准判断
// 9.判断字符串是否以某内容开头
System.out.println(s1.startsWith("abc")); // true
// 10.把字符串以指定内容分割(返回字符串数组)
String c3 = "A,B,C,D";
String[] letters = c3.split(",");
for (int i = 0; i < letters.length; i++)
{
System.out.println(letters[i]);
}
}
}
3. String 的注意事项
1)String 创建的字符串对象不可变(不可直接赋值修改)
补充:"+"连接改变的,实质上是产生了一个新字符串,通过改变变量的地址来指向新字符串对象
2)以"..."方式写出的字符串对象,会在堆内存中的字符串常量池中存储,而且相同内容的字符串只存储一份
3)以 new 方式创建字符串对象,每一次都是在堆内存开辟新空间存储
4)字符串内容比较时尽量使用 equals("=="可能会出bug)
4. String 的案例(面试题)
1)请问下面的代码需要开辟几个 String 大小的空间
String s1 = new String("abc");
答:1个或2个。如果字面量"abc"没有存储到字符串常量池中,会先将"abc"存储到常量池后,再在堆内存中开辟新空间来存储变量所指向的字符串对象。
2)在上一问的代码基础上,请问下面的代码需要开辟几个 String 大小的空间
String s2 = "abc";
答:0个。在上一问的代码基础上,常量池中已经存储了"abc",所以不需要开辟新空间
3)判断打印结果
public class Test {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "ab";
String s3 = s2 + "c";
System. out.println(s1 == s3);
}
}
答:false,在堆中调用常量池中的"ab"创建了一个新的"abc"对象
4)判断打印结果
public class Test {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "a" + "b" + "c";
System. out.println(s1 == s2);
}
}
答:true,Java程序存在编译优化机制,"a" + "b" + "c"自动优化为"abc"
二、ArrayList(集合)
1.集合
一种数据容器,类似于数组,ArrayList 是其中的一种
public class Test {
public static void main(String[] args) {
// 1.创建一个ArrayList对象
ArrayList list1 = new ArrayList();
// 2.在集合末尾添加数据
list1.add(666);
list1.add(666);
list1.add(98.5);
list1.add("cxk");
// 3.创建一个约束类型的ArrayList对象
ArrayList<String> list2 = new ArrayList<>();
//list2.add(98.5); // 类型不符会报错
list2.add("cxk");
// 4.在指定索引位置上添加元素
list1.add(1,123);
// 5.获取指定索引位置的元素
System.out.println(list1.get(1));
// 6.获取集合全部元素的个数
System.out.println(list1.size());
// 7.删除指定索引位置的元素,并返回被删除的元素
System.out.println(list1.remove(1));
// 8.删除指定内容的元素,成功返回true,反之返回false
// 补充:如果集合内有重复元素,只删除第一个元素
System.out.println(list1.remove("cxk"));
// 9.修改指定索引位置的元素,返回修改前的元素
System.out.println(list1.set(1,"abc"));
System.out.println(list1);
}
}
三、Object 类
在 java.lang 包下,是所有 class 的超类 -- 所有类都是 Object 类的子孙类
1. toString 方法
返回对象的字符串表示形式,常用于重写后返回对象的内容
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
public class Test{
public static void main(String[] args) {
Student s = new Student("cxk");
System.out.println(s);
}
}
2. equals 方法
默认是比较两个对象的地址是否相等,可以重写后比较对象的内容,返回比较的结果(boolean)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name);
}
3. clone 方法
当某个对象调用这个方法时,会复制一个一样的对象返回
public class Student implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test{
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student("cxk");
Student s2 = (Student) s1.clone();
System.out.println(s1.equals(s2));
}
}
注意:1)clone 方法是 protected 范围下,所以需要在类中重写才能使用
2)Cloneable 接口中没有成员,是一个标记接口,使JVM能够识别此类可以用 clone 方法
3)throws CloneNotSupportedException 是为了抛出异常,编译器会认为 clone 方法直接调用可能不安全
4)clone 所拷贝的对象为浅拷贝
补充:浅拷贝和深拷贝
四、Objects 类
Objects 类不同于 Object 类,在 java.util 包下,是一个工具类(不能被继承),提供一些静态方法
1. equals 方法
先做地址判断,再非空判断,最后比较内容
2. isNull、nonNull 方法
判断该对象是否为 null
五、包装类
1.认识包装类
将基本数据类型的数据包装为对象,实现万物皆对象
2.包装类的使用
1)自动装箱:将基本数据类型自动转换为包装类型
Integer a = 12;
2)自动拆箱:将包装类型自动转换为基本数据类型
int b = a;
3)泛型:不支持基本数据类型,但支持包装类型
//ArrayList<int> list = new ArrayList<>();
ArrayList<Integer> list = new ArrayList<>();
int a = 10;
list.add(a);
int b = list.get(0);
3.包装类的常见操作
1)把基本数据类型转换为字符串类型(两种方法)
Integer a = 12;
String s1 = Integer.toString(a);
System.out.println(s1+3);//"123"
String s2 = a.toString();
System.out.println(s2+3);//"123"
2)把字符串类型转换成对应的基本数据类型(两种方法)
注意:字符串类型的数值一定要是正确的数据类型,否则会出错
String s1 = "12";
int a1 = Integer.parseInt(s1);
System.out.println(a1+3);// 15
int b1 = Integer.valueOf(s1);
System.out.println(b1+3);// 15
String s2 = "99.5";
double a2 = Double.parseDouble(s2);
System.out.println(a2+0.5);// 100.0
double b2 = Double.valueOf(s2);
System.out.println(b2+0.5);// 100.0
建议使用 valueOf 方法,这样便于维护和修改
六、StringBuilder、StringBuffer 和 StringJoiner
1. StringBulider
代表可变字符串对象,相当于是一个容器,它里面装的字符串是可以改变的,用来操作字符串
好处:StringBuilder 比 String 更适合做字符串的修改操作,效率会更高,代码也会更简洁。
1)调用构造器
StringBuilder s1 = new StringBuilder();
StringBuilder s2 = new StringBuilder("abc");
无参构造器创建的对象是一个不包含任何字符,默认16个字符大小的容器
而有参构造器创建的对象是指定字符串内容,或指定字符串大小的容器
2)append 方法
append 方法可以接收任意数据类型,如 int,double,String,boolean,数组等
StringBuilder s1 = new StringBuilder();
s1.append(111);
s1.append("acb");
s1.append(99.2);
// 支持链式编程
s1.append(true).append(666);
注意:append 方法执行后会返回该对象,所以可以支持链式编程,递归思想等
3)reverse 和 length 方法
reverse:字符串反转
length:返回字符串长度
s1.reverse();
int len = s1.length();
4)toString 方法
将 StringBuilder 类型转换为 String 类型
String rs = s1.toString();
2. StringBuffer
用法:和上面的 StringBuilder 基本一致
作用:StringBuilder 是线程不安全的,而 StringBuffer 是线程安全的
3. StringJoiner
JDK8 开始才有的,跟 StringBuilder 一样,也是用来操作字符串的,也可以看成是一个容器,创建之后里面的内容是可变的。
好处:不仅能提高字符串的操作效率,并且在有些场景下使用它操作字符串,代码会更简洁
public class Test{
public static void main(String[] args) {
StringJoiner s = new StringJoiner(",");
s.add("1").add("2").add("3");
System.out.println(s);
}
}
最后结果为:1,2,3,它在创建对象时定义了一个分隔符,每次 add 操作后都会添加分隔符(最后一次不会)
public class Test{
public static void main(String[] args) {
StringJoiner s = new StringJoiner(",","[","]");
s.add("1").add("2").add("3");
System.out.println(s);
}
}
最后结果为:[1,2,3],定义分隔符的同时也可以添加开始符和结束符,创建对象后即存在
注意:StringJoiner 的 add 操作只能传输字符串类型
七、Math、System、Runtime、BigDecimal
1. Math
代表数学,是一个工具类,里面提供的都是对数据进行操作的一些静态方法。
public class Test{
public static void main(String[] args) {
System.out.println(Math.abs(-12)); // 12
System.out.println(Math.abs(-3.14));// 3.14
System.out.println(Math.ceil(3.00001));// 4.0
System.out.println(Math.floor(2.9999));// 2.0
System.out.println(Math.round(3.4999));// 3
System.out.println(Math.round(3.5000));// 4
System.out.println(Math.max(1,2)); // 2
System.out.println(Math.max(1.2,1.3));// 1.3
System.out.println(Math.pow(2,3)); // 2的3次方 8.0
System.out.println(Math.pow(1.2,1.1));// 1.2的1.1次方 1.222079251376429
System.out.println(Math.random()); // 范围:[0.0,1.0)
}
}
2. System
代表程序所在的系统,也是一个工具类。
1)exit 方法(一般不要使用)
public class Test{
public static void main(String[] args) {
System.out.println(1);
System.exit(0); // 正常结束进程以0为退出参数
System.out.println(2);
}
}
结果:只打印了1,因为 exit 手动结束了程序,不会在往后运行
注意:非0的状态代码表示异常终止,0为正常终止
2)currentTimeMillis 方法
返回从1970-01-01 00:00:00 开始计算,到当前时间的毫秒(ms)值
常用于计算程序运行时间
public class Test{
public static void main(String[] args) {
long time1 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
System.out.println("当前数字为:"+i);
}
long time2 = System.currentTimeMillis();
System.out.println((time2-time1)/1000.0+"s");
}
}
3. Runtime
Runtime是一个单例类,代表程序所在的运行环境。
public class Test{
public static void main(String[] args) throws IOException, InterruptedException {
Runtime r = Runtime.getRuntime();
// 我强大的电脑是20核处理器
System.out.println(r.availableProcessors());
// 1024 Byte = 1 KB 1024 KB = 1 MB
System.out.println(r.totalMemory()/1024.0/1024.0+"MB");
System.out.println(r.freeMemory()/1024.0/1024.0+"MB");
// QQ , 启动
Process p = r.exec("E:\\腾讯QQ\\Bin\\QQScLauncher.exe");
Thread.sleep(5000); // 使程序暂停5秒
p.destroy(); // 销毁接收的对象
// 退出程序
r.exit(0);
}
}
注意:1)Runtime 是单例类,只能通过 getRuntime 来获取 Runtime 唯一的对象
2)调用 exec 来运行外部程序时,会出现安全警告,需要 throws IOException 来抛出异常
3)sleep 方法以后会学,这里不再说明
4. BigDecimal
属于math包下,用于解决浮点型运算时,出现结果失真的问题。
public class Test{
public static void main(String[] args) throws IOException, InterruptedException {
double a = 0.1;
double b = 0.2;
System.out.println(a+b); // 0.30000000000000004
}
}
如上面代码所示,在某些情况下,Java中的浮点型运算会出现失真问题,所以要使用BigDecimal
public class Test{
public static void main(String[] args) throws IOException, InterruptedException {
double a = 0.1;
double b = 0.3;
BigDecimal a1 = new BigDecimal(Double.toString(a));
BigDecimal b1 = BigDecimal.valueOf(b); // 推荐使用这种方式
System.out.println(a1.add(b1)); // 加法
System.out.println(a1.subtract(b1));// 减法
System.out.println(a1.multiply(b1));// 乘法
BigDecimal c = a1.divide(b1,2, RoundingMode.HALF_UP);// 除法
double c1 = c.doubleValue();
System.out.println(c1);
}
}
注意:如果运算结果本身不为固定精度,则在运算时要设置结果的精度和取舍模式
BigDecimal c = a1.divide(b1,2, RoundingMode.HALF_UP);
上面的代码则是保留两位小数,采用HALF_UP模式(四舍五入)
八、时间和日期
1. Date
用于获取当前时间和其对应的时间毫秒值
public class Test{
public static void main(String[] args) {
Date d1 = new Date();
System.out.println(d1); // Mon Sep 25 16:20:34 CST 2023
long time = d1.getTime();
System.out.println(time);// 1695630034924
time += 60*1000; // 获取一分钟后的时间
Date d2 = new Date(time);// Mon Sep 25 16:21:34 CST 2023
System.out.println(d2);
d1.setTime(time);
System.out.println(d1); // 和上面一样
}
}
2. SimpleDateFormat
用于把时间日期数据格式化和解析字符串中的时间对象
public class Test{
public static void main(String[] args) {
Date d1 = new Date();
System.out.println(d1); // Mon Sep 25 16:20:34 CST 2023
long time = d1.getTime();
SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEEa");// 要输入时间格式
String rs1 = s.format(d1);
System.out.println(rs1);// 2023-09-25 16:42:38 周一下午
String rs2 = s.format(time);
System.out.println(rs2);// 同上
}
}
注意:创建 SimpleDateFormat 对象时,要输入正确的时间格式的参数,如:yyyy-MM-dd HH:mm:ss EEEa(其中对应的字母代表【年-月-日 时:分:秒 星期几 上/下午】)
public class Test{
public static void main(String[] args) throws ParseException {
SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd");// 要输入时间格式
String time = "2023-08-25";
Date d = s.parse(time);
System.out.println(d);// Fri Aug 25 00:00:00 CST 2023
}
}
注意:1)解析的字符串要与创建的 SimpleDateFormat 对象设置的时间格式要相同
2)调用 parse 方法抛出异常,即 throws ParseException
3. Calendar
用于获取当前日历对象,可以自由修改年月日时分秒
public class Test{
public static void main(String[] args) {
Calendar now = Calendar.getInstance();
System.out.println(now);
int year = now.get(Calendar.YEAR);
System.out.println(year);// 2023
Date d = now.getTime();
System.out.println(d); // Mon Sep 25 18:07:48 CST 2023
long time = now.getTimeInMillis();
System.out.println(time); // 1695636525522
now.set(Calendar.MONTH,9);// Calendar中的MONTH是从0开始计算的
now.set(Calendar.DAY_OF_MONTH,31);
now.add(Calendar.MONTH,2);
now.add(Calendar.DAY_OF_MONTH,-30);
}
}
注意:1)使用 getInstance 获取 Calendar 的子类时,获取的永远是当前的日期时间
2)使用 set、add 等方法修改时间时,如果超出设置量的范围,则会自动转换为正常的日期时间,不会出现日期错误等问题
九、高级时间日期
1. LocalDate,LocalTime,LocalDateTime
LocalDate:代表本地日期(年、月、日、星期)
LocalTime:代表本地时间(时、分、秒、纳秒)
LocalDateTime:代表本地日期、时间(年、月、日、星期、时、分、秒、纳秒)
1)LocalDate 常用方法
常用于获取和修改当前日期或指定日期
public class Test{
public static void main(String[] args) {
LocalDate ld = LocalDate.now(); // 2023-09-27
int year = ld.getYear();
int month = ld.getMonthValue();
int day = ld.getDayOfMonth();
int dayOfYear = ld.getDayOfYear();
int dayOfWeek = ld.getDayOfWeek().getValue();
System.out.println(year+"-"+month+"-"+day); // 2023-9-27
System.out.println("今天是今年第"+dayOfYear+"天"+" 星期"+dayOfWeek); // 今天是今年第270天 星期3
// 设置指定的年月日
LocalDate ld1 = ld.withYear(2077).withMonth(12).withDayOfMonth(30); // 2077-12-30
// 对年月日进行加减操作
LocalDate ld2 = ld.plusYears(2).plusMonths(2).plusDays(3);// 2025-11-30
LocalDate ld3 = ld.minusYears(2).minusMonths(2).minusDays(3);// 2021-07-24
// 获取指定日期的对象
LocalDate ld4 = LocalDate.of(2077,1,1);
// 比较两个日期
System.out.println(ld.equals(ld4)); // false
System.out.println(ld.isBefore(ld4));// true
System.out.println(ld.isAfter(ld4)); // false
}
}
2)LocalTime 常用方法
常用于获取和修改当前时间或指定时间
public class Test{
public static void main(String[] args) {
LocalTime lt = LocalTime.now(); // 10:47:19.042974700
int hour = lt.getHour();
int minute = lt.getMinute();
int second = lt.getSecond();
int nano = lt.getNano();
}
}
其他操作与 LocalDate 基本相同,这里不再演示
3)LocalDateTime 常用方法
常用于获取和修改当前日期时间或指定日期时间
public class Test{
public static void main(String[] args) {
LocalDateTime ldt = LocalDateTime.now(); // 2023-09-27T10:56:36.804232400
// LocalDate,LocalTime与LocalDateTime之间类型转换
LocalDate ld = ldt.toLocalDate(); // 2023-09-27
LocalTime lt = ldt.toLocalTime(); // 10:56:36.804232400
LocalDateTime ldt1 = LocalDateTime.of(ld,lt); // 2023-09-27T10:56:36.804232400
}
}
其他操作与 LocalDate,LocalDate 基本相同,这里不再演示
注意:1)这三种类提供的方法都可以实现链式编程
2)如果要修改指定的日期时间,修改的日期时间必须在正常的范围内
2. ZoneId,ZonedDateTime
ZoneId:代表时区Id
ZonedDateTime:代表带时区的日期时间
public class Test{
public static void main(String[] args) {
// 获取系统的默认时区(【洲名/城市名】或【国家名/城市名】)
ZoneId zoneId = ZoneId.systemDefault(); // Asia/Shanghai
String id = zoneId.getId(); // Asia/Shanghai
// 获取Java支持的全部时区对象
System.out.println(ZoneId.getAvailableZoneIds());
// 获取指定的时区对象
ZoneId zoneId1 = ZoneId.of("America/New_York");
// 获取指定时区的日期时间对象
// America/New_York UTC-4:00
ZonedDateTime zdt = ZonedDateTime.now(zoneId1); // 2023-09-26T23:25:18.970548-04:00[America/New_York]
// Asia/Shanghai UTC+8:00
ZonedDateTime zdt1 = ZonedDateTime.now(); // 2023-09-27T11:26:20.253267400+08:00[Asia/Shanghai]
// 国际标准时间 UTC
ZonedDateTime zdt2 = ZonedDateTime.now(Clock.systemUTC()); // 2023-09-27T03:31:28.345425Z
}
}
ZonedDateTime 的其他操作与 LocalDateTime 基本相同,这里不再演示
3. Instant
代表时间线,可以获取从1970-01-01 00:00:00 到当前时间的总秒数+不到一秒的纳秒数
public class Test{
public static void main(String[] args) {
Instant i = Instant.now();
System.out.println(i); // 2023-09-27T08:31:23.877134900Z
long second = i.getEpochSecond(); // 1695802628 (s)
int nano = i.getNano(); // 854942100 (ns)
// 增加或减少 秒、毫秒、纳秒
Instant i1 = i.plusSeconds(10).plusMillis(10).plusNanos(10); // 2023-09-27T08:31:33.887134910Z
Instant i2 = i.minusSeconds(10).minusMillis(10).minusNanos(10); // 2023-09-27T08:31:13.867134890Z
// 比较两个时间线
System.out.println(i1.equals(i2)); // false
System.out.println(i1.isBefore(i2));// false
System.out.println(i1.isAfter(i2)); // true
}
}
常用于记录代码的执行时间,或用于记录用户操作某个事件的时间点
4. DateTimeFormatter
用于对日期时间对象进行格式化或解析字符串中的日期时间
public class Test{
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt = LocalDateTime.now(); // 2023-09-27T17:00:20.818131300
// 格式化对象
String rs = formatter.format(ldt); // 正向格式化
System.out.println(rs); // 2023-09-27 17:00:20
String rs1 = ldt.format(formatter);// 反向格式化
System.out.println(rs1);// 2023-09-27 17:00:20
// 解析字符串
String rs2 = "2077-10-01 12:23:44";
LocalDateTime ldt1 = LocalDateTime.parse(rs2,formatter); // 2077-10-01T12:23:44
}
}
注意:被解析的字符串格式要与格式化器的时间格式保持一致
5. Period,Duration
1)Period(一段时期)
可以计算两个 LocalDate 对象相差的年数、月数、天数,支持 LocalDate
public class Test{
public static void main(String[] args) {
LocalDate ld1 = LocalDate.of(2023,10,28);
LocalDate ld2 = LocalDate.of(2077,12,1);
Period p = Period.between(ld1,ld2);
System.out.println(p.getYears()); // 53
System.out.println(p.getMonths());// 1
System.out.println(p.getDays()); // 3
}
}
注意:Period 的日期间隔只计算一位间隔,且遵循借位原则
2)Duration(持续时间)
可以计算两个时间对象相差的天数、小时数、分数、秒数、毫秒数、纳秒数;支持 LocalTime、LocalDateTime、 Instant 等时间。
public class Test{
public static void main(String[] args) {
LocalDateTime ldt1 = LocalDateTime.of(2023,10,1,12,12,12,12);
LocalDateTime ldt2 = LocalDateTime.of(2023,12,12,11,11,11,11);
Duration duration = Duration.between(ldt1,ldt2);
System.out.println(duration.toDays()); // 71
System.out.println(duration.toHours()); // 1726
System.out.println(duration.toMinutes());// 103618
System.out.println(duration.toSeconds());// 6217138
System.out.println(duration.toMillis()); // 6217138999
System.out.println(duration.toNanos()); // 6217138999999999
}
}
注意:Duration 的时间间隔是计算整体的间隔
十、Arrays 类
1.认识 Arrays
Arrays 类是一个操作数组的工具类
public class Test{
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5,6};
// 将数组转换为字符串类型
String arrStr = Arrays.toString(arr);
System.out.println(arrStr); // [1, 2, 3, 4, 5, 6]
// 拷贝指定范围的数组
int[] arr1 = Arrays.copyOfRange(arr,1,3);// 包前不包后
System.out.println(Arrays.toString(arr1)); // [2, 3]
// 拷贝指定大小的数组
int[] arr2 = Arrays.copyOf(arr,10);
System.out.println(Arrays.toString(arr2)); // [1, 2, 3, 4, 5, 6, 0, 0, 0, 0]
// 将数组中的原数据改为新数据
double[] arr3 = new double[]{99.8,22.6,100};
Arrays.setAll(arr3, new IntToDoubleFunction(){
@Override
public double applyAsDouble(int value) {
return BigDecimal.valueOf(arr3[value]).multiply(BigDecimal.valueOf(0.8)).doubleValue();
}
});
System.out.println(Arrays.toString(arr3)); // [79.84, 18.08, 80.0]
// 将数组中的数进行排序(默认为升序)
Arrays.sort(arr3);
System.out.println(Arrays.toString(arr3)); // [18.08, 79.84, 80.0]
}
}
注意:1)copyOfRange 和 copyOf 方法所拷贝的数组都是浅拷贝
2)对浮点型计算一定要注意失真问题,在必要的时候使用 BigDecimal
3)在示例中的 sort 方法只是对数进行了简单排序,它也可以把对象排序
2. sort 方法(排序对象)
方式一:让该对象的类实现Comparable(比较规则)接口,然后重写compareTo方法,自己来制定比较规则。
public class Student implements Comparable<Student>{
private String name;
private double height;
private int age;
// 重写compareTo方法,制定排序的比较规则,sort方法会自动调用compareTo方法
// 升序规定1:左边对象(this)大于右边对象(o),返回正整数
// 升序规定2:左边对象(this)小于右边对象(o),返回负整数
// 升序规定3:左边对象(this)等于右边对象(o),返回0
@Override
public int compareTo(Student o) {
//if(this.age>o.age) {
// return 1;
//}
//else if (this.age<o.age) {
// return -1;
//}
//return 0;
return this.age-o.age;
}
public Student(String name, double height, int age) {
this.name = name;
this.height = height;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", height=" + height +
", age=" + age +
'}';
}
}
注意: 1)Comparable 是一个泛型接口,需要传入类型
2)在外部调用 sort 方法会自动调用类内的 compareTo 方法
3)compareTo 方法中,示例注释的倒序规定与升序规定同理,交换顺序即可
4)如果比较的标准为整型,可以直接返回相减的结果,浮点型不能
方式二:使用 sort 方法,创建 Comparator 比较器接口的匿名内部类对象,然后制定比较规则
public class Test{
public static void main(String[] args) {
Student[] students = new Student[4];
students[0] = new Student("a",143.9,18);
students[1] = new Student("b",123.3,13);
students[2] = new Student("c",187.4,19);
students[3] = new Student("d",155.2,18);
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return Double.compare(o1.getHeight(), o2.getHeight());
}
});
System.out.println(Arrays.toString(students));
}
}
注意:1)Comparator 是一个泛型接口,要传入类型
2)它的比较规则和方法一相同,这里不再重复
3)如果比较的标准为浮点型,可以使用 Double 包装类的 compare 方法来返回结果
十一、方法引用
作用:不暴露实现方法和传入参数,并且能已改全改解耦合(目前只做了解)
标志性符号:【: :】(双冒号)
1.静态方法的引用
静态方法的引用:类名 :: 静态方法。
使用场景:如果某个Lambda表达式里只是调用一个静态方法,并且前后参数的形式一致,就可以使用静态方法引用。
2.实例方法的引用
实例方法的引用:对象名 :: 实例方法。
使用场景:如果某个Lambda表达式里只是调用一个实例方法,并且前后参数的形式一致,就可以使用实例方法引用。
3.特定类型方法的引用
特定类型的方法引用:类型 :: 方法
使用场景:如果某个 Lambda 表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数是作为方法的主调,后面的所有参数都是作为该实例方法的传入参数,则此时就可以使用特定类型方法引用。
4.构造器引用
构造器引用:类名::new。
使用场景:如果某个Lambda表达式里只是在创建对象,并且前后参数情况一致,就可以使用构造器引用。
十二、正则表达式
1.认识正则表达式
概念:由一些特定的字符组成,代表一个规则。
作用:1)用来校验数据的格式是否合法
2)在一段文本中查找符合规则的语句
2.书写规则
下面是一些基础规则:
public class Test {
public static void main(String[] args) {
System.out.println("a".matches("[a-z]"));// true
System.out.println("1".matches("\\d")); // true
System.out.println("A".matches("\\w")); // true
System.out.println("2a".matches("\\w")); // false
System.out.println("a3A".matches("[a-z]\\w{2,3}")); // true
System.out.println("a3A".matches("[^a-z]\\w{2,3}"));// false
}
}
注意:在Java中,\ 是有特殊含义的,如 \n、\t,所以书写部分字符时要使用 // 转义
下面还有一些常用的规则字符:
public class Test {
public static void main(String[] args) {
// (?i)-忽略大小写 |-或 ()-分组
System.out.println("Az".matches("(?i)[a-z]{2}")); // true
System.out.println("123".matches("\\d{3}|[a-z]{3}"));// true
System.out.println("123".matches("\\d{3}|[a-z]{3}"));// true
System.out.println("1a3".matches("\\d{3}|[a-z]{3}"));// false
}
}
3.应用:查找文本中的指定内容
public class Test {
public static void main(String[] args) {
String data = "电话: 18512516758,18512508907\n" +
"或者联系邮箱: boniu@itcast.cn\n" +
"座机电话: 01036517895, 010-98951256\n" +
"邮箱: bozai@itcast.cn\n" +
"邮箱2: dlei0009@163.com\n" +
"热线电话: 400-618-9090,400-618-4000,\n" +
"4006184000,4006189090\n";
// 1.编写正则表达式
String regex = "(1[3-9]\\d{9})|(\\w{2,}@\\w{2,10}(\\.\\w{2,3}){1,2})"+
"|(010-?\\d{5,10})|(\\d{3}-?\\d{3}-?\\d{4})";
// 2.将正则表达式封装给一个Pattern对象
Pattern pattern = Pattern.compile(regex);
// 3.通过Pattern对象来得到查找指定内容的Matcher匹配器
Matcher matcher = pattern.matcher(data);
// 4.通过匹配器来爬取内容
while(matcher.find())
{
System.out.println(matcher.group());
}
}
}
4.应用:对文本中的指定内容进行二次操作(替换,切割)
public class Test {
public static void main(String[] args) {
// 1.将文本中的非中文字符替换为“-”
String data1 = "蔡徐坤3bkwjeiqk王一博ajwdh9672肖战";
String s1 = data1.replaceAll("\\w+", "-");
System.out.println(s1); // 蔡徐坤-王一博-肖战
// 2.将文本中的重复字符替换单一字符
String data2 = "我我我我喜欢编编编编程程程!!!!";
String s2 = data2.replaceAll("(.)\\1+","$1");
System.out.println(s2); // 我喜欢编程!
// 3.将文本中的中文名字提取出来
String data3 = "蔡徐坤3bkwjeiqk王一博ajwdh9672肖战";
String[] names = data3.split("\\w+");
System.out.println(Arrays.toString(names)); // [蔡徐坤, 王一博, 肖战]
}
}
补充:1)实现替换操作:利用 String 类中的 replaceAll 方法,根据正则表达式来对指定内容替换,最后返回替换后的字符串
2)对重复字符单一化操作时,可以使用 \\1 来对指定内容分组,用 $1 来引用
3)实现分割操作:利用 String 类中的 split 方法,根据正则表达式以指定内容为分割符来分割文本,最后返回一个 String 数组
十三、异常
1.认识异常
概念:指程序出现的各种问题,存在于 java.lang.Throwable
分类:1)Error:代表系统级别异常,一般不需要程序员解决
2)Exception:代表程序级别异常,是在编译阶段或者运行阶段的异常
3)RuntimeException:RuntimeException及其子类是继承 Exception 类的,用于封装程序运行时的问题
异常处理:1)try{}catch{}语句
public class Test {
public static void main(String[] args) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse("1999-12-30 12:12:12");
System.out.println(d);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
2)throws 抛出异常
public class Test {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse("1999-12-30 12:12:12");
System.out.println(d);
}
}
2.自定义异常
1)自定义运行时异常
定义一个异常类并继承 RuntimeException,然后重写构造器
通过 throw new 异常类(xxx)来创建异常对象并抛出
public class NumIllegalRuntimeException extends RuntimeException{
public NumIllegalRuntimeException() {
}
public NumIllegalRuntimeException(String message) {
super(message);
}
}
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int number = sc.nextInt();
checkNum(number);
}
public static void checkNum(int num)
{
if(num>0&&num<100) {
System.out.println("输入正确");
}
else {
throw new NumIllegalRuntimeException("This number is illegal,"+ num +" is error");
}
}
}
注意: 自定义运行时异常反应不强烈,只有在运行时才会报错
2)自定义编译时异常
定义一个异常类并继承 Exception,然后重写构造器
通过 throw new 异常类(xxx)来创建异常对象并抛出
public class NumIllegalException extends Exception{
public NumIllegalException() {
}
public NumIllegalException(String message) {
super(message);
}
}
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int number = sc.nextInt();
checkNum(number);
}
public static void checkNum(int num) throws NumIllegalException {
if(num>0&&num<100) {
System.out.println("输入正确");
}
else {
throw new NumIllegalException("This number is illegal,"+ num +" is error");
}
}
}
注意:自定义运行时异常反应强烈,在编译时就会报错,需要抛出异常才能运行
3.异常处理
将底层方法的异常逐层往上抛,直到全部抛到顶层方法中后集中处理
处理方法:1)捕获异常,记录异常并响应合适的信息给用户
public class Test {
public static void main(String[] args) {
try {
test1();
} catch (Exception e) {
System.out.println("您当前操作有问题");
e.printStackTrace();
}
}
public static void test1() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse("2028-11-11 10:24");
System.out.println(d);
test2();
}
public static void test2 () throws Exception {
InputStream is = new FileInputStream("D:/meinv. png");
}
}
注意:优先识别并捕获高层方法的异常进行处理
2)捕获异常,尝试重新修复
public class Test {
public static void main(String[] args) {
while(true){
try {
checkNum();
break;
} catch (Exception e) {
System.out.println("输入数字不合法");
}
}
}
public static void checkNum(){
Scanner sc = new Scanner(System.in);
System.out.println("请你输入一个正整数:");
int number = sc.nextInt();
while (true) {
if(number>0) {
System.out.println("输入正确");
}
else {
System.out.println("输入错误");
}
}
}
}
将捕获异常套入循环,出现异常后再次循环,直到运行成功