ImportNew 网站的Java面试专题学习笔记html
String s = " Hello "; s += " World "; s.trim( ); System.out.println(s);
输出为“ Hello World ”,先后皆有空格。
字符串是不可变对象。
s.trim()虽然生成了一个新的字符串对象,可是却没有变量指向这个心生成的对象,s 仍然指向字符串s += " World "。
下图说明了,生成对象以及垃圾回收过程。java
可用StringBuilder来构造,由于其底层使用的是字符数组,全部操做都直接在字符数组上直接操做,并且他不是一个线程安全的类,执行速度上,相比于StringBuffer要快。面试
这一点若是深刻理解了String的Interning机制,就更好理解了。
Java程序在编译时,会将全部肯定下来的,存在双引号内的字符串,都存入常量池中,该常量池是类文件(.class)的一部分。常量池中存着许多表,其中 Constant_Utf8_info 表中,记录着会被初始化为 String 对象的字符串的字面值(iteral)。 在JVM 中,相应的类被加载运行后,常量池对应的映射到 JVM 的运行时常量池(run time constant pool)中。算法
关于String的intern()方法编程
当 intern 方法被调用,若是字符串池中已经拥有一个与该 String 的字符串值相等(即 equals()调用后为 true)的 String 对象时,那么池中的那个 String 对象会被返回。不然,池中会增长这个对象,并返回当前这个 String 对象。数组
现代的 JVM 实现里,考虑到垃圾回收(Garbage Collection)的方便,将 heap 划分为三部分: young generation 、 tenured generation(old generation)和 permanent generation( permgen )。字符串池是为了解决字符串重复的问题,生命周期长,它存在于 permgen 中。安全
所以,对于框架
String str = new String("abc");
JVM会生成两个字符串,一个是在常量池中,另一个是new在heap堆中。编程语言
Object s1 = new String("Hello"); Object s2 = new String("Hello"); if(s1 == s2) { System.out.println("s1 and s2 are =="); }else if (s1.equals(s2)) { System.out.println("s1 and s2 are equals()"); }
输出结果为s1 and s2 are equals()
主要考察对equals和==的理解,==比较引用的地址是否相同,equeal比较对象中真正的值。
详细的过程可见下图ide
另外,若是不是用 new关键字强制建立字符串对象的话,而是采用==,那么Java会默认采用字符串池,减小对象的建立。
String对象会建立一个字符串池(a pool of string),若是当前准备新建立的字符串对象的值在这个池子中已经存在,那么就不会生成新对象,而是复用池中已有的字符串对象。flyweight 模式的精髓就是对象复用。
重写发生在子类继承父类时,子类覆盖父类的方法时发生,是在运行时发生。
重载是在同一个类中,同一个方法,不一样参数时发生,是在编译期发生。
在Java 5中使用注解@override,来标示方法重写,若是编译时发现没有重写,则JVM会抛出编译异常。
可重入方法(re-entrant method)是能够安全进入的方法,即便同一个方法正在被执行,深刻到同一个线程的调用栈里面也不会影响这次执行的安全性。一个非可重入方法则不是能够安全进入的。例如,加入写文件或者向文件中写入日志的方法不是可重入方法时,有可能会毁坏那个文件。
若是一个方法调用了其自身的话,咱们称之为递归调用。假定栈空间足够的话,尽管递归调用比较难以调试,在Java语言中实现递归调用也是彻底可行的。递归方法是众多算法中替代循环的一个不错选择。全部的递归方法都是可重入的,可是不是全部可重入的方法都是递归的。
栈遵照LIFO(Last In First Out)规则,所以递归调用方法可以记住“调用者”而且知道此轮执行结束之返回至当初的被调用位置。递归利用系统栈来存储方法调用的返回地址。 Java是一种基于栈设计的编程语言。
循环的方式能够达到目的,没必要采用递归。可是在某些状况下采用递归方式则代码会更加简短易读。递归方法在循环树结构以及避免丑陋的嵌套循环的状况下是很是好用的。
常规递归方法(亦称,头递归)在上面演示了,这种方式会增长调用栈的大小。每次递归,其入口须要被记录在栈中。方法返回以前须要给countA(input.substring(1)的结果加一个count。所以,最后须要作的事实际上是加法运算,而非递归自己。
在尾递归中,最后要作的是递归,加法运算在以前就已经完成了。栈调用减小带来了内存消耗减小而且程序的性能更好。以下代码
public class TailRecursiveCall { public int countA(String input) { // exit condition – recursive calls must have an exit condition if (input == null || input.length() == 0) { return 0; } return countA(input, 0) ; } public int countA(String input, int count) { if (input.length() == 0) { return count; } // check first character of the input if (input.substring(0, 1).equals("A")) { count = count + 1; } // recursive call is the last call as the count is cumulative return countA(input.substring(1), count); } public static void main(String[] args) { System.out.println(new TailRecursiveCall().countA("AAA rating")); } }
ArrayList的大小是如何自动增长的?你能分享一下你的代码吗?
使用ensureCapacity, 在进行添加元素时,检查容量是否足够,不够的话,就将容量扩大3/2,并将旧数组中的元素使用Arrays.copyOf拷贝到新数组中。
什么状况下你会使用ArrayList?何时你会选择LinkedList?
ArrayList是在访问的次数远大于插入和删除的次数,使用ArrayList,由于ArrayList底层使用数组,访问的复杂度为O(1), 可是插入和删除就得频繁使用System.arraycopy复制数组。 LinkList主要在访问次数远小于插入和删除的次数时使用,其删除和插入的复杂度,但访问元素时几乎为O(n)。
当传递ArrayList到某个方法中,或者某个方法返回ArrayList,何时要考虑安全隐患?如何修复安全违规这个问题呢?
当array被当作参数传递到某个方法中,若是array在没有被复制的状况下直接被分配给了成员变量,那么就可能发生这种状况,即当原始的数组被调用的方法改变的时候,传递到这个方法中的数组也会改变。
将其副本拷贝出来再进行修改。
如何复制某个ArrayList到另外一个ArrayList中去?写出你的代码?
使用clone()方法,好比ArrayList newArray = oldArray.clone();
使用ArrayList构造方法,好比:ArrayList myObject = new ArrayList(myTempObject); 使用Collection的copy方法...
注意1和2是浅拷贝(shallow copy),何为浅拷贝?
浅拷贝就好比像引用类型,而深拷贝就好比值类型。浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不一样(名称不一样)。对其中任何一个对象的改动都会影响另一个对象。举个例子,一我的一开始叫张三,后来更名叫李四了,但是仍是同一我的,不论是张三缺胳膊少腿仍是李四缺胳膊少腿,都是这我的倒霉。
深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另一个对象形成影响。举个例子,一我的名叫张三,后来用他克隆(假设法律容许)了另一我的,叫李四,不论是张三缺胳膊少腿仍是李四缺胳膊少腿都不会影响另一我的。比较典型的就是Value(值)对象,如预约义类型Int32,Double,以及结构(struct),枚举(Enum)等。
还可用序列化技术来进行深拷贝,对象实现序列化接口,而后写入流,并读出来
// 将对象写到流里 ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(this); // 从流里读出来 ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); return (oi.readObject());
可是串行化却很耗时,在一些框架中,咱们即可以感觉到,它们每每将对象进行串行化后进行传递,耗时较多。
在索引中ArrayList的增长或者删除某个对象的运行过程?效率很低吗?解释一下为何?
频繁插入和删除,会频繁调用System.arrayCopy....效率低
参考地址
-[1] http://www.importnew.com/2228...
-[2] http://www.importnew.com/2223...
-[3] http://www.importnew.com/2217...
-[4] http://www.importnew.com/2329...
-[5] http://www.importnew.com/9928...
-[6] http://www.cnblogs.com/shuaiw...
-[7] http://blog.csdn.net/biaobiao...