本文主要是介绍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);
}
注意:
- 若一个类要实现Comparator接口:它一定要实现compare(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。
- 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类库的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!