Java編程思想重點筆記(Java開發必看)

来源:http://www.cnblogs.com/aishangJava/archive/2017/05/17/6866076.html
-Advertisement-
Play Games

學習Java的同學註意了!!! 學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入Java學習交流群,群號碼:618528494 我們一起學Java! Java編程思想,Java學習必讀經典,不管是初學者還是大牛都值得一讀,這裡總結書中的重點知識,這些知識不僅經常出現在各大知名公司的筆試面試過程 ...


學習Java的同學註意了!!! 
學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入Java學習交流群,群號碼:618528494  我們一起學Java!

Java編程思想,Java學習必讀經典,不管是初學者還是大牛都值得一讀,這裡總結書中的重點知識,這些知識不僅經常出現在各大知名公司的筆試面試過程中,而且在大型項目開發中也是常用的知識,既有簡單的概念理解題(比如is-a關係和has-a關係的區別),也有深入的涉及RTTI和JVM底層反編譯知識。

 


1. Java中的多態性理解(註意與C++區分)

  • Java中除了static方法和final方法(private方法本質上屬於final方法,因為不能被子類訪問)之外,其它所有的方法都是動態綁定,這意味著通常情況下,我們不必判定是否應該進行動態綁定—它會自動發生。

    • final方法會使編譯器生成更有效的代碼,這也是為什麼說聲明為final方法能在一定程度上提高性能(效果不明顯)。
    • 如果某個方法是靜態的,它的行為就不具有多態性:
      class StaticSuper {
          public static String staticGet() {
              return "Base staticGet()";
          }
      
          public String dynamicGet() {
              return "Base dynamicGet()";
          }
      }
      
      class StaticSub extends StaticSuper {
          public static String staticGet() {
              return "Derived staticGet()";
          }
      
          public String dynamicGet() {
              return "Derived dynamicGet()";
          }
      }
      
      public class StaticPolymorphism {
      
          public static void main(String[] args) {
              StaticSuper sup = new StaticSub();
              System.out.println(sup.staticGet());
              System.out.println(sup.dynamicGet());
          }
      
      }
      

      輸出:

      Base staticGet()
      Derived dynamicGet()

  • 構造函數並不具有多態性,它們實際上是static方法,只不過該static聲明是隱式的。因此,構造函數不能夠被override。

  • 在父類構造函數內部調用具有多態行為的函數將導致無法預測的結果,因為此時子類對象還沒初始化,此時調用子類方法不會得到我們想要的結果。

    class Glyph {
        void draw() {
            System.out.println("Glyph.draw()");
        }
        Glyph() {
            System.out.println("Glyph() before draw()");
            draw();
            System.out.println("Glyph() after draw()");
        }
    }
    
    class RoundGlyph extends Glyph {
        private int radius = 1;
    
        RoundGlyph(int r) {
            radius = r;
            System.out.println("RoundGlyph.RoundGlyph(). radius = " + radius);
        }
    
        void draw() {
            System.out.println("RoundGlyph.draw(). radius = " + radius);
        }
    }
    
    public class PolyConstructors {
    
        public static void main(String[] args) {
            new RoundGlyph(5);
    
        }
    
    }
    


    輸出:

    Glyph() before draw()
    RoundGlyph.draw(). radius = 0
    Glyph() after draw()
    RoundGlyph.RoundGlyph(). radius = 5

為什麼會這樣輸出?這就要明確掌握Java中構造函數的調用順序

(1)在其他任何事物發生之前,將分配給對象的存儲空間初始化成二進位0;
(2)調用基類構造函數。從根開始遞歸下去,因為多態性此時調用子類覆蓋後的draw()方法(要在調用RoundGlyph構造函數之前調用),由於步驟1的緣故,我們此時會發現radius的值為0;
(3)按聲明順序調用成員的初始化方法;
(4)最後調用子類的構造函數。

  • 只有非private方法才可以被覆蓋,但是還需要密切註意覆蓋private方法的現象,這時雖然編譯器不會報錯,但是也不會按照我們所期望的來執行,即覆蓋private方法對子類來說是一個新的方法而非重載方法。因此,在子類中,新方法名最好不要與基類的private方法採取同一名字(雖然沒關係,但容易誤解,以為能夠覆蓋基類的private方法)

  • Java類中屬性域的訪問操作都由編譯器解析,因此不是多態的。父類和子類的同名屬性都會分配不同的存儲空間,如下:

    // Direct field access is determined at compile time.
    class Super {
        public int field = 0;
        public int getField() {
            return field;
        }
    }
    
    class Sub extends Super {
        public int field = 1;
        public int getField() {
            return field;
        }
        public int getSuperField() {
            return super.field;
        }
    }
    
    public class FieldAccess {
    
        public static void main(String[] args) {
            Super sup = new Sub();
            System.out.println("sup.filed = " + sup.field + 
                    ", sup.getField() = " + sup.getField());
            Sub sub = new Sub();
            System.out.println("sub.filed = " + sub.field + 
                    ", sub.getField() = " + sub.getField() + 
                    ", sub.getSuperField() = " + sub.getSuperField());
        }
    
    }
    


    輸出:

    sup.filed = 0, sup.getField() = 1
    sub.filed = 1, sub.getField() = 1, sub.getSuperField() = 0

    Sub子類實際上包含了兩個稱為field的域,然而在引用Sub中的field時所產生的預設域並非Super版本的field域,因此為了得到Super.field,必須顯式地指明super.field。

2. is-a關係和is-like-a關係

  • is-a關係屬於純繼承,即只有在基類中已經建立的方法才可以在子類中被覆蓋,如下圖所示:

    基類和子類有著完全相同的介面,這樣向上轉型時永遠不需要知道正在處理的對象的確切類型,這通過多態來實現。

  • is-like-a關係:子類擴展了基類介面。它有著相同的基本介面,但是他還具有由額外方法實現的其他特性。

    缺點就是子類中介面的擴展部分不能被基類訪問,因此一旦向上轉型,就不能調用那些新方法。

3. 運行時類型信息(RTTI + 反射)

  • 概念
    RTTI:運行時類型信息使得你可以在程式運行時發現和使用類型信息。
  • 使用方式
    Java是如何讓我們在運行時識別對象和類的信息的,主要有兩種方式(還有輔助的第三種方式,見下描述):

    • 一種是“傳統的”RTTI,它假定我們在編譯時已經知道了所有的類型,比如Shape s = (Shape)s1;
    • 另一種是“反射”機制,它運行我們在運行時發現和使用類的信息,即使用Class.forName()
    • 其實還有第三種形式,就是關鍵字instanceof,它返回一個bool值,它保持了類型的概念,它指的是“你是這個類嗎?或者你是這個類的派生類嗎?”。而如果用==或equals比較實際的Class對象,就沒有考慮繼承—它或者是這個確切的類型,或者不是。
  • 工作原理
    要理解RTTI在Java中的工作原理,首先必須知道類型信息在運行時是如何表示的,這項工作是由稱為Class對象的特殊對象完成的,它包含了與類有關的信息。Java送Class對象來執行其RTTI,使用類載入器的子系統實現

無論何時,只要你想在運行時使用類型信息,就必須首先獲得對恰當的Class對象的引用,獲取方式有三種:
(1)如果你沒有持有該類型的對象,則Class.forName()就是實現此功能的便捷途,因為它不需要對象信息;
(2)如果你已經擁有了一個感興趣的類型的對象,那就可以通過調用getClass()方法來獲取Class引用了,它將返回表示該對象的實際類型的Class引用。Class包含很有有用的方法,比如:

package rtti;
interface HasBatteries{}
interface WaterProof{}
interface Shoots{}

class Toy {
    Toy() {}
    Toy(int i) {}
}

class FancyToy extends Toy
implements HasBatteries, WaterProof, Shoots {
    FancyToy() {
        super(1);
    }
}

public class RTTITest {

    static void printInfo(Class cc) {
        System.out.println("Class name: " + cc.getName() + 
                ", is interface? [" + cc.isInterface() + "]");
        System.out.println("Simple name: " + cc.getSimpleName());
        System.out.println("Canonical name: " + cc.getCanonicalName());
    }

    public static void main(String[] args) {
        Class c = null;
        try {
            c = Class.forName("rtti.FancyToy"); // 必須是全限定名(包名+類名)
        } catch(ClassNotFoundException e) {
            System.out.println("Can't find FancyToy");
            System.exit(1);
        }
        printInfo(c);

        for(Class face : c.getInterfaces()) {
            printInfo(face);
        }

        Class up = c.getSuperclass();
        Object obj = null;
        try {
            // Requires default constructor.
            obj = up.newInstance();
        } catch (InstantiationException e) {
            System.out.println("Can't Instantiate");
            System.exit(1);
        } catch (IllegalAccessException e) {
            System.out.println("Can't access");
            System.exit(1);
        }
        printInfo(obj.getClass());
    }

}


輸出:

Class name: rtti.FancyToy, is interface? [false]
Simple name: FancyToy
Canonical name: rtti.FancyToy
Class name: rtti.HasBatteries, is interface? [true]
Simple name: HasBatteries
Canonical name: rtti.HasBatteries
Class name: rtti.WaterProof, is interface? [true]
Simple name: WaterProof
Canonical name: rtti.WaterProof
Class name: rtti.Shoots, is interface? [true]
Simple name: Shoots
Canonical name: rtti.Shoots
Class name: rtti.Toy, is interface? [false]
Simple name: Toy
Canonical name: rtti.Toy

(3)Java還提供了另一種方法來生成對Class對象的引用,即使用類字面常量。比如上面的就像這樣:FancyToy.class;來引用。
這樣做不僅更簡單,而且更安全,因為它在編譯時就會受到檢查(因此不需要置於try語句塊中),並且它根除了對forName方法的引用,所以也更高效。類字面常量不僅可以應用於普通的類,也可以應用於介面、數組以及基本數據類型。


註意:當使用“.class”來創建對Class對象的引用時,不會自動地初始化該Class對象,初始化被延遲到了對靜態方法(構造器隱式的是靜態的)或者非final靜態域(註意final靜態域不會觸發初始化操作)進行首次引用時才執行:。而使用Class.forName時會自動的初始化。

為了使用類而做的準備工作實際包含三個步驟:
- 載入:由類載入器執行。查找位元組碼,並從這些位元組碼中創建一個Class對象
- 鏈接:驗證類中的位元組碼,為靜態域分配存儲空間,並且如果必需的話,將解析這個類創建的對其他類的所有引用。
- 初始化:如果該類具有超類,則對其初始化,執行靜態初始化器和靜態初始化塊。

這一點非常重要,下麵通過一個實例來說明這兩者的區別:

package rtti;
import java.util.Random;
class Initable {
        static final int staticFinal = 47;
        static final int staticFinal2 = ClassInitialization.rand.nextInt(1000);

        static {
            System.out.println("Initializing Initable");
        }
}
class Initable2 {
        static int staticNonFinal = 147;

        static {
            System.out.println("Initializing Initable2");
        }
}
class Initable3 {
        static int staticNonFinal = 74;

        static {
            System.out.println("Initializing Initable3");
        }
}
public class ClassInitialization {

        public static Random rand = new Random(47);

        public static void main(String[] args) {
            // Does not trigger initialization
            Class initable = Initable.class;
            System.out.println("After creating Initable ref");
            // Does not trigger initialization
            System.out.println(Initable.staticFinal);
            // Does trigger initialization(rand() is static method)
            System.out.println(Initable.staticFinal2);

            // Does trigger initialization(not final)
            System.out.println(Initable2.staticNonFinal);

            try {
                Class initable3 = Class.forName("rtti.Initable3");
            } catch (ClassNotFoundException e) {
                System.out.println("Can't find Initable3");
                System.exit(1);
            }
            System.out.println("After creating Initable3 ref");
            System.out.println(Initable3.staticNonFinal);
        }
}


輸出:

After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74


  • RTTI的限制?如何突破? — 反射機制
    如果不知道某個對象的確切類型,RTTI可以告訴你,但是有一個限制:這個類型在編譯時必須已知,這樣才能使用RTTI識別它,也就是在編譯時,編譯器必須知道所有要通過RTTI來處理的類。

可以突破這個限制嗎?是的,突破它的就是反射機制
Class類與java.lang.reflect類庫一起對反射的概念進行了支持,該類庫包含了FieldMethod以及Constructor類(每個類都實現了Member介面)。這些類型的對象是由JVM在運行時創建的,用以表示未知類里對應的成員。這樣你就可以使用Constructor創建新的對象,用get()/set()方法讀取和修改與Field對象關聯的欄位,用invoke()方法調用與Method對象關聯的方法。另外,還可以調用getFields()、getMethods()和getConstructors()等很便利的方法,以返回表示欄位、方法以及構造器的對象的數組。這樣,匿名對象的類信息就能在運行時被完全確定下來,而在編譯時不需要知道任何事情。


####反射與RTTI的區別
當通過反射與一個未知類型的對象打交道時,JVM只是簡單地檢查這個對象,看它屬於哪個特定的類(就像RTTI那樣),在用它做其他事情之前必須先載入那個類的Class對象,因此,那個類的.class文件對於JVM來說必須是可獲取的:要麼在本地機器上,要麼可以通過網路取得。所以RTTI與反射之間真正的區別隻在於:對RTTI來說,編譯器在編譯時打開和檢查.class文件(也就是可以用普通方法調用對象的所有方法);而對於反射機制來說,.class文件在編譯時是不可獲取的,所以是在運行時打開和檢查.class文件。

下麵的例子是用反射機制列印出一個類的所有方法(包括在基類中定義的方法):

package typeinfo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.regex.Pattern;

// Using reflection to show all the methods of a class.
// even if the methods are defined in the base class.
public class ShowMethods {
    private static String usage = 
        "usage: \n" + 
        "ShowMethods qualified.class.name\n" +
        "To show all methods in class or: \n" +
        "ShowMethods qualified.class.name word\n" +
        "To search for methods involving 'word'";

    private static Pattern p = Pattern.compile("\\w+\\.");

    public static void main(String[] args) {
        if(args.length < 1) {
            System.out.println(usage);
            System.exit(0);
        }
        int lines = 0;
        try {
            Class<?> c = Class.forName(args[0]);
            Method[] methods = c.getMethods();
            Constructor[] ctors = c.getConstructors();
            if(args.length == 1) {
                for(Method method : methods) {
                    System.out.println(p.matcher(method.toString()).replaceAll(""));
                }
                for(Constructor ctor : ctors) {
                    System.out.println(p.matcher(ctor.toString()).replaceAll(""));
                }
                lines = methods.length + ctors.length;
            } else {
                for(Method method : methods) {
                    if(method.toString().indexOf(args[1]) != -1) {
                        System.out.println(p.matcher(method.toString()).replaceAll(""));
                        lines++;
                    }
                }
                for(Constructor ctor : ctors) {
                    if(ctor.toString().indexOf(args[1]) != -1) {
                        System.out.println(p.matcher(ctor.toString()).replaceAll(""));
                        lines++;
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            System.out.println("No such Class: " + e);
        }

    }
}

輸出:

public static void main(String[])
public final native void wait(long) throws InterruptedException
public final void wait() throws InterruptedException
public final void wait(long,int) throws InterruptedException
public boolean equals(Object)
public String toString()
public native int hashCode()
public final native Class getClass()
public final native void notify()
public final native void notifyAll()
public ShowMethods()

4. 代理模式與Java中的動態代理

  • 代理模式
    在任何時刻,只要你想要將額外的操作從“實際”對象中分離到不同的地方,特別是當你希望能夠很容易地做出修改,從沒有使用額外操作轉為使用這些操作,或者反過來時,代理就顯得很有用(設計模式的關鍵是封裝修改)。例如,如果你希望跟蹤對某個類中方法的調用,或者希望度量這些調用的開銷,那麼你應該怎樣做呢?這些代碼肯定是你不希望將其合併到應用中的代碼,因此代理使得你可以很容易地添加或移除它們。

    interface Interface {
        void doSomething();
        void somethingElse(String arg);
    }
    
    class RealObject implements Interface {
    
        @Override
        public void doSomething() {
            System.out.println("doSomething.");
        }
    
        @Override
        public void somethingElse(String arg) {
            System.out.println("somethingElse " + arg);
        }
    }
    
    class SimpleProxy implements Interface {
    
        private Interface proxy;
    
        public SimpleProxy(Interface proxy) {
            this.proxy = proxy;
        }
    
        @Override
        public void doSomething() {
            System.out.println("SimpleProxy doSomething.");
            proxy.doSomething();
        }
    
        @Override
        public void somethingElse(String arg) {
            System.out.println("SimpleProxy somethingElse " + arg);
            proxy.somethingElse(arg);
        }
    }
    
    public class SimpleProxyDemo {
    
        public static void consumer(Interface iface) {
            iface.doSomething();
            iface.somethingElse("bonobo");
        }
    
        public static void main(String[] args) {
            consumer(new RealObject());
            consumer(new SimpleProxy(new RealObject()));
        }
    
    }
    


    輸出:

    doSomething.
    somethingElse bonobo
    SimpleProxy doSomething.
    doSomething.
    SimpleProxy somethingElse bonobo
    somethingElse bonobo

  • 動態代理
    Java的動態代理比代理的思想更向前邁進了一步,因為它可以動態地創建代理並動態地處理對所代理方法的調用。

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    class DynamicProxyHandler implements InvocationHandler {
    
        private Object proxy;
    
        public DynamicProxyHandler(Object proxy) {
            this.proxy = proxy;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("*** proxy: " + proxy.getClass() +
                    ". method: " + method + ". args: " + args);
            if(args != null) {
                for(Object arg : args)
                    System.out.println(" " + arg);
            }
            return method.invoke(this.proxy, args);
        }
    }
    
    public class SimpleDynamicProxy {
    
        public static void consumer(Interface iface) {
            iface.doSomething();
            iface.somethingElse("bonobo");
        }
    
        public static void main(String[] args) {
            RealObject real = new RealObject();
            consumer(real);
            // insert a proxy and call again:
            Interface proxy = (Interface)Proxy.newProxyInstance(
                    Interface.class.getClassLoader(), 
                    new Class[]{ Interface.class },
                    new DynamicProxyHandler(real));
    
            consumer(proxy);
        }
    
    }
    


    輸出:

    doSomething.
    somethingElse bonobo
    *** proxy: class typeinfo.\$Proxy0. method: public abstract void typeinfo.Interface.doSomething(). args: null
    doSomething.
    *** proxy: class typeinfo.\$Proxy0. method: public abstract void typeinfo.Interface.somethingElse(java.lang.String). args: [Ljava.lang.Object;@6a8814e9
    bonobo
    somethingElse bonobo

5. 即時編譯器技術 — JIT

Java虛擬機中有許多附加技術用以提升速度,尤其是與載入器操作相關的,被稱為“即時”(Just-In-Time,JIT)編譯器的技術。這種技術可以把程式全部或部分翻譯成本地機器碼(這本來是JVM的工作),程式運行速度因此得以提升。當需要裝載某個類時,編譯器會先找到其.class文件,然後將該類的位元組碼裝入記憶體。此時,有兩種方案可供選擇:
(1)一種就是讓即時編譯器編譯所有代碼。但這種做法有兩個缺陷:這種載入動作散落在整個程式生命周期內,累加起來要花更多時間;並且會增加可執行代碼的長度(位元組碼要比即時編譯器展開後的本地機器碼小很多),這將導致頁面調度,從而降低程式速度。
(2)另一種做法稱為惰性評估(lazy evaluation),意思是即時編譯器只在必要的時候才編譯代碼,這樣,從不會被執行的代碼也許就壓根不會被JIT所編譯。新版JDK中的Java HotSpot技術就採用了類似方法,代碼每次被執行的時候都會做一些優化,所以執行的次數越多,它的速度就越快。

6. 訪問控制許可權

  • Java訪問許可權修飾詞:public、protected、包訪問許可權(預設訪問許可權,有時也稱friendly)和private。
  • 包訪問許可權:當前包中的所有其他類對那個成員具有訪問許可權,但對於這個包之外的所有類,這個成員卻是private。
  • protected:繼承訪問許可權。有時基類的創建者會希望有某個特定成員,把對它的訪問許可權賦予派生類而不是所有類。這就需要protected來完成這一工作。protected也提供包訪問許可權,也就是說,相同包內的其他類都可以訪問protected元素。protected指明“就類用戶而言,這是private的,但對於任何繼承於此類的導出類或其他任何位於同一個包內的類來說,它卻是可以訪問的”。比如:
    基類:
    package access.cookie;
    public class Cookie {
        public Cookie() {
            System.out.println("Cookie Constructor");
        }
    
        void bite() {  // 包訪問許可權,其它包即使是子類也不能訪問它
            System.out.println("bite");
        }
    }
    

    子類:
    package access.dessert;
    import access.cookie.Cookie;
    
    public class ChocolateChip extends Cookie {
    
        public ChocolateChip() {
            System.out.println("ChocolateChip constructor");
        }
    
        public void chomp() {
            bite();  // error, the method bite() from the type Cookie is not visible
        }
    }
    

    可以發現子類並不能訪問基類的包訪問許可權方法。此時可以將Cookie中的bite指定為public,但這樣做所有的人就都有了訪問許可權,為了只允許子類訪問,可以將bite指定為protected即可。

7. 組合和繼承之間的選擇

  • 組合和繼承都允許在新的類中放置子對象,組合是顯式的這樣做,而繼承則是隱式的做。
  • 組合技術通常用於想在新類中使用現有類的功能而非它的介面這種情形。即在新類中嵌入某個對象,讓其實現所需要的功能,但新類的用戶看到的只是為新類所定義的介面,而非所嵌入對象的介面。為取得此效果,需要在新類中嵌入一個現有類的private對象。但有時,允許類的用戶直接訪問新類中的組合成分是極具意義的,即將成員對象聲明為public。如果成員對象自身都隱藏了具體實現,那麼這種做法是安全的。當用戶能夠瞭解到你正在組裝一組部件時,會使得埠更加易於理解。比如Car對象可由public的Engine對象、Wheel對象、Window對象和Door對象組合。但務必要記得這僅僅是一個特例,一般情況下應該使域成為private
  • 在繼承的時候,使用某個現有類,並開發一個它的特殊版本。通常,這意味著你在使用一個通用類,併為了某種特殊需要而將其特殊化。稍微思考一下就會發現,用一個“交通工具”對象來構成一部“車子”是毫無意義的,因為“車子”並不包含“交通工具”,它僅是一種交通工具(is-a關係)。
  • “is-a”(是一個)的關係是用繼承來表達的,而“has-a”(有一個)的關係則是用組合來表達的
  • 到底是該用組合還是繼承,一個最清晰的判斷方法就是問一問自己是否需要從新類向基類進行向上轉型,需要的話就用繼承,不需要的話就用組合方式。

8. final關鍵字

  • 對final關鍵字的誤解
    當final修飾的是基本數據類型時,它指的是數值恆定不變(就是編譯期常量,如果是static final修飾,則強調只有一份),而對對象引用而不是基本類型運用final時,其含義會有一點令人迷惑,因為用於對象引用時,final使引用恆定不變,一旦引用被初始化指向一個對象,就無法再把它指向另一個對
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一元購製作 一元奪寶APP開發 隨著一元購越來越火爆,參與人數增長很快,消費者也很喜歡這樣的消費模式,一人一塊錢,大伙湊錢買一件商品,然後等待“幸運號碼”揭曉,該商品的最後屬於其中一個幸運號碼所得到,這個新型的消費模式很受消費者的喜愛,商家也是看中了這個商機,也紛紛加入到了這個行業當中來。 一元奪寶 ...
  • 平臺簡介 Jeesz是一個分散式的框架,提供項目模塊化、服務化、熱插拔的思想,高度封裝安全性的Java EE快速開發平臺。 Jeesz本身集成Dubbo服務管控、Zookeeper註冊中心、Redis分散式緩存技術、FastDFS分散式文件系統、ActiveMQ非同步消息中間件、Nginx負載均衡等分 ...
  • 使用JPA中@Query 註解實現update 操作,代碼如下: @Transactional@Modifying(clearAutomatically = true)@Query(value = "update info p set p.status =?1 where p.id = ?2",na ...
  • PHP編程中經常需要用到伺服器的一些資料,特把$_SERVER的詳細參數整理下: $_SERVER['PHP_SELF'] 當前正在執行腳本的文件名,與 document root相關。 $_SERVER['argv'] 傳遞給該腳本的參數。 $_SERVER['argc'] 包含傳遞給程式的命令行 ...
  • java中的數據類型,可分為兩類:1.基本數據類型,也稱原始數據類型。byte,short,char,int,long,float,double,boolean 他們之間的比較,應用雙等號(==),比較的是他們的值。 2.複合數據類型(類) 當他們用(==)進行比較的時候,比較的是他們在記憶體中的存放 ...
  • ////////////////////////////////////////////////////////////////// 記性不好的可以收藏下: 1,下拉框: 稍微解釋一下: 1.select[@name='country'] option[@selected] 表示具有name 屬性, ...
  • SRC_DIR := src/ INC_DIR := include/ OBJ_DIR := build/ DEP_DIR := build/ EXE_DIR := build/ SRC := $(notdir $(shell ls $(SRC_DIR)*.cpp)) OBJ := $(patsub... ...
  • Golang 支持在一個平臺下生成另一個平臺可執行程式的交叉編譯功能。 Mac下編譯Linux, Windows平臺的64位可執行程式: Linux下編譯Mac, Windows平臺的64位可執行程式: Windows下編譯Mac, Linux平臺的64位可執行程式: GOOS:目標可執行程式運行操 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...