类加载:是指将类的class文件读入内存,并为之创建一个Java.lang.class对象。即当线程使用任何一个类时,系统都会为之创建一个java.lang.Class对象。可以在第一次使用某个类时加载该类,或者预加载机制。
java.lang.Class类封装一个对象和接口运行时的状态,当加载类时Class类型的对象将自动创建,Class没有公共构造方法,其对象是JVM在类加载时通过类加载器中的defineClass()方法自动构造的,不能显示的实例化一个class对象。
Class常用方法 | |
---|---|
static Class forName(String className) | 返回指定类名的Class对象 |
T newInstance() | 调用默认的构造方法,返回该Class对象的一个实例 |
String getName() | 返回Class对象锁对应的类名 |
Constructor>[] | getConstructors() |
ConstructorgetConstructors(Class>…parameterType) | 返回Class对象所对应的类的指定参数列表的public构造方法 |
Constructor>[]getDeclaredConstructors() | 返回Class对象所对应的所有构造方法,与访问权限无关 |
ConstructorgetDeclaredConstructors(Class>…parameterTypes) | 返回Class对象所对应类的所有构造方法,与访问权限无关 |
Method[] getMethod() | 返回Class对象所对应类的所有public方法 |
Method getMethod(String name,Class>…parameterType) | 返回Class对象所对应的指定参数列表的public方法 |
Method[] getDeclaredMechods() | 返回Class对象所对应的所有方法,与访问权限无关 |
Method getDeclaredMethod(String name,Class>…parameterTypes) | 返回Class对象对应类的指定参数列表的方法,与访问权限无关 |
Field[] getFields() | 返回Class对象所对应类的所有public成员变量 |
Field getField(String name) | 返回Class对象所对应的类的指定参数的public成员变量 |
Field[] getDeclaredFields() | 返回Class对象所对应类的所有public成员变量 |
Field getDeclaredField() | 返回Class对象所对应类所有的成员变量,与访问权限无关 |
Class>getDeclaringClasses()) | 返回Class对象所对应的外部类 |
Class>[] getDeclaredClasses() | 返回Class对象所对应的类里包含的所有内部类 |
Class super T>getSuperclass() | 返回Class对象所对应的类里的父类的Class对象 |
int getModifiers() | 返回Class对象所对应类的修饰符,返回的整数是修饰符的对应常量,需要是使用Modified工具类解码 |
Package getPackage() | 返回Class对象所对应的包 |
Class [] getInterfaces() | 返回Class对象所对应类实现的所用接口 |
Class LoadergetClassLoader() | 返回该类的类加载器 |
boolean isArray() | 判断Class对象是否表示一个数组类 |
boolean isEnum() | 判断Class对象是否表示一个枚举 |
boolean isInterface() | 判断Class对象是否表示一个接口 |
boolean isInstance(Object obj) | 判断obj对象是否是该Class对象的一个实例 |
boolean isAnnottation() | 返回Class对象是否标识一个注解类型 |
Annotation [] getAnnotation() | 返回Class对象所对应类上存在的所有注解 |
< A extends Annotation>A getAnnotation(Class annotationClass ) | 返回Class对象所对应类上存在的指定类型的注解 |
每个类被加载之后,会生成一个Class对象,通过Class对象可以访问JVM中该类的信息,一旦类被载入JVM中,同一个类将不会被再次载入,被载入的类都有一个唯一的标识,是该类得到全限定类名(包括包名和类名)。
在Java中获取Class对象的三种方式:
1,使用Class类的forName(String classNmae)静态方法,参数class代表所需要类的全限定类名。
2,调用某个类的class属性来获取该类对应的Class对象,对象.class;
3,调用某个类的getclass()方法来获取该类对应的class对象,该方法是Object类中的一个方法.
通过
类的Class属性获得该类所对应的Class对象,会始代码更安全。程序性更好,
string类型的字符串不能使用String.class方式。需要使用Class.forName(“java.lang.String”)
Class的forName()方法声明抛出ClassNotFoundException异常,调用必须捕获或抛出异常。
package com.liruilong.ClassDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Date;
public class ClassDemo {
public ClassDemo() {
// TODO Auto-generated constructor stub
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("--------String 的Class类对象-----------");
try {
Class strClass = Class.forName("java.lang.String");
System.out.println("Class对象:"+strClass);
}catch(ClassNotFoundException e){
e.printStackTrace();
System.out.println("有错");
}
System.out.println("++++++++Float的Class类对象++++++++");
Class fClass = Float.class;
System.out.println(fClass);
System.out.println("+++++++++Date类的Class类对象+++++++");
Date nowTime = new Date();
Class deteClass = nowTime.getClass();
System.out.println(deteClass);
System.out.println("--------Date类的父类:-----------");
System.out.println(deteClass.getSuperclass());
System.out.println("--------获取所有的Date类的构造方法:----------");
Constructor[] ctors = deteClass.getDeclaredConstructors();
for(Constructor c: ctors) {
System.out.println(c);
}
System.out.println("------Date类的所有public方法--------");
Method[] mtds = deteClass.getMethods();
for(Method m:mtds) {
System.out.println(m);
}
try {
@SuppressWarnings("deprecation")
Object obj = deteClass.newInstance();
System.out.println(obj);
}catch (Exception e) {
// TODO: handle exception
}
}
}
类加载步骤:当程序使用某个未加载到内存中的类时,系统将通过加载,连接和初始化三个步骤对类进行初始化.
类的加载:由JVM提供的类加载器完成,程序运行的基础,由JVM提供的类加载器通常被称为系统类加载器,开发者可以通过继承ClassLoader类创建自己的类加载。
使用不同的类加载器,可以从不同来源加载类的二进制数据。类加载器通常无需在首次使用类时加载该类,JVM允许系统预先加载某些类。
1,从本地文件系统或jar包中加载.class文件,大部分文件常用。
2,通过网络加载.class文件。
3,从java源代码文件动态编译成.class 文件,执行时加载。
类的连接:将二进制数据合并到JRE中,类连接分为三个阶段:
1,验证阶段:检验被加载的类是否有正确的内部结构,并和其它类协调一致;
2,准备阶段:负责为类的类变量分配内存,并设置默认初始值。
3,解析阶段:将类的二进制数据中的符合引用替换成直接引用。
类的初始化:是指针对类变量进行初始化,当程序使用任何一个类时,JVM会保证该类及其所有祖先类都被初始化,JVM初始化一个类包含以下几个步骤:
1,如果类没有被加载和连接,则程序先加载并连接该类。
2,如果类的直接父类未被初始化,则先初始化其直接父类。
3,如果类中有初始化语句,则直接执行初始化语句。
类加载器:
类加载器负责将磁盘上或网络上的.class文件加载到内存中,并为之生成对应的java.lang.Class对象。
类加载器负责加载所有的类,系统为所有被载入内存的类生成相应的Class对象,一旦类被载入class中,同一个类将不会被重复载入,被载入的类拥有一个唯一标识(全限定类名)。
JVM启动时,会形成由三个类加载器组成的初始化类加载器层次结构:(用户可以定义自己的类加载器)
1,Bootstrap ClassLoader:根类加载器,负责加载java的核心类库,例如:rt.jar包;
2,Extension ClassLoader:扩展类加载器,负责加载JRE的扩展目录中的jar包即%JAVA_HOME%/jre/lib/ext目录或者java.ext.dirs系统属性指定的目录。
3,System ClassLoader:系统类加载器,负责在JVM启动时加载来自java命令的-classpath选项,java.class.path系统属性或CLASSPATH环境变量中所指的jar包和类路径。
类加载器机制三种;
1,全盘负责:当一个类加载器加载类时,该类所依赖的其他类也将有该类加载器负责,除非显示使用类加载器载入。
2,父类委托:先尝试使用父类加载器来加载,父类加载器无法加载时尝试使用自己类路径中的加载器。
3,缓存机制:保证所有加载过得类都被缓存,当程序使用某个类时,类加载器先存缓存区中搜索该类,Class对象不存在时,才会读取该类的二进制文件,将其转化为Class文件并存入缓存中去。
类加载器大致经过步骤:(背面)
ClassLoader类:
java.lang.ClassLocader是一个抽象类,通过继承ClassLocader类来实现自定义的用户类加载器,以扩展JVM动态加载了类的方式,
方法 | 描述 |
---|---|
public Class>loadClass(String name) | 根据指定的名称加载类 |
protected Class > loadClass (String name,boolean reslve) | 根据指定的名称加载类, |
protexted Class > findClass(String name) | 根据指定类名查找类 |
propected final Class >findLoadedClass(String name) | 该方法时java类加载缓存机制的体现,如果JVM已经加载指定的类,则直接返回该类对应的Class实例,否则返回null |
protected final Class >defineClass(String name,byte[] b,int off,int len) | 将指定的来源于文件的或网络上的字节码文件(即.class文件)读入字节数组中,并转换为class对象。 |
protected final Class> findSystemClass() | 重本地系统文件中装入文件 |
public static ClassLoader getSystemClassLoader() | 用于返回系统类加载器的静态方法 |
public final ClassLoader getParent() | 获取当前类加载的父类加载器 |
public final void resolveClass(Class>c) | 链接指定的表 |
实现自定义类的加载器,可以通过重写ClassLoader类的LoadClass()或findClass()方法来实现,因为重写findClass()方法能够避免覆盖默认类加载器的父类委托和缓存机制两种策略,因此推存重载findClass方法。
package com.liruilong.ClassDemo;
import java.io.File;
import java.io.FileInputStream;
class Animal{
public void say() {
System.out.println("这是一个Animal类");
}
}
public class MyClassLoader extends ClassLoader {
public MyClassLoader() {
// TODO Auto-generated constructor stub
}
@Override
public Class<?>findClass(String className){
byte[] data = loadClassData(className);
return this.defineClass(className,data,0,data.length);
}
private byte[] loadClassData(String className) {
try{
String path = this.getClass().getResource("/").getPath();//查找具有改定名称的资源,返回url对象。
path = path.substring(1);
className = className.replace(".", "/");
File classFile = new File(path+className+".class");
long len = className.length();
byte[] rew = new byte[(int)len];
FileInputStream fin = new FileInputStream(classFile);
int r = fin.read(rew);
if(r != len) {
System.out.println("无法读取全部文件");
return null;
}else {
return rew;
}
}catch(Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
// TODO Auto-generated method stub
MyClassLoader mcl = new MyClassLoader();
Class<?>clazz = mcl.loadClass("com.liruilong.ClassDemo.Animal");
Animal animal = (Animal)clazz.newInstance();
animal.say();
}
}
java中对象在运行时会出现两种类型:编译时类型和运行时类型。
在程序运行时获取对象的真实信息有一下两种做法:
1,在知道对象类型的情况下,可以使用instanceof运算符进行判断,然后转型。
2,无法预知,必须通过反射来发现该对象和类的真实信息。
反射(Reflection)机制允许程序在运行时借助Reflection API取得任何类的内部信息,并不能直接操作对象的内部属性及方法。反射被视为动态语言的关键。
java反射机制主要提供了以下功能:
1,在运行时判断任意一个对象所属的类;
2,在运行时构造任意一个类的对象。
3,在运行时获取任意一个类所具有的成员变量和方法。
4,在运行时调用任意一个对象的方法。
5,生成动态代理。
Reflection API 提供了 Constructor,Field和Method类,这三个类定义在java.lang.reflect包中,分别用于描述类的构造方法,属性和方法。
Executable抽象类:java8在java.lang.reflect包下新增了一个Executable抽象类,代表可执行的类成员。Executable抽象类派生了Constructor和Method两个子类。Executable抽象类提供了大量方法来获取参数,修饰符或注解等信息。
方法 | 描述 |
---|---|
parameter [] getparameters() | 获取所有形参,返回一个parameter [] 数组 |
int getParameterCount() | 获取形参个数 |
abstract int getModifiers() | 获取修饰符,返回的整数是修饰符关键字对应的常量 |
boolean isVarArgs() | 判断是否包含数量可变的形参 |
Constructor类:用于表示类的构造方法。通过Class的getConstructor()方法来获取构造方法的集合。
方法 | 描述 |
---|---|
String getName() | 返回构造器的名称 |
Class [] getParameterTypes() | 返回当前构造方法的参数类型 |
int getModifiers() | 返回修饰符的整型标识,返回的整数是修饰符是标识常量,需要使用Modified工具类方法解码Modified.toSting(int mod),可以通过Modified.PUBLIC 查看对应的值 |
Method类:用于封装方法的信息,调用Class对象的getMethods()方法或getMethod()可以获取当前类的所有方法或指定的方法。
常用方法 | 功能描述 |
---|---|
String getName() | 返回方法的名称 |
Class[] getparameterType() | 返回方法的参数类型 |
int getModifieds() | 返回修饰符的整型标识 |
Class getReturnType() | 返回当前方法的返回类型 |
Field类:用于封装属性信息,调用Class对象的getFields()或getField()方法可以获取当前类的所有属性或指定属性。
常用方法 | 描述 |
---|---|
String getName() | 获取属性的名称 |
int getMOdifiers() | 返回修饰符的整型标识 |
getXxx(Object obj) | 获取属性的值,此处的Xxx对应的java8中的基本类型,如果属性是引用类型,直接使用get(Object obj)方法 |
setXxx(Object obj,Xxx val) | 设置属性的值,此处的Xxx对应Java8中的基本类型,如果属性是引用类型,直接使用set(Object obj,Object val)方法 |
Class [] getType() | 返回当前属性的类型 |
文件对象.setAccessible(true):设置通过反射访问该成员变量时取消访问权限检查。
调用getDeclaredFields()方法可以获取包括私有和受保护的所有属性,但不包括父类的属性;调用getField方法可以获得所有的public属性。包括从父类继承的。
parameter类:是JAVA8中新增的API,每个paramtete 对象代表一个参数。Parameter类提供许多方法来获取参数信息
方法 | 功能 |
---|---|
int getModifiers() | 获取参数的修饰符 |
String getName() | 获取参数的形参名 |
Type getparameterizedType() | 获取带泛型的形参类型 |
Class>getType() | 获取形参类型 |
boolean isVarArgs() | 判断该参数是否为可变参数 |
boolean isNamePreaent() | 判断.class文件中是否包含方法的形参名信息 |
package com.liruilong.reflec;
import com.liruilong.algorithm.Solution;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @Description : 创建对象的两种方法
* @Author: Liruilong
* @Date: 2019/9/21 21:42
*/
public class ClassDemo {
public static void main(String[] args) {
try {
Class aClass = Class.forName("com.liruilong.algorithm.Solution");
Solution solution = (Solution) aClass.newInstance();
Constructor constructor = aClass.getConstructor();
// 获取所有方法信息
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods ){
System.out.println(method.toString());
}
// 获取当前类的所有的成员变量
Field[] field = aClass.getDeclaredFields();
for (Field field1 : field){
System.out.println(field1.toString());
}
System.out.println("获取构造方法"
);
// 获取当前类的所有的构造方法
Constructor[] constructors = aClass.getDeclaredConstructors();
for (Constructor constructor1 : constructors){
System.out.println(constructor1.toString());
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
Object类的.class文件默认是不包含参数信息的。
定义枚举的关键字为enum,枚举类是一种特殊的类。(不能继承其他类,不能被继承)
与普通类的区别:
1, 枚举类可以实现一个或多个接口,使用enum定义的类继承java.lang.Enum类,不是Objec类,因此枚举类不能显示继承其他父类。
2,使用enum定义的非抽象的枚举类时默认会使用final修饰,因此枚举类 不能派生子类。
3,枚举类的构造方法只能使用private访问修饰符,如果省略,默认使用private修饰,强制指定只能是private。
4,枚举类的所有实例必须在枚举类的类体的第一行显示列出,否则该枚举类永远不能产生实例。实例默认public static final修饰。
定义枚举类:
[修饰符]enum 枚举类名{
//第一行列举枚举实例
}
public enum SeasonEnum{
SPRING,SUMMER,SPRINGS("春");
}
枚举类一旦被定义,就可以直接使用该类型的枚举实例,枚举实例的声明和使用方式类似于基本类型,但不能使用new关键字实例化一个枚举类型。枚举类型包含的预定义方法。
方法 | 功能描述 |
---|---|
public static enumtype [] values() | 返回一个枚举类型的数组,包含该枚举类的所有实例值 |
public static enumtype valueOf(Stirng str) | 返回指定名称的枚举实例值。 |
使用枚举类的实例:枚举类.实例;
包含属性和方法的枚举类:可以定义枚举的属性和方法以及构造方法,在定义枚举类构方法时,不能定义public构造方法,枚举类的构造方法只能使用private或缺省。
Enum类:
所有的枚举类都继承自java.lang.Enum该类定义了枚举类公共的方法,java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口。Enum常用方法:
方法 | 描述 |
---|---|
final int ordinal() | 返回枚举类实例值在类中的序号,该序号与申明顺序有关,从0开始 |
final int compareTo (enumtype e) | Enum实现了java.lang.Comparable接口,因此可以用于比较 |
boolean equals(Object other) | 比较两个枚举引用的对象是否相等 |
public String toStirng() | 返回枚举实例的名称,一般情况下不重写 |
public static |
返回指定枚举类型和指定名称的枚举实例值; |
注解(Annotation):在程序中可以对任何元素进行注解,包括java包,类,构造器,域,方法参数以及局部变量。注解就像修饰符一样,使用时在其前面增加@符号,信息被存储在"name=value"键值对中。希望程序中的注解在运行时起到一定的作用,需要使用配套的工具对注解中的信息进行访问和处理,这种工具称为APT(Annotation Processing Tool注解处理工具)
注解元数据的作用:
编写文档:通过注解中标识的元数据可以生生doc文档。
代码分析:通过注解中标识元数据,使用反射对代码进行分析。
编译检查:通过注解中标识的元数据,让编译器能够实现基本的编译检查。
java.lang.annotation.Annotation是一个接口,该接口时所有注解类型都要扩展的公共接口,但该接口本身不能定义注解类型。不能手动扩展。
基本注解:
@Override:用于限定重写的父类方法,使用该注解修饰的父类方法必须重写父类中的方法,否则发生编译错误。
@Deprecated:用于标识某个元素是否已过时,当程序使用以过时的类方法时,编译器会出警告。
@SuppressWarnings:用于抑制编译警告的发布,允许开发人员取消显示指定的编译器警告。参数见151笔记,会一直作用域该元素的所有子元素。
@FunctionalInterface:java8中新增,用于指定某个接口必须是函数接口。java8规定,如果一个接口只有一个抽象方法,则该接口就是函数式接口。是为Lambda表达式准备的,java8允许使用lambda表达式来创建函数式接口。
@SafeVarags:在java7中新增,用于抑制“堆污染”警告。堆污染。将一个不带泛型的变量赋值给一个带泛型的变量将导致该变量污染。
List list = new ArrayList();
list.add(10);
list<Stirng>ls = list;//发生堆污染
System.out.printin(ls.get(0));
抑制堆污染的警告:
使用@Safevarags注解修饰引发该警告的方法。使用@SuppressWarnins(“unchecked”)修饰。编译时使用-Xlint:varargs选项。、
元注解
JDK提供元注解,负责注解其他注解,java8在java.lang.annotation包中提供6个元注解。
@Target(ElementType.TYPE_USE)
@interface AnnotationDemoS{
}
public class AnnotationDemo implements @AnnotationDemoS Serializable{
public static void main( @AnnotationDemoS String[] args){
}
}
定义注解。定义一个注解类型与定义一个接口相似,在interface前面加@,语法:
[访问符] @interface 注解名{ …………}
@Target(ElementType.TYPE_USE)
@interface AnnotationDemoS{
String commaa();
int order() default 2;
}
自定义注解的成员由未实现的的方法组成,在使用时实现,在定义注解时,可以使用default语句声明注解成员的默认值
使用注解:注解大多是为其他工具提供运行信息或决策依据而设计的,任何的java程序都可以通过反射机制来查询注解信息。在java的java.lang.reflect包中新增一个AnnotationElement接口。用于反射过程中获取注解信息。
Annotation getAnnotation (Class annotype) | 返回调用的对象的注解 |
Annotation getAnnotations() | 返回调用对象的所有注解 |
Annotation geyDeclareedAnnotations() | 返回对象的所有非继承注解 |
Boolean isAnnotationPreset(Class annotype) | 判断与调用对象关联的注解是由annoType指定的 |
import java.io.Serializable;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)//最长注解
@Target(ElementType.TYPE_USE)
@interface AnnotationDemoS{
String commaa();
int order() default 2;
}
@AnnotationDemoS(commaa = "as")
public class AnnotationDemo implements Serializable{
public static void main( String[] args){
AnnotationDemoS annotationDemo = AnnotationDemo.class.getAnnotation(AnnotationDemoS.class);
System.out.println(annotationDemo);
输出:
@AnnotationDemoS(order=2, commaa=as)
为使反射机制获取注解的相关信息,在定义注解时必须将注解的保留策略设置为RetentionPolicy.RUNTIME。否则获取不到注解对象。程序将会引发NullPointerException异常.
国际化: 简写为I18N ,源于Internationalization一词I和N中间有18个字符,国际化在设计阶段就应该使其具备多种语言功能,当需要在应用中添加对一种语言或国家的支持时,无需对已有的软件进行重构。
本地化:(loclization,L10N)是设计和编写能够处理特定区域的,国家或地区,语言,文化或政治环境的应用程序的过程。
国际化的思路抽取具备语言特性描述到资源文件。java语言本身采用Unicode编码,在java中,解决国际化问题大部分类位于java.util包,相关的类有:Locale,ResourceBundle,ListResourceBundle,PropertyResourceBundle等。
Locale类:主要包含对地理区域化特征的封装,其特定对象表示某一特定的地理,政治或文化区域。一个Locale实例代表一种特定的语言和地区,可以通过Locale对象中的信息来输出其对应语言和地区的时间,日期,数字等格式,
Locale常用方法
Locale(String language) | 构造language指定语言的Locale对象 |
Locale(String language,String country) | 构造language指定语言和country指定国家的Locale对象 |
String getCountry() | 返回国家代码 |
String get DisplayCountry() | 返回国家名称 |
String getLanguage() | 返回语言代码 |
String getDisplayLanguage() | 返回语言的名称 |
Static Locale getDefault | 获取当前系统信息的对应的Locale对象 |
Static viod setDefault(new Locale) | 从新设置默认的Locale对象 |
Locale[] locales = Locale.getAvailableLocales();
Pattern pattern = Pattern.compile("zh");
for (int i = 0; i < locales.length; i++) {
Matcher matcher = pattern.matcher(locales[i].getLanguage());
if (matcher.matches()){
System.out.println("国家:" + locales[i].getDisplayCountry() + ":" + locales[i].getCountry());
System.out.println("语言:" + locales[i].getDisplayLanguage() + ":" + locales[i].getLanguage());
}else ;
}
在使用Locale时。指定的语言及国家信息需要java支持才通过,调用Locale.getAvailableLocales()方法取得当前java 支持的所有国家和语言。
ResourceBundle类:用于加载国家和语言资源包。
资源文件的命名方式:baseName_language_countrry.properties,baseName_language.properties, baseName.properties
baseName是资源文件的基本名称,可以随意命名:language和country必须是java支持的。
public static final ResourceBundle getBundle(String baseName) | 使用指定的基本名称,默认的语言环境和调用者的类加载器获取资源包 |
public Locale getLocale() | 返回键的枚举 |
public final Object getObject(String key) | 返回此资源包的语言环境 |
public final String[] getString(String key) | 从此资源包或其某个妇包中获得给定的键的字符串 |
ResourceBundle resourceBundle = ResourceBundle.getBundle("resourceName",Locale.getDefault());
System.out.println(resourceBundle.getString(""));
依赖Locale类,java类提供格式器来完成对数字,货币,日期以及消息的格式化。
不同的国家,数字的表示方式不一样。在java的java.text包中提供了一个NumberFormat类,用于完成对数字,百分比进行格式化和队对字符串对象进行解析。NumberFormat提供静态方法用于获取使用指定的Locale对象封装的NumberFormat实例,
static NumberFormate getNumberInstance() | 返回当前系统信息相关的默认的数字格式器 |
static NumberFormat getNumberInstance(Locale l) | 返回指定Locale为l 的数字格式器对象 |
static NumberFormat getPercentInstance() | 返回当前系统信息相关的默认百分比格式器 |
static NumberFormat getPercentInstance(Locale l) | 返回指定Locale为I 的百分比格式器对象 |
static NumberFormat getCurrencyInstance() | 返回当前系统信息相关的默认货币格式器 |
String format(double number) | 将数字number格式化为字符串返回 |
Number parse() | 将指定的字符串解析为Number对象 |
double value = 324234234.334543;
Locale locale = new Locale("zh","CN");
Locale locale1 = new Locale("en","US");
Locale locale2 = new Locale("de","DE");
NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
NumberFormat numberFormat4 = NumberFormat.getPercentInstance();
NumberFormat numberFormat1 = NumberFormat.getNumberInstance(locale1);
NumberFormat numberFormat2 = NumberFormat.getNumberInstance(locale2);
System.out.println(numberFormat.format(value));
System.out.println(numberFormat1.format(value));
System.out.println(numberFormat4.format(value));
java中日期和时间的格式化是通过DateFormat类来完成,该类的使用方与NumberForamat相似。
常用方法
static DateFormat getDateInstance() | 返回默认样式的日期格式器 |
static DateFormat getDateInstance(int style) | 返回默认指定样式的日期格式器 |
static DateFormat getDateInstance(int style,Locale alocale) | 返回默认指定样式和Locale信息的日期格式器 |
static DateForm getTimeInstance() | 返回默认样式的时间格式器(后两个一样) |
static DateFormat getDateTimeInstance() | 返回默认日期时间的格式器(后两个一样) |
常用样式控制:DateFormat,FULL DateFormat.LONG DateFormat.DEFAULT DateFormat.SHORT;
DateFormat dateFormat = DateFormat.getDateTimeInstance();
Date date = new Date();
System.out.println(dateFormat.format(date));
除了DateFormat类,java还提供日期格式器SimpleDateFormat类,通过预定义的模式字符串构造特定的模式串,然后根据模式串来创建SimpleDateFormat格式器对象。
DateFormat dateFormat = DateFormat.getDateTimeInstance();
Date date = new Date();
System.out.println(dateFormat.format(date));
Date date1 = new Date(System.currentTimeMillis());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
System.out.println(simpleDateFormat.format(date));
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("今年是:YYYY 年");
System.out.println(simpleDateFormat1.format(date));
模式字符 | 功能描述 | 模式字符 | 功能描述 | 模式字符 | 功能描述 |
---|---|---|---|---|---|
D | 一年中的第几天 | y | 年 | H(h) | 小时(0-11) |
d | 一月中的第几天 | s | 秒 | S | 毫秒 |
E | 星期中的第几天 | m | 分钟 | M | 月份 |
如果需要的模式串中使用的字符串(字符)不被SimpleDateformat 解释,可以在模式串中将其用单引号括起来。
SimpleDateformat 一般不用于国际化处理,而是为了以特定模式输出日期和时间。本地化使用。
国际化软件需要根据用户的本地化消息输出不同的格式,即动态实现消息的格式化,有java.text.MessageFormat类可以实现消息的动态处理。方法
方法 | 功能呢描述 |
---|---|
public MessageFormat(String pattern) | 构造方法,根据指定的模式字符串,构造默认语言环境下的MessageFormat对象 |
public Messageformat(Stringpattern,Locale locale) | 构造方法,根据指定的模式字符串和语言环境,构造该语言环境下的MessageFormat对象 |
public void applyPattern(String pattern) | 设置模式字符串 |
public String toPattern() | 返回消息格式当前状态的模式字符串 |
public setLocale(Locale locale) | 用于设置创建或比较子格式时所使用的语言环境, |
public final String format(Object obj) | 格式化一个对象以生成一个字符串,该方法是其父类Foramt的方法 |
public String formt(String pattern,Object…arguments) | 创建具有给定模式的MessageFormat对象,并使用该对象来格式化改定的参数 |
参数pattern为一个带占位符的模式字符串,可以根据实际情况使用实际的值来替换字符串的占位符,在模式字符串中,占位符用{}括起来。
{ n [,formatType][,formatStyle]}
//其中n代表占位符的索引,formatType代表格式类型,标识数字,日期,货时间,代表格式样式,用于具体的样式,如货币,完整日期等。
分类 | 格式类型 | 格式样式 | 功能描述 |
---|---|---|---|
数字 | number | integer currency percent #.## | 整型,货币,百分比,小数 |
日期 | date | full long medium short | 完整, 长,中等,短 |
时间 | time | full long medium short | 完整, 长,中等,短 |
通常使用MessageFormat进行消息格式化的步骤如下:
1,创建模式字符串,其动态变化的部分使用占位符代替,每个占位符可以重复出现多次。
2,根据模式字符串构造MessageFormat(pattern)对象。
3,创建Locale对象,并调用setLocale()方法设置MessageFormat对象的语言环境。
4,创建一个对象数组,并按照占位符的索引来组织数据。
5,调用forma()方法实现消息的格式化,将对象数组作为参数。
public static void main(String[] args) {
String pattern = "{0},开学快乐,欢迎你在{1,date,long}访问本系统,现在是{1,time,long}";
MessageFormat format = new MessageFormat(pattern);
Locale locale = Locale.getDefault();
format.setLocale(locale);
Object [] msgParams = {"lrl",new Date()};
System.out.print(format.format(msgParams));
}
在java8中新增一个DateTimeFormatter格式器类,相当于DateFormat和SimpleDateFormat的综合体,可以将时间日期对象格式化为字符串,还可以将特定的字符串解析为时间日期对象。
使用DateTimeFormatter进行格式化和解析,必须获取DateTimeFormatter对象,有三种方法:
LocalDateTime date = LocalDateTime.now();
//LocalDateTime是一个不可变的日期时间对象,代表日期时间,这个类是不可变的和线程安全的。
//static LocalDateTime now()//从默认时区的系统时钟获取当前的日期时间。
DateTimeFormatter[] da = new DateTimeFormatter[] {
DateTimeFormatter.ISO_DATE_TIME,
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG,FormatStyle.MEDIUM),
DateTimeFormatter.ofPattern("yyyy-MM-dd ")};
for(DateTimeFormatter d:da)
System.out.println(d.format(date));
允许在接口中定义默认方法和静态方法,默认方法又称扩展方法,需要在方法前使用default关键字修饰。静态方法就是类方法,需要在方法前使用static关键字修饰。都必须有方法体。
默认方法需要通过接口实现类访问。静态方法可以直接接口访问。
:支持将代码做为方法参数,允许使用更加方便的代码创建一个抽象方法的接口实例。实际上为一种匿名方法,由参数列表,箭头,方法体组成。
(参数列表)->{方法体}
//参数列表中的参数do都是匿名方法等待形参,允许省略形参的类型,即可以为明确类型或者推断类型,当参数是推断类型时,参数的数据类型将由jvm根据上下文自动推断。
//->为Lambda运算符
//方法体可以为单一的表达式或多条语句组成的语句块,如果只有一条语句,则允许省略方法体{},如果只有一条return语句,可以省略return,Lambda表达式需要返回值,如果方法体中仅有一条省略了return关键字的语句,则Lambda表达式会自动返回该语句的结果值。
(int i ,int j)->{
System.out.printin(x);
System.out.printin(y);
return x+y;
}
//如果参数类型只包含一个推断类型的参数时,可以省略小括号即
参数名->{方法体}
//只有一个参数和一条语句的Lambda表达式
参数名->表达式
//没有参数的Lambda表达式
()->{方法体}
Lambda的应用:
使用Lambda表达式输出集合内容,
String []string = {"sdf","sdf","sdfsdf"};
List<String> name = Arrays.asList(string);
name.forEach((names)->System.out.println(names));//Lambda表达式方式
name.forEach(System.out::println);//双冒号表达式,用于方法的引用
使用Lambda表达式实现排序:
List<String> arrNames = Arrays.asList("liruilong","shanhewuyang","qingchong");
Collections.sort(arrNames, new Comparator<String>() {//调用容器工具类排序方法。
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}
);
arrNames.forEach((x)-> System.out.println(x));
System.out.println("______________________________");
List<String> arrNamess = Arrays.asList("liruilong","shanhewuyang","qingchong");
Collections.sort(arrNamess,(a,b)->b.compareTo(a));
arrNamess.forEach(System.out::println);
//比较器:public interface Comparator强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
//int compare(T o1, T o2)比较用来排序的两个参数。根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。
:本质上是只有一个抽象方法的普通接口,可以被隐式的转换为Lambda表达式,需要用注解定义。默认方法和静态方法可以不属于抽象方法,可以在函数式接口中定义。
@FunctionalInterface
public interface 接口名{
//只有一个抽象方法;
}
函数式接口中可以额外定义多个抽象方法,但这些抽象方法签名必须和Object的public方法一样接口最终有确定的类实现, 而类的最终父类是Object。 因此函数式接口可以定义Object的public方法。
如以下的接口依然是函数式接口:
@FunctionalInterfacepublic
interface ObjectMethodFunctionalInterface {
void count(int i);
String toString(); //same to Object.toString
int hashCode(); //same to Object.hashCode
boolean equals(Object obj); //same to Object.equals
}
为什么限定public类型的方法呢?因为接口中定义的方法都是public类型的。 举个例子,下面的接口就不是函数式接口:
interface WrongObjectMethodFunctionalInterface {
void count(int i);
Object clone(); //Object.clone is protected
}
因为Object.clone方法是protected类型。
Lambda表达式只能为函数接口创建对像,即Lambda表达式只能实现具有一个抽象方法的接口,且给接口必须是有@FunctionlInterface 注释修饰。
:可以使用“::”来简化lambda表达式,由三部分组成,左边是容器,可以是类名或实例名,右边为方法名,可用于静态方法,实例方法,以及构造方法 ,语法:
容器::方法名
静态: 类名::静态方法名。
实例: 对象::实例方法名
构造: 类名:: new
@FunctionalInterface
public interface LrlDemo <F,T>{
T convert(F form);
}
LrlDemo<String,Integer> con = new LrlDemo<String, Integer>() {
@Override
public Integer convert(String form) {
return Integer.valueOf(form);
}
};
System.out.println(con.convert("34"));
LrlDemo<String,Integer> cos = form -> Integer.valueOf(form);
//LrlDemo cos = form -> Integer::valueOf;
LrlDemo<String,Integer> coss = Objects::hashCode;
System.out.println( cos.convert("45"));
System.out.println(con.convert("34")+cos.convert("45"));
}
无论是方法引用还是Lambda表达式,其最终原理和所实现的就是当某一个类中,或接口中某一个方法,其入口参数为一个接口类型时,使用方法引用或Lambda表达式可以快速而简洁地实现这个接口,不必繁琐的通过创建一个这个接口的实例对象。
Optional类:
空指针异常是导致程序运行失败的常见原因,goodle公司引入Optional类,通过使用检查空值的方式来防止代码污染。Option类实际为一个容器,额可以保存类型T得到值吗,或仅保存null,Option提供许多方法。