如下代码输出结果为:
public class Base {
private String basename = "base" ;
Base(){
callName();
}
public void callName (){
System.out.println(basename);
}
}
public class Sub extends Base {
private String basename = "sub" ;
@Override
public void callName (){
System.out.println(basename);
}
}
public class Test {
public static void main (String[] args){
Base b = new Sub();
}
}
如下代码输出结果为:null———>因为当调用new Sub()调用它的默认无参构造器时,默认就是调用父类的无参构造器,而在父类的无参构造器中调用了callName(),在基类中已经Override重写了该方法,所以这里是要调用基类重写的callName方法,而此时基类并没有被实例化,因此此时调用callName输出的baseName值为null(还未被实例化)
这里需要知道类加载的顺序:父类的静态代码块->子类的静态代码块->父类的非静态代码块->父类的构造器->子类的非静态代码块->子类的构造器->
关于HashMap和HashTable说法错误的是B:
A.两者都是用key-value 方式获取数据
B.Hashtable允许null 值作为key和value ,而HashMap不可以
C.HashMap不是同步的,而Hashtable是同步的
D.迭代HashMap采用快速失败机制,而Hashtable不是
HashTable和HashMap都实现了Map接口,因此都是使用key-value键值对来保存和获取数据;
对HashMap来说,他的键和值都可以为null,而对HashTable来说,他的键和值都不可以为null
HashTable的方法是Synchronized线程同步的,而HashMap不是,多个线程对HashMap的方法进行访问时,需要手动设置他的方法为线程同步
如下代码的运行结果:
public class Example{
String str=new String("tarena" );
char []ch={'a' ,'b' ,'c' };
public static void main (String args[]){
Example ex=new Example();
ex.change(ex.str,ex.ch);
System.out .print(ex.str+" and " );
System.out .print(ex.ch);
}
public void change (String str,char ch[]){
str="test ok" ;
ch[0 ]='g' ;
}
}
java中只有值传递,没有引用传递;对于引用类型来说,传递的值他的地址,因此引用类型变量的变量传递是会改变原来的值的;而String类型是一个特殊,因为在java中String类型是不可变的,java在内存中专门为String开辟了一个字符串常量池,用来锁定数据不被篡改,因此在change方法中,str是一个新的字符串对象,与原来的str对象毫无关系;因此最后的输出结果为: tarena and gbc
最终s的值为:
int i = 5 ;
int s = (i ++)+(++i )+(i --)+(--i );
最终s的值为24,而在s中的括号其实对于自增和自减都是没有用的,就是说s右边的运算式有没有括号都是一样的,i++仍然在括号之外才进行自增,i–仍然在括号之外才进行自减,最终s的值为5+7+7+5=24;而不是6+7+6+5
子类重写父类方法需要满足两同两小一大原则:方法名相同、参数列表相同;子类返回值类型要小于等于父类返回值类型、子类抛出的异常要小于等于父类抛出的异常类型;子类访问权限要大于等于父类的访问权限
如下代码编译运行是否能通过
class Test {
public static void hello () {
System.out .println("hello" );
}
}
public class MyApplication {
public static void main (String[] args) {
Test test=null ;
test.hello();
}
}
最终能通过编译运行并打印hello;因为hello是Test的静态成员方法,属于类,所以不管是否new创建了该类的对象,它都会进行初始化一次;因此最终还是能够正确调用hello方法
java的访问权限有public/protectes/default/private,其中default不能用于修饰变量;
StringBuffer和StringBuilder都可以用来创建可变字符串变量,区别在于StringBuffer是线程安全的,而StringBuilder是非线程安全的
对于一个ArrayList集合类的对象,在进行迭代的过程中进行指定的删除操作时,如何进行安全且正确的删除操作:
Iterator it = list.iterator ()
int index = 0
while (it.hasNext ())
{
Object obj = it.next ()
if (needDelete(obj)) //needDelete返回boolean,决定对应的数据是否要删除
{
//todo delete
}
index ++
}
A.it .remove ()
B.list .remove (obj)
C.list .remove (index)
D.list .remove (obj,index)
正确答案为A,因为在进行删除操作时,迭代器已经获取到了,而此时如果使用list对象的remove方法进行删除删除操作的话,就会产生错误;因此,处于安全的顾虑,在这里我们可以直接使用迭代器Iterator的remove方法来删除此时遍历到的数据
下面定义了两个线程,假设在线程t1在线程t2启动之前已经完成启动,则下面输出结果为:
public static void main (String[]args)throws Exception {
final Object obj = new Object();
Thread t1 = new Thread() {
public void run () {
synchronized (obj) {
try {
obj.wait();
System.out.println("Thread 1 wake up." );
} catch (InterruptedException e) {
}
}
}
};
t1.start();
Thread.sleep(1000 );
Thread t2 = new Thread() {
public void run () {
synchronized (obj) {
obj.notifyAll();
System.out.println("Thread 2 sent notify." );
}
}
};
t2.start();
}
A.Thread 1 wake up
Thread 2 sent notify.
B.Thread 2 sent notify.
Thread 1 wake up
C.A、B皆有可能
D.程序无输出卡死
最终结果为B,因为已经t1在t2启动之前已经启动了,这时候t1获得了obj对象的锁进入运行状态,此时在t1的run中调用了wait方法释放了锁并处于等待状态,而后t2启动,调用了notifyAll方法来通知唤醒obj对象对应的所有等待线程,而此时t2还没有释放obj锁,所以t1还是处于就绪装台;因此先执行t2线程,然后t2线程执行完之后t1才由就绪状态进入运行状态