架構(gòu)師日記-聊聊開發(fā)必掌握的那些實(shí)踐技能
構(gòu)建語言生態(tài)的優(yōu)勢,彌補(bǔ)其存在短板,始終是編程語言的一個(gè)演進(jìn)方向。
1999年,美國太空總署(NASA)的火星任務(wù)失?。涸谶@次任務(wù)中,火星氣候探測者號(hào)上的飛行系統(tǒng)軟件使用公制單位牛頓計(jì)算推進(jìn)器動(dòng)力,而地面人員輸入的方向校正量和推進(jìn)器參數(shù)則使用英制單位磅力,導(dǎo)致探測器進(jìn)入大氣層的高度有誤,最終瓦解碎裂。
2.1 關(guān)于命名
-
蛇形命名法(snake case):又叫下劃線命名法,使用下劃線,單詞小寫,比如:my_system; -
駝峰命名法(camel case):按照單詞首字母區(qū)分大小寫,又可細(xì)分為大駝峰命名法和小駝峰命名法,比如:MySystem,mySystem; -
匈牙利命名法(HN case):屬性+類型+描述,比如:nLength,g_cch,hwnd; -
帕斯卡命名法(Pascal case):全部首字母大寫,等同于大駝峰命名法,比如:MySystem; -
脊柱命名法(spinal case):使用中劃線,比如:my-system; -
自由命名法(studly caps):大小寫混雜,無簡明規(guī)則,比如:mySYSTEM,MYSystem;
2.1.1 命名字典
2.1.2 命名實(shí)踐
-
項(xiàng)目名全部小寫; -
包名全部小寫; -
類名首字母大寫,其余組成詞首字母依次大寫; -
變量名,方法名首字母小寫,如果名稱由多個(gè)單詞組成,除首字母外的每個(gè)單詞首字母都要大寫; -
常量名全部大寫;
-
自帶混淆功能的變量名:String zhrmghg = "極致縮寫型"; -
沒有意義的萬能變量名:String a,b,c="愛誰誰型"; -
長串拼音變量名:String HuaBuHua = "考古型"; -
各種符號(hào)混用:String $my_first_name_ = "打死記不住型"; -
大小寫,數(shù)字,縮寫混亂:String waitRPCResponse1 = "極易出錯(cuò)型";
public <PropertyType> get<PropertyName>();public void set<PropertyName>(<PropertyType> p)
public boolean is<PropertyName>();public void set<PropertyName>(boolean p)
-
一類是同一個(gè)jar包出現(xiàn)了多個(gè)不同的版本。應(yīng)用選擇了錯(cuò)誤的版本導(dǎo)致jvm加載不到需要的類或者加載了錯(cuò)誤版本的類;(借助maven管理工具相對(duì)容易解決) -
另一類是不同的jar包出現(xiàn)了類路徑相同的類,同樣的類出現(xiàn)在不同的依賴jar里,由于jar加載的先后順序?qū)е铝薐VM加載了錯(cuò)誤版本的類;(比較難以解決)
public class SkuKey implements Serializable {private Long stationNo;private Long skuId;// 省略get/set方法}
2.2 關(guān)于注釋
2.2.1 好的注釋
-
系統(tǒng)注釋:通過README.md文件體現(xiàn)宏觀的功能和架構(gòu)實(shí)現(xiàn); -
包注釋:通過package-info文件體現(xiàn)模塊職責(zé)邊界,另外該文件也支持聲明友好類,包常量以及為標(biāo)注在包上的注解(Annotation)提供便利; -
類注釋:主要體現(xiàn)功能職責(zé),版本支持,作者歸屬,應(yīng)用示例等相關(guān)信息; -
方法注釋:關(guān)注入?yún)?,出參,異常處理聲明,使用場景舉例等相關(guān)內(nèi)容; -
代碼塊和代碼行注釋:主要體現(xiàn)邏輯意圖,閉坑警示,規(guī)劃TODO,放大關(guān)注點(diǎn)等細(xì)節(jié)內(nèi)容;
2.2.2 壞的注釋
-
冗余式:如果一個(gè)函數(shù),讀者能夠很容易的就讀出來代碼要表達(dá)的意思,注釋就是多余的; -
錯(cuò)誤式:如果注釋地不清楚,甚至出現(xiàn)歧義,那還不如不寫; -
簽名式:類似“add by liuhuiqing 2023-08-05”這種注釋,容易過期失效而且不太可信(不能保證所有人每次都采用這種方式注釋),其功能完全可以由git代碼管理工具來實(shí)現(xiàn); -
長篇大論式:代碼塊里,夾雜了大篇幅的注釋,不僅影響代碼閱讀,而且維護(hù)困難; -
非本地注釋:注釋應(yīng)該在離代碼實(shí)現(xiàn)最近的地方,比如:被調(diào)用的方法注釋就由方法本身來維護(hù),調(diào)用方無需對(duì)方法做詳細(xì)的說明; -
注釋掉的代碼:無用的代碼應(yīng)該刪除,而不是注釋。歷史記錄交給git等代碼管理工具來維護(hù);
2.3 關(guān)于分層
2.3.1 系統(tǒng)分層
2.3.2 軟件伸縮性
軟件伸縮性指的是軟件系統(tǒng)在面對(duì)負(fù)載壓力時(shí),能夠保持原有性能并擴(kuò)展以支持更多任務(wù)的能力。
伸縮性可以有兩個(gè)方面,垂直伸縮性和水平伸縮性,垂直伸縮性是通過在同一個(gè)業(yè)務(wù)單元中增加資源來提高系統(tǒng)的吞吐量,比如增加服務(wù)器cpu的數(shù)量,增加服務(wù)器的內(nèi)存等。水平伸縮性是通過增加多個(gè)業(yè)務(wù)單元資源,使得所有的業(yè)務(wù)單元邏輯上就像是一個(gè)單元一樣。比如ejb分布式組件模型,微服務(wù)組件模型等都屬于此種方式。
2.4 小結(jié)
3.1 類定義
3.1.1 常量定義
public enum Color {RED, GREEN, BLUE;}
public class MyClass {public static final int MAX_VALUE = 100;}
3.1.2 工具類
public abstract class ObjectHelper {public static boolean isEmpty(String str) {return str == null || str.length() == 0;}}
3.1.3 JavaBean
public class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}
import lombok.Data;(chain = true)public class Person {private String name;private int age;}
3.1.4 不可變類
public final class String implements Serializable, Comparable<String>, CharSequence {}
java.lang.Stringjava.lang.Mathjava.lang.Booleanjava.lang.Characterjava.util.Datejava.sql.Datejava.lang.Systemjava.lang.ClassLoader
3.1.5 匿名內(nèi)部類
直接作為參數(shù)傳遞給方法或構(gòu)造函數(shù);
用于實(shí)現(xiàn)某個(gè)接口或抽象類的匿名實(shí)例;
public class Example {public static void main(String[] args) {// 創(chuàng)建一個(gè)匿名內(nèi)部類Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("Hello, World!");}};// 調(diào)用匿名內(nèi)部類的方法runnable.run();}}
3.1.6 聲明類
-
AutoCloseable:表示實(shí)現(xiàn)了該接口的類可以被自動(dòng)關(guān)閉,通常用于資源管理。 -
Comparable:表示實(shí)現(xiàn)了該接口的類可以與其他實(shí)現(xiàn)了該接口的對(duì)象進(jìn)行比較。 -
Callable:表示實(shí)現(xiàn)了該接口的類可以作為參數(shù)傳遞給線程池,并返回結(jié)果。 -
Cloneable:表示實(shí)現(xiàn)了該接口的類可以被克隆。 -
Enum:表示實(shí)現(xiàn)了該接口的類是一個(gè)枚舉類型。 -
Iterable:表示實(shí)現(xiàn)了該接口的類可以迭代。 -
Runnable:表示實(shí)現(xiàn)了該接口的類可以作為線程運(yùn)行。 -
Serializable:表示實(shí)現(xiàn)了該接口的類可以被序列化和反序列化。 -
interface:表示實(shí)現(xiàn)了該接口的類是一個(gè)接口,可以包含方法聲明。 -
Annotation:表示實(shí)現(xiàn)了該接口的類是一個(gè)注解,可以用于元數(shù)據(jù)描述。
3.1.7 Record 類
/*** 關(guān)鍵定義的類是不可變類* 將所有成員變量通過參數(shù)的形式定義* 默認(rèn)會(huì)生成全部參數(shù)的構(gòu)造方法* @param name* @param age*/public record Person(String name, int age) {public Person{if(name == null){throw new IllegalArgumentException("提供緊湊的方式進(jìn)行參數(shù)校驗(yàn)");}}/*** 定義的類中可以定義靜態(tài)方法* @param name* @return*/public static Person of(String name) {return new Person(name, 18);}}
Person person = new Person("John", 30);// Person person = Person.of("John");String name = person.name();int age = person.age();
public List<Person> sortPeopleByAge(List<Person> people) {record Data(Person person, int age){};return people.stream().map(person -> new Data(person, computAge(person))).sorted((d1, d2) -> Integer.compare(d2.age(), d1.age())).map(Data::person).collect(toList());}public int computAge(Person person) {return person.age() - 1;}
3.1.8 密封類
-
final修飾類,這樣類就無法被繼承了; -
package-private類,可以控制只能被同一個(gè)包下的類繼承;
sealed class SealedClass permits SubClass1, SubClass2 {}class SubClass1 extends SealedClass {}class SubClass2 extends SealedClass {}
3.2 方法定義
3.2.1 構(gòu)造方法
public class MyClass {private int myInt;private String myString;// 構(gòu)造方法public MyClass(int myInt, String myString) {this.myInt = myInt;this.myString = myString;}}
3.2.2 方法重寫
class Animal {public void makeSound() {System.out.println("Animal is making a sound");}}class Cat extends Animal {public void makeSound() {System.out.println("Meow");}}public class Main {public static void main(String[] args) {Animal myCat = new Cat();myCat.makeSound(); // 輸出 "Meow"}}
3.2.3 方法重載
public class Calculator {public int add(int a, int b) {return a + b;}public double add(double a, double b) {return a + b;}}public class Main {public static void main(String[] args) {Calculator calculator = new Calculator();int result1 = calculator.add(2, 3);double result2 = calculator.add(2.5, 3.5);System.out.println(result1); // 輸出 5System.out.println(result2); // 輸出 6.0}}
3.2.4 匿名方法
public static void main(String args[]) {List<String> names = Arrays.asList("hello", "world");// 使用 Lambda 表達(dá)式作為參數(shù)傳遞給 forEach 方法names.forEach((String name) -> System.out.println("Name: " + name));// 使用 Lambda 表達(dá)式作為獨(dú)立表達(dá)式使用Predicate<String> nameLengthGreaterThan5 = (String name) -> name.length() > 5;boolean isLongName = nameLengthGreaterThan5.test("John");System.out.println("Is long name? " + isLongName);}
3.3 對(duì)象定義
3.3.1 單例對(duì)象
-
控制資源的使用:通過線程同步來控制資源的并發(fā)訪問。 -
控制實(shí)例產(chǎn)生的數(shù)量:達(dá)到節(jié)約資源的目的。 -
作為通信媒介使用:也就是數(shù)據(jù)共享,它可以在不建立直接關(guān)聯(lián)的條件下,讓多個(gè)不相關(guān)的兩個(gè)線程或者進(jìn)程之間實(shí)現(xiàn)通信。
public enum Singleton {INSTANCE;public void someMethod() {// ...其他代碼...}}
3.3.2 不可變對(duì)象
-
將對(duì)象的狀態(tài)存儲(chǔ)在不可變對(duì)象中:String、Integer等就是內(nèi)置的不可變對(duì)象類型; -
將對(duì)象的狀態(tài)存儲(chǔ)在final變量中:final變量一旦被賦值就不能被修改; -
將對(duì)象的所有屬性都設(shè)為不可變對(duì)象:這樣就可以確保整個(gè)對(duì)象都是不可變的;
Collections.unmodifiableList(new ArrayList<>());
3.3.3 元組對(duì)象
public class Pair<A,B> {public final A first;public final B second;public Pair(A a, B b) {this.first = a;this.second = b;}public A getFirst() {return first;}public B getSecond() {return second;}}
public class Triplet<A,B,C> extends Pair<A,B>{public final C third;public Triplet(A a, B b, C c) {super(a, b);this.third = c;}public C getThird() {return third;}public static void main(String[] args) {// 表示姓名,性別,年齡Triplet<String,String,Integer> triplet = new Triplet("John","男",18);// 獲得姓名String name = triplet.getFirst();}}
public class Tuple<E> {private final E[] elements;public Tuple(E... elements) {this.elements = elements;}public E get(int index) {return elements[index];}public int size() {return elements.length;}public static void main(String[] args) {// 表示姓名,性別,年齡Tuple<String> tuple = new Tuple<>("John", "男", "18");// 獲得姓名String name = tuple.get(0);}}
-
存儲(chǔ)多個(gè)數(shù)據(jù)元素:Tuple可以存儲(chǔ)多個(gè)不同類型的數(shù)據(jù)元素,這些元素可以是基本類型、對(duì)象類型、數(shù)組等; -
簡化代碼:Tuple可以使代碼更加簡潔,減少重復(fù)代碼的編寫。通過Tuple,我們可以將多個(gè)變量打包成一個(gè)對(duì)象,從而減少了代碼量; -
提高代碼可讀性:Tuple可以提高代碼的可讀性。通過Tuple,我們可以將多個(gè)變量打包成一個(gè)對(duì)象,從而使代碼更加易讀; -
支持函數(shù)返回多個(gè)值:Tuple可以支持函數(shù)返回多個(gè)值。在Java中,函數(shù)只能返回一個(gè)值,但是通過Tuple,我們可以將多個(gè)值打包成一個(gè)對(duì)象返回;
NamedTuple namedTuple = Tuples.named("person", "name", "age");
3.3.4 臨時(shí)對(duì)象
-
盡量重用對(duì)象。由于系統(tǒng)不僅要花時(shí)間生成對(duì)象,以后可能還需花時(shí)間對(duì)這些對(duì)象進(jìn)行垃圾回收和處理,因此,生成過多的對(duì)象將會(huì)給程序的性能帶來很大的影響,重用對(duì)象的策略有緩存對(duì)象,也可以針對(duì)具體場景進(jìn)行定向優(yōu)化,比如使用StringBuffer代替字符串拼接的方式; -
盡量使用局部變量。調(diào)用方法時(shí)傳遞的參數(shù)以及在調(diào)用中創(chuàng)建的臨時(shí)變量都保存在棧中,速度較快。其他變量,如靜態(tài)變量、實(shí)例變量等,都在堆中創(chuàng)建,速度較慢; -
分代收集。分代垃圾回收策略,是基于這樣一個(gè)事實(shí):不同的對(duì)象的生命周期是不一樣的。因此,不同生命周期的對(duì)象可以采取不同的收集方式,以便提高回收效率;
3.3.5 Valhalla
性能增強(qiáng)通過展平對(duì)象圖和移除間接來解決。這將獲得更高效的內(nèi)存布局和更少的分配和垃圾回收。
當(dāng)用作泛型類型時(shí),原語和對(duì)象具有更相似的行為,這是更好的抽象。
評(píng)論
圖片
表情
