java 知识总结 -- 基础3

java面试笔记--基础3

以下内容总结于网络,仅供个人学习、复习使用,如有侵权,请告知删除🙏

异常

1、Exception 和 Error 有什么区别?

​ java中所有的异常都有一个共同的祖先:java.lang包下的 Throwable类。Throwable类有两个重要子类:Exception、Error

​ Exception:程序本身可以处理的异常,可以通过catch抓捕。

​ Exception又分为 Checked Exception (受检查异常 ,必须处理) 和 Unchecked Exception (不受检异常,可不处理)。

​ Error :程序无法处理的错误。不建议通过catch捕捉。例如:虚拟机运行错误、虚拟机内存不足、类定义错误等,发生这些异常时,jvm一般会选择线程终止。

2、Checked Exception 和 Unchecked Exception 有什么区别?

​ Checked Exception 受检查异常,受检查异常如果没有被 处理 (catch、throws),程序就没法编译通过。

​ 除了 RunTimeException 及其子类,剩下的Exception类及子类都属于受检查异常。

​ 常见的受检查异常:IO相关的异常、ClassNotFoundException、SQlException...

​ UnChecked Exception 不受检异常,不处理程序也能正常运行。

​ RuntimeException 及其子类都属于 非受检异常,常见的有:

  • NullPointerException 空指针异常
  • IllegalArgumentException 参数错误
  • ArrayIndexOutOfBoundsException 数组越界
  • ClassCastException 类型转换错误

3、Throwable 类常用方法有哪些?

  • String getMessage() :返回异常发生时的简要描述
  • String toString() :返回异常发生时的详细信息
  • String getLocalizedMessage() :返回异常对象的本地化信息。
  • void printStackTrace() :在控制台上打印 Throwable 对象封装的异常信息。

4、try-catch-finally 如何使用?

​ try :用于捕获异常。后面可接0个或多个catch块,如果没有catch块,就必须跟一个 finally块。

​ catch :用于处理异常。

​ finally :无论是否捕获或处理异常,finally 块的语句都会被执行。当 try中或者catch中有return语句时,finally 语句将在返回前执行。

注意不要在 finally 块中 使用 return 语句!

​ 当 try 中和 finally 中都有return ,try中的return 语句会被忽略覆盖。 因为 try 中return 的返回值会暂存在本地变量中,执行到finally 中 的return后,这个本地变量的值会被finally 中的 return 值覆盖。

5、finally 中的代码一定会执行吗?

不一定, finally之前 虚拟机被终止运行的话,finally 不会被执行。

程序所在的线程死亡 和 关闭 CPU 后 ,finally 语块不会被执行

6、如何使用 try-with-resources 代替try-catch-finally

  • 适用范围(资源的定义): 任何实现 java.lang.AutoCloseable或者 java.io.Closeable 的对象
  • 关闭资源和 finally 块的执行顺序:try-with-resources 语句中,任何 catch 或 finally 块在声明的资源关闭后运行

​ 面对必须要关闭的资源,应该优先使用 try-with-resources 而不是try-finally。代码更简短,更清晰,产生的异常对我们也更有用。

​ Java 中类似于InputStreamOutputStreamScannerPrintWriter等的资源都需要我们调用close()方法来手动关闭,一般情况下我们都是通过try-catch-finally语句来实现这个需求,

7、异常使用有哪些需要注意的地方?

  • 不要把异常定义为静态变量,因为这样会导致异常栈信息错乱。
  • 抛出的异常信息一定要有意义。
  • 建议抛出更加具体的异常
  • 使用日志打印异常之后就不要再抛出异常了

泛型

1、什么是泛型?有什么作用?

​ Java 泛型是 JDK 5 中引入的一个新特性。使用泛型参数,可以增强代码的可读性以及稳定性。

​ 编译器可以对泛型参数进行检测,并且通过泛型参数可以指定传入的对象类型

比如

ArrayList<Persion> persons = new ArrayList<Persion>() 

这行代码就指明了该 ArrayList 对象 只能传入 Persion 对象,如果传入其他类型的对象就会报错。

并且,原生 List 返回类型是 Object ,需要手动转换类型才能使用,使用泛型后编译器自动转换。

2、泛型的使用方式有哪几种?

泛型类、范性方法、泛型接口

1、泛型类:

//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{

    private T key;

    public Generic(T key) {
        this.key = key;
    }

    public T getKey(){
        return key;
    }
}

​ 如何实例化泛型类:

Generic<Integer> genericInteger = new Generic<Integer>(123456);
2、泛型方法:
   public static < E > void printArray( E[] inputArray ){
         for ( E element : inputArray ){
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }

​ 使用泛型方法:

// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray( intArray  );
printArray( stringArray  );

注意public static < E > void printArray( E[] inputArray ) 一般被称为静态泛型方法;

​ 在 java 中泛型只是一个占位符,必须在传递类型后才能使用。类在实例化时才能真正的传递类型参数,由于静态方法的加载先于类的实例化,也就是说类中的泛型还没有传递真正的类型参数,静态的方法的加载就已经完成了,所以静态泛型方法是没有办法使用类上声明的泛型的。只能使用自己声明的 <E>

3、泛型接口:
public interface Generator<T> {
    public T method();
}

​ 1、实现泛型接口,不指定类型

class GeneratorImpl<T> implements Generator<T>{
    @Override
    public T method() {
        return null;
    }
}

​ 2、实现泛型接口,指定类型:

class GeneratorImpl<T> implements Generator<String>{
    @Override
    public String method() {
        return "hello";
    }
}

3、项目中哪里用到了泛型?

  • 自定义接口通用返回结果 CommonResult<T> 通过参数 T 可根据具体的返回类型动态指定结果的数据类型
  • 定义 Excel 处理类 ExcelUtil<T> 用于动态指定 Excel 导出的数据类型
  • 构建集合工具类(参考 Collections 中的 sort, binarySearch 方法)。
  • ....

反射

1、何为反射?

​ 反射是框架的灵魂。应为 它赋予了我们在运行时分析类以及执行类中方法的能力。
通过反射,我们可以获取到任意一个类的所有属性和方法,并调用这些属性和方法。

2、反射机制的优缺点

​ 优点:1、提高了程序的灵活性和扩展性。2、降低了耦合性

​ 缺点:1、增加了安全问题,比如可能会无视泛型参数的安全检查。2、反射的性能差一些。3、使用反射会模糊程序的内部逻辑。

3、反射的应用场景

​ Spring、Spring Boot 、Mybatis等等框架。这些框架中也使用了动态代理。

动态代理的实现也依赖于反射。

注解的实现也依赖于反射。

注解

Annotation(注解)可以看作是一种特殊的注释,主要用于修饰类、方法或者变量。

注解本质是一个继承了Annotation 的特殊接口。

注解只有被解析之后才会生效,常见的解析方法有两种:

  • 编译期直接扫描 :编译器在编译 Java 代码的时候扫描对应的注解并处理,比如某个方法使用@Override 注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。
  • 运行期通过反射处理 :像框架中自带的注解(比如 Spring 框架的 @Value@Component)都是通过反射来进行处理的。

I/O

1、序列化与反序列化

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

​ 对于 Java 这种面向对象编程语言来说,我们序列化的都是对象(Object)也就是实例化后的类(Class),但是在 C++这种半面向对象的语言中,struct(结构体)定义的是数据结构类型,而 class 对应的是对象类型。

序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。

2、Java 序列化中如果有些字段不想进行序列化,怎么办?

​ 对于不想进行序列化的变量,使用 transient 关键字修饰。

transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时, 被 transient 修饰的变量值不会被持久化和恢复。

注意:

  • transient 只能修饰变量,不能修饰类和方法。
  • transient 修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 int 类型,那么反序列后结果就是 0
  • static 变量因为不属于任何对象(Object),所以无论有没有 transient 关键字修饰,均不会被序列化。

3、获取用键盘输入常用的两种方法

​ 方法 1:通过 Scanner

Scanner input = new Scanner(System.in);
String s  = input.nextLine();
input.close();

​ 方法 2:通过 BufferedReader

BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();

4、Java 中 IO 流分为几种?

  • 按照流的流向分,可以分为输入流和输出流;

  • 按照操作单元划分,可以划分为字节流和字符流;

  • 按照流的角色划分为节点流和处理流。

      Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。
    
  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。

  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

5、既然有了字节流,为什么还要有字符流?

不管是文件读写还是网络发送接收,信息的最小存储单元都是字节,那为什么 I/O 流操作要分为字节流操作和字符流操作呢?

​ 字符流是由 Java 虚拟机将字节转换得到的,这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。

​ 所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。

posted @ 2022-05-28 15:04  大星星不见了  阅读(36)  评论(0编辑  收藏  举报