軟件工程(2018)第三次個人作業
阿新 • • 發佈:2018-04-01
邏輯 靜態方法 equals sys bubuko 增加 body break 直接
軟件工程(2018)第三次個人作業
前方高能:本次作業中含有大量基礎知識,請不要嘲笑我QAQ
第三次作業來了。選擇看似相比有難度的(1)(其實是看不懂(2)在幹什麽)
題目要求:題目(1):最大連續子數組和(最大子段和)
背景
問題: 給定n個整數(可能為負數)組成的序列a[1],a[2],a[3],…,a[n],求該序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。當所給的整數均為負數時定義子段和為0,依此定義,所求的最優值為: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n 例如,當(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)時,最大子段和為20。
讀完題目,第一想到就是數組解決問題。定義數組a[]輸入變量後,遍歷數組,將數組每個元素分別開始向後逐步取和,將這些和存到數組b[]中,最後比較數組b[]中,取最大值即可。
看起來用到的方法都是數組的基本方法,第三次作業果然還是簡單(心裏長舒一口氣)
所以我的代碼構思是在某堂課上隨手完成的,大體思路算法有了後,在自己計算機上實現就行
但是實現又遇到了各種問題,一些在C上容易實現的方法,在Eclipse上還沒有試過
問題(1):Java中數組的定義(沒錯第一個問題就是這個)
解決:參考《瘋狂JAVA講義》。JAVA中數組可動態定義或靜態定義,前者定義後分配指定個數個內存,後者定義後直接給數組賦值,由系統來確定分配多少個空間。
int[] intArr;
intArr=new int[] {5,6,8,20};//靜態定義
int[] intArr2= {5,6,8,21};//簡化靜態定義方式
int[] intArr3;
intArr3=new int[4];//動態定義
int[] intArr4=new int[4];//簡化動態定義方式
與此同時,我想著把JAVA中所有關於數組的基本方法學習一下,數組的輸入輸出當然是最重要的
輸入:利用循環逐個輸入元素,循環變量控制條件是數組的length變量,此時須註意動態定義中會使length有可能大於我們程序運行時要輸入的元素個數,在此我有疑問,JAVA是否可以實現動態數組,我們先完成作業,然後再考慮這個
而JAVA中輸入方法為下
Scanner x = new Scanner(System.in);
int n1=x.nextInt();
輸出:利用循環遍歷數組元素,逐個輸出,輸出方法如下
System.out.println();//ln是換行
同時,了解到JAVA中有foreach循環遍歷數組,看起來能簡化代碼
for(int m:intArr)
System.out.println(m);//foreach用法,註意m相當於一個臨時變量,所以foreach方法無法完成數組的輸入。
由問題(1)引發的關於數組的基本用法先學到這裏,基本夠用
這樣程序代碼可以寫了,以下是我的程序代碼
import java.util.Scanner;
public class Sz
{
int n;//數組元素個數
static int[] a=new int[10] ;//輸入數組
static int[] b=new int[10];//以每個元素為首元素的組合集
public static void createArray(int n)//數組輸入
{
for(int i=0;i<n;i++)
{
Scanner x = new Scanner(System.in);
int n1=x.nextInt();
a[i]=n1;
}
}
public static int getMax(int i,int n)//生成數組第i個元素為首元素的組合最大的組合
{
int[] c=new int[10];//用於循環中求該元素為首元素的組合中最大的組合
c[i]=a[i];
for(int j=i;j<n-1;j++)
{
c[j+1]=c[j]+a[j+1];
}
for(int m:c)
{
if(b[i]<m)
b[i]=m;
}
return b[i];
}
public static int getbMax(int[] b)//將b[]中最大元素篩選出來
{
int max=0;
for(int m:b)
{
if(max<m)
max=m;
}
return max;
}
public static void main(String[] args)
{
Scanner x = new Scanner(System.in);
int n=x.nextInt();
createArray(n);
for(int i=0;i<n;i++)
{
getMax(i,n);
}
System.out.println(getbMax(b));
}
}
附上程序運行截圖
樣例1:數組(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)
樣例2:(a[1],a[2],a[3],a[4],a[5])=(-2,6,4,-7,-2)
註:以上代碼是我最終版本代碼,沒以下問題,我是先寫完程序再寫博客,所以要保證代碼都OK才會回憶我當時編程的心路過程(我覺得邊寫代碼邊寫博客真的破壞思路,當然我的夢想是能邊寫邊編而不影響思路,正如老師說過的直播寫代碼,我覺得OK)(我也有主播夢想)
寫代碼後調試修改了一些小錯誤,問題不大的,沒有寫出來。接下來碰到一個我不太理解的錯誤,雖然Eclipse強大的幫助修改能力教我將所以變量、方法加上static屬性,但是我還是要弄懂有啥區別
問題(2)JAVA中static的用法
老規矩,上網學
這次應該是問題明確又基礎,很容易找到回答
static表示“全局”或者“靜態”的意思,用來修飾成員變量和成員方法,也可以形成靜態static代碼塊,但是Java語言中沒有全局變量的概念。
被static修飾的成員變量和成員方法獨立於該類的任何對象。也就是說,它不依賴類特定的實例,被類的所有實例共享。
只要這個類被加載,Java虛擬機就能根據類名在運行時數據區的方法區內定找到他們。因此,static對象可以在它的任何對象創建之前訪問,無需引用任何對象。
用public修飾的static成員變量和成員方法本質是全局變量和全局方法,當聲明它類的對象市,不生成static變量的副本,而是類的所有實例共享同一個static變量。
static變量前可以有private修飾,表示這個變量可以在類的靜態代碼塊中,或者類的其他靜態成員方法中使用(當然也可以在非靜態成員方法中使用--廢話),但是不能在其他類中通過類名來直接引用,這一點很重要。實際上你需要搞明白,private是訪問權限限定,static表示不要實例化就可以使用,這樣就容易理解多了。static前面加上其它訪問權限關鍵字的效果也以此類推。
static修飾的成員變量和成員方法習慣上稱為靜態變量和靜態方法,可以直接通過類名來訪問,訪問語法為:
類名.靜態方法名(參數列表...)
類名.靜態變量名
用static修飾的代碼塊表示靜態代碼塊,當Java虛擬機(JVM)加載類時,就會執行該代碼塊(用處非常大,呵呵)。
1、static變量
按照是否靜態的對類成員變量進行分類可分兩種:一種是被static修飾的變量,叫靜態變量或類變量;另一種是沒有被static修飾的變量,叫實例變量。
兩者的區別是:
對於靜態變量在內存中只有一個拷貝(節省內存),JVM只為靜態分配一次內存,在加載類的過程中完成靜態變量的內存分配,可用類名直接訪問(方便),當然也可以通過對象來訪問(但是這是不推薦的)。
對於實例變量,沒創建一個實例,就會為實例變量分配一次內存,實例變量可以在內存中有多個拷貝,互不影響(靈活)。
所以一般在需要實現以下兩個功能時使用靜態變量:
? 在對象之間共享值時
? 方便訪問變量時
2、靜態方法
靜態方法可以直接通過類名調用,任何的實例也都可以調用,
因此靜態方法中不能用this和super關鍵字,不能直接訪問所屬類的實例變量和實例方法(就是不帶static的成員變量和成員成員方法),只能訪問所屬類的靜態成員變量和成員方法。
因為實例成員與特定的對象關聯!這個需要去理解,想明白其中的道理,不是記憶!!!
因為static方法獨立於任何實例,因此static方法必須被實現,而不能是抽象的abstract。
例如為了方便方法的調用,Java API中的Math類中所有的方法都是靜態的,而一般類內部的static方法也是方便其它類對該方法的調用。
靜態方法是類內部的一類特殊方法,只有在需要時才將對應的方法聲明成靜態的,一個類內部的方法一般都是非靜態的
以上內容摘自這篇博客(特意看了下歡迎轉載,具體示例也有)
個人總結:static修飾的變量和方法稱為靜態變量,可以在類中直接引用變量、直接調用方法
問題解決後,經過測試也都ok,突然想到,我用Eclipse調試不像c一樣熟練,而且我還沒掌握Eclipse調試的方法
問題(3)Eclipse中如何調試
網上學習。Eclipse支持設置斷點進行調試(否則程序直接運行到底)。右鍵代碼行頭數字設置breakpoint(或直接雙擊數字)(breakpoint更有詳細設置,支持條件斷點調試,有丶厲害啊這個)F5逐步調試,F6具體進入方法,F7跳出方法,CTRL+F2結束調試,嗯,我記住了
調試過程中,可將鼠標直接放在程序變量上顯示變量,更可以設置出Variables窗口查看其余變量(Window——show view——Other——debug——Variables),這樣跟C語言調試幾乎完美相同了,或許有更多功能待挖掘
接下來是測試
覆蓋方法
(1))語句覆蓋:選擇合適用例,所有語句被執行一次。
語句覆蓋是指選擇足夠的測試用例,使得運行這些測試用例時,被測程序的每一個語句至少執行一次,其覆蓋標準無法發現判定中邏輯運算的錯誤。
(2)判定覆蓋:每個判定至少取一次真、一次假。
判定覆蓋是設計足夠多的測試用例,使得程序中的每一個判斷至少獲得一次“真”和一次“假”,即使得程序流程圖中的每一個真假分支至少被執行一次。
(3)條件覆蓋:每個條件的各種可能結果至少滿足一次。
條件覆蓋是指選擇足夠的測試用例,使得運行這些測試用例時,判定中每個條件的所有可能結果至少出現一次,但未必能覆蓋全部分支。
(4)判定條件覆蓋:同時滿足判斷覆蓋和條件覆蓋。
判定條件覆蓋是設計足夠的測試用例,得使判斷中每個條件的所有可能取值至少執行一次,同時每個判斷本身所有可能結果也至少執行一次。缺點是忽略了條件的組合情況。
(5)條件組合覆蓋:所有組合情況都要覆蓋一次。
在白盒測試法中,選擇足夠的測試用例,使得每個判定中條件的各種可能組合都至少出現一次。顯然,滿足“條件組合覆蓋”的測試用例是一定滿足“判定覆蓋”、“條件覆蓋”和“判定/條件覆蓋”的。
選定的覆蓋方法(判定/條件覆蓋)我采用的是判定條件覆蓋:
(1)max>b[] max>0
(2)max>b[] max=0
(3)max<b[] max>0
(4)max<b[] max=0
選擇用例:
(1)測試用例:{-2,11,-4,9}
(2)測試用例:{-2,-3,-4,-5}
(3)測試用例:{1,2,3,4}
(4)測試用例:{-5,-4,-3,-1}
建立JUNIT測試單元,測試代碼如下
import static org.junit.Assert.*;
import org.junit.Test;
public class SzTest
{
int[] a=new int[] {-2,11,-4,9};
static int[] b=new int[] {14,16,5,9};
@Test
public void testGetMax()
{
assertEquals(14,new Sz().getMax(0,4));
assertEquals(16,new Sz().getMax(1,4));
assertEquals(5,new Sz().getMax(2,4));
assertEquals(9,new Sz().getMax(3,4));
}
@Test
public void testGetbMax()
{
assertEquals(16,new Sz().getbMax(b));
}
}
進行測試,出現問題(4)測試失敗
如圖
java.lang.AssertionError: expected:<14> but was:<0>
測試結果是0?不應該啊。於是設置斷點逐步調試,發現問題。由於Sz中a[]是靜態變量,在那個類中可以被程序直接引用修改。而在Sztest類的調用中,Sz的a[]的值一直為0。所以程序輸出為0。為了測試用,我將Sz.getMax中臨時定義a[]的值為{-2,11,-4,9}(僅測試用,不影響原程序),並且將Sztest中定義的a[]刪除(這的a[]沒有意義了),重新調試,OK。如圖。
接下來將剩余測試測完,直接列圖了
註:這裏的0是因為都是負數,所以默認全用0取代
註:這裏同理
單元測試完成!
後記:本次作業完成,問題也都圓滿解決,最後調試出現的問題是靠自己調試分析完成有點成就感。下次見~!
對了,動態數組還沒實現!
我覺得,如果能像建立鏈表一樣建立一個元素,然後每次輸入元素都連接上一個元素(鏈表靠地址連接,數組就靠建立數組時分配的連續內存這個順序連接),輸入結束後,數組也建立完成。通過思考和逐漸實踐,我覺得可行,但是總感覺自己理解不深導致沒有弄出來。最後在網上發現了跟我思路特別像的大佬實現了(侵刪)
// 定義一個初始長度為0的數組,用來緩存數據
private String[] src = new String[0];
// 增加
public void add(String s)
{
//定義新數組,長度是原數組長度+1
String[] dest = new String[src.length+1];
//將原數組的數據拷貝到新數組
System.arraycopy(src, 0, dest, 0, src.length);
//將新元素放到dest數組的末尾
dest[src.length]=s;
//將src指向dest
src=dest;
}
豁然開朗,突然想起來C語言中例如a[]中的"a"實際上是一個地址,可以指向別的地址,這樣我就可以將兩個數組的"數組頭"連接起來,模擬建立鏈表時光標的移動。不過我有疑問了:既然是指向,那原數組是不是應該及時刪除從而減少內存呢?刪除後才可以算做動態數組的完美建立。感覺又學到了丶東西。
本次作業也收獲頗豐呢(最終代碼)
軟件工程(2018)第三次個人作業