Java物件和類
java作為一種面向物件的語言,是支援多型,繼承,封裝,抽象,類,物件,例項,方法,過載.
該文章研究物件和類的概念.
- 物件 :物件是類的一個例項( 物件不是找個女朋友 ),有狀態和行為。例如,一個人是一個物件,它的狀態有:膚色、名字、性別;行為有:睡覺,、說話,吃等。
- 類 :類是一個模板,它描述一類物件的行為和狀態。
下圖中男孩女孩為類,而具體的每個人為該類的物件:
java中的物件
什麼是物件(object):萬物皆物件:物件就是個體
通過上圖可以認為人就是物件,像生活中車,動物,植物等,這些都可以認為是物件,而且這些物件都有自己的狀態和行為
用車舉例,它的狀態有,品牌,顏色,行為有,啟動,要玻璃窗等
對比現實物件和軟體物件,它們之間十分相似。
軟體物件也有狀態和行為。軟體物件的狀態就是屬性,行為通過方法體現。
在軟體開發中,方法操作物件內部狀態的改變,物件的相互呼叫也是通過方法來完成。
Java中的類
類是物件的圖紙,模板,
通過下面一個簡單的類來理解下Java中類的定義:
public class people{ String breed; int age; String color; void eating(){ } void hungry(){ } void sleeping(){ } }
一個類可以包含以下型別變數:
- 區域性變數 :在方法、構造方法或者語句塊中定義的變數被稱為區域性變數。變數宣告和初始化都是在方法中,方法結束後,變數就會自動銷燬。
- 成員變數 :成員變數是定義在類中,方法體之外的變數。這種變數在建立物件的時候例項化。成員變數可以被類中方法、構造方法和特定類的語句塊訪問。
- 類變數 :類變數也宣告在類中,方法體之外,但必須宣告為static型別。
一個類可以擁有多個方法,在上面的例子中:eating()、hungry()和sleeping()都是People類的方法。
類和物件之間的關係
類是物件的圖紙,模板.物件是類的例項化
重點在於如何構建這個藍圖,模板
如何去定義一個類,確定類和類之間的關係,下面介紹構造方法
構造方法
每個類都有構造方法。如果沒有顯式地為類定義構造方法,Java編譯器將會為該類提供一個預設構造方法。
在建立一個物件的時候,至少要呼叫一個構造方法。構造方法的名稱必須與類同名,一個類可以有多個構造方法。
物件新建的過程:Person p = new Person
從廣義的角度,構造器的作用:初始化:對物件使用之前進行資訊的預處理
構造器的定義:
①構造方法的方法名必須與類名保持一致
②構造方法沒有返回值型別;構造方法返回的一定物件本體的記憶體地址
③構造方法僅僅允許使用訪問許可權修飾符進行修飾
④系統給每一個類都提供一個預設的空構造(沒有引數的構造方法),這個構造方法僅僅用來給各個屬性賦預設值
⑤預設的控構造只能在沒有任何手動定義的構造器的類中使用:如果手動定義了一個構造方法,預設的構造方法就失效
⑥在同一個類中,可以存在多個構造器(構造器的過載)
方法的過載
java中如何區分兩個方法
①方法的呼叫者:方法的呼叫者一旦確定,就能夠根據呼叫者找到這個方法的歸屬
②方法名
③方法的形參列表
****注意:方法的返回值並不能用來區分兩個方法:一個方法在呼叫的時候,可以接收返回值也可以不接收,在不接收返回值的時候,就沒有辦法通過返回值(型別)區分兩個同名、相同形參列表、定義在同一個類中的方法
方法的過載定義:兩同一不同:在同一個類中,定義多個同名方法,使用不相同的形參列表進行區分
不同的形參列表
①形參列表的長度(形參的個數)
②形參的型別
③形參的順序
注意:
- 過載方法引數必須不同:
引數個數不同,如method(int x)與method(int x,int y)不同
引數型別不同,如method(int x)與method(double x)不同g
引數順序不同,如method(int x,double y)與method(double x,int y)不同
- 過載只與方法名與引數型別相關與返回值無關
如void method(int x)與int method(int y)不是方法過載,不能同時存在
- 過載與具體的變數識別符號無關
如method(int x)與method(int y)不是方法過載,不能同時存在
區分方法
建立物件
物件是根據類建立的。在Java中,使用關鍵字new來建立一個新的物件。建立物件需要以下三步:
- 宣告 :宣告一個物件,包括物件名稱和物件型別。
- 例項化 :使用關鍵字new來建立一個物件。
- 初始化 :使用new建立物件時,會呼叫構造方法初始化物件。
下面是一個建立物件的例子
public class People{ public People(String name){ //這個構造器僅有一個引數:name System.out.println("人的名字是 : " + name ); } public static void main(String []args){ // 下面的語句將建立一個People物件 Puppy p= new People( "tom" ); } }
編譯並執行上面的程式,會打印出下面的結果:
人的名字是 : tom
訪問例項變數和方法
通過已建立的物件來訪問成員變數和成員方法,如下所示:
/* 例項化物件 */ ObjectReference = new Constructor(); /* 訪問類中的變數 */ ObjectReference.variableName; /* 訪問類中的方法 */ ObjectReference.methodName();
例項
下面的例子展示如何訪問例項變數和呼叫成員方法:
public class People{ int peopleAge ; public People(String name){ // 這個構造器僅有一個引數:name System.out.println("人的名字是 : " + name ); } public void setAge( int age ){ peopleAge = age; } public int getAge( ){ System.out.println("人的年齡為 : " + peopleAge ); return peopleAge ; } public static void main(String []args){ /* 建立物件 */ People myPuppy = new People( "tom" ); /* 通過方法來設定age */ myPeople.setAge( 2 ); /* 呼叫另一個方法獲取age */ myPeople.getAge( ); /*你也可以像下面這樣訪問成員變數 */ System.out.println("變數值 : " + myPeople.peopleAge ); } }
結果:
人的名字是 : tommy 人的年齡為 : 2 變數值 : 2
原始檔宣告規則
在本節的最後部分,我們將學習原始檔的宣告規則。當在一個原始檔中定義多個類,並且還有import語句和package語句時,要特別注意這些規則。
- 一個原始檔中只能有一個public類
- 一個原始檔可以有多個非public類
- 原始檔的名稱應該和public類的類名保持一致。例如:原始檔中public類的類名是Employee,那麼原始檔應該命名為Employee.java。
- 如果一個類定義在某個包中,那麼package語句應該在原始檔的首行。
- 如果原始檔包含import語句,那麼應該放在package語句和類定義之間。如果沒有package語句,那麼import語句應該在原始檔中最前面。
- import語句和package語句對原始檔中定義的所有類都有效。在同一原始檔中,不能給不同的類不同的包宣告。
類有若干種訪問級別,並且類也分不同的型別:抽象類和final類等。這些將在訪問控制章節介紹。
除了上面提到的幾種型別,Java還有一些特殊的類,如:內部類、匿名類。
Java包
包主要用來對類和介面進行分類。當開發Java程式時,可能編寫成百上千的類,因此很有必要對類和介面進行分類。
Import語句
在Java中,如果給出一個完整的限定名,包括包名、類名,那麼Java編譯器就可以很容易地定位到原始碼或者類。Import語句就是用來提供一個合理的路徑,使得編譯器可以找到某個類。
例如,下面的命令列將會命令編譯器載入java_installation/java/io路徑下的所有類
import java.io.*;
一個簡單的例子
在該例子中,我們建立兩個類: Employee 和 EmployeeTest 。
首先開啟文字編輯器,把下面的程式碼貼上進去。注意將檔案儲存為 Employee.java。
Employee類有四個成員變數:name、age、designation和salary。該類顯式聲明瞭一個構造方法,該方法只有一個引數。
Employee.java 檔案程式碼:
import java.io.*; public class Employee{ String name; int age; String designation; double salary; // Employee 類的構造器 public Employee(String name){ this.name = name; } // 設定age的值 public void empAge(int empAge){ age =empAge; } /* 設定designation的值*/ public void empDesignation(String empDesig){ designation = empDesig; } /* 設定salary的值*/ public void empSalary(double empSalary){ salary = empSalary; } /* 列印資訊 */ public void printEmployee(){ System.out.println("名字:"+ name ); System.out.println("年齡:" + age ); System.out.println("職位:" + designation ); System.out.println("薪水:" + salary); } }
程式都是從main方法開始執行。為了能執行這個程式,必須包含main方法並且建立一個例項物件。
下面給出EmployeeTest類,該類例項化2個 Employee 類的例項,並呼叫方法設定變數的值。
將下面的程式碼儲存在 EmployeeTest.java檔案中。
EmployeeTest.java 檔案程式碼:
import java.io.*; public class EmployeeTest{ public static void main(String []args){ /* 使用構造器建立兩個物件 */ Employee empOne = new Employee("RUNOOB1"); Employee empTwo = new Employee("RUNOOB2"); // 呼叫這兩個物件的成員方法 empOne.empAge(26); empOne.empDesignation("初級程式設計師"); empOne.empSalary(1000); empOne.printEmployee(); empTwo.empAge(21); empTwo.empDesignation("小白程式設計師"); empTwo.empSalary(500); empTwo.printEmployee(); } }
結果:
自己可以敲一下鍵盤就知道了.........
物件在記憶體中的存在形式
Java vm記憶體:堆記憶體,棧記憶體
棧記憶體:隨時開闢,使用,回收-->Java中的方法在執行起來的時候
會分配一個專有的佔記憶體,方法執行結束,棧記憶體回收
堆記憶體:長久存在,用來儲存物件的本體的一個空間--》一個公共的記憶體空間
Person p = new Person();
P:物件引用,引用變數,物件名-》存在方法棧記憶體中
new Persoon();物件本體--> 堆記憶體
物件本體的空間開闢,賦值等過程是相對比較消耗時間和空間的,沒喲個物件本體,都是寶貴的資源,能夠重用就重用,物件本體不應該被隨便的回收,應該將物件本體放在一個穩定的空間內,所以將物件本體放在堆記憶體 中
凡是以物件本體和飲用方式存在的變數,都是引用資料型別的:陣列,類的物件
*****基本資料型別的變數都儲存在棧記憶體中,和物件本體不一樣
棧:儲存區域性變數;堆:儲存new出來的陣列或物件
物件的回收
當一個物件本體失去所有的引用的時候,這個物件本體就變成一個弱引用物件
JVM中提供一種機制——垃圾回收機制,垃圾回收器Garbage Collector
垃圾回收機制會定期掃描記憶體空間,找到所有的弱引用物件,對其回收
類的構成
類的構成: 變數和方法
1.變數:
按照位置進行劃分:
全域性變數:(屬性)
按照靜態進行劃分 :
靜態全域性變數(類屬性)
非靜態全域性變數 (物件屬性)
區域性變數:
定義在方法中的區域性變數
定義在程式碼塊中的區域性變數
定義在方法引數列表中的形參(形式引數)
區域性變數
區域性變數的生命週期:
想要訪問一個區域性變數,必須在這個區域性變數的生命週期之內對其進行訪問,在生命週期之外,要麼這個變數尚未開始,要麼這個變數的生命週期已經結束
形參(形式引數):
形參的生命週期貫穿整個方法
形參的傳遞機制:值傳遞:值的拷貝
對於基本資料型別:拷貝的是基本資料型別中的常量值,形參進行值得改變不會對實參的值發生影響
對於引用資料型別:拷貝的是引用變數中儲存的物件本體的地址,實參將地址拷貝給形參,實參和形參實際上指向的物件本體其實是同一個,其中一個對這個地址所在的物件本體進行修改,大家都能看得見
全域性變數:屬性(Filed)
生命在類之中、方法之外的變數
全域性變數的生命週期:在整個類中有效
屬性:相關資訊的描述,通過眾多的屬性的有機結合,就能夠描述一個特定的群體
靜態全域性變數和非靜態全域性變數:
靜態全域性變數(類屬性):
①類屬性都是用static進行修飾
②作用:用來描述一個類的物件都具有而且值相同的屬性
③類屬性的值是全部物件共享的,如果有一個物件對類屬性進行修改,其他物件的這個型別屬性的值也會發生變化
④類屬性儘量通過靜態的方式進行訪問——通過類名的方式進行訪問:類屬性既可以通過類名訪問也可以通過物件名訪問
⑤類屬性的作用
1。可以用來記錄一個類的屬性資訊
2.記錄程式在執行過程當中產生的中間值
3.在類與類之間傳遞資訊
非靜態全域性變數(物件屬性)
①物件屬性都不用static修飾
②物件屬性的作用;用來描述一個類的物件都具有,但是值個性化的屬性
③物件屬性的值是物件獨享的,一個物件的物件屬性發生改變,其他不會跟著發生改變
④物件屬性只能夠通過非靜態的方式進行訪問:物件屬性只能被物件名訪問,類名不能夠訪問物件屬性
其他:
類先定義在使用:用來new物件
Java中的命名法:駝峰命名
定義類、屬性、方法:
①命名儘量使用英語單詞
②明明使用的單詞片語能夠描述這個類、屬性、方法的作用
③包名儘量完全使用小寫字母:字母、數字、下劃線 類名:如果一個類名由多個單片語成,各個單詞的首字母大寫,如果出現縮寫,整個縮寫單詞都大寫:TestMyMD5 變數名和方法名:第一個單詞的首字母小寫,之後其他單詞的首字母大寫
add() addElement() eye eyeNumber 屬性的命名不允許使用數字作為開頭
空物件:null
①null並不依賴於任何一個類,null不是任何類的物件,沒有任何一個類與null相對應
②但是任何一個類物件的引用變數都可以引用null String str = null; Person p1 = null; Car c1 = null;
③null或者指向null的引用變數不能夠呼叫任何屬性和方法,否則會引起NullPointerException
2.方法:
方法:
按照功能進行區分:
main方法:
一般方法:
按照靜態與非靜態:
靜態方法:
非靜態方法:
構造方法
定義:一系列相關處理過程的集合
方法的原子性:功能的模組化:在定義方法的時候,儘量將功能差分成不可再細分的子功能,大功能的完成取決於子功能呢的呼叫和配合
方法的內聚性和耦合性:高內聚,低耦合
內聚性:方法自行完成任務,儘量不依賴於外界條件的性質
耦合性:方法完成一項任務需要依賴多少的外界條件
方法:
按照功能進行區分:
main方法:
一般方法:
按照靜態與非靜態:
靜態方法:
非靜態方法:
構造方法:
main方法:
Main方法是所有程式的入口,程式執行起來首先執行main方法,在通過main方法中對其他方法的呼叫,達到執行程式的目的,
形參String[] args:外部程式命令集
在一般情況下,args != null,args.length == 0
一般方法
方法定義的一般語法
Pubilc static void main(String[] args){}
[訪問許可權修飾符][靜態修飾符][final修飾符][同步修飾符]...返回值型別 方法名(形參列表)[宣告異常丟擲]
靜態方法:類方法
①使用static關鍵字進行修飾
②靜態方法的作用:靜態方法一般都是充當工具方法的作用,使用者僅關心這個方法的執行過程和執行結果,沒必要關心是誰提供的這個方法
④靜態方法(類方法)呼叫屬性:類方法只能夠呼叫類屬性,不能夠呼叫物件屬性
非靜態方法:物件方法
①物件方法不使用static修飾
②物件方法僅能夠通過物件呼叫:物件方法的呼叫結果取決於呼叫者
③物件方法的作用:因為呼叫者不同能夠引起執行結果的不同,這樣的方法應該宣告成物件方法
④物件方法訪問屬性:物件方法即可以訪問類屬性也可以訪問物件屬性
引數傳遞
引數傳遞,可以理解當我們要呼叫一個方法時,我們會把指定的數值,傳遞給方法中的引數,這樣方法中的引數就擁有了這個指定的值,可以使用該值,在方法中運算了。這種傳遞方式,我們稱為引數傳遞。
- l 在這裡,定義方法時,引數列表中的變數,我們稱為形式引數
- l 呼叫方法時,傳入給方法的數值,我們稱為實際引數
我們看下面的兩段程式碼,來明確下引數傳遞的過程:
public class ArgumentsDemo01 { public static void main(String[] args) { int a=5; int b=10; change(a, b);//呼叫方法時,傳入的數值稱為實際引數 System.out.println("a=" + a); System.out.println("b=" + b); } public static void change(int a, int b){//方法中指定的多個引數稱為形式引數 a=200; b=500; } }
結果:
a=5 b=10
再看另一段程式碼
public class ArgumentsDemo02 { public static void main(String[] args) { int[] arr = { 1, 2, 3 }; change(arr);// 呼叫方法時,傳入的數值稱為實際引數 for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } public static void change(int[] arr) {// 方法中指定的多個引數稱為形式引數 for (int i = 0; i < arr.length; i++) { arr[i] *= 2; } } }
程式的執行結果如下:
引數傳遞圖解
通過上面的兩段程式可以得出如下結論:
- 當呼叫方法時,如果傳入的數值為基本資料型別(包含String型別),形式引數的改變對實際引數不影響
- 當呼叫方法時,如果傳入的數值為引用資料型別(String型別除外),形式引數的改變對實際引數有影響
參考資料:http://www.runoob.com/java/java-object-classes.html