Java面试题

1、String为什么被设计成不可变

首先理解String为什么不可变

  • String类是final的,不可以被继承
    public final class String
  • 内部保证了引用的不可变和对外的不可见
    private final char value[];

String为什么被设计成不可变

  • 当两个变量指向同一个String,如果对其中一个进行修改,不会影响另一个的值
  • hashcode值不变,不用每次去计算,效率高
  • 在多线程环境下,由于操作对象不可变,保证线程安全

拓展:如何实现一个不可变的类

类使用final修饰
类的所有成员变量使用final修饰
不提供set方法
如果要修改类的状态,必须返回一个新的对象

2、CAS

什么是CAS?

  • Compare And Swap 比较并交换
  • 其作用是让CPU比较内存中某个值C是否和预期的值A相同,如果相同(没有其他线程对其进行操作)则将这个值更新为新值B,不相同(被其他线程修改过)则不做更新。
    Java面试题_第1张图片
    理解:修改之前获取的(待修改)值A,业务逻辑计算的新值B,以及待修改值对应的内存位置的C。

缺点:
ABA问题:通过版本号解决
自旋时间过长:在Unsafe的实现中使用了自旋锁的机制。在该环节如果CAS​操作失败,就需要循环进行CAS操作(do while循环同时将期望值更新为最新的),如果长时间都不成功的话,那么会造成CPU极大的开销。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升。
只能保证一个共享变量的原子操作:解决方法是把多个共享变量合并成一个共享变量进行CAS操作

3、公平锁和非公平锁

Java面试题_第2张图片

4、自旋锁

占用CPU,不断尝试获取锁
Java面试题_第3张图片

5、抽象类和接口的区别

Java面试题_第4张图片
java中为什么提供了接口还要提供抽象类?

当一个接口中有很多抽象方法,并且其有很多实现类,但是有些类不需要接口中某些方法。这样就会造成大量冗余的实现接口的方法。因为继承接口的类必须重写接口中的所有方法

改进:增加一个抽象类
找出接口中必要的方法,也就是子类必须实现的方法,定义成抽象方法,交由子类实现

通过接口和抽象类的结合,避免了在实现接口的子类中出现大量的“无意义”实现,这个“无意义”实现,被缓冲到了抽象类中,完美展现了代码复用(可以把抽象类理解成接口和实现类之间的缓冲)。

6、动态代理

代理三要素:
Java面试题_第5张图片
静态代理的优点:拓展原功能,不侵入源代码
静态代理的缺点:如果有10个真实对象,同时要代理的方法不同
在这里插入图片描述
方案一:创建不同的代理(重复创建多个逻辑相同,仅仅RealObject引用不同的Proxy。)
在这里插入图片描述
方案二:创建一个代理(导致proxy的膨胀)
通过创建一个proxy,持有不同的realObject,实现Action1、Action2、Action3接口在这里插入图片描述
动态代理:在运行时,动态生成一个持有RealObject、并实现代理接口的Proxy,同时注入我们相同的扩展逻辑

简单理解,动态代理就是我们上面提到的方案一,只不过这些proxy的创建都是自动的并且是在运行期生成的。

使用方式:将要扩展的功能写在一个InvocationHandler 实现类里

public class DynamicProxyHandler implements InvocationHandler {
    private Object realObject;

    public DynamicProxyHandler(Object realObject) {
        this.realObject = realObject;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //代理扩展逻辑
        System.out.println("proxy do");

        return method.invoke(realObject, args);
    }
}
public static void main(String[] args) {
        RealObject realObject = new RealObject();
        Action proxy = (Action) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Action.class}, new DynamicProxyHandler(realObject));
        proxy.doSomething();
}

Proxy.newProxyInstance 传入的是一个ClassLoader, 一个代理接口,和我们定义的handler,返回的是一个Proxy的实例。

你可能感兴趣的:(工作专区,java,jvm,开发语言)