1、笔试题目:html
1. 简述类与对象的区别,Java 虚函数的做用。java
类是对象的抽象,对象是类的具体实例。
类是抽象的,不占用内存,而对象是具体的,占有内存空间。设计模式
java中没有虚函数的概念,普通函数就至关于C++中的虚函数,不过能够在函数前加final使函数不能被重写。虚函数的做用是容许在派生类中从新定义与基类同名的函数,是多态性的一种体现。数组
2. Database table 写SQL语句去掉重复的记录,保留其中ID最小的一条。安全
delete from tablename where id not in (select min(id) from tablename group by col1,col2,...)多线程
3. 分析两个for循环的优缺点:函数
(1)优化
1 for(i=0; i<N; i++) 2 { 3 if(condition) 4 DoSomething(); 5 else 6 DoOtherthing(); 7 }
(2)this
1 if(condition) 2 { 3 for(i=0; i<N; i++) 4 DoSomething(); 5 } 6 else 7 { 8 for(i=0; i<N; i++) 9 DoOtherthing(); 10 }
程序(1)(前者):
优势:程序简洁
条件判断出如今for里面,意味着,即便我在DoSomething()或DoOtherthing()这2个函数中改变了condition的值,for循环也能正确执行个人意图,由于它在每次循环中都会从新检测conditon的值并针对condition的值作不一样动做,所谓以不变应万变,这是难能难得的.
缺点:多执行了N-1次逻辑判断,而且打断了循环“流水线”做业,使得编译器不能对循环进行优化处理,下降了效率。
若是condition一直不曾改变,咱们可怜的if必须每次循环都判断一下condition的真假.牺牲了运行时效率.spa
程序(2)(后者):
优势:循环的效率高。只进行一次判断,运行时效率高.适合那种condition的值不会改变的状况.
缺点:因为只在一开始进行一次判断,因此失去了改变condition的值的机会,也就是说,即便我在DoSomething()中改变了condition的值为false,这个程序也不会改变它的判断,它依然执行着DoSomething()的循环.咱们不能随时更换咱们须要进行的动做。这是牺牲了弹性。
N较大时,建议采用后面这种写法,因为前者老要进行逻辑判断,打断了循环“流水线”做业,使得编译器不能对循环进行优化处理,下降了效率。
4. 1-100的天然数,放到长度为99的数组a[]中,设计一个好的方法,可以方便地找出未被放入的数。
1 int total = 0; 2 for (int i=0; i<99; i++) { 3 total += a[i]; 4 } 5 System.out.println("未被放入的数是:"+(5050-total));
其余方法:
1).先将数组中的99个数字进行排序,而后将1-100个数字依次在数组中折半查找,能够借用java中已有的
Arrays.binarySearch
2).其实若是数组能够换成集合的话,能够直接判断集合中是否contain这1-100个数字就能够了
3).用HashMap实现,把数字1-100都放入HashMap中,key为数字,value随便,好比为1,for循环遍历长度为99的数组,对HashMap移除当前数字的key,最后剩下的1个数便是所求。
5. 用递归实现100!。
1 import java.math.BigInteger; 2 3 public class factorial { 4 5 public static BigInteger callFactorial(int n) { 6 if (n < 1) 7 throw new IllegalArgumentException(); 8 9 if (n == 1) 10 return BigInteger.ONE; 11 12 return callFactorial(n - 1).multiply(BigInteger.valueOf(n)); 13 } 14 15 public static void main(String[] args) { 16 System.out.println(callFactorial(100)); 17 } 18 }
6. 设计模式:Singleton、Factory(代码)
第一种(懒汉,线程不安全):
1 public class Singleton { 2 private static Singleton instance; 3 4 public static Singleton getInstance() { 5 if (instance == null) { 6 instance = new Singleton(); 7 } 8 return instance; 9 } 10 }
这种写法lazy loading很明显,可是致命的是在多线程不能正常工做。
第二种(懒汉,线程安全):
1 public class Singleton { 2 private static Singleton instance; 3 4 public static synchronized Singleton getInstance() { 5 if (instance == null) { 6 instance = new Singleton(); 7 } 8 return instance; 9 } 10 }
这种写法可以在多线程中很好的工做,并且看起来它也具有很好的lazy loading,可是,遗憾的是,效率很低,99%状况下不须要同步。
第三种(饿汉):
1 public class Singleton { 2 private static Singleton instance = new Singleton(); 3 4 public static Singleton getInstance() { 5 return instance; 6 } 7 }
这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然致使类装载的缘由有不少种,在单例模式中大多数都是调用getInstance方法, 可是也不能肯定有其余的方式(或者其余的静态方法)致使类装载,这时候初始化instance显然没有达到lazy loading的效果。
第四种(饿汉,变种):
1 public class Singleton { 2 private Singleton instance = null; 3 static { 4 instance = new Singleton(); 5 } 6 7 public static Singleton getInstance() { 8 return this.instance; 9 } 10 }
表面上看起来差异挺大,其实更第三种方式差很少,都是在类初始化即实例化instance。
第五种(静态内部类):
1 public class Singleton { 2 private static class SingletonHolder { 3 private static final Singleton INSTANCE = new Singleton(); 4 } 5 6 public static final Singleton getInstance() { 7 return SingletonHolder.INSTANCE; 8 } 9 }
这种方式一样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不一样的是(很细微的差异):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不必定被初始化。由于SingletonHolder类没有被主动使用,只有显示经过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,若是实例化instance很消耗资源,我想让他延迟加载,另一方面,我不但愿在Singleton类加载时就实例化,由于我不能确保Singleton类还可能在其余的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。
第六种(枚举):
1 public enum Singleton { 2 INSTANCE; 3 public void whateverMethod() { 4 } 5 }
这种方式是Effective Java做者Josh Bloch 提倡的方式,它不只能避免多线程同步问题,并且还能防止反序列化从新建立新的对象,可谓是很坚强的壁垒啊,不过,我的认为因为1.5中才加入enum特性,用这种方式写难免让人感受生疏,在实际工做中,我也不多看见有人这么写过。
第七种(双重校验锁):
1 public class Singleton { 2 private volatile static Singleton singleton; 3 4 public static Singleton getSingleton() { 5 if (singleton == null) { 6 synchronized (Singleton.class) { 7 if (singleton == null) { 8 singleton = new Singleton(); 9 } 10 } 11 } 12 return singleton; 13 } 14 }
这个是第二种方式的升级版,俗称双重检查锁定,详细介绍请查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html
在JDK1.5以后,双重检查锁定才可以正常达到单例效果。
总结
有两个问题须要注意:
1.若是单例由不一样的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每一个servlet使用彻底不一样的类装载器,这样的话若是有两个servlet访问一个单例类,它们就都会有各自的实例。
2.若是Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。无论怎样,若是你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
对第一个问题修复的办法是:
1 private static Class getClass(String classname) 2 throws ClassNotFoundException { 3 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 4 5 if(classLoader == null) 6 classLoader = Singleton.class.getClassLoader(); 7 8 return (classLoader.loadClass(classname)); 9 } 10 }
对第二个问题修复的办法是:
1 public class Singleton implements java.io.Serializable { 2 public static Singleton INSTANCE = new Singleton(); 3 4 protected Singleton() { 5 6 } 7 private Object readResolve() { 8 return INSTANCE; 9 } 10 }
对我来讲,我比较喜欢第三种和第五种方式,简单易懂,并且在JVM层实现了线程安全(若是不是多个类加载器环境),通常的状况下,我会使用第三种方式,只有在要明确实现lazy loading效果时才会使用第五种方式,另外,若是涉及到反序列化建立对象时我会试着使用枚举的方式来实现单例,不过,我一直会保证个人程序是线程安全的,并且我永远不会使用第一种和第二种方式,若是有其余特殊的需求,我可能会使用第七种方式,毕竟,JDK1.5已经没有双重检查锁定的问题了。
Factory模式:待续。