> 文章列表 > 小黑子—Java从入门到入土过程:第七章

小黑子—Java从入门到入土过程:第七章

小黑子—Java从入门到入土过程:第七章

Java零基础入门7.0

  • Java系列第七章
    • 1. 游戏打包exe
    • 2. API
      • 2.1 Math
      • 2.2 练习
        • 2.2.1 判断质数
        • 2.2.2 判断水仙花数(自幂数)
      • 2.3 System
      • 2.4 Runtime
      • 2.5 Object
        • 2.5.1 Object 的成员方法
          • (1) toString
          • (2) equals 比较两个对象是否相等
          • (3) clone方法(Object对象中的方法)
        • 2.5.2 Objects
      • 2.6 BigInteger(大的整数)
        • 2.6.1 Biglnteger构造方法:
          • (1) public BigInteger(int num,Random rnd) 获取随机大范围整数
          • (2) public BigInteger(String val) 获取指定的大整数
          • (3) public BigInteger(String val,int radix) 获取指定进制的大整数
          • (4) BigInteger.valueOf(long val)
        • 2.6.2 BigInteger常见成员方法
        • 2.6.3 BigInteger底层存储方式
      • 2.7 BigDecima(大的小数)
        • 2.7.1 Bigdecimal的常见方法
        • 2.7.2 Bigdecimal的底层存储方式
      • 2.7 正则表达式
        • 2.7.1 正则表达式的字符
        • 2.7.2 正则表达式的数量词
        • 2.7.3 正则表达式练习
          • 一、
          • 二、
        • 2.7.4 爬虫
          • 一、带条件的数据爬取
          • 二、贪婪爬取和非贪婪爬取
        • 2.7.5 正则表达式在字符串方法中的使用
        • 2.7.6 分组
          • (1)捕获分组
          • (2)非捕获分组
      • 2.8 JDK7时间
        • 2.8.1 Date 时间

Java系列第七章

1. 游戏打包exe

游戏打包exe要考虑的因素:
小黑子—Java从入门到入土过程:第七章
游戏打包核心步骤:
小黑子—Java从入门到入土过程:第七章

2. API

2.1 Math

Math是一个帮助我们用于进行数学计算的工具类私有化构造方法,所有的方法都是静态的

在源码中的定义
public final class Math extends Object

即Math是一个最终类,不能被继承

Math是一个帮助计算的工具类 私有化构造方法,所有方法都是静态的
类Math包含用于执行基本数字运算的方法,例如基本指数,对数,平方根和三角函数

Math中的方法基本上都是静态修饰,可以直接通过类名调用
Math. 方法名()
例如:
Math.abs(-11)

常用方法:
小黑子—Java从入门到入土过程:第七章

abs的bug

以int为例
int类型的取值范围是-2147483648~2147483647 如果没有正数与负数对应,那么传递负数结果有误

解决方案
使用absExact方法,当取值超过数据类型范围时,会有报错

System.out.println(Math.abs(88));//88System.out.println(Math.abs(-88));//88System.out.println(Math.absExact(-2147483648));//报错

小黑子—Java从入门到入土过程:第七章
ceil 向上取整
小黑子—Java从入门到入土过程:第七章

floor 去尾法
小黑子—Java从入门到入土过程:第七章
round 四舍五入
小黑子—Java从入门到入土过程:第七章
max 获取两个整数的最大值
小黑子—Java从入门到入土过程:第七章
min 获取两个整数的最小值
小黑子—Java从入门到入土过程:第七章
pow 获取a的b次幂
小黑子—Java从入门到入土过程:第七章

sqrt 开方 和 cbrt 开立方
小黑子—Java从入门到入土过程:第七章
random 获取随机数
小黑子—Java从入门到入土过程:第七章

2.2 练习

2.2.1 判断质数

小黑子—Java从入门到入土过程:第七章
但是这样遍历的话,如果要求的数非常大,那么就要从2开始依次遍历,非常麻烦

寻找因子的规律:
小黑子—Java从入门到入土过程:第七章
在左边因子当中,必定是小于等于该数(比如16)的平方根的
在右边因子当中,必定是大于等于该数(比如16)的平方根的

小黑子—Java从入门到入土过程:第七章
假设两个左右两边的数都是哪怕大于4的一点点,那么就不等于16
同理小于4的一点点时,也不等于
小黑子—Java从入门到入土过程:第七章
故其中一个因子当中一定是有小于等于平方根的,还有就是大于等于平方根的
以下因子情况:
小黑子—Java从入门到入土过程:第七章

所以判断一个数是否为质数的时候,不需要把所有的数全部遍历一遍,只需要把左边的因子数遍历判断就可以了

代码实现:

import java.util.Scanner;public class Main {public static void main(String[] args) {System.out.println("请输入一个整数:");Scanner sc = new Scanner(System.in);int number = sc.nextInt();System.out.println(isPrime(number));}public static boolean isPrime(int number) {int count =0;for (int i = 2; i <= Math.sqrt(number); i++) {count++;if (number % i == 0) {return false;}}System.out.println("循环了"+count+"次"+","+number+"是质数");return true;}
}

小黑子—Java从入门到入土过程:第七章

2.2.2 判断水仙花数(自幂数)

小黑子—Java从入门到入土过程:第七章

public class Main {public static void main(String[] args) {int count=0;//得到每一个三位数for (int i = 100; i < 999; i++) {//个位 十位 百位int ge =i%10;int shi = i/10%10;int bai = i/100%10;//判断://每一位的三次方之和 跟本身进行比较double sum = Math.pow(ge,3)+Math.pow(shi,3)+Math.pow(bai,3);if(sum==i){count++;System.out.println(i);}}System.out.println("循环了"+count+"次");}
}

小黑子—Java从入门到入土过程:第七章

2.3 System

小黑子—Java从入门到入土过程:第七章

计算器机的时间原点:
小黑子—Java从入门到入土过程:第七章
currentTimeMillis()来判断程序运行时间
小黑子—Java从入门到入土过程:第七章

        long l = System.currentTimeMillis();System.out.println(l);
//表示从开始到结束,采用了运行多少时间

arraycopy 数组拷贝

        /*第一个参数:数据源,表示被拷贝的数组第二个参数:从数据原中的第几个索引开始拷贝第三个参数:目的地,我要把数据拷贝到那个数组中第四个参数:目的地数组的索引第五个参数:拷贝的个数*/System.arraycopy(arr1,0,arr2,2,4);for (int i = 0; i < arr2.length; i++) {System.out.println(arr2[i]);}

小黑子—Java从入门到入土过程:第七章
arraycopy的细节:

  • 1.如果数据原数组和目的地数组的值都是基本数据类型,那么两者的类型必须保持一致,否则会报错
  • 2.在拷贝的时候,需要考虑数组的长度,如果超出范围也会报错
  • 3.如果数据原数组和目的地数组的值都是引用数据类型,那么,子类类型可以赋值给父类类型
//Person是Student的父类
Student s1=new Student("zhangsan",18);
Student s2=new Student("sanwu",18);
Student s3=new Student("lisan",18);
Student []arr1={s1,s2,s3};
Person []arr2=new Person[3];
//这里是可以赋值的,但在遍历arr2的时候,要把数据类型再进行转换,换成Student
System.arraycopy(arr1,0,arr2,0,3);
for(i=0;i<arr2.length.i++){
Student stu=(Student)arr2[i];
sout(stu.getname()+" "+stu.getage())

小黑子—Java从入门到入土过程:第七章
小结:

小黑子—Java从入门到入土过程:第七章

2.4 Runtime

Runtime表示当前虚拟机的运行环境

主要可以用来监视虚拟机的内存
这个类的方法不是静态的,需要先获取对象才能调用方法

小黑子—Java从入门到入土过程:第七章
1.获取Runtime的对象

//获取Runtime对象//java的Runtime对象只有一个Runtime r1=Runtime.getRuntime();Runtime r2=Runtime.getRuntime();System.out.println(r1==r2);

小黑子—Java从入门到入土过程:第七章

2.exit 停止虚拟机
这个实际上就是System.exit(0)的底层源码

Runtime.getRuntime().exit(0);

3.ailableProcessors 获取cpu线程数

System.out.println(Runtime.getRuntime().availableProcessors());

小黑子—Java从入门到入土过程:第七章

4.maxMemory 可以获得的总内存大小,单位byte字节

System.out.println(Runtime.getRuntime().maxMemory()/1024/1024);

小黑子—Java从入门到入土过程:第七章

5.totalMemory 已经获得的内存大小,单位byte字节

System.out.println(Runtime.getRuntime().totalMemory()/1024/1024);

小黑子—Java从入门到入土过程:第七章

6.freeMemory 虚拟机中剩余内存的大小

System.out.println(Runtime.getRuntime().freeMemory());

小黑子—Java从入门到入土过程:第七章
7. 运行cmd命令

        //以字符串的形式运行cmd命令//notepad,打开记事本//shutdown,关机//加上参数才能运行//-s,默认一分钟之后关机//-s -t 指定关机时间//-a 取消关机操作//-r 关机并重启Runtime.getRuntime().exec("notepad");

2.5 Object

小黑子—Java从入门到入土过程:第七章
Object中没有成员变量,所以没有带参构造,只有空参构造方法
public Object

Object 的构造方法
小黑子—Java从入门到入土过程:第七章

2.5.1 Object 的成员方法

小黑子—Java从入门到入土过程:第七章

(1) toString

返回对象的字符串表现形式

public class Test1 {public static void main(String[] args) {Object obj = new Object();String str1 = obj.toString();System.out.println(str1);//java.lang.Object@4eec7777 @之前是包名+类名,@后是对象的地址值sout(obj);//打印出来也是java.lang.Object@4eec7777}
}

System.out.println(参数)语句的解析
System是类名,out是一个静态变量,System.out:获取被打印的对象,println则是一个方法,参数就表示要打印的内容

核心逻辑:
当我们打印一个对象时,底层会调用对象的toString方法,把对象变成字符串,然后再打印在控制台上,打印完成后换行处理

将toString()打印地址值改成打印对象属性值的解决方案:

重写父类Object中的toString方法

toString方法的结论:

  • 如果打印一个对象,想要看到属性值的话,那么在子类中重写toString方法就行可以了
  • 在重写的方法中,把对象属性值进行拼接返回
    @Overridepublic String toString() {return name+","+age;}
(2) equals 比较两个对象是否相等

小黑子—Java从入门到入土过程:第七章

原本比较的是地址值,通过重写(直接用alt+回车的重写)可以比较对象里的属性值

    @Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return age == student.age && name.equals(student.name);}//测试Student stu1=new Student("zhangsan",18);Student stu2=new Student("zhangsan",18);
//        System.out.println(stu1);boolean s1=stu1.equals(stu2);System.out.println(s1);//结果为true

结论:

  1. 如果没有重写equals方法,那么默认使用Object中的方法进行比较,比较的是地址值是否相等
  2. 一般来讲地址值对我们意义不大,所以会进行方法的重写,将equals重写后比较的就是对象内部的属性值了

注:
String类中的equals方法会先判断参数是否为字符串,如果是字符串,再比较内部的属性,但是如果不是字符串,直接返回false
StringBuilder中没有equals方法,所以会直接使用Object中的equals方法,也就是比较地址值是否相等

(3) clone方法(Object对象中的方法)

clone()对象克隆
protected Object clone()
把A对象的属性值完全拷贝给B对象,也叫对象拷贝,对象复制

小黑子—Java从入门到入土过程:第七章
小黑子—Java从入门到入土过程:第七章

clone()源码,被protected修饰,所以不能直接调用,需要在子类里重写,
ctrl+鼠标点击就可以查看java源码

Cloneable接口

  • 如果一个接口中没有抽象方法
  • 表示当前接口是一个标记性接口
  • 当标记性接口(现在Cloneable表示)一旦实现,那么当前类的对象就可以被克隆
  • 如果没有实现,当前类的对象就不能克隆 即,要想一个对象被克隆的话,就必须实现Cloneable这个接口,然后才能被克隆

alt + 回车 选择第二个异常签名,让克隆不报错
小黑子—Java从入门到入土过程:第七章

User类:
import java.util.StringJoiner;public class User implements Cloneable{//实现额外的接口private int id;//游戏角色private String username;//用户名private String password;//密码private String path;//游戏图片private int[] data;//游戏进度public User() {}public User(int id, String username, String password, String path, int[] data) {this.id = id;this.username = username;this.password = password;this.path = path;this.data = data;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public int[] getData() {return data;}public void setData(int[] data) {this.data = data;}public String toString(){return "角色编号为:"+id+",用户名 "+username+",密码"+password+",游戏图片"+path+",游戏进度"+arrToString();}public String arrToString(){StringJoiner sj = new StringJoiner(",","[","]");for (int i = 0; i < data.length; i++) {sj.add(data[i]+"");}return sj.toString();}@Overrideprotected Object clone() throws CloneNotSupportedException {//调用父类中的clone方法//相当于让Java帮我们克隆一个对象,并把克隆之后的对象返回出去return super.clone();}
}测试类:
public class ObjectDemo2 {public static void main(String[] args) throws CloneNotSupportedException {//1.先创建一个对象int[] data ={1,2,3,4,5,6,7,8,9};User u1 = new User(1,"maghua","12345","girl1",data);//2.克隆对象,不能直接克隆//因为当前方法是受保护的,表示当前的克隆方法只能被本包的类、还有其他包中的子类所使用//又Object是定义再java.lang包下的,不能把代码写再lang包下//所以,如果想要clone()方法,只能去User重写克隆方法//细节://方法在底层会帮我们创建一个对象,并把原对象中的数据拷贝过去。//书写细节://1.重写object中的clone方法//2.让javabean类实现cloneable接口//3.创建原对象并调用clone就可以了User u2 = (User)u1.clone();//由于克隆出来的对象是用户对象,所以要进行强转System.out.println(u1);System.out.println(u2);}
}

小黑子—Java从入门到入土过程:第七章
图解:
小黑子—Java从入门到入土过程:第七章

Cloneable接口:

package java.lang;
public interface Cloneable {
}

对象克隆方式一:

浅克隆(浅拷贝)
拷贝时,先创建一个新的对象,然后 基本数据类型就拷贝其数值, 引用数据类型就拷贝其地址值
小黑子—Java从入门到入土过程:第七章

深克隆(深拷贝)
拷贝时,也会先创建一个新的对象,然后 基本数据类型就拷贝其数值,

  • 引用数据类型在拷贝时,如果是new出来的,就会在堆内存中开辟出一块新的空间,然后把之前老空间里的数据复制到新空间里,再把新空间的新地址值赋值给拷贝的变量,如果不是new出来的,像字符串,数据在串池里的,就会把之前的数据复用
    小黑子—Java从入门到入土过程:第七章

实现代码:

上方User类的重写方法修改:
@Overrideprotected Object clone() throws CloneNotSupportedException {//调用父类中的clone方法//相当于让Java帮我们克隆一个对象,并把克隆之后的对象返回出去//先把被克隆对象中的数组获取出来int[] data = this.data;//创建新的数组int[] newData = new int[data.length];//拷贝数组中的数据for (int i = 0; i < data.length; i++) {newData[i] = data[i];}//调用父类中的方法克隆对象User u = (User)super.clone();//因为父类中的克隆方法是浅克隆,替换克隆出来对象的数组地址值u.data = newData;return u;}测试类:User u2 = (User)u1.clone();int[] arr =u1.getData();arr[0] = 100;System.out.println(u1);System.out.println(u2);

小黑子—Java从入门到入土过程:第七章

以后用到深克隆到实际项目当中时,就会使用第三方的一个工具
使用步骤:

  • 1.先创建一个lib(Library)包,将第三方的工具导入当前项目中
  • 2.编写代码
 //先创建一个工具的对象Gson gson=new Gson();//把对象变成一个字符串String s= gson.toJson(u1);User user=gson.fromJson(s,User.class);//打印对象System.out.println(user);

这个深克隆的工具放到自己了解的路径当中
导入后,再点击添加到库中,如果有问题,看160集
小黑子—Java从入门到入土过程:第七章

有问题了看这篇文章

小结:
小黑子—Java从入门到入土过程:第七章

  • 浅克隆:不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来
  • 深克隆:基本数据类型拷贝过来,字符串复用,引用数据类型还会重新创建新的

总结:
小黑子—Java从入门到入土过程:第七章

2.5.2 Objects

objects是一个工具类,提供了一些方法去完成一些功能。
小黑子—Java从入门到入土过程:第七章

objects的成员方法:
小黑子—Java从入门到入土过程:第七章

Studnet s1 = null;
Stundent s2 = new Student("zhangsan",33);
boolean result=Objects.equals(s1,s2);
sout(Objects.isNull(s3));
sout(Objects.nonNull(s4));

直接使用Objects调用
细节:

  • 1.Objects的equals方法的底层会先判断s1是否为null,如果时null,直接返回false
  • 2.如果s1不为null,那么就利用s1再调用equals方法
  • 3.此时的s1为Student类型,所以调用的还是Student的equals方法,如果没有重写,调用的就是Object的equals方法,比较地址值,如果重写了,就会比较属性值。

总结:
小黑子—Java从入门到入土过程:第七章

2.6 BigInteger(大的整数)

BigInteger(大的整数):
BigInteger可以表示超出long范围的整数,上限可以看作无限
小黑子—Java从入门到入土过程:第七章

2.6.1 Biglnteger构造方法:

小黑子—Java从入门到入土过程:第七章

(1) public BigInteger(int num,Random rnd) 获取随机大范围整数

public BigInteger(int num,Random rnd) 获取随机大范围整数

范围[0~2的num次方-1],包含0和2的num次方-1

        Random r = new Random();BigInteger bd1 = new BigInteger(4,r);System.out.println(bd1);//2^4-1之内出现
(2) public BigInteger(String val) 获取指定的大整数
BigInteger bd2 = new BigInteger("999999999999999999999999999999999999999999999999999999999999999");//照样输出不会报错BigInteger bd2 = new BigInteger("1.1");//有小数,直接报错BigInteger bd2 = new BigInteger("abc");//字符串直接报错
(3) public BigInteger(String val,int radix) 获取指定进制的大整数

细节;
1.字符串中的数字必须是整数
2.字符串中的数字必须要跟进制吻合。
比如二进制中,那么只能写0和1,写其他的就报错。

BigInteger bd1=new BigInter("100",10);//这里获取的就是10进制的100,打印出来就是100
BigInteger bd2=new BigInter("100",2);//这里获取的就是2进制的100,打印出来就是4
BigInteger bd3=new BigInter("123",2);//这里代码就会报错,因为二进制中没有2和3
(4) BigInteger.valueOf(long val)

valueOf(long val),BigInteger的静态方法,可以获取BigInteger的对象,内部有优化

BigInteger(String val)与valueOf(long val)的不同之处
先来看相同的地方:他们都是创建一个大整数对象

BigInteger bd1=new BigInteger("100");
BigInteger bd1=BigInteger.valueOf(100);

不同:
1.valueOf能表示的范围比较小,只能在long的范围之内,不能超出long的范围
2.在内部,对常用的数字,-16到16进行了优化,提前把-16到16的BigInteger对象创建好了,多次获取时不会创建新的对象
3.如果超出了范围,就没有优化了,即使相同也会报错

BigInteger bd1=BigInteger.valueOf(16);
BigInteger bd2=BigInteger.valueOf(16);
sout(bd1==bd2);//==号比较的是地址值,结果是true,证明bd1和bd2是同一个对象BigInteger bd3=BigInteger.valueOf(17);
BigInteger bd4=BigInteger.valueOf(17);
sout(bd3==bd4);//==号比较的是地址值,结果是false,证明bd3和bd4不是同一个对象.
  • 如果不知道数据的范围有多大,就使用BigInteger(String val)
    去获取,如果知道数据的范围在long范围内,就使用valueOf来获取
  • 由于BigInteger对象一旦创建,内部记录的值就不能发生改变,所以,只要进行计算,像加减乘除在内的计算都会产生一个新的BigInteger对象

小结:
小黑子—Java从入门到入土过程:第七章

2.6.2 BigInteger常见成员方法

小黑子—Java从入门到入土过程:第七章
因为BigInteger是一个对象,不能直接相加减,要通过方法来完成
一些方法的使用:

import java.math.BigInteger;public class BigIntegerTest {public static void main(String[] args) {BigInteger bd1=BigInteger.valueOf(15);BigInteger bd2=BigInteger.valueOf(5);//加BigInteger bd3=bd1.add(bd2);System.out.println(bd3);//减BigInteger bd4=bd1.subtract(bd2);System.out.println(bd4);//乘BigInteger bd5=bd1.multiply(bd2);System.out.println(bd5);//除BigInteger bd6=bd1.divide(bd2);System.out.println(bd6);//除,获取商的同时获取余数 //返回值是一个数组,第一个值记录商,第二个值记录余数BigInteger[] bd7=bd1.divideAndRemainder(bd2);System.out.println(bd7);System.out.println(bd7[0]);System.out.println(bd7[1]);//比较是否相同Boolean a=bd1.equals(bd2);System.out.println(a);//次幂BigInteger bd9=bd1.pow(3);BigInteger bd10=bd2.pow(3);System.out.println(bd9+ " "+bd10);//返回较大值BigInteger bd11=bd1.max(bd2);System.out.println(bd11);//返回较小值BigInteger bd12=bd1.min(bd2);System.out.println(bd12);//转换类型int b=bd1.intValue();System.out.println(b);}
}

2.6.3 BigInteger底层存储方式

小黑子—Java从入门到入土过程:第七章
小黑子—Java从入门到入土过程:第七章

就是将一个大的整数分成多段存入数组中
1.先将数转化成补码
2.将补码32位分成一段,然后分成多段
3.再将多段补码转化为多段十进制整数
4.再将十进制整数存入数组

数组的最大长度就是int的最大值:2147483647(理论值)
小黑子—Java从入门到入土过程:第七章

总结:
小黑子—Java从入门到入土过程:第七章

2.7 BigDecima(大的小数)

小黑子—Java从入门到入土过程:第七章
小黑子—Java从入门到入土过程:第七章
小黑子—Java从入门到入土过程:第七章

计算机中的小数的存储方式
由于小数在计算机中用二进制表示时会很多,而每一种数据类型能够占用的空间是有限的,所以就会导致小数在计算机中的存储是不精确的

在实际应用中:
小黑子—Java从入门到入土过程:第七章

BigDecimal的作用:

  • 用于小数的精确计算
  • 用很大的小数

BigDecimal的一些构造方法:

方法名 说明
public BigDecimal(double val) 将 double 转换为 BigDecimal,但是,此构造方法的结果有一定的不可预知性,即还是有可能是不精确的,所以不推荐使用
public BigDecimal(String val) 将字符串转换为 BigDecimal。,它不会遇到 BigDecimal(double) 构造方法的不可预知问题。

与BigInteger一样,BigDecimal也有valueOf(long val)的静态方法,可以获取BigDecimal的对象,可以传递整数。

import java.math.BigDecimal;public class BigDecimalTest {public static void main(String[] args) {//通过传递double类型的小数来创建对象//这种方式有可能不精确,不推荐使用BigDecimal bd1=new BigDecimal(0.01);BigDecimal bd2=new BigDecimal(0.09);System.out.println(bd1);System.out.println(bd2);//结果为//0.01000000000000000020816681711721685132943093776702880859375//0.0899999999999999966693309261245303787291049957275390625//通过传递字符串表示的小数来创建对象//常用的方式1BigDecimal bd3=new BigDecimal("0.01");BigDecimal bd4=new BigDecimal("0.09");System.out.println(bd3);System.out.println(bd4);//结果为//0.01//0.09//通过静态方法获取对象BigDecimal bd6 = BigDecimal.valueOf(10);BigDecimal bd7 = BigDecimal.valueOf(10);System.out.println(bd6 == bd7);//trueBigDecimal bd6 = BigDecimal.valueOf(10.0);BigDecimal bd7 = BigDecimal.valueOf(10.0);System.out.println(bd6 == bd7);//false}
}

通过传递字符串表示的小数来创建对象valueOf静态方法的差别

  1. 如果表示的数字没有超出double的取值范围,推荐使用静态方法
  2. 如果要表示的数字比较大,超出了double的取值范围,建议使用构造方法
  3. 在静态方法中,如果传递的是0~10之间的整数(包括0和10),那么,方法会返回已经创建好的对象,不会重新new,跟BigInteger是一样的原理,但是,如果传递的是小数的话,就不会有优化,会创建新的对象

2.7.1 Bigdecimal的常见方法

小黑子—Java从入门到入土过程:第七章

//前面几个跟BigInteger没什么差别
//主要是除法BigDecimal bd1 = BigDecimal.valueOf(10.0);BigDecimal bd2 = BigDecimal.valueOf(3.0);//除法BigDecimal bd6 = bd1.divide(bd2);System.out.println(bd6);//报错
//当除不尽时,public BigDecimal divide(BigDecimal val)这个方法就会报错,
//此时,就要使用
//public BigDecimal divide(BigDecimal val,精确几位,舍入模式)。这个方法BigDecimal bd6 = bd1.divide(bd2,2,BigDecimal.ROUND_HALF_UP);System.out.println(bd6);//结果为3.33

其他的舍入模式可以在api文档中的RoundingMode类中可以找到:

数轴表示:

小黑子—Java从入门到入土过程:第七章

2.7.2 Bigdecimal的底层存储方式

小黑子—Java从入门到入土过程:第七章

Bigdecimal会先得到字符串里的字符,将字符串分成一个个单独的字符,在将字符转化为ascll码表上的数值,再存储到数组里。

例如:
小黑子—Java从入门到入土过程:第七章
总结:
小黑子—Java从入门到入土过程:第七章

2.7 正则表达式

正则表达式用于校验字符串的相关操作,可以很方便
字符串通过使用matches方法来匹配相应的正则表达式
"abc".matches("[bcd]");//结果为false

练习:
小黑子—Java从入门到入土过程:第七章

public static void main(String[] args) {//规则:6位及20位之内,0不能在开头,必须全部是数字//一个编程思想:// 先把异常数据进行过滤,// 过滤后得到的就是满足要求的数据了String qq = "1234567890";//System.out.println(checkQQ(qq));但是这样写就稍微麻烦了System.out.println(qq.matches("[1-9]\\\\d{5,19}"));//[5,19]表示6位到20位}public static boolean checkQQ(String qq){int len =qq.length();if(len<6||len>20){return false;}//0不能在开头if(qq.startsWith("0")){return false;}//必须全部是数字for (int i = 0; i < qq.length(); i++) {char c = qq.charAt(i);if(c<'0'||c>'9'){return false;}}return true;}
}

小黑子—Java从入门到入土过程:第七章
正则表达式的作用:

  • 1.校验字符串是否满足规则
  • 2.在一段文本中查找满足要求的内容

小黑子—Java从入门到入土过程:第七章

2.7.1 正则表达式的字符

小黑子—Java从入门到入土过程:第七章
上面的方括号([ ]) 在正则表达式中表示一个字符的范围,字符串里出现的字符一定要在这个范围之内,不在范围之内的话会返回false

  • ^在正则表达式中表示取反的意思
  • &&在正则表达式中表示取交集的意思
  • ()是分组
  • |在方括号外表示并集

在java中,\\表示转义字符,改变后面那个字符原本的含义
sout(" \\" ")打印出来的就是,sout(" " ")这样写会报错

\\\\就表示一个普通的\\符号,不表示转义字符

1.字符类例子:

取值判断
sout("a".metches("[abc]"));//true
sout("ab".metches("[abc]"));//false,因为这里的正则表达式只能表示一个字符的范围
//,如果想表示两个字符的范围的话,可以这样写
sout("ab".metches("[abc][abc]"));//这样才可以表示两个字符取反
sout("a".metches("[^abc]");//false
sout("d".metches("[^abc]");//true
sout("zz".metches("[^abc]"));//false
sout("zz".metches("[^abc][^abc]"));//falsea-z A-Z(包括头尾的范围)
sout("a".metches("[a-zA-Z]");//true
sout("z".metches("[a-zA-Z]");//true
sout("aa".metches("[a-zA-Z]");//false
sout("zz".metches("[a-zA-Z]");//false
sout("zz".metches("[a-zA-Z][a-zA-Z]");//true
sout("0".metches("[a-zA-Z0-9]");//ture[a-d[m-p]]: a到d,或m到p
sout("a".metches("[a-d[m-p]]");//true
sout("d".metches("[a-d[m-p]]");//true
sout("m".metches("[a-d[m-p]]");//true
sout("p".metches("[a-d[m-p]]");//true
sout("e".metches("[a-d[m-p]]");//false[a-z&&[def]]: a-z和def的交集。为: d, e, f
//如果要求两个范围的交集,那么需要写符号&&
//这里如果只写了一个&,那么就不表示为一个交集了,就是单纯的一个&符号,没有任何含义
sout("a".metches("[a-z&&[def]]");//false
sout("a".metches("[a-z&[def]]");//true
sout("d".metches("[a-z&&[def]]");//true
sout("&".metches("[a-z&[def]]");//true
sout("0".metches("[a-z&[def]]");//false[a-z&&[^bc]:a-z和非bc的交集。(等同于[ad-z])
sout("a".metches("[a-z&&[^bc]");//ture
sout("b".metches("[a-z&&[^bc]");//false
sout("0".metches("[a-z&&[^bc]");//false[a-z&&[^m-p]:a到z和除了m到p的交集。(等同于[a-lq-z])
sout("a".metches("[a-z&&[^m-p]");//true
sout("m".metches("[a-z&&[^m-p]");//false
sout("0".metches("[a-z&&[^m-p]");//false

2.预定义字符类:

.表示任意一个字符
sout("你".metches(".."))//false,因为有两个. ,所以需要两个字符
sout("你是".metches(".."))//true,
sout("你".metches("."))//true\\\\d
因为\\是转义字符,所以用\\\\来表示一个普通的\\,则\\\\d就表示\\d,而\\d表示任意的一个数字
// 简单来记: 两个\\表示一个\\
sout("0".metches("\\\\d"))//true
sout("n".metches("\\\\d"))//false
sout("21".metches("\\\\d"))//false
sout("21".metches("\\\\d\\\\d"))//true\\\\D
sout("n".metches("\\\\D"))//true
sout("1".metches("\\\\D"))//false\\\\w 只能是一位单词字符[a-zA-Z_0-9]
sout("n".metches("\\\\w"))//true
sout("_".metches("\\\\w"))//true
sout("8".metches("\\\\w"))//true
sout("你".metches("\\\\w"))//false\\\\W 非单词字符
sout("n".metches("\\\\W"))//false
sout("_".metches("\\\\W"))//false
sout("0".metches("\\\\W"))//false
sout("你".metches("\\\\W"))//true

2.7.2 正则表达式的数量词

正则表达式的数量词,可以一次表示多个字符
小黑子—Java从入门到入土过程:第七章

// x{6,} 至少出现6位
sout("234jnhs".matches("\\\\w{6,}"));//true
sout("23sx".matches("\\\\w{6,}"));//false//x{4}必须是4位
sout("3424".matches("[a-zA-Z0-9]{4}"))//true
sout("3A424".matches("[a-zA-Z0-9]{4}"))//false

忘记的可以看api文档Pattern

2.7.3 正则表达式练习

一、

小黑子—Java从入门到入土过程:第七章

public static void main(String[] args) {//细节://拿着一个正确的数据,从做到右依次去写//13112345678//分成三部分://第一部分:1表示手机号码只能以1开头//第二部分:[3-9]表示手机号码第二位只能是3-9之间的//第三部分: \\\\d{9}表示任意数字可以出现9次,也只能出现9次String regex1 = "1[3-9]\\\\d{9}";System.out.println("13112345678".matches(regex1));System.out.println("131".matches(regex1));System.out.println("------------------------------------------");//座机号码//020-2324242 02122442 027-42424 0712-3242434//思路://在书写座机号正则的时候需要把正确的数据分为三部分//一:区号 0\\\\d{2,3}//         0:表示区号一定是以0开头的//         \\\\d{2,3}:表示区号从第二位开始可以是任意的数字,可以出现2到3次//二:-   ?表示次数,0或一次//三:号码  号码的第一位也不能以0开头,从第二位开始可以是任意的数字,号码的总长度:5-10位String regex2 = "0\\\\d{2,3}-?[1-9]\\\\d{4,9}";System.out.println("020-2324242".matches(regex2));System.out.println("0712-3242434".matches(regex2));System.out.println("------------------------------------------");//邮箱号码//3232323@qq.com zhangsan@itcast.cnn dleieee9@163.com dleieees@pci.com.cn//思路://在书写邮箱号码正则的时候需要把正确的数据分为三部分//第一部分:@的左边 \\\\w+,一个非单词字符串出现多次//任意的字母数字下划线,至少出现一次就可以了//第二部分:@ 只能出现一次//第三部分: . 的左边可以是字符也可以是数字 [\\\\w&&[^_]]{2,6}//               任意的字母加数字,总共出现2-6次(此时不能出现下划线)//           .      \\\\.//           大写字母,小写字母都可以,只能出现2-3次[a-zA-Z]{2,3)String regex3 = "\\\\w+@[\\\\w&&[^_]]{2,6}(\\\\.[a-zA-Z]{2,3}){1,2}";System.out.println("3232323@qq.com".matches(regex3));System.out.println("dleieees@pci.com.cn".matches(regex3));}

小黑子—Java从入门到入土过程:第七章

在实际开发中,使用anyrule插件可以快捷创建正则表达式

右键点击
小黑子—Java从入门到入土过程:第七章
小黑子—Java从入门到入土过程:第七章

根据时间需求,把开头和末尾的符号删掉
String regex4="(?:[01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d";
作为区分

        //24小时的正则表达式 23:11:11String regex4 = "([01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d";System.out.println("23:11:11".matches(regex4));//true//优化String regex5 = "([01]\\\\d|2[0-3])(:[0-5]\\\\d){2}";System.out.println("23:11:11".matches(regex5));//true
二、

小黑子—Java从入门到入土过程:第七章

public static void main(String[] args) {//用户名要求:大小写字母,数字,下划线一共4-16位String regex1 = "\\\\w{4,16}";System.out.println("magua".matches(regex1));System.out.println("------------------------------------------");//身份证号码的简单校验://18位,前17位任意数字,最后一位可以是数字可以是大写或小写的xString regex2 = "[1-9]\\\\d{16}(\\\\d|X|x)";System.out.println("41080119930228457x".matches(regex2));//另类写法方式一String regex3 = "[1-9]\\\\d{16}[\\\\dXx]";System.out.println("510881197609822309".matches(regex3));System.out.println("------------------------------------------");//忽略大小写的书写方式//在匹配的时候忽略abc的大小写//方式二String regex4 = "a((?i)b)c";//表示只忽略a后面b的大小写System.out.println("abc".matches(regex4));//trueSystem.out.println("ABC".matches(regex4));System.out.println("aBc".matches(regex4));System.out.println("------------------------------------------");//方式三String regex5 = "[1-9]\\\\d{16}(\\\\d|(?i)x)";System.out.println("15040119810705387X".matches(regex5));System.out.println("------------------------------------------");//身份证号码的严格校验//4108011993 02 28 457x//前面6位:省份,市区,派出所等信息﹑第一位不能是0,后面5位是任意数字  [1-9]\\\\d{5}//年的前半段:18 19 20                                               (18|19|20)//年的后半段:任意数字出现两次                                        \\\\d{2}//月份: 01 ~ 09      10 11 12                                       (0[1-9]|1[0-2])//日期:  01 ~ 31  10~19 20~29 30 31                                 (0[1-9]|[12]\\\\d|3[01])//后面四位://任意数字出现3次最后一位可以是数字也可以是大写x或者小写x             \\\\d{3}[\\\\dXx]//编写正则的小心得://第一步:按照正确的数据进行拆分//第二步:找每一部分的规律,并编写正则表达式//第三步:把每一部分的正则拼接在一起,就是最终的结果书写的时候:从左到右去书写。String regex6 = "[1-9]\\\\d{5}(18|19|20)\\\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\\\d|3[01])\\\\d{3}[\\\\dXx]";String regex7 = "[1-9]\\\\d{5}(18|19|20)\\\\d{2}(0\\\\d|10|11|12)(0[1-9]|[1-2]\\\\d|30|31)\\\\d{3}[\\\\dXx]";System.out.println("41080119930228457x".matches(regex6));System.out.println("41080119930228457x".matches(regex7));}

小黑子—Java从入门到入土过程:第七章

小结(一些符号的作用):
小黑子—Java从入门到入土过程:第七章
小黑子—Java从入门到入土过程:第七章

2.7.4 爬虫

爬虫即在一段文本中查找满足的内容

使用的两个类:

  • Pattern:表示正则表达式

获取正则表达式的对象:

通过Pattern的静态方法complie获取正则表达式的对象
Pattern p=Pattern.compile(“Java\\d{0,2}”);

  • Matcher:文本匹配器,作用按照正则表达式的规则去读取字符串,从头开始读取,在大字符串中去找符合匹配规则的字串

1.通过Matcher获取文本匹配器的对象 Matcher m=p.matcher(str);(用m读取str,按照p的规则找里面的小串)

2.通过循环获取每一个满足条件的值,循环里通过Matcher的group方法获取具体字符串,循环的条件是由Matcher对象的find方法来判断循环是否停止

练习1:
小黑子—Java从入门到入土过程:第七章

ctrl + alt + m 快捷键 选中内容快捷包含生成一个方法

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public static void main(String[] args) {String str = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,"+"因为这两个是长期支持版木,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";//获取正则表达式的对象Pattern p = Pattern.compile("Java\\\\d{0,2}");//通过Pattern的静态方法complie获取正则表达式的对象//获取文本匹配器的对象//m:文本匹配器的对象//str:大串//p:规则//解释:m要在str中找符合p规则的小串Matcher m = p.matcher(str);//拿着文本匹配器从头开始读取,寻找是否有满足规则的子串//如果没有,方法返回false//如果有,返回true,在底层记录字串的起始索引喝结束索引+1//0,4boolean b = m.find();//方法底层会根据find方法记录的索引进行字符串的截取//subString(起始索引,结束索引);包头不包尾//(0,4)但是不包含4索引//会把截取的小串进行返回String s1 = m.group();System.out.println(s1);//第二次在调用find的时候,会继续读取后面的内容//读取到第二个满足要求的字串,方法会继续返回true//并把第二个子串的起始索引和结束索引+1,进行记录b = m.find();//第二次调用group方法的时候,会根据find方法记录的索引再次截取子串String s2 = m.group();System.out.println(s2);}

小黑子—Java从入门到入土过程:第七章

但是,这样写有个弊端,就是不能找到所有符合p正则条件的

修改:

 String str = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,"+"因为这两个是长期支持版木,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";//method1(str);//1.获取正则表达式的对象Pattern p = Pattern.compile("Java\\\\d{0,2}");//2.获取文本匹配器的对象//拿着m去读取str,找到符合p规则的字符Matcher m = p.matcher(str);//3.利用循环获取所以符合p规则的while(m.find()){String s = m.group();System.out.println(s);}

小黑子—Java从入门到入土过程:第七章
练习2:
小黑子—Java从入门到入土过程:第七章

import java.util.regex.Matcher;
import java.util.regex.Pattern;public class RegexDemo1 {public static void main(String[] args) {/*手机号正则表达式:1[3-9]\\d{9}邮箱正则表达式:\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}}){1,2}座机电话表达式:0\\d{2,3}-?[1-9]\\d{4,9}热线电话表达式:400-?[1-9]\\\\d{2}-?[1-9]\\\\d{3}*/String s ="来黑马程序员学习Java, "+"电话:18512516758,18512508907"+"或者联系邮箱: boniu@itcast.cn, " +"座机电话: 01036517895,010-98951256"+"邮箱: bozai@itcast.cn," +"热线电话: 400-618-9090 , 400-618-4080,4006184008,4006189090";String regex = "(1[3-9]\\\\d{9})" +"|(\\\\w+@[\\\\w&&[^_]]{2,6}(\\\\.[a-zA-Z]{2,3}}){1,2})" +"|(0\\\\d{2,3}-?[1-9]\\\\d{4,9})" +"|(400-?[1-9]\\\\d{2}-?[1-9]\\\\d{3})";//1.获取正则表达式的对象Pattern p = Pattern.compile(regex);//2.获取文本匹配器的对象//利用m去读取s,会按照p的规则找里面的小串Matcher m = p.matcher(s);//3.利用循环获取每一个数据while (m.find()){String str = m.group();System.out.println(str);}}}

小黑子—Java从入门到入土过程:第七章

一、带条件的数据爬取

小黑子—Java从入门到入土过程:第七章

import java.util.regex.Matcher;
import java.util.regex.Pattern;public class Main {public static void main(String[] args) {String s="Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11," +"因为这两个是长期支持版本,下一个长期支持版木是Java17,相信在未来不久Java17也会逐渐登上历史舞台";//1.定义正则表达式//?理解位前面的数据Java//=表示在Java后面要跟随的数据//但是在获取的时候,只获取前半部分//需求1:String regex1 = "((?i)Java)(?=8|11|17)";//需求2:String regex2 = "((?i)Java)(8|11|17)";String regex3 = "((?i)Java)(?:8|11|17)";//?:同样表示2的效果,选取中有的//需求3:String regex4 = "((?i)Java)(?!8|11|17)";Pattern p = Pattern.compile(regex1);Matcher m = p.matcher(s);while (m.find()){System.out.println(m.group());}}}

小黑子—Java从入门到入土过程:第七章

二、贪婪爬取和非贪婪爬取
  1. 贪婪爬取:在爬取时,尽可能多的爬取数据
  2. 非贪婪爬取:尽可能少爬取数据
    小黑子—Java从入门到入土过程:第七章

Java默认为贪婪爬取
如果我们在数量词+ *后加上问号,那么此时就是非贪婪爬取
小黑子—Java从入门到入土过程:第七章

import java.util.regex.Matcher;
import java.util.regex.Pattern;public class Main {public static void main(String[] args) {String s="Java自从95年问世以来,abbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa" +"经历了很多版本,目前企业中用的最多的是Java8和Java11," +"因为这两个是长期支持版本,下一个长期支持版木是Java17,相信在未来不久Java17也会逐渐登上历史舞台";String regex1 = "ab+?";String regex2 = "ab+";Pattern p1 = Pattern.compile(regex1);Matcher m1 = p1.matcher(s);Pattern p2 = Pattern.compile(regex2);Matcher m2 = p2.matcher(s);while(m1.find()){System.out.println(m1.group());}while(m2.find()){System.out.println(m2.group());}}}

小黑子—Java从入门到入土过程:第七章

2.7.5 正则表达式在字符串方法中的使用

小黑子—Java从入门到入土过程:第七章
如果说字符串方法的形参名字为regex,那么就能识别正则表达式
小黑子—Java从入门到入土过程:第七章
练习:

public static void main(String[] args) {String s="小实时jasndjasnoid1231小淡淡ndasjkldnan213131小灰灰";//细节://方法在底层跟之前一样也会创建文本解析器的对象//然后从头开始去读取字符串的内容,只要有满足的,那么久用第二个参数去替换String result = s.replaceAll("[\\\\w&&[^_]]+","vs");System.out.println(result);String[] arr = s.split("[\\\\w&&[^_]]+");for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}}

小黑子—Java从入门到入土过程:第七章

2.7.6 分组

分组就是一个小括号

与数学的计算相似
在正则表达式中,\\\\x(组号)就表示把第x组的数据再拿出来用一次

每组是有组号的 ,也就是序号

  • 规则1;从1开始,连续不间断
  • 规则2;以左括号为基准,最左边的是第一组,其次为第二组,依次类推
    小黑子—Java从入门到入土过程:第七章
(1)捕获分组

练习1:
小黑子—Java从入门到入土过程:第七章

    public static void main(String[] args) {//a123a b2324b c1234c d2131a(false)String regex1="(.).+\\\\1";//  (.)表示首字符可以是任意的//  .+表示在该中间是任意的//  \\\\1 表示把第1组的内容再拿出来用一次,也就是(.)里面的首字母,再后面也必须要相同System.out.println("a123a".matches(regex1));System.out.println("b2324b".matches(regex1));System.out.println("c1234c".matches(regex1));System.out.println("d2131a".matches(regex1));System.out.println("——————————————————————————————————————————————————");//abc213abcString regex2="(.+).+\\\\1";//  (.+)表示任意一个字符至少出现一次System.out.println("abc123abc".matches(regex2));System.out.println("bc2324bc".matches(regex2));System.out.println("csx1234csx".matches(regex2));System.out.println("dz2131ac".matches(regex2));System.out.println("——————————————————————————————————————————————————");//aaa123aaa//括起来两个括号之后,表示两组,\\\\1 就要变成 \\\\2了//这里的\\\\2:把第2组再拿出来再使用// *: 作用于\\\\2,表示是后面重复的内容出现0次或多次// 大括号的一个整体,后面的\\\\1表示拿出来一个整体用String regex3="((.)\\\\2*).+\\\\1";System.out.println("aaa123aaa".matches(regex3));System.out.println("aaa123aab".matches(regex3));}

小黑子—Java从入门到入土过程:第七章
小黑子—Java从入门到入土过程:第七章
练习2:
小黑子—Java从入门到入土过程:第七章

 public static void main(String[] args) {String str="我要学学编编编编编编编编程程程程程程程程程程程";//需求:把重复的内容替换为单个的学学//学学         学//编编编编      编//程程程程程程   程//(.)   表示把重复内容的第一个字符看作第一组// \\\\1  表示第一个字符再次出现// +   表示至少一次// $1  表示把正则表达式中第一组的内容,再拿出来用String result = str.replaceAll("(.)\\\\1+","$1");System.out.println(result);}

小黑子—Java从入门到入土过程:第七章

(2)非捕获分组

分组之后不需要再用本组数据,仅仅是把数据括起来,不占用组号,不能用\\\\组号来使用非捕获

小黑子—Java从入门到入土过程:第七章
上述表格就的具体案例就在带条件的数据爬取

小黑子—Java从入门到入土过程:第七章
小结:
小黑子—Java从入门到入土过程:第七章

2.8 JDK7时间

JDK7时间有三个类
Date:时间类
SimpleDateFormat: 格式化时间类
小黑子—Java从入门到入土过程:第七章

Calender: 日历类

2.8.1 Date 时间

时间的相关知识
小黑子—Java从入门到入土过程:第七章
小黑子—Java从入门到入土过程:第七章
小黑子—Java从入门到入土过程:第七章
Date的常用方法:

方法名 作用
public Date() 创建Date对象,表示当前时间
public Date(long date) 创建Date对象,表示指定时间
public void setTime(long time) 设置/修改毫秒值
public long getTime 获取时间对象的毫秒值

展示:
注意导包一定要点java.lang的
小黑子—Java从入门到入土过程:第七章

    public static void main(String[] args) {//1.创建对象表示一个时间Date d1 = new Date();System.out.println(d1);//2.创建对象表示一个指定的时间Date d2 = new Date(0L);//对于long类型的,可以加个LSystem.out.println(d2);//表示从时间原点开始过了0毫秒//3.setTime 修改时间d2.setTime(1000L);System.out.println(d2);//在时间原点上过了1000毫秒,也就是1秒//4.getTime获取当前时间的毫秒值long time = d2.getTime();//在左边基础上 ctrl + alt + vSystem.out.println(time);}

小黑子—Java从入门到入土过程:第七章
练习:
小黑子—Java从入门到入土过程:第七章

public static void main(String[] args) {Random r=new Random();//创建两个时间对象Date d2=new Date(Math.abs(r.nextInt()));Date d3=new Date(Math.abs(r.nextInt()));long time1 = d2.getTime();long time2 = d3.getTime();if(time1>time2){System.out.println("第一个时间在后面,第二个时间在前面");}else if(d2.getTime()< d3.getTime()){System.out.println("第二个时间在后面,第一个时间在前面");}else{System.out.println("表示两个时间一样");}method1();}private static void method1() {//1.创建一个对象,表示时间原点Date d1=new Date(0L);//2.获取的时间的毫秒值long time=d1.getTime();//3.在这个基础上我们要加一年的毫秒值即可time=time+1000L*60*60*24*365;//4.把计算之后的时间毫秒值,再设置回d1当中d1.setTime(time);System.out.println(d1);}

小黑子—Java从入门到入土过程:第七章
小结:
小黑子—Java从入门到入土过程:第七章