策略模式:告別if else
閱讀完本篇文章你將了解到什么是策略模式,策略模式的優(yōu)缺點(diǎn),以及策略模式在源碼中的應(yīng)用。
策略模式引入
在軟件開(kāi)發(fā)中,我們常常會(huì)遇到這樣的情況,實(shí)現(xiàn)某一個(gè)功能有多條途徑,每一條途徑對(duì)應(yīng)一種算法,此時(shí)我們可以使用一種設(shè)計(jì)模式來(lái)實(shí)現(xiàn)靈活地選擇解決途徑,也能夠方便地增加新的解決途徑。
譬如商場(chǎng)購(gòu)物場(chǎng)景中,有些商品按原價(jià)賣(mài),商場(chǎng)可能為了促銷(xiāo)而推出優(yōu)惠活動(dòng),有些商品打九折,有些打八折,有些則是返現(xiàn)10元等。而優(yōu)惠活動(dòng)并不影響結(jié)算之外的其他過(guò)程,只是在結(jié)算的時(shí)候需要根據(jù)優(yōu)惠方案結(jié)算。
再比如不同的人出去旅游出行的交通方式也不同,經(jīng)濟(jì)條件好的會(huì)選擇高鐵飛機(jī),而普通人可能會(huì)選擇綠皮火車(chē)。
富豪老王打算去西藏旅游,老王定了豪華酒店,并且定了機(jī)票當(dāng)天直達(dá)。而普通人老張也要去西藏旅游,他打算選擇乘坐高鐵出行。而學(xué)生黨的我小汪肯定會(huì)選擇綠皮火車(chē),主要是為了看路邊的風(fēng)景,而不是因?yàn)楦F。
下面我們用代碼來(lái)描述一下上訴場(chǎng)景:
public class Travel { private String vehicle;//出行方式 private String name; public String getName() { return name; } public Travel(String name) { this.name = name; } public void setName(String name) { this.name = name; } public void setVehicle(String vehicle) { this.vehicle = vehicle; } public String getVehicle() { return vehicle; } public void TravelTool(){ if(name.equals("小汪")){ setVehicle("綠皮火車(chē)"); }else if(name.equals("老張")){ setVehicle("高鐵"); }else if(name.equals("老王")){ setVehicle("飛機(jī)"); } System.out.println(name+"選擇坐"+getVehicle()+"去西藏旅游"); } } public class Test { public static void main(String[] args) { Travel travel1 = new Travel("小汪"); Travel travel2 = new Travel("老王"); Travel travel3 = new Travel("老張"); travel1.TravelTool(); travel2.TravelTool(); travel3.TravelTool(); } } 小汪選擇坐綠皮火車(chē)去西藏旅游 老王選擇坐飛機(jī)去西藏旅游 老張選擇坐高鐵去西藏旅游
以上代碼雖然完成了我們的需求,但是存在以下問(wèn)題:
Travel類(lèi)的TravelTool方法非常龐大,它包含各種人的旅行實(shí)現(xiàn)代碼,在代碼中出現(xiàn)了較長(zhǎng)的 if…else… 語(yǔ)句,假如日后小汪發(fā)達(dá)了也想體驗(yàn)一下做飛機(jī)去西藏旅游,那就要去修改TravelTool方法。違反了 “開(kāi)閉原則”,系統(tǒng)的靈活性和可擴(kuò)展性較差。
算法的復(fù)用性差,如果在另一個(gè)系統(tǒng)中需要重用某些算法,只能通過(guò)對(duì)源代碼進(jìn)行復(fù)制粘貼來(lái)重用,無(wú)法單獨(dú)重用其中的某個(gè)或某些算法。
策略模式
策略模式的介紹
策略模式(Strategy Pattern)中,定義算法族,分別封裝起來(lái),讓他們之間可以互相替換,此模式讓算法的變化獨(dú)立于使用算法的客戶(hù)這算法體現(xiàn)了幾個(gè)設(shè)計(jì)原則,
第一、把變化的代碼從不變的代碼中分離出來(lái);
第二、針對(duì)接口編程而不是具體類(lèi)(定義了策略接口);
第三、多用組合/聚合,少用繼承(客戶(hù)通過(guò)組合方式使用策略)。
策略模式的原理類(lèi)圖
角色分析
Context
(環(huán)境類(lèi)):環(huán)境類(lèi)是使用算法的角色,它在解決某個(gè)問(wèn)題(即實(shí)現(xiàn)某個(gè)方法)時(shí)可以采用多種策略。在環(huán)境類(lèi)中維持一個(gè)對(duì)抽象策略類(lèi)的引用實(shí)例,用于定義所采用的策略。
Strategy
(抽象策略類(lèi)):它為所支持的算法聲明了抽象方法,是所有策略類(lèi)的父類(lèi),它可以是抽象類(lèi)或具體類(lèi),也可以是接口。環(huán)境類(lèi)通過(guò)抽象策略類(lèi)中聲明的方法在運(yùn)行時(shí)調(diào)用具體策略類(lèi)中實(shí)現(xiàn)的算法。
ConcreteStrategy
(具體策略類(lèi)):它實(shí)現(xiàn)了在抽象策略類(lèi)中聲明的算法,在運(yùn)行時(shí),具體策略類(lèi)將覆蓋在環(huán)境類(lèi)中定義的抽象策略類(lèi)對(duì)象,使用一種具體的算法實(shí)現(xiàn)某個(gè)業(yè)務(wù)處理。
我們下面用策略模式來(lái)改進(jìn)一下上面旅行的代碼例子。
抽象策略類(lèi)
Discount
public abstract class AbstractTravle { private String vehicle; private String name; public AbstractTravle(String vehicle, String name) { this.vehicle = vehicle; this.name = name; } public String getVehicle() { return vehicle; } public String getName() { return name; } public abstract void TravelTool(); }
ConcreteStrategy
(具體策略類(lèi))
public class XiaoWang extends AbstractTravle{ public XiaoWang(String vehicle, String name) { super(vehicle, name); } @Override public void TravelTool() { System.out.println(getName()+"選擇坐"+getVehicle()+"去西藏旅游"); } } public class LaoWang extends AbstractTravle{ public LaoWang(String vehicle, String name) { super(vehicle, name); } @Override public void TravelTool() { System.out.println(getName()+"選擇坐"+getVehicle()+"去西藏旅游"); } } public class LaoZhang extends AbstractTravle{ public LaoZhang(String vehicle, String name) { super(vehicle, name); } @Override public void TravelTool() { System.out.println(getName()+"選擇坐"+getVehicle()+"去西藏旅游"); } }
環(huán)境類(lèi)
public class Context { private AbstractTravle abstractTravle; public Context(AbstractTravle abstractTravle) { this.abstractTravle = abstractTravle; } public void TravelTool() { System.out.println(abstractTravle.getName()+"選擇坐"+abstractTravle.getVehicle()+"去西藏旅游"); } }
策略模式總結(jié)
public class Test { public static void main(String[] args) { Context context1 = new Context(new LaoWang("飛機(jī)", "老王")); context1.TravelTool(); Context context2 = new Context(new LaoZang("高鐵", "老張")); context2.TravelTool(); Context context3 = new Context(new XiaoWang("綠皮火車(chē)", "小汪")); context3.TravelTool(); } } 老王選擇坐飛機(jī)去西藏旅游 老張選擇坐高鐵去西藏旅游 小汪選擇坐綠皮火車(chē)去西藏旅游
策略模式的主要優(yōu)點(diǎn)如下:
1.模式提供了對(duì) “開(kāi)閉原則” 的完美支持,用戶(hù)可以在不修改原有系統(tǒng)的基礎(chǔ)上選擇算法或行為,也可以靈活地增加新的算法或行為。
2.模式提供了管理相關(guān)的算法族的辦法。策略類(lèi)的等級(jí)結(jié)構(gòu)定義了一個(gè)算法或行為族,恰當(dāng)使用繼承可以把公共的代碼移到抽象策略類(lèi)中,從而避免重復(fù)的代碼。
3.模式提供了一種可以替換繼承關(guān)系的辦法。如果不使用策略模式而是通過(guò)繼承,這樣算法的使用就和算法本身混在一起,不符合 “單一職責(zé)原則”,而且使用繼承無(wú)法實(shí)現(xiàn)算法或行為在程序運(yùn)行時(shí)的動(dòng)態(tài)切換。
4.模式可以避免多重條件選擇語(yǔ)句。多重條件選擇語(yǔ)句是硬編碼,不易維護(hù)。
5.模式提供了一種算法的復(fù)用機(jī)制,由于將算法單獨(dú)提取出來(lái)封裝在策略類(lèi)中,因此不同的環(huán)境類(lèi)可以方便地復(fù)用這些策略類(lèi)。
策略模式的主要缺點(diǎn)如下:
1.端必須知道所有的策略類(lèi),并自行決定使用哪一個(gè)策略類(lèi)。這就意味著客戶(hù)端必須理解這些算法的區(qū)別,以便適時(shí)選擇恰當(dāng)?shù)乃惴?。換言之,策略模式只適用于客戶(hù)端知道所有的算法或行為的情況。
2.將造成系統(tǒng)產(chǎn)生很多具體策略類(lèi),任何細(xì)小的變化都將導(dǎo)致系統(tǒng)要增加一個(gè)新的具體策略類(lèi)。
3.同時(shí)在客戶(hù)端使用多個(gè)策略類(lèi),也就是說(shuō),在使用策略模式時(shí),客戶(hù)端每次只能使用一個(gè)策略類(lèi),不支持使用一個(gè)策略類(lèi)完成部分功能后再使用另一個(gè)策略類(lèi)來(lái)完成剩余功能的情況。
適用場(chǎng)景
1.系統(tǒng)需要?jiǎng)討B(tài)地在幾種算法中選擇一種,那么可以將這些算法封裝到一個(gè)個(gè)的具體算法類(lèi)中,而這些具體算法類(lèi)都是一個(gè)抽象算法類(lèi)的子類(lèi)。換言之,這些具體算法類(lèi)均有統(tǒng)一的接口,根據(jù) “里氏代換原則” 和面向?qū)ο蟮亩鄳B(tài)性,客戶(hù)端可以選擇使用任何一個(gè)具體算法類(lèi),并只需要維持一個(gè)數(shù)據(jù)類(lèi)型是抽象算法類(lèi)的對(duì)象。
2.對(duì)象有很多的行為,如果不用恰當(dāng)?shù)哪J?,這些行為就只好使用多重條件選擇語(yǔ)句來(lái)實(shí)現(xiàn)。此時(shí),使用策略模式,把這些行為轉(zhuǎn)移到相應(yīng)的具體策略類(lèi)里面,就可以避免使用難以維護(hù)的多重條件選擇語(yǔ)句。
3.望客戶(hù)端知道復(fù)雜的、與算法相關(guān)的數(shù)據(jù)結(jié)構(gòu),在具體策略類(lèi)中封裝算法與相關(guān)的數(shù)據(jù)結(jié)構(gòu),可以提高算法的保密性與安全性。
源碼分析策略模式的典型應(yīng)用
Java Comparator 中的策略模式
java.util.Comparator 接口是比較器接口,可以通過(guò) Collections.sort(List,Comparator) 和 Arrays.sort(Object[],Comparator) 對(duì)集合和數(shù)據(jù)進(jìn)行排序,下面為示例程序
@Data @AllArgsConstructor public class Student { private Integer id; private String name; @Override public String toString() { return "{id=" + id + ", name='" + name + "'}"; } }
// 降序 public class DescSortor implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o2.getId() - o1.getId(); } } // 升序 public class AscSortor implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.getId() - o2.getId(); } }
通過(guò) Arrays.sort() 對(duì)數(shù)組進(jìn)行排序
public class Test1 { public static void main(String[] args) { Student[] students = { new Student(3, "張三"), new Student(1, "李四"), new Student(4, "王五"), new Student(2, "趙六") }; toString(students, "排序前"); Arrays.sort(students, new AscSortor()); toString(students, "升序后"); Arrays.sort(students, new DescSortor()); toString(students, "降序后"); } public static void toString(Student[] students, String desc){ for (int i = 0; i < students.length; i++) { System.out.print(desc + ": " +students[i].toString() + ", "); } System.out.println(); } } 排序前: {id=3, name='張三'}, 排序前: {id=1, name='李四'}, 排序前: {id=4, name='王五'}, 排序前: {id=2, name='趙六'}, 升序后: {id=1, name='李四'}, 升序后: {id=2, name='趙六'}, 升序后: {id=3, name='張三'}, 升序后: {id=4, name='王五'}, 降序后: {id=4, name='王五'}, 降序后: {id=3, name='張三'}, 降序后: {id=2, name='趙六'}, 降序后: {id=1, name='李四'},
通過(guò) Collections.sort() 對(duì)集合List進(jìn)行排序
public class Test2 { public static void main(String[] args) { List<Student> students = Arrays.asList( new Student(3, "張三"), new Student(1, "李四"), new Student(4, "王五"), new Student(2, "趙六") ); toString(students, "排序前"); Collections.sort(students, new AscSortor()); toString(students, "升序后"); Collections.sort(students, new DescSortor()); toString(students, "降序后"); } public static void toString(List<Student> students, String desc) { for (Student student : students) { System.out.print(desc + ": " + student.toString() + ", "); } System.out.println(); } } 排序前: {id=3, name='張三'}, 排序前: {id=1, name='李四'}, 排序前: {id=4, name='王五'}, 排序前: {id=2, name='趙六'}, 升序后: {id=1, name='李四'}, 升序后: {id=2, name='趙六'}, 升序后: {id=3, name='張三'}, 升序后: {id=4, name='王五'}, 降序后: {id=4, name='王五'}, 降序后: {id=3, name='張三'}, 降序后: {id=2, name='趙六'}, 降序后: {id=1, name='李四'},
我們向 Collections.sort() 和 Arrays.sort() 分別傳入不同的比較器即可實(shí)現(xiàn)不同的排序效果(升序或降序)
這里 Comparator 接口充當(dāng)了抽象策略角色,兩個(gè)比較器 DescSortor 和 AscSortor 則充當(dāng)了具體策略角色,Collections 和 Arrays 則是環(huán)境角色
Spring Resource 中的策略模式
Spring 把所有能記錄信息的載體,如各種類(lèi)型的文件、二進(jìn)制流等都稱(chēng)為資源,譬如最常用的Spring配置文件。
在 Sun 所提供的標(biāo)準(zhǔn) API 里,資源訪(fǎng)問(wèn)通常由 java.NET.URL 和文件 IO 來(lái)完成,尤其是當(dāng)我們需要訪(fǎng)問(wèn)來(lái)自網(wǎng)絡(luò)的資源時(shí),通常會(huì)選擇 URL 類(lèi)。
URL 類(lèi)可以處理一些常規(guī)的資源訪(fǎng)問(wèn)問(wèn)題,但依然不能很好地滿(mǎn)足所有底層資源訪(fǎng)問(wèn)的需要,比如,暫時(shí)還無(wú)法從類(lèi)加載路徑、或相對(duì)于 ServletContext 的路徑來(lái)訪(fǎng)問(wèn)資源,雖然 Java 允許使用特定的 URL 前綴注冊(cè)新的處理類(lèi)(例如已有的 http: 前綴的處理類(lèi)),但是這樣做通常比較復(fù)雜,而且 URL 接口還缺少一些有用的功能,比如檢查所指向的資源是否存在等。
Spring 改進(jìn)了 Java 資源訪(fǎng)問(wèn)的策略,Spring 為資源訪(fǎng)問(wèn)提供了一個(gè) Resource 接口,該接口提供了更強(qiáng)的資源訪(fǎng)問(wèn)能力,Spring 框架本身大量使用了 Resource 接口來(lái)訪(fǎng)問(wèn)底層資源。
public interface Resource extends InputStreamSource { boolean exists(); // 返回 Resource 所指向的資源是否存在 boolean isReadable(); // 資源內(nèi)容是否可讀 boolean isOpen(); // 返回資源文件是否打開(kāi) URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; // 返回資源對(duì)應(yīng)的 File 對(duì)象 long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String var1) throws IOException; String getFilename(); String getDescription(); // 返回資源的描述信息 }
Resource 接口是 Spring 資源訪(fǎng)問(wèn)策略的抽象,它本身并不提供任何資源訪(fǎng)問(wèn)實(shí)現(xiàn),具體的資源訪(fǎng)問(wèn)由該接口的實(shí)現(xiàn)類(lèi)完成——每個(gè)實(shí)現(xiàn)類(lèi)代表一種資源訪(fǎng)問(wèn)策略。
Spring 為 Resource
接口提供的部分實(shí)現(xiàn)類(lèi)如下:
1.lResource
:訪(fǎng)問(wèn)網(wǎng)絡(luò)資源的實(shí)現(xiàn)類(lèi)。
2.assPathResource
:訪(fǎng)問(wèn)類(lèi)加載路徑里資源的實(shí)現(xiàn)類(lèi)。
3.leSystemResource
:訪(fǎng)問(wèn)文件系統(tǒng)里資源的實(shí)現(xiàn)類(lèi)。
4.rvletContextResource
:訪(fǎng)問(wèn)相對(duì)于ServletContext 路徑里的資源的實(shí)現(xiàn)類(lèi):
5.putStreamResource
:訪(fǎng)問(wèn)輸入流資源的實(shí)現(xiàn)類(lèi)。
6.teArrayResource
:訪(fǎng)問(wèn)字節(jié)數(shù)組資源的實(shí)現(xiàn)類(lèi)。
7.itableResource
:寫(xiě)資源文件
類(lèi)圖如下:
AbstractResource 資源抽象類(lèi)實(shí)現(xiàn)了 Resource 接口,為子類(lèi)通用的操作提供了具體實(shí)現(xiàn),非通用的操作留給子類(lèi)實(shí)現(xiàn),所以這里也應(yīng)用了模板方法模式。(只不過(guò)缺少了模板方法)
Resource 不僅可在 Spring 的項(xiàng)目中使用,也可直接作為資源訪(fǎng)問(wèn)的工具類(lèi)使用。意思是說(shuō):即使不使用 Spring 框架,也可以使用 Resource 作為工具類(lèi),用來(lái)代替 URL。
譬如我們可以使用 UrlResource 訪(fǎng)問(wèn)網(wǎng)絡(luò)資源。
也可以通過(guò)其它協(xié)議訪(fǎng)問(wèn)資源,file: 用于訪(fǎng)問(wèn)文件系統(tǒng);http: 用于通過(guò) HTTP 協(xié)議訪(fǎng)問(wèn)資源;ftp: 用于通過(guò) FTP 協(xié)議訪(fǎng)問(wèn)資源等
public class Test { public static void main(String[] args) throws IOException { UrlResource ur = new UrlResource("http://image.laijianfeng.org/hello.txt"); System.out.println("文件名:" + ur.getFilename()); System.out.println("網(wǎng)絡(luò)文件URL:" + ur.getURL()); System.out.println("是否存在:" + ur.exists()); System.out.println("是否可讀:" + ur.isReadable()); System.out.println("文件長(zhǎng)度:" + ur.contentLength()); System.out.println("\n--------文件內(nèi)容----------\n"); byte[] bytes = new byte[47]; ur.getInputStream().read(bytes); System.out.println(new String(bytes)); } } 文件名:hello.txt 網(wǎng)絡(luò)文件URL:http://image.laijianfeng.org/hello.txt 是否存在:true 是否可讀:true 文件長(zhǎng)度:47 --------文件內(nèi)容---------- hello world! welcome to http://laijianfeng.org
Spring Bean 實(shí)例化中的策略模式
Spring實(shí)例化Bean有三種方式:構(gòu)造器實(shí)例化、靜態(tài)工廠(chǎng)實(shí)例化、實(shí)例工廠(chǎng)實(shí)例化
具體實(shí)例化Bean的過(guò)程中,Spring中角色分工很明確,創(chuàng)建對(duì)象的時(shí)候先通
<?xml
version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans <bean id="person" class="com.demo.Person"></bean>
<bean id="personWithParam" class="com.demo.Person">
<constructor-arg name="name" value="小旋鋒"/>
</bean>
<bean id="personWirhParams" class="com.demo.Person">
<constructor-arg name="name" value="小旋鋒"/>
<constructor-arg name="age" value="22"/>
</bean>
</beans>
過(guò) ConstructorResolver 找到對(duì)應(yīng)的實(shí)例化方法和參數(shù),再通過(guò)實(shí)例化策略 InstantiationStrategy 進(jìn)行實(shí)例化,根據(jù)創(chuàng)建對(duì)象的三個(gè)分支( 工廠(chǎng)方法、有參構(gòu)造方法、無(wú)參構(gòu)造方法 ), InstantiationStrategy 提供了三個(gè)接口方法:
public interface InstantiationStrategy { // 默認(rèn)構(gòu)造方法 Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) throws BeansException; // 指定構(gòu)造方法 Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner, Constructor<?> ctor, Object[] args) throws BeansException; // 指定工廠(chǎng)方法 Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner, Object factoryBean, Method factoryMethod, Object[] args) throws BeansException; }
InstantiationStrategy 為實(shí)例化策略接口,扮演抽象策略角色,有兩種具體策略類(lèi),分別為
SimpleInstantiationStrategy
和 CglibSubclassingInstantiationStrategy
leInstantiationStrategy 中對(duì)這三個(gè)方法做了簡(jiǎn)單實(shí)現(xiàn),如果工廠(chǎng)方法實(shí)例化直接用反射創(chuàng)建對(duì)象,如果是構(gòu)造方法實(shí)例化的則判斷是否有 MethodOverrides,如果有無(wú) MethodOverrides 也是直接用反射,如果有 MethodOverrides 就需要用 cglib 實(shí)例化對(duì)象,SimpleInstantiationStrategy 把通過(guò) cglib 實(shí)例化的任務(wù)交給了它的子類(lèi)ibSubclassingInstantiationStrategy。
總結(jié)
1.略類(lèi)之間可以自由切換,由于策略類(lèi)實(shí)現(xiàn)自同一個(gè)抽象,所以他們之間可以自由切換。
2.于擴(kuò)展,增加一個(gè)新的策略對(duì)策略模式來(lái)說(shuō)非常容易,基本上可以在不改變?cè)写a的基礎(chǔ)上進(jìn)行擴(kuò)展。
3.使用多重條件,如果不使用策略模式,對(duì)于所有的算法,必須使用條件語(yǔ)句進(jìn)行連接,通過(guò)條件判斷來(lái)決定使用哪一種算法,在上一篇文章中我們已經(jīng)提到,使用多重條件判斷是非常不容易維護(hù)的。
以上就是java策略模式的詳細(xì)內(nèi)內(nèi)容。也請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java測(cè)試框架Mockito的簡(jiǎn)明教程
這篇文章主要介紹了Java測(cè)試框架Mockito的簡(jiǎn)明教程,Mock 測(cè)試是單元測(cè)試的重要方法之一。本文介紹了基于 Java 語(yǔ)言的 Mock 測(cè)試框架 – Mockito 的使用。,需要的朋友可以參考下2019-06-06SpringAOP實(shí)現(xiàn)日志收集管理功能(步驟詳解)
這篇文章主要介紹了SpringAOP實(shí)現(xiàn)日志收集管理功能,本文分步驟通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03JavaWeb實(shí)現(xiàn)壓縮多個(gè)文件并下載實(shí)例詳解
本文通過(guò)實(shí)例代碼給大家講解了javaweb實(shí)現(xiàn)壓縮多個(gè)文件并下載功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-07-07MyBatis3源碼解析之如何獲取數(shù)據(jù)源詳解
用myBatis3與spring整合的時(shí)候,我們可以通過(guò)多種方式獲取數(shù)據(jù)源,下面這篇文章主要給大家介紹了關(guān)于MyBatis3源碼解析之如何獲取數(shù)據(jù)源的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單計(jì)算器
這篇文章主要介紹了Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單計(jì)算器,文章我圍繞實(shí)現(xiàn)簡(jiǎn)單計(jì)算器的相關(guān)代碼展現(xiàn)全文,具有一定的參考價(jià)值,需要的小伙伴可以參考一下,2022-01-01Spring實(shí)戰(zhàn)之Bean的后處理器操作示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之Bean的后處理器操作,結(jié)合實(shí)例形式詳細(xì)分析了Bean的后處理器相關(guān)配置、操作方法及使用注意事項(xiàng),需要的朋友可以參考下2019-12-12java用arraycopy實(shí)現(xiàn)多擊事件
這篇文章主要介紹了java用arraycopy實(shí)現(xiàn)多擊事件的多種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11SpringBoot詳細(xì)講解視圖整合引擎thymeleaf
這篇文章主要分享了Spring Boot整合使用Thymeleaf,Thymeleaf是新一代的Java模板引擎,類(lèi)似于Velocity、FreeMarker等傳統(tǒng)引擎,關(guān)于其更多相關(guān)內(nèi)容,需要的小伙伴可以參考一下2022-06-06SpringBoot中Bean拷貝及工具類(lèi)封裝的實(shí)現(xiàn)
本文主要介紹了SpringBoot中Bean拷貝及工具類(lèi)封裝的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05