Java作為一種面向對象語言。支持以下基本概念:
- 多態
- 繼承
- 封裝
- 抽象
- 類
- 對象
- 實例
- 方法
- 重載
這篇文章,我們主要來看下:
- 對象:對象是類的一個實例(對象不是找個女朋友),有狀態和行為。例如,一條狗是一個對象,它的狀態有:顏色、名字、品種;行為有:搖尾巴、叫、吃等。
- 類:類是一個模板,它描述一類對象的行為和狀態。
下圖中男孩女孩為類,而具體的每個人為該類的對象:
現在讓我們深入了解什么是對象。看看周圍真實的世界,會發現身邊有很多對象,車,狗,人等等。所有這些對象都有自己的狀態和行為。拿一條狗來舉例,它的狀態有:名字、品種、顏色,行為有:叫、搖尾巴和跑。對比現實對象和軟件對象,它們之間十分相似。軟件對象也有狀態和行為。軟件對象的狀態就是屬性,行為通過方法體現。在軟件開發中,方法操作對象內部狀態的改變,對象的相互調用也是通過方法來完成。
接下來,我們來看下類。類可以看成是創建Java對象的模板,通過下面一個簡單的類來理解下Java中類的定義:
public class Dog{ String breed; int age; String color; void barking(){ } void hungry(){ } void sleeping(){ } }
一個類可以包含以下類型變量:
- 局部變量:在方法、構造方法或者語句塊中定義的變量被稱為局部變量。變量聲明和初始化都是在方法中,方法結束后,變量就會自動銷毀。
- 成員變量:成員變量是定義在類中,方法體之外的變量。這種變量在創建對象的時候實例化。成員變量可以被類中方法、構造方法和特定類的語句塊訪問。
- 類變量:類變量也聲明在類中,方法體之外,但必須聲明為static類型。
一個類可以擁有多個方法,在上面的例子中:barking()、hungry()和sleeping()都是Dog類的方法。然后我們來看下構造方法。每個類都有構造方法。如果沒有顯式地為類定義構造方法,Java編譯器將會為該類提供一個默認構造方法。在創建一個對象的時候,至少要調用一個構造方法。構造方法的名稱必須與類同名,一個類可以有多個構造方法。下面是一個構造方法示例:
public class Puppy{
public Puppy(){
}
public Puppy(String name){
// 這個構造器僅有一個參數:name
}
}
然后我們來看下對象創建。對象是根據類創建的。在Java中,使用關鍵字new來創建一個新的對象。創建對象需要以下三步:
- 聲明:聲明一個對象,包括對象名稱和對象類型。
- 實例化:使用關鍵字new來創建一個對象。
- 初始化:使用new創建對象時,會調用構造方法初始化對象。
下面是一個創建對象的例子:
public class Puppy{
public Puppy(String name){
//這個構造器僅有一個參數:name
System.out.println("小狗的名字是 : " + name );
}
public static void main(String []args){
// 下面的語句將創建一個Puppy對象
Puppy myPuppy = new Puppy( "tommy" );
}
}
編譯並運行上面的程序,會打印出下面的結果:
小狗的名字是 : tommy
然后我們來通過已創建的對象來訪問成員變量和成員方法,如下所示:
/* 實例化對象 */
ObjectReference = new Constructor(); /* 訪問類中的變量 */ ObjectReference.variableName; /* 訪問類中的方法 */ ObjectReference.MethodName();
下面的例子展示如何訪問實例變量和調用成員方法:
public class Puppy{
int puppyAge;
public Puppy(String name){
// 這個構造器僅有一個參數:name
System.out.println("小狗的名字是 : " + name );
}
public void setAge( int age ){
puppyAge = age;
}
public int getAge( ){
System.out.println("小狗的年齡為 : " + puppyAge );
return puppyAge;
}
public static void main(String []args){
/* 創建對象 */
Puppy myPuppy = new Puppy( "tommy" );
/* 通過方法來設定age */
myPuppy.setAge( 2 );
/* 調用另一個方法獲取age */
myPuppy.getAge( );
/*你也可以像下面這樣訪問成員變量 */
System.out.println("變量值 : " + myPuppy.puppyAge );
}
}
編譯並運行上面的程序,產生如下結果:
小狗的名字是 : 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。該類顯式聲明了一個構造方法,該方法只有一個參數。
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文件中:
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();
}
}
編譯這兩個文件並且運行 EmployeeTest 類,可以看到如下結果:
$ javac EmployeeTest.java $ java EmployeeTest 名字:RUNOOB1 年齡:26 職位:高級程序員 薪水:1000.0 名字:RUNOOB2 年齡:21 職位:菜鳥程序員 薪水:500.0
我們要知道java因強制要求類名(唯一的public類)和文件名統一,因此在引用其它類時無需顯式聲明。在編譯時,編譯器會根據類名去尋找同名文件。
package 的作用就是 c++ 的 namespace 的作用,防止名字相同的類產生沖突。Java 編譯器在編譯時,直接根據 package 指定的信息直接將生成的 class 文件生成到對應目錄下。如 package aaa.bbb.ccc 編譯器就將該 .java 文件下的各個類生成到 ./aaa/bbb/ccc/ 這個目錄。
import 是為了簡化使用 package 之后的實例化的代碼。假設 ./aaa/bbb/ccc/ 下的 A 類,假如沒有 import,實例化A類為:new aaa.bbb.ccc.A(),使用 import aaa.bbb.ccc.A 后,就可以直接使用 new A() 了,也就是編譯器匹配並擴展了 aaa.bbb.ccc. 這串字符串。
了解了上面的東西,我們就來看下,為什么java文件中只能含有一個public類?java 程序是從一個 public 類的 main 函數開始執行的,(其實是main線程),就像 C 程序 是從 main() 函數開始執行一樣。 只能有一個 public 類是為了給類裝載器提供方便。 一個 public 類只能定義在以它的類名為文件名的文件中。每個編譯單元(文件)都只有一個 public 類。因為每個編譯單元都只能有一個公共接口,用 public 類來表現。該接口可以按照要求包含眾多的支持包訪問權限的類。如果有一個以上的 public 類,編譯器就會報錯。 並且 public類的名稱必須與文件名相同(嚴格區分大小寫)。 當然一個編譯單元內也可以沒有 public 類。
然后我們再來看下,java中成員變量與類變量的區別。首先,由static修飾的變量稱為靜態變量,其實質上就是一個全局變量。如果某個內容是被所有對象所共享,那么該內容就應該用靜態修飾;沒有被靜態修飾的內容,其實是屬於對象的特殊描述。不同的對象的實例變量將被分配不同的內存空間, 如果類中的成員變量有類變量,那么所有對象的這個類變量都分配給相同的一處內存,改變其中一個對象的這個類變量會影響其他對象的這個類變量,也就是說對象共享類變量。
1、兩個變量的生命周期不同
成員變量隨着對象的創建而存在,隨着對象的回收而釋放。
靜態變量隨着類的加載而存在,隨着類的消失而消失。
2、調用方式不同
成員變量只能被對象調用。
靜態變量可以被對象調用,還可以被類名調用。
3、別名不同
成員變量也稱為實例變量。
靜態變量也稱為類變量。
4、數據存儲位置不同
成員變量存儲在堆內存的對象中,所以也叫對象的特有數據。
靜態變量數據存儲在方法區(共享數據區)的靜態區,所以也叫對象的共享數據。
static 關鍵字,是一個修飾符,用於修飾成員(成員變量和成員函數)。來看下特點:
1、想要實現對象中的共性數據的對象共享。可以將這個數據進行靜態修飾。
2、被靜態修飾的成員,可以直接被類名所調用。也就是說,靜態的成員多了一種調用方式。類名.靜態方式。
3、靜態隨着類的加載而加載。而且優先於對象存在。
再來看下弊端:
1、有些數據是對象特有的數據,是不可以被靜態修飾的。因為那樣的話,特有數據會變成對象的共享數據。這樣對事物的描述就出了問題。所以,在定義靜態時,必須要明確,這個數據是否是被對象所共享的。
2、靜態方法只能訪問靜態成員,不可以訪問非靜態成員。
因為靜態方法加載時,優先於對象存在,所以沒有辦法訪問對象中的成員。
3、靜態方法中不能使用this,super關鍵字。
因為this代表對象,而靜態在時,有可能沒有對象,所以this無法使用。
什么時候定義靜態成員呢?或者說:定義成員時,到底需不需要被靜態修飾呢?
成員分兩種:
1、成員變量。(數據共享時靜態化)
該成員變量的數據是否是所有對象都一樣:
如果是,那么該變量需要被靜態修飾,因為是共享的數據。
如果不是,那么就說這是對象的特有數據,要存儲到對象中。
2、成員函數。(方法中沒有調用特有數據時就定義成靜態)
如果判斷成員函數是否需要被靜態修飾呢?
只要參考,該函數內是否訪問了對象中的特有數據:
如果有訪問特有數據,那方法不能被靜態修飾。
如果沒有訪問過特有數據,那么這個方法需要被靜態修飾。
然后就來看下成員變量和靜態變量的區別:
1、成員變量所屬於對象。所以也稱為實例變量。
靜態變量所屬於類。所以也稱為類變量。
2、成員變量存在於堆內存中。
靜態變量存在於方法區中。
3、成員變量隨着對象創建而存在。隨着對象被回收而消失。
靜態變量隨着類的加載而存在。隨着類的消失而消失。
4、成員變量只能被對象所調用 。
靜態變量可以被對象調用,也可以被類名調用。
所以,成員變量可以稱為對象的特有數據,靜態變量稱為對象的共享數據。
然后看類變量類型。1.局部變量:在方法、構造方法、語句塊中定義的變量。其聲明和初始化在方法中實現,在方法結束后自動銷毀:
public class ClassName{ public void printNumber(){ int a; } // 其他代碼 }
2.成員變量:定義在類中,方法體之外。變量在創建對象時實例化。成員變量可被類中的方法、構造方法以及特定類的語句塊訪問:
public class ClassName{ int a; public void printNumber(){ // 其他代碼 } }
3.類變量:定義在類中,方法體之外,但必須要有 static 來聲明變量類型。靜態成員屬於整個類,可通過對象名或類名來調用:
public class ClassName{ static int a; public void printNumber(){ // 其他代碼 } }
再來看下類的構造方法:
1、構造方法的名字和類名相同,並且沒有返回值。
2、構造方法主要用於為類的對象定義初始化狀態。
3、我們不能直接調用構造方法,必須通過new關鍵字來自動調用,從而創建類的實例。
4、Java的類都要求有構造方法,如果沒有定義構造方法,Java編譯器會為我們提供一個缺省的構造方法,也就是不帶參數的構造方法。
完事看下new關鍵字的作用:
1、為對象分配內存空間。
2、引起對象構造方法的調用。
3、為對象返回一個引用。
有時候,你想擁有所有對象通用的變量。 這是通過靜態修改器完成的。 在其聲明中具有靜態修飾符的字段稱為靜態字段或類變量。 他們與類相關,而不是與任何對象相關聯。這就是是 Oracle 對於靜態的定義。
然后我們來使用java類實例化一個對象的時候,如果在類中不顯式的聲明其構造函數,則會使用一個默認的構造函數來初始化對象:
//一個沒有顯式聲明構造函數的類 Public class People{ int age = 23; Public void getAge(){ System.out.print("the age is "+age); } } //用這個類來實例化一個對象 People xiaoMing = new People(); // People() 是People類的默認構造函數,它什么也不干 xiaoMing.getAge();//打印年齡
我們也可以在聲明類的時候顯式的聲明一個構造函數:
//一個帶顯式構造函數的類 Public class People{ int age = 23; Public void getAge(){ System.out.print("the age is "+ age); } // 顯式聲明一個帶參數的構造函數,用於初始化年齡 Public People(int a){ this.age = a; } } //用這個類來實例化一個對象 People xiaoMing = new People(20); // 使用帶參數的構造函數來實例化對象 xiaoMing.getAge(); // 打印出來的年齡變為20
再來看下成員變量與局部變量的區別:
1.聲明位置不同
成員變量也就是屬性,在類中聲明的。
局部變量,在方法中聲明或代碼塊中聲明。
2.初始值不同
成員變量如果沒有賦值則是有默認值的,數據類型不同則默認值不同。
局部變量是沒有默認值,也就是說必須先聲明,再賦值,最后才使用。
3.在一個類中,局部變量可以與成員變量同名,但是局部變量優先,如果非要訪問成員變量的屬性,則必須使用 this.color
this 代表當前這個對象,也就是當前誰調用這個方法則這個對象就是誰。
還有對象與引用的區別。對象是具體的一個實例,如:new Student(); new 表示創建一個對象,並在堆內存中開辟一塊空間。引用名稱是存放的對象的地址。
內部類:將一個類的定義放在另一個類的定義內部,這就是內部類。如同一個人是由大腦、肢體、器官等身體結果組成,而內部類相當於其中的某個器官之一,例如心臟:它也有自己的屬性和行為(血液、跳動)。顯然,此處不能單方面用屬性或者方法表示一個心臟,而需要一個類,而心臟又在人體當中,正如同是內部類在外部內當中。然后看個不用內部類的例子:
public class Person { private int blood; private Heart heart; } public class Heart { private int blood; public void test() { System.out.println(blood); } }
看個使用內部類的例子:
public class Person { private int blood; public class Heart { public void test() { System.out.println(blood); } } public class Brain { public void test() { System.out.println(blood); } } }
看下內部類的優缺點:
- 優點:可訪問外部類私有屬性(心臟可訪問身體的血液,而不是外部抽血)。
- 缺點:破壞原有類的程序結構(屬性、構造方法、普通方法、內部類)。
然后看下應用中的實例:
//外部類 class Out { private int age = 12; //內部類 class In { public void print() { System.out.println(age); } } } public class Demo { public static void main(String[] args) { Out.In in = new Out().new In(); in.print(); //或者采用下種方式訪問 /* Out out = new Out(); Out.In in = out.new In(); in.print(); */ } }
運行結果是12。從上面的例子不難看出,內部類其實嚴重破壞了良好的代碼結構,但為什么還要使用內部類呢?因為內部類可以隨意使用外部類的成員變量(包括私有)而不用生成外部類的對象,這也是內部類的唯一優點。如同心臟可以直接訪問身體的血液,而不是通過醫生來抽血。程序編譯過后會產生兩個 .class 文件,分別是 Out.class 和 Out$In.class。其中 $ 代表了上面程序中 Out.In 中的那個。Out.In in = new Out().new In() 可以用來生成內部類的對象,這種方法存在兩個小知識點需要注意:
- 1.開頭的 Out 是為了標明需要生成的內部類對象在哪個外部類當中。
- 2.必須先有外部類的對象才能生成內部類的對象,因為內部類的作用就是為了訪問外部類中的成員變量。
如果對這部分內部類感興趣的小伙伴們可以看下 Java 內部類詳解 java 中的內部類總結
這次的分享就到這里了。有點繁瑣,但是很重要哦。如果感覺不錯的話,請多多點贊支持哦。。。
原文地址:https://blog.csdn.net/luyaran/article/details/80092771