Java基础回顾系列-第五天-高级编程之API类库

2024-09-08 15:32

本文主要是介绍Java基础回顾系列-第五天-高级编程之API类库,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Java基础回顾系列-第五天-高级编程之API类库

    • Java基础类库
      • StringBuffer
      • StringBuilder
      • String
      • CharSequence接口
      • AutoCloseable接口
      • Runtime
      • System
      • Cleaner
      • 对象克隆
    • 数字操作类
      • Math数学计算类
      • Random随机数生成类
      • BigInteger/BigDecimal大数字操作类
    • 日期操作类
      • Date
      • SimpleDateFormat
      • LocalDate(本地时间)
      • LocalTime(本地时间)
      • LocalDateTime(本地日期时间)
      • ZonedDateTime(时区)
      • Duration(持续时间)
      • DateTimeFormatter
      • 其它API
      • 与传统日期处理的转换
    • 正则表达式类
      • Pattern正则表达式编译
      • Matcher匹配
      • 示例
    • 国际化程序实现
      • Locale
      • ResourceBundle获取资源文件
    • 开发支持类库
      • Arrays
      • UUID
      • Optional 空指针异常处理
    • ThreadLocal
      • 定时调度
        • Timer
        • TimerTask
      • Base64加解密
    • 比较器
      • Comparable(自然排序)
      • Comparator(定制排序)
      • Comparable和Comparator区别比较
    • System类

Java基础类库

String类是字符串的首选类型,其最大的特点是内容不允许修改;
StringBuffer与StringBuilder类的内容允许修改;
StringBuffer是JDK1.0的时候提供的,属于线程安全的操作;StringBuilder是在JDK1.5提供的,属于线程不安全操作;

StringBuffer

线程安全,可变的字符序列。持有synchronized关键字。效率比StringBuilder低。

StringBuilder

线程不安全,可变的字符序列。

String

public final class String implements java.io.Serializable, Comparable<String>, CharSequence

  • String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。在早期的JVM实现版本中,被final修饰的方法会被转为内嵌调用以提升执行效率。而从Java SE5/6开始,就渐渐摈弃这种方式了。因此在现在的Java SE版本中,不需要考虑用final去提升方法调用效率。只有在确定不想让该方法被覆盖时,才将方法设置为final。
  • String类中所有的成员属性,从上面可以看出String类其实是通过char数组来保存字符串的。
  • 从上面的三个方法可以看出,无论是sub操、concat还是replace操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。

在这里要永远记住一点:
“对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。

package javase.util;public class JavaAPIDemo {public static void main(String[] args) {String str1 = "hello world";String str2 = new String("hello world");String str3 = "hello world";String str4 = new String("hello world");System.out.println(str1==str2); // falseSystem.out.println(str1==str3); // trueSystem.out.println(str2==str4); // falseString str5 = str1 + str2; // String中的“+”等同于StringBuilder中的append}
}

在这里插入图片描述
在这里插入图片描述  在这里插入图片描述

CharSequence接口

public interface CharSequence
Since:1.4
已有的子类:CharBuffer, Segment, String, StringBuffer, StringBuilder

方法说明
char charAt​(int index)获取指定索引字符
int length()获取字符串的长度
CharSequence subSequence​(int start, int end)截取部分字符串
package javase.util;public class JavaAPIDemo {public static void main(String[] args) {// 子类实例向上转型CharSequence string = "String字符串";CharSequence stringBuffer = new StringBuffer();CharSequence stringBuilder = new StringBuilder();// 获取指定索引字符char c = string.charAt(0);System.out.println("c = " + c); // S// 获取字符串的长度int length = string.length();System.out.println("length = " + length); // 9// 字符串截取CharSequence charSequence = string.subSequence(0, 6);System.out.println("charSequence = " + charSequence); // String}
}

AutoCloseable接口

AutoCloseable接口位于java.lang包下,从JDK1.7开始引入。 在关闭之前可以保存资源(例如文件或套接字句柄)的对象。 主要用于日后进行资源开发的处理上,以实现资源的自动关闭(释放资源),例如:在以后进行文件、网络、数据库开发的过程中由于服务器的资源有限。

在1.7之前,我们通过try{} finally{} 在finally中释放资源。
在finally中关闭资源存在以下问题:
1、自己要手动写代码做关闭的逻辑;
2、有时候还会忘记关闭一些资源;
3、关闭代码的逻辑比较冗长,不应该是正常的业务逻辑需要关注的;

对于实现AutoCloseable接口的类的实例,将其放到try后面(我们称之为:带资源的try语句),在try结束的时候,会自动将这些资源关闭(调用close方法)。
带资源的try语句的3个关键点:
1、由带资源的try语句管理的资源必须是实现了AutoCloseable接口的类的对象。
2、在try代码中声明的资源被隐式声明为fianl。
3、通过使用分号分隔每个声明可以管理多个资源。

示例:

package javase.util;/*** 自动关闭实例*/
public class JavaAPIDemo {public static void main(String[] args) {// 2、由带资源的try语句管理的资源必须是实现了AutoCloseable接口的类的对象。try (ConnectionLock connectionLock = new ConnectionInnerLock()){connectionLock.work();}catch (Exception e) {e.printStackTrace();}}
}
// 1、接口继承/类实现AutoCloseable接口
interface ConnectionLock extends AutoCloseable {/*** 任务事项*/void work();/*** 释放资源*/void unlock();
}class ConnectionInnerLock implements ConnectionLock {@Overridepublic void work() {System.out.println("连接成功...");for (int i = 0; i < 10; i++) {System.out.println("i = " + i);}System.out.println("任务结束...");}/*** 释放资源*/@Overridepublic void unlock() {System.out.println("释放资源");}@Overridepublic void close() throws Exception {this.unlock();}
}

Runtime

Runtime 类代表着Java程序的运行时环境,每个Java程序都有一个Runtime实例,该类会被自动创建,我们可以通过Runtime.getRuntime() 方法来获取当前程序的Runtime实例。

获取最大可用内存空间:public long maxMemory();默认的配置为本机系统内存的四分之一
获取可用内存空间:public long totalMemory();默认的配置为本机系统六十四分之一
获取空闲内存空间:public long freeMemory()
手工进行GC处理:public void gc();

package javase.util;public class JavaAPIDemo {public static void main(String[] args) {// 获取实例对象Runtime runtime = Runtime.getRuntime();//获取可用内存long value = Runtime.getRuntime().freeMemory();System.out.println("可用内存为:"+value/1024/1024+"mb");//获取jvm的总数量,该值会不断的变化long  totalMemory = Runtime.getRuntime().totalMemory();System.out.println("全部内存为:"+totalMemory/1024/1024+"mb");//获取jvm 可以最大使用的内存数量,如果没有被限制 返回 Long.MAX_VALUE;long maxMemory = Runtime.getRuntime().maxMemory();System.out.println("可用最大内存为:"+maxMemory/1024/1024+"mb");// 获取jvm可用的处理器核心的数量。获取本机的CPU内核数,并发访问量int availableProcessors = runtime.availableProcessors();System.out.println("可用的处理器核心的数量为: " + availableProcessors);// 运行垃圾收集器。runtime.gc();// 执行系统命令:比如打开一个程序等
//		runtime.exec()// 以Java格式返回Java Runtime Environment的版本Runtime.Version。Runtime.Version version = Runtime.version();System.out.println("version = " + version); // 11.0.2+9-LTS}
}

System

参考博文:Java中的System类

Cleaner

JDK9之后用cleaner机制代替了finalize机制,提供了内存清理的另一方法。 我们都知道finalize机制饱受诟病,因为它回收对象前要先执行Object.finalize()中的逻辑,降低了内存回收的效率,而且它不能保证被及时执行,这点很致命,导致对象不能及时被回收,例如如果利用finalize关闭打开的文件时,因为系统的文件描述符是有限的,如果不能及时的关闭文件,会导致无法打开更多的文件。

垃圾回收是java的最大特点之一,可以大大的解决了c++中依靠程序员去释放内存的压力,但是java提供的用来做为GC前的最后一道防线的finalize方法却并不友好

finalize的缺点

首先finalize的执行时间并不确定,其次finalize会阻碍GC的快速回收,在jvm进行GC时会启动一个finalizethread,当遇到有重写了finalize方法的对象时,会将对象放入finalizethread的中,并形成一个队列,暂时挂起,且运行时间并不确定,这就导致了对象回收的缓慢,如果队列中存在重写的finalize方法有死锁问题则会导致后面的方法都无法执行,这就是我们不提倡重写finalize的原因。

Java9垃圾回收机制

在java9中已经将finalize方法废弃。占有非堆资源的对象实例,类应该提供一个方法以明确释放这些资源,如果合适的话他们也应该实现AutoCloseable接口。
java.lang.ref.Cleaner和java.lang.ref.PhantomReference提供更灵活和有效的方式,在对象无法再访问时释放资源。

传统回收示例:

package javase.util;import java.lang.ref.Cleaner;/*** 传统的垃圾回收*/
public class JavaAPIDemo {public static void main(String[] args) {Obj obj = new Obj();obj = null;System.gc(); // 垃圾回收}
}
class Obj {public Obj(){System.out.println("对象产生了");}@Overrideprotected void finalize() throws Throwable {System.out.println("对象回收了");new Exception("死之前说句话");}
}
package javase.util;import java.lang.ref.Cleaner;/*** Cleaner实现回收*/
public class JavaAPIDemo {public static void main(String[] args) {while (true) {new CleaningExample();}}
}
class CleaningExample implements AutoCloseable {// A cleaner, preferably one shared within a libraryprivate static final Cleaner cleaner = Cleaner.create();static class State implements Runnable {State() {System.out.println("init");// initialize State needed for cleaning action}public void run() {System.out.println("clean");// cleanup action accessing State, executed at most once}}private final State state;private final Cleaner.Cleanable cleanable;public CleaningExample() {this.state = new State();this.cleanable = cleaner.register(this, state);}public void close() {cleanable.clean();}
}

对象克隆

Object类中:protected Object clone() throws CloneNotSupportedException
首先,如果此对象的类未实现接口Cloneable,则 CloneNotSupportedException抛出a。请注意,所有数组都被认为是实现接口,Cloneable并且clone数组类型方法的返回类型T[] 是T[]T是任何引用或基本类型。否则,此方法创建此对象的类的新实例,并使用该对象的相应字段的内容初始化其所有字段,就像通过赋值一样; 这些字段的内容本身不会被克隆。因此,该方法执行该对象的“浅拷贝”,而不是“深拷贝”操作。
该类Object本身并不实现接口 Cloneable,因此clone在类的对象上调用该方法Object将导致在运行时抛出异常。

类实现Cloneable接口以向该Object.clone()方法指示该方法合法地为该类的实例制作字段的字段副本。
在未实现Cloneable接口的实例上调用Object的clone方法会 导致 CloneNotSupportedException抛出异常。
按照惯例,实现此接口的类应Object.clone使用公共方法覆盖 (受保护)。

数字操作类

Math数学计算类

package javase.util;public class JavaAPIDemo {public static void main(String[] args) {/***Math.sqrt()//计算平方根*Math.cbrt()//计算立方根*Math.pow(a, b)//计算a的b次方*Math.max( , );//计算最大值*Math.min( , );//计算最小值*/System.out.println(Math.sqrt(16));   //4.0System.out.println(Math.cbrt(8));    //2.0System.out.println(Math.pow(3,2));     //9.0System.out.println(Math.max(2.3,4.5));//4.5System.out.println(Math.min(2.3,4.5));//2.3/*** abs求绝对值*/System.out.println(Math.abs(-10.4));    //10.4System.out.println(Math.abs(10.1));     //10.1/*** ceil天花板的意思,就是返回大的值*/System.out.println(Math.ceil(-10.1));   //-10.0System.out.println(Math.ceil(10.7));    //11.0System.out.println(Math.ceil(-0.7));    //-0.0System.out.println(Math.ceil(0.0));     //0.0System.out.println(Math.ceil(-0.0));    //-0.0System.out.println(Math.ceil(-1.7));    //-1.0/*** floor地板的意思,就是返回小的值*/System.out.println(Math.floor(-10.1));  //-11.0System.out.println(Math.floor(10.7));   //10.0System.out.println(Math.floor(-0.7));   //-1.0System.out.println(Math.floor(0.0));    //0.0System.out.println(Math.floor(-0.0));   //-0.0/*** random 取得一个大于或者等于0.0小于不等于1.0的随机数*/System.out.println(Math.random());  //小于1大于0的double类型的数System.out.println(Math.random()*2);//大于0小于1的double类型的数System.out.println(Math.random()*2+1);//大于1小于2的double类型的数/*** rint 四舍五入,返回double值* 注意.5的时候会取偶数    异常的尴尬=。=*/System.out.println(Math.rint(10.1));    //10.0System.out.println(Math.rint(10.7));    //11.0System.out.println(Math.rint(11.5));    //12.0System.out.println(Math.rint(10.5));    //10.0System.out.println(Math.rint(10.51));   //11.0System.out.println(Math.rint(-10.5));   //-10.0System.out.println(Math.rint(-11.5));   //-12.0System.out.println(Math.rint(-10.51));  //-11.0System.out.println(Math.rint(-10.6));   //-11.0System.out.println(Math.rint(-10.2));   //-10.0/*** round 四舍五入,float时返回int值,double时返回long值*/System.out.println(Math.round(10.1));   //10System.out.println(Math.round(10.7));   //11System.out.println(Math.round(10.5));   //11System.out.println(Math.round(10.51));  //11System.out.println(Math.round(-10.5));  //-10System.out.println(Math.round(-10.51)); //-11System.out.println(Math.round(-10.6));  //-11System.out.println(Math.round(-10.2));  //-10}
}

Random随机数生成类

参考博文:
JAVA中Random分析 https://shift-alt-ctrl.iteye.com/blog/2432102
Java-Random类常用方法详解:https://blog.csdn.net/goodbye_youth/article/details/81110123

BigInteger/BigDecimal大数字操作类

参考博文:
java大数详解

日期操作类

可变性:像日期和时间这样的类应该是不可变的。
偏移性: Date中的年份是从1900开始的,而月份都从0开始。
格式化: 格式化只对Date有用,Calendar则不行。
此外,它们也不是线程安全的;不能处理闰秒等。
总结: 对日期和时间的操作一直是Java程序员最痛苦的地方之一。一般地,我们会使用Joda-Time外部依赖Lib进行开发。

Java 8 吸收了 Joda-Time 的精华,以一个新的开始为 Java 创建优秀的 API。新的 java.time 中包含了所有关于本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime和持续时间(Duration)的类。历史悠久的 Date 类新增了 toInstant() 方法,用于把 Date 转换成新的表示形式。这些新增的本地化时间日期 API 大大简化了日期时间和本地化的管理。

  • java.time – 包含值对象的基础包
  • java.time.chrono – 提供对不同的日历系统的访问
  • java.time.format – 格式化和解析时间和日期
  • java.time.temporal – 包括底层框架和扩展特性
  • java.time.zone – 包含时区支持的类

Date

public Date() {
this(System.currentTimeMillis());
}
public Date(long date) {
fastTime = date;
}

从Date的构造函数可以观察得出:

将long转为Date:public Date(long date)
将Date转为long:public long getTime()

SimpleDateFormat

线程不安全的类。以及解决方案
详解参考:https://mp.weixin.qq.com/s/YmmM1KdGX_g46Sn_vFQraA
方法:

  • Date转字符串: public final String format​(Date date)
  • 字符串转Date: public Date parse​(String source) throws ParseException

构造:

  • public SimpleDateFormat​(String pattern)

LocalDate(本地时间)

LocalTime(本地时间)

LocalDateTime(本地日期时间)

Java的Date,Calendar类型使用起来并不是很方便,而且Date类(据说)有着线程不安全等诸多弊端。同时若不进行封装,会在每次使用时特别麻烦。于是Java8推出了线程安全、简易、高可靠的时间包。并且数据库中也支持LocalDateTime类型,在数据存储时候使时间变得简单。Java8这次新推出的包括三个相关的时间类型:LocalDateTime年月日十分秒;LocalDate日期;LocalTime时间;三个包的方法都差不多。
在这里插入图片描述

参考博文:https://blog.csdn.net/kingboyworld/article/details/75808108

ZonedDateTime(时区)

Duration(持续时间)

DateTimeFormatter

方法:

  • public static DateTimeFormatter ofPattern​(String pattern):静态方法 , 返 回 一 个 指 定 字 符 串 格 式 的DateTimeFormatter
    -public String format​(TemporalAccessor temporal): 格式化一个日期、时间,返回字符串
  • public TemporalAccessor parse​(CharSequence text):将指定格式的字符序列解析为一个日期、时间

具体的格式可以看API:https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/time/format/DateTimeFormatter.html

package javase.util;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;public class JavaAPIDemo {public static void main(String[] args) {LocalDateTime now = LocalDateTime.now();// 格式化DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");String format = now.format(dateTimeFormatter);System.out.println(" [yyyy-MM-dd HH:mm:ss],format = " + format);format = now.format(DateTimeFormatter.ISO_DATE);System.out.println(" [2019-04-26],format = " + format);format = now.format(DateTimeFormatter.BASIC_ISO_DATE);System.out.println(" [20190426],format = " + format);format = now.format(DateTimeFormatter.ISO_TIME);System.out.println(" [15:48:25.3219472],format = " + format);format = now.format(DateTimeFormatter.ISO_DATE_TIME);System.out.println(" [2019-04-26T15:45:11.9338055],format = " + format);// 本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG)format = now.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG));System.out.println(" [2019年4月26日],format = " + format);}
}

其它API

在这里插入图片描述

与传统日期处理的转换

在这里插入图片描述

正则表达式类

详细参考:https://mp.weixin.qq.com/s/uzmvuBy3_hTCanQ-MlBO0Q

Pattern正则表达式编译

匹配信息:https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/regex/Pattern.html
获取实例:public static Pattern compile​(String regex)

Matcher匹配

获取实例:Pattern类 public Matcher matcher​(CharSequence input)

示例

package javase.util;import java.util.regex.Matcher;
import java.util.regex.Pattern;public class TestMain {public static void main(String[] args) {// 要求取出“#{内容}”标记中的所有内容String str = "INSERT INTO dept(deptno,dname,loc) VALUES (#{deptno},#{dname},#{loc})" ;String regex = "#\\{\\w+\\}" ;Pattern pat = Pattern.compile(regex) ; // 编译正则表达式Matcher mat = pat.matcher(str) ;while(mat.find()) {	// 是否有匹配成功的内容System.out.println(mat.group(0).replaceAll("#|\\{|\\}", ""));}}
}

国际化程序实现

Locale

构造方法:

  • public Locale​(String language)
  • public Locale​(String language, String country)

获取本地Local:public static Locale getDefault()
常量:Locale.CHINA

package javase.util;import java.util.Locale;public class TestMain {public static void main(String[] args) {// 构造函数Locale locale = new Locale("zh", "CN");System.out.println("locale = " + locale); // zh_CN// 静态方法Locale aDefault = Locale.getDefault();System.out.println("aDefault = " + aDefault); // zh_CN// 常量Locale china = Locale.CHINA;System.out.println("china = " + china); // zh_CN}
}

ResourceBundle获取资源文件

package javase.util;import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;public class TestMain {public static void main(String[] args) {// 构造函数Locale locale = new Locale("zh", "CN");System.out.println("locale = " + locale); // zh_CN// 静态方法Locale aDefault = Locale.getDefault();System.out.println("aDefault = " + aDefault); // zh_CN// 常量Locale china = Locale.CHINA;System.out.println("china = " + china);// 获取资源文件ResourceBundle resourceBundle = ResourceBundle.getBundle("资源文件名", aDefault);// 读取资源文件信息String key = resourceBundle.getString("资源文件中的key");// 消息格式化String format = MessageFormat.format(key, "参数1", "参数2");}
}

开发支持类库

Arrays

参考博文:Java-Arrays类常用方法详解 https://blog.csdn.net/goodbye_youth/article/details/81003817

二分法查找:public static int binarySearch​(byte[] a, byte key)
数组比较:public static int compare​(double[] a, double[] b)
数组相等判断:public static boolean equals​(char[] a, char[] a2)
数组填充:public static void fill​(byte[] a, byte val)
数组排序:public static void sort​(Object[] a)
数组转String:public static String toString​(char[] a)

需要依赖排序。

UUID

简介:
UUID 含义是通用唯一识别码 (Universally Unique Identifier),这是一个软件建构的标准。也是被开源软件基金会 (Open Software Foundation, OSF) 的组织应用在分布式计算环境 (Distributed Computing Environment, DCE) 领域的一部分。
UUID 的目的,是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。如此一来,每个人都可以建立不与其它人冲突的 UUID。在这样的情况下,就不需考虑数据库建立时的名称重复问题。

UUID 的目的是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。
如此一来,每个人都可以建立不与其它人冲突的 UUID。在这样的情况下,就不需考虑数据库建立时的名称重复问题。
目前最广泛应用的 UUID,即是微软的 Microsoft’s Globally Unique Identifiers (GUIDs),而其他重要的应用,
则有 Linux ext2/ext3 档案系统、LUKS 加密分割区、GNOME、KDE、Mac OS X 等等。

UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成的API。
按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字

UUID由以下几部分的组合:
(1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
(2)时钟序列。
(3)全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。
UUID的唯一缺陷在于生成的结果串会比较长。关于UUID这个标准使用最普遍的是微软的GUID(Globals Unique Identifiers)。
在ColdFusion中可以用CreateUUID()函数很简单地生成UUID,其格式为:xxxxxxxx-xxxx- xxxx-xxxxxxxxxxxxxxxx(8-4-4-16),
其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。而标准的UUID格式为:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12),可以从cflib 下载CreateGUID() UDF进行转换。

Optional 空指针异常处理

参考博文:使用Java8中Optional机制的正确姿势 https://www.jb51.net/article/127425.htm
理解、学习与使用 JAVA 中的 OPTIONAL https://www.cnblogs.com/zhangboyu/p/7580262.html

Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException) —— 每个 Java 程序员都非常了解的异常。
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。

我们从一个简单的用例开始。在 Java 8 之前,任何访问对象方法或属性的调用都可能导致 NullPointerException:

String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();

在这个小示例中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查:

if (user != null) {Address address = user.getAddress();if (address != null) {Country country = address.getCountry();if (country != null) {String isocode = country.getIsocode();if (isocode != null) {isocode = isocode.toUpperCase();}}}
}

你看到了,这很容易就变得冗长,难以维护。

为了简化这个过程,我们来看看用 Optional 类是怎么做的。从创建和验证实例,到使用其不同的方法,并与其它返回相同类型的方法相结合,下面是见证 Optional 奇迹的时刻。

创建 Optional 实例

  • public static <T> Optional<T> of​(T value):
  • public static <T> Optional<T> ofNullable​(T value)

访问 Optional 对象的值

  • public T get()

验证是否存在值

  • public boolean isPresent():是否存在值
  • public boolean isEmpty():是否没有值

返回默认值

  • public T orElse​(T other): 如果存在值,则返回该值,否则返回其他值。
  • public T orElseGet​(Supplier<? extends T> supplier):如果存在值,则返回该值,否则返回由供应函数生成的结果。

返回异常

  • public T orElseThrow():如果存在值,则返回该值,否则抛出NoSuchElementException。

转换值

  • public <U> Optional<U> map​(Function<? super T,​? extends U> mapper)

过滤值

  • public Optional<T> filter​(Predicate<? super T> predicate)

链式操作
待补充。

ThreadLocal

ThreadLocal是啥?以前面试别人时就喜欢问这个,有些伙伴喜欢把它和线程同步机制混为一谈,事实上ThreadLocal与线程同步无关。ThreadLocal虽然提供了一种解决多线程环境下成员变量的问题,但是它并不是解决多线程共享变量的问题。那么ThreadLocal到底是什么呢?

API介绍:

该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。 ThreadLocal实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

所以ThreadLocal与线程同步机制不同,线程同步机制是多个线程共享同一个变量,而ThreadLocal是为每一个线程创建一个单独的变量副本,故而每个线程都可以独立地改变自己所拥有的变量副本,而不会影响其他线程所对应的副本。可以说ThreadLocal为多线程环境下变量问题提供了另外一种解决思路。

ThreadLocal类接口很简单,只有4个方法,我们先来了解一下。
public void set(Object value) 设置当前线程的线程局部变量的值;
public Object get() 该方法返回当前线程所对应的线程局部变量;
public void remove() 将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度;
protected Object initialValue() 返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的默认实现直接返回一个null。

除了这四个方法,ThreadLocal内部还有一个静态内部类ThreadLocalMap,该内部类才是实现线程隔离机制的关键,get()、set()、remove()都是基于该内部类操作。ThreadLocalMap提供了一种用键值对方式存储每一个线程的变量副本的方法,key为当前ThreadLocal对象,value则是对应线程的变量副本。

单线程情况下:

package javase.util;/*** 多线程的情况*/
public class JavaAPIDemo {public static void main(String[] args) {new Thread(()->{Message msg = new Message() ;	// 实例化消息主体对象msg.setInfo("第一个线程的消息"); // 设置要发送的内容Channel.setMessage(msg); // 设置要发送的消息Channel.send(); // 发送消息},"消息发送者A") .start() ;new Thread(()->{Message msg = new Message() ;	// 实例化消息主体对象msg.setInfo("第二个线程的消息"); // 设置要发送的内容Channel.setMessage(msg); // 设置要发送的消息Channel.send(); // 发送消息},"消息发送者B") .start() ;new Thread(()->{Message msg = new Message() ;	// 实例化消息主体对象msg.setInfo("第三个线程的消息"); // 设置要发送的内容Channel.setMessage(msg); // 设置要发送的消息Channel.send(); // 发送消息},"消息发送者C") .start() ;}
}class Channel {	// 消息的发送通道private static Message message ;private Channel() {}public static void setMessage(Message m) {message = m ;}public static void send() {	// 发送消息System.out.println("【消息发送】" + message.getInfo());}
}
class Message {	// 要发送的消息体private String info ;public void setInfo(String info) {this.info = info;}public String getInfo() {return info;}
}

结果:

【消息发送】第二个线程的消息
【消息发送】第二个线程的消息
【消息发送】第二个线程的消息

package javase.util;/*** 多线程的情况*/
public class JavaAPIDemo {public static void main(String[] args) {new Thread(()->{Message msg = new Message() ;	// 实例化消息主体对象msg.setInfo("第一个线程的消息"); // 设置要发送的内容Channel.setMessage(msg); // 设置要发送的消息Channel.send(); // 发送消息},"消息发送者A") .start() ;new Thread(()->{Message msg = new Message() ;	// 实例化消息主体对象msg.setInfo("第二个线程的消息"); // 设置要发送的内容Channel.setMessage(msg); // 设置要发送的消息Channel.send(); // 发送消息},"消息发送者B") .start() ;new Thread(()->{Message msg = new Message() ;	// 实例化消息主体对象msg.setInfo("第三个线程的消息"); // 设置要发送的内容Channel.setMessage(msg); // 设置要发送的消息Channel.send(); // 发送消息},"消息发送者C") .start() ;}
}class Channel {	// 消息的发送通道private static final ThreadLocal<Message> THREADLOCAL = new ThreadLocal<Message>() ;private Channel() {}public static void setMessage(Message m) {THREADLOCAL.set(m);}public static void send() {	// 发送消息System.out.println("【消息发送】" + THREADLOCAL.get().getInfo());}
}
class Message {	// 要发送的消息体private String info ;public void setInfo(String info) {this.info = info;}public String getInfo() {return info;}
}

输出结果:

【消息发送】第二个线程的消息
【消息发送】第三个线程的消息
【消息发送】第一个线程的消息

定时调度

定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程的方式进行处理,所以它和多线程技术还是有非常大的关联的。在JDK中Timer类主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务,但封装任务的类却是TimerTask类。

Timer
        //只执行一次public void schedule(TimerTask task, long delay);public void schedule(TimerTask task, Date time);//循环执行// 在循环执行类别中根据循环时间间隔又可以分为两类public void schedule(TimerTask task, long delay, long period) ;public void schedule(TimerTask task, Date firstTime, long period) ;public void scheduleAtFixedRate(TimerTask task, long delay, long period)public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
TimerTask

只执行一次:

Timer timer = new Timer();//延迟1000ms执行程序
timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("IMP 当前时间" + this.scheduledExecutionTime());}
}, 1000);
//延迟10000ms执行程序
timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("IMP 当前时间" + this.scheduledExecutionTime());}
}, new Date(System.currentTimeMillis() + 10000));

循环执行:

Timer timer = new Timer();//前一次执行程序结束后 2000ms 后开始执行下一次程序
timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("IMP 当前时间" + this.scheduledExecutionTime());}
}, 0,2000);//前一次程序执行开始 后 2000ms后开始执行下一次程序
timer.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {System.out.println("IMP 当前时间" + this.scheduledExecutionTime());}
},0,2000);

Base64加解密

参考:http://www.runoob.com/java/java8-base64.html

比较器

Comparable(自然排序)

Comparable是排序接口。若一个类实现了Comparable接口,就意味着该类支持排序。实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。
此外,实现此接口的对象可以用作有序映射中的键或有序集合中的集合,无需指定比较器。
该接口定义如下:

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

T表示可以与此对象进行比较的那些对象的类型。
此接口只有一个方法compare,比较此对象与指定对象的顺序,如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
现在我们假设一个Person类,代码如下:

class Person {String name;int age;public Person(String name, int age) {super();this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}
}

现在有两个Person类的对象,我们如何来比较二者的大小呢?我们可以通过让Person实现Comparable接口:

package javase.util;import java.util.Arrays;/***排序*/
public class JavaAPIDemo {public static void main(String[] args) {Person[] people = new Person[]{new Person("xujian", 20), new Person("xiewei", 10)};System.out.println("排序前");for (Person person : people) {System.out.print(person.getName() + ":" + person.getAge());}Arrays.sort(people);System.out.println("\n排序后");for (Person person : people) {System.out.print(person.getName() + ":" + person.getAge());}}
}/*** 根据年龄排序*/
class Person implements Comparable<Person>{String name;int age;public Person(String name, int age) {super();this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}@Overridepublic int compareTo(Person o) {return this.age - o.getAge();}
}

Comparator(定制排序)

当元素的类型没有实现Comparable接口而又不方便修改代码,或者实现了Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序,强行对多个对象进行整体排序的比较。 Comparator是比较接口,我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过实现Comparator来新建一个比较器,然后通过这个比较器对类进行排序。该接口定义如下:

@FunctionalInterface
public interface Comparator<T> {int compare(T o1, T o2);boolean equals(Object obj);
}

注意:

  1. 若一个类要实现Comparator接口:它一定要实现compare(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。
  2. int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。

现在假如上面的Person类没有实现Comparable接口,该如何比较大小呢?我们可以新建一个类,让其实现Comparator接口,从而构造一个“比较器"。

package javase.util;import java.util.Arrays;
import java.util.Comparator;/***排序*/
public class JavaAPIDemo {public static void main(String[] args) {Person[] people = new Person[]{new Person("xujian", 20), new Person("xiewei", 10)};System.out.println("排序前");for (Person person : people) {System.out.print(person.getName() + ":" + person.getAge());}// 使用比较器Arrays.sort(people, new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {return o1.getAge() - o2.getAge();}});// Lambda 排序比较器Arrays.sort(people, (o1, o2)->{return o1.getAge() - o2.getAge();});System.out.println("\n排序后");for (Person person : people) {System.out.print(person.getName() + ":" + person.getAge());}}
}/*** 根据年龄排序*/
class Person{String name;int age;public Person(String name, int age) {super();this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}
}

Comparable和Comparator区别比较

  • Comparable是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”。而
    Comparator是比较器,我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
  • Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
  • 两种方法各有优劣, 用Comparable 简单, 只要实现Comparable 接口的对象直接就成为一个可以比较的对象,但是需要修改源代码。 用Comparator 的好处是不需要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去就可以比大小了, 并且在Comparator 里面用户可以自己实现复杂的可以通用的逻辑,使其可以匹配一些比较简单的对象,那样就可以节省很多重复劳动了。

总结一下,两种比较器Comparable和Comparator,后者相比前者有如下优点:
1、如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,写比较算法。
2、实现Comparable接口的方式比实现Comparator接口的耦合性 要强一些,如果要修改比较算法,要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修 改。从这个角度说,其实有些不太好,尤其在我们将实现类的.class文件打成一个.jar文件提供给开发者使用的时候。实际上实现Comparator 接口的方式后面会写到就是一种典型的策略模式。
当然,这不是鼓励用Comparator,意思是开发者还是要在具体场景下选择最合适的那种比较器而已。

System类

  • System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。该类位于java.lang包。
  • 由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便的进行调用。
  • 成员变量
    • System类内部包含in、out和err三个成员变量,分别代表标准输入流(键盘输入),标准输出流(显示器)和标准错误输出流(显示器)。
  • 成员方法
    • native long currentTimeMillis()
      该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。
    • void exit(int status)
      该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表异常退出。使用该方法可以在图形界面编程中实现程序的退出功能等
    • void gc()
      该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中垃圾回收算法的实现以及系统执行时的情况。
    • String getProperty(String key):
      该方法的作用是获得系统中属性名为key的属性对应的值。

这篇关于Java基础回顾系列-第五天-高级编程之API类库的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1148490

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor