sql中in和exist兩者的區(qū)別
- in和exists一般搭配子查詢來使用,in的話也可以單獨的使用in(a,b,c...)這種方式來使用胁塞;
- in關鍵字會先執(zhí)行子查詢即對內表的查詢碘举,再與外表做笛卡爾積(即若外表有1000條記錄格侯,內表有100,則會生成1000*100條記錄)吱晒,再根據(jù)條件篩選數(shù)據(jù)甸饱,而exists會先執(zhí)行外表查詢,再進行內外的條件判斷篩選結果(外表結果集為100仑濒,則內表的子查詢會執(zhí)行100次)叹话;
- 兩者對索引的使用也存在不同,使用in關鍵字時墩瞳,使用的是外表的索引渣刷,而exists則是使用的內表的索引;所以當外表較大矗烛,使用外表索引辅柴,子查詢內表結果較小的時候,使用in的效率會更高瞭吃,而當外表較小碌嘀,而子查詢內表結果較大,且子查詢使用索引的時候歪架,使用exists效率會更高股冗,若內外表查詢結果集相差不大,兩者效率基本相持和蚪,可根據(jù)所使用的索引來確定具體使用的關鍵字止状;
- in中子查詢結果會放入內存,進行條件判斷是在內存中進行的攒霹,而exists每一次的內外條件相比較是在數(shù)據(jù)庫中完成的怯疤;
舉個例子,下列有如下兩個表
有如下兩條SQL
--使用in關鍵字
SELECT *
FROM t_user
WHERE id IN (
SELECT user_id
FROM t_order
);
--使用exists關鍵字
SELECT t.*
FROM t_user t
WHERE EXISTS (
SELECT tt.user_id
FROM t_order tt
WHERE t.id = tt.user_id
)
最后輸出的結果是一致的
IN
首先分析使用in關鍵字的sql催束,執(zhí)行這條sql首先會先執(zhí)行子查詢即內表t_order表的查詢
SELECT user_id FROM t_order
然后與外表的查詢生成一個笛卡爾積
然后根據(jù)sql中的條件將t_user的id和t_order的user_id進行比較集峦,不相等的記錄被刪除,最后輸出對應的結果集抠刺。
EXISTS
使用exists的語句塔淤,與in不同,他會默認先查詢外表的查詢
SELECT t.* FROM t_user t
然后在根據(jù)外表查詢結果的每一行速妖,與子查詢內表執(zhí)行一次高蜂,若子查詢的條件符合(本例sql即t_user的id和t_order的user_id相等),則返回true罕容,若不符合則返回false备恤,則刪除外表查詢結果集中的一行挺举,最后完成篩選
EXISTS (
SELECT tt.user_id
FROM t_order tt
WHERE t.id = tt.user_id
)
例子中外表和內表的查詢數(shù)據(jù)都差不多,但是外表查詢用到了主鍵id烘跺,主鍵同時也是默認創(chuàng)建的索引,而內表查詢中未使用索引脂崔,可使用in關鍵字
注意:in關鍵字不對null進行處理滤淳,not in是不會使用索引的,會對內外表進行全掃描砌左,所以效率極低脖咐,而not exists仍會使用內表的索引
2、Java反射機制介紹
JAVA反射機制是在運行狀態(tài)中汇歹,對于任意一個類屁擅,都能夠知道這個類的所有屬性和方法;對于任意一個對象产弹,都能夠調用它的任意一個方法和屬性派歌;這種動態(tài)獲取的信息以及動態(tài)調用對象的方法的功能稱為java語言的反射機制。
Java中使用反射一般是通過Class類這個對象來完成痰哨,JavaAPI中的描述如下:
這個類沒有構造方法胶果,Class 對象是在加載類時由 Java 虛擬機以及通過調用類加載器中的defineClass 方法自動構造的。Class對象下有很多方法斤斧,下面介紹一部分常用的方法早抠。
參考:http://blog.csdn.net/sinat_38259539/article/details/71799078
String str = new String("demo");//新建一個String對象
//常用的Class對象獲取方式
Class cla = str.getClass();
cla = String.class;
cla = Class.forName("java.lang.String"); //通過類的真實路徑來返回一個Class類,一般使用這個撬讽,可以動態(tài)獲取所需要的Class對象
Object obj = cla.newInstance(); //通過newInstance返回實例
//獲取對應類蕊连、接口的構造函數(shù)
Constructor[] cons =cla.getConstructors(); //獲取所有的public的構造函數(shù)
Constructor con = cla.getConstructor(int.class); //獲取對應參數(shù)類型的public構造函數(shù),class入?yún)⒖勺?cons = cla.getDeclaredConstructors(); //獲取所有構造函數(shù)
con = cla.getDeclaredConstructors(char.class); //獲取對應入?yún)㈩愋偷乃袠嬙旌瘮?shù)
//獲取對應類游昼、接口的成員變量
Field[] fields = cla.getFields(); //獲取所有的public成員變量
Field field = cla.getField("demo"); //獲取對應名稱public成員變量
field.set(obj, "demo"); //對應靜態(tài)變量進行賦值甘苍,參數(shù):1、實例烘豌,2羊赵、修改后的數(shù)據(jù)
fields = cla.getDeclaredFields();
field = cla.getDeclaredField("demo");
//獲取類、接口下的成員方法
Method[] methods = cla.getMethods(); //獲取所有的public成員方法
Method method = cla.getMethod("demo", String.class); //獲取對應名稱的public成員方法
methods = cla.getDeclaredMethods();
method = cla.getDeclaredMethod("demo", String.class);
method.invoke(obj, "demo"); //調用方法扇谣,入?yún)ⅲ旱谝粋€實例昧捷,第二個對應的是入?yún)?
//調用main函數(shù)
method = cla.getMethod("main", String[] args);
method.invoke(obj, (Object)new String[]{"d","e","m","o"});
3、獲得實例中的new和newInstance的區(qū)別
- 兩者加載的構造器不同罐寨,new關鍵字可以調用任意的pulbic構造器靡挥,newInstance只能調用無參構造器;
- 用new關鍵字時鸯绿,類可以沒有被加載跋破,而使用newInstance必須保證類已經被加載了簸淀;
- new,強類型毒返,相對高效租幕,newInstance,弱類型拧簸,低效率劲绪;
- new關鍵字完成了類加載、類的實例化盆赤,而newInstance則需要手動完成類加載贾富,Class.forName("java.lang.String")這行代碼完成了類的加載;
- newInstance可以通過傳入ClassName來動態(tài)的加載類的實例牺六,而不需要顯式的調用類的構造器
//用new創(chuàng)建對象颤枪,可以調用無參或者有參的public構造函數(shù)
String str1 = new String();
//用newInstance
Class cla = Class.forName("java.lang.String"); //初始化,完成類的加載
Object obj = cla.newInstance(); //實例化
String str2 = (String)obj; //向下轉型為子類
4淑际、單例模式中餓漢模式和懶漢模式的區(qū)別
共同特點:構造器為私有化
(1)畏纲、懶漢模式:延遲加載,當被使用時才會加載春缕,“時間換空間策略”霍骄,單純的懶漢模式是非線程安全的,若有多個線程同時進入getInstance方法淡溯,線程1先進入if代碼塊后读整,線程二同時控制代碼,會造成兩個實例被返回咱娶,可以通過使用靜態(tài)內部類(靜態(tài)內部類在外部類加載時會進行初始化)或者雙重檢查(靜態(tài)變量上需添加volatile米间,由于指令重排問題會導致線程不安全)的方式來達到線程安全的要求
(2)、餓漢模式:類加載時膘侮,實例已經完成屈糊,存放于內存中,“空間換時間”策略琼了,線程安全
餓漢模式
public class SingletonDemo1(){
private static SingletonDemo1 singletonDemo1 = new SingletonDemo1();
private SingletonDemo1(){
//私有構造函數(shù)
}
private static SingletonDemo1 getInstance(){
return singletonDemo1;
}
}
懶漢模式
public class SingletonDemo2(){
private static SingletonDemo2 singletonDemo2 = null;
private SingletonDemo2(){
//私有構造函數(shù)
}
private static SingletonDemo2 getInstance(){
if(singletonDemo2 == null){ //懶漢單例模式的實例未被創(chuàng)建
singletonDemo2 = new SingletonDemo2();
}
return singletonDemo2 ;
}
}
懶漢模式-雙重檢查
public class SingletonDemo2(){
private static volatile SingletonDemo2 singletonDemo2 = null;
private SingletonDemo2(){
//私有構造函數(shù)
}
private static SingletonDemo2 getInstance(){
if(singletonDemo2 == null){ //懶漢單例模式的實例未被創(chuàng)建
synchronized(SingletonDemo2.class){
if(singletonDemo2 == null){
singletonDemo2 = new SingletonDemo2();
}
}
}
return singletonDemo2 ;
}
}
Holder式逻锐,通過靜態(tài)內部類實現(xiàn)懶加載
public class Singleton {
private static class SingletonHolder{
static {
System.out.println("單例內部類被初始化");
}
private static Singleton singleton = new Singleton();
private SingletonHolder(){
System.out.println("單例內部類構造函數(shù)調用");
}
}
private Singleton(){
System.out.println("靜態(tài)內部類調用外部類的私有構造函數(shù)");
if (SingletonHolder.singleton !=null) {
try {
throw new IllegalAccessException("單例對象已經被實例化,請不要非法反射構造函數(shù)");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
public static Singleton getInstance(){
return SingletonHolder.singleton;
}
}
Holder懶加載結果
public class SingleTonTest { //測試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("獲取單例實例-----");
Singleton singleton = Singleton.getInstance(); //獲取單例類的實例
System.out.println("獲取單例結束-----"+singleton);
}
}
控制臺輸出結果
- 可以從輸出結果中看到在完成單例類加載的時候雕薪,靜態(tài)內部類并未被初始化昧诱,此時單例類的唯一實例還未被調用創(chuàng)建,不會占用內存空間所袁。
- 在獲取單例實例時盏档,即靜態(tài)內部類被調用時,靜態(tài)內部類首先完成了初始化燥爷,調用了外部類的構造函數(shù)生成實例蜈亩,完成懶加載懦窘;
- 此方法在實現(xiàn)懶加載的同時,亦保證了線程安全稚配,且未使用synchronized悲觀鎖畅涂,性能好