如何使用Java管理對(duì)象方法?針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
從策劃到設(shè)計(jì)制作,每一步都追求做到細(xì)膩,制作可持續(xù)發(fā)展的企業(yè)網(wǎng)站。為客戶提供成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)、網(wǎng)站策劃、網(wǎng)頁(yè)設(shè)計(jì)、主機(jī)域名、虛擬空間、網(wǎng)絡(luò)營(yíng)銷、VI設(shè)計(jì)、 網(wǎng)站改版、漏洞修補(bǔ)等服務(wù)。為客戶提供更好的一站式互聯(lián)網(wǎng)解決方案,以客戶的口碑塑造優(yōu)易品牌,攜手廣大客戶,共同發(fā)展進(jìn)步。
public class HelloWorld { public void hello() { System.out.println("hello world!"); } } HelloWorld helloWorld = new HelloWorld(); helloWorld.hello();
你們看,我有一個(gè) HelloWorld 類,我用 new 就能直接創(chuàng)建一個(gè)對(duì)象,然后就能使用這個(gè)對(duì)象中所有的方法了,多簡(jiǎn)單啊。
二弈是工作兩年的我,他一臉鄙視的對(duì)小弈說(shuō),你別整天 HelloWorld 好不好,還有啊,除了 new 你就不會(huì)其他的了,能不能有點(diǎn)追求???
小弈對(duì)二弈說(shuō)那你說(shuō)除了 new 還有什么辦法啊?
二弈說(shuō)可以通過(guò) Class 的 newInstance 或者 Constructor 的 newInstance 來(lái)創(chuàng)建對(duì)象實(shí)例啊。
不過(guò)你得記住,Class 的 newInstance 只能對(duì)那些擁有可見(jiàn)的(Accessible)無(wú)參構(gòu)造函數(shù)的類,才能進(jìn)行對(duì)象的實(shí)例化,而 Constructor 就沒(méi)有這些限制。
大弈是工作三年的我,他說(shuō),雖然你們的方法都可以用來(lái)創(chuàng)建對(duì)象,但都還是手動(dòng)創(chuàng)建的,太原始了,生產(chǎn)力太低。
工欲善其事,必先利其器,我們也得找個(gè)高效的生產(chǎn)力工具。IOC 容器你們了解吧?
以前我們?cè)谝粋€(gè)對(duì)象中如果要調(diào)用另外一個(gè)對(duì)象的方法時(shí),都是通過(guò) new 或者反射來(lái)手動(dòng)創(chuàng)建該對(duì)象,但是每次都這樣做太累了,并且類之間的耦合也很高。
通過(guò) IOC 容器,我們可以把所有的對(duì)象交給容器來(lái)管理,在使用之前只需要定義一下對(duì)象,然后再使用到該對(duì)象時(shí),IOC 容器就會(huì)幫我們把該對(duì)象初始化好,這樣是不是更方便呢?
大弈說(shuō)完,舉了一個(gè)例子:
@Bean public class RegisterService { public void register() { // do register } } @Bean public class LoginService { public void login() { // do login } } @Bean public class HelloWorld { @Autowired private RegisterService registerService; @Autowired private LoginService loginService; public void hello() { // 注冊(cè) registerService.register(); // ... // 登錄 loginService.login(); } }
IOC 容器通過(guò)一種叫 Bean 的注解,在系統(tǒng)啟動(dòng)時(shí)掃描所有通過(guò) Bean 標(biāo)注的類,對(duì)這些類進(jìn)行實(shí)例化,然后將所有的對(duì)象都保存在容器中。再掃描所有通過(guò) Autowired 標(biāo)注的屬性或者方法,從容器中找到與之匹配(通過(guò)名稱或者類型等)的對(duì)象將具體的對(duì)象賦值給這些屬性。這樣我們就可以直接將這些對(duì)象拿來(lái)使用了,作為一個(gè)伸手黨是不是很幸福啊。
老弈是工作五年的我,他聽了大弈的話后,提出了一個(gè)問(wèn)題,對(duì)于新的項(xiàng)目可以使用這種 IOC 的容器,可是對(duì)于那些遺留的老項(xiàng)目來(lái)說(shuō),要使用 IOC 來(lái)改造是不太符合實(shí)情的。
我舉個(gè)例子,在一個(gè)遺留的老項(xiàng)目中,有一個(gè)核心的接口 Handler:
public interface Handler{ RES handle(REQ request); }
Handler 接口有很多的實(shí)現(xiàn)類,我們需要對(duì)不同的請(qǐng)求來(lái)調(diào)用不同的 Handler 實(shí)現(xiàn)類進(jìn)行處理,如果用 IOC 容器來(lái)管理這些實(shí)現(xiàn)類,顯然不太合適,因?yàn)槲覀兲幚碇笆遣恢涝撚媚膫€(gè) Handler 實(shí)現(xiàn)類的。
大弈想了想,如果 Handler 接口只有幾個(gè)固定的實(shí)現(xiàn)類,并且在使用時(shí)只會(huì)使用一個(gè)來(lái)進(jìn)行處理,那么倒是可以在啟動(dòng)前通過(guò)配置的方式來(lái)確定具體使用哪種 Handler ,比如可以通過(guò) @Conditional 根據(jù)某些條件來(lái)確定加載具體的對(duì)象,但是這種要在使用時(shí)才能確定 Handler 對(duì)象的類型確實(shí)比較棘手。
老弈看大家都不說(shuō)話了,就繼續(xù)說(shuō)了下去。
為了要在調(diào)用方法時(shí)使用不同的 Handler 來(lái)處理不同的而請(qǐng)求,需要確定兩種類,一種是請(qǐng)求類,一種是處理類,并且要讓請(qǐng)求類和處理類一一對(duì)應(yīng)起來(lái)。
假設(shè)我們的請(qǐng)求類是一個(gè) Packet 類,每一個(gè)具體的請(qǐng)求類都繼承自這個(gè)基類。
那么想要確定每一個(gè)具體的 Packet 是什么類型的,可以有很多種方法,可以為每個(gè) Packet 取一個(gè)唯一的名字,例如:
public abstract class Packet { public abstract String name(); }
也可以為每一個(gè) Packet 指定一個(gè)標(biāo)志,例如:
public abstract class Packet { public abstract int symbol(); }
但是不管哪種方式,每一個(gè) Packet 的實(shí)現(xiàn)類都需要實(shí)現(xiàn)抽象類中的方法,來(lái)“標(biāo)志”自己是哪種 Packet。
我們以第二種方式舉例,假設(shè)我們有兩個(gè)具體的 Packet:
public class RegisterPacket extends Packet { // 注冊(cè)所需要的其他參數(shù) int symbol() { return 1; } } public class LoginPacket extends Packet { // 登錄所需要的其他參數(shù) int symbol() { return 2; } }
這樣當(dāng)我們接收到 request 對(duì)象時(shí),通過(guò)調(diào)用 request.symbol() 就知道這個(gè) request 是哪種類型的 Packet 了,這時(shí)只要找到具體的 Handler 實(shí)現(xiàn)類來(lái)處理就可以了。
那請(qǐng)求類已經(jīng)可以確定了,怎樣確定 Handler 處理類呢?我們是否也可以在 Handler 接口中定義一個(gè) symbol 方法呢,像這樣:
public interface Handler{ int symbol(); RES handle(REQ request); }
這樣的話,只要在所有的實(shí)現(xiàn)類中實(shí)現(xiàn) symbol 方法來(lái)標(biāo)注該 Handler 是用來(lái)處理何種 request 的即可。
public RegisterHandler implements Handler{ int symbol(){ return 1; } RES handle(RegisterPacket request){ // 具體的處理方法 } } public LoginHandler implements Handler { int symbol(){ return 2; } RES handle(LoginPacket request){ // 具體的處理方法 } }
最后把所有的 Handler 實(shí)現(xiàn)類都實(shí)例化后保存在一個(gè) HandlerProvider 中,要使用時(shí)再到 HandlerProvider 中來(lái)獲取即可:
public interface HandlerProvider { Handler getHandler(int symbol); }
那怎樣獲取到所有的 Handler 的實(shí)現(xiàn)類呢,有兩種方法。
一種是通過(guò) ServiceLoader.load(Handler.class) 的方式來(lái)獲取,不過(guò)這種通過(guò) spi 的方式需要在項(xiàng)目的 resources/META-INF/services/ 目錄下創(chuàng)建一個(gè) xxx.Handler 的文件,并在文件中將所有 Handler 的實(shí)現(xiàn)類的完全類限定符列出來(lái)。
另一種比較簡(jiǎn)單的方式是通過(guò)掃描的方式,獲取到所有 Handler 的實(shí)現(xiàn)類。
到現(xiàn)在為止,我們的實(shí)現(xiàn)還算可以,但是有一個(gè)問(wèn)題,那就是在 Handler 接口中我們?cè)黾恿艘粋€(gè)方法,這樣做就對(duì)原來(lái)的代碼進(jìn)行了侵入。
為了讓原來(lái)的代碼保持不變,我們可以定義一個(gè)注解來(lái)標(biāo)注在所有的 Handler 實(shí)現(xiàn)類上,比如這樣:
@Symbol(1) public RegisterHandler implements Handler{ RES handle(RegisterPacket request){ // 具體的處理方法 } } @Symbol(2) public LoginHandler implements Handler { RES handle(LoginPacket request){ // 具體的處理方法 } }
這樣就將 Handler 的實(shí)現(xiàn)和標(biāo)注進(jìn)行了解耦了,也可以通過(guò)掃描 @Symbol 注解來(lái)獲取到所有的 Handler 實(shí)現(xiàn)類,不過(guò)這樣做的缺點(diǎn)就是假如我忘記對(duì)某個(gè) Handler 實(shí)現(xiàn)類添加 @Symbol 注解,到時(shí)候就獲取不到該 Handler 了。
大家聽完老弈的話之后,都陷入了沉思,我靠,還可以這么玩,真有趣。
這時(shí)候現(xiàn)在的我,也就是逅弈,說(shuō)了一句,如果我有一個(gè)接口,他只有幾個(gè)固定的實(shí)現(xiàn)類,我不想搞那一套那么重的實(shí)現(xiàn)方式,但是我也需要?jiǎng)討B(tài)的獲取實(shí)現(xiàn)類來(lái)對(duì)請(qǐng)求進(jìn)行處理,那我該怎么辦呢?
比如我有一個(gè)序列化的接口,如下所示:
public interface Serializer { byte[] serialize(Packet packet); }
然后只有五種具體的序列化的實(shí)現(xiàn)類,如下所示:
public class JdkSerializer implements Serializer { @Override public byte[] serialize(Packet packet) { // 具體的序列化操作 } } public class FastJsonSerializer implements Serializer { @Override public byte[] serialize(Packet packet) { // 具體的序列化操作 } } public class HessianSerializer implements Serializer { @Override public byte[] serialize(Packet packet) { // 具體的序列化操作 } } public class KryoSerializer implements Serializer { @Override public byte[] serialize(Packet packet) { // 具體的序列化操作 } } public class ProtoStuffSerializer implements Serializer { @Override public byte[] serialize(Packet packet) { // 具體的序列化操作 } }
那么我們?cè)撛趺创_定使用哪種序列化方式對(duì)參數(shù) packet 進(jìn)行序列化呢?
使用老弈剛剛說(shuō)的那一套也確實(shí)能夠?qū)崿F(xiàn),不過(guò)太麻煩了,又得對(duì) Packet 定義 symbol,又得對(duì) Hander 實(shí)現(xiàn)類進(jìn)行標(biāo)注,還得掃描所有的實(shí)現(xiàn)類。
我只有五個(gè)實(shí)現(xiàn)類,不需要搞那么麻煩的。
其實(shí)很簡(jiǎn)單,只需要定義一個(gè)枚舉類,表示序列化的算法,然后對(duì) Packet 增加一個(gè) algorithm 方法用來(lái)表示,使用何種序列化算法,如下所示:
public enum SerializeAlgorithm { JDK((byte) 1), FAST_JSON((byte) 2), HESSIAN((byte) 3), KRYO((byte) 4), PROTO_STUFF((byte) 5); private byte type; SerializeAlgorithm(byte type) { this.type = type; } } public abstract class Packet implements Serializable { public abstract byte algorithm(); }
然后定義一個(gè) SerializerChooser 根據(jù)不同的算法選擇不同的 Serializer 實(shí)現(xiàn)類即可:
public interface SerializerChooser { Serializer choose(byte algorithm); }
因?yàn)楦鶕?jù)算法是可以知道對(duì)應(yīng)的序列化接口的,所以就沒(méi)有必要去掃描了,直接把幾種序列化的實(shí)現(xiàn)類枚舉出來(lái)即可,對(duì)象的實(shí)例可以使用單例模式,如下所示:
public class DefaultSerializerChooser implements SerializerChooser { private DefaultSerializerChooser() { } public static SerializerChooser getInstance() { return Singleton.get(DefaultSerializerChooser.class); } @Override public Serializer choose(byte algorithm) { SerializeAlgorithm serializeAlgorithm = SerializeAlgorithm.getEnum(algorithm); switch (serializeAlgorithm) { case JDK: { return Singleton.get(JdkSerializer.class); } case FAST_JSON: { return Singleton.get(FastJsonSerializer.class); } case HESSIAN: { return Singleton.get(HessianSerializer.class); } case KRYO: { return Singleton.get(KryoSerializer.class); } case PROTO_STUFF: { return Singleton.get(ProtoStuffSerializer.class); } default: { return null; } } } }
Java的基本數(shù)據(jù)類型分為:1、整數(shù)類型,用來(lái)表示整數(shù)的數(shù)據(jù)類型。2、浮點(diǎn)類型,用來(lái)表示小數(shù)的數(shù)據(jù)類型。3、字符類型,字符類型的關(guān)鍵字是“char”。4、布爾類型,是表示邏輯值的基本數(shù)據(jù)類型。
關(guān)于如何使用Java管理對(duì)象方法問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。