Java的命令模式
一种方法的执行有不同的结果,假设对数组进行处理;
public interface Command{
void process(int[] target);
}
public class ProcessArray{
public void process(int[] target,Command com){
com.process(target);
}
}
内部类
非静态内部类
非静态内部类可以调用外部类的成员变量和方法.如果内部类和外部内部类的成员变量的名称相同,调用的时候可以使用this.xx
,外部类.this.xx
进行区分.如果不指定,则首先去调用局部变量,再去查找内部类的成员变量,最后才去查找外部类的成员变量.
非静态内部类不能有静态成员和静态方法.
在外部创建内部类的实例:
OuterClass.InnerClass varName = OuterInstance.new InnerConstrucetor()
非静态内部类的子类在创建时要调用内部类的构造函数,此时要保证外部类实例的存在:
public class SubClass extends Out.In{
public SubClass(Out out){
out.super("hello"); //super代表In的构造器
}
}
In和SubClass类的使用的相同点:都存在Outer实例的引用,差别:创建方式和不一样.
静态内部类
静态内部类,不能访问外部类的成员变量.可以访问静态变变量.
在外部类外使用静态内部类:
Outer.Inner varName = new Outer.Inner();
使用静态内部类较非静态内部类更加简单,因此需要使用静态内部类时,优先考虑使用静态内部类.
Java8改进的匿名内部类
匿名内部类只能实现一个接口或者只能继承一个父类;
new 实现接口() | 父类构造器(实参列表)
{}
匿名内部类不能定义为抽象类;
匿名内部类不能定义构造器,但可以定义初始化块;
创建接口的匿名内部类,只能使用无参构造;
java8之前,Java要求被局部内部类,匿名内部类访问的局部变量必须使用final修饰,从Java8开始这个限制取消了,Java8更加智能:如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰.
Java8中的java.util.function定义了大量函数式接口
Arrays.parallelprefix(arr,(left,right) -> left * right);根据前一个索引的值(left)和当前值(right)计算当前值
Arrays.parallelsetAll(arr,index -> index * 5); 根据索引设定值
枚举类
在某些情况下,一个类的队形是有限而且是固定的,比如季节类.它只有4个.这种有限而且固定的类,在java中被称为枚举.
Java5新增了一个enum关键字,用以定义枚举类.它可以有自己的成员变量,方法,可以实现多个接口(和普通类相似).
不同之处:
- 枚举类可以实现一个或多个接口,使用eunm定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,因而枚举类不能显式继承其他父类.其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口.
- 使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类。
- 枚举类的构造器只能使用private访问控制符,如果省略了构造器的访问修饰符,则默认使用private修饰;如果强制指定访问控制符,,则只能指定private修饰符。
- 枚举类的说有实例必须在枚举的第一行显式列出,否则这个枚举类永远都不能产生实例。列出这些实例时,系统会自动添加public static final 修饰,无须程序员添加。
枚举类默认提供了一个values()
,返回的是枚举数组,该方法可以很方便地遍历所有的枚举值.
SPRING,SUMMER,FAIL,WINTER;
enum类从Enum中继承的方法有:
- int compareTo(E o);如果该枚举对象位于指定枚举对象之后,则返回正整数;之前则返回负整数;否则返回零;
- String name();返回枚举实例的名称,和toStirng一样;
- String toString();
- int ordinal();返回该枚举实例的索引;
- 类方法 public static <T extends Enum> T valueOf(Class enumType,String name);
枚举通常应该设计成有几个不可变实例;因此建议把枚举类的成员变量都使用private final修饰,如果所有的变量成员都使用了final修饰符来修饰,则必须在构造器里为这些变量指定初始值(或者在定义成员变量时指定默认值,或者在初始化块中指定初始值,但这两种并不常见),因此应该为枚举类显式定义带参数的构造器。
public enum SexEnm {
MAIL("男"),FEMAIL("女");
private final String sex;
private SexEnm(String sex) {
this.sex = sex;
}
public String getSex() {
return sex;
}
}
枚举实现接口
实现接口时,需要重写接口中的方法,方法可以直接写在枚举类中,但这种写法每个枚举实例执行的行为将会一样。我们可以为每个实例重写,而不用在枚举类中重写;
public interface SexDesc{
void info();
}
public enum SexEnm implements SexDesc{
MAIL("男"){
@Override
public void info() {
System.out.println("此实例是男");
}
},FEMAIL("女"){
@Override
public void info() {
System.out.println("此实例时女");
}
};
private final String sex;
private SexEnm(String sex) {
this.sex = sex;
}
public void info() {
// TODO Auto-generated method stub
}
public String getSex() {
return sex;
}
}
如此每个实例的表现行为则不一样.(此时枚举类是抽象枚举类,MAIL,FEMAIL是他的匿名实现类)
枚举含有抽象方法
如果想枚举的各个实例有不同的方法,则可以定义抽象方法来实现.
public enum Operation {
PLUS{
@Override
public double eval(double x, double y) {
return x + y;
}
},
MINUS{
@Override
public double eval(double x, double y) {
return x - y;
}
},
TIMES{
@Override
public double eval(double x, double y) {
return x * y;
}
},
DIVIDE{
@Override
public double eval(double x, double y) {
return x / y;
}
};
public abstract double eval(double x,double y);
}
使用抽象方法时,enum也不能使用abstract标志(编译时会自动给加上abstract关键字);
垃圾回收机制
对象回收的时候,会首先调用对象的finalize()方法(清理对象资源,也有可能是可恢复状态变回可达状态),对对象做最后的处理.
- 可达状态
- 可恢复状态 没有变量指向这个对象,但还没有调用finalize()方法
- 不可达状态 调用了finalize()方法,但依旧没有变量指向该对象
强制执行垃圾回收
System.gc()
;Runtime.getRuntime().gc()
;
但不能确定系统是否进行垃圾回收?何时进行垃圾回收?
当jvm执行finalize()方法时出现异常时,垃圾回收机制不会报告异常,程序继续执行.
由于finalize方法不一定会被执行,所以清理资源的方法最后不要放再finalize()方法中.
强行执行finalize方法,需要在强制执行垃圾回收后执行.
System.runFinalization();
Runtime.getRuntime().runFinalization();
对象的软、弱和虚引用
在java.lang.ref包下提供了3个类:SoftReference、PhantomReference和weakReference,它们分别代表了系统对对象的3中引用方式:软引用、虚引用、和弱引用。因此java语言对对象的引用有4中方式:
- 强引用(StrongReference)这是Java程序中最常用的引用方式。程序创建一个对象,并把对象赋值给一个引用变量。处于可达状态。
- 软引用(SoftReference)软件需要通过SoftReference类来实现,当一个对象只有软引用时,它有可能被垃圾回收机制回收。对于软引用的对象来说,当系统内存足够时,它不会被系统回收。当系统内存不足时,就会对其进行回收。软引用通常用于对内存敏感的程序中。
- 弱引用(WeakReference)通过WeakReference类实现,弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引用的对象而言,当系统垃圾回收运行时,不管系统内存是否足够,总会回收该对象所占用的内存。
- 虚引用(PhantomRefefence)虚引用通过PhantomReference类实现,虚引用完全类似于没有引用。对对象本身没有太大影响,对象甚至感觉不到虚引用的存在。它和没有引用的效果大致相同。主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,需要和引用队列一起使用(ReferenceQueue)联合使用。不能使用虚引用获得对象
上面三个引用类都包含了一个get()方法,用于获取被他们所引用的对象。
引用队列由java.lang.ref.ReferenceQueue类表示,它用于保存被回收后对象的引用。当联合使用软引用、弱引用和引用队列时,系统在回收被引用对象之后,将把被回收对象对应的引用添加到关联的引用队列中。
与软引用和弱引用不同的是,虚引用在对象被释放之前,将把它对应的虚引用添加多它关联的引用队列中,这使得在对象被回收之前采取性对。
软引用和弱引用可以单独使用,但虚引用不能单独使用吗,单独使用没有太大的意义。虚引用的主要作用就是跟踪对象被垃圾回收的状态,程序可以检查与虚引用关联的引用队列中是否已经包含了该虚引用,从而了解虚引用所引用的对象是否即将被回收。
弱引用实例:
public static void main(String[] args) {
String str = new String("你快乐吗?");
WeakReference<String> wr = new WeakReference<String>(str);
str = null;
System.out.println(wr.get());
System.gc();
System.runFinalization();
System.out.println(wr.get());
}
虚引用单独使用没有什么意义
public static void main(String[] args) {
String str = new String("我很快乐!");
ReferenceQueue<String> strQueue = new ReferenceQueue<String>();
PhantomReference<String> prf = new PhantomReference<String>(str, strQueue);
str = null;
System.out.println(prf.get()); //null
System.gc();
System.runFinalization();
System.out.println(strQueue.poll() == prf);//true
}
如果希望系统能够快速得进行垃圾回收,这些引用就特别有用,但使用的同时要让强引用失效,否则没有意义;
如果弱引用和软引用的引用对象被清理时,还想继续使用,就必须重新显示地进行创建对象.
Java修饰符
strictfp
strictfp关键字的含义是FP-strict,也就是精确浮点的意思.在Java虚拟机进行浮点运算时,如果没有指定stricfp关键字,Java的编译器和运行环境在浮点运算上不一定令人满意.一旦使用strictfp来修饰类、接口或者方法时,那么在所修饰的范围内Java的编译器和运行时环境就会完全依照浮点规范IEEE-754来执行。因此想让浮点运算更加精确,就可以使用strictfp关键字来修饰类、接口和方法。
native
native关键字主要用于修饰一个方法,使用native修饰的方法类似于一个抽象方法。与抽象方法不同的是,native方法通常采用C语言来实现。如果某个方法需要利用平台相关特性,或者访问系统硬件等,则可以使用native修饰该方法,再把该方法交给C去实现。一旦Java程序中包含native方法,这个程序将失去跨平台的功能。
JAR文件
JAR文件的全称是Java Archive File,意思就是Java档案文件。通常JAR文件是一种压缩文件,与常见的ZIP压缩文件兼容,通常也被成为JAR包。JAR文件与ZIP文件的区别就是在JAR文件中默认包含了一个名为META-INF/MANIFEST.MF的文件清单,这个清单是在生成JAR文件时由系统自动创建的。
MANIFEST的意思是货单的意思。
清单目录文件只是一个普通的文本文件,使用记事本编辑即可。清单文件的内容是key-value形式,具体格式: key:<空格>value;
要求如下:
- 每行只能定义一个key-value对,每行的key-value对之前不能有空格,即key-value对必须顶格写。
- 每组key-value对之间以:":"分割,少些了冒号或者是空格都是错误的。
- 文件开头不能有空行。
- 文件必须以一个空行结束。
Scanner
Scanner是一个基于正则表达式的文本扫描器,它可以从文件、输入流、字符串中解析出基本数据类型和字符串值。Scanner类提供了多个构造器,不同的构造器可以接收文件、输入流、字符串作为数据源。用于从文件、输入流、字符串中解析数据。
Scanner主要提供了两个方法来扫描输入。
hasNestXxx()
:是否还有下一个输入项,其中Xxx可以是Int、Long等代表基本数据类型的字符串。如果只是判断是否包含下一个字符串,则直接使用hasNext();nextXxx()
;获取下一个输入项。
- 在默认情况下,Scan使用空白(包含空格、Tab、回车)作为多个输入项之间的分隔符。
- 使用确定的分割符时,调用sc.useDelimiter("\n");
当不希望使用空格作为分隔符时,例如想要每次读取一行,可以使用上行方法改为回车。
为Scanner设置分隔符使用userDelimiter(Stirng paattern)
,该方法的参数应该是一个正则表达式。
实际上Scanner已经提供了读取一行的方法:
boolean hasNextLine();
返回输入源是否有下一行。String nextLine();
返回输入源中的下一行字符串。
Scanner 读取文件内容
`Scanner sc = new Scanner(new File("ScannerFileTest.java"));`
系统相关
Java在不同操作系统上运行时,可能需要取得平台相关的属性,或者调用平台命令来完成特定功能。Java提供了System类和Runtime类来与程序的平台进行交互。
System 类
带表Java程序的运行平台,程序不能创建System类的对象,System类提供了一些变量和类方法,允许直接通过System类来调用这些类变量和方法。
System类提
- 供了代表标准输入、标准输出和错误输出的类变量;
- 提供一些静态方法用于访问环境变量、系统属性的方法
- 加载文件和动态链接库的方法(主要对native方法有用)
API:
Map<String,String> env = System.getenv()
;获取系统所有的环境变量String getenv("JAVA_HOME")
;获取指定的环境变量;Properties System.getProperties()
获取所有的属性;- 将属性保存到文件中(props.store(new FileOutputStream(“props.txt”),“System Properties”));文件将保留在项目根目录,而不是当前目录或者时资源目录,与src平级
String System.getProperty("os.name")
;- 获取时间
currentTimeMillis();和nanoTime()
; System.identityHashCode(Object x)
返回指定对象的精确hashCode值,也就是根据对象的地址计算出hashCode值.当类实例的hashCode()方法被重写时,就不能使用hashCode方法唯一标识该对象,但通过identityHashCode()方法返回的hashCode值,依然是根据对象地址得到的hashCode值,是唯一标识.- 赋值数组
System.arraycopy(src, srcPos, dest, destPos, length);
Runtim 类
Runtime类代表Java程序的运行时环境,不能创建Runtime类的对象,但可以通过getRuntime()
获取运行时环境实例.
Rumtime类代表Java程序,可以获取JVM的信息,如处理器数量,内存等.
- 处理器数量:
availableProcessors()
- 空闲内存数:
freeMemory()
- 总内参数:
totalMemory()
- 可用最大内存:
maxMemory()
可以执行一个进程来执行操作系统的指令
exec (notepad.exe)
;
常用类
Object类
API:
boolean equals(Object obj);
protected void finalize();
Class<?> getClass();
int hashCode();
默认是根据对象的地址计算出来的,但很多类都重写了该方法。String toString();
默认是类名@十六进制的hashCode值。clone()
;该方法用于帮助其他对象来实现“自我克隆”,克隆后得到一个副本。
自定义类实现克隆的步骤如下:
- 自定义类实现Cloneable接口,这是一个标记性接口,实现该接口的对象可以实现自我克隆,接口里没有任何方法。
- 自定义类实现自己的clone()方法。
- 实现clone()方法时通过super.clone();调用Object实现的clone()方法来得到该对象的副本,并返回该副本。
Object的克隆方法只是浅克隆,只克隆本对象,对象内部的引用类型并没有克隆而是采用直接引用原对象的属性。若要实现深克隆,需要进行递归克隆。克隆的效率高,例如数组的的克隆比copy方法快乐近2倍。
Java7新增的Objects类
该工具类主要提供“空指针”安全性。例如一个对象不知道是不是为空,调用toString方法时会报错,此时可以使用Objects的toString方法。
API:
- hashCode(obj);
- toString(obj);
- requireNonNull(obj[,String msg]);当传入的参数不为null时,返回该参数;否则引发NullPointerExcepton异常。msg是异常的信息;
String、StringBuffer、StringBuilder类
字符串就是一连串的字符序列(CharSequence可以认为是字符串协议接口,String、StringBuffer和StringBuilder是该接口的实现类);
String是不可变类,StringBuffer对象则是一个字符序列可变的字符串,可以改变字符串,一旦生成了最终想要的字符串,就可以调用toString()方法将其转换为一个String对象。
JDK1.5又新增了一个StringBuilder类,它与StringBuffer相似,只是StringBuffer是线程安全的,而StringBuilder是线程不安全的。但StringBuilder的性能略高,在通常情况下,应该优先考虑使用StringBuilder类;
String
构造器:
String()
;创建一个0个字符串序列的String对象,并不是null;String(byte[] bytes,Charset charset)
;使用指定的字符值将指定的byte[]数组解码成一个新的String对象。String(byte[] bytes,int offset,int length)
;使用平台默认的字符集将指定的byte[]数组从offset开始、长度为length的子数组解码成一个新的String对象。String(byte[] bytes,int offset,int length,String charsetName);
String(byte[] ,String charsetName);
String(char[],int offset,int length);
String(String original);
String(StringBuffer buffer);
String(StringBuilder builder);
API:
char charAt(int index);
int compareTo(String anotherString);
比较两个字符串的大小。如果字符串序列相同,则返回0;不相等时,从两个字符串第0个字符开始比较,返回第一个不相等的字符差。另一种情况,较长字符串的前面部分正好是较短字符串,则返回它们的长度差。String concat(String str)
;字符串拼接,与"+"功能相同。static String copyValueOf(char[] data)
;将字符数组连缀成字符串,如String(char[] content)构造器的功能相同。static String copyValueof(char[] data,int offset,int count);
boolean endsWith(String suffix);
boolean equals(Object anObject);
字符序列完全相同时返回trueboolean equalsIgnoreCase(String str);
byte[] getBytes();
void getChars(int srcBegins,int srcEnd,char[] dst,int dstBegin);
从字符串的srcBegin开始到srcEnd结束的字符复制到dst,复制开始的位置是detBegin;int indexOf(char ch);
int indexOf(char ch,int fromIndex);
int indexOf(Stirng str);
int indexOf(String str,int fromIndex);
int lastIndexOf(char ch);
int lastIndexOf(char ch,int fromIndex);
int lastIndexOf(String str);
int lastIndexOf(String str,int fromIndex);
int length();
String replace(char oldChar,char newChar);
boolean startsWith(String prefix);
boolean startsWith(String prefix,int toffset);
String subString(int beginIndex);
String subString(int beginIndex,int endIndex);
char[] toCharArray()
String toLowerCase();
String toUpperCase();
static String valueOf(X x);
X为基本数据类型
因为String是不可变的,所以操作是会产生很多临时的对象,使用StringBuffer或StringBuffer就可以避免这个问题。
StringBuilder和StringBuffer有两个属性:length和capacity;length表示包含的字符序列的长度,是可以变化的,通过length()和setLength(int len)访问和修改字符序列的长度。capacity属性表示容量,capacity通常比length大,程序通常无须关心capacity属性。
主要方法有:
void append(String);
insert (int index,String str);
replace(int offset,int endIndex,String str);
delete(int offset,int endIndex);
reverse();
setLength(int length)
Math类
Math提供了更多复杂的运算。以及PI及E静态属性。
常用方法 random 生成一个伪随机数,该值大于等于0.0且小于1.0。
Java7的ThreadLocalRandom与Random
Random类专门用于生成一个伪随机数,它有两个构造器,一个构造器使用默认的种子(以当前时间为种子),另一种需要显示传入一个long型整数的种子。
ThredLocalRandom类时Java7新增的一个类,他是Random的增强版。在多线程下使用可以减少多线程资源竞争,具有更好的线程安全性。
具体用法相似,它提供了一个静态方法current()
来获取ThreadLocalRandom对象,获取该对象之后即可调用nextXxx()
方法来获取伪随机数。
相比Math,提供了更多的方式来生成各种伪随机数。
方法:
- xxx nextXxx();生成相应类型的随机数,xxx为基本数据类型。
- byte[] nextBytes(byte[] bytes);生成随机的byte[];
- xxx nextXxx(xxx start,xxx end);生成start与end之间的随机数。
Random产生的随机数并不是真的随机,当种子相同时,产生的的随机数甚至是相同的。值得注意的是同一时间创建的默认对象的种子也是相同的,但没有绝对相同的时间。
BigDecimal类
由于double和float的两种基本 浮点类型的运算会出现精度丢失,Java使用BigDecimal来精确计算浮点数。
常用构造方法:
BigDecimal(double value)
; 该构造器不建议使用,存在一定的不可预知性,会创建一个近似的数,而不是相等的数(同样会造成精度丢失).BigDecimal(String doubleStr)
;
直接使用double来构建BigDecimal时,使用Bigdecimal.valueOf(double doubleValue)
;
BigDecimal类提供了add(),subtract(),multiply(),divide(),pow()等方法对精确浮点数进行常规算数运算;
一般使用BigDecimal来计算浮点数后,需要把BigDecimal再次转化为double进行返回,我们可以直接使用基于BigDecimal为基础的Arith工具类,直接对double进行运算,其内部把double转为BigDecimal,但只能使用简单的运算.
Java8的日期,时间类
Date不仅无法实现国际化,而且它对不同属性也使用了前后矛盾的偏移量,比如月份和小时都是从0开始的,而月份中的天数是从1开始的,年又是从1900开始的.而java.util.Calendar则显得过于复杂.Java8吸取了Joda-Time库的经验,提供了一套全新的日期时间库.
Data 类 java.util.Date
Date类提供了6个构造器,但4个已经Deprecated,只剩下2个:
- Date();生成一个代表当前时间的Date对象.该构造器在底层调用了System.currentTimeMillis(),获得long整数作为日期参数.
- Date(long date);
Date对象的方法:其余的大多数都Deprecated了.
boolean after(Date when)
测试该日期是否在指定日期when之后.boolean before(Date when)
long getTime()
返回该时间对应的long型整数.void setTime(long time)
设置Date对象的时间.
如果需要对日期,时间进行加减运算,或获取指定时间的年,月,日,时,分,秒信息,可以使用Calendar工具类.
Calendar 类
Calendar是一个抽象类,因此不能直接实例化,获取Calendar实例使用的方法是Calendar.getInstance();
,方法根据TimeZone,Locale类来获取特定的Calendar,如果不指定TimeZone,Locale,则使用默认的来创建Calendar.
实例方法:
Date getTime()
获取Date实例;void setTime()
设置Date;void add(int field,int amount)
根据日历的规则,为给定的日历字段添加或减去指定的时间量;int getActualMaximum(int field)
返回指定日历字段可能拥有的最大值.例如月,最大值为11.int getActualMinimum(int field)
返回指定日历字段可能拥有的最小.例如月,最小为0.void roll(int field,int amount)
与add()方法类似,区别在于加上amount后超过该字段所能表示的最大范围时,也不会向上一个字段进位;void set(int field,int value)
;void set(int year,int month,int date)
设置Calendar对象的年月日三个字段的值.void set(int year,int month,int date,int hourOfDay,int minute,int second)
上述方法中的int field参数,可以用Calendar的类变量,如Calendar.YEAR,需要注意的是,月份是从0开始的,因此设置8月时要设置为7
add方法和roll方法的区别:
add
- 如果增加一个域超过限定大小,则进到上一位.如:2018-07-22,增加6个月就会变为2019-2-22.
- 如果下一个域需要变化时,设为变化最小的值,如:2018-07-29,增加6个月就会变为2019-2-28.
roll - 域超过限定大小时,上一位并不会改变.
- 和add相同
设置Calendar的容错性
Calendar cal = Calendar.getInstance();
cal.setLenient(false);
lenient:宽大的,仁慈的
默认情况下是开启lenient模式,当设置Month为13时,年份会进一位;
当setLenient(false)时,
是指Month为13时,会报错.
set()方法延迟修改
set(f,value)方法将f更改为value,尽管日历字段是立即更改了,但Calendar所代表的时间Date却不会立即修改,直到下次调用get(),getTime(),getTimeInMillis(),add()或roll时才会重新计算日历的时间.为了避免不必要的计算(计算日期对应的long);
Java8新增的日期,时间包
Java8专门新增了一个java.time包,该包下包含了如下常用类
- Clock:该类用于获取指定时区的当前日期,时间.该类可取代System类的currentTimeMillis()方法.提供了更多方法获取当前日期,时间.经过测试,Clock获取的时间都是0时区的时间,当使用当地时间调用时,就会产生效果,输出本Clock的本地时间,值得注意的是Clock.systemUTC()获取的是0时区的时钟,systemDefaultZone()获取的是系统默认的时区的Clock。
- Duration:代表持续时间.该类可以非常方便地获取一段时间.
- Instant:代表一个具体地时刻,可以精确到纳秒.该类提供了静态的now()方法,也提供了静态的now(Clock clock)来获取clock对应的时刻.还提供了minusXxx()方法和plusXxx()方法表示在当前时间上增加和减少一段时间.获取的时间也是0时区的
- LocalDate:提供了静态方法now()以及now(Clock clock),也提供了plusXxx()和minusXxx()方法;
- LocalTime;
- LocalDateTime;
- MonthDay; 代表月日例如 04-12提供了now()以及now(Clock clock)
- Year:该类仅代表年,now(),now(Clock clock),plusYears(),minusYears();
- YearMonth:该类仅代表年月;
- ZonedDateTime:该类代表一个时区化的日期,时间.
- ZoneId:代表一个时区.
- DayOfWeek:这是一个枚举类,定义了周日到周六的枚举值.
- Month:这也是一个枚举类,定义了一月到十二月.
public static void main(String[] args) {
System.out.println("测试Clock");
Clock clock = Clock.systemUTC();
System.out.println(clock);
Clock clock1 = Clock.systemDefaultZone();
Clock system = Clock.system(ZoneId.of("+08:00"));
System.out.println(system.instant());
System.out.println("当前时刻是:" + clock.instant());
System.out.println("系统默认时间" + clock1.instant());
System.out.println(new Date());
System.out.println("获取毫秒值");
System.out.println("System.currentTimeMillis : "+System.currentTimeMillis());
System.out.println(clock.millis());
System.out.println("测试Duration");
Duration d = Duration.ofSeconds(6000);
System.out.println("6000秒相当于多少分" + d.toMinutes());
//可以在始终上进行便宜Duration
Clock offset = Clock.offset(clock, d);
System.out.println(offset.instant());
//Instant的用法
Instant instantNow = Instant.now();
System.out.println(instantNow);
//添加60秒,减少用minus方法
Instant plusSeconds = Instant.now().plusSeconds(6000);
System.out.println(plusSeconds);
//添加5小时40分
Instant plus = Instant.now().plus(Duration.ofHours(5).plusMinutes(40));
System.out.println(plus);
//2019-08-28T03:46:15.344Z 一般是这种形式的也可以解析
Instant parse = Instant.parse("2019-08-28T03:46:15.344Z");
System.out.println(parse);
LocalDate cnow = LocalDate.now(clock1);
System.out.println(cnow);
LocalDate now = LocalDate.now();
System.out.println(now);
LocalDate.ofYearDay(2014, 244);
LocalDate.of(2014, Month.FEBRUARY, 23);
LocalTime timeNow = LocalTime.now();
System.out.println(timeNow);
LocalTime timeClock = LocalTime.now(system);
System.out.println(timeClock);
LocalTime UTCTime = LocalTime.now(clock);
System.out.println(UTCTime);
LocalDateTime localDateTime = LocalDateTime.now();
localDateTime.plusHours(25).plusMinutes(3);
Year year = Year.now();
year.plusYears(3);
YearMonth ym = year.atMonth(10);
ym.plusYears(5).minusMonths(3);
}
正则表达式
String提供了一些方法
- boolean matches(String regex);判断字符串是否匹配正则表达式.
- String replaceAll(String regex,String replacement);将该字符串中所有匹配regex的子串替换成replacement.
- String replaceFirst(String regex,String replacement);
- String[] split(String regex);
除此之外,Java还提供了Pattern和Matcher两个专门用于提供正则表达式支持.正则表达式式一个用于匹配字符串的模板.
匹配模式:
- Greedy (贪婪模式):数量表示符(*+)默认采用贪婪模式,除非另有表示.贪婪模式的表达式会一直匹配下去,知道无法匹配为止.
- Reluctant(勉强模式):用问号后缀表示(?),它只会匹配最少的字符.也称为最小匹配模式.
public static void main(String[] args) {
String str = "hello,java!";
System.out.println(str.replaceFirst("\\w*", "@")); //@,java
System.out.println(str.replaceFirst("\\w*?", "@"));//@hello,java!
}
使用正则表达式
一旦在程序中定义了正则表达式,就可以使用Pattern和Matcher来使用正则表达式.
Pattren对象式正则表达式编译后在内存中的表示形式,因此表达式字符串必须被编译为Pattren对象,然后再利用Pattren对象创建的Matcher对象.执行匹配所设计的状态保留再Matcher对象中,多个Matcher对象可共享同一个Pattern对象.
典型的调用顺序如下:
Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaab");
boolean b = m.matches();
//上面定义的Pattern可以重复使用,如果只用一次,可以使用:
boolean b = Pattern.matches("a*b","aaaaaab");
//这个语句实际上等于上面的三局,只是每次不重复使用,每次都要编译,效率较低
Pattern是不可变类,可供多个并发线程安全使用.
Matcher类提供了如下几个常用方法.
- find()返回目标字符串中是否包含于Pattern匹配的子串.
- group()返回上一次与Pattern匹配的子串
- start()返回上一次与Pattern匹配的子串再目标字符串中开始的位置.
- end()返回上一次与Pattern匹配的子串在目标子串中的结束位置加1.
- lookingAt()返回目标字符串前面部分与Pattern是否匹配(加入matches()类似于equels(),那么本方法就类似于startsWith()方法)
- matches()返回整个字符串与Pattern是否匹配.
- reset()将现有的Matcher对象应用于一个新的字符序列.
- replaceAll(String replacement);将匹配的子串替换
- replaceFirst(String replacement);
String str1 = "我想求购一本《疯狂Java讲义》,尽快联系我13500006666"
+ "交朋友,电话号码是13611125565"
+ "出售二手电脑,联系方式15899903312";
Matcher matcher = Pattern.compile("((13\\d)|(15\\d))\\d{8}")
.matcher(str1);
while(matcher.find()) {
System.out.println(matcher.group());
}
//输出结果
//13500006666
//13611125565
//15899903312
fin()方法还可以传入一个int类型的参数,带int参数的find()方法将从int索引处向下搜索.
start()和end()主要用于确定子串在目标字符串中的位置.
String regStr = "Java is very easy!";
Matcher matcher2 = Pattern.compile("\\w+").matcher(regStr);
while(matcher2.find()) {
System.out.println(matcher2.group() + "开始的位置是:" + matcher2.start()
+ "结束的位置是: " + matcher2.end() );
}
国际化与格式化
国际化是指应用程序运行时,可根据客户端请求来自的国家/地区,语言的不同而显式不同的界面.国际化的英文单词时Internationlization,因为单词太长了,有时也简称I18N,其中I是这个单词的第一个字母,18表示中间省略的字母的个数,而N代表这个单词的最后一个字母.
国际化在不同的区域使用时,会呈现本地语言的提示.这个过程也被称为Localization,即本地化.类似于I18N,本地化也称为L10N.
Java8国际化支持升级到了Unicode6.2.0字符集,因此提供了对不同国家和语言的支持.已经有了国际化和本地化的特征及API;
Java国际化的思路
Java程序的国际化思路是将程序中的标签,提示等信息放在资源文件中,程序需要支持哪些国家,语言环境,就对应提供相应的资源文件.资源文件是key-value对,每个资源文件中的key是不变的,但value则随不同的国家,语言而改变.
Java程序的国际化主要通过如下三个类完成:
- java.util.ResouceBundle:用于加载国家,语言资源包;
- java.util.Locale:用于封装特定的国家/区域,语言环境;
- java.text.MessageFormat:用于格式化带占位符的字符串.
资源文件的命名可以有如下三种形式:
- baseName_language_country.properties
- baseName_language.properties
- baseName.properties
其中baseName是资源文件的基本名,用户可随意指定,而language和country都不可随意变化,必须是java所支持的语言和国家.
Java支持的国际和语言
通过Locale.getAvaliableLocales()
方法,返回以个Locale的数组,这个数组包含了Java所支持的国家和语言.
Locale[] locales = Locale.getAvailableLocales();
for(Locale locale: locales) {
System.out.println(locale.getDisplayCountry() +"-->" + locale.getCountry());
System.out.println(locale.getDisplayLanguage() +"-->" + locale.getLanguage());
System.out.println("-------------------------------------------------");
}
完成程序国际化
在资源目录创建基本的信息文件,例如:mess.properties
创建指定的信息文件,例如:mess.zh_CN.properties
java也提供了生成指定文件的指令,但必须要有基本文件:指令设置在bin文件下
native2ascii mess.properties mess.en.US.properties
可以帮助创建文件,并存在默认的key-value
Locale的获取方式:
构造器:
- Locale(String language);
- Locale(String language,String country);
- Locale(String language,String country,String c);
静态方法:
Locale.getDefault(Category categoty);
public static void main(String[] args) {
Locale locale = Locale.getDefault(Locale.Category.FORMAT);
ResourceBundle bundle = ResourceBundle.getBundle("mess", locale);
System.out.println(bundle.getString("hello"));
}
静态属性:
Locale.CHINA
通过改变系统的语言环境设置,可以输出不同的结果;
使用MessageFormat处理包含占位符的字符串
在properties文件中定义key-value值:例如:msg=Hello,{0}!Today is {1}.
public static void main(String[] args) {
Locale locale = Locale.getDefault(Category.FORMAT);
ResourceBundle bundle = ResourceBundle.getBundle("mess", locale);
String msg = bundle.getString("msg");
System.out.println(MessageFormat.format(msg, "程",new Date()));
}
使用Java类文件代替资源文件
使用类文件名必须是BaseName_language_country,这与属性文件的命名相识.
该类必须继承ListResourceBundle,并重写getContents()方法,该方法返回Object数组,该数组的每一项都是key-value对.
例如:
public class myMess_zh_CN extends ListResourceRundle {
private final Object myData[][] =
{
{"msg","{0},你好!今天的日期是{1}"} ...
};
//重写getContents方法
public Object[][] getContents(){
return myData;
}
}
如果系统中同时存在Java类文件和properties资源文件,ResourceBundle搜索资源文件的顺序(如果系统的语言设置为zh_CN)
- baseName_zh_CN.class
- baseName_zh_CN.properties
- baseName_zh.class
- baseName_zh.properties
- baseName.class
- baseName.properties
使用NumberFormat格式化数字
MessageFormat是抽象类Format的子类,与此同时,NumberFormat和DateFormat也Format类的两个类.NumberFormat、DateFormat可以将数值、日期转化成字符串,也可以将字符串转换成为数值、日期。
NumberFormat和DateFormat都包含了format()和pase()方法。
NumberFormat是一个抽象基类,所以无法通过它的构造器来常见NumberFormate对象,它提供了如下几个类方法来得到NumberFormate对象。
- getCurrencyInstance():返回默认Locale的货币格式器。也可以在调用该方法的传入指定的Locale,则获取指定Locale的货币格式器。
- getIntegerInstance():返回默认Locale的整数格式器。也可以在调用该方法的传入指定的Locale。
- getNumberInstance():返回默认Locale的通用数值格式器。也可以在调用该方法的传入指定的Locale。
- getPercentInstance():返回默认的Locale的百分数格式器。也可以在调用该方法的传入指定的Locale。
double db = 1234000.567;
Locale[] locales = {Locale.CHINA,Locale.JAPAN,Locale.US,Locale.GERMAN};
NumberFormat[] nf = new NumberFormat[12];
for(int i = 0 ; i <locales.length ; i++) {
nf[i * 3] = NumberFormat.getNumberInstance(locales[i]);
nf[i * 3 + 1] = NumberFormat.getPercentInstance(locales[i]);
nf[i * 3 + 2] = NumberFormat.getCurrencyInstance(locales[i]);
}
for(int i =0 ; i < locales.length ; i ++) {
String tip = i == 0 ? "中国格式" :
i == 1 ? "日本格式" :
i == 2 ? "美国格式" :"德国格式";
System.out.println(tip);
System.out.println("通用数值格式:"+ nf[i*3].format(db));
System.out.println("百分比格式:"+ nf[i*3 + 1].format(db));
System.out.println("货币格式:"+ nf[i*3 + 2].format(db));
}
}
至于NumberFormat类将字符串解析成数值的意义不大(因为可以使用Integer,Double等包装类可以完成解析)
使用DateFormat格式化日期,时间
与NumberFormat相似的是,DateFormat也是一个抽象类,它也提供了如下几个类方法用于获取DateFormat对象.
- getDateInstance():返回一个日期格式器,它格式化的字符串只有日期没有时间.可以传入多个参数,用于指定日期样式和Locale等参数;如果不指定这些参数,这使用默认参数.
- getTimeInstance():返回一个时间格式器,它格式化后的字符串只有时间,没有日期.可以传入多个参数.
- getDateTimeInstance();返回一个日期,时间格式器,格式化后的字符串既有日期,也有时间.可以传入多个参数.
上面的三个方法可以指定日期样式、时间样式参数,它们是DateFormat的4个静态常量:FULL、LONG、MEDIUM和SHORT,通过这4个样式参数可以控制生成的格式化字符串。
Date dt = new Date();
Locale[] locales = {Locale.CHINA,Locale.US};
DateFormat[] df = new DateFormat[16];
for(int i = 0 ; i < locales.length; i++) {
df[i * 8] = DateFormat.getDateInstance(DateFormat.SHORT, locales[i]);
df[i * 8 + 1] = DateFormat.getDateInstance(DateFormat.MEDIUM, locales[i]);
df[i * 8 + 2] = DateFormat.getDateInstance(DateFormat.LONG, locales[i]);
df[i * 8 + 3] = DateFormat.getDateInstance(DateFormat.FULL, locales[i]);
df[i * 8 + 4] = DateFormat.getTimeInstance(DateFormat.SHORT, locales[i]);
df[i * 8 + 5] = DateFormat.getTimeInstance(DateFormat.MEDIUM, locales[i]);
df[i * 8 + 6] = DateFormat.getTimeInstance(DateFormat.LONG, locales[i]);
df[i * 8 + 7] = DateFormat.getTimeInstance(DateFormat.FULL, locales[i]);
}
for(int i = 0 ; i < locales.length ; i ++) {
String tip = i == 0 ? "--中国日期格式--" : "美国日期格式";
System.out.println(tip);
System.out.println("SHORT格式的日期格式:" + df[i * 8].format(dt));
System.out.println("MEDIUM格式的日期格式:" + df[i * 8 + 1].format(dt));
System.out.println("LONG格式的日期格式:" + df[i * 8 + 2].format(dt));
System.out.println("FULL格式的日期格式:" + df[i * 8 + 3].format(dt));
System.out.println("SHORT格式的时间格式:" + df[i * 8 + 4].format(dt));
System.out.println("MEDIUM格式的时间格式:" + df[i * 8 + 5].format(dt));
System.out.println("LONG格式的时间格式:" + df[i * 8 + 6].format(dt));
System.out.println("FULL格式的时间格式:" + df[i * 8 + 7].format(dt));
}
获得DateFormate之后,可以调用它的setLenient(boolean lenient)
方法来设置该格式器是否采用严格的语法.不严格时,参数为true,对于字符串2004-2-31将会转换为2004年3月2日;如果采用严格的日期语法,解析该字符串将抛出错误.
DateFormat的pase()方法,把字符串解析为Date对象,但要求被解析的字符串必须符合日期字符串的要求.对应SHORT…的要求,否则可能抛出异常;
7.6.8 使用SimpleDateFormat格式化日期
前面介绍了DateFormat的Pase()方法,但它的格式较为单一,必须符合满足对应SHORT,MEDIUM,LONG,FULL对应的格式,为了更好地格式话日期,解析日期字符串,Java提供了SimpleDateFormat类;
它时DateFormat的子类.仅仅需要给出一个简单的模板就能解析日期.
日期模板如: Gyyyy年中第D天 y###MMM##d
对应 公元2014年中第101天 14###三月##21
如果想知道更多的日期,时间占位符,可以查阅API文档中SimpleDateFormat类的说明
Java8中的时间格式
DateTimeFormaater,获取方式:
- 直接使用静态常量创建DateTimeFormatter格式器.DateTimeFormatter类提供了大量形如ISO_LOCAL_DATE,ISO_LOCAL_TIME,ISO_LOCAL_DATE_TIME等静态常量,这些静态常量就是DateTimeFormatter实例.
- 使用不同风格的枚举值来创建DateTimeFormatter格式器.在FormatStyle枚举类中定义了FULL,LONG,MEDIUM,SHORT四个枚举值,分别代表日期时间的不同风格.
- 根据模式字符串来创建DateTimeFormatter格式器,类似于SimpleDateFormat,如果需要了解DateTimeFormatter支持哪些模式字符串,则需要参考API文档;
使用DateTimeFormatter完成格式化
可通过如下两种方式:
- 调用DateTimeFormatter的format(TemporalAccessor temporal) 方法格式化,其中LacalDate、LocalDateTime、LocalTime等都是TemporalAccessor接口的实现类。
- 调用LocalDate、LocalDateTime、LocalTime等日期、时间对象的formate(DateTimeFormatter formatter)方法执行格式化。
public static void main(String[] args) {
DateTimeFormatter[] formatters = new DateTimeFormatter[] {
DateTimeFormatter.ISO_LOCAL_DATE,
DateTimeFormatter.ISO_LOCAL_TIME,
DateTimeFormatter.ISO_LOCAL_DATE_TIME,
//使用不同风格来创建
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM),//使用当地的locale国际化
DateTimeFormatter.ofLocalizedTime(FormatStyle.LONG),
DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"),//kk和HH代表24制的
DateTimeFormatter.ofPattern("Gyyyy%%MMM%%%dd HH:mm:ss")
};
LocalDateTime now = LocalDateTime.now();
for (int i = 0; i < formatters.length; i++) {
String format = formatters[i].format(now);
System.out.println(format);
}
}
结果:
2019-08-29
15:30:49.828
2019-08-29T15:30:49.828
2019年8月29日 星期四 15:30:49
下午03时30分49秒
2019-08-29 15:30:49
公元2019%%八月%%%29 15:30:49
p308
w DateTimeFormatter[] {
DateTimeFormatter.ISO_LOCAL_DATE,
DateTimeFormatter.ISO_LOCAL_TIME,
DateTimeFormatter.ISO_LOCAL_DATE_TIME,
//使用不同风格来创建
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM),//使用当地的locale国际化
DateTimeFormatter.ofLocalizedTime(FormatStyle.LONG),
DateTimeFormatter.ofPattern(“yyyy-MM-dd hh:mm:ss”),//kk和HH代表24制的
DateTimeFormatter.ofPattern(“Gyyyy%%MMM%%%dd HH:mm:ss”)
};
LocalDateTime now = LocalDateTime.now();
for (int i = 0; i < formatters.length; i++) {
String format = formatters[i].format(now);
System.out.println(format);
}
}
结果:
2019-08-29
15:30:49.828
2019-08-29T15:30:49.828
2019年8月29日 星期四 15:30:49
下午03时30分49秒
2019-08-29 15:30:49
公元2019%%八月%%%29 15:30:49
p308