sql中in和exist两者的区别
- in和exists一般搭配子查询来使用,in的话也可以单独的使用in(a,b,c...)这种方式来使用;
- in关键字会先执行子查询即对内表的查询,再与外表做笛卡尔积(即若外表有1000条记录,内表有100,则会生成1000*100条记录),再根据条件筛选数据,而exists会先执行外表查询,再进行内外的条件判断筛选结果(外表结果集为100,则内表的子查询会执行100次);
- 两者对索引的使用也存在不同,使用in关键字时,使用的是外表的索引,而exists则是使用的内表的索引;所以当外表较大,使用外表索引,子查询内表结果较小的时候,使用in的效率会更高,而当外表较小,而子查询内表结果较大,且子查询使用索引的时候,使用exists效率会更高,若内外表查询结果集相差不大,两者效率基本相持,可根据所使用的索引来确定具体使用的关键字;
- in中子查询结果会放入内存,进行条件判断是在内存中进行的,而exists每一次的内外条件相比较是在数据库中完成的;
举个例子,下列有如下两个表
有如下两条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,执行这条sql首先会先执行子查询即内表t_order表的查询
SELECT user_id FROM t_order
然后与外表的查询生成一个笛卡尔积
然后根据sql中的条件将t_user的id和t_order的user_id进行比较,不相等的记录被删除,最后输出对应的结果集。
EXISTS
使用exists的语句,与in不同,他会默认先查询外表的查询
SELECT t.* FROM t_user t
然后在根据外表查询结果的每一行,与子查询内表执行一次,若子查询的条件符合(本例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
)
例子中外表和内表的查询数据都差不多,但是外表查询用到了主键id,主键同时也是默认创建的索引,而内表查询中未使用索引,可使用in关键字
注意:in关键字不对null进行处理,not in是不会使用索引的,会对内外表进行全扫描,所以效率极低,而not exists仍会使用内表的索引
2、Java反射机制介绍
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为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类,一般使用这个,可以动态获取所需要的Class对象
Object obj = cla.newInstance(); //通过newInstance返回实例
//获取对应类、接口的构造函数
Constructor[] cons =cla.getConstructors(); //获取所有的public的构造函数
Constructor con = cla.getConstructor(int.class); //获取对应参数类型的public构造函数,class入参可变
cons = cla.getDeclaredConstructors(); //获取所有构造函数
con = cla.getDeclaredConstructors(char.class); //获取对应入参类型的所有构造函数
//获取对应类、接口的成员变量
Field[] fields = cla.getFields(); //获取所有的public成员变量
Field field = cla.getField("demo"); //获取对应名称public成员变量
field.set(obj, "demo"); //对应静态变量进行赋值,参数:1、实例,2、修改后的数据
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"); //调用方法,入参:第一个实例,第二个对应的是入参
//调用main函数
method = cla.getMethod("main", String[] args);
method.invoke(obj, (Object)new String[]{"d","e","m","o"});
3、获得实例中的new和newInstance的区别
- 两者加载的构造器不同,new关键字可以调用任意的pulbic构造器,newInstance只能调用无参构造器;
- 用new关键字时,类可以没有被加载,而使用newInstance必须保证类已经被加载了;
- new,强类型,相对高效,newInstance,弱类型,低效率;
- new关键字完成了类加载、类的实例化,而newInstance则需要手动完成类加载,Class.forName("java.lang.String")这行代码完成了类的加载;
- newInstance可以通过传入ClassName来动态的加载类的实例,而不需要显式的调用类的构造器
//用new创建对象,可以调用无参或者有参的public构造函数
String str1 = new String();
//用newInstance
Class cla = Class.forName("java.lang.String"); //初始化,完成类的加载
Object obj = cla.newInstance(); //实例化
String str2 = (String)obj; //向下转型为子类
4、单例模式中饿汉模式和懒汉模式的区别
共同特点:构造器为私有化
(1)、懒汉模式:延迟加载,当被使用时才会加载,“时间换空间策略”,单纯的懒汉模式是非线程安全的,若有多个线程同时进入getInstance方法,线程1先进入if代码块后,线程二同时控制代码,会造成两个实例被返回,可以通过使用静态内部类(静态内部类在外部类加载时会进行初始化)或者双重检查(静态变量上需添加volatile,由于指令重排问题会导致线程不安全)的方式来达到线程安全的要求
(2)、饿汉模式:类加载时,实例已经完成,存放于内存中,“空间换时间”策略,线程安全
饿汉模式
public class SingletonDemo1(){
private static SingletonDemo1 singletonDemo1 = new SingletonDemo1();
private SingletonDemo1(){
//私有构造函数
}
private static SingletonDemo1 getInstance(){
return singletonDemo1;
}
}
懒汉模式
public class SingletonDemo2(){
private static SingletonDemo2 singletonDemo2 = null;
private SingletonDemo2(){
//私有构造函数
}
private static SingletonDemo2 getInstance(){
if(singletonDemo2 == null){ //懒汉单例模式的实例未被创建
singletonDemo2 = new SingletonDemo2();
}
return singletonDemo2 ;
}
}
懒汉模式-双重检查
public class SingletonDemo2(){
private static volatile SingletonDemo2 singletonDemo2 = null;
private SingletonDemo2(){
//私有构造函数
}
private static SingletonDemo2 getInstance(){
if(singletonDemo2 == null){ //懒汉单例模式的实例未被创建
synchronized(SingletonDemo2.class){
if(singletonDemo2 == null){
singletonDemo2 = new SingletonDemo2();
}
}
}
return singletonDemo2 ;
}
}
Holder式,通过静态内部类实现懒加载
public class Singleton {
private static class SingletonHolder{
static {
System.out.println("单例内部类被初始化");
}
private static Singleton singleton = new Singleton();
private SingletonHolder(){
System.out.println("单例内部类构造函数调用");
}
}
private Singleton(){
System.out.println("静态内部类调用外部类的私有构造函数");
if (SingletonHolder.singleton !=null) {
try {
throw new IllegalAccessException("单例对象已经被实例化,请不要非法反射构造函数");
} 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);
}
}
控制台输出结果
- 可以从输出结果中看到在完成单例类加载的时候,静态内部类并未被初始化,此时单例类的唯一实例还未被调用创建,不会占用内存空间。
- 在获取单例实例时,即静态内部类被调用时,静态内部类首先完成了初始化,调用了外部类的构造函数生成实例,完成懒加载;
- 此方法在实现懒加载的同时,亦保证了线程安全,且未使用synchronized悲观锁,性能好