xiaobai

xiaobai

积分: 1175
收藏: 0
注册时间: 1 年前
xiaobai 题目
xiaobai 8 月前
xiaobai 11 月前
xiaobai 11 月前
xiaobai 11 月前
xiaobai 11 月前
xiaobai 11 月前
xiaobai 11 月前
xiaobai 问题答案
xiaobai 4 月前 回答题目 Java 8 有哪些新的特性?
  • Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中
  • 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有 Java 类或对象(实例)的方法或构造器。与 lambda 联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
  • 新工具 − 新的编译工具,如:Nashorn 引擎 jjs、 类依赖分析器 jdeps。
  • Stream API −新添加的 Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中
  • Date Time API − 加强对日期与时间的处理。
  • Optional 类Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
  • Nashorn, JavaScript 引擎 − Java 8 提供了一个新的Nashorn javascript 引擎,它允许我们在 JVM 上运行特定的 javascript 应用。
xiaobai 4 月前 回答题目 序列化和反序列化是什么?

序列化和反序列化是什么?

Java 序列化:是指把 Java 对象转换为字节序列的过程,主要作用是保存和传递对象。

Java 反序列化:是指把字节序列恢复为 Java 对象的过程,主要作用是重建文件或者网络上的对象。

为什么需要序列化与反序列化?

  • 可以实现分布式对象,如远程方法调用。
  • 不仅可以保存一个对象的数据,还能递归保存对象引用的每个对象的数据。如此可以"深复制"对象,即复制对象本身及引用的对象本身。
  • 序列化可以将内存中的类写入文件或数据库中。这样下次需要实例化的时候,只要反序列化即可将类实例化到内存中,并保留序列化时类中的所有变量和状态。
  • 序列化以后就都是字节流,可以进行通用的格式传输或保存。传输结束以后,要再次使用,就进行反序列化还原,这样对象还是对象,文件还是文件。

如何实现 Java 序列化与反序列化?

  1. 首先我们要把准备要序列化类,实现 Serializable 接口

    class Person implements Serializable {}
    
  2. 然后序列化到文件中:

    File file = new File("person.obj");
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
    
    //实例化类
    Person person = new Person();
    //把类对象序列化
    oos.writeObject(person);
    oos.close();
    

遇到什么问题?

  1. 如果使用ObjectOutPutStream方式序列化对象,里面一定要有serialVersionUID,否则旧数据会反序列化失败。
  2. 一旦序列化保存到磁盘操作后,就不要修改类名了,否则数据反序列化就会失败。

尽量把对象转化成JSON保存更稳妥

xiaobai 4 月前 回答题目 Java 范型主要解决什么问题?

泛型的本质是参数化类型,这种参数类型可以用在1)、2)接口和3)方法的创建中,分别称为泛型类、泛型接口、泛型方法。

在 Java SE 1.5 之前没有泛型的情况的下只能通过对类型 Object 的引用来实现参数的任意化,其带来的缺点是要做显式强制类型转换,而这种强制转换编译期是不做检查的,容易把问题留到运行时。

所以 泛型的好处是在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率,避免在运行时出现 ClassCastException

xiaobai 4 月前 回答题目 手动创建一个java.lang.String类,请问这个类是否可以被类加载器加载?

不可以。

在 Java 的实现中,类的加载优先由其父类加载器来完成,也就是双亲委托机制

我们自定义的 java.lang.String 类,如下图,在最底层的自定义部分,其父类加载器是 Application ClassLoader,而 Application ClassLoader 的父类加载器是 Extension ClassLoader,Extension ClassLoader 的父类加载器是 Bootstrap ClassLoader:

Bootstrap ClassLoader
 ↑
Extension ClassLoader
 ↑
Application ClassLoader
 ↑
Custom ClassLoader

根据父类加载器优先原则,加载过程会层层向上,最终会在顶部的 Bootstrap ClassLoader 中找到java.lang.String,Bootstrap 加载器将其加载到内存中,忽略自定义 java.lang.String 类。

如果我们自定义一个 com.oomake.String 类,那么在检查过程中,顶部的3个父类加载器都无法加载到它,才会逐层往下最终落到自定义加载器进行加载。

这种双亲委托机制可以有效避免重复加载,当父亲已经加载了该类的时候,子类 ClassLoader 就不会再夹在一次,确保了类的加载安全。

另外,如果自己写 ClassLoader 来加载自定义的java.lang.String 类可不可以呢?

也不可能,因为针对java.*开头的类,JVM 的实现中已经保证了必须由 Bootstrap ClassLoader 来加载。

最后,附上顶部三个类加载器对应所在位置:

  • BootstrapClassLoader(启动类加载器) :最顶层的加载类,由 C++ 实现,负责加载 %JAVA_HOME%/lib目录下的jar包和类或者或被 -Xbootclasspath 参数指定的路径中的所有类。
  • ExtensionClassLoader(扩展类加载器) :主要负责加载目录 %JRE_HOME%/lib/ext 目录下的 jar 包和类,或被 java.ext.dirs 系统变量所指定的路径下的 jar 包。
  • AppClassLoader(应用程序类加载器) :面向我们用户的加载器,负责加载当前应用classpath下的所有 jar 包和类。
xiaobai 4 月前 回答题目 请列出 5 个 Java 运行时异常

常见RuntimeException:

  1. ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常
  2. ClassCastException:试图将对象强制转换为不是实例的子类时,抛出该异常
  3. IllegalArgumentException:抛出的异常表明向方法传递了一个不合法或不正确的参数
  4. IndexOutOfBoundsException:指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出
  5. NoSuchElementException:表明枚举中没有更多的元素
  6. NullPointerException:当应用程序试图在需要对象的地方使用 null 时,抛出该异常
xiaobai 4 月前 回答题目 Java 如何实现 3 种单例模式?

一 、饿汉式单例模式

class Singleton {
    // 创建类的同时创建对象,并保存在静态属性中,
    // 因为并不是按需创建对象,所以称为饿汉式
    private static final Singleton instance = new Singleton();
    // 将构造方法设为私有,禁止外部自行创建对象
    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}

缺点:类每次使用时都会初始化静态成员变量(调用该类的其它方法时也会初始化),浪费内存。

优点:因为保存对象的是静态属性,对象随类一同创建,以后不再改变,所以线程安全。

二 、懒汉式

class Singleton {
    // 创建类的时候不会创建对象
    private static Singleton instance;
    // 将构造方法设为私有,禁止外部自行创建对象
    private Singleton() {}

    public static Singleton getInstance() {
        // 如果对象未创建过,则实例化,
        // 并保存起来供下次直接使用
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

缺点:
会有线程安全问题,加上同步代码块解决线程安全问题但效率低,加上双重检锁提高访问效率。
没有第一次检锁时,无论单例成员变量有没有被初始化线程都会等待。
加上第一次检索,当单例成员变量被初始化后无需等待直接返回对象引用。


三 、登记式

class Singleton {
    // 将构造方法设为私有,禁止外部自行创建对象
    private Singleton() {}

    private static class Holder {
        private static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.instance;
    }
}

优点:内部类只有在被外部类调用时才会加载,延缓加载时间。

xiaobai 4 月前 回答题目 Java 的 final 关键字有什么用途?

final 是一个非访问修饰符 适用于 变量、方法或类

  • final 修饰 变量 时,被修饰的变量必须被初始化(赋值),且后续不能修改其值,实质上是常量;
  • final 修饰 方法 时,被修饰的方法无法被所在类的子类重写(覆写);
  • final 修饰 时,被修饰的类不能被继承,并且 final 类中的所有成员方法都会被隐式地指定为final 方法,但成员变量则不会变。

参考资料:详解Java中的final关键字

xiaobai 6 月前 回答题目 Java 类的实例化顺序是怎样的?

Java对象实例化的顺序为:

  1. 加载父类的静态成员变量和静态代码块
  2. 加载子类的静态成员变量和静态代码块
  3. 加载父类成员变量和方法块
  4. 加载父类的构造函数
  5. 加载子类成员变量和方法块
  6. 加载子类的构造函数
xiaobai 6 月前 回答题目 Java String、StringBuffer 和 StringBuilder 有什么区别?

Java StringStringBufferStringBuilder 的区别如下:

  • String:不可变字符串,每次操作都会生成新的String对象;效率低;适合操作少量数据场景。
  • StringBuffer:可变字符串;效率低;线程安全;适合多线程操作大数据量字符串缓冲区场景。
  • StringBuilder:可变字符序列;效率高;线程不安全。适合单线程操作大数据量字符串缓冲区场景。

另外,String可以赋值为null,其他两个不可以,必须是通过new创建新对象。

String s = null;
StringBuffer s = new StringBuffer();
StringBuilder s = new StringBuilder();
xiaobai 8 月前 回答题目 Spring Cloud 和 Dubbo 有什么区别?

Spring Cloud 和 Dubbo 都是微服务框架,主要区别有:

组件 Dubbo Spring Cloud
注册中心 Zookeeper Spring Cloud Netflix Eureka
调用方式 RPC REST API
监控 Dubbo-monitor Spring Boot Admin
断路器 不完善 Spring Cloud Netflix Hystrix
网关 Spring Cloud Netflix Zuul
分布式配置 Spring Cloud Config
服务跟踪 Spring Cloud Sleuth
消息总线 Spring Cloud Bus
数据流 Spring Cloud Stream
批量任务 Spring Cloud Task

简单来说,Dubbo更像是一个RPC框架,Spring Cloud是一套完整微服务框架解决方案。

xiaobai 6 月前 回答题目 Java 如何创建线程池?

JAVA中创建线程池主要有两类方法。

一类是通过ThreadPoolExecutor类,该类提供7个构造参数,可根据需要来自定义一个线程池。

另一类是通过Executors工厂类提供的方法,该类提供了4种不同的线程池可供使用。

  1. newCachedThreadPool():创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
  2. newFixedThreadPool():创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
  3. newScheduledThreadPool():创建一个周期性的线程池,支持定时及周期性执行任务。
  4. newSingleThreadExecutor():创建一个单线程的线程池,可保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

这两类的区别和联系:

Executors类提供的4种方式,其底层其实都是通过ThreadPoolExecutor类来实现的。

换句话说,就是Executors类工厂通过参数的组合,组装出了上面提到的4种类型线程池供不同场景使用。

所以一般推荐通过ThreadPoolExecutor方式创建线程池,这样可以明确线程池的运行规则,规避资源耗尽的风险。

FixedThreadPoolSingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。

CachedThreadPoolScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。