【java面试题】java常见面试题目(含答案)
如何用数组实现队列?
用数组实现队列时要注意 溢出 现象,这时我们可以采用循环数组的方式来解决,即将数组收尾相接。使用front指针指向队列首位,tail指针指向队列末位。 (推荐学习:java常见面试题)
内部类访问局部变量的时候,为什么变量必须加上final修饰?
因为生命周期不同。局部变量在方法结束后就会被销毁,但内部类对象并不一定,这样就会导致内部类引用了一个不存在的变量。
所以编译器会在内部类中生成一个局部变量的拷贝,这个拷贝的生命周期和内部类对象相同,就不会出现上述问题。
但这样就导致了其中一个变量被修改,两个变量值可能不同的问题。为了解决这个问题,编译器就要求局部变量需要被final修饰,以保证两个变量值相同。
在JDK8之后,编译器不要求内部类访问的局部变量必须被final修饰,但局部变量值不能被修改(无论是方法中还是内部类中),否则会报编译错误。利用javap查看编译后的字节码可以发现,编译器已经加上了final。
long s = 499999999 * 499999999 在上面的代码中,s的值是多少?
根据代码的计算结果,s的值应该是-1371654655,这是由于Java中右侧值的计算默认是int类型。
非静态内部类能定义静态方法吗?
public class OuterClass{private static float f = 1.0f;
class InnerClass{
public static float func(){return f;}
}
}
以上代码会出现编译错误,因为只有静态内部类才能定义静态方法。
Lock 和 Synchronized 有什么区别?
1. 使用方法的区别- **Synchronized**:在需要同步的对象中加入此控制,`synchronized`可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
- **Lock**:需要显示指定起始位置和终止位置。一般使用`ReentrantLock`类做为锁,多个线程中必须要使用一个`ReentrantLock`类做为对象才能保证锁的生效。且在加锁和解锁处需要通过`lock()`和`unlock()`显示指出。所以一般会在`finally`块中写`unlock()`以防死锁。
2. 性能的区别
`synchronized`是托管给JVM执行的,而`lock`是java写的控制锁的代码。在Java1.5中,`synchronize`是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象,性能更高一些。但是到了Java1.6,发生了变化。`synchronize`在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在Java1.6上`synchronize`的性能并不比Lock差。
- **Synchronized**:采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着 **其他线程只能依靠阻塞来等待线程释放锁**。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。
- **Lock**:用的是乐观锁方式。所谓乐观锁就是,**每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止**。乐观锁实现的机制就是`CAS`操作。我们可以进一步研究`ReentrantLock`的源代码,会发现其中比较重要的获得锁的一个方法是`compareAndSetState`。这里其实就是调用的CPU提供的特殊指令。
3. `ReentrantLock`:具有更好的可伸缩性:比如时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票。
float 变量如何与 0 比较?
folat类型的还有double类型的,这些小数类型在趋近于0的时候直接等于0的可能性很小,一般都是无限趋近于0,因此不能用==来判断。应该用|x-0|<err来判断,这里|x-0|表示绝对值,err表示限定误差。
//用程序表示就是fabs(x) < 0.00001f
如何新建非静态内部类?
内部类在声明的时候必须是 Outer.Inner a,就像int a 一样,至于静态内部类和非静态内部类new的时候有点区别:
Outer.Inner a = new Outer().new Inner()(非静态,先有Outer对象才能 new 内部类)
Outer.Inner a = new Outer.Inner()(静态内部类)
Java标识符命名规则
可以包含:字母、数字、$、_(下划线),不可用数字开头,不能是 Java 的关键字和保留字。
你知道哪些JDK中用到的设计模式?
装饰模式:java.io
单例模式:Runtime类
简单工厂模式:Integer.valueOf方法
享元模式:String常量池、Integer.valueOf(int i)、Character.valueOf(char c)
迭代器模式:Iterator
职责链模式:ClassLoader的双亲委派模型
解释器模式:正则表达式java.util.regex.Pattern
ConcurrentHashMap如何保证线程安全
JDK 1.7及以前:
ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
JDK 1.8:
Segment虽保留,但已经简化属性,仅仅是为了兼容旧版本。
插入时使用CAS算法:unsafe.compareAndSwapInt(this, valueOffset, expect, update)。 CAS(Compare And Swap)意思是如果valueOffset位置包含的值与expect值相同,则更新valueOffset位置的值为update,并返回true,否则不更新,返回false。插入时不允许key或value为null
与Java8的HashMap有相通之处,底层依然由“数组” 链表 红黑树;
底层结构存放的是TreeBin对象,而不是TreeNode对象;
CAS作为知名无锁算法,那ConcurrentHashMap就没用锁了么?当然不是,当hash值与链表的头结点相同还是会synchronized上锁,锁链表。
Thread.sleep() & Thread.yield()&Thread.wait()的区别
sleep()和yield()都会释放CPU。
sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。
Thread.sleep和Thread.yield()不会导致锁行为的改变,如果当前线程是拥有锁的,那么Thread.sleep不会让线程释放锁。如果能够帮助你记忆的话,可以简单认为和锁相关的方法都定义在Object类中,因此调用Thread.sleep是不会影响锁的相关行为。
Thread.sleep和Object.wait都会暂停当前的线程,对于CPU资源来说,不管是哪种方式暂停的线程,都表示它暂时不再需要CPU的执行时间。OS会将执行时间分配给其它线程。区别是,调用wait后,需要别的线程执行notify/notifyAll才能够重新获得CPU执行时间。
arraylist 和 linkedlist 的区别?
ArrayList和LinkedList两者都实现了List接口,但是它们之间有些不同。
(1)ArrayList是由Array所支持的基于一个索引的数据结构,所以它提供对元素的随机访问
(2)与ArrayList相比,在LinkedList中插入、添加和删除一个元素会更快
(3)LinkedList比ArrayList消耗更多的内存,因为LinkedList中的每个节点存储了前后节点的引
以上是 【java面试题】java常见面试题目(含答案) 的全部内容, 来源链接: utcz.com/a/123739.html