摘要:字符串字符串是存儲在字符串常量池中的。面試題假設字符串常量池中不存在字符串,那么創建了幾個對象答兩個堆空間的值字符數組和字符串常量池中的實體。程序運行期間,靜態存儲的數據將隨時等候調用。中的數組會自動記性范圍檢查會造成少量的內存開銷。
字符串
字符串是存儲在字符串常量池中的。例如以下的兩個字符串的內存地址值是一樣的:
String str1 = "hello" + "world"; String str2 = "helloworld"; System.out.println(str1 == str2); // true System.out.println(str1.equals(str2)); // true String str3 = "hello"; String str4 = "world"; String str5 = str3 + str4; System.out.println(str5 == str2); // false System.out.println(str5.equals(str2)); // true
在以上的代碼中str2和str5的地址值不相同,如果我們對str5使用intern()方法即:
String str6 = str5.intern(); // native方法 System.out.println(str6 == str2); // true
就可以返回true。
面試題:假設字符串常量池中不存在字符串"hello",那么String s = new String("hello")創建了幾個對象?
答:兩個堆空間的value值(字符數組)和字符串常量池中的hello實體。我們可以通過查看new String(String str)的源碼:
/** The value is used for character storage. */ private final char value[]; /** * Initializes a newly created {@code String} object so that it represents * the same sequence of characters as the argument; in other words, the * newly created string is a copy of the argument string. Unless an * explicit copy of {@code original} is needed, use of this constructor is * unnecessary since Strings are immutable. * * @param original * A {@code String} */ public String(String original) { this.value = original.value; this.hash = original.hash; }用句柄操作對象
java中一切皆對象,但是我們操作的實際上是指向這個對象的句柄(Handle),這個句柄也叫做引用(Reference)或者指針(Pointer)。我們可以將這一情形想象成用遙控器(Handler)操作電視機(Object)。沒有電視機,遙控器。
即使沒有電視機,遙控器也可以多帶帶存在。即句柄是可以多帶帶存在的(并不指向任何實體)。例如:
String s;
以上的java代碼創建的僅僅是句柄而不是對象。此時如果向s發送一條消息,將會或者一個運行時異常,因此更安全的做法是:創建一個句柄的時候進行顯式初始化:
String s = "";java程序運行時數據的存放位置
Register。處理器內部(最快),由于數量有限,所以寄存器是根據需要有編譯器分配的。我們對它沒有直接的控制權,也不可能在程序中找到寄存器的任何蹤跡。
Stack。駐留與常規RAM區域,速度僅次于寄存器。我們可以通過它的“堆棧指針”獲得直接的處理支持。(指針下移創建新的內存;指針上移釋放內存)。創建程序時java編譯器必須準確知道堆棧內保存的所有數據的“長度”以及“存在的時間”(必須生成相應的代碼,以便于向上或者向下移動指針)。這一限制無疑影響了程序的靈活性,所以java將對象的句柄保存在堆棧中,但是對象并不存放在其中。
Heap。一種常規用途的內存池(也是在RAM區域),保存java對象。Heap最吸引人的地方在于:編譯器并不必要知道要從堆中分配多少存儲空間,也不必要知道存儲的數據要在堆中停留多長時間——用堆保存數據會得到更大的靈活性。然而every coin have two slices,在堆中分配存儲空間會花費更長的時間!
靜態存儲。“靜態”(Static)指的是“位于固定位置”。程序運行期間,靜態存儲的數據將隨時等候調用。我們可以用static關鍵字指出一個對象的特定元素是靜態的,但是java對象本身永遠不會置入靜態存儲空間。
常數存儲。常數存儲通常直接置于一個程序代碼內部。這樣做是安全的,因為他們永遠不會被改變。有的常數需要嚴格保護,可以考慮將他們置入只讀存儲器(ROM)。
非RAM存儲:將數據完全保存子啊一個程序之外。對典型的2個例子就是“流式對象”和“固定對象”。
流式對象:對象-->字節流-->另一臺機器
固定對象:對象-->磁盤
基本數據類型基本數據類型由于比較常用,而堆棧的效率又高于堆。所以基本數據類型都是保存在堆棧中。對于基本數據類型我們不需要用new,而是創建了一個并非句柄的“自動變量”,該變量容納了具體的值,并保存在堆中可以更高效的存取。
java中的數組在C、C++中使用數組是非常危險的,因為那些數組只是內存塊,如果程序訪問自己內存塊之外的數據或者在初始化之前使用內存會產生不可預料的后果。在C++中應該盡量避免使用數組而換用Standard TemplateLibrary中更安全的容器。java中的數組會自動記性范圍檢查會造成少量的內存開銷。但是我們可以換來更高的工作效率。
變量作用域變量的作用域是由花括號的位置決定的。
在C、C++中以下的代碼是合法的:
{ int x = 10; { int x = 100; // 不合法Duplicate local variable x } }
但是在java中編譯器會認為變量x已經被定義,所以C、C++能將一個變量“隱藏”在一個更大的作用域中,java的設計者認為這樣使程序產生了混淆。
注意區分成員變量和局部變量成員變量都有默認值,而局部變量必須進行初始化。
文檔注釋文檔注釋只能為public和protected的成員處理文檔,private和default的不會被javadoc提取。文檔注釋中可以嵌入html,例如:
異常方法拋出異常的時候,該方法會從棧上立即被取出,而異常再度丟給棧上的方法(也就是調用方),如果調用方沒有對異常進行處理而是繼續拋出異常,調用方就會從棧上彈出,異常再度交給此時的棧頂方法,如此一路下去……
finally中的代碼有一種情況下是執行不到的:finally的前面出現了System.exit(0)。
static只能修飾類的成員(變量和方法),不能修飾局部變量。static變量存放在方法區。隨著類的加載而加載,存在方法區中,隨著類的消失而消失,生命周期最長。如果沒有給定初值,static變量會被默認置0.(或者null)。
Q:如果一個類被標記為final,再將該類中的方法標記位final是不是很多余?
A:不只是多余,而且是多了很多!如果一個類為final,那么它根本就沒有子類,根本不可能覆寫父類中的方法(只有繼承才有覆寫)。
工程目錄如下:
加載該配置文件應該這樣寫:
public class Test { @org.junit.Test public void test() throws IOException { ClassLoader classLoader = this.getClass().getClassLoader(); InputStream is = classLoader.getResourceAsStream("org/gpf/conf/db.properties"); Properties properties = new Properties(); properties.load(is); properties.list(System.out); } }利用反射獲取父類的泛型
public class Personjava中的枚舉{ // some code... } public class Student extends Person { // some code... } Class> clazz = Student.class; Type type = clazz.getGenericSuperclass(); // org.gpf.Person ParameterizedType parameterizedType = (ParameterizedType) type; // type的子接口 for (Type c : parameterizedType.getActualTypeArguments()) { System.out.println(((Class>)c).getName()); // Class是Type接口的實現類 }
所謂枚舉,就是枚舉類的對象的個數是有限的,可以窮舉出來的類。實際上單例模式也可以使用枚舉來實現(Effective Java中的單例經典實現,枚舉只有一個成員)。jdk1.5之前需要自定義枚舉類,jdk1.5之后提供了enum關鍵字用于定義枚舉類。
例如季節是有限的:春夏秋冬。
// jdk1.5之前的枚舉類 public class Season { // 1.聲明final屬性 private final String seasonName; // 季節名 private final String seasonDescribe; // 季節描述 // 2.為保證實例的數目是確定的需要私有化構造器,在構造器中初始化final的屬性 private Season(String seasonName,String seasonDescribe) { this.seasonName = seasonName; this.seasonDescribe = seasonDescribe; } // 3.通過公用的方法調用屬性 public String getSeasonName() { return seasonName; } public String getSeasonDescribe() { return seasonDescribe; } // 4.內部實例化枚舉類的對象 public static final Season SPRING = new Season("spring", " 春暖花開"); public static final Season SUMMER = new Season("summer", " 夏日炎炎"); public static final Season FALL = new Season("fall", " 秋高氣爽"); public static final Season WINTER = new Season("spring", " 白雪皚皚"); public String show() { return "Season [seasonName=" + seasonName + ", seasonDescribe=" + seasonDescribe + "]"; } }
我們可以使用以下的方式進行調用
Season season = Season.FALL; System.out.println(season.show()); System.out.println(season.getSeasonName() + "-->" + season.getSeasonDescribe());
jdk1.5之后我們可以使用enum關鍵字來簡化枚舉類的定義:
// jdk1.5之后的枚舉類 public enum Season { SPRING("spring", " 春暖花開"), SUMMER("summer", " 夏日炎炎"), FALL("fall", " 秋高氣爽"), WINTER("spring", " 白雪皚皚"); private final String seasonName; // 季節名 private final String seasonDescribe; // 季節描述 private Season(String seasonName,String seasonDescribe) { this.seasonName = seasonName; this.seasonDescribe = seasonDescribe; } public String getSeasonName() { return seasonName; } public String getSeasonDescribe() { return seasonDescribe; } public String show() { return "Season [seasonName=" + seasonName + ", seasonDescribe=" + seasonDescribe + "]"; } }
這樣使用枚舉類:
Season season = Season.FALL; System.out.println(season.show()); System.out.println(season.getSeasonName() + "-->" + season.getSeasonDescribe()); Season[] seasons = Season.values(); // 返回所有枚舉類的對象的數組 for (Season s : seasons) { System.out.println(s.getSeasonName()); } season = Season.valueOf("SUMMER"); // 返回枚舉類型的對象 System.out.println(season);
我們也可以讓枚舉類型實現接口:
// jdk1.5之后的枚舉類 public enum Season implements Info{ SPRING("spring", " 春暖花開"){ @Override public void show() { System.out.println(1); } }, SUMMER("summer", " 夏日炎炎"){ @Override public void show() { System.out.println(2); } }, FALL("fall", " 秋高氣爽"){ @Override public void show() { System.out.println(3); } }, WINTER("spring", " 白雪皚皚"){ @Override public void show() { System.out.println(4); } }; private final String seasonName; // 季節名 private final String seasonDescribe; // 季節描述 private Season(String seasonName,String seasonDescribe) { this.seasonName = seasonName; this.seasonDescribe = seasonDescribe; } public String getSeasonName() { return seasonName; } public String getSeasonDescribe() { return seasonDescribe; } @Override public void show() { System.out.println("Season [seasonName=" + seasonName + ", seasonDescribe=" + seasonDescribe + "]"); } }
以上的程序中每個枚舉類型的實例都各自實現自己的方法!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64732.html
摘要:如果一個程序只包含固定數量且其生命周期都是已知的對象,那么這是一個非常簡單的程序。 如果一個程序只包含固定數量且其生命周期都是已知的對象,那么這是一個非常簡單的程序。 1.泛型和類型安全的容器 通過使用泛型,可以在編譯期防止將錯誤類型的對象放置到容器中. 2.基本概念 Java容器類庫的用途是保存對象,并將其劃分為兩個不同的概念:Collection,Map. Collection...
摘要:迭代器通常被成為輕量級對象創建它的代價很小。與迭代器可以用于數組和所有對象,之所以能夠工作,是因為繼承了接口。 點擊進入我的博客 我覺得本章名字改成容器似乎更好理解,持有對象讓人感到一頭霧水我們需要在任意時刻和任意位置創建任意數量的對象,所以依靠創建命名的引用來持有對象已經滿足不了需求。Java可以用數組和其他容器類來(List、Set、Queue、Map)來解決這個問題,不同的容器...
摘要:匿名函數的好處在于可以減少局部變量,以免污染現有的運行環境。另外通過,這三個符號運行的匿名函數比運行的匿名函數可以減少一個字符的使用但是我們通常使用加因為其他的操作符可能會帶來其他的影響更多可以參考 js中的立即執行函數 ( function(){…} )()和( function (){…} () )是兩種javascript立即執行函數的常見寫法 問題: 為什么會出現上面的兩種不一...
摘要:所以那些匿名函數附近使用括號或一些一元運算符的慣用法,就是來引導解析器,指明運算符附近是一個表達式。 Immediately-invoked Function Expression(IIFE,立即調用函數),簡單的理解就是定義完成函數之后立即執行。因此有時候也會被稱為自執行的匿名函數(self-executing anonymous function)。 IIFE的叫法最早見于Ben...
閱讀 2001·2019-08-29 16:27
閱讀 1370·2019-08-29 16:14
閱讀 3372·2019-08-29 14:18
閱讀 3455·2019-08-29 13:56
閱讀 1252·2019-08-29 11:13
閱讀 2118·2019-08-28 18:19
閱讀 3439·2019-08-27 10:57
閱讀 2273·2019-08-26 11:39