java編程思想-02一切都是對象

1.用引用操縱對象

一切都看作對象,但操作的標識符實際是對象的一個引用(reference);

例如遙控器(引用)來操縱電視機(對象),實際操控的遙控器(引用),再由遙控器來調控電視機(對象)。如果在房間四處走動,同時又想調控電視機,那麼只需攜帶遙控器(引用),而不是電視機(對象)。

沒有電視機,遙控器可獨立存在,也就是說,你擁有一個引用,並不一定要有一個對象與它關聯,例如創建一個String的引用:

String s;

這裏創建的只是引用,並不是對象,如果此時向s發送一個消息,就會返回一個運行時錯誤,此時s沒有與任何對象關聯,因此,比較安全的做法是:創建一個引用的同時便進行初始化

String s="hello123";

此處java語言的一個特性,字符串可以用帶引號的文本初始化,但是還是建議對對象採用更通用的初始化方法。

2.必須由你創建所有對象

當你創建一個引用時,就希望它能與一個新的對象想關聯,通常我們是通過new操作符來實現的。

String s=new String("hello123");

2.1存儲到什麼地方

程序運行時,對象怎麼進行放置安排的?內存怎麼分配的?有5個不同的地方可以存儲數據:

  1. 寄存器,最快的存儲區,因爲它位於處理器內部,不同於其他存儲區的地方。但是寄存器的數量有限,因此寄存器需要根據需求進行分配,而不能人爲控制;

  2. 堆棧,位於通用RAM(隨機訪問存儲器),也即內存中,但通過堆棧指針可以從處理器那裏獲得直接支持。堆棧指針若向下移動,則分配新的內存;若向上移動,則釋放那些內存。這是一種快速有效的分配存儲方法,僅次於寄存器。創建程序時,java系統必須知道存儲在堆棧內所有項的確切生命週期,以便上下移動堆棧指針,這一約束限制了程序的靈活性,所以雖然某些java數據存儲在堆棧中—-特別是對象引用,但是java對象並不存儲與其中;

  3. , 一種通用的內存池(位於RAM區),用於存放所有的java 對象,堆不同於堆棧的好處是:編譯器不需要知道存儲的數據在堆裏存活多長時間。因此,在堆裏分配存儲有很大的靈活性。當需要一個對象時,只需用new寫一行簡單的代碼,當執行這行代碼時,會自動在堆裏進行內存分配,當然,爲這種靈活性必須付出相應的代價,用堆進行存儲分配和清理可能比用堆棧進行存儲需要更多的時間;

  4. 常量存儲 , 常量值通常直接存放在程序代碼內部,這樣做是安全的,因爲他們永遠不會被改變。有時,在嵌入式系統中,常量本身會和其他部分分隔離開,所以在這種情況下,可以選擇將其存放在ROM(只讀存儲器)中;

  5. 非RAM存儲 , 如果數據完全存活於程序之外,那麼它可以不受程序的任何控制,在程序沒有運行時也可以存在。器中兩個基本的例子是“流對象”和“持久化對象”。在“流對象”中,對象轉化成字節流,通常被髮送給另一臺機器。在“持久化對象”中,對象被存放於磁盤上,因此,即使程序終止,它們仍可以保持自己的狀態。這種存儲方式的技巧在於:把對象轉化成可以存放在其他媒介上的事物,在需要時,可恢復成常規的,基於RAM的對象。

2.2 特例—-基本類型

在程序設計中經常用到一系列類型,它們需要特殊對待。你可以把它們想象成“基本”類型。之所以特殊對待,是因爲new將對象存在“堆”裏,故用new創建一個對象——特別是小的、簡單的變量,往往不是很有效。因此,對於這些類型,Java採取與C和C++相同的方法。也就是說,不用new來創建變量,而是創建一個並非是“引用”的“自動”變量。這個變量擁有它的“值”,並置於堆棧中,因此更加高效。

Java確定每種基本類型所佔存儲空間的大小,不會隨着硬件架構而改變,存儲空間大小的不變性使java程序更具可移植性 。
這裏寫圖片描述

所有數值類型都有正負號,所以不要去尋找無符號的數值類型。

Boolean類型所佔的空間大小沒有明確指出,僅定義爲能夠取字面值的true和false;

基本類型具有包裝器類,繼承Object類,可以在堆中創建一個非基本對象,用來表示對應的基本類型,例如:

int a=10;
Integer object=new Integer(10);

注:這裏涉及到自動拆箱自動裝箱,之後會提到。

—–高精度數字

java 提供了兩個用於高精度計算的類:BigIntegerBigDecimal(java.math.*)。雖然它們大體上屬於”包裝器類”的範疇,但二者都沒有對應的基本類型。這兩個類包含的方法,提供的操作與對基本類型所能執行的操作相似,只不過必須以方法調用方式取代運算符方式來實現。

  1. BigInteger:支持任意精度的整數,可以精確地表示任意大小的整數值,同時在運算過程中不會丟失任何信息。
  2. BigDecimal:可以精確地表示任意精度的小數,同時在運算過程中不會丟失任何信息。

2.3 java中的數組

C和C++的數組就是內存塊,如果一個程序訪問其自身內存塊之外的數組,或在數組初始化前使用內存,會產生難以預料的後果。

java的主要目標之一是安全性,java確保數組會被初始化,而且不能在它的範圍之外被訪問,這種範圍檢查,是以每個數組上少量的內存開銷及運行時的下標檢查爲代價的,但由此換來的是安全性和效率的提高,因此付出的代價是值得的。

當創建一個數組對象時,實際上就是創建了一個引用數組,並且每個引用都會自動被初始化爲一個特定值,該值擁有自己的關鍵字null,一旦java 看到null,就知道這個引用還沒有指向某個對象,在使用任何引用前必須爲其指定一個對象,如果試圖使用一個還是null的引用,在運行時將會報錯,因此,常犯的數組錯誤在java中就可以避免,還可以創建用來存放基本數據類型的數組,同樣,編譯器也能確保這種數組的初始化,因爲它會將這種數組所佔的內存全部置零。

3 永遠不需要銷燬對象

3.1 作用域

作用域決定了在其內定義的變量名的可見性和生命週期,在C,C++和Java中,作用域由花括號的位置決定, 例如:

{
    int a=1;
    //此處只有a可以用
    {
      int b=2;
      //此處a和b都可以用
    }
    //此處只有a可以用,b已經超過作用域了
}

作用域裏定義的變量只可用於作用域結束之前
在C和C++中以下代碼是合法的,但在java中是錯的。

 {
     int a=2;
     {
       int a=3;  //不合法Illegal
     }
}

編譯器會報告變量a已經定義過,所以,在C和C++中將一個較大作用域的變量“隱藏”起來的做法,在java中是不允許的,因爲java設計者認爲這樣會導致程序混亂。

3.2 對象的作用域

java 對象不具備和基本類型一樣的生命週期,當用new創建一個java對象時,它可以存活於作用域之外,所以假如這樣寫:

{
    String s=new String("hello123");
}

引用s在作用域終點就消失了,然而,s指向的String對象仍繼續佔據內存空間,在這一段代碼中,我們無法在這個作用域之後訪問這個對象,因爲對它唯一的引用已超出了作用域的範圍。

事實證明:由new創建的對象,只要你需要,就會一直保留下去,這樣,許多C++的編程問題在java中就完全消失了,在C++中,你不僅必須要確保對象的保留時間與你需要這些對象的時間一樣長,而且還必須在你使用完它們之後,將其銷燬。

大家想一下,如果java讓對象繼續存在,那麼靠什麼才能防止這些對象填滿內存空間,進而阻塞你的程序呢?

這正是C++裏面可能發生的問題,這也是java神奇之所在,java 有一個垃圾回收器,用來監視用new創建的所有對象,並辨別哪些不會再被引用的對象,隨後釋放這些對象的內存空間,以便供其他新的對象使用,也就是說,你根本不必擔心內存回收的問題,你只需要創建對象,一旦不再需要,它們就會自行消失,這樣做就消除了這類編程問題(即“內存泄漏”),這是由於程序員忘記釋放內存而產生的問題。

4.創建新的數據類型:類

class 關鍵字 之後緊跟着的是新類型的名稱,例如 :

class ClassA{ 
    /* 
    Class Body 
    */ 
} 

這樣就創建了一種新的類型,你可以用new 創建這個ClassA這個類型的對象了;

ClassA a=new ClassA(); 

但是,在定義它的所有方法之前,還沒有辦法能讓它去做更多的事情。

4.1 字段和方法

一旦定義了一個類(在java中你所做的全部工作就是定義類,產生哪些類的對象,以及發送消息給這些對象),就可以在 類中設置兩種類型的元素:字段(或稱數據成員)和方法(或稱成員函數),字段可以是任何類型的對象,可以通過其引用與其進行通信,也可以是基本類型中的一種,如果字段是對某個對象的引用,那麼必須初始化該引用,以便使其與一個實際的對象相關聯。

每個對象都有用來存儲其字段的空間,普通字段不能在對象間共享,下面是一個具有某些字段的類:

class DataOnly{
  int i;
  double d;
  boolean b;
}

儘管這個類除了存儲數據之外什麼也不能做,但是仍舊可以像下面這樣創建它的一個對象:

DataOnly data=new DataOnly();

可以給字段賦值,但首先必須要知道如何引用一個對象的成員,具體的實現爲:在對象引用的名稱之後緊接着一個句點,然後再接着是對象內部的成員名稱。

 objectReference.member(對象引用的成員)
 例如:
 data.i=2;
 data.d=1.1;
 data.b=true;

想修改的數據也有可能位於對象所包含的其他對象中,在這種情況下,只需要再使用連接句點即可,例如:

myPlane.leftTank.capacity=100;

DataOnly類除了保存數據外沒別的用處,因爲它沒有任何成員方法,如果想了解成員方法的運行機制,就得先了解參數和返回值的概念,稍後將對此作簡略描述。

基本成員默認值

若類的某個成員是基本數據類型,即使沒有初始化,java也會確保它獲得一個默認值,以確保哪些是基本類型的成員變量得到初始化(C++沒有此功能),防止產生程序錯誤,但是,這些初始化對你的程序來說,可能是不正確的,甚至是不合法的,所以最好明確地對變量進行初始化。

然而上述確保初始化的方法並不適用於”局部“變量(即並非某個類的字段)。因此,如果在某個方法定義中有 int x;那麼變量x得到的可能是任意值(與C和C++中一樣),而不會被自動初始化爲零,所以在使用x前,應當對其賦一個適當的值,如果忘記了這麼做,java會在編譯時返回一個錯誤,告訴你此變量沒有初始化,這正是java優於C++的地方,(許多C++編譯器會對未初始化變量給予警告,而java則視爲錯誤)
這裏寫圖片描述

5 方法、參數和返回值

許多程序設計語言(像C和C++)用函數這個術語來描述命名子程序,而在java裏卻常用方法這個術語來表示“做某些事情 的方式“。java的方法決定了一個對象能夠接收什麼樣的消息。方法的基本組成部分包括:名稱、參數、返回值和方法體

下面是它最基本的形式:

ReturnType methodName(/* argument list*/){ 
/* Method Body*/ 
} 

返回類型描述的是在調用方法之後從方法返回的值,參數列表給出了要傳給方法的信息的類型和名稱,方法名和參數列表(合起來稱爲“方法簽名”)唯一的標識出某個方法。

java中的方法只能作爲類的一部分來創建,方法只能通過對象調用,且這個對象必須能執行這個方法調用。 如果試圖在某個對象上調用它並不具備的方法,那麼在編譯時就會得到一條錯誤信息,通過對象調用方法時,需要先列出對象名,緊接着是句點,然後是方法名和參數列表。如:

objectName.methodName(arg1,arg2,arg3);

例如 對象a,可以調用方法f(),返回值爲int 。

int x=a.f(); 

這種調用方法的行爲通常稱爲發送消息給對象,消息爲f(),對象爲a。面向對象的
程序設計通常稱爲“向對象發送消息”。

5.1 參數列表

方法的參數列表也是採用對象形式,在參數列表必須指定每個所傳遞對象的類型和名字,java中任何傳遞對象的場合,實際上傳遞的也是引用,並且引用的類型必須正確,如果參數被設爲String類型,則必須傳遞一個String對象,否則編譯器將拋出錯誤。
例如:

int storage(){
  return s.length() +1;
}

這個方法必須置於某個類的定義內才能被正確編譯。

將s傳遞給此方法,就可以把他當作其他對象一樣進行處理(可以給它傳遞消息),在這裏,s的length()方法被調用,它是String類提供的方法之一,會返回字符串包含的字符數。

return關鍵字包括兩方面:
1、代表“已經做完,離開此方法”;
2,如果方法返回值,這個值要放在return語句後面,這個例子中返回值是計算s.length()+1這個表達式得到的。

返回值可以返回任意類型,如果不想返回值,可以指示此方法返回void(空),例子如下:

boolean flag() {return true;}
double aaa(){return 2.73;}
void nothing{return;}
void nothing2{}

若返回類型是void,return關鍵字的作用只是退出方法,因此,沒有必要到方法結束時才離開,可在任何地方返回,但如果返回類型不是void,那麼無論在何處返回,編譯器都會強制返回一個正確類型的返回值

6 構建一個java程序

6.1 名字可見性

如果在程序的某個模塊使用了一個名字,在程序的另一模塊也使用了相同的名字,爲了避免名字重複引發的問題,java採用了全新的方法避免問題,爲了給一個類庫生成不會與其他名字混淆的名字,java設計者希望程序員反過來使用自己的域名,因爲這樣可以保證他們肯定是獨一無二的,我的域名是www.lyt.com,我的各種類庫都被命名爲com.lyt.XXX(XXX可以是支付,也可以是購買),反轉後,句點代表子目錄的劃分。這種機制意味着所有的文件都能夠自動存活於自己的名字空間內,而且同一個文件內的每個類都有唯一的標識符

6.2 運用其他構件

如果想在自己的程序中使用預先定義好的類,那麼編譯器就必須知道怎麼定位它們,當然,這個類可能就在發出調用的那個源文件中,在這種情況下, 就可以直接使用這個類—即使這個類在文件的後面纔會被定義(java 消除了所謂的“向前引用”問題) 。
如果那個類位於其他文件中,可以使用關鍵字import來準確的告訴編譯器想要的類是什麼。import指示編輯器導入一個包,也就是一個類庫。大多時候,我們使用與編譯器附在一起的java標準庫裏的構件。例如:

import java.util.ArrayList; 

這行代碼告訴編譯器,你想使用java的ArrayList類,但是util包含了數量衆多的類,有時你想使用其中的幾個,同時又不想明確地逐一聲明:那麼你很容易使用通配符“*”來達到這個目的:

import java.util.*; 

6.3 static 關鍵字

當創建類時,實際就是描述類的對象的外觀和行爲。執行new來創建對象時,數據儲存空間才被分配,其方法才能被外界調用。
有兩種用上述方法無法解決:
1、只想爲某特定域分配單一存儲空間,而不考慮創建對象的個數;
2、希望某個方法不與包含它的類的任何對象關聯在一起,也就是說,即使沒有創建對象,也能夠調用這個方法。

通過static關鍵字可以滿足這兩方面的需要,當聲明一個事物是static時,就意味着這個域或方法不會與包含它的類的任何對象實例關聯在一起,所以,即使從未創建過某個類的對象,也可以調用其static方法或訪問其static域,通常,你必須創建一個對象,並用它來訪問數據或方法。因爲非static域和方法必須知道他們一起運作的特定對象。 例如:

class User(){
    static int age=17;
}

生成了一個static字段,並進行了初始化;現在創建兩個StaticTest對象,User.age也只有一份存儲空間, 這兩個對象共享一個age,

User user=new User(); 
User user1=new User(); 

user.age和user1.age指向同一存儲空間,因此他們具有相同的值17。
引用static變量有兩種方法:
1、可以通過對象定位它,user.age;
2、也可以通過其類名直接引用,User.age,而對於非靜態成員則不行。
使用類名引用static變量是首選,靜態方法和靜態對象的操作類似,引用可以用ClassName.method()形式,定義靜態方法:

class User(){
  static void sing(){
        //唱歌
  }
}

當static作用於某個字段時,會改變數據創建的方式(因爲一個static字段對每個類來說都只有一份存儲空間,而非static字段則是對每個對象有一個存儲空間),但是如果static作用於某個方法,差別卻沒那麼大, static方法的一個重要用法就是在不創建任何對象的前提下就可以調用它,這一點對於定義main()方法很重要,這個方法是運行一個應用的入口點。

 import java.util.*;
 public class Util {
   public static void main(String[] args) {
       System.out.println(new Date());   // java標準庫裏的Date類
   }
 }

程序開頭,必須聲明import語句,引入在文件代碼中需要用到的額外類,之所以說它們“額外”,是因爲有一個特定類會自動被導入到每一個java文件中:java.lang。java.lang是默認導入到每個java文件中的,所以它的所有類都可以被直接使用。system是它裏面的類。

類的名字必須和文件名相同,如果創建一個獨立運行的程序,文件中必須存在某個類和該文件同名,且那個類必須包含一個名爲main()的方法。

public static void main(String[] args){

}

其中 public 關鍵字是指這是一個可由外部調用的方法,main()方法的參數是一個String對象的數組,程序中並未用到args,但是java編譯器要求必須這麼做,因爲args要用來存儲命令行參數

7.1 編譯和運行

要編譯、運行java程序,首先要有一個java開發環境,安裝好jdk,配置好路徑,確保計算機能找到javac和java這兩個文件,打開到java文件所在目錄,輸入:

javac HelloWorld.java   //編譯

正常不會有任何反應,如果有錯會返回錯誤,接着運行

java HelloWord   //運行

8 註釋和嵌入式文檔

java中有兩種註釋風格。

/**
 *第一種
 *
 */

/**
  第一種            
 */// 第二種 單行註釋

8.1 註釋文檔

javadoc用於提取註釋的工具,是jdk安裝的一部分,提取註釋標籤,輸出一個HTML文件,用於Web瀏覽器查看。

8.2 語法

三種類型的註釋文檔,對應註釋後面的三種元素:類、域、方法。

 /**
  A Class comment
  */
public class Documentationl{
    /**
     A field comment
     */
    public int i;
    /**
    A method comment
     */
    public void f(){}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章