2018-03-18 部分問題日記

sql中in和exist兩者的區(qū)別

  1. in和exists一般搭配子查詢來使用,in的話也可以單獨(dú)的使用in(a,b,c...)這種方式來使用;
  2. in關(guān)鍵字會(huì)先執(zhí)行子查詢即對(duì)內(nèi)表的查詢,再與外表做笛卡爾積(即若外表有1000條記錄,內(nèi)表有100,則會(huì)生成1000*100條記錄),再根據(jù)條件篩選數(shù)據(jù),而exists會(huì)先執(zhí)行外表查詢,再進(jìn)行內(nèi)外的條件判斷篩選結(jié)果(外表結(jié)果集為100,則內(nèi)表的子查詢會(huì)執(zhí)行100次);
  3. 兩者對(duì)索引的使用也存在不同,使用in關(guān)鍵字時(shí),使用的是外表的索引,而exists則是使用的內(nèi)表的索引;所以當(dāng)外表較大,使用外表索引,子查詢內(nèi)表結(jié)果較小的時(shí)候,使用in的效率會(huì)更高,而當(dāng)外表較小,而子查詢內(nèi)表結(jié)果較大,且子查詢使用索引的時(shí)候,使用exists效率會(huì)更高,若內(nèi)外表查詢結(jié)果集相差不大,兩者效率基本相持,可根據(jù)所使用的索引來確定具體使用的關(guān)鍵字;
  4. in中子查詢結(jié)果會(huì)放入內(nèi)存,進(jìn)行條件判斷是在內(nèi)存中進(jìn)行的,而exists每一次的內(nèi)外條件相比較是在數(shù)據(jù)庫中完成的;

舉個(gè)例子,下列有如下兩個(gè)表


t_user

t_order表

有如下兩條SQL

--使用in關(guān)鍵字
SELECT *
FROM t_user 
WHERE id IN (
    SELECT user_id
    FROM t_order 
);

--使用exists關(guān)鍵字
SELECT t.*
FROM t_user t
WHERE EXISTS (
    SELECT tt.user_id
    FROM t_order tt
    WHERE t.id = tt.user_id
)

最后輸出的結(jié)果是一致的


sql的結(jié)果

IN

首先分析使用in關(guān)鍵字的sql,執(zhí)行這條sql首先會(huì)先執(zhí)行子查詢即內(nèi)表t_order表的查詢

SELECT user_id FROM t_order

然后與外表的查詢生成一個(gè)笛卡爾積


生成的結(jié)果集(未篩選)

然后根據(jù)sql中的條件將t_user的id和t_order的user_id進(jìn)行比較,不相等的記錄被刪除,最后輸出對(duì)應(yīng)的結(jié)果集。

EXISTS

使用exists的語句,與in不同,他會(huì)默認(rèn)先查詢外表的查詢

SELECT t.* FROM t_user t
外表查詢結(jié)果集

然后在根據(jù)外表查詢結(jié)果的每一行,與子查詢內(nèi)表執(zhí)行一次,若子查詢的條件符合(本例sql即t_user的id和t_order的user_id相等),則返回true,若不符合則返回false,則刪除外表查詢結(jié)果集中的一行,最后完成篩選

EXISTS (
    SELECT tt.user_id
    FROM t_order tt
    WHERE t.id = tt.user_id
)

例子中外表和內(nèi)表的查詢數(shù)據(jù)都差不多,但是外表查詢用到了主鍵id,主鍵同時(shí)也是默認(rèn)創(chuàng)建的索引,而內(nèi)表查詢中未使用索引,可使用in關(guān)鍵字

注意:in關(guān)鍵字不對(duì)null進(jìn)行處理,not in是不會(huì)使用索引的,會(huì)對(duì)內(nèi)外表進(jìn)行全掃描,所以效率極低,而not exists仍會(huì)使用內(nèi)表的索引

2、Java反射機(jī)制介紹

JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語言的反射機(jī)制。
Java中使用反射一般是通過Class類這個(gè)對(duì)象來完成,JavaAPI中的描述如下:

Class對(duì)象

這個(gè)類沒有構(gòu)造方法,Class 對(duì)象是在加載類時(shí)由 Java 虛擬機(jī)以及通過調(diào)用類加載器中的defineClass 方法自動(dòng)構(gòu)造的。Class對(duì)象下有很多方法,下面介紹一部分常用的方法。
參考:http://blog.csdn.net/sinat_38259539/article/details/71799078

String str = new String("demo");//新建一個(gè)String對(duì)象
//常用的Class對(duì)象獲取方式
Class cla = str.getClass();
cla = String.class;
cla = Class.forName("java.lang.String"); //通過類的真實(shí)路徑來返回一個(gè)Class類,一般使用這個(gè),可以動(dòng)態(tài)獲取所需要的Class對(duì)象

Object obj = cla.newInstance(); //通過newInstance返回實(shí)例

//獲取對(duì)應(yīng)類、接口的構(gòu)造函數(shù)
Constructor[] cons  =cla.getConstructors(); //獲取所有的public的構(gòu)造函數(shù)
Constructor con  = cla.getConstructor(int.class); //獲取對(duì)應(yīng)參數(shù)類型的public構(gòu)造函數(shù),class入?yún)⒖勺?cons  = cla.getDeclaredConstructors(); //獲取所有構(gòu)造函數(shù)
con  = cla.getDeclaredConstructors(char.class); //獲取對(duì)應(yīng)入?yún)㈩愋偷乃袠?gòu)造函數(shù)

//獲取對(duì)應(yīng)類、接口的成員變量
Field[] fields = cla.getFields(); //獲取所有的public成員變量
Field field = cla.getField("demo"); //獲取對(duì)應(yīng)名稱public成員變量
field.set(obj, "demo"); //對(duì)應(yīng)靜態(tài)變量進(jìn)行賦值,參數(shù):1、實(shí)例,2、修改后的數(shù)據(jù)
fields = cla.getDeclaredFields();
field = cla.getDeclaredField("demo");

//獲取類、接口下的成員方法
Method[] methods = cla.getMethods(); //獲取所有的public成員方法
Method method = cla.getMethod("demo", String.class); //獲取對(duì)應(yīng)名稱的public成員方法
methods = cla.getDeclaredMethods();
method = cla.getDeclaredMethod("demo", String.class);
method.invoke(obj, "demo"); //調(diào)用方法,入?yún)ⅲ旱谝粋€(gè)實(shí)例,第二個(gè)對(duì)應(yīng)的是入?yún)? 
//調(diào)用main函數(shù)
method = cla.getMethod("main", String[] args);
method.invoke(obj, (Object)new String[]{"d","e","m","o"});

3、獲得實(shí)例中的new和newInstance的區(qū)別

  1. 兩者加載的構(gòu)造器不同,new關(guān)鍵字可以調(diào)用任意的pulbic構(gòu)造器,newInstance只能調(diào)用無參構(gòu)造器;
  2. 用new關(guān)鍵字時(shí),類可以沒有被加載,而使用newInstance必須保證類已經(jīng)被加載了;
  3. new,強(qiáng)類型,相對(duì)高效,newInstance,弱類型,低效率;
  4. new關(guān)鍵字完成了類加載、類的實(shí)例化,而newInstance則需要手動(dòng)完成類加載,Class.forName("java.lang.String")這行代碼完成了類的加載;
  5. newInstance可以通過傳入ClassName來動(dòng)態(tài)的加載類的實(shí)例,而不需要顯式的調(diào)用類的構(gòu)造器
//用new創(chuàng)建對(duì)象,可以調(diào)用無參或者有參的public構(gòu)造函數(shù)
String str1 = new String();

//用newInstance
Class cla = Class.forName("java.lang.String"); //初始化,完成類的加載
Object obj = cla.newInstance(); //實(shí)例化
String str2 = (String)obj; //向下轉(zhuǎn)型為子類

4、單例模式中餓漢模式和懶漢模式的區(qū)別

共同特點(diǎn):構(gòu)造器為私有化
(1)、懶漢模式:延遲加載,當(dāng)被使用時(shí)才會(huì)加載,“時(shí)間換空間策略”,單純的懶漢模式是非線程安全的,若有多個(gè)線程同時(shí)進(jìn)入getInstance方法,線程1先進(jìn)入if代碼塊后,線程二同時(shí)控制代碼,會(huì)造成兩個(gè)實(shí)例被返回,可以通過使用靜態(tài)內(nèi)部類(靜態(tài)內(nèi)部類在外部類加載時(shí)會(huì)進(jìn)行初始化)或者雙重檢查(靜態(tài)變量上需添加volatile,由于指令重排問題會(huì)導(dǎo)致線程不安全)的方式來達(dá)到線程安全的要求
(2)、餓漢模式:類加載時(shí),實(shí)例已經(jīng)完成,存放于內(nèi)存中,“空間換時(shí)間”策略,線程安全

餓漢模式

public class SingletonDemo1(){
      private static SingletonDemo1 singletonDemo1 = new SingletonDemo1();      
      private SingletonDemo1(){
            //私有構(gòu)造函數(shù)
      }
      private static SingletonDemo1 getInstance(){
              return singletonDemo1;
      }
}

懶漢模式

public class SingletonDemo2(){
      private static SingletonDemo2 singletonDemo2 = null;      
      private SingletonDemo2(){
            //私有構(gòu)造函數(shù)
      }
      private static SingletonDemo2 getInstance(){
             if(singletonDemo2  == null){ //懶漢單例模式的實(shí)例未被創(chuàng)建
                 singletonDemo2  = new SingletonDemo2();
             }
            return singletonDemo2 ;
      }
}

懶漢模式-雙重檢查

public class SingletonDemo2(){
      private static volatile SingletonDemo2 singletonDemo2 = null;      
       private SingletonDemo2(){
            //私有構(gòu)造函數(shù)
      }
      private static SingletonDemo2 getInstance(){
             if(singletonDemo2  == null){ //懶漢單例模式的實(shí)例未被創(chuàng)建
                 synchronized(SingletonDemo2.class){ 
                     if(singletonDemo2  == null){
                          singletonDemo2  = new SingletonDemo2();
                     }
                  }
             }
            return singletonDemo2 ;
      }
}

Holder式,通過靜態(tài)內(nèi)部類實(shí)現(xiàn)懶加載

public class Singleton {
    private static class SingletonHolder{
        static {
            System.out.println("單例內(nèi)部類被初始化");
        }
                private static Singleton singleton = new Singleton();
        private SingletonHolder(){
            System.out.println("單例內(nèi)部類構(gòu)造函數(shù)調(diào)用");
        }
    }
    
    private Singleton(){
        System.out.println("靜態(tài)內(nèi)部類調(diào)用外部類的私有構(gòu)造函數(shù)");
        if (SingletonHolder.singleton !=null) {
            try {
                throw new IllegalAccessException("單例對(duì)象已經(jīng)被實(shí)例化,請(qǐng)不要非法反射構(gòu)造函數(shù)");
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static Singleton getInstance(){
        return SingletonHolder.singleton;
    }
}

Holder懶加載結(jié)果

public class SingleTonTest { //測(cè)試Holder的懶加載
    public static void main(String[] args) throws ClassNotFoundException  {
        System.out.println("類載入---------");
        Class cla = Class.forName("demo.Singleton"); //完成單例類的加載
        System.out.println("類載入完成------"+cla);
        System.out.println("獲取單例實(shí)例-----");
        Singleton singleton = Singleton.getInstance(); //獲取單例類的實(shí)例
        System.out.println("獲取單例結(jié)束-----"+singleton);
    }
}

控制臺(tái)輸出結(jié)果


執(zhí)行順序
  1. 可以從輸出結(jié)果中看到在完成單例類加載的時(shí)候,靜態(tài)內(nèi)部類并未被初始化,此時(shí)單例類的唯一實(shí)例還未被調(diào)用創(chuàng)建,不會(huì)占用內(nèi)存空間。
  2. 在獲取單例實(shí)例時(shí),即靜態(tài)內(nèi)部類被調(diào)用時(shí),靜態(tài)內(nèi)部類首先完成了初始化,調(diào)用了外部類的構(gòu)造函數(shù)生成實(shí)例,完成懶加載;
  3. 此方法在實(shí)現(xiàn)懶加載的同時(shí),亦保證了線程安全,且未使用synchronized悲觀鎖,性能好
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,670評(píng)論 18 399
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 4,011評(píng)論 0 11
  • 轉(zhuǎn) # https://www.cnblogs.com/easypass/archive/2010/12/ 08/...
    呂品?閱讀 10,123評(píng)論 0 44
  • 1、水的一生 水,可以是水,也可以是冰,是蒸汽。 放在杯子里,被人喝掉,是一生;化作山間流水,源源不斷,又是一生。...
    藍(lán)柿閱讀 363評(píng)論 4 4
  • 追趕火車于我而言,真算得上經(jīng)驗(yàn)豐富了。 周五的下午,腦子里便策劃坐哪趟車,往往擔(dān)心臨時(shí)有事故意把時(shí)...
    吳二水_bf67閱讀 382評(píng)論 1 1

友情鏈接更多精彩內(nèi)容