【面试系列】Java面试知识篇(六)

发布于:2022-12-15 ⋅ 阅读:(566) ⋅ 点赞:(0)

在这里插入图片描述
个人简介:

📦个人主页:赵四司机
🏆学习方向:JAVA后端开发
📣种一棵树最好的时间是十年前,其次是现在!
🔔博主推荐网站:牛客网 刷题|面试|找工作神器
💖喜欢的话麻烦点点关注喔,你们的支持是我的最大动力。

前言

由于很快到了金九银十的秋招季节,博主最近也在找一些面经资源,但是发现很多都不全,后来我发现了牛客网这个网站,发现里面不仅可以看面经,还能刷题模拟面试,要是你要找各种招聘信息也可以在上面找到,我愿称之为程序员必备网站,下面把它推荐给你们!
链接地址:牛客网

在这里插入图片描述

48.序列化&反序列化

  • 序列化:需要持久化Java对象而将数据结构或对象转换成二进制字符流的过程。

  • 反序列化:将序列化过程中产生的二进制字符流转换成数据结构或者Java对象。

  • 使用场景:

    • 对象在进行网络传输之前进行序列化,接收到序列化对象之后再进行反序列化。
    • 将对象存储到文件中时候进行序列化,将对象从文件中读出时候进行反序列化。
    • 将对象存储到缓存数据库时需要进行序列化,将对象从缓存数据库中读取出来时候进行反序列化。
  • 序列化与反序列化发生在OSI七层模型中的表示层,属于TCP/IP协议应用层的一部分。

49.线程池的7个参数含义

ThreadPoolExecutor构造方法中的7个参数:

  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • workQueue:等待队列,当任务提交时候,如果线程池中线程数量大于等于corePoolSize时,就将任务封装成worker对象放入等待队列。
  • keepAliveTime:线程池维护线程所允许最大的空闲时间。假如线程池中线程数大于corePoolSize,这时候如果没有新任务提交,核心线程之外的空闲线程不会立即被销毁,而是会等待,直到等待时间超过keepAliveTime。
  • unit:空闲时间单位
  • threadFactory:用来创建新的线程
  • handler:它是RejectedExecutionHandler类型的变量,表示线程池的饱和策略,当阻塞队列满了并且没有空闲线程(即达到最大线程数),这时候有新任务过来就会采用一种策略来处理这个任务。线程池提供了4中策略:
    • AbortPolicy:直接抛出异常,这是默认策略。
    • CallerRunsPolicy:用调用者所在线程来执行任务,表现为当前页面被阻塞,直到当前调用者所在线程处理完毕。
    • DiscardOldestPolicy:丢弃阻塞队列中最前面的任务,并执行当前任务。
    • DiscardPolicy:直接抛弃任务。

简单来说,在执行execute()方法时候如果状态一直是Running时,执行过程如下:

  1. 如果任务数小于核心线程数,这时候即使核心线程有空闲的也依然还会创建新的线程,而且核心线程是不会被销毁的;
  2. 如果任务数大于等于核心线程数,但是阻塞队列未满,则将任务添加到阻塞队列中;
  3. 如果任务数大于核心线程数,而且阻塞队列已满,但是线程数未达到最大线程数,这时候就会创建新的线程来执行新的任务;
  4. 如果任务数大于核心线程数,且阻塞队列已满,线程数达到最大线程数,这时候有新任务过来则根据拒绝策略来处理该任务。

50.为什么不推荐使用Executors中执行的默认方法来创建线程池

  • 默认方法中不能指定阻塞队列的长度,默认是整形的最大值,假如有大量的资源过来会占用很大的内存空间
  • 默认方法中没有指定对应的拒绝策略,如果请求数量过多会对服务器造成很大的隐患

51.代理模式

代理模式是指使用代理对象来代替对真实对象的访问,这样就可以在不修改原目标对像的情况下提供额外的功能操作,扩展目标对像的功能,比如在某个对象的执行方法前后增加一些自定义的操作。通过代理模式,可以实现对真实角色的保护,有时候代理增强逻辑中可以判断是否去执行其代理的真实对象的业务逻辑;除此之外,还能实现低耦合高内聚,拓展性强,可以实现用不同的代理类,做不一样的代理操作,方便更换具体的增强逻辑。

(1)静态代理

静态代理实现步骤:

  1. 定义一个接口及其实现类;
  2. 创建一个代理类同样实现这个接口
  3. 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。

静态代理很好的实现了在不修改原有代码的基础上进行业务拓展的功能,这也是它主要的功能。但是静态代理是代理类在创建的时候,接口以及代理类就已经确定了,因此一个静态代理类只能代理一个类,如果要代理的方法很多,势必要对为一个方法进行代理,造成代码的冗余 。除此之外,假如接口新增了一个方法,除了实现类要实现这个方法之外,所有代理类也需要实现这个方法,这就增加了代码维护的复杂度。

代码实现:

package baguwen.proxy;

public interface takeout {
    void orderTakeouts();
}
package baguwen.proxy;

public class takeoutImpl implements takeout{

    @Override
    public void orderTakeouts() {
        System.out.println("成功点了一份炸鸡");
    }
    
}
package baguwen.proxy;

public class takeProxy implements takeout{
    private takeout takeout01;

    public takeProxy(takeout takeout01) {
        this.takeout01 = takeout01;
    }

    @Override
    public void orderTakeouts() {
        System.out.println("下订单");
        takeout01.orderTakeouts();
        System.out.println("评价");
    }
    
}
package baguwen.proxy;

public class test {
    public static void main(String[] args) {
        takeoutImpl takeimpl =  new takeoutImpl();
        takeProxy proxy = new takeProxy(takeimpl);
        proxy.orderTakeouts();
    }
}

(2)动态代理

动态代理是你想让他代理谁就代理谁,它会给你生成一个代理对象(代理你指定的类),用其完成具体代码逻辑。动态代理类的源码是在程序运行期间有JVM根据反射、ASM生成Java的字节码等机制动态生成,所以不存在代理类的字节码文件,代理类和委托类的关系都是在程序运行时确定。(ASM:一个小而快的字节码处理框架,用来转换字节码并生成新的类)。

代码示例:

创建一个接口及其实现类:

package baguwen.dongtaiProxy;

public interface takeout {
    void orderTakeouts();
}
package baguwen.dongtaiProxy;

public class takeoutImpl implements takeout{

    @Override
    public void orderTakeouts() {
        System.out.println("成功点了一份炸鸡");
    } 
}

自定义InvocationHandler并重写invoke方法

package baguwen.dongtaiProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理
 */
public class proxyHandler implements InvocationHandler{
    //被代理对象
    private Object targetObject;

    /**
     * 获取代理对象
     * @param targeObject
     * @return
     */
    public Object getProxyInstance(Object targeObject) {
        this.targetObject = targeObject;

        //第一个参数:被代理对象的类加载器
        //第二个参数:被代理对象的所有接口
        //第三个参数:当前对象,当前对象实现了InvocationHandler所以有invoke方法,通过invoke方法可以实现被代理对象的方法
        return Proxy.newProxyInstance(targeObject.getClass().getClassLoader(), targeObject.getClass().getInterfaces(), this);
    }

    //proxy:被代理的对象
    //method:要调用的方法
    //args:方法需要的参数
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("下订单");
        Object result = method.invoke(targetObject, args);
        System.out.println("评价");
        return result;
    }
    
}

通过Proxy.newProxyInstance创建代理对象

package baguwen.dongtaiProxy;

public class test {
    public static void main(String[] args) {
        proxyHandler proxyHandler = new proxyHandler();
        takeout takeIpml = (takeout) proxyHandler.getProxyInstance(new takeoutImpl());

        takeIpml.orderTakeouts();    //自动调用invoke方法
    }
}

当动态代理对象调用原生方法时候,最终实际上调用的是invoke方法,然后invoke方法替代我们去调用了被代理对象的原生方法。

温馨提示:上面只是我总结的面经知识,如果你想要更全面的可以到网站自行查看喔。
友情链接:牛客网
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到