備註:如有不正之處,請多諒解並歡迎批評指正。轉載請標明連接: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