备注:如有不正之处,请多谅解并欢迎批评指正。转载请标明连接:https://www.cnblogs.com/pmbb/p/11409443.htmljavascript
枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之因此特殊是由于它既是一种类(class)类型却又比类类型多了些特殊的约束,可是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。下面先来看看什么是枚举?如何定义枚举?html
回忆一下下面的程序,这是在没有枚举类型时定义常量常见的方式java
1 public class DayDemo { 2 3 public static final int MONDAY =1; 4 5 public static final int TUESDAY=2; 6 7 public static final int WEDNESDAY=3; 8 9 public static final int THURSDAY=4; 10 11 public static final int FRIDAY=5; 12 13 public static final int SATURDAY=6; 14 15 public static final int SUNDAY=7; 16 17 }
上述的常量定义常量的方式称为int枚举模式,这样的定义方式并无什么错,但它存在许多不足,如在类型安全和使用方便性上并无多少好处,若是存在定义int值相同的变量,混淆的概率仍是很大的,编译器也不会提出任何警告,所以这种方式在枚举出现后并不提倡,如今咱们利用枚举类型来从新定义上述的常量,同时也感觉一把枚举定义的方式,以下定义周一到周日的常量python
1 //枚举类型,使用关键字enumenum Day { 2 MONDAY, TUESDAY, WEDNESDAY, 3 THURSDAY, FRIDAY, SATURDAY, SUNDAY 4 }
至关简洁,在定义枚举类型时咱们使用的关键字是enum,与class关键字相似,只不过前者是定义枚举类型,后者是定义类类型。枚举类型Day中分别定义了从周一到周日的值,这里要注意,值通常是大写的字母,多个值之间以逗号分隔。同时咱们应该知道的是枚举类型能够像类(class)类型同样,定义为一个单独的文件,固然也能够定义在其余类内部,更重要的是枚举常量在类型安全性和便捷性都颇有保证,若是出现类型问题编译器也会提示咱们改进,但务必记住枚举表示的类型其取值是必须有限的,也就是说每一个值都是能够枚举出来的,好比上述描述的一周共有七天。那么该如何使用呢?以下:程序员
1 public class EnumDemo { 2 3 public static void main(String[] args){ 4 //直接引用 5 Day day =Day.MONDAY; 6 } 7 8 } 9 //定义枚举类型 10 enum Day { 11 MONDAY, TUESDAY, WEDNESDAY, 12 THURSDAY, FRIDAY, SATURDAY, SUNDAY 13 }
就像上述代码那样,直接引用枚举的值便可,这即是枚举类型的最简单模型。算法
咱们大概了解了枚举类型的定义与简单使用后,如今有必要来了解一下枚举类型的基本实现原理。实际上在使用关键字enum建立枚举类型并编译后,编译器会为咱们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说经过关键字enum建立枚举类型在编译后事实上也是一个类类型并且该类继承自java.lang.Enum类。下面咱们编译前面定义的EnumDemo.java并查看生成的class文件来验证这个结论:数据库
//查看目录下的java文件 zejian@zejiandeMBP enumdemo$ ls EnumDemo.java //利用javac命令编译EnumDemo.java zejian@zejiandeMBP enumdemo$ javac EnumDemo.java //查看生成的class文件,注意有Day.class和EnumDemo.class 两个 zejian@zejiandeMBP enumdemo$ ls Day.class EnumDemo.class EnumDemo.java
利用javac编译前面定义的EnumDemo.java文件后分别生成了Day.class和EnumDemo.class文件,而Day.class就是枚举类型,这也就验证前面所说的使用关键字enum定义枚举类型并编译后,编译器会自动帮助咱们生成一个与枚举相关的类。咱们再来看看反编译Day.class文件:express
1 //反编译Day.class 2 final class Day extends Enum 3 { 4 //编译器为咱们添加的静态的values()方法 5 public static Day[] values() 6 { 7 return (Day[])$VALUES.clone(); 8 } 9 //编译器为咱们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法 10 public static Day valueOf(String s) 11 { 12 return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s); 13 } 14 //私有构造函数 15 private Day(String s, int i) 16 { 17 super(s, i); 18 } 19 //前面定义的7种枚举实例 20 public static final Day MONDAY; 21 public static final Day TUESDAY; 22 public static final Day WEDNESDAY; 23 public static final Day THURSDAY; 24 public static final Day FRIDAY; 25 public static final Day SATURDAY; 26 public static final Day SUNDAY; 27 private static final Day $VALUES[]; 28 29 static 30 { 31 //实例化枚举实例 32 MONDAY = new Day("MONDAY", 0); 33 TUESDAY = new Day("TUESDAY", 1); 34 WEDNESDAY = new Day("WEDNESDAY", 2); 35 THURSDAY = new Day("THURSDAY", 3); 36 FRIDAY = new Day("FRIDAY", 4); 37 SATURDAY = new Day("SATURDAY", 5); 38 SUNDAY = new Day("SUNDAY", 6); 39 $VALUES = (new Day[] { 40 MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY 41 }); 42 } 43 }
从反编译的代码能够看出编译器确实帮助咱们生成了一个Day类(注意该类是final类型的,将没法被继承)并且该类继承自java.lang.Enum类,该类是一个抽象类(稍后咱们会分析该类中的主要方法),除此以外,编译器还帮助咱们生成了7个Day类型的实例对象分别对应枚举中定义的7个日期,这也充分说明了咱们前面使用关键字enum定义的Day类型中的每种日期枚举常量也是实实在在的Day实例对象,只不过表明的内容不同而已。注意编译器还为咱们生成了两个静态方法,分别是values()和 valueOf(),稍后会分析它们的用法,到此咱们也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每一个在枚举类型中定义好变量的对应实例对象,如上述的MONDAY枚举类型对应public static final Day MONDAY;,同时编译器会为该类建立两个方法,分别是values()和valueOf()。ok~,到此相信咱们对枚举的实现原理也比较清晰,下面咱们深刻了解一下java.lang.Enum类以及values()和valueOf()的用途。编程
Enum是全部 Java 语言枚举类型的公共基本类(注意Enum是抽象类),如下是它的常见方法:windows
返回类型 方法名称 方法说明
Int compareTo(E o) 比较此枚举与指定对象的顺序
boolean equals(Object other) 当指定对象等于此枚举常量时,返回 true。
Class<?> getDeclaringClass() 返回与此枚举常量的枚举类型相对应的 Class对象
String name() 返回此枚举常量的名称,在其枚举声明中对其进行声明
int ordinal() 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)
String toString() 返回枚举常量的名称,它包含在声明中
static<T extends Enum<T>> T static valueOf(Class<T> enumType, String name) 返回带指定名称的指定枚举类型的枚举常量。
这里主要说明一下ordinal()方法,该方法获取的是枚举变量在枚举类中声明的顺序,下标从0开始,如日期中的MONDAY在第一个位置,那么MONDAY的ordinal值就是0,若是MONDAY的声明位置发生变化,那么ordinal方法获取到的值也随之变化,注意在大多数状况下咱们都不该该首先使用该方法,毕竟它老是变幻莫测的。compareTo(E o)方法则是比较枚举的大小,注意其内部实现是根据每一个枚举的ordinal值大小进行比较的。name()方法与toString()几乎是等同的,都是输出变量的字符串形式。至于valueOf(Class<T> enumType, String name)方法则是根据枚举类的Class对象和枚举名称获取枚举常量,注意该方法是静态的,后面在枚举单例时,咱们还会详细分析该方法,下面的代码演示了上述方法:
1 package com.zejian.enumdemo; 2 3 /** 4 * Created by zejian on 2017/5/7. 5 * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] 6 */ 7 public class EnumDemo { 8 9 public static void main(String[] args){ 10 11 //建立枚举数组 12 Day[] days=new Day[]{Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY, 13 Day.THURSDAY, Day.FRIDAY, Day.SATURDAY, Day.SUNDAY}; 14 15 for (int i = 0; i <days.length ; i++) { 16 System.out.println("day["+i+"].ordinal():"+days[i].ordinal()); 17 } 18 19 System.out.println("-------------------------------------"); 20 //经过compareTo方法比较,实际上其内部是经过ordinal()值比较的 21 System.out.println("days[0].compareTo(days[1]):"+days[0].compareTo(days[1])); 22 System.out.println("days[0].compareTo(days[1]):"+days[0].compareTo(days[2])); 23 24 //获取该枚举对象的Class对象引用,固然也能够经过getClass方法 25 Class<?> clazz = days[0].getDeclaringClass(); 26 System.out.println("clazz:"+clazz); 27 28 System.out.println("-------------------------------------"); 29 30 //name() 31 System.out.println("days[0].name():"+days[0].name()); 32 System.out.println("days[1].name():"+days[1].name()); 33 System.out.println("days[2].name():"+days[2].name()); 34 System.out.println("days[3].name():"+days[3].name()); 35 36 System.out.println("-------------------------------------"); 37 38 System.out.println("days[0].toString():"+days[0].toString()); 39 System.out.println("days[1].toString():"+days[1].toString()); 40 System.out.println("days[2].toString():"+days[2].toString()); 41 System.out.println("days[3].toString():"+days[3].toString()); 42 43 System.out.println("-------------------------------------"); 44 45 Day d=Enum.valueOf(Day.class,days[0].name()); 46 Day d2=Day.valueOf(Day.class,days[0].name()); 47 System.out.println("d:"+d); 48 System.out.println("d2:"+d2); 49 } 50 /** 51 执行结果: 52 day[0].ordinal():0 53 day[1].ordinal():1 54 day[2].ordinal():2 55 day[3].ordinal():3 56 day[4].ordinal():4 57 day[5].ordinal():5 58 day[6].ordinal():6 59 ------------------------------------- 60 days[0].compareTo(days[1]):-1 61 days[0].compareTo(days[1]):-2 62 clazz:class com.zejian.enumdemo.Day 63 ------------------------------------- 64 days[0].name():MONDAY 65 days[1].name():TUESDAY 66 days[2].name():WEDNESDAY 67 days[3].name():THURSDAY 68 ------------------------------------- 69 days[0].toString():MONDAY 70 days[1].toString():TUESDAY 71 days[2].toString():WEDNESDAY 72 days[3].toString():THURSDAY 73 ------------------------------------- 74 d:MONDAY 75 d2:MONDAY 76 */ 77 78 } 79 enum Day { 80 MONDAY, TUESDAY, WEDNESDAY, 81 THURSDAY, FRIDAY, SATURDAY, SUNDAY 82 }
到此对于抽象类Enum类的基本内容就介绍完了,这里提醒你们一点,Enum类内部会有一个构造函数,该构造函数只能有编译器调用,咱们是没法手动操做的,不妨看看Enum类的主要源码:
1 //实现了Comparable 2 public abstract class Enum<E extends Enum<E>> 3 implements Comparable<E>, Serializable { 4 5 private final String name; //枚举字符串名称 6 7 public final String name() { 8 return name; 9 } 10 11 private final int ordinal;//枚举顺序值 12 13 public final int ordinal() { 14 return ordinal; 15 } 16 17 //枚举的构造方法,只能由编译器调用 18 protected Enum(String name, int ordinal) { 19 this.name = name; 20 this.ordinal = ordinal; 21 } 22 23 public String toString() { 24 return name; 25 } 26 27 public final boolean equals(Object other) { 28 return this==other; 29 } 30 31 //比较的是ordinal值 32 public final int compareTo(E o) { 33 Enum<?> other = (Enum<?>)o; 34 Enum<E> self = this; 35 if (self.getClass() != other.getClass() && // optimization 36 self.getDeclaringClass() != other.getDeclaringClass()) 37 throw new ClassCastException(); 38 return self.ordinal - other.ordinal;//根据ordinal值比较大小 39 } 40 41 @SuppressWarnings("unchecked") 42 public final Class<E> getDeclaringClass() { 43 //获取class对象引用,getClass()是Object的方法 44 Class<?> clazz = getClass(); 45 //获取父类Class对象引用 46 Class<?> zuper = clazz.getSuperclass(); 47 return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper; 48 } 49 50 51 public static <T extends Enum<T>> T valueOf(Class<T> enumType, 52 String name) { 53 //enumType.enumConstantDirectory()获取到的是一个map集合,key值就是name值,value则是枚举变量值 54 //enumConstantDirectory是class对象内部的方法,根据class对象获取一个map集合的值 55 T result = enumType.enumConstantDirectory().get(name); 56 if (result != null) 57 return result; 58 if (name == null) 59 throw new NullPointerException("Name is null"); 60 throw new IllegalArgumentException( 61 "No enum constant " + enumType.getCanonicalName() + "." + name); 62 } 63 64 //.....省略其余没用的方法 65 }
经过Enum源码,能够知道,Enum实现了Comparable接口,这也是可使用compareTo比较的缘由,固然Enum构造函数也是存在的,该函数只能由编译器调用,毕竟咱们只能使用enum关键字定义枚举,其余事情就放心交给编译器吧。
1 //由编译器调用 2 protected Enum(String name, int ordinal) { 3 this.name = name; 4 this.ordinal = ordinal; 5 }
values()方法和valueOf(String name)方法是编译器生成的static方法,所以从前面的分析中,在Enum类中并没出现values()方法,但valueOf()方法仍是有出现的,只不过编译器生成的valueOf()方法需传递一个name参数,而Enum自带的静态方法valueOf()则须要传递两个方法,从前面反编译后的代码能够看出,编译器生成的valueOf方法最终仍是调用了Enum类的valueOf方法,下面经过代码来演示这两个方法的做用:
Day[] days2 = Day.values();
System.out.println("day2:"+Arrays.toString(days2));
Day day = Day.valueOf("MONDAY");
System.out.println("day:"+day);
/**
输出结果:
day2:[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
day:MONDAY
*/
从结果可知道,values()方法的做用就是获取枚举类中的全部变量,并做为数组返回,而valueOf(String name)方法与Enum类中的valueOf方法的做用相似根据名称获取枚举变量,只不过编译器生成的valueOf方法更简洁些只需传递一个参数。这里咱们还必须注意到,因为values()方法是由编译器插入到枚举类中的static方法,因此若是咱们将枚举实例向上转型为Enum,那么values()方法将没法被调用,由于Enum类中并无values()方法,valueOf()方法也是一样的道理,注意是一个参数的。
上述咱们提到当枚举实例向上转型为Enum类型后,values()方法将会失效,也就没法一次性获取全部枚举实例变量,可是因为Class对象的存在,即便不使用values()方法,仍是有可能一次获取到全部枚举实例变量的,在Class对象中存在以下方法:
返回类型 |
方法名称 |
方法说明 |
|
|
返回该枚举类型的全部元素,若是Class对象不是枚举类型,则返回null。 |
|
|
当且仅当该类声明为源代码中的枚举时返回 true |
所以经过getEnumConstants()方法,一样能够垂手可得地获取全部枚举实例变量下面经过代码来演示这个功能:
1 //正常使用 2 3 Day[] ds=Day.values(); 4 5 //向上转型Enum 6 7 Enum e = Day.MONDAY; 8 9 //没法调用,没有此方法 10 11 //e.values(); 12 13 //获取class对象引用 14 15 Class<?> clasz = e.getDeclaringClass(); 16 17 if(clasz.isEnum()) { 18 19 Day[] dsz = (Day[]) clasz.getEnumConstants(); 20 21 System.out.println("dsz:"+Arrays.toString(dsz)); 22 23 }
/**
输出结果:
dsz:[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
*/
正如上述代码所展现,经过Enum的class对象的getEnumConstants方法,咱们仍能一次性获取全部的枚举实例常量。
关于枚举与switch是个比较简单的话题,使用switch进行条件判断时,条件参数通常只能是整型,字符型。而枚举型确实也被switch所支持,在java 1.7后switch也对字符串进行了支持。这里咱们简单看一下switch与枚举类型的使用:
1 /** 2 * Created by zejian on 2017/5/9. 3 * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] 4 */ 5 6 enum Color {GREEN,RED,BLUE} 7 8 public class EnumDemo4 { 9 10 public static void printName(Color color){ 11 switch (color){ 12 case BLUE: //无需使用Color进行引用 13 System.out.println("蓝色"); 14 break; 15 case RED: 16 System.out.println("红色"); 17 break; 18 case GREEN: 19 System.out.println("绿色"); 20 break; 21 } 22 } 23 24 public static void main(String[] args){ 25 printName(Color.BLUE); 26 printName(Color.RED); 27 printName(Color.GREEN); 28 29 //蓝色 30 //红色 31 //绿色 32 } 33 }
更多枚举知识:http://www.javashuo.com/article/p-wngwtdfk-ca.html
String字符串 具体看StringApi文档:https://www.runoob.com/manual/jdk1.6/java/lang/String.html
字符串经常使用方法:http://www.javashuo.com/article/p-kyqingnh-ep.html
在学习String类时,API中说字符串缓冲区支持可变的字符串,什么是字符串缓冲区呢?接下来咱们来研究下字符串缓冲区。
查阅StringBuffer的API,线程安全的可变字符序列。一个相似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但经过某些方法调用能够改变该序列的长度和内容。
1 String s = "abcd";
2 s = s+1;
3 System.out.print(s);// result : abcd1
咱们明明就是改变了String型的变量s的,为何说是没有改变呢? 其实这是一种欺骗,JVM是这样解析这段代码的:首先建立对象s,赋予一个abcd,而后再建立一个新的对象s用来 执行第二行代码,也就是说咱们以前对象s并无变化,因此咱们说String类型是不可改变的对象了,因为这种机制,每当用String操做字符串时,其实是在不断的建立新的对象,而原来的对象就会变为垃圾被GC回收掉,可想而知这样执行效率会有多底。
而StringBuffer与StringBuilder就不同了,他们是字符串变量,是可改变的对象,每当咱们用它们对字符串作操做时,其实是在一个对象上操做的,这样就不会像String同样建立一些而外的对象进行操做了,固然速度就快了。
3.一个特殊的例子:
1 String str = “This is only a” + “ simple” + “ test”;
3 StringBuffer builder = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
你会很惊讶的发现,生成str对象的速度简直太快了,而这个时候StringBuffer竟然速度上根本一点都不占优点。其实这是JVM的一个把戏,实际上:
String str = “This is only a” + “ simple” + “test”;
其实就是:
String str = “This is only a simple test”;
因此不须要太多的时间了。但你们这里要注意的是,若是你的字符串是来自另外的String对象的话,速度就没那么快了,譬如:
String str2 = “This is only a”;
String str3 = “ simple”;
String str4 = “ test”;
String str1 = str2 +str3 + str4;
这时候JVM会规规矩矩的按照原来的方式去作。
4.StringBuilder与 StringBuffer
StringBuilder:线程非安全的
StringBuffer:线程安全的
当咱们在字符串缓冲去被多个线程使用是,JVM不能保证StringBuilder的操做是安全的,虽然他的速度最快,可是能够保证StringBuffer是能够正确操做的。固然大多数状况下就是咱们是在单线程下进行的操做,因此大多数状况下是建议用StringBuilder而不用StringBuffer的,就是速度的缘由。
对于三者使用的总结: 1.若是要操做少许的数据用 = String
2.单线程操做字符串缓冲区 下操做大量数据 = StringBuilder
3.多线程操做字符串缓冲区 下操做大量数据 = StringBuffer
Java有八种基本类型,byte, short, int, long, float, double, char, boolean。
对应八种包装类,Byte, Short, Integer, Long, Double, Character, Boolean。
以int为例,建立一个基本类型的变量时,基本类型以下:
int a = 0;
包装类型以下:
Integer a = new Integer(0);
内存方式
基本类型属于原始数据类型,变量中存储的就是原始值。包装类型属于引用数据类型,变量中存储的是存储原始值的地址的引用。
咱们先不考虑内存空间各类不一样区域对应不一样的用途,将其看作一个总体,思考如下语句的内存分配过程。
int a = 0;
分配一块内存,即为a,存入的值为0。
Integer b = new Integer(0);
分配一块内存,咱们称之为内存1,存储0,再分配一块内存,咱们称之为内存2。内存2中存放的是指向内存1的引用。
==和equals
基本类型的比较要用==,包装类型的比较要用equals。==是Java自己的一个操做符,它的做用是比较两个变量在内存中的地址,相同为true,不一样为false。equals则是一个方法。Java中,全部的类都有一个根级父类,Ojbect,包括包装类。在不重写的状况下,直接equals实际上就是在调用Ojbect类的equals方法。而Ojbect类的equals方法,也是使用==来做判断的。换句话说,若是没有通过重写,equals和==的结果是一致的。而若是咱们想要达到某些其它目的,好比说,咱们但愿在地址不一样,可是值相同的状况下,也能返回true,就须要本身重写equals方法。
看到这里,产生了一个疑惑,以下:
int a = 0;
int b = 0;
System.out.println(a == b);
true仍是false?
初看以为是true,再想以为是false,程序运行结果是true。
疑惑:两个变量的值虽然是相同的,可是,它们在内存中的地址应该是不一样的,只是其中的值相同。按照==的运算规则,理由获得false的结果(内存中的地址不一样),结果倒是true,为何?
事实:当定义b时,JVM会先检查内存中是否已经有了0这个值,若是没有,则建立,若是有(如以前已经执行过int a = 0),则不会再建立,而是直接将变量b指向变量a全部内存的地址,就好像执行的语句是int b = a。换句话说,a和b最终指向的内存空间,其实仍是一致的。
基于上述事实,又产生了一个疑惑,以下:
int a = 0;
int b = 0;
b = b + 1;
System.out.printlt(a == 2);
疑惑:由于变量b实质上指向的是变量a的内存地址,因此,对b的修改,实质上就是对a的修改,输出结果应该是true,但实际上false。
事实:这里,又有一个误解。当执行b = b + 1时,JVM事实上并非直接去对变量a的内存地址中的值作操做。事实上是,它先取出了a的值,为1。而后进行 +1 操做,获得2。这时候,JVM并不会直接把变量a内存地址中的值直接改为2,而是会在内存中寻找是否有2,有就将b指向相应地址,没有就把b内存地址中的值修改成2。
自动装箱和自动拆箱
在实际使用时,经过咱们在定义包装类型的定义时,不会使用new关键字,而是以下:
Integer a = 0;
思考这条语句为何成立?难道是说字面量0就至关于一个Integer对象吗?固然不是,这实际上是Java为了方便,为咱们提供了自动装箱的机制。事实上,这条语句至关于:
Integer a = Integer.values(0);
values是Integer 类提供一个静态方法,返回值是一个Integer实例。
和自动装箱相反,当你把一个包装类型的变量赋值到一个基本类型的变量时,Java会进行自动拆箱的过程,执行intValue方法。
Integer a = 0;(自动装箱)
int b = a;(自动拆箱)
缓存机制
在执行Integer a = new Integer(0)时,JVM会先建立一个对象,值为0,再把这个对象的引用赋值给a。基于节约内存和效率等的考虑,Java对包装类的values方法作了一些优化。如Integer,在执行Integer a = Integer.values(0)时,Java会在缓存中直接找到以前建立好的缓存,直接把0相应的引用给a。
又产生了以前的疑惑吗?其实Java的操做还和以前是同样的。因此,并不会出现不合理的问题。
观察源码发现,Boolean的缓存是true,false,Byte、Short、Integer、Long的缓存都是-128到127。Character是缓存是0~127对应的字符。Float,Double没有缓存。为何没有?你也这样疑惑吗?
其实答案很简单,思考一下缓存的意义就能够想到,为了使缓存真正有效果,应该把最经常使用的一部分数据放到缓存里。可是,对于小数来讲,选定一个集合,其中元素的个数是无限的。因此,Java可能在想这个问题的时候,实在是想不出来,应该以什么标准判断,把什么元素放到缓存里,因而,就放弃了,干脆不要缓存了。
初始化
基本类型和包装类型的另一个区别时,在用做类变量时,在初始化的时候,包装类型会被初始化成null,基本类型会被初始化成特定的值,如int为0,boolean为false等。
使用原则
最后咱们来整理一下基本类和包装类在实际使用时,应该遵循哪些原则?
1. 尽可能使用values方法。最大可能使用缓存,提升程序的效率。
2. 类变量使用包装类。想象有一个和数据库表对应的实体类,若是你使用的基本数据类型,在插入时,可能会插入一些让你意想不到的初始值。
3. 方法的参数要使用包装类。使用包装类意味着你在调用时,能够令若干个参数为null。null是无心义的。可是若是你使用了基本数据类型,那么,你将不得不传入一个值,即便这个值对你来讲,没有什么意义。
4. 方法的返回值要根据是否可为null来肯定使用包装类仍是基本类。当一个方法的返回值,必定不会出现null的状况时,推荐使用基本类来做为返回值。这样,调用者在拿到这个方法的返回值时,就没必要担忧它是为null了。
5. 方法内部(局部变量)使用基本类型。基本类型的时间效率和效率上来讲,都是要优于包装类的。因此,在方法内部,能使用基本类型尽可能不要使用包装类。
6. 小数的计算。严格来讲,这条不属于基本类型和包装类的内容,可是,这里顺便提交,当涉及到小数的计算时,要考虑到计算的精度问题,可使用BigDecimal,也能够经过缩小计量单位,达到化零为整的目的,这个,根据具体场景来肯定。
一、public Date()
分配 Date 对象并初始化此对象,以表示分配它系统的时间(精确到毫秒)。
二、public Date(long date)
分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。
一、public boolean after(Date when)
测试此日期是否在指定日期以后。
返回:当且仅当此 Date 对象表示的瞬间比 when 表示的瞬间晚,才返回 true;不然返回 false。
二、public boolean before(Date when)
测试此日期是否在指定日期以前。
返回:当且仅当此 Date 对象表示的瞬间比 when 表示的瞬间早,才返回 true;不然返回 false。
三、public int compareTo(Date anotherDate)
比较两个日期的顺序。
返回:若是参数 Date 等于此 Date,则返回值 0;若是此 Date 在 Date 参数以前,则返回小于 0 的值;若是此 Date 在 Date 参数以后,则返回大于 0 的值。
四、public boolean equals(Object obj)
比较两个日期的相等性。当且仅当参数不为 null,而且是一个表示与此对象相同的时间点(到毫秒)的 Date 对象时,结果才为 true。
五、public long getTime()
返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
六、public void setTime(long time)
设置此 Date 对象,以表示 1970 年 1 月 1 日 00:00:00 GMT 之后 time 毫秒的时间点。
七、public String toString()
把此 Date 对象转换为如下形式的 String:
dow mon dd hh:mm:ss zzz yyyy其中:
dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)。
mon 是月份 (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)。
dd 是一月中的某一天(01 至 31),显示为两位十进制数。
hh 是一天中的小时(00 至 23),显示为两位十进制数。
mm 是小时中的分钟(00 至 59),显示为两位十进制数。
ss 是分钟中的秒数(00 至 61),显示为两位十进制数。
zzz 是时区(并能够反映夏令时)。标准时区缩写包括方法 parse 识别的时区缩写。若是不提供时区信息,则 zzz 为空,即根本不包括任何字符。
yyyy 是年份,显示为 4 位十进制数。
DateFormat类:
功能:将系统默认的时间格式转化为自定义的格式,或者将自定义的格式转化为系统格式
注意:这是一个抽象类,因此咱们使用他的时候要建立他子类的对象。SimpleDateFormat类
SimpleDateFormat类:
构造方法:SimpleDateFormat(String pattern)括号内为咱们自定义的时间格式。
成员方法:1.public final String format(Date date)
{
return format(date, new StringBuffer(),
DontCareFieldPosition.INSTANCE).toString();
}
把咱们传进去的date格式时间转化为咱们自定义的格式
2.public Date parse(String source) throws ParseException
{
ParsePosition pos = new ParsePosition(0);
Date result = parse(source, pos);
if (pos.index == 0)
throw new ParseException("Unparseable date: \"" + source + "\"" ,
pos.errorIndex);
return result;
}
把自定义格式的字符串转为date格式
自定义格式:(区分大小写)
y 年
M 月
d 日
H 时
m 分
s 秒
例:"yyyy-mm-dd HH:mm:ss"
1 SimpleDateFormat a = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss"); 2 Date date = new Date(1000L); 3 System.out.println(a.format(date)); 4 System.out.println(a.parse("1970-00-01 08:00:01"));
1970-00-01 08:00:01
Thu Jan 01 08:00:01 CST 1970
1 * log(a) a的天然对数(底数是e) 2 * log10(a) a 的底数为10的对数 3 * log1p(a) a+1的天然对数 4 * 值得注意的是,前面其余函数都有重载,对数运算的函数只能传double型数据并返回double型数据 5 */ 6 System.out.println(Math.log(Math.E));//输出1.0 7 System.out.println(Math.log10(10));//输出1.0 8 System.out.println(Math.log1p(Math.E-1.0));//输出1.0 9 /* 10 * 6.幂 11 * exp(x) 返回e^x的值 12 * expm1(x) 返回e^x - 1的值 13 * pow(x,y) 返回x^y的值 14 * 这里可用的数据类型也只有double型 15 */ 16 System.out.println(Math.exp(2));//输出E^2的值 17 System.out.println(Math.pow(2.0, 3.0));//输出8.0 18 19 /* 20 * 7.随机数 21 * random()返回[0.0,1.0)之间的double值 22 * 这个产生的随机数其实能够经过*x控制 23 * 好比(int)(random*100)后能够获得[0,100)之间的整数 24 */ 25 System.out.println((int)(Math.random()*100));//输出[0,100)间的随机数 26 27 /* 28 * 8.转换 29 * toDegrees(a) 弧度换角度 30 * toRadians(a) 角度换弧度 31 */ 32 System.out.println(Math.toDegrees(Math.PI));//输出180.0 33 System.out.println(Math.toRadians(180));//输出 π 的值 34 /* 35 * 9.其余 36 */ 37 38 //copySign(x,y) 返回 用y的符号取代x的符号后新的x值 39 System.out.println(Math.copySign(-1.0, 2.0));//输出1.0 40 System.out.println(Math.copySign(2.0, -1.0));//输出-2.0 41 42 //ceil(a) 返回大于a的第一个整数所对应的浮点数(值是整的,类型是浮点型) 43 //能够经过强制转换将类型换成整型 44 System.out.println(Math.ceil(1.3443));//输出2.0 45 System.out.println((int)Math.ceil(1.3443));//输出2 46 47 //floor(a) 返回小于a的第一个整数所对应的浮点数(值是整的,类型是浮点型) 48 System.out.println(Math.floor(1.3443));//输出1.0 49 50 //rint(a) 返回最接近a的整数的double值 51 System.out.println(Math.rint(1.2));//输出1.0 52 System.out.println(Math.rint(1.8));//输出2.0 53 54 55 //nextAfter(a,b) 返回(a,b)或(b,a)间与a相邻的浮点数 b能够比a小 56 System.out.println(Math.nextAfter(1.2, 2.7));//输出1.2000000000000002 57 System.out.println(Math.nextAfter(1.2, -1));//输出1.1999999999999997 58 //因此这里的b是控制条件 59 60 //nextUp(a) 返回比a大一点点的浮点数 61 System.out.println(Math.nextUp(1.2));//输出1.2000000000000002 62 63 //nextDown(a) 返回比a小一点点的浮点数 64 System.out.println(Math.nextDown(1.2));//输出1.1999999999999997 65 } 66 }
另外,当我尝试这样使用数学类的时候是错误的:
Math m = new Math();
m.sqrt(4.0);
为何呢?
查了下Math的源码,惊呆了!它的构造方法竟然是这样写的:
private Math() {}
构造方法写成私有的额(⊙o⊙)…
因此根本就不能建立对象啊!
后来仔细想一想其实这是很合理的。
在面向对象中,类是抽象的而对象是具体的,数学自己也是抽象的而无法具体,因此这里只有一个数学类而不能实例化对象。
Random类中实现的随机算法是伪随机,也就是有规则的随机。在进行随机时,随机算法的起源数字称为种子数(seed),在种子数的基础上进行必定的变换,从而产生须要的随机数字。
相同种子数的Random对象,相同次数生成的随机数字是彻底相同的。也就是说,两个种子数相同的Random对象,第一次生成的随机数字彻底相同,第二次生成的随机数字也彻底相同。这点在生成多个随机数字时须要特别注意。
查看Random的API文档:
public Random(long seed);
使用单个 long
种子建立一个新的随机数生成器。该种子是伪随机数生成器的内部状态的初始值,该生成器可经过方法 next(int) 维护。
调用 new
Random
(seed)
等效于:
Random rnd = new Random();
rnd.setSeed(seed);
public void setSeed(long seed)
使用单个 long
种子设置此随机数生成器的种子。setSeed
的常规协定是它更改此随机数生成器对象的状态,使其状态好像是刚刚使用参数 seed
做为种子建立它的状态同样。经过将种子自动更新为
(seed ^ 0x5DEECE66DL) & ((1L << 48) - 1)
并清除 nextGaussian() 使用的 haveNextNextGaussian
标志,Random
类可实现 setSeed
方法。
Random
类实现的 setSeed
刚好只使用 48 位的给定种子。可是,一般重写方法可能使用 long
参数的全部 64 位做为种子值。
因为参数seed同样,相同种子数的Random对象,相同次数生成的随机数字是彻底相同的。
下面介绍一下Random类的使用,以及如何生成指定区间的随机数组以及实现程序中要求的概率。
一、Random对象的生成
Random类包含两个构造方法,下面依次进行介绍:
a、public Random()
该构造方法使用一个和当前系统时间对应的相对时间有关的数字做为种子数,而后使用这个种子数构造Random对象。
b、public Random(long seed)
该构造方法能够经过制定一个种子数进行建立。
示例代码:
Random r = new Random();
Random r1 = new Random(10);
再次强调:种子数只是随机算法的起源数字,和生成的随机数字的区间无关。
二、Random类中的经常使用方法
Random类中的方法比较简单,每一个方法的功能也很容易理解。须要说明的是,Random类中各方法生成的随机数字都是均匀分布的,也就是说区间内部的数字生成的概率是均等的。下面对这些方法作一下基本的介绍:
a、public boolean nextBoolean()
该方法的做用是生成一个随机的boolean值,生成true和false的值概率相等,也就是都是50%的概率。
b、public double nextDouble()
该方法的做用是生成一个随机的double值,数值介于[0,1.0)之间。
c、public int nextInt()
该方法的做用是生成一个随机的int值,该值介于int的区间,也就是-231到231-1之间。
若是须要生成指定区间的int值,则须要进行必定的数学变换,具体能够参看下面的使用示例中的代码。
d、public int nextInt(int n)
该方法的做用是生成一个随机的int值,该值介于[0,n)的区间,也就是0到n之间的随机int值,包含0而不包含n。
若是想生成指定区间的int值,也须要进行必定的数学变换,具体能够参看下面的使用示例中的代码。
e、public void setSeed(long seed)
该方法的做用是从新设置Random对象中的种子数。设置完种子数之后的Random对象和相同种子数使用new关键字建立出的Random对象相同。
三、Random类使用示例
使用Random类,通常是生成指定区间的随机数字,下面就一一介绍如何生成对应区间的随机数字。如下生成随机数的代码均使用如下Random对象r进行生成:
Random r = new Random();
a、生成[0,1.0)区间的小数
doubled1 = r.nextDouble();
直接使用nextDouble方法得到。
b、生成[0,5.0)区间的小数
double5d2 = r.nextDouble() *;
由于nextDouble方法生成的数字区间是[0,1.0),将该区间扩大5倍便是要求的区间。
c、生成[1,2.5)区间的小数
double1.51d3 = r.nextDouble() *+;
生成[1,2.5)区间的随机小数,则只须要首先生成[0,1.5)区间的随机数字,而后将生成的随机数区间加1便可。
同理,生成任意非从0开始的小数区间[d1,d2)范围的随机数字(其中d1不等于0),则只须要首先生成[0,d2-d1)区间的随机数字,而后将生成的随机数字区间加上d1便可。
同理,生成[0,d)区间的随机小数,d为任意正的小数,则只须要将nextDouble方法的返回值乘以d便可。
d、生成任意整数
intn1 = r.nextInt();
直接使用nextInt方法便可。
e、生成[0,10)区间的整数
int n2 = r.nextInt(10);
n2 = Math.abs(r.nextInt() % 10);
以上两行代码都可生成[0,10)区间的整数。
第一种实现使用Random类中的nextInt(int n)方法直接实现。
第二种实现中,首先调用nextInt()方法生成一个任意的int数字,该数字和10取余之后生成的数字区间为(-10,10),而后再对该区间求绝对值,则获得的区间就是[0,10)了。
同理,生成任意[0,n)区间的随机整数,均可以使用以下代码:
int n2 = r.nextInt(n);
n2 = Math.abs(r.nextInt() % n);
f、生成[0,10]区间的整数
int n3 = r.nextInt(11);
n3 = Math.abs(r.nextInt() % 11);
相对于整数区间,[0,10]区间和[0,11)区间等价,因此即生成[0,11)区间的整数。
g、生成[-3,15)区间的整数
int n4 = r.nextInt(18) - 3;
n4 = Math.abs(r.nextInt() % 18) - 3;
生成非从0开始区间的随机整数,能够参看上面非从0开始的小数区间实现原理的说明。
h、概率实现
按照必定的概率实现程序逻辑也是随机处理能够解决的一个问题。下面以一个简单的示例演示如何使用随机数字实现概率的逻辑。
在前面的方法介绍中,nextInt(int n)方法中生成的数字是均匀的,也就是说该区间内部的每一个数字生成的概率是相同的。那么若是生成一个[0,100)区间的随机整数,则每一个数字生成的概率应该是相同的,并且因为该区间中总计有100个整数,因此每一个数字的概率都是1%。按照这个理论,能够实现程序中的概率问题。
示例:随机生成一个整数,该整数以55%的概率生成1,以40%的概率生成2,以5%的概率生成3。实现的代码以下:
int n5 = r.nextInt(100);
int m; //结果数字
if(n5 < 55){ //55个数字的区间,55%的概率
m = 1;
}else if(n5 < 95){//[55,95),40个数字的区间,40%的概率
m = 2;
}else{
m = 3;
}
由于每一个数字的概率都是1%,则任意55个数字的区间的概率就是55%,为了代码方便书写,这里使用[0,55)区间的全部整数,后续的原理同样。
固然,这里的代码能够简化,由于概率都是5%的倍数,因此只要以5%为基础来控制概率便可,下面是简化的代码实现:
int n6 = r.nextInt(20);
int m1;
if(n6 < 11){
m1 = 1;
}else if(n6 < 19){
m1 = 2;
}else{
m1 = 3;
}
在程序内部,概率的逻辑就能够按照上面的说明进行实现。
4、其它问题
a、相同种子数Random对象问题
前面介绍过,相同种子数的Random对象,相同次数生成的随机数字是彻底相同的,下面是测试的代码:
Random r1 = new Random(10);
Random r2 = new Random(10);
for(int i = 0;i < 2;i++){
System.out.println(r1.nextInt());
System.out.println(r2.nextInt());
}
在该代码中,对象r1和r2使用的种子数都是10,则这两个对象相同次数生成的随机数是彻底相同的。
若是想避免出现随机数字相同的状况,则须要注意,不管项目中须要生成多少个随机数字,都只使用一个Random对象便可。
b、关于Math类中的random方法
其实在Math类中也有一个random方法,该random方法的工做是生成一个[0,1.0)区间的随机小数。
经过阅读Math类的源代码能够发现,Math类中的random方法就是直接调用Random类中的nextDouble方法实现的。
只是random方法的调用比较简单,因此不少程序员都习惯使用Math类的random方法来生成随机数字。
1.该类主要表明了应用程序的运行环境。一个RunTime就表明一个运行环境。
2.RunTime类经常使用的方法:
(1) getRuntime():该方法用于返回当前应用程序的运行环境对象。
(2) exec(String command):该方法用于根据指定的路径执行对应的可执行文件。
①实例:
1 public class Demo7 { 2 public static void main(String[] args) throws IOException, InterruptedException { 3 Runtime runtime = Runtime.getRuntime(); 4 Process process = runtime.exec("C:\\Windows\\notepad.exe");//打开记事本程序,并返回一个进程 5 Thread.sleep(3000); //让当前程序中止3秒。 6 process.destroy(); 7 } 8 }
②运行结果:
(3) freeMemory():该方法用于返回Java虚拟机中的空闲内存量,以字节为单位。
(4) maxMemory():该方法用于返回Java虚拟机试图使用的最大内存量。
(5) totalMemory():该方法用于返回Java虚拟机中的内存总量。
①实例:
1 public class Demo8 { 2 public static void main(String[] args) throws IOException{ 3 Runtime runtime = Runtime.getRuntime(); 4 System.out.println("Java虚拟机中的空闲内存量:"+runtime.freeMemory()); 5 System.out.println("Java 虚拟机试图使用的最大内存量:"+ runtime.maxMemory()); 6 System.out.println("返回 Java 虚拟机中的内存总量:"+ runtime.totalMemory()); 7 } 8 }
②运行结果:
更多runtime的使用:https://www.cnblogs.com/huhx/p/baseusejavaruntime1.html
在API中system类介绍的比较简单,咱们给出定义,system中表明程序所在系统,提供了对应的一些系统属性信息和系统操做。
注意,system类不能手动建立对象,由于构造方法被私有化(即被private关键字修饰),组织外界建立对象(即不能用new关键字生成一个对象)。System类中的都是静态方法(static关键字修饰),类名访问便可。在JDK中,有许多这样的类。在 System
类提供的设施中,有标准输入、标准输出和错误输出流;对外部定义的属性和环境变量的访问;加载文件和库的方法;还有快速复制数组的一部分的实用方法。
System的经常使用方法
1>.获取系统当前毫秒值(public static long currentTimeMillis())
获取当前系统时间与1970年01月01日00:00点以前的毫秒差值,咱们能够用它来测试程序的执行时间。代码以下:
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.demo; 8 9 public class SystemDemo { 10 public static void main(String[] args) { 11 long start = System.currentTimeMillis(); 12 for(int i=1;i<=9;i++) { 13 for(int j=1;j<=i;j++) { 14 System.out.printf("%d x %d = %d \t",j,i,(j*i)); 15 } 16 System.out.println(); 17 } 18 long end = System.currentTimeMillis(); 19 System.out.printf("程序运行时间为[%d]毫秒!",(end-start)); 20 } 21 } 22 23 24 /* 25 以上代码执行结果以下: 26 1 x 1 = 1 27 1 x 2 = 2 2 x 2 = 4 28 1 x 3 = 3 2 x 3 = 6 3 x 3 = 9 29 1 x 4 = 4 2 x 4 = 8 3 x 4 = 12 4 x 4 = 16 30 1 x 5 = 5 2 x 5 = 10 3 x 5 = 15 4 x 5 = 20 5 x 5 = 25 31 1 x 6 = 6 2 x 6 = 12 3 x 6 = 18 4 x 6 = 24 5 x 6 = 30 6 x 6 = 36 32 1 x 7 = 7 2 x 7 = 14 3 x 7 = 21 4 x 7 = 28 5 x 7 = 35 6 x 7 = 42 7 x 7 = 49 33 1 x 8 = 8 2 x 8 = 16 3 x 8 = 24 4 x 8 = 32 5 x 8 = 40 6 x 8 = 48 7 x 8 = 56 8 x 8 = 64 34 1 x 9 = 9 2 x 9 = 18 3 x 9 = 27 4 x 9 = 36 5 x 9 = 45 6 x 9 = 54 7 x 9 = 63 8 x 9 = 72 9 x 9 = 81 35 程序运行时间为[42]毫秒! 36 */
2>.结束正在运行的Java程序(public staitc void exit(int status))
参数传入一个数字便可。一般传入0记为正常状态,其它为异常状态。
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.demo; 8 9 public class SystemDemo { 10 public static void main(String[] args) { 11 int counts = 0; 12 while(true) { 13 System.out.println("yinzhengjie"); 14 if(counts==5) { 15 System.exit(0); 16 } 17 counts++; 18 } 19 } 20 } 21 22 23 /* 24 以上代码执行结果以下: 25 yinzhengjie 26 yinzhengjie 27 yinzhengjie 28 yinzhengjie 29 yinzhengjie 30 yinzhengjie 31 */
3>.垃圾回收器(public static void gc())
用来运行JVM中的垃圾回收器,完成内存中垃圾的清除。
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.demo; 8 9 class Student{ 10 //清除垃圾时,会默认调用被清空对象的finalize方法。 11 public void finalize() { 12 System.out.println("垃圾已经被收取啦!"); 13 } 14 } 15 16 17 public class SystemDemo { 18 public static void main(String[] args) { 19 new Student(); 20 new Student(); 21 new Student(); 22 new Student(); 23 new Student(); 24 System.gc(); 25 26 } 27 } 28 29 30 /* 31 以上代码执行结果以下:(输出结果不必定是三行,有多是0行或者5行哟!每次运行的结果几乎是不一致的) 32 垃圾已经被收取啦! 33 垃圾已经被收取啦! 34 垃圾已经被收取啦! 35 */
4>.肯定当前的系统属性(public static getProperties getProperties()
)
/* @author :yinzhengjie Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ EMAIL:y1053419035@qq.com */ package cn.org.yinzhengjie.demo; public class SystemDemo { public static void main(String[] args) { System.out.println(System.getProperties()); } } /* 以上代码执行结果以下: {sun.desktop=windows, awt.toolkit=sun.awt.windows.WToolkit, java.specification.version=9, file.encoding.pkg=sun.io,
sun.cpu.isalist=amd64, sun.jnu.encoding=GBK, java.class.path=D:\10.Java\JavaSE\eclipse\Myprogram\workspace\Day6\bin,
java.vm.vendor=Oracle Corporation, sun.arch.data.model=64, user.variant=, java.vendor.url=http://java.oracle.com/, user.timezone=,
os.name=Windows 7, java.vm.specification.version=9, sun.java.launcher=SUN_STANDARD, user.country=CN,
sun.boot.library.path=D:\10.Java\jdk-9.0.4\bin, sun.java.command=cn.org.yinzhengjie.demo.SystemDemo, jdk.debug=release,
sun.cpu.endian=little, user.home=C:\Users\Administrator, user.language=zh, java.specification.vendor=Oracle Corporation,
java.home=D:\10.Java\jdk-9.0.4, file.separator=\, java.vm.compressedOopsMode=Zero based, line.separator= , java.vm.specification.vendor=Oracle Corporation, java.specification.name=Java Platform API Specification,
java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment, user.script=, sun.management.compiler=HotSpot 64-Bit Tiered Compilers,
java.runtime.version=9.0.4+11, user.name=Administrator, path.separator=;, os.version=6.1, java.runtime.name=Java(TM) SE Runtime
Environment, file.encoding=GBK, java.vm.name=Java HotSpot(TM) 64-Bit Server VM, java.vendor.url.bug=http://bugreport.java.com/bugreport/,
java.io.tmpdir=C:\Users\ADMINI~1\AppData\Local\Temp\, java.version=9.0.4, user.dir=D:\10.Java\JavaSE\eclipse\Myprogram\workspace\Day6,
os.arch=amd64, java.vm.specification.name=Java Virtual Machine Specification, java.awt.printerjob=sun.awt.windows.WPrinterJob,
sun.os.patch.level=Service Pack 1, java.library.path=D:\10.Java\jdk-
9.0.4\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;D:/10.Java/jdk-9.0.4/bin/server;D:/10.Java/jdk-9.0.4/bin;D:\10.Java\jdk-
9.0.4\bin;C:\ProgramData\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShe
ll\v1.0\;D:\04.Python\python3.6.5\Scripts\;D:\04.Python\python3.6.5\;D:\10.Java\JavaSE\eclipse\Myprogram\eclipse;;., java.vm.info=mixed
mode, java.vendor=Oracle Corporation, java.vm.version=9.0.4+11, sun.io.unicode.encoding=UnicodeLittle, java.class.version=53.0} */
各个属性关系对应图以下:
5>. System类方法复制数组(public static notive void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
)【notive 是能够调用当前操做系统来实现数组拷贝的】
用来实现将源数组部分元素复制到目标数组的指定位置。各个参数功能以下:
Object src:要复制的原数组;
Int srcPos:数组源的起始索引;
Object dest:复制后的目标数组;
int destPos:目标数组起始索引;
int length,指定复制的长度;
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.demo; 8 9 public class SystemDemo { 10 public static void main(String[] args) { 11 int[] src = {1,22,333,4444,5555,666666,7777777}; 12 int[] dest = {10,20,30}; 13 System.arraycopy(src, 2, dest, 0, 2); 14 15 for(int i=0;i<dest.length;i++) { 16 System.out.println(dest[i]); 17 } 18 } 19 } 20 21 22 /* 23 以上代码执行结果以下: 24 333 25 4444 26 30 27 */
在实际项目当中,咱们常常会涉及到对时间的处理,例如登录网站,咱们会看到网站首页显示XXX,欢迎您!今天是XXXX年。。。。某些网站会记录下用户登录的时间,好比银行的一些网站,对于这些常常须要处理的问题,Java中提供了Calendar这个专门用于对日期进行操做的类,那么这个类有什么特殊的地方呢,首先咱们来看Calendar的声明
public abstract class Calendar extends Objectimplements Serializable, Cloneable, Comparable<Calendar>
该类被abstract所修饰,说明不能经过new的方式来得到实例,对此,Calendar提供了一个类方法getInstance,以得到此类型的一个通用的对象,getInstance方法返回一个Calendar对象(该对象为Calendar的子类对象),其日历字段已由当前日期和时间初始化:
Calendar rightNow = Calendar.getInstance();
为何说返回的是Calendar的子类对象呢,由于每一个国家地区都有本身的一套日历算法,好比西方国家的第一个星期大部分为星期日,而中国则为星期一,咱们来看看getInstance方法获取实例的源码
1 /** 2 * Gets a calendar using the default time zone and locale. The 3 * <code>Calendar</code> returned is based on the current time 4 * in the default time zone with the default locale. 5 * 6 * @return a Calendar. 7 */ 8 public static Calendar getInstance() 9 { 10 Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT)); 11 cal.sharedZone = true; 12 return cal; 13 }
其中createCalendar方法就是根据不一样国家地区返回对应的日期子类
1 private static Calendar createCalendar(TimeZone zone, 2 Locale aLocale) 3 { 4 Calendar cal = null; 5 6 String caltype = aLocale.getUnicodeLocaleType("ca"); 7 if (caltype == null) { 8 // Calendar type is not specified.未指定日历类型。 9 // If the specified locale is a Thai locale,若是指定的区域设置是泰国区域设置, 10 // returns a BuddhistCalendar instance.返回佛教徒日历实例。 11 if ("th".equals(aLocale.getLanguage()) 12 && ("TH".equals(aLocale.getCountry()))) { 13 cal = new BuddhistCalendar(zone, aLocale); 14 } else { 15 cal = new GregorianCalendar(zone, aLocale); 16 } 17 } else if (caltype.equals("japanese")) { 18 cal = new JapaneseImperialCalendar(zone, aLocale); 19 } else if (caltype.equals("buddhist")) { 20 cal = new BuddhistCalendar(zone, aLocale); 21 } else { 22 // Unsupported calendar type.不支持的日历类型。 23 // Use Gregorian calendar as a fallback.使用公历做为回退。 24 cal = new GregorianCalendar(zone, aLocale); 25 } 26 27 return cal; 28 }
为了更加便捷的对日期进行操做,Calendar类对YEAR、MONTH、DAY_OF_MONTH、HOUR等日历字段之间的转换提供了一些方法,并为操做日历字段(例如得到下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000,格里高利历)的偏移量。
下面看看Calendar经常使用的方法
1 package com.test.calendar; 2 3 import java.util.Calendar; 4 5 import org.junit.Before; 6 import org.junit.Test; 7 8 public class CalendarDemo { 9 Calendar calendar = null; 10 11 @Before 12 public void test() { 13 calendar = Calendar.getInstance(); 14 } 15 16 // 基本用法,获取年月日时分秒星期 17 @Test 18 public void test1() { 19 // 获取年 20 int year = calendar.get(Calendar.YEAR); 21 22 // 获取月,这里须要须要月份的范围为0~11,所以获取月份的时候须要+1才是当前月份值 23 int month = calendar.get(Calendar.MONTH) + 1; 24 25 // 获取日 26 int day = calendar.get(Calendar.DAY_OF_MONTH); 27 28 // 获取时 29 int hour = calendar.get(Calendar.HOUR); 30 // int hour = calendar.get(Calendar.HOUR_OF_DAY); // 24小时表示 31 32 // 获取分 33 int minute = calendar.get(Calendar.MINUTE); 34 35 // 获取秒 36 int second = calendar.get(Calendar.SECOND); 37 38 // 星期,英语国家星期从星期日开始计算 39 int weekday = calendar.get(Calendar.DAY_OF_WEEK); 40 41 System.out.println("如今是" + year + "年" + month + "月" + day + "日" + hour 42 + "时" + minute + "分" + second + "秒" + "星期" + weekday); 43 } 44 45 // 一年后的今天 46 @Test 47 public void test2() { 48 // 同理换成下个月的今天calendar.add(Calendar.MONTH, 1); 49 calendar.add(Calendar.YEAR, 1); 50 51 // 获取年 52 int year = calendar.get(Calendar.YEAR); 53 54 // 获取月 55 int month = calendar.get(Calendar.MONTH) + 1; 56 57 // 获取日 58 int day = calendar.get(Calendar.DAY_OF_MONTH); 59 60 System.out.println("一年后的今天:" + year + "年" + month + "月" + day + "日"); 61 } 62 63 // 获取任意一个月的最后一天 64 @Test 65 public void test3() { 66 // 假设求6月的最后一天 67 int currentMonth = 6; 68 // 先求出7月份的第一天,实际中这里6为外部传递进来的currentMonth变量 69 // 1 70 calendar.set(calendar.get(Calendar.YEAR), currentMonth, 1); 71 72 calendar.add(Calendar.DATE, -1); 73 74 // 获取日 75 int day = calendar.get(Calendar.DAY_OF_MONTH); 76 77 System.out.println("6月份的最后一天为" + day + "号"); 78 } 79 80 // 设置日期 81 @Test 82 public void test4() { 83 calendar.set(Calendar.YEAR, 2000); 84 System.out.println("如今是" + calendar.get(Calendar.YEAR) + "年"); 85 86 calendar.set(2008, 8, 8); 87 // 获取年 88 int year = calendar.get(Calendar.YEAR); 89 90 // 获取月 91 int month = calendar.get(Calendar.MONTH); 92 93 // 获取日 94 int day = calendar.get(Calendar.DAY_OF_MONTH); 95 96 System.out.println("如今是" + year + "年" + month + "月" + day + "日"); 97 } 98 }
程序输出结果:
1 如今是2016年11月7日11时42分18秒星期2 2 一年后的今天:2017年11月7日 3 6月份的最后一天为30号 4 如今是2000年 5 如今是2008年8月8日
Calendar类中也有before,after,compareTo等方法,用法与Date类的相似,只是如今推荐用Calendar类操做日期。
Java8 新增了很是多的特性,咱们主要讨论如下几个:
Lambda 表达式 − Lambda容许把函数做为一个方法的参数(函数做为参数传递进方法中。
方法引用 − 方法引用提供了很是有用的语法,能够直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可使语言的构造更紧凑简洁,减小冗余代码。
默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
Date Time API − 增强对日期与时间的处理。
Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它容许咱们在JVM上运行特定的javascript应用。
更多的新特性能够参阅官网:What's New in JDK 8 https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
1 import java.util.Collections; 2 import java.util.List; 3 import java.util.ArrayList; 4 import java.util.Comparator; 5 6 public class Java8Tester { 7 public static void main(String args[]){ 8 9 List<String> names1 = new ArrayList<String>(); 10 names1.add("Google "); 11 names1.add("Runoob "); 12 names1.add("Taobao "); 13 names1.add("Baidu "); 14 names1.add("Sina "); 15 16 List<String> names2 = new ArrayList<String>(); 17 names2.add("Google "); 18 names2.add("Runoob "); 19 names2.add("Taobao "); 20 names2.add("Baidu "); 21 names2.add("Sina "); 22 23 Java8Tester tester = new Java8Tester(); 24 System.out.println("使用 Java 7 语法: "); 25 26 tester.sortUsingJava7(names1); 27 System.out.println(names1); 28 System.out.println("使用 Java 8 语法: "); 29 30 tester.sortUsingJava8(names2); 31 System.out.println(names2); 32 } 33 34 // 使用 java 7 排序 35 private void sortUsingJava7(List<String> names){ 36 Collections.sort(names, new Comparator<String>() { 37 @Override 38 public int compare(String s1, String s2) { 39 return s1.compareTo(s2); 40 } 41 }); 42 } 43 44 // 使用 java 8 排序 45 private void sortUsingJava8(List<String> names){ 46 Collections.sort(names, (s1, s2) -> s1.compareTo(s2)); 47 } 48 }
执行以上脚本,输出结果为:
$ javac Java8Tester.java $ java Java8Tester 使用 Java 7 语法: [Baidu , Google , Runoob , Sina , Taobao ] 使用 Java 8 语法: [Baidu , Google , Runoob , Sina , Taobao ]
接下来咱们将详细为你们简介 Java 8 的新特性:
1 | Lambda 表达式 |
2 | 方法引用 |
3 | 函数式接口 |
4 | 默认方法 |
5 | Stream |
6 | Optional 类 |
7 | Nashorn, JavaScript 引擎 |
8 | 新的日期时间 API |
9 | Base64 |
参考网址:
枚举:http://www.javashuo.com/article/p-wngwtdfk-ca.html
基本类型与包装类型:https://baijiahao.baidu.com/s?id=1635392670198214134&wfr=spider&for=pc
Date类的使用:https://blog.csdn.net/qq_27501261/article/details/79423886
SimpleDateFormat类的使用:https://blog.csdn.net/chen404897439/article/details/92560038
Math类的使用:http://www.javashuo.com/article/p-dpwzvuai-gx.html
Random类的使用:https://blog.csdn.net/huiweizuotiandeni/article/details/70244526
Runtime类的使用:http://www.javashuo.com/article/p-fjxywkxm-kd.html
system:http://www.javashuo.com/article/p-ucztwami-bs.html
calendar:http://www.javashuo.com/article/p-xjdjmcvy-cy.html
Java8新特性:https://www.runoob.com/java/java8-new-features.html