「模擬器跑得好,實機卻當機」是 J2ME 開發者的共同痛點。本文彙整 11 類最常見的模擬器與實機行為差異,涵蓋字型、字串比較、整數運算、影像處理、執行緒暫停、隨機數、資料型別溢位、浮點支援、方法大小限制、運算子優先權與物件陣列初始化,附程式碼對照與解決方案。
S60 裝置:每次 drawString 前必須重新設定字型與顏色,否則沿用上一次狀態導致顯示異常。
Font smallFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
g.setFont(smallFont);
g.setColor(0xFFFFFF);
g.drawString("文字", 0, 0, Graphics.TOP | Graphics.HCENTER);
QB/其他平台:字型設定會保留,僅需設定一次。
== vs equals()模擬器支援 str.intern() == "字串",實機必須使用 str.equals("字串")。切勿依賴字串常駐池行為。
// 錯誤:9/10 先運算得 0
money *= 9/10; // 結果永遠 0
// 正確:先乘後除
money = money * 9 / 10;
Image src = Image.createImage("/pics/state.png");
Image[] frames = new Image[4];
for (int i=0; i<4; i++) {
// 模擬器 DirectUtils.createImage 可能失敗,實機正常
frames[i] = Image.createImage(src, -i*frameW, 0, frameW, src.getHeight(), Sprite.TRANS_NONE);
}
建議統一使用標準 Image.createImage(Image, x, y, w, h, transform) 避免平台差異。
v60/k700 等機型在 if (isStop) continue; 空迴圈會死機,改用 if (isStop) { try { Thread.sleep(50); } catch(Exception e){} continue; } 釋放 CPU。
S40 會拋出例外導致程式終止,S60/K700/V300 僅卡頓並回傳 0。防禦性程式設計:if (divisor != 0) result = rand() % divisor;
int a=1_000_000_000, b=1_000_000_000; int c=a*b; 編譯通過但溢位截斷。byte c = (byte)(a+b); 必須明確轉型,否則編譯錯誤。Math.sin 在 WTK、Nokia 6230i、SE K700 可用,但 S60 早期機型(3230、6670)不支援。建議:採用定點數或查表法實作三角函數。
大型地圖陣列易超過限制。解決方案:將資料外置為 .gl 二進位資源檔,執行期讀取解析。
// J2SE 產生資源檔
DataOutputStream out = new DataOutputStream(new FileOutputStream("map1.gl"));
out.writeByte(mapId); out.writeByte(tileId); out.writeByte(rows); out.writeByte(cols); out.writeByte(collision);
for (byte[] row : mapData) for (byte v : row) out.writeByte(v);
// J2ME 讀取
byte[] data = readFile("/res/map"+mapNum+".gl");
byte[][] map = parseMapData(data);
String s = "123" + str == null ? "null" : str; // 等同 ("123"+str)==null ? ...
// 正確加括號
String s = "123" + (str == null ? "null" : str);
JTextField[] fields = new JTextField[10]; // 僅配置參考陣列,元素皆為 null
for (int i=0; i<10; i++) fields[i] = new JTextField(); // 必須逐一 new
equals()Thread.sleep實機測試不可替代,建議建立「最小驗證機型清單」覆蓋主流平台(S40、S60、UIQ、SE JP、Moto EZX 等)。