1、 DateFromat和SimpleDateFormat
2、 NumberFormat和DecimalFormat
3、 Arrays及二叉树排序原理
4、 观察者设计模式
5、 大数操作:BigInteger、BigDecimal,并且可以做到精确到指定的小数位
6、 Math及Random类
DateFormat与MessageFormat一样属于Format类的子类,此类专门用于格式化使用。
java.util.Date本身已经可以很好的指定出一个具体的日期,但是这样的日期在使用时的格式不是很理想,所以可以通过DateFormat进行指定格式的转换。
在DateFormat中存在以下的格式操作:
● public final String format(Date date):接收Date型数据变为String型数据
取得DateFormat类的实例:
● public static final DateFormat getDateInstance():根据默认环境取得
● public static final DateFormat getDateInstance(int style):指定显示样式取得
● public static final DateFormat getDateTimeInstance():取得日期时间
● public static final DateFormat getDateTimeInstance(int dateStyle, inttimeStyle)
范例:默认格式显示:
package org.lxh.dateformatdemo; import java.text.DateFormat; import java.util.Date; public class DateFormatDemo01 { public static void main(String[] args) { Date date = new Date(); DateFormat formatDate = DateFormat.getDateInstance(); DateFormat formatDatetime = DateFormat.getDateTimeInstance(); System.out.println(formatDate.format(date)); // 格式化日期 System.out.println(formatDatetime.format(date)); // 格式化日期 } } |
2012-4-30 2012-4-30 16:39:19 |
以上的操作形式实际上是将Date类型的格式进行了转换,当然,以上的操作是默认进行转换的,实际上也可以根据指定的风格进行转换。
package org.lxh.dateformatdemo;
import java.text.DateFormat; import java.util.Date;
public class DateFormatDemo02 { public static void main(String[] args) { Date date = new Date(); DateFormat formatDate = DateFormat.getDateInstance(DateFormat.FULL); DateFormat formatDatetime = DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL); System.out.println(formatDate.format(date)); // 格式化日期 System.out.println(formatDatetime.format(date)); // 格式化日期 } } |
2012年5月1日 星期二 2012年5月1日 星期二 上午12时00分01秒 CST |
SimpeDateFormat本身是DateFormat的子类,但是其主要功能有两个:
● 取得指定格式的日期及时间
● 进行日期格式化的转换操作
例如,现在有如下一个日期时间格式:
● 原格式:2012-05-01 12:20:01:345
● 转换后的格式:2012年05月01日 星期二 上午12时20分01秒345毫秒
面对这样的功能,就只能使用SimpleDateFormat类,在以上的两个日期中,所有的日期数字都是固定的,只是显示的格式不一样而已。如果想要进行日期格式化的转换,则首先应该将第一个日期的日期数字取出,在日期数字换上新的格式进行显示。
如果想要抠出日期,则肯定要先指定一个模版。
No |
日期 |
模版标记 |
长度 |
描述 |
1 |
年 |
y |
4 |
在操作的时候要使用yyyy表示年份 |
2 |
月 |
M |
2 |
使用MM表示月份 |
3 |
日 |
d |
2 |
使用dd表示日 |
4 |
时 |
H |
2 |
0~23的范围,使用HH表示时 |
5 |
分 |
m |
2 |
mm表示分 |
6 |
秒 |
s |
2 |
ss表示秒 |
7 |
毫秒 |
S |
3 |
SSS表示毫秒 |
所以,下面就可以利用以上的格式进行转换操作,格式在SimpleDateFormat实例化的时候使用。
● 构造方法:public SimpleDateFormat(String pattern),指定一个模版
如果要完成转换功能,则可以使用如下的方法:
● public Date parse(String source) throws ParseException
package org.lxh.dateformatdemo;
import java.text.SimpleDateFormat; import java.util.Date;
public class SimpleDateFormatDemo01 { public static void main(String[] args) throws Exception { String str = "2009-03-03 09:21:35.345"; SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS"); SimpleDateFormat sdf2 = new SimpleDateFormat( "yyyy年MM月dd日 hh时mm分ss秒SSS毫秒"); Date date = sdf1.parse(str);// 提取格式中的日期 String newStr = sdf2.format(date);// 改变格式 System.out.println("转换之后的日期:" + newStr); } } |
转换之后的日期:2009年03月03日 09时21分35秒345毫秒 |
在实际的开发中,许多的日期都是以文本的形式输入程序之中,则此时肯定要使用String进行接收,但是,如果在程序开发中要使用的肯定是Date型表示日期,所以此时就必须将String变为Date。
package org.lxh.dateformatdemo;
import java.text.SimpleDateFormat; import java.util.Date;
public class SimpleDateFormatDemo02 { public static void main(String[] args) throws Exception { String str = args[0].trim();// 接收参数 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS"); Date date = sdf.parse(str);// 提取格式中的日期 System.out.println(date); } } |
Wed May 02 09:50:23 CST 2012 |
在之前的程序基础上加入一个时间戳的操作,时间戳的概念如下:
● 例如有如下时间:2012-05-01 09:59:51.669
● 时间戳如下:20120501095951669
package org.lxh.dateformatdemo;
import java.text.SimpleDateFormat; import java.util.Date;
public class DateTime { private SimpleDateFormat sdf = null;
public String getDate() { // 2009-03-02 this.sdf = new SimpleDateFormat("yyyy-MM-dd"); String str = this.sdf.format(new Date()); return str; }
public String getDateTime() { // 2009-03-02 16:19:34.123 this.sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS"); String str = this.sdf.format(new Date()); return str; }
public String getDateComplete() { // 2009年03月02日 this.sdf = new SimpleDateFormat("yyyy年MM月dd日"); String str = this.sdf.format(new Date()); return str; }
public String getDateTimeComplete() { // 2009年03月02日16时19分34秒123毫秒 this.sdf = new SimpleDateFormat("yyyy年MM月dd日 hh时mm分ss秒SSS毫秒"); String str = this.sdf.format(new Date()); return str; }
public String getDateTimeStamp(){ this.sdf = new SimpleDateFormat("yyyyMMddhhmmssSSS"); String str = this.sdf.format(new Date()); return str; }
public static void main(String args[]) { DateTime dt = new DateTime() ; System.out.println(dt.getDateTimeComplete()); System.out.println(dt.getDateTime()); System.out.println(dt.getDateTimeStamp()); } } |
2012年05月01日 09时59分51秒668毫秒 2012-05-01 09:59:51.669 20120501095951670 |
主要是操作数学方法的,在Math类中提供了一系列的数学操作方法,所有的方法都是以static型的,所以使用时为了方便,可以采用static import。
package org.lxh.mathdemo;
public class MathDemo { public static void main(String[] args) { System.out.println("PI = " + Math.PI); System.out.println(Math.max(1, 2)); System.out.println(Math.round(89.9876)); // 四舍五入 } } |
PI = 3.141592653589793 2 90 |
但是此四舍五入操作是将小数点之后的全部内容进行四舍五入,并没有指定位数。
特别注意一下四舍五入方法:public static long round(double a)
package firstCourse; public class APIDemo { public static void main(String args[]) throws Exception { System.out.println("round(Math.round(15.55) --> " + Math.round(15.55)); System.out.println("round(Math.round(15.65) --> " + Math.round(15.65)); System.out.println("round(Math.round(-15.65) --> " + Math.round(-15.65)); System.out.println("round(Math.round(-15.55) --> " + Math.round(-15.55)); System.out.println("round(Math.round(-15.5) --> " + Math.round(-15.5)); System.out.println("round(Math.round(-15.6) --> " + Math.round(-15.6)); } } |
round(Math.round(15.55) --> 16 round(Math.round(15.65) --> 16 round(Math.round(-15.65) --> -16 round(Math.round(-15.55) --> -16 round(Math.round(-15.5) --> -15 round(Math.round(-15.6) --> -16 |
如果在进行四舍五入操作的时候,是负数,且小数点的内容不大于5的话会被忽略,大于5才会进位。
面试题:请说出如下的计算结果“Math.round(12.5)”、“Math.round(-12.5)”;
Math.round(12.5)→ 13
Math.round(-12.5)→ -12
使用Random可以取得一系列的指定范围的随机数。
范例:产生10个不大于100的整数。
package org.lxh.randomdemo;
import java.util.Random;
public class RandomDemo { public static void main(String[] args) { Random random = new Random(); for (int i = 0; i < 10; i++) { System.out.print(random.nextInt(100) + "、"); } } } |
89、89、8、48、87、68、71、30、80、78、 |
NumberFormat是Format的子类。Format三个子类都是国际化相关的。
在NumberFormat中包含一个子类:DecimalFormat,那么通过此类可以完成进一步的数字格式化操作。
从国际化的角度来看,不光只有语言需要进行国际化的操作,对于日期,货币及数字的显示也同样需要进行格式化的操作。
在区域和语言选项中可以清楚的发现这一点:
通过以下的方法完成数字的格式化操作:
● public final String format(double number)
通过以下的方法取得NumberFormat实例:
● public static final NumberFormat getInstance()
package org.lxh.numberformatdemo;
import java.text.NumberFormat;
public class NumberFormatDemo { public static void main(String args[]) { int temp = 1000010000; NumberFormat num = NumberFormat.getInstance() ; System.out.println(num.format(temp)); } } |
1,000,010,000 |
因为现在的区域是中国,所以数字的格式化上按每三位一个“,”的形式出现。
DecimalFormat是NumberFormat的子类,与SimpleDateFormat类似,在此类中也存在了一套模版的设置。
package org.lxh.numberformatdemo;
import java.text.DecimalFormat;
class FormatDemo { public void format(String pattern, double value) { // 此方法专门用于格式化 DecimalFormat format = new DecimalFormat(pattern); // 指定操作的模板 String str = format.format(value);// 转换操作 System.out.println("数字按照格式(" + pattern + ")的转换结果是:" + str); } }
public class DecimalFormatDemo { public static void main(String[] args) { FormatDemo format = new FormatDemo() ; format.format("000,000.000", 23456.45) ; format.format("###,###.###", 23456.45) ; format.format("000,000.000¥", 23456.45) ; format.format("###,###.###¥", 23456.45) ; format.format("##.###%", 0.34567) ; format.format("00.###%", 0.034567) ; } } |
数字按照格式(000,000.000)的转换结果是:023,456.450 数字按照格式(###,###.###)的转换结果是:23,456.45 数字按照格式(000,000.000¥)的转换结果是:023,456.450¥ 数字按照格式(###,###.###¥)的转换结果是:23,456.45¥ 数字按照格式(##.###%)的转换结果是:34.567% 数字按照格式(00.###%)的转换结果是:03.457% |
大数的操作指的是数字非常的大,大到已经超过了整个数据类型的保存范围,例如:
● 999999999999999999999999……999999999999999999999999
所以,此时就需要使用对象的形式进行操作,在最早如果碰到了以上的问题,实际上都是采用了字符串的形式进行处理的。
Java中为了解决这样的难题提供了两个大数字对象:BigInteger、BigDecimal。
BigInteger:大整数数据 |
BigDecimal |
java.lang.Object java.lang.Number java.math.BigInteger |
java.lang.Object java.lang.Number java.math.BigDecimal |
这两个类都是Number的子类,在Number类中定义了一系列的从包装类去除基本数据类型的方法,并且所有的数字包装类都是Number的子类。
BigInteger表示大的整形数据。
此类的定义:
public class BigInteger extends Number implements Comparable |
Number下还包括各个基本数据类型的包装类
package org.lxh.largenumberdemo;
import java.math.BigInteger;
public class BigIntegerDemo { public static void main(String[] args) { String num1 = "9999999999999999999999999999999999"; String num2 = "9999999999999999999999999999999998"; BigInteger big1 = new BigInteger(num1); // 实例化BigInteger对象 BigInteger big2 = new BigInteger(num2); // 实例化BigInteger对象 System.out.println("加法操作:" + big1.add(big2)); System.out.println("减法操作:" + big1.subtract(big2)); System.out.println("乘法操作:" + big1.multiply(big2)); System.out.println("除法操作:" + big1.divide(big2)); BigInteger result[] = big1.divideAndRemainder(big2);// 进行除法操作,有余数 System.out.println("相除之后的商是:" + result[0]); System.out.println("相除之后的余数是:" + result[1]); } } |
加法操作:19999999999999999999999999999999997 减法操作:1 乘法操作:99999999999999999999999999999999970000000000000000000000000000000002 除法操作:1 相除之后的商是:1 相除之后的余数是:1 |
大整数操作,一般只能完成一些基本数学计算,如果是一些太复杂的数学计算,只能按照字符串的位进行操作了。
BigDecimal类的主要功能是进行小数的大数计算,而且最重要的是可以精确到指定的四舍五入位数。
● 常量:public static final int ROUND_HALF_UP
● 构造:public BigDecimal(double val)
如果想要进行四舍五入的操作,则必须依靠除法进行::
● public BigDecimal divide(BigDecimal divisor, int scale, introundingMode)
|- scale:表示四舍五入的位数
|- roundingMode:四舍五入的操作模式,ROUND_HALF_UP
范例:完成四舍五入操作。
package org.lxh.largenumberdemo;
import java.math.BigDecimal;
class MyMath { public static double add(String num1, String num2) { BigDecimal bd1 = new BigDecimal(num1); BigDecimal bd2 = new BigDecimal(num2); return bd1.add(bd2).doubleValue(); }
public static double sub(String num1, String num2) { BigDecimal bd1 = new BigDecimal(num1); BigDecimal bd2 = new BigDecimal(num2); return bd1.subtract(bd2).doubleValue(); }
public static double mul(String num1, String num2) { BigDecimal bd1 = new BigDecimal(num1); BigDecimal bd2 = new BigDecimal(num2); return bd1.multiply(bd2).doubleValue(); }
public static double div(String num1, String num2, int scale) { BigDecimal bd1 = new BigDecimal(num1); BigDecimal bd2 = new BigDecimal(num2); return bd1.divide(bd2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); }
public static double round(double num, int scale) { BigDecimal bd1 = new BigDecimal(num); BigDecimal bd2 = new BigDecimal(1); return bd1.divide(bd2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } }
public class BigDecimalDemo { public static void main(String[] args) { String num1 = "12345.97891"; String num2 = "5334.5121"; System.out.println("加法操作:" + MyMath.round(MyMath.add(num1, num2), 2)); System.out.println("减法操作:" + MyMath.round(MyMath.sub(num1, num2), 2)); System.out.println("乘法操作:" + MyMath.round(MyMath.mul(num1, num2), 2)); System.out.println("除法操作:" + (MyMath.div(num1, num2, 2))); } } |
加法操作:17680.49 减法操作:7011.47 乘法操作:6.585977388E7 除法操作:2.31 |
克隆就是复制,可以将一个对象的内容完整的复制下来。
Object类提供以下的方法,完成对象的克隆:
● protected Object clone() throws CloneNotSupportedException
对于克隆操作并不是每一个对象都应该具备的,在java中只有部分对象才有可能进行克隆的操作,但是这部分对象必须有一个明确的说明。
如果希望被克隆的对象,那么其所在的类必须实现Cloneable接口,此接口没有定义任何的方法,所以此接口只是一个标识接口,表示一种能力。
package org.lxh.clonedemo;
class Person implements Cloneable { // 表示此类的对象可以被克隆 private String name;
public Person(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String toString() { return "姓名:" + this.getName(); }
/* * 此处必须覆写Object类的clone()方法, * 因为此方法属于protected访问权限。 */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
public class CloneDemo {
public static void main(String[] args) throws CloneNotSupportedException { Person per1 = new Person("张三"); Person per2 = (Person)per1.clone();//将Object类强制向下转型。 per2.setName("李四") ; System.out.println(per1) ; System.out.println(per2) ; } } |
姓名:张三 姓名:李四 |
Arrays是一个与数组操作相关的类,可以使用此类进行数组的排序操作。
package org.lxh.arraysdemo; import java.util.Arrays;
public class ArraysDemo { public static void main(String[] args) { int i1[] = { 1, 2, 3, 4, 5, 6 }; int i2[] = { 6, 5, 4, 3, 2, 1 }; Arrays.sort(i2); System.out.println(Arrays.equals(i1, i2)); Arrays.fill(i2, 3); // 将数组2的内容全部填充为3 System.out.println(Arrays.toString(i2));// 输出内容 } } |
true [3, 3, 3, 3, 3, 3] |
在Arrays类中定义了以下的一个方法,可以直接为一个对象数组进行排序。
● public static void sort(Object[] a)
定义一个学生类,学生类中包含编号、姓名、年龄、成绩,现在要求按照成绩进行排序,如果成绩相等,则按照年龄进行排序。
package org.lxh.comparesdemo;
import java.util.Arrays;
class Student { private int stuno; private String name; private int age; private float score;
public Student(int stuno, String name, int age, float score) { this.stuno = stuno; this.name = name; this.age = age; this.score = score; }
public String toString() { // 覆写toString() return "学生编号:" + this.stuno + ";姓名:" + this.name + ";年龄:" + this.age + ";成绩:" + this.score; } }
public class CompareDemo01 { public static void main(String[] args) { Student stu[] = { new Student(1, "张三", 21, 99.1f), new Student(2, "李四", 20, 99.1f), new Student(3, "王五", 21, 89.1f), new Student(4, "赵六", 21, 80.1f), new Student(5, "孙七", 19, 80.1f) }; System.out.println("============== 数组声明之前 ==============="); print(stu); System.out.println("============== 数组排序之后 ==============="); Arrays.sort(stu);// 排序 print(stu); }
public static void print(Student stu[]) { for (int i = 0; i < stu.length; i++) { System.out.println(stu[i]); } } } |
============== 数组声明之前 =============== 学生编号:1;姓名:张三;年龄:21;成绩:99.1 学生编号:2;姓名:李四;年龄:20;成绩:99.1 学生编号:3;姓名:王五;年龄:21;成绩:89.1 学生编号:4;姓名:赵六;年龄:21;成绩:80.1 学生编号:5;姓名:孙七;年龄:19;成绩:80.1 ============== 数组排序之后 =============== |
Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable at java.util.ComparableTimSort.countRunAndMakeAscending(Unknown Source) at java.util.ComparableTimSort.sort(Unknown Source) at java.util.ComparableTimSort.sort(Unknown Source) at java.util.Arrays.sort(Unknown Source) at CompareDemo01.main(CompareDemo01.java:35) |
以上的程序出现了类的转换异常。此时就需要读取DOC文档的说明。
在sort()方法中强调,如果要想实现对象数组的排序,则所有的元素都必须实现Comparable接口。
Comparable接口实际上属于比较器的操作接口,此接口定义如下:
public Interface Comparable int compareTo(T o){ //定义compareTo()方法,此方法完成排序操作 //比较代码 } } |
关于compareTo()这个方法应该并不陌生,曾经在String类中讲解过此方法的应用,当时的功能是用于进行字符串的大小比较,那么下面先来观察一下String类的定义:
public final class String extends Object implements Serializable,Comparable |
通过String的定义可以发现,此类也是Comparable接口的子类,所以String类中的compareTo()方法实际上就是覆写了Comparable接口中的compareTo()方法。
关于compareTo()方法返回值有三种类型:
● 小于:-1
● 等于:0
● 大于:1
范例:使用比较器完成功能。
package org.lxh.comparesdemo; import java.util.Arrays;
class Student implements Comparable private int stuno; private String name; private int age; private float score;
public Student(int stuno, String name, int age, float score) { this.stuno = stuno; this.name = name; this.age = age; this.score = score; }
public int compareTo(Student stu) { if (this.score > stu.score) { return -1; } else if (this.score < stu.score) { return 1; } else { if (this.age > stu.age) { return 1; } else if (this.age < stu.age) { return -1; } else { return 0; } } }
public String toString() { // 覆写toString() return "学生编号:" + this.stuno + ";姓名:" + this.name + ";年龄:" + this.age+ ";成绩:" + this.score; } }
public class Te { public static void main(String[] args) { Student stu[] = { new Student(1, "张三", 21, 99.5f), new Student(2, "李四", 20, 99.9f), new Student(3, "王五", 21, 89.1f), new Student(4, "赵六", 21, 80.1f), new Student(5, "孙七", 19, 80.6f) }; System.out.println("============== 数组声明之前 ==============="); print(stu); System.out.println("============== 数组排序之后 ==============="); Arrays.sort(stu);// 排序 print(stu); } public static void print(Student stu[]) { for (int i = 0; i < stu.length; i++) { System.out.println(stu[i]); } } } |
============== 数组声明之前 =============== 学生编号:1;姓名:张三;年龄:21;成绩:99.5 学生编号:2;姓名:李四;年龄:20;成绩:99.9 学生编号:3;姓名:王五;年龄:21;成绩:89.1 学生编号:4;姓名:赵六;年龄:21;成绩:80.1 学生编号:5;姓名:孙七;年龄:19;成绩:80.6 ============== 数组排序之后 =============== 学生编号:2;姓名:李四;年龄:20;成绩:99.9 学生编号:1;姓名:张三;年龄:21;成绩:99.5 学生编号:3;姓名:王五;年龄:21;成绩:89.1 学生编号:5;姓名:孙七;年龄:19;成绩:80.6 学生编号:4;姓名:赵六;年龄:21;成绩:80.1 |
通过以上的程序应该可以发现,如果以后要进行对象数组的排序,则对象数组所在的类必须实现Comparable接口,而且需要再次提醒的是,在类库中已经实现Comparable接口的有很多,例如:Integer、Character、java.util.Date,证明这些类的对象都是可以进行排序以及大小比较的。
在Comparable发现compareTo()方法中可以返回三种类型:-1、0、1,实际上此种排序就类似于常见的BT(Binary Tree)排序。
现在假设有如下几个数字:7、4、6、1、11、0、9、5、3,那么如果使用二叉树排序,则首先应该将第一个元素作为根节点,之后每个元素,如果比第一个元素小,则放在左子树,如果比第一个元素大则放在右子树,依次类推。
二叉树完成之后要使用中序遍历的方式完成:左— 根 — 右。
排序之后的数据:0、1、3、4、5、6、7、9、11
那么此时,实际上就可以通过此种代码并结合Comparable接口实现一个二叉树的算法。
但是在讲解之前,需要注意个问题,String类型或Integer类型的数据都可以使用Comparable进行接收。
package org.lxh.comparesdemo;
public class CompareableDemo02 { public static void main(String[] args) { Comparable // Comparable System.out.println(data); // 调用toString()是String类中的 } } |
hello |
既然要使用排序,则全部的数据都按照Comparable接收。
package org.lxh.comparesdemo;
class BinaryTree { // 定义二叉树的操作类 class Node { private Comparable data; // 保存操作的数据内容 private Node left; // 左子树 private Node right;// 右子树
public Node(Comparable> data) { this.data = data; }
public void addNode(Node newNode) { if (newNode.data.compareTo(this.data) <= 0) { // 放在左子树 if (this.left == null) { // 还没有左子树,可以直接保存在此节点下的左子树 this.left = newNode;// 保存左子树 } else { this.left.addNode(newNode);// 向下继续判断 } } if (newNode.data.compareTo(this.data) > 0) { // 放在右子树 if (this.right == null) { // 还没有右子树,可以直接保存在此节点下的右子树 this.right = newNode;// 保存右子树 } else { this.right.addNode(newNode);// 向下继续判断 } } }
public void printNode() { // 采用中序遍历 if (this.left != null) { // 存在左子树 this.left.printNode(); // 继续找到下面的左子树 } System.out.println(this.data); // 找到根内容 if (this.right != null) { // 存在右子树 this.right.printNode(); // 继续找到下面的右子树 } } }
private Node root; // 根节点
public void add(Comparable data) { // 接收数据 Node newNode = new Node(data); // 实例化节点类 if (this.root == null) { // 没有根节点 this.root = newNode; // 第一个节点作为根节点 } else { this.root.addNode(newNode); } }
public void print() { // 输出 this.root.printNode();// 输出全部的节点 } }
public class CompareableDemo03 { public static void main(String[] args) { BinaryTree bt = new BinaryTree() ; bt.add(3) ; bt.add(5) ; bt.add(1) ; bt.add(0) ; bt.add(1) ; bt.add(9) ; bt.print() ; } } |
0 1 1 3 5 9 |
对于此代码只需要了解其基本的排序原理即可。
Comparable接口在使用的时候直接在类中实现即可,那么如果现在一个类已经开发完成了,则肯定不能再去修改,所以,此时为了此类也具备排序的功能,可以使用Comparator接口完成排序的操作。先观察Comparator接口的定义:
public interface Comparator int compare(T o1, T o2); boolean equals(Object obj); } |
只要是类肯定会存在equal()方法,所以现在只关心compare()方法。
在Arrays类中提供了以下的排序方法:
● public static
范例:有如下的学生类。
package org.lxh.comparesdemo;
public class Student { private int stuno; private String name; private int age; private float score;
public Student(int stuno, String name, int age, float score) { this.stuno = stuno; this.name = name; this.age = age; this.score = score; }
public boolean equals(Object obj) { // 覆写equals,完成对象的比较 if (this == obj) { return true; } if (!(obj instanceof Student)) { return false; } Student stu = (Student) obj; // 向下转型 if (this.stuno == stu.stuno && this.name.equals(stu.name) && this.age == stu.age && this.score == stu.score) { return true; // 对象相等 } else { return false; // 对象不等 } }
public String toString() { // 覆写toString() return "学生编号:" + this.stuno + ";姓名:" + this.name + ";年龄:" + this.age+ ";成绩:" + this.score; }
public int getStuno() { return stuno; }
public String getName() { return name; }
public int getAge() { return age; }
public float getScore() { return score; } } |
本类并不具备排序的功能,所以,现在需要单独定义一个排序的操作类,使用Comparator完成,下面使用Comparator专门为Student类实现比较操作。
package org.lxh.comparesdemo;
import java.util.Comparator;
public class StudentComparator implements Comparator
public int compare(Student stu1, Student stu2) { if (stu1.getScore() > stu2.getScore()) { return -1; } else if (stu1.getScore() < stu2.getScore()) { return 1; } else { if (stu1.getAge() > stu2.getAge()) { return 1; } else if (stu1.getAge() < stu2.getAge()) { return -1; } else { return 0; } } }
public boolean equals(Object obj) { return this.equals(obj); } } |
下面就可以直接使用Arrays类提供的sort()方法进行排序的操作了。
package org.lxh.comparesdemo; import java.util.Arrays;
public class ComparatorDemo { public static void main(String[] args) { Student stu[] = { new Student(1, "张三", 21, 99.1f), new Student(2, "李四", 20, 99.1f), new Student(3, "王五", 21, 89.1f), new Student(4, "赵六", 21, 80.1f), new Student(5, "孙七", 19, 80.1f) }; System.out.println("============== 数组声明之前 ==============="); print(stu); System.out.println("============== 数组排序之后 ==============="); Arrays.sort(stu,new StudentComparator());// 排序 //StudentComparator在实现Comparator print(stu); }
public static void print(Student stu[]) { for (int i = 0; i < stu.length; i++) { System.out.println(stu[i]); } } } |
============== 数组声明之前 =============== 学生编号:1;姓名:张三;年龄:21;成绩:99.1 学生编号:2;姓名:李四;年龄:20;成绩:99.1 学生编号:3;姓名:王五;年龄:21;成绩:89.1 学生编号:4;姓名:赵六;年龄:21;成绩:80.1 学生编号:5;姓名:孙七;年龄:19;成绩:80.1 ============== 数组排序之后 =============== 学生编号:2;姓名:李四;年龄:20;成绩:99.1 学生编号:1;姓名:张三;年龄:21;成绩:99.1 学生编号:3;姓名:王五;年龄:21;成绩:89.1 学生编号:5;姓名:孙七;年龄:19;成绩:80.1 学生编号:4;姓名:赵六;年龄:21;成绩:80.1 |
在开发中明显使用Comparable最为简单,Comparator实际上只是作为一个挽救的操作出现,尽可能不要使用,因为要分为两个类,麻烦。
面试题:请解释Comparable和Comparator的区别?
1) 这两个都是用于进行排序规则指定的操作接口;
2) java.lang.Comparable,是在类定义的时候就实现好的,里面只有一个compareTo()方法;
3) java.util.Comparator,是类的排序功能的补救,指的是为一个已经开发好的类重新定义一个排序的规则,里面有两个方法:compara()、equal();
注意:以后只要看见一组对象的排序功能,不管是如何操作的,永远都会存在比较器,而比较器基本上只使用Comparable接口。
如果要想实现这样的这样的观察者设计,需要使用Observer接口和Observable类完成。
package org.lxh.obserdemo; import java.util.Observable; import java.util.Observer;
public class Person implements Observer { // arg表示改变之后的内容 // o表示观察的对象 public void update(Observable o, Object arg) { System.out.println(o + "*** 被观察的操作有所更改。" + arg); } } |
每一个人表示一个观察者,所有的观察者要观察Observable的子类。
package org.lxh.obserdemo; import java.util.Observable;
public class House extends Observable { private float price;
public House(float price) { this.price = price; } public String toString(){ return "房子" ; } public float getPrice() { return price; }
public void setPrice(float price) { super.setChanged();// 通知内容已经可以被修改了 this.price = price; // 一旦修改,则表示价格改变,那么价格改变之后,实际上应该立刻通知所有的观察者 super.notifyObservers(price);// 通知所有的观察者已经改变 } } |
当房子的价格被改变的时候,首先要设置改变的发生点,之后通过notifyObservers()方法,唤醒全部的观察者。
package org.lxh.obserdemo;
public class TestObserver { public static void main(String[] args) { House h = new House(3000.0f); Person per1 = new Person(); Person per2 = new Person(); Person per3 = new Person(); h.addObserver(per1);// 增加一个观察者 h.addObserver(per2);// 增加一个观察者 h.addObserver(per3);// 增加一个观察者 h.setPrice(6000.0f);// 要通知观察者内容已经被改变了 } } |
房子*** 被观察的操作有所更改。6000.0 房子*** 被观察的操作有所更改。6000.0 房子*** 被观察的操作有所更改。6000.0 |
反射机制可以说是Java的一大特色,例如,如果现在是一个普通用户操作的话,直接使用“对象.方法()”的形式即可完成所有的功能,而所谓的反射指的是可以采用不直接准确类对象的方式调用方法,也可以实现与之相同的功能,以及可以根据反射对类进行反操作。
在正常的情况下,肯定是需要先知道一个类的完整“包.类”之后再产生对象,如果收现在想反着来,通过一个给定的对象知道对象所属的“包.类”,那么就必须依靠Object类中的getClass()方法完成了。
package firstCourse;
public class APIDemo { public static void main(String[] args) { java.util.Date date = new java.util.Date(); System.out.println(date.getClass()); System.out.println(date.getClass().getName()); } } |
class java.util.Date java.util.Date |
现在可以通过对象反向回去了,而getClass()方法的定义如下:
Ÿ Object类定义的方法:public final Class> getClass()
可以发现getClass()方法所返回的是一个Class类的实例化对象,而且还有泛型的定义,但是这种在反射上使用的泛型基本上没有任何的用处,都使用“?”表示。
对于Class类而言,其本身属于所有反射机制操作的源头,所以这个类的对象有三种实例化的方式:
Ø 方式一:利用Object类中的getClass()方法完成;
Ø 方式二:利用“类.class”取得Class类的实例化对象;→在Hibernate中使用
Ø 方式三:利用Class类的一个static方法完成:
u public static Class> forName(StringclassName)
throws ClassNotFoundException
-- 此种方法将传入一个完整的“包.类”名称,取得Class类实例化对象
范例:使用方式二。
package firstCourse; public class APIDemo { public static void main(String[] args) { Class> cls = java.util.Date.class; System.out.println(cls.getName()); } } |
java.util.Date |
范例:使用方式三。
package firstCourse;
public class APIDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("java.util.Date"); System.out.println(cls.getName()); } } |
java.util.Date |
而这三种操作方法之中以forName()这种形式使用最多。
当取得一个实例化对象之后最大的好处是可以通过Class类的一个方法进行指定类的对象实例化操作:
n public T newInstance() throws InstantiationException,IllegalAccessException
范例:定义一个Vehicle类
package reflection; class Vehicle{ @Override public String toString() { return "Vehicle()类中的一个对象。"; } } |
使用new关键字实例化对象:
public class ReflectionDemo{ public static void main(String[] args) throws Exception { Vehicle car = new Vehicle(); System.out.println(car.toString()); } } |
Vehicle()类中的一个对象。 |
通过反射机制完成此操作:
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Vehicle"); Vehicle car = (Vehicle) cls.newInstance(); System.out.println(car); } } |
Vehicle()类中的一个对象。 |
通过以上的代码可以发现,使用反射操作可以替代掉关键字new实例化对象的过程。
对于现在对象的两种实例化方式,最常用的还是关键字new,但是反射机制的操作有其固定的应用范畴。
范例:回顾工厂设计模式
package reflection; interface Vehicle { public abstract String drive(); }
class Ferrari implements Vehicle { @Override public String drive() { return "开着法拉利"; } }
class Porsche implements Vehicle{ @Override public String drive() { return "开着保时捷"; } }
class VehicleFactory{ public static Vehicle getVehicle(String className){ if(className.equals("Ferrari")){ return new Ferrari(); } else if(className.equals("Porsche")){ return new Porsche(); } return null; } }
public class ReflectionDemo { public static void main(String[] args) throws Exception { Vehicle car = VehicleFactory.getVehicle("Porsche"); System.out.println(car.drive()); } } |
开着保时捷 |
这种传统的工厂设计模式有一个最大的问题:如果现在增加子类的话,一定要修改工厂类,那么如果说现在希望即使增加再多的子类,例如Lamborghini类,工厂类也可以不修改呢?那么就使用反射。
package reflection;
interface Vehicle { public abstract String drive(); }
class Ferrari implements Vehicle { @Override public String drive() { return "开着法拉利"; } }
class Porsche implements Vehicle { @Override public String drive() { return "开着保时捷"; } }
/* * 新增的Lamborghini类。 */ class Lamborghini implements Vehicle { @Override public String drive() { return "开着兰博基尼"; } }
class VehicleFactory { public static Vehicle getVehicle(String className) { Vehicle car = null; try { // 此处要处理异常 car = (Vehicle) Class.forName(className).newInstance(); /* * 上面语句等价于以下两个语句 * Class> cls = Class.forName(className); * car = (Vehicle) cls.newInstance(); */ } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return car; } }
public class ReflectionDemo { public static void main(String[] args) throws Exception { Vehicle car = VehicleFactory.getVehicle("reflection.Lamborghini"); System.out.println(car.drive()); } } |
开着兰博基尼 |
通过反射机制可以让程序开发更加灵活,因为使用反射方式实例化对象的时候需要传入完整的“包.类”的字符串名称即可。
当然,在以后使用框架开发的时候,如果发现一些配置文件之中写上了完整的“包.类”,而在程序中又可以直接使用此类对象的话,那么很明显就是利用反射完成的操作。
通过反射机制可以很方便的剖析一个类的组成结构,例如在Class类中定义了如下几个方法:
n 取得父类对象:public Class super T> getSuperclass()
n 取得全部的接口:public Class>[] getInterfaces()
范例:验证操作
package reflection;
interface Vehicle { }
class Car { }
class Ferrari extends Car implements Vehicle, Cloneable { }
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Ferrari"); System.out.println("父类:" + cls.getSuperclass().getName()); Class> clsi[] = cls.getInterfaces(); for (int i = 1; i <= clsi.length; i++) { System.out.println("接口" + i + ":" + clsi[i-1].getName()); } } } |
父类:reflection.Car 接口1:reflection.Vehicle 接口2:java.lang.Cloneable |
如果Ferrari类没有继承任何类,则getSuperclass()返回Object父类,因为所有类都默认继承Object类。
还有注意,这种操作本身对于普通的开发没有任何的帮助。
一个类中会定义多个构造方法,而如果要想取得全部的构造方法,可以使用Class类中的以下方法:
n 取得全部构造:public Constructor>[]getConstructors()
throws SecurityException
n 取得指定构造:public Constructor
throws NoSuchMethodException, SecurityException
我们观察Constructor的继承关系:
java.lang.Object java.lang.reflect.AccessibleObject java.lang.reflect.Constructor |
可以发现,此类是在reflect包中的。
范例:取得全部构造方法。
package reflection;
import java.lang.reflect.Constructor;
class Ferrari { private String owner; private double price;
public Ferrari() { }
public Ferrari(String owner, double price) { this.owner = owner; this.price = price; }
@Override public String toString() { return "车主:" + this.owner + ",价格:" + this.price; } }
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Ferrari"); Constructor> cons[] = cls.getConstructors(); for (int i = 0; i < cons.length; i++) { System.out.println(cons[i]); //等价于下面语句 //System.out.println(cons[i].toString()); } } } |
public reflection.Ferrari() public reflection.Ferrari(java.lang.String,double) |
在之前已经学习过了,使用Class类可以进行对象的实例化操作,那么如果现在的Ferrari类变为以下的形式呢:
class Ferrari { private String owner; private double price;
// 把无惨构造方法去掉 public Ferrari(String owner, double price) { this.owner = owner; this.price = price; }
@Override public String toString() { return "车主:" + this.owner + ",价格:" + this.price; } }
|
本类现在已经没有构造方法了,那么还能使用Class类反射实例化对象吗?
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Ferrari"); System.out.println(cls.newInstance()); } } |
Thread [main] (Suspended (exception InstantiationException)) Class Class ReflectionDemo.main(String[]) line: 24 |
可以发现,现在出错了。此错误表示实例化错误,实际上如果使用了Class类进行对象的实例化操作,所调用的构造方法还是无参的,而如果现在类中没有无参构造方法的话,则应该使用Constructor类提供的另外一个方法完成:
n 实例化对象:public T newInstance(Object... initargs)
throwsInstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
如果现在类中没有无参构造方法则通过反射实例化对象的流程如下:
n 使用Class类的getConstructor()方法,取得一个指定参数个数的构造;
n 利用Constructor类中newInstance(…)方法传递指定的参数,并实例化对象。
范例:通过Class类和Constructor类来完成有参构方法的实例化。
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Ferrari"); Constructor> cons[] = cls.getConstructors(); System.out.println(cons[0].newInstance("Wolex", 5698000)); } } |
车主:Wolex,价格:$5698000.0 |
很明显这种操作要比之前的代码更加的麻烦,所以之所以在之前建立简单Jave类的时候一直强调类中必须有无参构造方法,其目的就在于此。
在一个类中会定义许多的普通方法,这些普通方法如果想要去的则可以使用Class类的如下操作:
n 取得全部方法:public Method[] getMethods() throwsSecurityException
n 取得一个方法:public Method getMethod(String name, Class>...parameterTypes)
throwsNoSuchMethodException, SecurityException
既然返回类型是Method类,那先来观察一下Method类的继承关系:
java.lang.reflect |
java.lang.Object java.lang.reflect.AccessibleObject java.lang.reflect.Method |
和Constructor()一样,都属于java.lang.reflect包。
范例:定义一个Porsche类。
package reflection;
import java.lang.reflect.Constructor; import java.lang.reflect.Method;
class Porsche { public void drive() { // Porsche类中无参无返回值的方法 System.out.println("哥正开着保时捷!"); }
public String getSpeed() { // 无参,有返回值 return "320 km/h"; }
public String calculateTime(String startAddress, String destination) { // 有参,有返回值 return startAddress + " TO " + destination + ":45 minutes"; } } |
利用Method类来取得其全部方法,当然,这些方法应该包含了继承而来的。
实现一:简单的列出类中的全部方法
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Porsche"); Method allMethod[] = cls.getMethods(); for (int i = 0; i < allMethod.length; i++) { System.out.println(allMethod[i]); } } } |
public void reflection.Porsche.drive() public java.lang.String reflection.Porsche.getSpeed() public java.lang.String reflection.Porsche.calculateTime(java.lang.String, java.lang.String) public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() |
此时已经列出了Porsche类中的全部方法,但是此时的方法列出格式是由Method类内部所固定好的。
下面将通过手工的方法来取出Porsche类的方法。在此之前先观察Method类的一个方法:
n 返回整形数字modifier:public int getModifiers()
而Modifier本身也是一个类,观察其定义:
java.lang.reflect Class Modifier |
java.lang.Object java.lang.reflect.Modifier |
此类中的成员属性和方法都属于static的,关于每个constant表示哪个访问权限,可以查阅文档获得。下面要使用的是Modifier类其中的一个方法:
n 返回访问权限:public staticString toString(intmod) → mod指示modifier数值
实现二:手工实现方法的全部信息取得
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Porsche"); Method allMethod[] = cls.getMethods(); for (int i = 0; i < allMethod.length; i++) { System.out.print(Modifier.toString(allMethod[i].getModifiers()) + " "); System.out.print(allMethod[i].getReturnType().getName() + " "); System.out.print(allMethod[i].getName()); System.out.print("("); Class> param[] = allMethod[i].getParameterTypes(); for (int j = 0; j < param.length; j++) { System.out.print(param[j].getName() + " arg-" + j); if (j < param.length - 1) { // 后面还有内容 System.out.print(","); } } System.out.print(")"); Class> exp[] = allMethod[i].getExceptionTypes(); if (exp.length > 0) { System.out.print(" throws "); for (int j = 0; j < exp.length; j++) { System.out.print(exp[j].getName()); if (j < exp.length - 1) { System.out.print(","); } } } System.out.println(); // 换行 } } } |
public void drive() public java.lang.String getSpeed() public java.lang.String calculateTime(java.lang.String arg-0,java.lang.String arg-1) public final void wait() throws java.lang.InterruptedException public final void wait(long arg-0,int arg-1) throws java.lang.InterruptedException public final native void wait(long arg-0) throws java.lang.InterruptedException public native int hashCode() public final native java.lang.Class getClass() public boolean equals(java.lang.Object arg-0) public java.lang.String toString() public final native void notify() public final native void notifyAll() |
本程序对于普通的开发没有任何的用处,但是对于开发工具的开发就很有用处了,因为有一个随笔提示功能,就是按照此种方法实现的。
对于之前的所有操作,可以发现,一个类产生实例化对象之后,通过“对象.方法()”调用类中的若干操作,实际上这个功能完全可以由反射来实现,而在Method类中定义了如下一个方法:
n 调用方法:public Object invoke(Objectobj, Object... args)
throwsIllegalAccessException,
IllegalArgumentException,
InvocationTargetException
范例:通过反射调用Porsche类中的无参,无返回方法
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Porsche"); Object obj = cls.newInstance(); // 调用方法必须有实例化对象 Method met = cls.getMethod("drive"); // 找到方法 met.invoke(obj);// 调用方法 } } |
哥正开着保时捷! |
范例:调用无参,有返回值的方法
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Porsche"); Object obj = cls.newInstance(); // 调用方法必须有实例化对象 Method met = cls.getMethod("getSpeed"); // 找到方法 Object ref = met.invoke(obj); //调用方法 System.out.println(ref); /* * 上面两条语句等价于: * System.out.println(met.invoke(obj)); */ } } |
速度:320 km/h |
范例:调用有参的,有返回值的
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Porsche"); Object obj = cls.newInstance(); // 调用方法必须有实例化对象 Method met = cls.getMethod("calculateTime", String.class, String.class); // 找到方法 Object ref = met.invoke(obj, "London", "Paris"); // 调用方法 System.out.println(ref); } } |
London TO Paris:45 minutes |
在之前编写简单Java类的时候一直强调,类中的属性必须封装,封装之后的属性必须通过getter、setter设置和取得,而且必须有自己严格的方法命名规范。
package reflection;
import java.lang.reflect.Method;
class Porsche { private String model;
public String getModel() { return "型号:" + model; }
public void setModel(String model) { this.model = model; }
}
public class ReflectionDemo { public static void main(String[] args) throws Exception { String attribute = "model"; // 要操作的是name属性 Class> cls = Class.forName("reflection.Porsche"); Object obj = cls.newInstance(); // 调用方法必须有实例化对象 Method setMet = cls.getMethod("set" + initcap(attribute), attribute.getClass()); setMet.invoke(obj, "CAYENNE II"); Method getMet = cls.getMethod("get" + initcap(attribute)); System.out.println(getMet.invoke(obj)); }
public static String initcap(String str) { return str.substring(0, 1).toUpperCase() + str.substring(1); } } |
型号:CAYENNE II |
希望以后如果在开发之中,发现用户传递完整的“包.类”,或者是传递属性的名称,实际上就表示此类操作要通过反射完成。
对于类中的属性取得依然使用Class类所提供的功能,但是此时的属性分为两种情况:
n 取得继承而来的全部属性:public Field[] getFields() throws SecurityException
n 取得继承而来的指定属性:public Field getField(String name)
throws NoSuchFieldException, SecurityException
n 取得本类定义的全部属性:public Field[] getDeclaredFields() throws SecurityException
n 取得本类定义的指定属性:public Field getDeclaredField(String name)
throwsNoSuchFieldException, SecurityException
范例:取得一个类的全部属性
package reflection;
import java.lang.reflect.Field;
interface Vehicle { public static final String MASSAGE = "Traffic Tool"; }
abstract class Car implements Vehicle { private String name; //改为public下面方法才能取得 }
class Lamborghini extends Car { private String owner; private double price; }
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Lamborghini"); { // 代码块 Field all[] = cls.getFields(); for (int i = 0; i < all.length; i++) { System.out.println(all[i].getName()); } // 这里获取不了抽象类Car中的name属性,因为访问权限为private } { Field all[] = cls.getDeclaredFields(); for (int i = 0; i < all.length; i++) { System.out.println(all[i].getName()); } } } } |
MASSAGE owner price |
在Field类中定义了两个方法:
n 设置属性:public void set(Object obj, Object value)
throwsIllegalArgumentException, IllegalAccessException
n 取得属性:public Object get(Object obj)
throwsIllegalArgumentException, IllegalAccessException
还要了解的是AccessibleObject类,先观察其定义:
java.lang.Object java.lang.reflect.AccessibleObject |
Direct Known Subclasses: Constructor, Field, Method |
此类也属于java.lang.reflect包,可见Field类继承了此类,所以拥有此类的方法,其中继承过来的setAccessible(…)方法可以对获取的成员属性设置是否封装。
n 取消封装:public static void setAccessible(AccessibleObject[] array, booleanflag)
throwsSecurityException
范例:直接通过反射操作私有属性
package reflection;
import java.lang.reflect.Field;
class Lamborghini { private String owner; }
public class ReflectionDemo { public static void main(String[] args) throws Exception { Class> cls = Class.forName("reflection.Lamborghini"); Object obj = cls.newInstance(); Field ownerField = cls.getDeclaredField("owner");// 找到owner属性 ownerField.setAccessible(true); // 取消封装 ownerField.set(obj, "Wolex");// 等价于:对象.属性 = 内容 System.out.println(ownerField.get(obj)); } } |
Wolex |
本程序只是作为一个娱乐进行了基本的说明,根本就不会使用,因为对于属性的访问而言,已经有了明确的标准,必须通过setter、getter。
在类库的学习之中,以下的几个程序必须清楚:
1、 StringBuffer类的使用;
2、 Date、SimpleDateFormat;
3、 比较器:Comparable;
4、 四舍五入操作BigDecimal;
5、 正则符号以及String对正则的支持,讲解的所有题目都要求会独立完成;
6、 通过反射也可以实现对象的实例化,关注无参的构造实现即可。