Java速通10:面试常问

116 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情

1.instanceof

可以通过instanceof判断某个类型 是否 属于某种类型。

public class Animal {}

public interface Runnable {}

public class Dog extends Animal implements Runnable {}

Object dog = new Dog();

sout(dog instanceof Dog);      // true
sout(dog instanceof Animal);   // true
sout(dog instanceof Runnable); // true
sout(dog instanceof String);   // false

2.UUID

uuid目的是让分布式系统中所有元素都能有唯一的标识符,而不需要通过中央控制器来做标识符的指定。

UUID.randomUUID();

3.类方法的调用细节:

// 父类
public class Animal {
    public static void run(){
        sout("Animal");
    }
}

// 子类
public class Dog extends Animal {
    public static void run(){
        sout("Dog");
    }
}


// 测试
Dog.run();    // Dog
Animal.run(); // Animal

Dog dog = new Dog();
dog.run();  // Dog

Animal dog2 = new Dog();
dog2.run(); // Animal

4.泛型

1.泛型简介
泛型即数据类型参数化。
Java中的泛型,只在编译阶段有效:在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

1.泛型接收的类型
只能传入引用数据类型,不能传入基本数据类型。

2.不能定义静态泛型变量
原因:由于静态变量在java程序一运行时就已经被载入内存,而此时它的类型无法确定,而开辟空间必须知道类型,两者矛盾。

3.方法前面的<T>
<T>指定方法级别的泛型,是告诉编译器,当前的方法的值传入类型可以和类初始化的泛型类不同,也就是将该方法定义为泛型方法,不需要与类初始化的泛型类相同。

在java中泛型只是一个占位符,必须在传递类型后才能使用。就泛型类而言,类实例化时才能传递真正的类型参数,由于静态方法的加载先于类的实例化,也就是说类中的泛型还没有传递真正的类型参数时,静态方法就已经加载完成。显然,静态方法不能使用/访问泛型类中的泛型。(类加载时,静态的东西先加载)

// 此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
public class Demo<T> {

    private T key;

    /**
     * 返回一个通用的 Demo对象
     * Demo<T> 返回值类型
     * 静态方法参数中如果使用泛型,必须指定方法级别的泛型
     */
    public static <T> Demo<T> success(T result){
        
        tresult.toString();

        return responseResult;
    }

    // 静态方法参数中如果使用泛型,必须将该方法定义为泛型方法
    public static <T> void failure(T result){
        return result.toString();
    }

    // 非静态方法也可定义 泛型方法
    public <T> void print1(T result){
        return result.toString();
    }

    // 使用类级别的泛型
    public void print2(T result){
        return result.toString();
    }
}

调用:

Demo<String> demo = new Demo<>();

demo.print1(new Integer(1));  // 可以编译
demo.print2(new Integer(1));  // 不能编译

// 静态 泛型方法
Demo.failure(new Integer(1));

4.缓存与缓冲的区别

1.缓冲 Buffer
缓冲的作用是协调上下层应用之间的性能差异:如上层流量大、速度快,而下层流量小、速度慢。这会导致下层接不住上层。这时就需要一个缓冲区来暂存上层发给下层的多余流量,然后一点点发给下层。
缓冲有效减少了上层组件对下层组件的等待时间,无需等待下层组件完全接收数据,即可进行其他操作,从而提高系统性能。
举例:消息队列(做流量削峰)、StringBuffer、BufferedWrite。
注意:缓冲区大小要适中,太小起不到缓冲作用,太大会浪费系统资源,增加GC负担。
1.缓存
缓存也是为了提升系统性能而开辟的一块空间。与缓冲不同的是,缓存是将反复被使用的数据存储起来,供程序直接调用,避免程序反复从数据库中读取相同的数据。
举例:MyBatis、Redis。

5.final、finally、finalize 有什么区别?

  1. final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量不能被重新赋值(配合static才能使变量变常量)。
  2. finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
  3. finalize是Object类中的一个方法,而Object类是所有类的父类。该方法一般由垃圾回收器来调用,当我们调用System的gc()方法的时候,由垃圾回收器调用finalize(),回收垃圾。

6.forward 和 redirect 的区别?

Forward和Redirect代表了两种请求转发方式:直接转发和间接转发。

  1. 直接转发方式(Forward),客户端和浏览器只发出一次请求,Servlet、HTML、JSP或其它信息资源,由第二个信息资源响应该请求,在请求对象request中,保存的对象对于每个信息资源是共享的。

  2. 间接转发方式(Redirect)实际是两次HTTP请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。

举个通俗的例子:

  • 直接转发就相当于:“A找B借钱,B说没有,B去找C借,借到借不到都会把消息传递给A”;
  • 间接转发就相当于:"A找B借钱,B说没有,让A去找C借"。

后面将持续补充.....