Java基础温习2

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中继承的方法有:

  1. int compareTo(E o);如果该枚举对象位于指定枚举对象之后,则返回正整数;之前则返回负整数;否则返回零;
  2. String name();返回枚举实例的名称,和toStirng一样;
  3. String toString();
  4. int ordinal();返回该枚举实例的索引;
  5. 类方法 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()方法(清理对象资源,也有可能是可恢复状态变回可达状态),对对象做最后的处理.

  1. 可达状态
  2. 可恢复状态 没有变量指向这个对象,但还没有调用finalize()方法
  3. 不可达状态 调用了finalize()方法,但依旧没有变量指向该对象

强制执行垃圾回收

  1. System.gc();
  2. Runtime.getRuntime().gc();
    但不能确定系统是否进行垃圾回收?何时进行垃圾回收?
    当jvm执行finalize()方法时出现异常时,垃圾回收机制不会报告异常,程序继续执行.
    由于finalize方法不一定会被执行,所以清理资源的方法最后不要放再finalize()方法中.

强行执行finalize方法,需要在强制执行垃圾回收后执行.

System.runFinalization();
Runtime.getRuntime().runFinalization();

对象的软、弱和虚引用

在java.lang.ref包下提供了3个类:SoftReference、PhantomReference和weakReference,它们分别代表了系统对对象的3中引用方式:软引用、虚引用、和弱引用。因此java语言对对象的引用有4中方式:

  1. 强引用(StrongReference)这是Java程序中最常用的引用方式。程序创建一个对象,并把对象赋值给一个引用变量。处于可达状态。
  2. 软引用(SoftReference)软件需要通过SoftReference类来实现,当一个对象只有软引用时,它有可能被垃圾回收机制回收。对于软引用的对象来说,当系统内存足够时,它不会被系统回收。当系统内存不足时,就会对其进行回收。软引用通常用于对内存敏感的程序中。
  3. 弱引用(WeakReference)通过WeakReference类实现,弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引用的对象而言,当系统垃圾回收运行时,不管系统内存是否足够,总会回收该对象所占用的内存。
  4. 虚引用(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;
要求如下:

  1. 每行只能定义一个key-value对,每行的key-value对之前不能有空格,即key-value对必须顶格写。
  2. 每组key-value对之间以:":"分割,少些了冒号或者是空格都是错误的。
  3. 文件开头不能有空行。
  4. 文件必须以一个空行结束。

Scanner

Scanner是一个基于正则表达式的文本扫描器,它可以从文件、输入流、字符串中解析出基本数据类型和字符串值。Scanner类提供了多个构造器,不同的构造器可以接收文件、输入流、字符串作为数据源。用于从文件、输入流、字符串中解析数据。
Scanner主要提供了两个方法来扫描输入。

  1. hasNestXxx():是否还有下一个输入项,其中Xxx可以是Int、Long等代表基本数据类型的字符串。如果只是判断是否包含下一个字符串,则直接使用hasNext();
  2. nextXxx();获取下一个输入项。
  • 在默认情况下,Scan使用空白(包含空格、Tab、回车)作为多个输入项之间的分隔符。
  • 使用确定的分割符时,调用sc.useDelimiter("\n");
    当不希望使用空格作为分隔符时,例如想要每次读取一行,可以使用上行方法改为回车。
    为Scanner设置分隔符使用userDelimiter(Stirng paattern),该方法的参数应该是一个正则表达式。
    实际上Scanner已经提供了读取一行的方法:
  1. boolean hasNextLine();返回输入源是否有下一行。
  2. String nextLine();返回输入源中的下一行字符串。

Scanner 读取文件内容

`Scanner sc = new Scanner(new File("ScannerFileTest.java"));`

系统相关

Java在不同操作系统上运行时,可能需要取得平台相关的属性,或者调用平台命令来完成特定功能。Java提供了System类和Runtime类来与程序的平台进行交互。

System 类

带表Java程序的运行平台,程序不能创建System类的对象,System类提供了一些变量和类方法,允许直接通过System类来调用这些类变量和方法。
System类提

  • 供了代表标准输入、标准输出和错误输出的类变量;
  • 提供一些静态方法用于访问环境变量、系统属性的方法
  • 加载文件和动态链接库的方法(主要对native方法有用)

API:

  1. Map<String,String> env = System.getenv();获取系统所有的环境变量
  2. String getenv("JAVA_HOME");获取指定的环境变量;
  3. Properties System.getProperties()获取所有的属性;
  4. 将属性保存到文件中(props.store(new FileOutputStream(“props.txt”),“System Properties”));文件将保留在项目根目录,而不是当前目录或者时资源目录,与src平级
  5. String System.getProperty("os.name");
  6. 获取时间currentTimeMillis();和nanoTime();
  7. System.identityHashCode(Object x)返回指定对象的精确hashCode值,也就是根据对象的地址计算出hashCode值.当类实例的hashCode()方法被重写时,就不能使用hashCode方法唯一标识该对象,但通过identityHashCode()方法返回的hashCode值,依然是根据对象地址得到的hashCode值,是唯一标识.
  8. 赋值数组System.arraycopy(src, srcPos, dest, destPos, length);

Runtim 类

Runtime类代表Java程序的运行时环境,不能创建Runtime类的对象,但可以通过getRuntime()获取运行时环境实例.
Rumtime类代表Java程序,可以获取JVM的信息,如处理器数量,内存等.

  • 处理器数量:availableProcessors()
  • 空闲内存数:freeMemory()
  • 总内参数:totalMemory()
  • 可用最大内存:maxMemory()

可以执行一个进程来执行操作系统的指令
exec (notepad.exe);

常用类

Object类

API:

  1. boolean equals(Object obj);
  2. protected void finalize();
  3. Class<?> getClass();
  4. int hashCode();默认是根据对象的地址计算出来的,但很多类都重写了该方法。
  5. String toString();默认是类名@十六进制的hashCode值。
  6. 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

构造器:

  1. String();创建一个0个字符串序列的String对象,并不是null;
  2. String(byte[] bytes,Charset charset);使用指定的字符值将指定的byte[]数组解码成一个新的String对象。
  3. String(byte[] bytes,int offset,int length);使用平台默认的字符集将指定的byte[]数组从offset开始、长度为length的子数组解码成一个新的String对象。
  4. String(byte[] bytes,int offset,int length,String charsetName);
  5. String(byte[] ,String charsetName);
  6. String(char[],int offset,int length);
  7. String(String original);
  8. String(StringBuffer buffer);
  9. 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);字符序列完全相同时返回true
  • boolean 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属性。
    主要方法有:
  1. void append(String);
  2. insert (int index,String str);
  3. replace(int offset,int endIndex,String str);
  4. delete(int offset,int endIndex);
  5. reverse();
  6. setLength(int length)

Math类

Math提供了更多复杂的运算。以及PI及E静态属性。
常用方法 random 生成一个伪随机数,该值大于等于0.0且小于1.0。

Java7的ThreadLocalRandom与Random

Random类专门用于生成一个伪随机数,它有两个构造器,一个构造器使用默认的种子(以当前时间为种子),另一种需要显示传入一个long型整数的种子。
ThredLocalRandom类时Java7新增的一个类,他是Random的增强版。在多线程下使用可以减少多线程资源竞争,具有更好的线程安全性。
具体用法相似,它提供了一个静态方法current()来获取ThreadLocalRandom对象,获取该对象之后即可调用nextXxx()方法来获取伪随机数。
相比Math,提供了更多的方式来生成各种伪随机数。
方法:

  1. xxx nextXxx();生成相应类型的随机数,xxx为基本数据类型。
  2. byte[] nextBytes(byte[] bytes);生成随机的byte[];
  3. 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个:

  1. Date();生成一个代表当前时间的Date对象.该构造器在底层调用了System.currentTimeMillis(),获得long整数作为日期参数.
  2. Date(long date);

Date对象的方法:其余的大多数都Deprecated了.

  1. boolean after(Date when)测试该日期是否在指定日期when之后.
  2. boolean before(Date when)
  3. long getTime()返回该时间对应的long型整数.
  4. void setTime(long time)设置Date对象的时间.

如果需要对日期,时间进行加减运算,或获取指定时间的年,月,日,时,分,秒信息,可以使用Calendar工具类.

Calendar 类

Calendar是一个抽象类,因此不能直接实例化,获取Calendar实例使用的方法是Calendar.getInstance();,方法根据TimeZone,Locale类来获取特定的Calendar,如果不指定TimeZone,Locale,则使用默认的来创建Calendar.
实例方法:

  1. Date getTime()获取Date实例;
  2. void setTime()设置Date;
  3. void add(int field,int amount)根据日历的规则,为给定的日历字段添加或减去指定的时间量;
  4. int getActualMaximum(int field)返回指定日历字段可能拥有的最大值.例如月,最大值为11.
  5. int getActualMinimum(int field)返回指定日历字段可能拥有的最小.例如月,最小为0.
  6. void roll(int field,int amount)与add()方法类似,区别在于加上amount后超过该字段所能表示的最大范围时,也不会向上一个字段进位;
  7. void set(int field,int value);
  8. void set(int year,int month,int date)设置Calendar对象的年月日三个字段的值.
  9. 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

  1. 如果增加一个域超过限定大小,则进到上一位.如:2018-07-22,增加6个月就会变为2019-2-22.
  2. 如果下一个域需要变化时,设为变化最小的值,如:2018-07-29,增加6个月就会变为2019-2-28.
    roll
  3. 域超过限定大小时,上一位并不会改变.
  4. 和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包,该包下包含了如下常用类

  1. Clock:该类用于获取指定时区的当前日期,时间.该类可取代System类的currentTimeMillis()方法.提供了更多方法获取当前日期,时间.经过测试,Clock获取的时间都是0时区的时间,当使用当地时间调用时,就会产生效果,输出本Clock的本地时间,值得注意的是Clock.systemUTC()获取的是0时区的时钟,systemDefaultZone()获取的是系统默认的时区的Clock。
  2. Duration:代表持续时间.该类可以非常方便地获取一段时间.
  3. Instant:代表一个具体地时刻,可以精确到纳秒.该类提供了静态的now()方法,也提供了静态的now(Clock clock)来获取clock对应的时刻.还提供了minusXxx()方法和plusXxx()方法表示在当前时间上增加和减少一段时间.获取的时间也是0时区的
  4. LocalDate:提供了静态方法now()以及now(Clock clock),也提供了plusXxx()和minusXxx()方法;
  5. LocalTime;
  6. LocalDateTime;
  7. MonthDay; 代表月日例如 04-12提供了now()以及now(Clock clock)
  8. Year:该类仅代表年,now(),now(Clock clock),plusYears(),minusYears();
  9. YearMonth:该类仅代表年月;
  10. ZonedDateTime:该类代表一个时区化的日期,时间.
  11. ZoneId:代表一个时区.
  12. DayOfWeek:这是一个枚举类,定义了周日到周六的枚举值.
  13. 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提供了一些方法

  1. boolean matches(String regex);判断字符串是否匹配正则表达式.
  2. String replaceAll(String regex,String replacement);将该字符串中所有匹配regex的子串替换成replacement.
  3. String replaceFirst(String regex,String replacement);
  4. 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类提供了如下几个常用方法.

  1. find()返回目标字符串中是否包含于Pattern匹配的子串.
  2. group()返回上一次与Pattern匹配的子串
  3. start()返回上一次与Pattern匹配的子串再目标字符串中开始的位置.
  4. end()返回上一次与Pattern匹配的子串在目标子串中的结束位置加1.
  5. lookingAt()返回目标字符串前面部分与Pattern是否匹配(加入matches()类似于equels(),那么本方法就类似于startsWith()方法)
  6. matches()返回整个字符串与Pattern是否匹配.
  7. reset()将现有的Matcher对象应用于一个新的字符序列.
  8. replaceAll(String replacement);将匹配的子串替换
  9. 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程序的国际化主要通过如下三个类完成:

  1. java.util.ResouceBundle:用于加载国家,语言资源包;
  2. java.util.Locale:用于封装特定的国家/区域,语言环境;
  3. 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的获取方式:
构造器:

  1. Locale(String language);
  2. Locale(String language,String country);
  3. 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)

  1. baseName_zh_CN.class
  2. baseName_zh_CN.properties
  3. baseName_zh.class
  4. baseName_zh.properties
  5. baseName.class
  6. 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对象.

  1. getDateInstance():返回一个日期格式器,它格式化的字符串只有日期没有时间.可以传入多个参数,用于指定日期样式和Locale等参数;如果不指定这些参数,这使用默认参数.
  2. getTimeInstance():返回一个时间格式器,它格式化后的字符串只有时间,没有日期.可以传入多个参数.
  3. 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完成格式化

可通过如下两种方式:

  1. 调用DateTimeFormatter的format(TemporalAccessor temporal) 方法格式化,其中LacalDate、LocalDateTime、LocalTime等都是TemporalAccessor接口的实现类。
  2. 调用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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值