Java系列(十六)__Java常用类库(2)
如果说现在有两个非常大的数字(超过了double范围)要进行数学的加法计算,你该怎么做?可以使用字符串保存数字,而后按照每个字符进行手工的处理数学计算。但是这样的复杂度非常的高,为此,在Java里面专门提供了一个java.math包,此包之中提供有两个大数字操作类:BigInteger(大整数操作类)、BigDecimal(大小数操作类)。
1、 大整数操作类:BigInteger
BigInteger是Number的子类,但是在使用的时候肯定不用转为Number类型,在BigInteger类之中定义的构造方法:public BigInteger(String val),因为数据量一大,肯定使用String表示;
范例:完成四则运算
package cn.mldn.demo; import java.math.BigInteger; public class TestDemo { public static void main(String[] args)throws Exception { BigInteger bigA = new BigInteger("4238903289023809238097890") ; BigInteger bigB = new BigInteger("98234789234") ; System.out.println(bigA.add(bigB)); System.out.println(bigA.subtract(bigB)); System.out.println(bigA.multiply(bigB)); System.out.println(bigA.divide(bigB)); BigInteger result [] = bigA.divideAndRemainder(bigB) ; System.out.println("商:" + result[0] + "、余数:" + result[1]); System.out.println(bigA.pow(Integer.MAX_VALUE)); } } |
以上只是针对于BigInteger类做了一个演示,但是这样的代码意义不大,而且也没使用的环境,如果你在工作之中,如果真遇见了数学问题,一定要去找数学公式第三方开发包。
2、 大小操作类:BigDecimal
此类的操作与BigInteger类基本是相同的,也提供有基本的数学计算,但是与这些数学计算相比,BigDecimal类有一个非常重要的功能。
Math类之中的round()方法进行四舍五入操作过程之中,采用的是所有小数点全部进位,不能保留小数位,但是很多时候这样的做法是不可取,例如:如果某家公司的年收入是按照亿计算,今年收获了5.321亿美金,按照Math.round()结果就成了5亿,所以Math.round()是一个不可能使用的操作,这个时候只能够利用BigDecimal类完成。
在BigDecimal类里面提供有一个除法操作:public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
· 第一个参数:被除数,此处也应该是BigDecimal类型;
· 第二个参数:保留的小数位数;
· 第三个参数:进位模式(public static final int ROUND_HALF_UP)。
范例:实现准确的四舍五入操作
package cn.mldn.demo; import java.math.BigDecimal; class MyMath {
public static double round(double num,int scale) { BigDecimal bigNum = new BigDecimal(num) ; // 只有变为BigDecimal才可以调用divide()方法 BigDecimal result = bigNum.divide(new BigDecimal(1), scale, BigDecimal.ROUND_HALF_UP); return result.doubleValue() ; } } public class TestDemo { public static void main(String[] args)throws Exception { System.out.println(MyMath.round(7809.652198, 2)); System.out.println(MyMath.round(7809.652198, 1)); System.out.println(MyMath.round(-13.5, 0)); } } |
此代码是在工作上使用最多的四舍五入操作模式,如果有需要代码直接使用。
“java.util.Arrays.sort()”是一个数组排序操作,实际上这一个操作就是调用了java.util包中Arrays类的sort()方法完成的,而Arrays是一个数组操作的工具类(数组一般使用较少,所以此工具类意义不大)。
范例:使用Arrays类
package cn.mldn.demo; import java.util.Arrays; public class TestDemo { public static void main(String[] args)throws Exception { int dataA[] =new int[] { 1, 2, 3 }; int dataB[] =new int[] { 1, 2, 3 }; System.out.println(Arrays.equals(dataA, dataB)); Arrays.fill(dataA, 9); System.out.println(Arrays.toString(dataA)); } } |
随着数组的使用减少,此类的操作也越来越少。
在之前接触过了Arrays类,那么下面来观察在Arrays类之中存在的一个方法:
· 对象数组排序:public static void sort(Object[] a);
范例:实现排序
package cn.mldn.demo; import java.util.Arrays; class Person { private String name ; private int age ; public Person(String name,int age) { this.name = name ; this.age = age ; } public String toString() { return "姓名:" +this.name + ",年龄:" + this.age ; } } public class TestDemo { public static void main(String[] args)throws Exception { Person per[] = new Person[] { new Person("张三", 20), new Person("李四", 18), new Person("王五", 25) }; Arrays.sort(per); // 排序 for (int x = 0; x < per.length; x++) { System.out.println(per[x]); } } } |
Exception in thread "main" java.lang.ClassCastException: cn.mldn.demo.Person cannot be cast to java.lang.Comparable |
现在发现此时需要让Person类实现Comparable接口,而这就是一个比较器。
java.lang.Comparable接口是一个最为常用的比较器,那么此接口定义如下:
public interface Comparable { public int compareTo(T o) ; } |
在Comparable接口之中发现只有一个compareTo()方法,而此方法可以返回三种类型的数据:-1(小)、0(等)、1(大)。
在之前学习String类的时候也学习过compareTo()方法,当时的方法是可以进行大小判断的。
范例:使用比较器
package cn.mldn.demo; import java.util.Arrays; class Person implements Comparable { private String name ; private int age ; public Person(String name,int age) { this.name = name ; this.age = age ; } public String toString() { return "姓名:" +this.name + ",年龄:" + this.age ; } @Override public int compareTo(Person o) { if (this.age > o.age) { return 1 ; } else if (this.age < o.age) { return -1 ; } return 0; } } public class TestDemo { public static void main(String[] args)throws Exception { Person per[] = new Person[] { new Person("张三", 20), new Person("李四", 18), new Person("王五", 25) }; Arrays.sort(per); // 排序 for (int x = 0; x < per.length; x++) { System.out.println(per[x]); } } } |
以后只要是一组对象的大小比较一定使用Comparable接口。
既然已经知道了对象之间的大小比较操作,那么就可以利用此概念实现二叉树开发,二叉树的基本原则如下:
· 一个节点下可以保存两个节点,分别称为左子树和右子树;
· 取第一个数据作为根节点,比根节点小的数据要放在左子树上,比根节点大的数据要放在右子树上;
· 在进行输出的时候按照中序遍历(左-中-右)进行数据的取出。
范例:下面将利用Comparable实现二叉树操作
· 大小关系利用Comparable接口的compareTo()方法实现。
package cn.mldn.test; class Person implements Comparable { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return "姓名:" +this.name + ",年龄:" + this.age; } @Override public int compareTo(Person o) { if (this.age > o.age) { return 1; } else if (this.age < o.age) { return -1; } return 0; } } @SuppressWarnings("rawtypes") class BinaryTree { private class Node { // 一定要存在节点类 private Comparable data; // 必须排序的是Comparable接口对象 private Node left; // 左子树 private Node right; // 右子树 public Node(Comparable data) { this.data = data; } @SuppressWarnings("unchecked") public void addNode(Node newNode) { if (this.data.compareTo(newNode.data) > 0) { if (this.left == null) { this.left = newNode ; }else { this.left.addNode(newNode); } } else { if (this.right == null) { this.right = newNode ; }else { this.right.addNode(newNode); } } } public void toArrayNode() { if (this.left != null) { // 有左子树 this.left.toArrayNode(); } BinaryTree.this.retData[BinaryTree.this.foot++] =this.data; if (this.right != null) { this.right.toArrayNode(); } } } // ====================================== private Node root; // 根节点 private int count ; // 统计对象的保存个数 private int foot ; // 操作脚标 private Object [] retData ; public void add(Comparable data) { if (data ==null) { return; } Node newNode = new Node(data); // 将新的数据变为一个节点 if (this.root ==null) { // 根节点没有数据 this.root = newNode ; // 新的节点将作为根节点存在 } else { // 要保存在合适的节点中 this.root.addNode(newNode); } this.count ++ ; // 保存个数加一 } public Object[] toArray() { if (this.root ==null) { return null ; } this.foot = 0 ; this.retData =new Object [this.count] ; // 以保存大小开辟数组 this.root.toArrayNode(); return this.retData ; } } public class BinaryTreeDemo { public static void main(String[] args) { BinaryTree bt = new BinaryTree() ; bt.add(new Person("张三",20)); bt.add(new Person("李四",18)); bt.add(new Person("王五",25)); Object [] data = bt.toArray() ; for (int x = 0; x < data.length; x++) { System.out.println(data[x]); } } } |
如果日后笔试之中有人问:请写个二叉树,李老师讲过,我不会。
在工作之中如果使用到比较器,肯定首选的是Comparable,但是Comparable有一个特点,它必须在一个类定义的时候就实现好接口,可是如果有这样一种情况,一个类已经开发完成了,并且此类不允许再做出任何修改,此类在先期设计的时候没有考虑对象数组的排序问题,后期又提出了此问题。但是代码已经不能变更,那么这个时候如何排序呢?
范例:开发好的类
class Person { private String name ; private int age ; public Person(String name,int age) { this.name = name ; this.age = age ; } public String toString() { return "姓名:" +this.name + ",年龄:" + this.age ; } public String getName() { return name; } public int getAge() { return age; } } |
Person类已经开发完成,并且打包后交给客户使用了,但是后来需要进行对象数组排序,那么这个时候由于Person类无法修改了,那么只能够采用另外一种比较器:java.util.Comparator接口,此接口定义如下。
public interface Comparator { public int compare(T o1, T o2) ; public boolean equals(Object obj) ; } |
范例:定义专门的比较规则
class PersonComparator implements Comparator { @Override public int compare(Person o1, Person o2) { if (o1.getAge() > o2.getAge()) { return -1 ; } else if (o1.getAge() < o2.getAge()) { return 1 ; } return 0; } } |
此类只能够为Person类服务。随后如果要排序的时候在Arrays类有如下一个方法:
· 排序:public static void sort(T[] a, Comparator
离散数学是正则的理论基础,有想弄明白的,可以去研究一下。正则是一种简化的验证手段,例如,现在完成这样的一个代码。
范例:要求判断一个字符串是否由数字组成
· 思路:将字符串变为字符数组,而后每个字符进行依次判断。
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String num = "12a3" ; System.out.println(isNumber(num)); } public static boolean isNumber(String str) { char [] data = str.toCharArray() ; for (int x = 0; x < data.length; x++) { if (data[x] > '9' || data[x] < '0') { return false ; } } return true ; } } |
但是这样一个非常简单或者说是简单到极点的验证写出了八行代码,如果再复杂一点呢?代码量会更多。
范例:利用正则操作
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String num = "123" ; System.out.println(num.matches("\\d+")); } } |
发现如果在验证操作上使用正则符号进行匹配,则代码量可以非常少,而“"\\d+"”就是正则表达式。
正则表达式最早是在Linux下发展起来的,在JDK 1.4之前,如果需要使用到正则那么要通过apache下载正则的应用包。而从JDK 1.4开始,Java正式支持了正则的开发,同时给出了一个java.util.regex开发包,提供正则的操作类。
但是需要注意的是,在java.util.regex包里面只提供有两个核心类:
· java.util.regex.Pattern:正则标记编译类;
· java.util.regex.Matcher:正则匹配操作;
而在开发之中很少去直接使用以上的两个类,一般都使用String类完成,因为从JDK 1.4之后开始,String类之中多了正则的支持,同时提供了以下支持正则的开发方法。
No. |
方法名称 |
类型 |
描述 |
1 |
public boolean matches(String regex) |
普通 |
正则验证 |
2 |
public String replaceAll(String regex, String replacement) |
普通 |
全部替换 |
3 |
public String replaceFirst(String regex, String replacement) |
普通 |
替换首个 |
4 |
public String[] split(String regex) |
普通 |
全拆分 |
5 |
public String[] split(String regex, int limit) |
普通 |
部分拆分 |
下面的讲解以String类之中几个操作方法为主。
如果要操作正则,那么必须使用一系列的正则匹配符号,而所有的正则匹配符号都在java.util.regex.Pattern类提供。
1、 表示单个字符(每出现一个只表示一位)
· x:表示只能由x组成;
· \\:表示转义字符“\”;
· \t:匹配“tab”键;
· \n:匹配换行;
2、 表示字符的选用范围(每出现一个只表示一位)
· [abc]:表示可能是a、b、c中的任意一个;
· [^abc]:表示不是a、b、c中的任意一个;
· [a-zA-Z]:表示任意的一位字母,可能是大写或小写;
· [0-9]:表示任意的一位数字;
3、 简化表达式(每出现一个只表示一位)
· .:表示任意的一位字符;
· \d:表示是一位数字,相当于“[0-9]”;
· \D:表示不是一位数字, 相当于“[^0-9]”;
· \s:表示是任意的一位空格;
· \S:表示不是任意的一位空格;
· \w:表示是大小写字母、数字、_中的任意一位,相当于“[a-zA-Z0-9_]”;
· \W:表示不使大小写字母、数字、_中的任意一位,相当于“[^a-zA-Z0-9_]”;
4、 边界匹配
· ^:写在正则之前,表示正则开始;
· $:写在正则最后,表示正则结束;
5、 数量表示:如果没有数量,每一个出现的标记只能表示一位
· 正则?:表示此正则规范只能够出现0次或1次;
· 正则*:表示此正则出现0次、1次或多次;
· 正则+:表示此正则出现1次或多次;
· 正则{n}:表示此正则出现正好n次;
· 正则{n,}:表示此正则出现n次以上;
· 正则{n,m}:表示此正则出现n ~ m次;
6、 逻辑操作符
· 正则X正则Y:表示正则X判断后执行正则Y;
· 正则X|正则Y:表示两个正则二选一;
· (正则):包含多个正则,成为一组;
正则的所有操作方法都通过String类完成,所以下面将通过一些代码,通过String来操作正则。
范例:字符串替换
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String num = "fajkljkl32890*()jlk3809F*)(&w(*r&)(*4379RF8SD09" ; String regex = "[^a-zA-Z]" ; System.out.println(num.replaceAll(regex, "")); } } |
范例:字符串拆分
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String num = "a1b22c333d44444e5555555f66666666g" ; String regex = "\\d+" ; String result [] = num.split(regex) ; for (int x = 0; x < result.length; x++) { System.out.println(result[x]); } } } |
范例:现在用户名由字母、数字、_所组成,要求其范围在6~15位
· 正则:[a-zA-Z0-9_] = \w;
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String num = "hello123" ; String regex = "\\w{6,15}" ; System.out.println(num.matches(regex)); } } |
范例:现在某个学校的学生成绩要求按照“姓名:年龄:生日:成绩”,那么这样的数据可以出现多条,多条纪录之间使用“|”分隔,例如“SMITH:20:1990-09-15:90.9|ALLEN:19:1991-11-11:89.6|TONY:21:1989-07-28:100”。
那么下面首先要进行问题的拆分,因为这个验证之中包含有许多的小规则:
· 规则一:验证姓名,不设置长度限制(一位或多位,使用+表示);
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String str = "SMITH" ; String regex = "[a-zA-Z]+" ; System.out.println(str.matches(regex)); } } |
· 规则二:验证年龄,年龄是一个数字,长度范围1 ~ 3位;
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String str = "19" ; String regex = "\\d{1,3}" ; System.out.println(str.matches(regex)); } } |
· 规则三:验证生日,生日是数字组合
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String str = "1998-08-01" ; String regex = "\\d{4}-\\d{2}-\\d{2}" ; System.out.println(str.matches(regex)); } } |
· 规则四:验证小数,成绩最多3位,小数位最多2位。
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String str = "98.12" ; String regex = "\\d{1,3}(\\.\\d{1,2})?" ; System.out.println(str.matches(regex)); } } |
· 组合验证规则:
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String str = "SMITH:20:1990-09-15:90.9" ; String regex= "[a-zA-Z]+:\\d{1,3}:\\d{4}-\\d{2}-\\d{2}:\\d{1,3}(\\.\\d{1,2})?" ; System.out.println(str.matches(regex)); } } |
· 满足于多组信息的操作;
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String str = "SMITH:20:1990-09-15:90.9|ALLEN:19:1991-11-11:89.6|TONY:21:1989-07-28:100" ; String regex = "([a-zA-Z]+:\\d{1,3}:" + "\\d{4}-\\d{2}-\\d{2}:" + "\\d{1,3}(\\.\\d{1,2})?\\|?)+" ; System.out.println(str.matches(regex)); } } |
范例:验证email地址,email地址的验证规则如下:
· 用户名只能够以字母、数字、_、.组成;
· 用户名只能够以字母开头;
· 用户名的长度要求在6~20位之间;
· 域名只能是:.com、.cn、.net、.com.cn、.net.cn、.edu、.gov;
package cn.mldn.demo; public class TestDemo { public static void main(String[] args)throws Exception { String str = "[email protected]" ; String regex = "[a-zA-Z][a-zA-Z0-9_\\.]{5,19}" + "@[a-zA-Z-0-9]+\\." + "(com|cn|net|gov|edu|com\\.cn|net\\.cn)" ; System.out.println(str.matches(regex)); } } |
虽然从感觉上看这些符号组合比较麻烦,但是习惯了就好,可是应该知道正则的出现让代码简化了不少,尤其是针对于验证操作。
我们在正常的使用过程之中只关心类产生对象,而后通过对象调用类之中的方法,可是,除了这种传统的方式外,也可以利用反射操作。
既然有反,那么一定存在有正,正是指的通过类产生对象,而后通过对象执行操作。而反呢?通过对象找到他所在的类的信息。所有的对象都支持反这一操作,因为Object类有一个方法:public final Class