面向對象(OOP)的三個特徵:
1. 封裝(根源和最根本的屬性)
2. 繼承
3. 多態
類與對象
面向對象的編程思想力圖使在計算機語言中事物的描述與現實世界中該事物的本來面目儘可能地一致,類(Class)和對象(Object)就是面向對象方法的核心概念。類是對某一類事物的描述,是抽象的、概念上的定義;對象是實際存在的該類事物的個體,是具體的,因爲也稱實例(Instance)。
如:
汽車設計圖類,具體的一輛大衆汽車爲一個實例。
1.1類的定義
類可以將數據與函數封裝在一起,其中數據表示類的屬性,函數表示類的行爲。即定義了類就要定義類的屬性(類的成員變量)與行爲(方法)(類的成員方法)。
如:
class Person {
int age;
void printAge() {
int age = 60; // 函數內部重新定義的一個局部變量,區別與成員變量
System.out.println(“age is ” + age);
}
}
1.2對象的產生與使用
僅有汽車設計圖是無法實現汽車功能的,只有產生了實際的汽車才行,同樣想要實現類的屬性和行爲,必須創建具體的對象。
如:(new關鍵字創建新對象)
Person p1 = new Person();
對象引用句柄p1(代表符合)是在棧中分配的一個變量,對象p1(實際對象)在堆中分配,原理同於數組。
注意:定義成員變量時,有默認值,局部變量時,必須初始化.
每個創建的對象都是有自己的生命週期的。
對象的3中生命週期:
第一種:
第二種:
第三種:(p1 = null;後,產生的Person對象不會變成垃圾,因爲這個對象仍被p2所引用,
直到p2超出其作用域而無效)
1.3對象的比較
有2種方式可用與對象間的比較,‘==’與equals()方法
‘==’操作符用於比較兩個變量的值(內存地址)是否相等,比如基本數據類型間的比較。
equal()方法用於比較2個對象的內容是否一致。
程序代碼:(==)
class Compare {
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
String str3 = str1;
// str1 與 str2 是不同的對象,== 比較的是內存地址啦
System.out.println(str1 == str2);
System.out.println(str1 == str3);
}
}
結果:
false
true
圖解:
程序代碼:(equals())
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
String str3 = str1;
// equals()比較的是值
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
}
}
1.4實現類的封裝性
private:
正如人一樣,人的身高不能隨便修改,只能通過各種攝取營養的方法去修改這個屬性。這是一種保護。那麼怎樣對一個類的成員實現保護呢?只需要在定義類成員(包括變量和方法)時,使用private 關鍵字說明這個成員的訪問權限,private訪問權限定義了類的私有成員,爲高保護權限。只能被這個類的其他成員方法調用,不能被其他類中的方法所調用。
代碼:
class Person {
private int age;
public void shout() {
System.out.println(age); // 這一句沒有錯誤,原因在上
}
}
class TestPerson {
public static void main(String[] args) {
new Person().age = -30; // 報錯,age是Person裏的私有成員,不能在其他類中直// 接調用和訪問。
}
}
結論:private 修飾的類成員,爲該類的私有成員,只能在該類的內部訪問。
public:
用public 關鍵字修飾類成員,這些成員就是公共的,並可以在任意類中訪問,當然,要在一個類外部訪問這個類的成員必須用格式:‘對象.對象成員’。
爲了實現良好的封裝性,通常將類的成員變量聲明爲private,在通過public方法來對這個變量進行訪問。
代碼:
class Person {
private int age;
public viod setAge(int pAge) {
this.age = pAge;
}
pulic int getAge() {
return age;
}
}
public class TestPerson {
public static void main(String[] args) {
Person p1 = new Person();
p1.setAge(3);
System.out.println(p1.getAge());
}
}
對類成員變量private,提供一個多個public方法實現對該成員變量的訪問或修改,即封裝
封裝目的:
1. 隱藏類的細節;
2. 讓使用者只能通過事先定好的方法來訪問數據,限制對屬性的不合理操作;
3. 易於修改、維護、數據檢查。
protected:
包訪問權限,只有處在同一包中的類可以訪問該成員。
1.5構造函數
每創建一個Person對象,構造方法都會被調用一次。構造方法是public的,亦可爲private(如單態設計模式)
根類名相同。沒有返回值,沒有return語句。可以重載。
使用構造器可以避免疏忽對變量的初始化,並且可以實現一次完成對類的所有實例對象的初始化。
如果聲明類時,不寫該類的構造函數,將默認產生一個無參構造,如果聲明瞭有參構造,並且沒有寫默認構造器,將不會有默認構造器了。所以:
建議聲明類時寫上類的默認構造函數。
如果在聲明Person對象時,必須指定姓名。可以去掉默認構造函數。並寫一個有參數的參數函數代替之。
如:
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
創建Person類對象。因爲沒有了默認構造函數,聲明類對象就必須這樣:
Person p1 = new Person(“zhaoyubetter”);
1.6‘this’引用句柄
this 指當前對象(創建的對象即new出來的對象)
1. this 引用變量,指向調用這個方法對象(指對象自己);
如:
class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
1. 通過this引用把當前的對象作爲一個參數傳遞給其他的方法;
假設有一個容器類和一個部件類,在容器類的某個方法要創建部件類的實例對象,而部件類的構造方法要接收一個代表其所在容器的參數。
如:
class Container {
Componet comp;
public void addComponet() {
comp = new Componet(this); // 將this作爲對象引用傳遞
}
}
class Componet {
Container myContainer;
public Componet(Container c) {
this.myContainer = c;
}
}
想象的內存圖:(看代碼,想象內存的變量。要活學)
1. 構造方法是在產生對象時被Java系統自動調用的,我們不能在程序中像其他調用其他方法一樣去調用構造方法。但是我們可以通過this在一個構造方法裏調用執行其他重載的構造方法。
如:
public class Person {
String name;
int age;
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this(name); // 通過this,執行第一個構造方法中的代碼
this.age = age;
}
}
構造器,調用構造器用this
1.7 finalize() 方法
finalize() 方法是Object類的一個方法,任何一個類都從Object那繼承了這個方法,finalize()方法是在對象被當成垃圾從內存中釋放前調用,而不是在對象變成垃圾前調用,垃圾回收器的啓用不由程序員控制,並無規律可循,並不是一產生垃圾,它就調用,不是很可靠的機制,即我們無法保證每個對象的finalize()最終被調用了。我們只需瞭解finalize()方法的作用即可
如:
class Person {
public void finalize() {
System.out.println(“the object is going.”);
}
public static void main(String[] args) {
new Person();
new Person();
new Person();
System.out.println(“the program is ending!”);
}
}
程序運行結果爲:
the program is ending!
我們並沒有看到垃圾回收器時finalize方法被調用。
1.7 System.gc()方法的作用
由於finalize()方法特點:如果程序某段運行時產生了大量垃圾,而finalize()方法也不回來被調用,這裏我們就需要強制自動垃圾回收器來清理工作了,類似與垃圾桶滿了,沒有人來清理,我們可以主動打電話通知人來清理。
修改後代碼:
class Person {
public void finalize() {
System.out.println(“the object is going.”);
}
public static void main(String[] args) {
new Person();
new Person();
new Person();
System.gc(); // 強制調用清理
System.out.println(“the program is ending!”);
}
}
程序運行結果爲:
the object is going.
the object is going.
the object is going
the program is ending!
1.8 函數的參數傳遞
1.8.1 基本數據類型的參數傳遞
方法的形式參數就相當於方法中的局部變量,方法調用結束時被釋放,並不會影響到主程序中同名的局部變量。
程序代碼:
class PassValue {
public static void main(String[] args) {
int x = 5;
change(x);
System.out.println(x);
}
public static void change(int x) {
x = 3;
}
}
內存分析:
結論:
基本數據類型的變量作爲實參傳遞,並不能改變這個變量的值。
String類似
1.8.2 引用數據類型的參數傳遞
對象的引用變量並不是對象本身,它們只是對象的句柄(名稱),類似於一個人可以有個名稱一樣(如:英文名,中文名),一個對象可以有多個句柄(見:基礎部分)
程序代碼:
class PassRef {
int x;
public static void main(String[] args) {
PassRef obj = new PassRef();
obj.x = 5;
change(obj);
System.out.println(obj.x);
}
public static change(PassRef obj) { // 傳遞的爲對象的引用 obj
System.out.println(obj.x); // 這裏打印爲5.
obj.x = 3;
}
}
內存分析:
結論:
Java語言在被給調用方法的參數賦值時,只採用傳值的方式。所以,基本數據類型傳遞的是該數據的值本身,而引用數據類型傳遞的也是這個變量的值本身,即對象的引用(句柄),而非對象本身,但通過方法調用改變了對象的內容,但是對象的引用是不能改變的。
所以最終值發生了變量。數組 也屬於引用數據類型。
比如:有人叫張三,他的小名叫‘小狗子’,說‘小狗子’怎麼的,就是講張三怎麼的。
將代碼轉變一下:
class PassRef {
int x;
public static void main(String[] args) {
PassRef obj = new PassRef();
obj.x = 5;
change(obj);
System.out.println(obj.x);
}
public static change(PassRef obj) { // 傳遞的爲對象的引用 obj
obj = new PassRef(); // 重新創建對象
//obj=2;
obj.x = 3;
}
}
內存自己分析下:
change() 方法執行了,但是沒有起作用。這樣的代碼沒有任何意義。
1.9 static關鍵字
1.9.1 靜態變量
當我們編寫一個類時,其實就是在描述其對象的屬性和行爲,並沒有產生實質上的對象,只有通過new關鍵字纔會產生出對象,這時系統纔會分配內存空間給對象。
有時候我們希望無論是否產生了對象,某些特定的數據在內存空間裏只有一份。類似於每個中國人的國家名稱的變量,每個中國人都共享它。在該前面加上static關鍵字,在內存中只存在一份,這就是靜態成員變量。無需創建類的對象,直接用類名就可以引用。
注意:我們不能把任何方法體內的變量聲明爲靜態
如:
fun {
static int i = 0;
}
用static標識符修飾的變量,它們在類載入時創建,只要內存在,static變量就存在。
即static只能是類的成員變量
1.9.2 靜態方法
類似於靜態成員,我們也可以創建靜態方法,即不必創建對象就可以調用的方法。換句話說就是使該方法不必和對象綁在一起。呵呵。加static即可。
程序代碼:
class Chinese {
static void sing() {
System.out.println("static 方法");
singOurCountry(); // 錯誤,無法從靜態方法訪問非靜態方法。(原因:靜態)
}
void singOurCountry() {
sing(); // 類中成員方法直接訪問靜態成員方法
}
}
public class TestChinese {
public static void main(String[] args) {
Chinese.sing();
Chinese ch1 = new Chinese();
ch1.sing();
ch1.singOurCountry();
}
}
類的靜態成員稱爲‘類成員’(class members),
類的靜態成員變量稱爲‘類屬性’(class attributes),
類的靜態成員方法稱爲‘類方法’(class methods)
採用static關鍵字聲明的屬性和方法不屬於類的某個實例對象。
使用類的靜態方法時,注意:(其都是static原理)
1. 在靜態方法裏只能直接調用其他的靜態成員(包括變量和方法),而不能直接訪問類中的非靜態成員
2. 靜態方法不能爲任何方式引用this和super關鍵字,this和super的前提是創建類的對象。
3. main()方法是靜態的。因此JVM在執行main方式時。不創建main方法所在類的實例對象。在main()中,不能直接訪問該類的非靜態成員,必須先創建類的對象。
瞭解原理:什麼都簡單了 易經
2.1 靜態代碼塊
一個類中可以使用不包含在任何方法中的靜態代碼塊(static block),當類被載入時,先執行靜態代碼塊,且只被執行一次。靜態塊經常用來進行類屬性的初始化。
如:
class StaticCode {
static String country; // 類屬性(靜態成員變量)
static {
country = “China”;
System.out.println(“StaticCode is loading”);
}
}
public class TestStaticCode {
static {
System.out.println(“TestStaticCode is loading!!!”);
}
public static void main(String[] args) {
System.out.println(“begin executing main method”);
new StaticCode();
new StaticCode();
}
}
結果爲:
TestStaticCode is loading!!!
begin executing main method
StaticCode is loading (靜態塊只執行一次)
當一個程序中用到了其他的類,纔會去轉載那個類,並不是程序在啓動時就裝載所有的可能要用到的類。
2.1.1 單態設計模式
設計模式是自愛大量的實踐中總結和理論化之後的優選代碼結構、編程風格以及解決問題的思考方式。設計模式就像是經典的棋譜。易
程序代碼:
public class TestSingle {
private static final TestSingle onlyone = new TestSingle(); // static final 靜態常量
public static TestSingle getTestSingle() { // 供外界訪問的方法
return onlyone;
}
private TestSingle() {} // 構造方法私有,防止外界創建類對象
}
我們只能夠通過getTestSingle()獲得TestSingle的對象。這就是單態。
2.1.2 理解main方法的語法
由於Java虛擬機需要調用類的main()方法,所以該方法的訪問權限必須是public 的,又因爲Java虛擬機在執行main()方法必須創建對象,所有該方法必須是static的,該方法接收一個String類型的數組參數,該數組中保存執行Java命令時傳遞給所運行的類的參數。
程序代碼:
public class TestMain {
public static void main(String[] args) {
for(int i=0; i<args.length; i++) {
System.out.println(args[i]);
}
}
}
在命令行下輸入 java TestMain first second
結果如下:
first
second
3.1 內部類
在一個類內部定義類,這就是嵌套類(nested classes),也叫內部類、內置類。
內部類可以直接訪問嵌套它的類的成員,包括private成員,但是嵌套類的成員卻不能被嵌套它的類直接訪問。
特殊的匿名內部類(見面向對象下部分)
3.1.1 類中定義內部類
內部類的使用範圍,僅限於這個類的內部,內部類可以聲明成private或protected。
程序代碼:
class Outer {
int outor_i = 100;
void test() {
Inner in = new Inner();
in.display();
}
class Inner {
void display() {
System.out.println(“display: out_i = ” + outer_i); // 直接訪問外圍類成員
}
}
}
public class InnerClassDemo {
public static void main(String[] args) {
Outer outer = new Outer();
outer.test();
}
}
內部類應用場合:
當一個類中的程序代碼要用到另外一個類的實例對象,而另外一個類中的程序代碼又要訪問第一個類中的成員,將另外一個類做成第一個類的內部類。程序編碼就容易的多,實際應用很廣。
類似的內部類也可以聲明爲static類型的。呵呵。。根據static的特性。自己想吧。
如果內部類的成員變量與外部類的成員變量重名我們應該這樣做:
如:
public class Outer {
private int size;
public class Inner {
private int size;
public void doStuff(int size) {
size++; // 引用的是doStuff函數的形參
this.size++; // 引用的Inner類的成員變量
Outer.this.size++; // 引用的Outer類中的成員變量
}
}
}
3.1.2內部類如何被外部類引用
內部類可以通過創建對象從外部類之外訪問,只要將內部類聲明爲public即可
程序代碼:
class Outer {
private int size = 10;
public class Inner {
public void doStuff() {
System.out.println(++size);
}
}
}
public class TestInner {
public static void main(String[] args) {
Outer outer = new Outer(); // 先創建外圍類對象
Outer.Inner inner = new out.new Inner(); // 創建內部類對象
inner.doStuff();
}
}
3.1.3方法中定義的內部類
嵌套類也可以定義在方法中。也可以在程序塊的範圍之內定義內部類。甚至是for循環內。
程序代碼:
class Outer {
int outer_i = 10;
void test() {
for(int i=0; i<5; i++) {
class Inner {
void display() {
System.out.println(outer_i);
}
}
Inner inner = new Inner();
inner.display();
}
}
}
在方法中定義的內部類只能訪問方法中的final類型的局部變量,final定義的局部變量是個常量,它的生命週期超出方法運行的生命週期。
如:
class InOut {
String str = new String(“between”);
public void method(final int iArgs) {
int it315 = 3;
class Bicycle {
public void say() {
System.out.println(str);
System.out.pritnln(iArg);
System.out.println(it315); //不能訪問!
}
}
}
}
4.1 使用Java的文檔註釋
文檔註釋包含在‘/** … */’之間。開發者可以利用javadoc 工具將這些信息取出,然後轉化成HTML說明文檔。Java API 就是由javadoc生成的。
程序代碼:
import java.io.*;
/**
* Title: engineer類<br>
* Description:通過enginner類來說用Java中的文檔註釋<br>
* Copyright:(c) 2009 www.ncsmsoft.com<br>
* Company:ncsmsfot<br>
* @author:zhaoyubetter
* @version:1.0
*/
public class Engineer {
public String engineer_name;
/**
* 構造函數
* @param name engineer的名稱
*/
public Engineer(String name) {
}
/**
* repairing 方法的說明
* @param sum 修理機器總數
* @param alltime 修理的總時間
* @return Repair的數量
*/
public Engineer(int sum,int alltime) {
}
}
在命令行模式下輸入:javadoc –d engineer –version –author Engineer.java
-d 表示文件存放的目錄
engineer 文件所存放的目錄名
-version 要求javadoc程序在說明文件中加入版本信息