顯示具有 Java 標籤的文章。 顯示所有文章
顯示具有 Java 標籤的文章。 顯示所有文章

2021-10-16

《Thinking in Java》 的時代眼淚

最近接了一個 Java 家教,目前在上 OO 概念的部份。因為本來就不是靠唸書學會 OO 的(而且也沒學得多好),加上閒來無事,所以拿起塵封已久的《Thinking in Java》中文版(2002 初版)來看一下有沒有啥遺漏的。

等等… 「(p.252) 利用 imports 來改變行為」?這是什麼鬼?書上是這樣寫的:

Java 並不具備 C 的「條件編譯(conditional compilation)」功能。此功能讓你得以切換開關,令程式產生不同行為,無須更動程式碼。Java 拿掉這個功能的原因,可能是因為此功能在 C 語言中多半被用來解決跨平台問題,也就是根據不同的平台編譯程式碼中的不同部份。由於 Java 本身的設計可自動跨越不同平台,所以應該不需要此功能。

不過條件編譯仍具有其他實用價值。除錯便是常用的用途之一:在開發過程開啟除錯功能,在出貨產品中關閉除錯功能。Allen Holub(www.holub.com)提出了以 packages 來模擬條件編譯的想法。根據這一想法,他在 Java 裡頭產生原本在 C 語言中極為有用的 assertion 機制。

基本上就是寫兩個叫做 Assert 的 class、擺在不同 package 下,要出貨的時候把 Assert 的 import 改成空殼 Assert 的路徑。

真的是時代的眼淚阿… [遠目]

其實中文版出版的同一年,Java 1.4 也出了,其中一個重大的改進就是導入 assert。

這裡還有個不可靠的印象,就是當年有些人認為:「 assert 導入的太晚,現在用(誕生於 1997 年,正逐漸統治世界 XD) JUnit 就好了」

時至今日,JUnit 勉強算是有用一點,assert 則是從來沒用過,只有在追 GXT 的 source code 有看到過。至於「利用 imports 來改變行為」這事… 連我這種不碰 Spring 的咖小都知道應該要套個 DI 什麼的,而不是去動 import 這種鬼想法…

當然,現在回頭用吐槽的姿態看二十年前的概念實在沒什麼太大的意義,那時本來就還在盤古開天,連 design pattern 都還是浪頭上的顯學,殺個豬公也不是什麼太奇怪的事情,就只能說是時代的眼淚。

BTW… Allen Holub 的網站還在… 不知道當事人還記不記得這段黑歷史… wwwww

2020-02-09

Maven Central Repo 強制使用 HTTPS

今天炸了 project 突然無法 build 的問題,mvn 跳了三行 download pom 檔之後就宣告 fail。拿 URL 到 browser 上頭確認是不是 server 掛了,結果得到一個 501,然後給了一句:「More information at https://links.sonatype.com/central/501-https-required 」。

簡單地說,就是從 2020.01.15 開始,兩大預設 central repo 都強制要求要用 https 連線,不然就賞你 501。

好,知道是知道了,但是不知道要去哪裡改,至少 conf/settings.xml 看不出所以然、我也不知道別招了… (艸

狗了一下發現(還)沒啥災情,想到我還是在用 n 年前的 3.2.1 版,於是抓了最新的 3.6.3 版,四海昇平。

幹,那就這樣吧… [逃]

2014-06-01

[新手止難] 為什麼有些東西要弄得那麼複雜?

昨日驚見一文〈「互相之臉」所謂何用〉 [註1],
討論文章數目,為數月來爪哇連線版鮮見現象。
拜讀過各家之言後,回顧提問者之疑……

此時夜半人靜、身困馬桶不得其門而出
沉吟許久,上下交迫間,腦中浮現劣思緒緒
顧不得少年得痔之慮,返回書桌振鍵急敲,以求一吐為快
乃成於此篇陋文,敗筆處處
還望各方高人雅士撥冗指點一二,以正方家


敝痞在資訊領域打混唬爛多年
整理出〈資訊三大唬爛金句〉
其中首要綱領,為挪用錢鍾書所言:

道不虛明,有為而發  

或用俗語釋之

殺頭生意有人作  
賠本生意沒人幹  

雖凡天下學問,約莫均可套用此理
但惟資訊領域,可全然貫穿通透

只因此門學問均屬人為打造,鮮少摻雜自然現象
間或參雜不確定性,也可用機率模型解釋
又因效率為首,一切循規蹈矩最為便利
是謂「資訊領域少例外,規格讀通各處行」

所以,當吾人遇一理論或一設計,艱澀難懂
正當唸之無味棄之被當,國罵聲不絕與耳時
不妨憶此金句,自問之:

懶惰乃人類之美德  
若發明者不夠懶惰,安可流傳至今?

抱此想法續讀,不僅情緒平復、易於心領神會
倘使發現更懶惰之法,圖林獎豈非垂手可得?


今吾人已瞭解其所敘述之架構
但僅見「超頭」處處 [註2]
再三盤算依然覺得:此乃蝕本生意,作不得! 作不得!
莫非敝痞之虎爛金句有誤? 非也…

在此,逆舉老子之說,搭配程式設計為例:

小國寡民,使有什佰之器而不用,使民重死而不遠徒。  
雖有舟輿,無所乘之;雖有甲兵,無所陳之;使民復結繩而用之。  
甘其食,美其服,安其居,樂其俗。  
鄰國相望,雞犬之聲相聞,民至老死不相往來。

將「國」類比至「程式規模」
將「民」類比至「程式設計師」
將「舟輿」、「甲兵」類比至各式軟體架構

若是程式規模小彼此之間關係簡單
又或是無須與其他程式設計師往來
吾人可任意而為之,無所窒礙
此時亦難窺得舟輿甲兵之利,也就無從了解軟體架構之妙

禪宗分南北,北宗主漸修,南宗主頓悟
若漸修之途不可得,何妨留至來日機到緣成,自然頓悟?

敝痞此說,並非低漸修而貴頓悟
漸修之途不可省,則來日方能有頓悟之緣
即便汝輩天縱英名,亦不能自恃而於此處偷懶
以免淪於「重新再造輪」之窘境,慎之! 慎之!

[註1] 「互相之臉」亦有人美譯為「臉間」
其原文為 interface,常見地球語稱之「介面」 \囧/

[註2] 「超頭」其原文為 overhead \囧/


原文寫於 2006.04.15 PTT Java 版。

嗯… 那個時候國文比較好 [笑]

[新手止難] 寫程式就像寫作文 \囧/

各位 Java 版的版友大家好, 我的唬濫分析師痞子猴

痞子今天來這裡幹什麼?
教大家怎麼唬濫新手嘛…

痞子有沒有說,寫程式就像寫作文
你一定要把題目搞清楚
不然馮京變馬涼,就算有痞子那樣唬濫的功力
寫出來的程式還是錯嘛
神 仙 難 救 白 目 客

……..

國文老師有沒有說
寫作文要有起承轉合、要立論嚴謹、要思路清晰
痞子有沒有說,程式要定義好 Input 跟 Output
整個流程都能用火星文講給任何一個火星人聽得懂
你有聽痞子的話嗎? 沒有嘛…
自己都還沒想清楚整個 Input,Output 跟流程
就跑到板上問要怎麼寫、問要用什麼語法、問要用什麼類別
結果大家都以為你是作業文,把你這篇文章作空
你要痞子怎麼辦? 痞子也不知道怎麼辦… [丟滑鼠]

……..

國文老師有沒有說,字跡要端正、成語要用對、要避免錯別字
痞子有沒有說,縮排排版要確實、基礎語法要搞清楚、要避免不良變數名稱
你有聽痞子的話嗎? 沒有嘛…
結果程式碼迴圈區段搞錯,語法 & 邏輯錯誤一堆,程式碼難讀難懂
搞得作多也不是,作空也不成
你要痞子怎麼辦? 痞子也不知道要怎麼辦… [丟鼠墊]

……..

國文老師有沒有說,立論的問題點要各個擊破
痞子有沒有說,要把功能切細再切細,然後一小個一小個分開解決
你有聽痞子的話嗎? 沒有嘛…
重頭到尾都混亂不清 bug 一堆,結果大盤走勢不明朗
你要痞子怎麼辦? 痞子也不知道該怎麼辦… [丟鍵盤]

……..

好的痞子教你用 Java,壞的痞子要你用 .net
結果你不好好學 Java,現在要寫案子了,才發現被 M$ 套牢
你要痞子怎麼辦? 痞子也不知道怎麼辦… [丟螞蟻書]

……..

好! 我們進一段廣告
為了慶祝痞子順利 po 完這篇文章
廣告期間徵召各類 Java 駐版技術達人
有優待!

進廣告


原文寫於 2006.02.23 PTT Java 版,語氣是模仿當時一位股票名嘴的風格。

2014-02-05

GWT 與 HttpClient 的版本衝突

標題好難下,不過說穿了其實根本也沒什麼。 [死]

故事是這樣的:在一個既有的 GWT project 中想在 server side 用 HttpClient 抓 web API 的東西,抓了 4.2.3 版回來,想說先寫個 application 來測,結果一直炸 java.lang.NoSuchMethodError,明明我只跑最簡單的範例阿阿阿阿混蛋:

public static void main(String[] args) throws Exception {
    System.out.println(
        Request.Get("http://localhost:8080/BSS.html")
        .execute().returnContent()
    );
}

狗了一下好像沒人有我這樣的問題,該不會只是因為我沒用 Maven 吧 [胡思亂想ing]。於是先弄個乾淨的 Java project……. 沒問題;那在 GWT project 下用 JSP / Servlet 執行 HttpClient 碼…… 也沒問題 [死]。

停下來想了一下,該不會 GWT 本來就有用 HttpClient?GWT RPC 底下用 HttpClient 好像完全合情合理,只是 HttpClient 的版本不同結果就哭哭了?結果還果真如此,上面那個例子中找不到的 setConfig() 的 HttpRequestBase,是用 gwt-div.jar 裡頭的版本。

基本上在不想搞懂 classloader 以及 Eclipse 設定之類 blahblah 的討人厭東西,這個 issue 就到這邊為止了,反正不用要 application 就沒事了 [毆飛]

只是我常常遇到官方 example 跑不起來,這到底是怎樣的人品阿阿阿阿 [淚奔]

2012-04-12

Haskell 與超人 [上]

人類是一條繫在動物與超人之間的繩索,一條高懸於深淵的繩索,要從一端越過另一端是危險的。  ——尼采
這篇文章的內容會有點凌亂,甚至應該屬於另一個 Blog 的範圍。不過真正讓我開始下筆書寫的觸發點,卻是 Haskell 與這幾年寫程式的有感而發,那也就放在這邊了。

先說標題的「超人」。雖然開頭引用了尼采的超人說,不過我沒打算(也沒能力)使用尼采的「超人」定義,而是普羅大眾習慣的「superman」,或著用「天才」可能更適切一點。但是用 superman 來誤解尼采的這番話,其實另有一番味道在......

我覺得這幾年的程式語言發展逐漸有「超人化」的現象。

2011-11-28

我所用的免費開發工具

「如果開發工具就是要我們付一堆錢,那我就讓你們看見窮人的驕傲」

  • JavaSE:從 Sun 到 Oracle,至少官方的 JVM 與 SDK 都還是免費下載使用。
  • Eclipse:以 Java Programmer 來說,這是一套很完整的 IDE。code assist、JavaDoc...... 到 Refactory 的功能(自從有了 rename 功能之後,變數名稱都可以亂取了呢... [毆飛])都無須安裝 plugin 就具備。如果以「我只是想寫個簡單的 Java」角度來說,可能還會有點嫌棄裡頭有太多東西了(瞧瞧那精美的 Ant、Maven、Mylyn......)
  • Google Plugin for Eclipse(GPE):包含 GWT、GAE 的 SDK 與 GAE 的 local runtime 以及整合 GWT 的 deploy 工具。GWT 裡頭還提供了 GWT Designer 這個處理 layout 的 UI 工具。現在還整合 Google Code、Google API....... 統統不用錢。btw... 其實 GWT Designer 本來是商業軟體,後來被 Google 買下來之後就......
再加上用 Google Doc (算是能)解決 M$ Office 的檔案問題,買 Notebook 本來就附贈 Windows 的版權(我沒厲害到想去用 Linux 或是 Mac),電腦打開來,還真的沒什麼值錢的軟體...... 

這就是窮人的驕傲 [炸]

2008-09-02

從傳統語言轉到 OOP 的輔助文章

發表於某站某版... 貼過來增加文章數目 [茶]
(不然... 委靡不振的 AdSense 收入阿阿阿阿... [遠目])

我不是 OO 強者,我都是硬幹的寫 OO [挺]
所以,我沒辦法回答你太高深的問題
只能分享一下 n 年前從 QB 轉到 Java 來的時候跨過的障礙.... [遠目]

首先,以某種角度而言,OO 裡頭的 method 跟平常的 function...
棍... 根本沒什麼差別... Orz

反正需要的話,參數還是要傳、該 return 的還是要 return
在同一個 class 裡頭互相用的 method 更是有這種感覺
(前頭不用寫 xxxx.fooMethod(),只要寫 fooMethod() 就可以用啦 XD)

棍... 那差別在哪?

不如來看字串... 如果你要把一個字串裡頭所有的 ps 都變成 87
傳統 function 情況下大概是這樣寫...

String aString = "I am ps";
String bString = "ps is 87";
aString = replace(aString, "ps", "87");
bString = replace(bString, "ps", "87");

function replace(sourceString, targetString, replaceString){
//跳過,要 return 新字串就是了
//要不 return 也可,不過那跟 oo 比較沒啥關係,就跳過
}

用 OO 的寫法... 喔... 因為 String 這個 class Java 已經提供了
所以直接看用法,那會是

String aString = "I am ps";
String bString = "ps is 87";
aString = aString.replace("ps", "87");
bString = bString.replace("ps", "87");

基本上,正常人(或是像我這種異常人 XD)會覺得 OO 的寫法比較好讀

不過,光是這樣好像也沒什麼
那麼,來設計一下橋牌的遊戲

如果在傳統(沒有 OO)的寫法大概要這樣寫

//第一個是哪一個玩家,第二個是 13 張排,後面一個塞花色一個塞點數
int card[][][] = new int[4][13][2];

//每個人的墩數,英文應該叫做 trick 吧? (錯了就算了)
int trick[] = new int[4];

//其他懶得寫了,現在已經沒辦法用這種方法思考
//請自行想像發揮,然後跟後頭 OO 版本的坐個對應

可是 OO 的話,大概會寫成

//包 card 物件
class Card{
int number;
int flower; //花色 lol
//getter 跟 setter 之類就請自便
}

//包玩家
class Player{
Card card[] = new Card[13];
int trick;

//每一輪第一個,可以隨便出
public Card aHand(){
//blahblah
}

//不然就會被限定花色
public Card aHand(int flower){
//blahblah
}

//贏了一墩
public void winTrick(){
trick++;
}
}

//遊戲流程大概就會長成這樣
Player player[] = new Player[4];
Card trickCard[] = new Card[4];
int startPlayer; //紀錄這一輪由誰開始

//發牌叫牌就跳過 [茶]

trickCard[0] = player[startPlayer].aHand();
for(int i=1; i<4; i++){
trickCard[i] =
player[(startPlayer+i)%4].aHand(trickCard[0].getFlower());
}

startPlayer=this.getTrickWiner();
player[startPlayer].winTrick();

private int getTrickWiner(){
//根據 trickCard 跟 startPlayer 寫一堆判斷之類 blahblah
}

上面講的這些,大概是封裝 encapsulation 的範疇
如果之前已經寫過不少程式,那麼有了這個基礎(或是跨過這個障礙)
再看其他 OO 的東西,或許會比較快樂一點

是說,我也只會這個 [遠目]

2007-03-31

Java Browser Project 調查報告

→http://html.xamjwg.org/index.jsp
測試了幾個自己的網站,網頁 support 奇慘無比。我實在看不出來他哪裡支援 JavaScript 跟 CSS =.=(難道 JSPWiki 裡頭的 CSS 不是 2.0 版?)、編碼支援也很糟糕。重點是,連我的首頁(只有基本到不行的 html 碼)都顯示的很糟糕....

→http://browserlaunch2.sourceforge.net/
這是一個有點莫名其妙的 library,他不是寫一個瀏覽器,而是讓 Java 去呼叫外部的 browser 然後開啟你要的網頁(路人:人家明明 project 名稱就這樣寫了,幹麼說人家莫名其妙 [踹])。雖然跟要找的東西無關,不過還是順便 memo 一下。

→http://www.icesoft.com
這個測了一下,還不錯,不過他的 open source 很奇怪。有給某些 source,不過和新的部份好像都沒有附... 不過倒是在他的 Developer Guide 當中找到應該更貼近目前需要的名詞「HTML Rendering」

就先到這裡 hold 住吧...

2007-03-18

快快樂樂學 RMI - Start!

身為一個只會寫 Java 的人而言,遇到分散式系統,好像不能不實際來 try 一下 RMI。不過對於用 Eclipse 習慣的人而言... 寫 RMI 實在好麻煩啊... [泣],google 找到的 plug-in 似乎只有這個,但是看到他要收費,馬上就軟掉(?)了。Ant 又完全沒有用的習慣,還是回憶一下 bat 檔怎麼寫吧... Orz

好了,言歸正傳。

因 為種種點點點我不知道的原因(理論上是因為 Java 沒有多重繼承,然後 RMI 又希望溝通的物件是 parent class 是 java.rmi.server.UnicastRemoteObject,不過這純屬猜測... API 懶得看 XD),所以真正要讓 client 可以遠端呼叫的物件,必須讓它 implement 自己訂做的 interface,而且這個 interface 還得 extend java.rmi.Remote。所以,就先來定義一下 interface 吧:

FooInterface.java
package test.remoteInterface;

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Date;

public interface FooInterface extends Remote {
public Date getServerDate() throws RemoteException;

public String getServerMessage() throws RemoteException;

public void setServerMessage(String s) throws RemoteException;
}


這 裡留了幾個惡搞的空間。只要 Server 跟 client 不同機器,且時間調的不一樣,那麼 getServerDate() 就可以顯現出差異。而 serverMessage 的 setter 跟 getter 是用來測試 server 端的物件如果被改變,能不能 keep 住。下面這個實做就變得沒啥好講了... [茶]

FooObject.java
package test.remoteObject;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Date;
import test.remoteInterface.FooInterface;

public class FooObject extends UnicastRemoteObject implements FooInterface {
/**
* 為了減少 warning 而加的
*/
private static final long serialVersionUID = 1L;

private String message;

public FooObject() throws RemoteException {
super();
message = "Default Message";
}

public Date getServerDate() throws RemoteException {
return new Date();
}

public String getServerMessage() throws RemoteException {
return message;
}

public void setServerMessage(String s) throws RemoteException {
message = s;
}
}


然後追加一個 server,執行這個就可以讓他接客了 \囧/
FooServer.java
package test;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import test.remoteObject.FooObject;

public class FooServer {
public static void main(String[] args) {
FooObject foo;
try {
foo = new FooObject();
Naming.rebind("rmi://127.0.0.1/FooServer", foo);
} catch (RemoteException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
System.out.println("Foo Server is ready");
}
}


最後,當然是來寫 client 端啊:
FooClient.java
package test;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.Date;
import test.remoteInterface.FooInterface;

public class FooClient {
/**
* 1: ip 位址
* 2: 設定字串
* @param args 參數
*/
public static void main(String[] args) {
try {
FooInterface foo = (FooInterface) Naming.lookup("rmi://" + args[0]
+ "/FooServer");
System.out.println("Server Date : " + foo.getServerDate());
System.out.println("Client Date : " + new Date());
System.out
.println("Before set message : " + foo.getServerMessage());
foo.setServerMessage(args[1]);
System.out.println("After set message : " + foo.getServerMessage());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}
}
給 我等一下![指],並不是這樣子 compile 完,分別執行 FooServer 跟 FooClient 就能用。你還得針對 FooObject 另外作 rmi compile 的處理。在這個 foo 的例子當中,就要下(classpath 等問題請自己搞定)
rmic test.remoteObject.FooObject
這會另外產生 FooObject_Stub.class,在包 client 的執行環境,這個檔也要加進去。

另外,在執行 FooServer 之前,得先執行一個程式 rmiregistry(在 {JDK}/bin/)。如果你也不幸是用 Windows,那在 command line 的環境下打「start rmiregistry」,會另外自動開個黑窗。

一切都順利的話,就可以來真正來玩玩看了。[茶]

錯誤排除區:
java.net.ConnectException→咪的,rmiregistry 啟動了沒?

備註區:
網路上的範例幾乎都有在 client/server 端的程式碼來這行:
System.setSecurityManager(newRMISecurityManager())
但是... 我用了反而問題一大堆(有設定 java.policy 了),還搞出一堆靈異現象。拿掉那行反而一點事情也沒有 [爆]。