Thread和Runnable之间的区别
线程实现的两种方法,只是Thread只能是继承,而Runnable是接口
Java只支持一个继承,所以你的类如果还要继承自其它的类,就不能用Thread了,就可以用Runnable接口了。
Thread同样是实现了Runnable,所以Thread还是Runnable的实现。
两种方法均需执行线程的start方法为线程分配必须的系统资源、调度线程运行并执行线程的run方法。
线程的消亡不建议通过调用一个stop()命令。而是让run()方法自然结束
在创建java.lang.Thread类的一个实例时就会"诞生"一个新线程。Thread对象表示Java解释器中一个实际的线程,并作为控制和协调其执行的一个句柄。利用Thread对象,可以启动线程、等待其完成、要求它睡眠一段时间,或者时中断其活动。Thread类的构造函数可接受线程应当在哪里开始执行等等有关的信息。从概念上说,我们只希望告诉它所要运行的方法,但是由于Java中不存在方法指针(至少没有这种意义的方法指针),因此我们并不能直接指定方法。与此不同,必须采取一种迂回线路,即使用java.lang.Runnable接口来创建一个对象,此对象中包含一个“可运行”的方。
Runnable接口只定义了唯一的一个通用方法。
public interface Runnable {
abstract public void run();
}
每个线程的生命周期都始于执行Runnable对象中的run()方法。此对象是传递给线程构造函数的“目标对象”。run()方法可以包含任何代码、但它必须是公共的,不仅没有任何实参,而且也没有返回值,另外不会抛出任何受查异常。任何包含有合适的run()方法的类都可以声明它实现了Runnable接口。此类的实例就是一个可运行的对象,它可以作为一个线程的目标。如果不希望将run()直接放在对象中(而且通常不会这样做),则可以建立一个适配器类,由它作为一个Runnable类。适配器的run()方法则能够在线程启动后调用任何需要的方法。
抽象类和接口的区别
在类来继承抽象类时,只需实现部分具体方法和全部抽象方法,而实现接口则要实现里面的全部方法。
在接口中无成员变量,而抽象类中可有成员变量。
在Java中引进接口主要是为了解决多继承的问题。
String与StringBuffer的比较
例子:
public class Test {
public static void
stringReplace(String text) {
text = text.replace("j",
"i");
System.out.println(text);
text=text.replaceAll("j",
"i");
System.out.println(text);
}
public static void
bufferReplace(StringBuffer text) {
text = text.append("C");
}
public static void
main(String args[]) {
String textString = new
String("java");
StringBuffer textBuffer = new
StringBuffer("java");
stringReplace(textString);
bufferReplace(textBuffer);
System.out.println(textString +
textBuffer);
}
}
结果是:
iava
iava
javajavaC
从表面上看String类型的对象改变了值,但事实是他不能改变值,只能改变指向的地址
StringBuffer则不同,直接改变指向的地址中保留的值
String a =
"a";//假设a指向地址0x0001,
a =
"b";//重新负值后a指向地址0x0002,但0x0001地址中保存的"a"依旧存在,但已经不再是a所指向的。
String a = "yacht1" + "yacht2" + "yacht3"
+ "yacht4";
StringBuffer sb = new StringBuffer();
sb.append("yacht1") ; sb.append("yacht2"); sb.append("yacht3") ;
sb.append("yacht4"); String a =
sb.toString();
我们需要理解程序过程的两个时期,一个是编译时,一个是运行时,在编译时,编译器会对你的程序做出优化,所以红色的String
a会被优化成yacht1yacht2yacht3yacht4,而蓝色的StringBuffer只会在运行时才处理;结果是蓝色比红色效率高。
大家可以测试下面程序:
String a=null;
StringBuffer sb = new
StringBuffer();
System.out.println(System.currentTimeMillis());
for (int i = 0; i < 100000;
i++) {
a +=
String.valueOf(i);
}
System.out.println(System.currentTimeMillis());
for (int i = 0; i < 100000;
i++) {
sb.append(i);
}
a = sb.toString();
System.out.println(System.currentTimeMillis());
"=="
与"equals()区别
String s1 =
"hello";String s2 = "hello";
String s3 = new
String("hello");String s4 = new String("hello");
System.out.println("s1.equals(s2)
= " + s1.equals(s2));
System.out.println("s1 == s2 ?
" + (s1 == s2));
System.out.println("s3.equals(s4)
= " + s3.equals(s4));
System.out.println("s3 == s2 ?
" + (s3 ==s2));
结果是:
s1.equals(s2) = true
s1 == s2 ? true
s3.equals(s4) = true
s3 == s2 ? false
结论:"=="比较的是两个对象的地址,"equals()"比较的是两个对象的值.
自动装箱拆箱功能(jdk1.5)
来看一个小例子:
public class Test{
public static void main(String[] args){
Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1==i2);
}
}
/*
public class Test{
public static void main(String[] args){
Integer i1 = 200;
Integer i2 = 200;
System.out.println(i1==i2);
}
}
*/
结果前者为true,后者为false,这有点令人惊讶,两个例子语法完全一样,只不过改了个数值而已,结果却相反,其实这与==运算符的比较有关,==操作符既可以用来比较两个基本数据类型的变量值是否相等,也可以用于判断两个对象变量名称是否引用至同一个对象.在jdk5.0开始,有了自动装箱这个功能,对于值从-128到127之间的值,它们被装箱为Integer对象后,会存在内存中被重用,所以上面的第一个例子使用==进行比较时,i1和i2实际上引用至同一个对象,结果返回true,如果超出此范围,被装箱后的Integer对象并不会被重用,即相当于每次装箱时都新建一个Integer对象,所以后面的例子使用==进行比较时,i1和i2引用的是不同的对象.如果使用"i1.equals(i2)"那就是返回true了.我们使用自动装箱和拆箱功能时一定要小心.
清单1
// 示范 自动装箱和拆箱
class AutoBox {
public static void main(String args[]) {
Integer iOb
= 100; // 自动装箱一个int
int i = iOb;
// 自动拆箱
System.out.println(i + " " + iOb); //
运行结果显示 100 100
}
}
清单2
// 对方法参数和返回值进行自动装箱和拆箱
class AutoBox2 {
// 取一个Integer 型参数,返回一个int型值
static int m(Integer v) {
return v ;
// 自动拆箱为int 型
} public static void main(String args[])
{ Integer iOb
= m(100); System.out.println(iOb);
}
}
清单 3
// 产生在表达式内的自动装箱和拆箱
class AutoBox3 {
public static void main(String args[]) {
Integer iOb,
iOb2;
int i;
iOb =
100;
System.out.println("Original value of iOb: " + iOb);
++iOb;
System.out.println("After ++iOb: " + iOb);
// 在这里,iOb先拆箱,
然后表达式求值,表达式的值被重新装箱指派给iOb2.
iOb2 = iOb +
(iOb / 3);
System.out.println("iOb2 after expression: " + iOb2);
//
同样的表达式被求值,但结果没有装箱.
i = iOb +
(iOb / 3);
System.out.println("i after expression: " + i);
}
}
清单 4
class AutoBox4 {
public static void main(String args[]) {
Integer iOb
= 100;;
Double dOb =
98.6;
dOb = dOb +
iOb;
System.out.println("dOb after expression: " + dOb);
}
}
清单 5
// 自动装箱和拆箱一个Boolean 和 Character.
class AutoBox5 {
public static void main(String args[]) {
//
Autobox/unbox a boolean.
Boolean b =
true;
//
当在条件表达式中使用b时自动拆箱.
if(b)
System.out.println("b is true");
//
自动装箱和拆箱一个 char.
Character ch
= 'x'; // box a char
char ch2 =
ch; // unbox a char
System.out.println("ch2 is " + ch2);
}
}
override和overload区别
两个函数同名但参数不同时使用overload以使系统承认它们是合法的。
而override只用于类方法中,当子类继承了父类的同名同参数方法时使用
override.(当然父类的方法必须有virtual说明)。如果在写子类方法中不用
override说明,则在把子类对象作为父类类型调用时就会调用父类的方法而不是
Hashtable和HashMap类
有三个重要的不同之处。第一个不同主要是历史原因。Hashtable是基于陈旧的Dictionary类的,HashMap是Java
1.2引进的Map接口的一个实现。
也许最重要的不同是Hashtable的方法是同步的,而HashMap的方法不是。这就意味着,虽然你可以不用采取任何特殊的行为就可以在一个多线程的应用程序中用一个Hashtable,但你必须同样地为一个HashMap提供外同步。一个方便的方法就是利用Collections类的静态的synchronizedMap()方法,它创建一个线程安全的Map对象,并把它作为一个封装的对象来返回。这个对象的方法可以让你同步访问潜在的
HashMap。这么做的结果就是当你不需要同步时,你不能切断Hashtable中的同步(比如在一个单线程的应用程序中),而且同步增加了很多处理费用。
第三点不同是,只有HashMap可以让你将空值作为一个表的条目的key或value。HashMap中只有一条记录可以是一个空的
key,但任意数量的条目可以是空的value。这就是说,如果在表中没有发现搜索键,或者如果发现了搜索键,但它是一个空的值,那么get()将返回
null。如果有必要,用containKey()方法来区别这两种情况。
一些资料建议,当需要同步时,用Hashtable,反之用HashMap。但是,因为在需要时,HashMap可以被同步,HashMap
的功能比Hashtable的功能更多,而且它不是基于一个陈旧的类的,所以有人认为,在各种情况下,HashMap都优先于Hashtable。
Collection 和 Collections的区别
答:Collection是集合类的上级接口,继承与他的接口主要有Set
和List.
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作
char型变量中能不能存贮一个中文汉字?
答:是能够定义成为一个中文的,因为java中以unicode编码,一个char占16个字节,所以放一个中文是没问题的
如果用gbk的本地编码的话可以.如果用utf-8的话,可能不行。
简述synchronized和java.util.concurrent.locks.Lock的异同
答:主要相同点:Lock能完成synchronized所实现的所有功能
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。