牛客题集

牛客错题集(一)

2018-06-03  本文已影响0人  菊地尤里

1.有如下4条语句:()
1、Integer i01=59;
2、int i02=59;
3、Integer i03=Integer.valueOf(59);
4、Integer i04=new Integer(59);
以下输出结果为false的是:
正确答案: C 你的答案: D (错误)
System.out.println(i01==i02);
System.out.println(i01==i03);
System.out.println(i03==i04);
System.out.println(i02==i04);

首先常量池这个概念,原来以为只要是一个整型,都会放进到常量池,比如,0,1,12222222等。查找后发现,Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127并且大于等于-128时才可使用常量池,因为他们至占用一个字节(-128~127);

再者Integer.valueOf方法中也有判断,如果传递的整型变量>= -128并且小于127时会返回IntegerCache类中一个静态数组中的某一个对象, 否则会返回一个新的Integer对象,代码如下

public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

所以如果你测试如下代码

public static void main(String[] args) {
         
        Integer a = 127;
        Integer b = 127;
         
        Integer c = 128;
        Integer d = 128;
         
        System.out.println(a == b);
        System.out.println(c == d);
    }

结合自动封装、常量池以及Integer.valueOf方法就不难得出,答案时true和false;

①无论如何,Integer与new Integer不会相等。不会经历拆箱过程,
②两个都是非new出来的Integer,如果数在-128到127之间,则是true,否则为false
java在编译Integer i2 = 128的时候,被翻译成-> Integer i2 = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存
③两个都是new出来的,都为false
④int和integer(无论new否)比,都为true,因为会把Integer自动拆箱为int再去比

2.Object中的equals方法是对象的比较,由于String类重写了该方法实现了内容的比较。

3.void waitForSignal()
{
Object obj = new Object();
synchronized(Thread.currentThread())
{
obj.wait();
obj.notify();
}
}
正确答案: A 你的答案: D (错误)
Which statement is true?
This code may throw an InterruptedException
This code may throw an IllegalStateException
This code may throw a TimeOutException after ten minutes
This code will not compile unless”obj.wait()”is replaced with”(Thread)obj).wait()”
Reversing the order of obj.wait()and obj.notify()may cause this method to complete normally
这题有两个错误的地方,第一个错误是 wait() 方法要以 try/catch 包覆,或是掷出 InterruptedException 才行
因此答案就是因为缺少例外捕捉的 InterruptedException

第二个错误的地方是, synchronized 的目标与 wait() 方法的物件不相同,会有 IllegalMonitorStateException ,不过 InterruptedException 会先出现,所以这不是答案

最后正确的程式码应该是这样:

 void waitForSignal() {
Object obj = new Object();
         synchronized (obj) {
             try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
obj.notify();
}
}

3.静态成员变量或静态代码块>mian方法>非静态成员变量或非静态代码块>构造方法

4.Which lines of the following will produce an error?

  1. byte a1 = 2, a2 = 4, a3;
  2. short s = 16;
  3. a2 = s;
  4. a3 = a1 * a2;

正确答案: A 你的答案: B (错误)
Line 3 and Line 4
Line 1 only
Line 3 only
Line 4 only

short类型转为byte类型出错
a1*a2结果为int类型,转为byte类型出错(java中如果碰到char、byte和short参与运算时,会自动将这些值转换为int类型然后再进行运算)

5.成员变量有初始值,而局部变量没有初始值

6.JDK1.8 接口可以有default、static方法

interface A {
abstract void a();
static void s() {
}
default void d(){

}
void b();//默认用abstract修饰
int a = 0;//默认用static final 修饰 

}

7.以下程序的输出结果为

class Base{
    public Base(String s){
        System.out.print("B");
    }
}
public class Derived extends Base{
    public Derived (String s) {
        System.out.print("D");
    }
    public static void main(String[] args){
        new Derived("C");
    }
}

正确答案: D 你的答案: A (错误)
BD
DB
C
编译错误

子类构造方法在调用时必须先调用父类的,由于父类没有无参构造,必须在子类中显式调用,修改子类构造方法如下即可:
public Derived(String s){
super("s");
System.out.print("D");
}

8.finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源的回收,例如关闭文件等。 错
可以覆盖此方法提供垃圾收集时的其他资源的回收,但是不是关闭文件这种操作而是一些通过调用了native方法产生的资源的释放.

深入理解java虚拟机中说到:
当对象不可达后,仍需要两次标记才会被回收,首先垃圾收集器会先执行对象的finalize方法,但不保证会执行完毕(死循环或执行很缓慢的情况会被强行终止),此为第一次标记。第二次检查时,如果对象仍然不可达,才会执行回收。

9.依然是JVM
jvm中垃圾回收分为scanvenge gc和full GC,其中full GC触发的条件可能有哪些
正确答案: C D E 你的答案: B D E (错误)
栈空间满
年轻代空间满
老年代满
持久代满
System.gc()

1,新生代:(1)所有对象创建在新生代的Eden区,当Eden区满后触发新生代的Minor GC,将Eden区和非空闲Survivor区存活的对象复制到另外一个空闲的Survivor区中。(2)保证一个Survivor区是空的,新生代Minor GC就是在两个Survivor区之间相互复制存活对象,直到Survivor区满为止。
2,老年代:当Survivor区也满了之后就通过Minor GC将对象复制到老年代。老年代也满了的话,就将触发Full GC,针对整个堆(包括新生代、老年代、持久代)进行垃圾回收。
3,持久代:持久代如果满了,将触发Full GC。

简单来说
年轻代的GC 叫 young GC ,有时候也叫 minor GC 。年老代或者永久代的 GC ,叫 full GC ,也叫 major GC 。
持久代为方法区,方法区又叫永久代,用于存放静态文件,满了以后也会触发负full GC

10.以下哪几种方式可用来实现线程间通知和唤醒:( )
正确答案: A C 你的答案: B D (错误)
Object.wait/notify/notifyAll
ReentrantLock.wait/notify/notifyAll
Condition.await/signal/signalAll
Thread.wait/notify/notifyAll

wait()、notify()和notifyAll()是 Object类 中的方法
从这三个方法的文字描述可以知道以下几点信息:
1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;
有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法
(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问
题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然
应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程
的锁,如果通过线程来操作,就非常复杂了。
上面已经提到,如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即
锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者
synchronized方法)。
调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,
等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从
而让其他线程有机会继续执行,但它并不释放对象锁);
notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象
的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。
同样地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用
notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。
Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,在阻塞队列那一篇博文中就讲述到了,阻塞队列实际上是使用了Condition来模拟线程间协作。
Condition是个接口,基本的方法就是await()和signal()方法;
Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用Conditon中的await()对应Object的wait(); Condition中的signal()对应Object的notify(); Condition中的signalAll()对应Object的notifyAll()

上一篇 下一篇

猜你喜欢

热点阅读