1)现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?
这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。
2)java的堆和栈的区别:
堆:是一个运行时数据区,类的对象从中分配空间。这些对象通过new,newarray,anewarray和muitianewarray等指令建立,它们不需要程序代码来显示释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是运行时动态分配内存的,Java垃圾收集器会自动收走这些不再使用的数据。但是缺点是:由于是要在运行时动态分配内存,存取速度较慢。
栈的优势:存取速度比堆要快,仅次于寄存器,栈数据可以共享。缺点是,存在栈中的数据大小与生命期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(int,short,long,byte,float,double,boolean,char)和对象句柄。
3)java算法:
a、冒泡排序:从头开始,依次对相邻的两个元素进行比较,发现有顺序错误就将它们调换过来!重复地进行,直到没有元素需要交换。是稳定排序法,最好的时间复杂度是O(n);
b、快速排序:思想是(Divide)选取一个基元X开始(一般选取数组的第一个元素),通过某种分区操作将数组划分成两个部分,左边部分小于等于X,右边部分大于等于X。(Conquer)左右两个子数组递归地调用Divide过程,(Combine)快排作为就地排序算法,不需要任何合并操作!
c、二分查找:前提(数据必须是顺序存储结构,且必须按照关键字的大小有序排列),查找一个数在数组(长度为n)的用二分查找的时间复杂度是o(log(n))。
d、递归:程序调用自身,并非不会无休止地调用下去,需要有一个出口,当满足条件时程序也就结束!不然的话,就会出现程序死循环。
4)JVM调用GC的频度还是很高的,主要两种情况下进行垃圾回收:第一个就是当应用程序线程空闲,另一个是java内存堆不足时,会不断调用GC,若连续回收都解决不了内存堆的不足。
4)、junit测试的方法的类型必须是public ,其他类型运行时会报错。方法的返回类型也必须是void,并且不能有参数,并且不能是static方法,只允许一个不带参数的构造方法,即默认的构造方法,一个或者多个都会报错。
@Before在该方法前面加上这个注释,会在每个测试方法迁都会执行该方法,注意:方法必须没有参数。
@After在该方法前面加上这个注释,会在每个测试方法执行后都会执行该方法。
assertNotNull(obj);断言对象不为空,是空的就会报错:“java.lang.AssertionError”
assertNull(obj);断言对象是空的,不为空就会报错:“java.lang.AssertionError”
5)、List<Object> args = null; args.add(param);//这样会报错
分析原因:List是一个接口,没有实现是不能使用的,需要ArryList的实现类或者其他实现类才能使用
应该改为:List<Object> args = new ArrayList<Object>();args.add(param);
6)、如A是父类,B是继承于A,两者都有构造函数和A有print(),B也覆盖了A的方法,那么可以A a = new B();此时a.print();调用的是B的print()方法,如果B的print()没有覆盖父类的方法,而是自己创建的print();那么这时a.print();调用的还是B的print()方法。另外,假设A有print1()方法,B没有覆盖A的print1();这时a.print1();不会报错,它将调用的是A自己的print1()方法。最后,假如A没有了print()方法,而是print()是只有B有,这时a.print();将直接调用B的print();方法,总结: 对于类的继承,父类通过子类实例化后,构造方法都在先执行父类的构造方法,再依次执行子类的构造方法。方法的调用原则是先执行子类的方法,如果子类复写了父类的方法,将执行子类的方法,如果子类没有复写父类的方法,但是方法重名,也会只执行子类的方法。如果子类没有对应的方法,那么就执行父类的方法!(不要以为子类没有该方法编译器会报错,因为继承了父类的方法!)请注意:B b = new A();编译器会报错,这也是不合逻辑的!
public class Test03 {
public static void main(String[] args) {
A b = new B();
b.print1();
//new C();
}
}
class A {
public A() {
System. out.println(“A” );
}
public void print(){
System. out.println(“哈哈A” );
}
public void print1(){
System. out.println(“哈哈A1” );
}
}
class B extends A {
public B() {
System. out.println(“B” );
}
public void print() {
System. out.println(“哈哈B” );
}
}
class C extends A {
public C() {
super();
System. out.println(“B” );
}
}
以上关于main方法中同时执行:new B();new C();将打印出的结果: A B A C,即每一次调用都产生一个父类的实例,就是上面所述的每次创建实例 都是构造方法先从父类开始依次执行到当前的子类
7、Java.util.Date : 包含年、月、日、时、分、秒信息
Java.sql.Date :包含年、月、日信息
Java.util.Calendar : 包含年、月、日、时、分、秒、毫秒信息。
Java.sql.Timestamp : 包含年、月、日、时、分、秒、纳秒(nano)信息。
8、java的安全性体现:
a/第一个就是garbage collection,(垃圾回收)会让你的程序不容易出现内存泄露。内存泄露是很危险的,在内存泄露的时候黑客可以黑你的电脑。
b/exception,你有没有发现你想用null赋值的东西会出exception?这个就是exception的好处,出现exception的时候程序员可以让程序停止运行,这样的话就不会被黑客黑了
c/就是指针,java里面没有指针!这样的话人们就不能access不该access的内存了,C的话就非常危险了,黑客可以让C的程序stack overflow,然后overflow的内存地址跳到一个不该跳的地方.
9、序列化和反序列化
java串行化技术可以使你将一个对象的状态写入一个byte流里,并且可以从其他地方把该Byte流里的数据读出来,重新构造一个相同的对象。这种机制允许你将对象通过网络传播,并可以随时持久化到数据库、文件等系统。java的串行机制是RMI、EJB等技术的技术基础。用途:利用对象的串行化实现保存应用程序的当前工作状态,下次再启动的时候将自动地恢复到上次执行的状态。
序列化就是一种用来处理对象流的机制。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现Serializable接口,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象。接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为Obj的对象写出(即保存其状态),要恢复的话则用输入流。
10、hibernate/ibatis的实体类都需要无参的构造方法?因为hibernate/ibatis需要根据无参构造方法反射出该实体,如果重写了构造方法,那么hibernate/ibatis就不能知道构造方法反射出实体,因此还要在此基础上加上一个无参的构造方法!
11、接口和抽象类的区别:
声明方法的存在而不去实现它的类被叫做抽象类。它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类。不能创建abstract类的实例,
12、equal()和”==”的区别:
equal()对于字符串来说是比较内容的,而对于非字符串来说是比较其指向的对象是否相同的。
==比较符也是比较指向的对象是否相同的也就是对象在内存中的首地址。
因为Sting类重新定义了equal(),而比较的是值,而不是地址。
关于equal()与==区别从以下几个方面来说:
a、如果是基本类型的比较,那么只能用==来比较。不能用equal()(编译不通过)
b、对于基本类型的包装类型:比如:Boolean,Character,Byte,Short,Integer,Long,Float,Double等的引用变量,==是比较地址的,而equal()是比较内容的。
c、字符串为null的比较要使用==,不能使用equal(),不然会报空指针异常
13、java的static块。它是只在类第一次加载的时候执行一次,而且它会先于构造函数执行。
java 的static变量是不会加载类的,也不会执行static块。
14、异常处理
1、捕获异常之后,将继续执行后面的程序;如果是不作处理或者直接抛出异常,java将在碰到异常后直接报错,程序不再运行
2、在捕获(catch)多个异常时,要把范围小的异常写在前面,不然编译器会报错“Unreachable catch block for RuntimeException. It is already handled by the catch block for Exception” 而且在捕获到小范围的异常之后,将不再执行大范围异常catch里面的语句:如RuntimeException执行了,那后面的Exception就不执行了。
15、内部类:
1、内部类是嵌套在另一个类中的类。
2、内部类不可以在其他类或main方法里实例化,必须使用如下方法(非静态内部类)
外部类.内部类 对象名 = new 外部类().new 内部类();
3、非静态内部类不可以声明静态成员,静态内部类的非静态成员可以访问其外部类的静态成员,静态内部类声明为静态的成员不可以访问外部的非静态成员,静态内部类声明为静态的成员可以访问外部类中静态的成员
16、static
1、类成员,直接使用 类名.成员 调用。
2、静态的成员不能使用this、super关键字。
3、静态的方法只能访问静态的成员。
4、静态的方法不能被非静态方法重载和复写
17、同步,异步,阻塞,非阻塞概念的理解:
同步:就是本身去调用一个功能,该功能没有结果前,我死等结果。
异步:就是本身去调用一个功能,不需要知道该功能结果,该功能有结果后通知我。(回调通知)
阻塞:就是调用自己,自己没有接收数据或者得到结果之前,自己不会返回。
非阻塞:就是调用自己,自己立刻返回,通过select通知调用者。
同步I/O和异步I/O的区别在于:数据拷贝的时候进程是否阻塞。
阻塞I/O和非阻塞I/O的区别在于:应用程序的调用是否立即返回。
18、常见的Runtime Exception有:
NullPointException –空指针异常
ClassCastException –类型强制转换异常
IllegalArgumentException –传递非法参数异常
ArithmeticException –算术运算异常
ArrayStoreException -向数组中存放与声明异常
IndexOutOfBoundsException — 下标越界异常
NegativaArraySizeException –创建一个大小为负数的数组错误异常
NumberFormatException -数字格式异常
SecurityException –安全异常
UnsupportedOperationException –不支持的操作异常
19、java Math的floor,round和ceil的总结
floor:向下取整
ceil:向上取整
round:四舍五入
总结:画一个水平左边轴,在坐标轴上标出该点,向下就向左取最靠近的整数,向上就向右取最靠近的整数,四舍五入就是该点距离左边的整数的距离是四以内,包括四就取左边的值,否则取右边的值
20、java的锁机制 (http://blog.csdn.net/yangzhijun_cau/article/details/6432216)
说明:一段synchronized的代码被一个线程执行前,它要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁);如果这个时候同步对象的锁被其他线程拿走了,它就只能等了(线程阻塞在锁池等待队列中)。取到锁后,它就开始执行同步代码(被synchronized修饰的代码);线程执行完同步代码后就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。这样就保证同步代码在同一时刻只有一个线程在执行。
关于线程的同步,一般有以下解决方法:
1、在需要同步的方法签名中加入synchronized关键字。
2、使用synchronized块对需要进行同步的代码块进行同步。
3、使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象。
多线程的线程同步机制实际上是靠锁的概念来控制的。那么在Java程序中,锁是如何体现的呢?
在Java程序运行时环境中,JVM需要对两类共享的数据进行协调。
1)保存在堆中的实例变量
2)保存在方法区中的类变量
这两类数据是被所有线程共享的。(程序不需要协调保存在Java栈当中的数据、因为这些数据是属于拥有该栈的线程所私有的.)在Java虚拟机中,每个对象和类在逻辑上都是和一个监听器相关联的。对于对象来说,相关联的监听器保护对象的实例变量。
对类来说,监听器保护类的类变量。
(如果一个对象没有实例变量,或者一个类没有变量,相关联的监听器就什么也不监视)
为了实现监听器的排他性监视能力,Java虚拟机为每个对象和类都关联一个锁了,(锁住一个对象就是获取对象相关联的监听器)
类锁实际上用对象锁来实现。当虚拟机装载一个class文件的时候,它就会创建一个java.lang.Class类的实例。当锁住一个对象的时候,实际上锁住的就是那个类的class对象。
一个线程可以多次对同一个对象上锁。对于每一个对象,Java虚拟机维护一个加锁计数器,线程每获得一次该对象,计数器加1。
synchronized的缺陷:如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有。
2)线程执行发生异常,此时JVM会让线程自动释放锁。
问题来了:如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,这样多么影响执行效率。需要lock来解决。
总结:Lock和synchronized的不同点:
1)Lock是一个接口,而synchronized是java中的关键字,synchronized是内置的语言实现。
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁的现象发生,因此使用Lock需要在finally块中释放锁。
3)Lock可以让等待的锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断。(lockInterruptibly()的用法体现了Lock的可中断性)
4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5)Lock可以提高多个线程进行读操作的效率。(参考:http://www.cnblogs.com/dolphin0520/p/3923167.html)
21、Java main方法中的args可以接受的参数。如命令行输入 “java 类名 参数”参数
将通过args数据传进去,这样就可以通过数组动态得到用户输入的值。多个参数中间用间隔隔开。
22、HashMap和Hashtable的区别:都是Map接口的实现类
HashMap是多线程的,是线程不安全的,是没有分类和无序的。它的key,value中的key和value都可以是null,速度相对hashtable较快。
Hashtable类似于hashMap,它是线程同步,所以是线程安全的。它的key,value中的key和value是不能为空的,为空将报空指针异常。
遍历集合都可以通过以下方式实现:Map<String,Object> m
for(Map.Entry<String,Object> e : m.entrySet() ){System.out.println(e.getKey() + ” ” + e.getValue());}
23、java中Collection和Collections的区别:参考(http://pengcqu.iteye.com/blog/492196)
java.util.Collection
java.util.Collections:这个类是个包装类,里面都是静态的属性和方法组成,通过私有化构造方法达到不能被实例化的目的。(可以将其看作是一个工具类)
例子,其中sort()对list进行从小到大的排序,reverse()把list的顺序变成从右往左排列
//注意List是实现Collection接口的
List<Integer> list = new ArrayList<Integer>();
Integer array[] = { 112, 111, 23, 456, 231 };
/*for (int i = 0; i < array.length; i++) {
list.add(array[i]);
} */
list = Arrays.asList(array);//这个更加好用简单,直接将数组转成List,需要注意:标记紫色的部分的类型必须是一致的,就算是int和Integer也要区分开来。
Collections. sort(list); //将List排序
for (int i = 0; i < array.length; i++) {
System. out.println(list.get(i));
}
–测试按照中文第一个字母的排序
List<String> names = new ArrayList<String>();
//names.add(“李小安”);
names.add( “李小明”);
names.add( “陈小君”);
names.add( “wks”);
names.add( “leed”);
names.add( “oll”);
names.add( “郭小小”);
Collections. sort(names, new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
String s1 = String. valueOf(o1.charAt(0));
String s2 = String. valueOf(o2.charAt(0));
System. out.println(s1 + ” ” + s2);
//获取中文第一个字母
return Collator.getInstance(Locale.CHINESE).compare(s1, s2);
}});
for(String s : names){
System. out.println(s);
}
23、java的接口可以继承多个接口如接口A和接口B,现在有接口C,可以这样写interface C extends A,B{},接口定义的成员变量可以看做是final static的,通过类名.变量名调用,不过一般接口中定义成员变量
24、java的方法内的变量(局部变量)不能带访问修饰符(private,protected,public),但是可以用final来修饰。
如 void test(){ private int i; System.out.println(i);}这个就是错误的,i的修饰符不能用private
25、关于i++和++i的介绍:
{int i = 0; int j = i++;},运算后的j的值是0,因为先把i的值赋予j,之后i再自身加上1.
{int i = 0; int z = ++z;},运算后z的结果是1,原因是x先自身加上1再把结果赋予z.
记住:++号在后面的意思是先赋值给其他变量后自身再加1,++号在前面就是先自身加上1把这时的结果再赋值给变量。
注意,如int x=0;System.out.println(x++);输出结果是0;System.out.println(++x);输出的结果是1
26、final 修饰的成员变量可以在定义的时候就赋值,不赋值在类上编译器就会提示“The blank final field i may not have been initialized”,我们也可以在构造方法中给它赋值,不然构造方法在编译器中也会出错误上述相同的错误。如果该变量同时加上static 修饰,那么构造方法中就不能赋值类,只能通过定义变量的时候直接赋值的一种方式类,因为static加载的顺序比构造方法还要早。
27、关于&和&&,|和||的区别
&和&&都是与的操作,其中与操作分为两种,&是普通与,&&是短路与。
&表示两边都要判断,不管左边是否满足,&&表示如果左边是false,就不需要判断右边了,返回的肯定的false。
|和||都是或的操作,|是普通的或操作,||是短路的或操作。
|表示两边都要进行判断,不管左右两边是否满足,||表示如左边是true,就不需要判断右边类,返回的结果肯定酒店true。
总结:在开发中为了考虑性能,都是选择&&(短路与)和||(短路或)
28、对于switch(),如果执行case语句,只要没有碰到break语句,就会去执行下一个case中符合条件的部分,知道碰到break或者没有case与之相匹配就退出循环。
29:java goto,continue,break,return的区别:
goto,是保留字段,目前在java中还没有使用,在c中可以接一个方法,直接执行另外一个方法。
continue:是加速循环,跳出本次循环,进行下一步的循环。
break:是跳出当前的循环,如嵌套多重循环,将只跳出当前的循环。
return:是跳出当前的调用对象。
要想跳出多重的循环,可以通过boolean标记,在循环里面设置新的值使其直接跳出循环。
需要注意,return和return false,return false是用在方法中有返回值的情况下,return用在没有方法体的返回中,两者要用好,混淆使用导致编译器会报错。
30、关于try,catch,finally的使用
try语句不能单独使用,否则编译器会报错,后面必须有catch或者finally中一个或多个。
31、java.util.NoSuchElementException异常
原因是在对Iterator进行迭代的时候,在while(it.hasNext())中,it.next()使用一次将指针移向下一位,要是在循环里面使用多次就会出现该问题。
解决办法是:先定义变量临时把it.next()保存下来。就不会出现该异常了。
32、关于+=运算符(如i+=j)
它不仅简单等效于i = i + j; 它还做了一步操作,就是强制将类型转化,最终等效于i = (i的类型) i + j;
如:int i= 5; long j = 8; 要是简单的 i =i + j;会编译不通过,要是i += j;就行,因为最终等效于i = (int)(i + j); 经过计算,结果也是一样。
33、成员变量具有缺省值,局部变量则没有缺省值。
34、JAVA基础-栈与堆,static、final修饰符、内部类和Java内存分配(http://blog.csdn.net/mooxin/article/details/7517847) 很不错的介绍,必须多回顾。
35、关于String和StringBuffer做字符串拼接的使用和区别。
根据34点的知识,可以发现string赋值的数据都是保存在栈中的,是不可变得,每次改变不会重新修改原来的值,都会重新创建新的字符串来存放。所以做在大量字符串的拼接的,用string+= 模式将使内存中开辟大量的内存空间存放每次的新字符串,很浪费资源。
36、Comparator和 Comparable的区别:
相同点:都是java的一个接口,并且是用来对自定义的class比较大小。
区别:Comparable定义在类的内部,实现Comparable接口要覆盖compareTo方法,在compareTo方法里面实现比较。是对对象来进行排序,它是定义在java.lang包下的,由于实现了compareTo方法,所以两个对象可以直接调用这个方法进行比较。,Comparator是定义在类的外部, 是对容器上的元素进行排序。它是定义在java.util包下。使用的是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。
37、UnChecked(RuntimeException)异常和Checked(Exception)异常, Throwable是所有java程序中错误处理的父类,有两个子类:Error和Exception。所有异常的都继承自Exception,RuntimeException也继承了Exception。其中RuntimeException是运行异常,jvm在编译的时候它和它的子类都不被检查的异常,所以调用者也不必强制去捕获该异常。其他继承Exceptio的异常都是编译会检查的异常,需要调用者去try,catch();RuntimeException异常不需要调用处理,运行时候发生异常,已经无法再让程序运行下去,所以,不让调用者处理,直接让程序停止,由调用者对代码进行修正。
37、在使用HttpURLConnection发送GET请求的时候,发现不能设置urlConnection.setRequestMethod(“GET”); 找了很久才发现,原来是设置了 httpurlconnection.setDoOutput(true); 这句会把请求改变为post请求了。所以get请求就把这句去掉就行。
