實驗二 面向對象(上)
實驗2-1 定義學生類
一、實驗描述
1、 考核知識點
名稱:類和對象
2、 練習目標
- 掌握類定義的方式
- 掌握如何在類中定義成員變量和成員方法
3、 需求分析
在面向對象的思想中最核心就是對象,在程序中創建對象的前提是需要定義一個類。本實驗設計一個表示學生的類,該類具有表示姓名的屬性name和表示年齡的屬性age,同時還具有表示說話行為的方法speak(),用於輸出學生的姓名和年齡。
4、 設計思路(實現原理)
1)使用class關鍵字定義一個表示學生類型的類,類名為Student。
2)在Student類中定義兩個成員變量name和age,分別用來表示姓名和年齡。其中,name的數據類型為String,變量age的數據類型為int。
3)在Student類中定義一個表示說話行為的speak()方法,用於輸出學生的姓名和年齡。
二、實驗實現
package cn.imust.Example201;
public class Example201 {
public static class Student{
static int age=20;
static String name="范敏";
static void speak(){
System.out.println("我是"+name+",今年"+age+"歲");
}
public static void main(String[] args)
{
speak();
}
}
}
運行結果為截圖2-0:
圖2-0
三、實驗總結
1、Java語言嚴格區分大小寫,class和Class是不同的,在定義類時只能使用class關鍵字
2、在Student類中,成員變量name是String類型,String表示一個字符串
實驗2-2 同一對象被多個變量引用
一、實驗描述
1、 考核知識點
名稱:對象創建與使用
2、 練習目標
- 掌握如何創建類的對象
- 掌握如何使用兩個或者多個變量引用同一個實例對象。
3、 需求分析
在程序中,一個對象可能在多處使用,這樣就可能需要有多個變量來引用這個對象。本實驗基於實驗2-1,創建三個學生對象,它們的引用變量分別是s1、s2和s3,首先分別使用s1和s2引用,為name和age賦值,然后調用speak()方法,最后將s2變量賦值給s3, s3也調用speak()方法。
4、 設計思路(實現原理)
1)編寫Example01類
2)在main()方法中,創建Student類的第一個對象,其引用變量為s1,使用s1調用name和age變量分別為它們賦值為“張三”和“19”,然后調用speak()方法。
3)創建Student類的第二個對象,其引用變量為s2,使用s2分別為name和age賦值為“李四”和“20”,然后調用speak()方法。
4)創建Student類的第三個對象,其引用變量為s3,將s2的值賦給s3,然后使用s3調用speak()方法。
二、實驗實現
package cn.imust.Example202;
public class Student {
static int age;
static String name;
static void speak(){
System.out.println(name+"今年"+age+"歲了");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s1=new Student();
Student s2=new Student();
Student s3=new Student();
s1.name="張三";s1.age=19;s1.speak();
s2.name="李四";s2.age=20;s2.speak();
s3=s2;s3.speak();
}
}運行結果截圖2-1:
圖2-1 運行結果
三、實驗總結
1、Student s3 = s2這句代碼的作用是將s2引用的內存地址賦值給s3,換句話說,就是使變量s3和s2引用了同一個Student對象,因此s3.speak()方法和s2.speak()方法打印的結果相同。為了更加深刻地了解這句代碼的含義,下面通過一張內存圖來演示,具體如圖2-2所示。
圖2-2 內存圖
2、可以使用兩個或者多個變量引用同一個實例對象,只要通過其中一個變量對該對象的屬性進行修改,使用其它引用變量訪問時,訪問的都是修改后的屬性。
實驗2-3 類的封裝
一、實驗描述
1、 考核知識點
名稱:類的封裝
2、 練習目標
- 了解為什么要對類進行封裝
- 了解如何實現類的封裝
3、 需求分析
在實驗2-2中,s1對象的年齡是可以隨便賦值的,如果將age的值賦值為-30,顯然違背了事實。為了解決這類問題,我們需要對類進行封裝,防止外界對類中的成員變量隨意訪問。本實驗使用private關鍵字對成員變量name和age進行私有化,同時分別提供一個setName(String n)和setAge(int a)方法用於外界的訪問,其中setAge(int a)中需要對age進行判斷。
4、 設計思路(實現原理)
1) 編寫測試類Example02,將屬性age的值設為-30,演示不合理現象。
2) 對Student類進行修改,將name和age屬性使用private修飾,然后定義getName()、setName(String n)、getAge()和setAge(int a)四個對外訪問name和age的方法。
3) 在setAge(int a)方法中對傳入的參數進行檢查,如果輸入值為負數,則打印出“設置的年齡不合法”,如果不為負數,才將其設置為age屬性的值。
4)對Example02類進行修改,在main()方法中創建Student類的實例對象,通過調用對象的setName(String n)和setAge(int a)方法來設置的name屬性和age屬性值,並調用speak()方法。
二、實驗實現
package cn.imust.Example203;
public class Student {
private static int age;
private static String name;
String getName(){
return name;
}
static void setName(String n){
name=n;
}
int getAge(){
return age;
}
static void setAge(int a){
/*if(a<0)
System.out.println("年齡不合理 !");
else*/ age=a;
}
static void speak(){
System.out.println("我的名字是"+name+",今年"+age+"歲了");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student.setAge(-30);
Student.setName("范敏");
Student.speak();
}
}
運行結果截圖為圖2-3:
圖2-3 運行結果
可以看出,當將age的值設置為-30后,程序不會報錯,但卻違背了現實。
2、對Student類進行封裝:
package cn.imust.Example203;
public class Student {
private static int age;
private static String name;
String getName(){
return name;
}
static void setName(String n){
name=n;
}
int getAge(){
return age;
}
static void setAge(int a){
if(a<0)
System.out.println("年齡不合理 !");
else age=a;
}
static void speak(){
System.out.println("我的名字是"+name+",今年"+age+"歲了");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student.setAge(-30);
Student.setName("范敏");
Student.speak();
}
}
運行結果截圖為圖2-4:
圖2-4 運行結果
三、實驗總結
1、Student的name和age屬性使用private關鍵字修飾為私有后,在Example02類中不能再使用s1.name和s1.age的方式訪問這兩個屬性,只能通過public類型的setName(String n)和setAge(int a)方法進行訪問。在上面的代碼中,調用setAge(int a)方法時的傳入參數為-30,由於參數小於0,會打印出“設置的年齡不合法”,並不會將負數賦值給age屬性。由此可見,只要實現了封裝就能對外界的訪問進行控制,避免對私有變量隨意修改而引發問題。
實驗2-4 定義有參的構造方法
一、實驗描述
1、 考核知識點
名稱:構造方法的定義
2、 練習目標
- 掌握有參構造方法的定義方式
- 理解系統會自動分配無參構造方法的情況
3、 需求分析
如果希望在創建對象的時候直接為其屬性賦值,可以定義有參的構造方法。有參構造方法指的是在初始化對象時,接受外部傳入的值並賦給對象的屬性。本實驗使用有參構造方法完成對象屬性的初始化。
4、 設計思路(實現原理)
1) 定義一個Student類,該類有一個age屬性,在類中定義一個有參數的構造方法,該參數用於為age屬性賦值。
2) 編寫一個測試類Example04,在main()方法中通過有參構造方法創建一個對象。
3) 打印該對象age屬性的值。
二、實驗實現
1、對Student類進行修改:
public class Student {
int age;
public Student(int Age){
age=Age;
}
public void speak(){
System.out.println("我今年"+age+"歲了。");
} }
2、定義Example04類:
package cn.imust.Example204;
public class Student {
int age;
public Student(int Age){
age=Age;
}
public void speak(){
System.out.println("我今年"+age+"歲了。");
}
Public class Example204{
public static void main(String[] args) {
// TODO Auto-generated method stub
Student stu=new Student(20);
stu.speak();
}
}
運行結果截圖為圖2-5:
圖2-5 運行結果
三、實驗總結
1、從運行結果可以看出,new Student(20)語句調用了有參的構造方法Student(int mAge),動態地將20傳遞給了age屬性。和普通方法一樣,構造方法中同樣可以接收多個參數,只要在使用new關鍵字創建對象時,傳入數量相同和類型一致的參數,就可以自動地調用對應的構造方法。
2、思考一下:在Example04的main()方法中是否能夠使用new Student()創建對象呢?答案是否定的,因為new Student()會調用無參的構造方法,而本實驗的Student類中並沒有定義無參的構造方法。
有些同學肯定會問,之前的Student類都沒有定義無參的構造方法,卻能使用new Student()創建對象,本實驗為什么不行呢?這是因為一個類中如果沒有定義構造方法,系統會默認為其分配一個方法體為空的無參構造方法,而一旦定義了構造方法,系統就不再提供默認的構造方法。本實驗中由於我們定義了一個有參的構造方法,所以系統不會默認分配無參的構造方法,此時如果通過new Student()去調用無參的構造方法,程序就會發生錯誤。
實驗2-5 構造方法的重載
一、實驗描述
1、 考核知識點
名稱:構造方法重載
2、 練習目標
- 掌握如何在類中定義重載的構造方法
3、 需求分析
和普通方法一樣,構造方法也可以重載。不同的構造方法,可以為不同的屬性進行賦值。本實驗通過調用不同構造方法創建對象,並根據構造方法的輸出結果對構造方法的重載進行學習。
4、 設計思路(實現原理)
1)對Student類進行修改,在類中定義三個重載的構造方法,包括無參的構造方法,接收一個String類型參數的構造方法,接收String類型和int類型兩個參數的構造方法。
2)編寫測試類Example05,在main()方法中,分別使用三個重載的構造方法創建三個Student對象。
二、實驗實現
1、對Student類進行修改:
public class Student {
public Student(){
System.out.println("無參函數被調用!");
}
public Student(String name){
System.out.println("一個參數函數被調用!");
System.out.println(name);
}
public Student(String name,int age)
{
System.out.println("倆個參數函數被調用!");
System.out.println(name+" "+age);
}
}
2、定義Example05類:
Public class Example205{
public static void main(String[] args) {
// TODO Auto-generated method stub
Student stu=new Student();
Student stu1=new Student("范敏");
Student stu2=new Student("范敏",20);
}
}
運行結果截圖為圖2-6:
圖2-6 運行結果
三、實驗總結
一個類中可以定義多個重載的構造方法,在創建對象時,根據傳入參數的不同會調用相應的構造方法。
實驗2-6 this關鍵字訪問構造方法
一、實驗描述
1、 考核知識點
名稱:this關鍵字的使用
2、 練習目標
- 掌握如何在構造方法中使用this關鍵字訪問重載的構造方法
3、 需求分析
如果一個類中定義了多個重載的構造方法,為了避免在重載的構造方法中重復書寫代碼,可以在一個構造方法中使用this關鍵字調用其它的構造方法。本實驗使用this關鍵字調用其他的構造方法。
4、 設計思路(實現原理)
1) 在Student類中創建多個重載的構造方法,包括無參的構造方法和一個參數的構造方法,以及兩個參數的構造方法。
2) 在一個參數的構造方法中使用this關鍵字調用無參構造方法,在兩個參數的構造方法中調用一個參數的構造方法。
3) 編寫測試類Example06,在main()方法中,調用兩個參數的構造方法創建對象,演示構造方法的執行順序。
二、實驗實現
1、對Student類進行修改:
class Student{
public Student(){
System.out.println("無參函數被調用!");
}
public Student(int age){
this();
System.out.println("一個參數函數被調用!");
}
public Student(String name,int age){
this(20);
System.out.println("倆個參數函數被調用!");
}
}2、定義Example06類:
public class Example206 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student stu=new Student("范敏",20);
}
}
運行結果截圖為圖2-7:
圖2-7 運行結果
三、實驗總結
1、從運行結果可以看出,三個構造方法都被調用了,為了更加清楚地了解三個構造方法的執行順序,下面通過一張圖例進行說明,如圖2-9所示。
圖2-8 構造方法的執行順序
2、在構造方法中,使用 this調用重載構造方法的代碼必須放在第一行,否則程序不能通過編譯,這就限定了在一個構造方法中只能調用一次重載的構造方法。
3、在構造方法中可以通過this.方法名([參數 …])的方式調用普通的成員方法,但是在普通的成員方法中不能使用this([參數 …])的方式來調用構造方法。
實驗2-7 垃圾回收機制
一、實驗描述
1、 考核知識點
名稱:垃圾回收機制
2、 練習目標
- 掌握垃圾回收機制的特點
- 掌握垃圾回收相關的方法
3、 需求分析
垃圾對象會占用一定的內存空間,當垃圾對象積累到一定程度后,Java虛擬機會自動進行垃圾回收。但是,如果希望程序可以及時通知Java虛擬機回收垃圾對象,可以通過System.gc()方法強制啟動垃圾回收器回收垃圾。本實驗通過System.gc()方法強制啟動垃圾回收器回收垃圾。
4、 設計思路(實現原理)
1) 對Student類進行修改,在類中對finalize()方法進行重寫。
2) 編寫測試類Example07,創建若干個Student對象,然后調用System.gc()方法通知垃圾回收期回收垃圾,為了確保可以看到垃圾回收的過程,可以在類中編寫一個簡單的循環語句,延長程序執行時間。
二、實驗實現
1、對Student類進行修改:
class Student{
public void finalize(){
System.out.println("對象將作為垃圾回收!");
}
}
2、定義Example07類,代碼如下所示:
public class Example207 {
/**
* @param args
*/
public static void main(String[] args) {
Student stu1=new Student();
Student stu2=new Student();
stu1=null;
stu2=null;
System.gc();
for(int i=0;i<1000000;i++){
}
}
}
運行結果截圖為圖2-9:
圖2-9 運行結果
三、實驗總結
1、從運行結果可以看到,兩個Student對象的finalize()方法都被調用了,這表示兩個對象作為垃圾被回收了。如果把System.gc()這行代碼注釋,會發現命令行窗口不會打印任何內容,這說明對象在變成垃圾后不會被立即回收,同時也驗證了System.gc()方法的作用。
2、由於System.gc()方法只是通知Java虛擬機盡快進行垃圾回收,這意味着垃圾回收器也可能不會馬上運行,因此,在程序的最后使用了一個for循環來延長程序運行的時間,從而確保能夠看到垃圾對象被回收的過程。
3、Student類中定義的 finalize()方法其簽名必須是public(protected) void finalize()[throw Throwable]{},這樣做的原因會涉及到后面的一些知識,比如類的繼承、Object類、方法的重寫、異常等等,同學們在學完這些內容后就會明白其中的道理。
實驗2-8靜態變量
一、實驗描述
1、 考核知識點
名稱:靜態變量
2、 練習目標
- 了解靜態變量的作用
- 掌握靜態變量的定義和使用方式
3、 需求分析
當多個對象存儲的數據相同時,可以使用靜態變量的方式存儲。例如,有一個Student類具有name、className屬性,請根據該類創建出三個Student對象,並將這些對象的className值都設置為“三年級二班”。
4、 設計思路(實現原理)
1) 定義Student類,並在類中定義name和className屬性。
2) 編寫測試類Example08,在main()方法中創建三個學生對象,並分別為這些對象的name和className屬性賦值,然后輸出這些對象的name和className值。
3) 對Student類進行修改,將className定義為靜態變量。
4) 修改測試類,在main()方法中使用Student.className = “三年級二班”語句為靜態變量className進行賦值,然后輸出這些對象的name和className值。
為了更好地理解Student類中靜態變量className和Student實例對象的關系,下面通過一個圖例進行演示,如圖2-10所示:
圖2-10 靜態變量與實例對象的關系
二、實驗實現
1、定義Student類:
class Student {
String name;
int className;
void speak(){
System.out.println(name+"是"+className+"班的");
}
}
2、定義Example08類:
public class Example208 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s1=new Student();
Student s2=new Student();
Student s3=new Student();
s1.name="范敏";s1.className=1;s1.speak();
s2.name="袁潤";s2.className=6;s2.speak();
s3.name="陳晨";s3.className=1;s3.speak();
}
}
運行結果截圖為圖2-11:
圖2-11 運行結果
3、對Student類進行修改,代碼如下所示:
class Student {
String name;
static String className;
void speak(){
System.out.println(name+"是"+className+"班的");
} 4、對Example08類進行修改,代碼如下所示:
public class Example208 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s1=new Student();
Student s2=new Student();
Student s3=new Student();
Student.className="三年級二班";
s1.name="范敏";s1.speak();
s2.name="袁潤";s2.speak();
s3.name="陳晨";s3.speak();
}
}
運行結果截圖為圖2-12:
圖2-12 運行結果
三、實驗總結
1、本實驗中,三個Student對象的className屬性值均為“三年級二班”,對於這樣的相同數據,沒有必要在每個對象中都開辟一塊空間存儲,完全可以在內存中只用一塊空間存儲,並被一個類的所有實例對象所共享。在Java中提供了一個static關鍵字,使用static關鍵字修飾的成員變量稱為靜態變量,靜態變量能被該類所有實例對象共享。
2、靜態變量可以使用“類名.靜態方法名”的方式訪問,也可以通過“對象引用變量.靜態方法名”的方式訪問,例如本例中的靜態變量className,通過Student.className或者s2.className這兩種方式訪問都是可以的,不過更推薦使用前一種方式。
實驗2-9 靜態方法中訪問類的成員
一、實驗描述
1、 考核知識點
名稱:靜態方法
2、 練習目標
- 了解在靜態方法中只能訪問類的靜態成員,而不能訪問非靜態成員。
3、 需求分析
在程序中經常會調用方法,但靜態方法之間、靜態方法和非靜態方法之間,它們是否能夠互相調用呢?請編寫一個測試類,在類中定義若干個靜態方法和非靜態方法,通過方法之間的相互調用,演示靜態方法和非靜態方法的調用情況。
4、 設計思路(實現原理)
1)編寫Example10類,在類中定義兩個靜態方法staticMethod1()、staticMethod2(),兩個非靜態方法nonStaticMethod1()、nonStaticMethod2()
2)在Example10類中,針對定義的四個方法進行互相調用,觀察調用情況。
二、實驗實現
1、定義Example10類,在類中定義上述的四個方法和一個main()方法:
package cn.imust.Example209;
public class Example209 {
static void Method1(){
}
static void Method2(){
int b=3;
}
void nostaticMethod1(){
int c=4;
}
void nostaticMethod2(){
int d=5;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
2、對靜態方法staticMethod1()進行修改,在方法中訪問靜態方法staticMethod2()。在main()方法中調用靜態方法staticMethod1():
package cn.imust.Example209;
public class Example209 {
static void Method1(){
Method2();
}
static void Method2(){
int a=10;
System.out.println(a);
}
void nostaticMethod1(){
int c=4;
System.out.println(c);
}
void nostaticMethod2(){
//static void Method2();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Method1();
}
}
運行結果截圖為圖2-13:
運行結果
從運行結果可以看到,程序正常執行,這說明在靜態方法中可以訪問靜態方法。
3、對靜態方法staticMethod1()進行修改,在方法中訪問非靜態方法nonStaticMethod1():
package cn.imust.Example209;
public class Example209 {
static void Method1(){
nostaticMethod1();
}
static void Method2(){
int a=10;
System.out.println(a);
}
void nostaticMethod1(){
int c=4;
System.out.println(c);
}
void nostaticMethod2(){
//static void Method2();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Method1();
}
}
運行結果截圖為圖2-14:
圖2-13 運行結果
從錯誤提示信息可以看到,發生錯誤的原因是在靜態方法中訪問了非靜態的方法。
4、在staticMethod1()方法中,將代碼“nonStaticMethod1()”注釋掉,並對非靜態方法nonStaticMethod1()進行修改,在方法中分別調用靜態方法statiMethod1()和非靜態方法nonStaticMethod2()。在main()方法中創建Example10的實例對象,調用nonStaticMethod1()方法:
package cn.imust.Example209;
public class Example209 {
static void Method1(){
System.out.println("靜態函數1被調用");
// nostaticMethod1();
}
static void Method2(){
System.out.println("靜態函數2被調用");
}
void nostaticMethod1(){
System.out.println("非靜態函數1被調用");
Method1();
nostaticMethod2();
}
void nostaticMethod2(){
//static void Method2();
System.out.println("非靜態函數2被調用");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Example209 e=new Example209();
e.nostaticMethod1();
}
}
運行結果截圖為圖2-15:
圖2-14 運行結果
從運行結果可以看到,程序正常執行,這說明在非靜態方法中既可以方法靜態方法,也可以訪問非靜態方法。
三、實驗總結
1、在靜態方法中只能訪問靜態方法,在非靜態方法中可以訪問靜態方法和非靜態方法。
2、思考一下:在靜態方法中是否能夠訪問靜態變量和非靜態變量?其實和上面的講解一樣,非靜態變量只能通過對象或者對象的引用變量訪問,而靜態方法在創建對象之前就可以通過類名直接訪問,因此在靜態方法中不能訪問非靜態變量,只能訪問靜態變量。
實驗2-10 代碼塊
一、實驗描述
1、 考核知識點
名稱:靜態代碼塊
2、 練習目標
- 理解代碼塊的不同分類
- 掌握不同代碼塊的作用及其執行時機
3、 需求分析
有時候,需要將某一段關聯緊密的或者實現了某一功能的代碼封裝的一個代碼塊中。為了讓初學者熟悉代碼塊的應用,本實驗將編寫一個包含了靜態代碼塊,局部代碼塊和構造代碼塊的類,演示不同代碼塊之間的執行時機。
4、 設計思路(實現原理)
1)編寫Example11類,在類中定義一個靜態代碼塊、一個構造代碼塊、一個無參的構造方法和一個成員方法localBlock(),在localBlock()方法中定義一個局部代碼塊。
2)創建Example11類的兩個實例對象,使用Example11類型的變量e1和e2引用,並通過變量e1和e2調用這兩個對象的localBlock()方法。
二、實驗實現
定義Example11類,代碼如下所示:
package cn.imust.Example10;
public class Example210 {
static{
System.out.println("靜態代碼塊被調用!");
}
public Example210(){
System.out.println("構造方法被調用!");
}
{
System.out.println("構造代碼塊被調用!");
}
public void localBlock(){
System.out.println("localBlock函數被調用!");
{
System.out.println("局部代碼塊被調用!");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Example210 q1=new Example210();
q1.localBlock();
Example210 q2=new Example210();
q2.localBlock();
}
}
運行結果截圖為圖2-16:
圖2-15 運行結果
三、實驗總結
1、靜態代碼塊在加載類的時候執行,由於類只在第一次使用時被加載,且只加載一次,因此靜態代碼塊只執行一次。從運行結果可以看到,雖然創建了兩個Example11的實例對象,由於Example11類只會加載一次,所以“靜態代碼塊”只打印一次。
在實際開發中,經常有一些代碼需要在類加載時就執行,比如加載數據庫驅動,這些代碼就應該放在靜態代碼塊中。
2、構造代碼塊在創建類的實例對象時執行,也就是說每次創建類的實例對象時,都會執行一次構造代碼塊。從運行結果可以看到,構造代碼塊優先於構造方法執行,因此在實際開發中,可以把重載構造方法中重復的代碼抽取到構造代碼塊中執行。
3、局部代碼塊定義在方法中,它在方法被調用的時候執行。使用局部代碼塊是為了限制變量的生命周期,使變量在使用完畢后被盡快回收,從而節省內存空間。
實驗2-11 單例設計模式
一、實驗描述
1、 考核知識點
名稱:單例設計模式
2、 練習目標
- 了解什么是單例設計模式
- 掌握單例設計模式的特點
3、 需求分析
在程序開發中,經常需要保證類的實例對象只有一個,這時,可以將類設計為單例設計模式。本實驗將編寫一個實現了單例設計模式的類。
4、 設計思路(實現原理)
1)定義一個類Singleton,為了保證該類只能創建一個實例對象,在類中定義一個私有的構造方法。
2)在類中創建一個該類的實例對象,並且定義一個靜態變量INSTANCE(變量名隨意)引用此實例對象。
3)為了防止外界使用Singleton.INSTANCE的方式直接訪問該實例對象,將INSTANCE變量使用private關鍵字修飾為私有,同時提供一個用於返回實例對象的靜態方法。
二、實驗實現
1、定義Singleton類,在類中定義一個私有,無參的構造方法:
2、創建Singleton的一個實例對象,定義一個私有的靜態變量INSTANCE引用這個對象:
3、定義一個靜態方法getInstance()將實例對象返回:
class Singleton{
private Singleton(){}
private static Singleton INSTANCE=new Singleton();{
}
public static Singleton getInstance(){
return INSTANCE;
}
}
4、定義Example12類,在類的main()方法中調用兩次getInstance()方法,獲得兩個Singleton的實例對象,使用“==”比較這兩個對象是否相等,代碼如下所示:
public class Example212 {
public static void main(String[] args) {
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
System.out.println(s1==s2);
}
}
運行結果截圖為圖2-17:
圖2-16 運行結果
從運行結果可以看到,兩個對象使用“==”比較的結果為 true,說明兩次獲得的是同一個Singleton的實例對象。
三、實驗總結
1、為了更加深刻的理解單例設計模式,下面對單例設計模式的特點進行歸納:
l 類中定義一個無參的構造方法,並且聲明為私有
l 在類的內部創建該類的一個實例對象,使用私有的靜態變量引用該實例對象
l 定義靜態方法返回該實例對象
2、本實驗中這種代碼格式的單例設計模式被形象的稱為餓漢式單例設計模式,這是因為在程序中,無論是否需要該類的實例對象,在類加載時都會創建一個實例對象。
實驗2-12 靜態內部類成員的定義和訪問
一、實驗描述
1、 考核知識點
名稱:靜態內部類
2、 練習目標
- 理解在靜態內部類中可以定義靜態成員和非靜態成員
- 掌握訪問靜態內部類中靜態成員和非靜態成員的方式
3、 需求分析
靜態內部類中可以定義靜態成員和非靜態成員,這兩者的訪問方式有所不同。本實驗將設計一個靜態內部類,並編寫測試類演示如何訪問靜態內部類中的靜態成員和非靜態成員。
4、 設計思路(實現原理)
1)定義外部類Outer,在Outer類中定義一個靜態內部類Inner。
2)在Inner中定義兩個String類型變量,一個靜態變量staticField,其值為“靜態內部類的靜態變量”,一個非靜態變量nonStaticField,其值為“靜態內部類的非靜態變量”。定義兩個方法,一個靜態方法staticMethod()打印“靜態內部類的靜態方法”,一個非靜態方法nonStaticMethod()打印“靜態內部類的非靜態方法”。
3)編寫Example13類,在類的main()方法中調用Inner類中的變量和方法。
二、實驗實現
1、定義外部類Outer和靜態內部類Inner,代碼如下所示:
class Outer{
static class Inner{
static String staticField="靜態內部類的靜態變量!";
String nostaticField="靜態內部類中的非靜態變量";
static void staticMethod(){
System.out.println("靜態內部類的靜態方法!");
}
void nostaticMethod(){
System.out.println("靜態內部類的非靜態方法!");
}
}
}
2、定義Example13類,在類的main()方法中,通過“外部類.內部類.靜態成員”的方式訪問靜態變量staticField和靜態方法staticMethod(),代碼如下所示:
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Outer.Inner.staticField);
Outer.Inner.staticMethod();
}
}
運行結果截圖為圖2-18:
圖2-17 運行結果
3、對類Example13進行修改,在main()方法中創建靜態內部類Inner的實例對象,使用Outer.Inner類型的變量inner引用,通過變量inner訪問非靜態變量nonStaticField和非靜態方法nonStaticMethod(),代碼如下所示:
public class Example212 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Outer.Inner n=new Outer.Inner();
//System.out.println(Outer.Inner.staticField);
System.out.println(n.nostaticField);
//Outer.Inner.staticMethod();
n.nostaticMethod();
}
}
運行結果截圖為圖2-19:
圖2-18 運行結果
三、實驗總結
靜態內部類中可以定義靜態成員和非靜態成員,如果要訪問非靜態成員,必須通過靜態內部類的對象或者對象的引用變量。如果要訪問靜態成員,則可以直接通過“外部類.靜態內部類.靜態成員”的方式,不需要創建靜態內部類的實例對象。
實驗2-13 方法內部類訪問局部變量
一、實驗描述
1、 考核知識點
名稱:方法內部類
2、 練習目標
- 掌握方法內部類中訪問的局部變量需要使用final關鍵字修飾
3、 需求分析
在方法內部類中可以訪問外部類的成員變量,同樣,在方法內部類中也可以訪問其所在方法中定義的局部變量,但是局部變量必須使用final關鍵字來修飾。本實驗將編寫一個方法內部類,演示如何在方法內部類中訪問局部變量。
4、 設計思路(實現原理)
1)編寫Example14類,在類中定義一個test()方法,test()方法中定義一個int類型的局部變量num,其值為5。
2)在test()方法中定義一個方法內部類Inner,在Inner類中定義一個show()方法,方法中訪問局部變量num。在test()方法中創建內部類Inner的實例對象,調用其show()方法。
3)在Example14的main()方法中,創建Example14的實例對象,使用變量e引用該對象,通過變量e調用test()方法。
二、實驗實現
1、定義Example14類,代碼如下所示:
public class Example14{
void test() {
int num = 5;
class Inner {
void show() {
System.out.println("局部變量num的值為" + num);
};
}
Inner inner = new Inner();
inner.show();
}
public static void main(String[] args) {
Example14 e = new Example14();
e.test();
}
}
運行結果如圖2-20所示:
圖2-19 錯誤提示信息
從錯誤提示信息可以看到,發生錯誤的原因是方法內部類中訪問的局部變量num沒有被聲明為最終類型,即沒有使用final關鍵字修飾。
對程序進行修改,將局部變量num使用 final關鍵字修飾,再次運行程序,運行結果如圖2-23所示。
圖2-20 運行結果
三、實驗總結
方法內部中訪問的局部變量必須使用final關鍵字修飾,否則程序在編譯時會發生錯誤。
