面试中会遇到的 15 个 Java 冷知识,你懂多少?
往期热门文章:
1、一个比 Spring Boot 快 44 倍的 Java 框架
3、太牛逼了!项目中用了Disruptor之后,性能提升了2.5倍
来源:sanesee.com/article/15-forgettable-java-question
3.String字符串常量
以上代码运行结果为:true。这就说明a和b引用的是同一String对象。
以上代码启动交换a和b所引用的对象,但实际编译执行会发现没有成功交换。这也就证明Java不是按引用调用的,a和b仅代表两个Person对象的值,而不是代表两个对象的引用,在参数传递上与int等基本类型的值没有区别。
14.限定泛型变量
限定类型可以有多个,使用“&”分隔。比如:
<T super Comparable & Serializable>
无论何时定义一个泛型类型,都自动提供了一个相应的原始类型。程序运行时,擦除类型变量,并替换为第一个限定类型(比如
SaneseeDemo
不能用类型参数代替基本类型。因此没有SaneseeDemo
不能实例化参数化类型的数组,例如:
这是因为array被擦除类型之后,它的类型为SaneseeDemo [],元素类型为SaneseeDemo,则相应的原始类型为Object[],那么就可以往里面添加任何类型的元素,也就会出现类型错误。
如果需要使用参数化类型对象,只有一种安全而有效的方法:使用ArrayList。
同时,也不能在静态域或方法中引用类型变量,无法通过编译,比如:
因为类型擦除之后,就变为原始类型Object了。如果它可以正常执行,那么任何类型的T最终都会变为Object,那么不管传入什么类型的T,最终只能获取相同的单例,这与我们想要的功能是不一致的。
不能抛出或捕获泛型类型的异常。
Sanesee
不能向<? extends Employee >类型的变量调用set方法的。假设它可以执行,因为程序无法知道这个变量的具体类型,它的类型可能是Manager,也可能是Executive,那么就会出现类型转换错误。只能向这个变量调用get方法,因为程序把获得的值自动转换为Employee类型,子类型可以自动转换为父类型。
不能向<? super Employee>类型的变量调用get方法,因为程序无法知道返回的具体类型,它的类型可能是Person,也有可能是Object。只能调用set方法,因为不管是传入Employee还是其子类,都可以成功执行。
总之,带有super限定的通配符可以向泛型对象写入,带有extends限定的通配符可以从泛型对象读取。而Sanesee和Sanesee类型的不同点在于,Sanesee类型擦除以后就是Object了,所以根本无法使用Object类型的对象去调用它,只有将它放在静态方法中执行一些简单的操作。
15.多线程
当对一个线程调用interupt方法时,线程的中断状态将被置位。每个线程都具有一个boolean类型的中断标志,通过它来判断线程是否被中断:
Thread.currentThread().isInterupted()
线程的run方法不能抛出任何受检异常,而受检异常会导致程序终止。然而,不需要使用catch语句来处理可以被传播的异常。相反,就在线程死亡之前,异常被传递到一个用于未捕获异常的处理器。可以使用setUncaughtExceptionHandler为任何线程安装一个处理器。
可以调用ReentrantLock的lock和unlock方法获取锁或解除锁,也可以调用条件对象Condition(由ReentrantLock的newCondition方法获取)的await和singal实现等待或通知功能。两种方式的不同点在于,lock获取到的锁,会等到程序执行结束调用unlock时才会释放,哪怕正在执行耗时比较长的任务,或者处于等待状态。而Condition在需要等待的地方调用await方法,进入等待集,当锁可用时,该线程继续保持阻塞状态,直到另一个线程调用同一条件上的singalAll方法为止(singal方法可以随机解除一个线程的阻塞状态)。然而,最好不使用Lock/Condition或synchronized关键字,可以使用java.util.concurrent包中的任何一种机制,它会为你处理所有的加锁。如果一定得使用关键字,优先使用synchronized,这样可以减少代码的数量,其次才是Lock/Condition。
ThreadLocal类型可以使得每个线程拥有自己的独立的变量。volatile类型为实例域的访问提供了一种免锁机制,可以使得线程每次都可以访问到最新的值(volatile变量不能保证原子性)。
ReentrantReadWriteLock类适合读数据多而写数据少的情形:
ReentrantReadWriteLock.readLock();//多个线程共享,排斥写
ReentrantReadWriteLock.writeLock();//单个线程使用,排斥读写
官方建议放弃使用stop和suspend方法,是因为stop强制终止一个线程是极不安全的操作,而suspend本身容易导致死锁。
在阻塞队列中,生产者向队列中插入元素,消费者向队列获取元素。当队列为空时,消费者线程会被阻塞;当队列慢时,生产者线程将被阻塞。LinkedBlockingQueue可以不限容量,而ArrayBlockingQueue需要指定容量。PriorityBlockingQueue是一个带有优先级的队列,DelayQueue需要在指定延迟慢之后才能移除元素。LinkedTransferQueue的transfer(item)方法允许生产者线程等待,直到消费者准备就绪并移除这个item元素。
java.util.concurrent包提供了Map、集合等的并发实现,ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet和ConcurrentLinkedQueue,允许并发访问数据结构。
任何集合类型可以通过使用同步包装器变成线程安全的(当然最好使用java.util.concurrent包中定义的集合,因为它们经过了精心的设计):
List
Map<K, V> map = Collections.synchronizedMap(new HashMap<K, V>());
Runnable封装了一个异步运行的任务,可以看做没有参数和返回值的异步方法。Callable与Runnable类似,但它有返回值。
Executors是一个线程执行器,用于管理线程的创建和执行,使用它可以创
建多种线程池,常见线程池如下:
Executors.newCachedThreadPool:必要时创建新线程,空闲线程会保留60秒。
Executors.newFixedThreadPool:创建固定容量的线程池。
Executors.newSingleThreadPool:创建只有一个线程的线程池。
Executors.newScheduledThreadPool:用于预定指定的线程池。
最近热文阅读:
最近热文阅读: