Java常用类
阅读原文时间:2021年04月20日阅读:1

1、StringBuffer类

1.1、认识StringBuffer类
首先我们知道String类的一个特性是String的内容一旦声明则不可改变,如果改变,则改变的的是String的引用地址,对于经常该百年的字符串使用String性能极差,此时我们可以使用StringBuffer类。

1.2、StringBuffer常用方法
StringBuffer支持的方法大部分与String类似。
因为StringBuffer在开发中可以提升代码性能,所以使用较多。

1.2.1、字符串连接操作 append()

1.2.2、在字符串指定位置添加字符 insert()

1.2.3、字符串反转操作 reverse()

1.2.4、替换字符串指定范围的内容 replace()

1.2.5、字符串截取 substring()

1.2.6、删除指定范围的字符串 delete()

1.2.7、查找指定的内容是否存在 indexOf()
测试代码:

/**
 * stringBuffer常用方法
 * 
 * @author guai
 *
 */

public class StringBufferMethond {
    // @Test
    // 字符串连接操作 append()
    public void methond1() {
        StringBuffer buf = new StringBuffer();
        buf.append("guai");
        buf.append(" is ");
        buf.append(18).append(" ages");
        System.out.println("字符串连接方法测试:" + buf);
    }

    // @Test
    // 在字符串指定位置添加字符 insert()
    public void methond2() {
        StringBuffer buf = new StringBuffer();
        buf.append("guai");
        buf.insert(0, "Hello");
        System.out.println("在字符串指定位置添加字符测试:" + buf);
    }

    // @Test
    // 字符串反转操作 reverse()
    public void methond3() {
        StringBuffer buf = new StringBuffer();
        buf.append("guai");
        buf.reverse();
        System.out.println("字符串反转操作测试:" + buf);
    }

    // @Test
    // 替换字符串指定范围的内容 replace()
    public void methond4() {
        StringBuffer buf = new StringBuffer();
        buf.append("guai");
        buf.replace(0, 1, "sh");
        System.out.println("替换字符串指定范围的内容测试:" + buf);
    }

    // @Test
    // 字符串截取 substring()
    public void methond5() {
        StringBuffer buf = new StringBuffer();
        buf.append("guai");
        String str = buf.substring(0, 3);
        System.out.println("字符串截取测试:" + str);
    }

    // @Test
    // 删除指定范围的字符串 delete()
    public void methond6() {
        StringBuffer buf = new StringBuffer();
        buf.append("guai");
        buf.delete(3, 4);
        System.out.println("删除指定范围的字符串测试:" + buf);
    }

    // @Test
    // 查找指定的内容是否存在 indexOf()
    public void methond7() {
        StringBuffer buf = new StringBuffer();
        buf.append("guai");
        // 结果为-1表示没有找到指定内容否则反之
        System.out.println("查找指定的内容是否存在测试:" + buf.indexOf("ui"));
        System.out.println("查找指定的内容是否存在测试:" + buf.indexOf("ai"));
    }
}

2、Runtime类

1.1、认识Runtime类
在Java中Runtime类表示运行时操作类,是一个封装了JVM进程的类,每一个JVM都对应着一个Runtime类的实例,此实例由JVM运行时为其实例化。所以在JDK文档中没有Runtime类中构造方法的定义,这是因为Runtime类本身的构造方法是私有化的(单例设计),如果要创建一个Runtime实例,只能通过下面的方法:
Runtime run=Runtime.getRuntime();
也就是说在Runtime类中提供了一个gtetRuntime()静态方法,此类可以取得Runtime类的实例,那么获取Runtime的实例有啥用处呢?
既然Runtime表示每一个JVM实例,所以可以通过Runtime取得一些系统性信息。

1.2、Runtime常用方法:

1.2.1、观察JVM的内存
JCVM最大内存: maxMemory()
JVM当前空余内存:freeMemory()
垃圾回收: gc()

1.2.2、执行本机可执行程序
运行可执行程序:exec()
关闭运行程序:destory()
通过exec()可以启动一个系统可执行程序,查看exec方法的返回值可以发现,其返回值为Process,表示一个操作系统进程的类,我们可以使用该类对启动的程序进行管理。比如关闭比操作。

测试方法:
  @Test
    //执行本机可执行程序  运行可执行程序:exec() 关闭运行程序:destory(
    public void methond2() {
        Runtime runtime = Runtime.getRuntime();
        Process pro = null;

        try {
               //打开程序
            pro=runtime.exec("notepad.exe");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //关闭进程
        pro.destroy();
    }

3、国际化程序

国际化操作指一个程序可以适应多种语言。

3.1、实现思路
根据不同的国家配置不同的资源文件(属性文件.properties),所有资源文件以**“key-value”**的形式出现,在程序执行中只要根据key找到value就可以显示value中的内容,以后只要key不变,value中的内容可以任意更换。
若要实现Java程序的国际化操作,必须使用以下3个类完成。
1)java.util.Locale:用于表示一个国家语言类
2)java.util.ResourceBundle:用于访问资源文件。
3)java.text.MessageFormat:格式化资源文件的占位字符串。

操作流程:通过Local类所指定的区域码,然后ResourceBundle根据Local类所指定的区域码找到相应的资源文件,如果资源文件中存在动态文本,则使用MessageForamt进行格式化操作。

Locale类:
方法定义: public Locale(String language) 构造方法 作用: 根据语言代码构造一个语言环境
方法定义: public Locale(String language,String country) 构造方法 作用: 根据语言和国家构造一个语言环境
ResourceBundle类:
常用方法:
public static final ReaourceBundle getBundle(String baseName) 作用:取得ResourceBundle的实例,并指定要操作的资源文件名称;
public static final ReaourceBundle getBundle(String baseName,Local local) 作用:取得Resource Bundle的实例,并指定要操作的资源文件名称和区域码;
public final String getString(String key) 作用:根据key从资源文件中取出对应的value;
MessageFormat类:
用到的方法:
public static String format(String pattern,Object…arguments);

3.2、实现
3.2.1、创建静态文本
属性文件:

下面列出了一个中文的属性文件,中文在属性文件中显示的时Unicode编码

注意:属性文件的名称需要按照要求命名即:名称_国家代码格式 且多个语言属性文件的名称需要一样。例:language_zh_CN 其中 _zh_CN表示中文中国
测试代码:

@Test
    //非动态文本
    public void staticProcessing() {
        //表示国家语言
        Locale zhLoc = new Locale("zh", "CN");
        Locale enLoc = new Locale("en", "US");
        Locale frLoc = new Locale("fr", "FR");

        //加载属性文件
        ResourceBundle zhrb = ResourceBundle.getBundle("com.shuai.ChapterEleven.languageProcessing.Language", zhLoc);
        ResourceBundle enrb = ResourceBundle.getBundle("com.shuai.ChapterEleven.languageProcessing.Language", enLoc);
        ResourceBundle frrb = ResourceBundle.getBundle("com.shuai.ChapterEleven.languageProcessing.Language", frLoc);
                                    //通过键值对读取数据
        System.out.println("中文:" + zhrb.getString("info"));
        System.out.println("英文:" + enrb.getString("info"));
        System.out.println("法文:" + frrb.getString("info"));
    }

结果:

3.2.2、动态文本
动态文本指在属性文件中加入占位符(可以加入多个{0}{1}…{n})再通过MessageFormat类的format方法将需要添加的内容填充到占位符的位置上。
属性文件:

测试代码:

@Test
    // 动态文本
    public void dynamicProcessing() {
        // 表示国家语言
        Locale zhLoc = new Locale("zh", "CN");
        Locale enLoc = new Locale("en", "US");
        Locale frLoc = new Locale("fr", "FR");

        // 加载属性文件
        ResourceBundle zhrb = ResourceBundle.getBundle("com.shuai.ChapterEleven.languageProcessing.Language", zhLoc);
        ResourceBundle enrb = ResourceBundle.getBundle("com.shuai.ChapterEleven.languageProcessing.Language", enLoc);
        ResourceBundle frrb = ResourceBundle.getBundle("com.shuai.ChapterEleven.languageProcessing.Language", frLoc);
        // 通过简直对读取数据
        String str1=zhrb.getString("info");
        String str2=enrb.getString("info");
        String str3=frrb.getString("info");
                                                                //拼接文本
        System.out.println("中文:" + MessageFormat.format(str1,"乖"));
        System.out.println("英文:" + MessageFormat.format(str2,"guai"));
        System.out.println("法文:" + MessageFormat.format(str3,"guai"));
    }

结果:

4、System类

4.1、System类是一些与系统相关属性和方法的集合,而且在System类中所有方法都是静态的,要想引用这些属性和方法,直接使用System类调用即可。

4.2、常用方法:
void exit(int status) 作用 :系统退出,如果status为非0就表示退出
void gc() 运行垃圾回收机制,调用的就是Runtime类中的gc方法
long currentTimemillis() 返回以毫秒为单位的当前时间
void arraycopy(Object src,int srcPos,Object dest,int destPos,int length) 数组复制操作
Properties getProperties() 取得当前系统的全部属性
String getProperty(String key) 根据键值取的属性的具体内容

4.3、部分方法测试:

/**
 * system类的常用方法
 * 
 * @author guai
 *
 */
public class SystemMethod {
    // @Test
    // long currentTimemillis() 返回以毫秒为单位的当前时间
    public void currentTimeMillisTest() {
        Long startTime = System.currentTimeMillis();
        int sum = 0;
        for (int i = 0; i < 1000000000; i++) {
            sum += i;
        }
        Long endTime = System.currentTimeMillis();
        System.out.println("消耗时间:" + (endTime - startTime) + "毫秒");
    }

    // @Test
    // Properties getProperties() 取得当前系统的全部属性
    public void getPropertiesTest() {
        System.getProperties().list(System.out);
    }

    // @Test
    // String getProperty(String key) 根据键值取的属性的具体内容
    public void getPropertyTest() {
        System.out.println("系统版本为:" + System.getProperty("os.name") + System.getProperty("os.version")
                + System.getProperty("os.arch") + System.getProperty("user.home"));
    }

    //垃圾对象的回收 例
    class Person{
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
        //对象释放空间时默认调用此方法
        public void finalize()throws Throwable{
            System.out.println("对象释放"+this);
        }
    }

    @Test
    public void gcTest() {
        Person per=new Person();
        per.setName("guai");
        //断开引用 释放空间
        per=null;
        //强制释放空间
        System.gc();
    }
}

5、Date类

Date类是一个较为简单的操作类,在使用中直接使用 java.util.Date类的构造方法并进行输出就可以得到一个完整的日期,构造方法定义如下:
public Date();
使用:
System.out.println(“当前日期”+new Date());

6、Calendar类

Calendar类可以取得的时间精确到毫秒。但是这个类本身是一个抽象类,若想使用一个抽象类,则必须依靠对象的多态性,通过子类进行父类的实例化操作,Calendar的子类时GregorianCalendar类。

6.1、Calendar类中的常量和方法
常量:fianl int YEAR ,MONTH, DAY_OF_MONTH,HOUR_OF_DAY,MINUTE,SECOND,MILLISECOND
方法:
public static Calendar getInstance() 根据默认时区实例化对象
boolean after(Object when)判断一个日期是否在指定日期之后
boolean before(Object when)判断一个日期是否在指定日期之前
int get(int field) 返回给日历字段的值
getTimeInMillis():返回从格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000到Calendar对象表示的时间之间的毫秒数。

7、DateFormat类

格式化日期格式
7.1、常用方法
final DateFormat getDateInstace()获得默认对象
final DateFormat getDateInstance(int style,Locale aLocale)根据Locale得到对象
final DateFormat getDateTimeInstance()得到日期对象
final DateFormat getDateTimeInstance(int dateStyle,int timeStyle,Locale aLocale)根据Locale得到日期时间对象

7.2、测试:
1)

@Test
    public void getDateMethod() {
        DateFormat df1=DateFormat.getDateInstance();
        DateFormat df2=DateFormat.getDateTimeInstance();

        System.out.println("DATE: "+df1.format(new Date()));
        System.out.println("DATETIME: "+df2.format(new Date()));
    }

结果:

2)

@Test
    public void getDateMethod1() {
        DateFormat df1=DateFormat.getDateInstance(DateFormat.YEAR_FIELD,new Locale("zh","CN"));
        DateFormat df2=DateFormat.getDateTimeInstance(DateFormat.YEAR_FIELD,DateFormat.ERA_FIELD,new Locale("zh","CN"));

        System.out.println("DATE: "+df1.format(new Date()));
        System.out.println("DATETIME: "+df2.format(new Date()));
    }

结果:

8、SimpleDateFormat类

在开发中,经常将一个日期格式转化为另一种日期格式,例将2020-02-02 02:02:02转化为2020/02/02 02:02:02或2020年02月02日 02时02分02秒。此时可以是使用SimpleDateFormat类完成。
常用方法:
SimpleDateFormat(Stringpattern) 构造 通过指定的模板构造对象
Date parse(String source)throws ParseException 将一个表示日期的字符串变为Date类型
final String format(Date date) 将一个Date类型,按照格式变为String类型

测试方法:

@Test
    public void testMethod() {
        try {
            //将字符串中的日期提取出来
            Date date=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2020-02-11 12:51:22");
            System.out.println(date);
            System.out.println("格式后:"+new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒").format(date));
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }       
    }

9、Calendar类与SimpleDateFormat的结合应用

9.1、 获取当前日期并对年月日进行加减操作

// 对获取到的当前日期中的年月日进行加减操作
    //@Test
    public void testMethod() {
        // 获取当前日期
        Calendar calendar = Calendar.getInstance(new Locale("zh", "CN"));
        // 格式化当前日期
        System.out.println("原始日期:" + new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()));
        // 当前日期的年份减一
        calendar.add(Calendar.YEAR, -1);
        // 当前日期的月份减一
        calendar.add(Calendar.MONTH, -1);
        // 当前日期的天数减一
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        System.out.println("修改后的日期" + new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()));

        // 当前日期的年份加一
        calendar.add(Calendar.YEAR, 1);
        // 当前日期的月份加一
        calendar.add(Calendar.MONTH, 1);
        // 当前日期的天数加一
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        System.out.println("修改后的日期" + new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()));

    }

结果:

9.2、获取两个时间之间的时间间隔
//获取两个时间之间的时间间隔

@Test
    public void testMethod1() {
        //获取两个时间之间间隔的小时
        Calendar calendar=Calendar.getInstance();
        Calendar calendar2=Calendar.getInstance();
        calendar2.add(Calendar.HOUR_OF_DAY, -12);

        System.out.println("calendar: "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar.getTime()));
        System.out.println("calendar2: "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar2.getTime()));

        //getTimeInMillis():返回从格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000到Calendar对象表示的时间之间的毫秒数
        long startTime=calendar.getTimeInMillis();
        long endTime=calendar2.getTimeInMillis();
        System.out.println("calendar: "+startTime);
        System.out.println("calendar2: "+endTime);
        double hour=((startTime-endTime)/1000.0/60/60);
        System.out.println("两个日期间隔的小时数:"+hour);

        double day=((startTime-endTime)/1000.0/60/60/24.0);
        System.out.println("两个日期间隔的天数: "+day);
    }


结果:

10、Math类

Math类是数学操作类,提供了一系列的数学操作方法,包括绝对值、三角函数等,在Math类中提供的方法都是静态方法。
基本方法:
Math.sqrt(9.0) 9开根号
max(10,32)
min(10,30)
pow(2,3) 求2的3次方
round(33.6) 四舍五入
abs(-10) 求绝对值

11、Random类

boolean nextBoolean() 随机生成一个boolean值
double nextDouble() 随机生成一个double值
float nextFloat()随机生成一个float值
int nextInt()随机生成一个int值
int nextInt(int n) 随机生成一个给定的最大int值
long nextLong() 随机生成一个long值

12、NumberFormat类

NumberFormat用于对数字的格式化,根据不同的国家习惯进行格式化
常用方法:
static Locale[]getAvailableLocales() 返回所有语言环境的数组
static final NumberFormat getInstance() 返回当前默认语言环境的数字格式
static NumberFormat getInstance(Locale inLocale) 返回指定语言环境的数字格式
static final NumberFormat getCurrencyInstance() 返回当前默认环境的货币格式
static NumberFormat getCurrencyInstance(Locale inLocale) 返回指定语言环境的数字格式

13、DecimalFormat 类

格式化数字主要用于自定义格式的使用
测试:

/**
 * 自定义格式化数字
 * @author guai
 *
 */
class FormatDemo{
    public void format1(String pattern,double value) {
        DecimalFormat df=null;
        df=new DecimalFormat(pattern);
        String str=df.format(value);
        System.out.println("使用:"+pattern+" 格式化数字: "+value+": "+ str);
    }
}
public class DecimalFormatMethod {
    /**
     * 0 表示数字,每一个0表示以为数字,如果该位不存在则显示0
     * #表示数字,如果不存在则不显示
     * .小数点分隔符或货币的小数分隔符
     * ,分组分隔符
     * %前缀或后缀数字乘以100并显示为百分数
     */
    @Test
    public void testMethod() {
        FormatDemo fm=new FormatDemo();
        fm.format1("###,###.###", 111222.34567);
        fm.format1("000,000.000", 11222.34567);
        fm.format1("###,###.###¥", 11222.34567);
        fm.format1("000,000.000¥", 11222.34567);
        fm.format1("##.###%", 0.123456);
        fm.format1("00.##%", 0.0345678);
        fm.format1("###.###\u2030", 0.345678);
    }
}

结果:

14、BigInteger类

当一个数字非常大时,则无法使用基本数据类型接受,所以,最早碰到大数字时往往使用String接收,然后采用拆分的方式进行计算,但较为麻烦,在Java中为了解决这样的难题提供了BigInteger类,BigInteger表示大整数类,定义在java.math包中。

14.1、BigInteger常用方法:
BigInteger(String val) 构造方法,将一个字符串变为BigInteger类型
BigInteger add(BigInteger val) 加法
BigInteger subtract(BigInteger val) 减法
BigInteger multiply(BigInteger val ) 乘法
BigInteger divide(BigInteger val) 除法
BigInteger max(BigInteger val) 返回连个数字中的最大值
BigInteger min(BigInteger val) 返回连个数字中的最小值
BigInteger [] divideAndRemainder(BigInteger val) 触发操作,数组的第一个元素为除法的商,第二个元素为除法的余数

15、BigDeciaml类

对于不需要准去计算计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDeciaml类,而且使用BigDecimal类也可以进行大数的操作。

15.1、BigDecimal类常用方法
BigDecimal (double val) 将double类型转换为BigDecimal
BigDecimal(int val) …
BigDecimal(String val) …
BigDecimal add(BigDecimal augend) …
BigDecimal subtract(BigDecimal subtrahend) 减法
BigDecimal multiply(BigDecimal multiplicand) 乘法
BigDecimal divide(BigDecimal divisor) 除法

测试方法:

/**
 * BigDecimal 高精度计算
 * @author guai
 *
 */

public class BigDecimalMethod {
    @Test
    public void testMethod() {
        BigDecimal bd=new BigDecimal(12.256);
        BigDecimal bd1=new BigDecimal(12.256);
        BigDecimal result=bd.multiply(bd1);

        System.out.println("double:"+12.256*12.256);
        System.out.println("result:" +result);
        //除一并保留3位小数的四舍五入
        System.out.println("BigDecimal 四舍五入:"+ (result.divide(new BigDecimal(1),3,BigDecimal.ROUND_HALF_UP)));
        System.out.println("BigDecimal保存可长的数 并运算"+new BigDecimal("211321321231541541541541541413121131241465465454").add(new BigDecimal(1)));
    }
}

结果: 精度确实比double高

16、对象克隆技术

在java中支持对象克隆操作,直接使用Object类中的clone()方法即可,方法定义如下
protected Object clone() throws CloneNoSupportException
以上方法是受保护的类型,所以在子类中必须覆写此方法,而且覆写之后应该扩大访问群贤,才可以被外部调用,但是具体的克隆方法的实现还是在Object中,所以在覆写的方法中只需要调用Object类中的clone()方法即可完成操作,而且在对象所在的类中必须实现Cloneable接口才可以完成对象的克隆操作。

测试:

/**
 * 对象克隆
 * @author guai
 *
 */

//实现Cloneable接口
class Person implements Cloneable{
    private String name;

    public Person(String name) {
        super();
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    //覆写clone方法
    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }

    @Override
    public String toString() {
        return "Person [name=" + name + "]";
    }

}
public class ObjectCloneMethod {
    @Test
    public void testMethod() throws CloneNotSupportedException {
        Person p1=new Person("guai");
        Person p2;
        p2=(Person) p1.clone();
        Person p3=p1;
        System.out.println("对象克隆:p1:" +p1+" 克隆对象p2:"+p2);
        System.out.println("克隆对象与直接赋值方法的区别");
        System.out.println("直接赋值:" +p1.equals(p3));
        System.out.println("对象克隆:" +p1.equals(p2));
    }
}

结果:

直接赋值属于引用传递, p1,p3指向同一块内存区域
而对象克隆则开辟了新的内存空间,p1和p2指向的不是同一块内存区域。

17、Arrays类

Arrays 类是数组的操作类,定义在java.util包中,主要功能是实现数组元素的查找、数组内容的填充、排序等
常用方法:
static boolean equals(int[]a,int[]a2) 判断两个数组是否相等,此方法被重载多次,可以判断各种数组类型的数组
static void fill(int[]a,int cal) 将指定内容填充到数组中,此方法被重载多次、、、、
static void sort(int []a) 数组排序,此方法被重载多次、、、、
static int binarySearch(int[] a,int key) 对排序后的数组进行检索,此方法被重载多次、、、
static String toString(int[] a) 输出数组信息,此方法被重载多次、、、
例:

/**
 * Arrays数组操作类
 * @author guai
 *
 */
public class ArraysMethod {
    @Test
    public void testMethod() {
        int temp[]= {3,5,7,9,45,2};
        Arrays.sort(temp);
        System.out.println("排序后的数组: "+Arrays.toString(temp));

        int point=Arrays.binarySearch(temp, 5);
        System.out.println("3在数组中的位置: "+point);

        Arrays.fill(temp, 3);
        System.out.println("数组填充: "+Arrays.toString(temp));
    }
}

结果:

18、Comparable 接口

在Arrays中可以使用sort()方法对数组进行排序操作,而且该方法被重载多次可以对任意类型的数组排序,排列时会根据数据的大小进行排序。同样此方法也可以对Object类数组进行排序,但是使用此方法排序需要对象所在的类必须实现Comprarble接口,此接口就是用于指定对象排序规则的。

18.1、Comparable接口的定义如下:

public interface Comparable<T>{
    public int comparaTo(T o);
}

从定一中可以发现,在Comparable接口中使用了Java泛型技术。其中只有一个comparaTo()方法,此方法返回一个int类型的数据,当时此int的值只能是以下三种:
1:表示大于
-1:表示小于
0:表示相等

18.2、应用:

 /**
 * Comparable 接口的应用
 * 
 * @author guai
 *
 */

// 实现Cloneable接口
class Student implements Comparable<Student> {
    private String name;

    private float score;

    public float getScore() {
        return score;
    }
    public void setScore(float score) {
        this.score = score;
    }
    public Student(String name, float score) {
        super();
        this.name = name;
        this.score = score;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student [name=" + name + ", score=" + score + "]";
    }

    // 覆写comparaTor()方法,自定义排序规则  根据学生成绩对数组排序
    @Override
    public int compareTo(Student stu) {
        // TODO Auto-generated method stub
        if (this.score > stu.score) {
            return 1;
        } else if (this.score == stu.score) {
            return 0;
        } else {
            return -1;
        }
    }
}

public class ComparableApply {

    @Test
    public void testMethod() {
        Student st[] = { new Student("guai", 99), new Student("shuai", 100), new Student("cabbage", 99.9f) };
        //排序
        Arrays.sort(st);
        System.out.println("排序后的数组: " + Arrays.toString(st));

    }
}

结果:

18.3、分析比较器的排序原理
Arrays.sort()排序过程就是数据结构中的二叉树排序算法,通过二叉树进行排序,然后利用中序遍历的方式把内容依次读取出来。
二叉树排序的基本原理就是:将第一个内容作为根节点保存,如果后面的值比根节点的值小,则放在根节点的左子树,如果后面的值比根节点大,则放在根节点的右子树。
按照这样的思路根据中序遍历的原理(左子树->根节点->右子树)即可将数字排序读取出来。

下面按照以上思路编写一个简单的二叉树排序操作。
因为Integer类已经实现Comparable接口,为了简化代码可直接使用Integer类。
由下面代码可见,Comparable接口可直接通过Integer对象实例化,然后直接输出Comparable接口对象时,实际调用的时Integer的toString()方法,我们下面将使用Comparable接口完成二叉树。

二叉树:

/**
 * 比较器排序原理分析之二叉树
 * 
 * @author guai
 *
 */

class BinaryTree{
    // 定义内部类 二叉树节点
    class Node {

        private Comparable data;
        private Node left;
        private Node rigth;

        // 添加节点

        public void AddNode(Node newNode) {
            // 通过Integer实例化comparable然后调用在Integer中实现好的comparaTo方法
            if (newNode.data.compareTo(this.data) < 0) {
                if (this.left == null) {
                    this.left = newNode;
                } else {
                    this.left.AddNode(newNode);
                }
            } else if (newNode.data.compareTo(this.data) >= 0) {
                if (this.rigth == null) {
                    this.rigth = newNode;
                } else {
                    this.rigth.AddNode(newNode);
                }
            }
        }

        // 中序遍历二叉树
        public void printNode() {
            //遍历左子树
            if (this.left != null) {
                this.left.printNode();
            }
            //輸出根节点
            System.out.print(this.data + "\t");
            //遍历右子树
            if(this.rigth!=null) {
                this.rigth.printNode();
            }
        }
    };

    //根节点
    private Node root;
    public void add(Comparable data) {
        //每传入一个元素就声明一个根节点
        Node newNode=new Node();
        newNode.data=data;

        if(root==null) {
            root=newNode;
        }else {
            root.AddNode(newNode);
        }
    }
    //输出节点
    public void print() {
        this.root.printNode();
    }
}

public class BinaryTreeDemo {

    @Test
    public void testMethod() {
        BinaryTree bt=new BinaryTree();
        bt.add(4);
        bt.add(0);
        bt.add(222);
        bt.add(3);
        bt.add(90);
        bt.add(33);
        bt.add(1);
        bt.add(23);
        bt.add(3);

        System.out.println("排序后的结果: ");
        bt.print();
    }
}

测试结果:

19、Comparator 接口 另一种比较器

如果一个类已完成开发,但是在此类建立初期没有实现Comparable接口,此时肯定是无法进行对象排序操作的,所以为了解决这样的问题,java定义了另一种比较器的操作接口——Comparator此接口定义在java.util包中。

19.1、ComparaTor接口的定义

public Interface Comparator<T>{
    public int compara(T o1,T o2);
    boolean equals(Object obj);
}

可以发现,在此接口中也存在一个compra()方法,但是与之前不同的是,此方法接收两个个对象,参数一是需要比较的对象数组,参数二是自定义比较方法,其返回值依然是0,-1,1.
此接口使用与之前不同的是,需要单独指定好一个比较器的比较规则类才可以完成数组排序。

19.2、实现:

/**
 * Comparator比较器的使用
 * 
 * @author guai
 *
 */

// 实现Cloneable接口
class Student2 {
    private String name;

    private float score;

    public float getScore() {
        return score;
    }
    public void setScore(float score) {
        this.score = score;
    }
    public Student2(String name, float score) {
        super();
        this.name = name;
        this.score = score;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student2 [name=" + name + ", score=" + score + "]";
    }
}
//实现Comparator接口,自定义比较方法
class StudentComparator implements Comparator<Student2> {
    @Override
    public int compare(Student2 stu1, Student2 stu2) {
        // TODO Auto-generated method stub
        if (stu1.getScore() > stu2.getScore()) {
            return 1;
        } else if (stu1.getScore() == stu2.getScore()) {
            return 0;
        } else {
            return -1;
        }
    }
}

public class ComparatorApply {
    @Test
    public void testMethod() {
        Student2 st[] = { new Student2("guai", 99), new Student2("shuai", 100), new Student2("cabbage", 99.9f) };
        //参数一需要比较的对象数组参数二自定义比较方法
        Arrays.sort(st,new StudentComparator());

        System.out.println(Arrays.toString(st));
    }
}

结果:

20、观察者设计模式

什么是观察者模式?举一个简单的例子,现在跟多的购房者都在关注房子价格的变化,每当房子价格变化时,所有的购房者都可以观察到,实际上以上的购房者都是观察者。
在Java中可以直接依靠Onservable类和Onserver接口轻松实现以上功能。

20.1、在java.util包中提供了Observable类,和Observer接口,使用它们即可完成观察者模式。需要被观察者的类必须继承Observable类。
Observable类的常用方法如下:
void addOberver(Observer o) 添加一个观察者
void deleteObserver(Oberver o) 删除一个观察者
void setChanged() 被观察者状态改变
void notifyObservers(Object arg) 通知所有观察者状态改变 参数表示变化后的数据

同时需要观察者实现Obervable接口
Oberver接口定义如下:

public interface Observer{
   //第一个参数表示被观察者实例,第二个参数表示修改的内容。
    void update(Observable o, Object arg);
}

实现方法:

/**
 * 观察者模式
 * @author guai
 *
 */
//被观察者类
class House extends Observable {
    private float price;

    public House(float price) {
        super();
        this.price = price;
    }
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        // 设置变化点
        super.setChanged();
        // 通知观察者价格变动   
        super.notifyObservers(price);
        this.price = price;
    }
    @Override
    public String toString() {
        return "房子价格为" + this.price;
    }
}
//观察者类
class HoursePriceObserver implements Observer {
    private String name;

    public HoursePriceObserver(String name) {
        this.name = name;
    }
    public void update(Observable obj, Object arg) {
        if (arg instanceof Float) {
            System.out.print(this.name + "观察到价格更改为:");
            System.out.println(((Float) arg).floatValue());
        }
    }
}

public class ObserverMethod {
    @Test
    public void testMethos() {
        House house = new House(10000);
        HoursePriceObserver hpo = new HoursePriceObserver("A");
        HoursePriceObserver hpo1 = new HoursePriceObserver("B");
        HoursePriceObserver hpo2 = new HoursePriceObserver("C");
        //添加观察者
        house.addObserver(hpo);
        house.addObserver(hpo1);
        house.addObserver(hpo2);
        System.out.println(house);
        house.setPrice(6666);
        System.out.println(house);
    }
}

测试结果:

19.3让我们瞅瞅源码
setChaged方法在Observable类中的定义:

notifyObservers方法在Observable类中的定义

可见当价格改变时设置chaged标识为true然后调用notifyObservers方法,通过notifyObservers调用观察者的update方法

21、正则表达式

使用正则表达式可以方便的对数据进行匹配,还可以执行更加复杂的字符串验证、拆分、替换功能。

21.1、例如:判断一个字符串是否有数字组成。
//判断一个字符串是否由数字组成方法1

//@Test
    public void testMethod() {
        String str="12345678";
        boolean flag=true;

        char c[]=str.toCharArray();
        for(int i=0;i<c.length;i++) {
            if(c[i]<'0'||c[i]>'9') {
                flag=false;
                break;
            }
        }
        if(flag) {
            System.out.println("是由数字组成");
        }else {
            System.out.println("不是由数字组成");
        }
    }

    @Test
    //判断一个字符串是否由数字组成方法2
    public void testMethod1() {
        String str="12345678";
        //正则验证
        if(Pattern.compile("[0-9]+").matcher(str).matches()) {
            System.out.println("是由数字组成");
        }else {
            System.out.println("不是由数字组成");
        }
    }

显然使用了正则表达式的方法二更为简单

21.2、Pattern类和Matcher类
如果要在程序中应用正则表达式必须依靠Pattern类和Matcher类,这两个类都在java.util.regex包中定义。
Pattern类的主要作用是进行正则规范的编写,而Mathcer类则主要执行规范。

常用正则规范:
\ 表示反斜线(\)字符
\t 表示制表符
\n 表示换行
[abc] 表示字符a、b、c
[^abc] 表示处理字符a、b、c之外的任意字符
[a-zA-Z0-9] 表示由字符、数字组成
\d 表示数字
\D 表示非数字
\w 表示字母、数字、下划线
\W 表示非字母、数字、下划线
\s 表示所有空白字符(换行、空格等)
\S 表示都有非空白字符
^ 行的开头
$ 行的结尾
. 匹配处理换行符之外的任意字符

数量表示 (X表示一组规范)
X 必须出现一次
X? 可以出现0次或一次
X* 可以出现0次、1次或多次
X+ 可以出现1次或多次
X{n} 必须出现n次
X{n,} 必须出现n次以上
X{n,m} 必须出现n~m次

逻辑运算符(X、Y表示一组规范)
XY X规范后跟着Y规范
X|Y X规范或Y规范
(X) 作为一个捕获组规范

21.3、Pattern类的常用方法
static Pattern complie(String regex) 指定正则表达式规则返回Pattern类实例
Matcher matcher(CharSequence input) 指定需要验证的字符串并返回Mathcer类实例
String[] sqlit(CharSequence input) 字符串拆分

21.4、Matcher类的常用方法
boolean matcher() 执行验证
String replaceAll(String replacement) 字符串替换

21.5、测试方法

/**
 * 正则表达式的使用
 * @author guai
 *
 */
public class RegexMethod {
    //判断一个字符串是否由数字组成方法1
    //@Test
    public void testMethod() {
        String str="12345678";
        boolean flag=true;

        char c[]=str.toCharArray();
        for(int i=0;i<c.length;i++) {
            if(c[i]<'0'||c[i]>'9') {
                flag=false;
                break;
            }
        }
        if(flag) {
            System.out.println("是由数字组成");
        }else {
            System.out.println("不是由数字组成");
        }
    }

    //@Test
    //判断一个字符串是否由数字组成方法2
    public void testMethod1() {
        String str="1456a";
        //正则验证
        if(Pattern.compile("[0-7]{2}+[a-z]").matcher(str).matches()) {
            System.out.println("是由数字组成");
        }else {
            System.out.println("不是由数字组成");
        }
    }
    //@Test
    //验证一个字符串是否是合法的日期格式
    public void testMethod2() {
        String str="2020-02-02";
        String pat="\\d{4}-\\d{2}-\\d{2}";
        //指定正则表达式规则
        Pattern pa=Pattern.compile(pat);
        //创建matcher实例
        Matcher matcher=pa.matcher(str);
        //执行验证
        if(matcher.matches()) {
            System.out.println("日期格式合法");
        }else {
            System.out.println("日期格式不合法");
        }
    }

    //@Test
    //按照字符串的数字将字符串拆分
    public void testMethod3() {
        String str="A1B7877887C3";
        String pat="\\d+";
        //指定正则表达式规则
        Pattern p=Pattern.compile(pat);
        //进行字符串拆分
        String s[]=p.split(str);
        for(int i=0;i<s.length;i++) {
            System.out.println(s[i]);
        }
    }

    @Test
    //使用Matcher类中的替换操作
    public void testMethod4() {
        String str="A123B23C32D32";
        String regex="\\d";
        Pattern pattern=Pattern.compile(regex);
        Matcher matcher=pattern.matcher(str);
        String newstr= matcher.replaceAll("-");
        System.out.println(newstr);
    }
}

撒花撒花