《面试宝典》:检验是否为合格的初中级程序员的面试知识点,你都知道了吗?查漏补缺

欢迎关注文章系列,一块儿学习
《提高能力,涨薪可待篇》
《面试知识,工做可待篇》
《实战演练,拒绝996篇》
也欢迎关注微信公众号【Ccww笔记】,原创技术文章第一时间推出
若是此文对你有帮助、喜欢的话,那就点个赞呗,点个关注呗!

《面试知识,工做可待篇》-Java笔试面试基础知识大全

前言

是否是感受找工做面试是那么难呢?

在找工做面试应在学习的基础进行总结面试知识点,工做也指日可待,欢迎一块儿学习【面试知识,工做可待】系列
《面试知识,工做可待篇》java

1. Java环境

1. 1 Java 和 C++ 的区别?

  • 都是面向对象的语言,都支持封装、继承和多态。
  • Java 不提供指针来直接访问内存,程序内存更加安全。
  • Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不能够多继承,可是接口能够多继承。
【重要】Java 有自动内存管理机制,不须要程序员手动释放无用内存。

1.2 JDK、JRE、JVM

1.2.1 JDK

JDK 即为 Java 开发工具包,包含编写 Java 程序所必须的编译、运行等开发工具以及 JRE。开发工具如:程序员

  • 用于编译 Java 程序的 javac 命令。
  • 用于启动 JVM 运行 Java 程序的 Java 命令。
  • 用于生成文档的 Javadoc 命令。
  • 用于打包的 jar 命令等等。

1.2..2 JRE

JRE 即为 Java 运行环境,提供了运行 Java 应用程序所必须的软件环境,包含有 Java 虚拟机(JVM)和丰富的系统类库。系统类库即为 Java 提早封装好的功能类,只需拿来直接使用便可,能够大大的提升开发效率。面试

1.2..3 JVM

JVM 即为 Java 虚拟机,提供了字节码文件(.class)的运行环境支持。数据库

1.2..4 三者关系

  • JDK > JRE > JVM

1.2 为何 Java 被称做是“平台无关的编程语言”?

  • Java 虚拟机是一个能够执行 Java 字节码的虚拟机进程。
  • Java 源文件( .java )被编译成能被 Java 虚拟机执行的字节码文件( .class )。
  • Java 被设计成容许应用程序能够运行在任意的平台,而不须要程序员为每个平台单独重写或者是从新编译。Java 虚拟机让这个变为可能,由于它知道底层硬件平台的指令长度和其余特性。编程

    1.3 什么是字节码?

这个问题,面试官能够衍生提问,Java 是编译执行的语言,仍是解释执行的语言。
Java 中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。segmentfault

编译程序只须要面向虚拟机,生成虚拟机可以理解的代码,而后由解释器来将虚拟机代码转换为特定系统的机器码执行。在 Java 中,这种供虚拟机理解的代码叫作字节码(即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。数组

每一种平台的解释器是不一样的,可是实现的虚拟机是相同的。Java 源程序通过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,而后在特定的机器上运行。这也就是解释了 Java 的编译与解释并存的特色。缓存

1.4 Java 源代码

=> 编译器 => JVM 可执行的 Java 字节码(即虚拟指令)=> JVM => JVM 中解释器 => 机器可执行的二进制机器码 => 程序运行安全

1.5 采用字节码的好处?

Java 语言经过字节码的方式,在必定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特色。因此 Java 程序运行时比较高效,并且,因为字节码并不专对一种特定的机器,所以,Java程序无须从新编译即可在多种不一样的计算机上运行。性能优化

解释型语言:解释型语言,是在运行的时候将程序翻译成机器语言。解释型语言的程序不须要在运行前编译,在运行程序的时候才翻译,专门的解释器负责在每一个语句执行的时候解释程序代码。这样解释型语言每执行一次就要翻译一次,效率比较低
例如:Python、PHP 。

2. 面向对象和面向过程

2.1 什么是面向对象?

面向对象是一种思想,万事万物抽象成一个对象,这里只讨论面向对象编程(OOP),Java 是一个支持并发、基于类和面向对象的计算机编程语言。

2.1.1 类class

类是抽象的概念,是万事万物的抽象,是一类事物的共同特征的集合。
用计算机语言来描述,是属性方法的集合。

2.1.2 对象instance、object

对象是类的具象,是一个实体。
对于咱们每一个人这个个体,都是抽象概念人 类 的不一样的 实体 。

面向对象软件开发具备如下优势:

  • 代码开发模块化,更易维护和修改。
  • 代码复用性强。
  • 加强代码的可靠性和灵活性。
  • 增长代码的可读性。

2.2 面向对象的特征

2.1 封装

封装,给对象提供了隐藏内部特性和行为的能力。对象提供一些能被其余对象访问的方法来改变它内部的数据。

在 Java 当中,有 4 种修饰符: default、public、private 和 protected 。每一种修饰符给其余的位于同一个包或者不一样包下面对象赋予了不一样的访问权限,权限以下:

访问权限 子类 其余包
public
protect ×
default × ×
private × × ×

封装好处:

  • 经过隐藏对象的属性来保护对象内部的状态。
  • 提升了代码的可用性和可维护性,由于对象的行为能够被单独的改变或者是扩展。
  • 禁止对象之间的不良交互提升模块化。

2.2 继承

继承,使对象基于基类字段和方法,新增自定义的的方法和属性。继承提供了代码的重用行,也能够在不修改类的状况下给现存的类添加新特性。

继承属性:

  • 子类拥有父类非 private 的属性和方法。
  • 子类能够拥有本身属性和方法,即子类能够对父类进行扩展。
  • 子类能够用本身的方式实现父类的方法

2.3 多态

多态,程序中定义的引用变量所指向的具体类型和经过该引用变量发出的方法调用在编程时并不肯定,而是在程序运行期间才肯定,即一个引用变量到底会指向哪一个类的实例对象,该引用变量发出的方法调用究竟是哪一个类中实现的方法,必须在由程序运行期间才能决定。

Java中有两种形式能够实现多态:

  • 继承(多个子类对同一方法的重写)
  • 接口(实现接口并覆盖接口中同一方法)

2.3 面向对象和面向过程的区别?

面向过程:就是分析出解决问题所须要的步骤,而后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就能够了,

面向对象:是把构成问题事务分解成各个对象,创建对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

3.1 面向过程

  • 优势:性能比面向对象高,由于类调用时须要实例化,开销比较大,比较消耗资源。好比,单片机、嵌入式开发、Linux/Unix 等通常采用面向过程开发,性能是最重要的因素。
  • 缺点:没有面向对象易维护、易复用、易扩展。

3.2 面向对象

  • 优势:易维护、易复用、易扩展,因为面向对象有封装、继承、多态性的特性,能够设计出低耦合的系统,使系统更加灵活、更加易于维护。
  • 缺点:性能比面向过程低。

3. Java数据类型(基本数据类型和引用类型)

3. 1 基本数据类型以下:

  • 整数值型:byte、short、int、long
  • 字符型:char
  • 浮点类型:float、double
  • 布尔型:boolean

    img

整数型:默认 int 型,小数默认是 double 型。Float 和 Long 类型的必须加后缀。

好比:float f = 100f 。

引用类型声明的变量是指该变量在内存中实际存储的是一个引用地址,实体在堆中。

3.2 引用类型

引用类型指向一个对象,不是原始值,指向对象的变量是引用变量

在java里面除去基本数据类型的其余类型都是引用类型,本身定义的class类都是引用类型,能够像基本类型同样使用。

引用类型常见的有:String、StringBuffer、ArrayList、HashSet、HashMap等

特别注意,String 是引用类型不是基本类型。

3.3 引用类型简介

引用类型 对象是否可引用 回收时间 使用场景
强引用 能够 从不回收 广泛对象的状态
软引用 能够 内存不足时 内存敏感的高速缓存
弱引用 能够 下一次GC 对象缓存
虚引用 不能够 下一次GC 通常用于追踪垃圾收集器的回收动做

3. 4 什么是值传递和引用传递?

  • 值传递,是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
  • 引用传递,通常是对于对象型变量而言的,传递的是该对象地址的一个副本,并非原对象自己。

    通常认为,Java 内的传递都是值传递,Java 中实例对象的传递是引用传递。

3. 5 char 型变量中能不能存贮一个中文汉字?为何?

  • C 语言中,char 类型占 1 个字节,而汉字占 2 个字节,因此不能存储。
  • Java 语言中,char 类型占 2 个字节,并且 Java 默认采用 Unicode 编码,一个 Unicode 码是 16 位,因此一个 Unicode 码占两个字节,Java 中不管汉字仍是英文字母,都是用 Unicode 编码来表示的。因此,在 Java 中,char 类型变量能够存储一个中文汉字。

3.6 equals 与 == 的区别?

  • 值类型(int,char,long,boolean 等)都是用 == 判断相等性。
  • 对象引用的话

    • == 判断引用所指的对象是不是同一个。
    • equals 方法,是 Object 的成员函数,有些类会覆盖(override) 这个方法,用于判断对象的等价性。
例如 String 类,两个引用所指向的 String 都是 "abc" ,但可能出现他们实际对应的对象并非同一个(和 JVM 实现方式有关),所以用 == 判断他们可能不相等,但用 equals 方法判断必定是相等的。

4. Java类Class

类是对事物的抽象,抽象类是对类的抽象,接口是对抽象类的抽象。

4.1 Java 对象(Class)建立的方式?

  • 使用 new 关键字建立对象。
  • 使用 Class 类的 newInstance 方法(反射机制)。
  • 使用 Constructor 类的 newInstance 方法(反射机制)。
  • 使用 clone 方法建立对象。
  • 使用(反)序列化机制建立对象。

4.2 抽象类与接口

4.2.1 抽象类

从面向对象的角度来说,咱们知道全部的对象都是经过类来描绘的,可是反过来却不是这样,并非 全部的类都是用来描绘对象的,若是一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就能够认为是抽象类

抽象类除了不能实例化对象以外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类同样。因为抽象类不能实例化对象,因此抽象类必须被继承,才能被使用。

4.2.2 接口

接口,在JAVA编程语言中是一个抽象类型,主要是抽象方法的集合,接口中的变量定义必须为public static final类型。接口一般以interface来声明。

4.2.3 抽象类与接口的对比

参数 抽象类 接口
默认的方法实现 它能够有默认的方法实现 接口彻底是抽象的。它根本不存在方法的实现
实现 子类使用extends关键字来继承抽象类。若是子类不是抽象类的话,它须要提供抽象类中全部声明的方法的实现。 子类使用关键字implements来实现接口。它须要提供接口中全部声明的方法的实现
构造器 抽象类能够有构造器 接口不能有构造器
与正常Java类的区别 除了你不能实例化抽象类以外,它和普通Java类没有任何区别 接口是彻底不一样的类型
访问修饰符 抽象方法能够有publicprotecteddefault这些修饰符 接口方法默认修饰符是public。你不可使用其它修饰符。
main方法 抽象方法能够有main方法而且咱们能够运行它 接口没有main方法,所以咱们不能运行它。(java8之后接口能够有default和static方法,因此能够运行main方法)
多继承 抽象方法能够继承一个类和实现多个接口 接口只能够继承一个或多个其它接口
速度 它比接口速度要快 接口是稍微有点慢的,由于它须要时间去寻找在类中实现的方法。
添加新方法 若是你往抽象类中添加新的方法,你能够给它提供默认的实现。所以你不须要改变你如今的代码 若是你往接口中添加方法,那么你必须改变实现该接口的类。

4.3 讲讲类的实例化顺序

初始化顺序以下:

->父类静态变量

->父类静态代码块
    ->子类静态变量、
        ->子类静态代码块
            ->父类非静态变量(父类实例成员变量)
                ->父类构造函数
                    ->子类非静态变量(子类实例成员变量)
                        ->子类构造函数

4.4 内部类

简单的说,就是在一个类、接口或者方法的内部建立另外一个类。这样理解的话,建立内部类的方法就很明确了。固然,详细的能够看看 《Java 内部类总结(吐血之做)》 文章。

4.4.1 内部类的做用是什么?

  • 内部类能够很好的实现隐藏(通常的非内部类,是不容许有 private 与protected权限的,但内部类能够)
  • 内部类拥有外围类的全部元素的访问权限
  • 但是实现多重继承
  • 能够避免修改接口而实现同一个类中两种同名方法的调用。

4.5 Anonymous Inner Class(匿名内部类)是否能够继承其它类?是否能够实现接口?

能够继承其余类或实现其余接口,在 Java 集合的流式操做中,咱们经常这么干。

4.6 内部类能够引用它的包含类(外部类)的成员吗?有没有什么限制?

一个内部类对象能够访问建立它的外部类对象的成员,包括私有成员。

若是你把静态嵌套类看成内部类的一种特例,那在这种状况下不能够访问外部类的普通成员变量,而只能访问外部类中的静态成员,例如:

class Outer {
    static int x;
    static class Inner {
        void test() {
            syso(x);
        }
    }
}

4.7 构造方法、构造方法重载

4.7.1 构造方法

当新对象被建立的时候,构造方法会被调用。每个类都有构造方法。在程序员没有给类提供构造方法的状况下,Java 编译器会为这个类建立一个默认的构造方法。

4.7.2 构造方法重载

Java 中构造方法重载和方法重载很类似。能够为一个类建立多个构造方法。每个构造方法必须有它本身惟一的参数列表。

4. 8 重载和重写的区别?

4.8.1 重写 override

  • 方法名、参数、返回值相同。
  • 子类方法不能缩小父类方法的访问权限。
  • 子类方法不能抛出比父类方法更多的异常(但子类方法能够不抛出异常)。
  • 存在于父类和子类之间。
  • 方法被定义为 final 不能被重写。

4.8.2 重载 overload

  • 参数类型、个数、顺序至少有一个不相同。
  • 不能重载只有返回值不一样的方法名。
  • 存在于父类和子类、同类中。

4.9 hashCode() 以及equals()

4.9.1 为何须要子类实现这两个方法?

父类的 equals ,通常状况下是没法知足子类的 equals 的需求。

好比全部的对象都继承 Object ,默认使用的是 Object 的 equals 方法,在比较两个对象的时候,是看他们是否指向同一个地址。可是咱们的需求是对象的某个属性相同,就相等了,而默认的 equals 方法知足不了当前的需求,因此咱们要重写 equals 方法。

若是重写了 equals 方法,就必须重写 hashCode 方法,不然就会下降 Map 等集合的索引速度。

4.9.2 说一说你对 java.lang.Object 对象中 hashCode 和 equals 方法的理解。在什么场景下须要从新实现这两个方法?

理解答案与4.8.1差很少,

equals 方法,用于比较对象的内容是否相等。

当覆盖了 equals 方法时,比较对象是否相等将经过覆盖后的 equals 方法进行比较(判断对象的内容是否相等)。
hashCode 方法,大多在集合中用到。

将对象放入到集合中时,首先判断要放入对象的 hashCode 值与集合中的任意一个元素的 hashCode 值是否相等,若是不相等直接将该对象放入集合中。
若是 hashCode 值相等,而后再经过 equals 方法判断要放入对象与集合中的任意一个对象是否相等,若是 equals 判断不相等,直接将该元素放入到集合中,不然不放入。

4.9.3 有没有可能 2 个不相等的对象有相同的 hashCode?

可能会发生,这个被称为哈希碰撞。固然,相等的对象,即咱们重写了 equals 方法,必定也要重写 hashCode 方法,不然将出现咱们在 HashMap 中,相等的对象做为 key ,将找不到对应的 value 。

4.9.4 equals 和 hashCode 的关系

  • equals 不相等,hashCode 可能相等。
  • equals 相等,请重写 hashCode 方法,保证 hashCode 相等。

    通常来讲,hashCode 方法的重写,能够看看 《科普:为何 String hashCode 方法选择数字31做为乘子》 方法。

5. 经常使用类

5.1 String、StringBuffer、StringBuilder

5.1.1 String、StringBuffer、StringBuilder 的区别?

可变性:

String 类中使用 final 关键字字符数组保存字符串,代码:

private final char value[],

因此string对象是不可变的。

StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在 AbstractStringBuilder 中也是使用字符数组保存字符串 char[] value ,可是没有用 final 关键字修饰,代码:

char[]value

这两种对象都是可变的。

线程安全性:

  • String中的对象是不可变的,也就能够理解为常量,线程安全。
  • StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,因此是线程安全的。
  • StringBuilder并无对方法进行加同步锁,因此是非线程安全的。
AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操做,如expandCapacity、append、insert、indexOf等公共方法。

性能:
每次对String 类型进行改变的时候,都会生成一个新的String对象,而后将指针指向新的String 对象。

StringBuffer每次都会对StringBuffer对象自己进行操做,而不是生成新的对象并改变对象引用。相同状况下使用StirngBuilder 相比使用StringBuffer 仅能得到10%~15% 左右的性能提高,但却要冒多线程不安全的风险。

5.1.2对于三者使用的总结

  • 操做少许的数据用 = String
  • 单线程操做字符串缓冲区 下操做大量数据 = StringBuilder,甚至有时,咱们为了不每一个线程重复建立 StringBuilder 对象,会经过 ThreadLocal + StringBuilder 的方式,进行对 StringBuilder 的重用
  • 多线程操做字符串缓冲区 下操做大量数据 = StringBuffer

5.1.3 String s = new String("xyz") 会建立几个对象?

首先,在 String 池内找,找到 "xyz" 字符串,不建立 "xyz" 对应的 String 对象,不然建立一个对象。
而后,遇到 new 关键字,在内存上建立 String 对象,并将其返回给 s ,又一个对象。
因此,总共是 1 个或者 2 个对象

5.1.4 StringTokenizer 是什么?

StringTokenizer ,是一个用来分割字符串的工具类。

示例代码以下:

StringTokenizer st = new StringTokenizer(”Hello World”);
while (st.hasMoreTokens()) {
    System.out.println(st.nextToken());
}

输出以下:

Hello
World

5.2 什么是自动拆装箱?

自动装箱和拆箱,就是基本类型和引用类型之间的转换。

5.2.1 什么要转换?

若是你在 Java5 下进行过编程的话,你必定不会陌生这一点,你不能直接地向集合( Collection )中放入原始类型值,由于集合只接收对象。

5.3 int 和 Integer 有什么区别?

  • int 是基本数据类型。
  • Integer 是其包装类,注意是一个类。

    须要注意下 Integer 的缓存策略

5.2.3 理解Java Integer 的缓存策略

6. 关键字

6.1 final、finally、finalize

6.1.1 final

final ,是修饰符关键字。

  • Class 类:若是一个类被声明为 final ,意味着它不能再派生出新的子类,不能做为父类被继承。所以一个类不能既被声明为 abstract 的,又被声明为 final 的。
  • 变量或方法声明为 final ,能够保证它们在使用中不被改变。被声明为 final 的变量必须在声明时给定初值,而在之后的引用中只能读取,不可修改。被声明为 final 的方法也一样只能使用,不能重写。

    另外,在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。可是若是方法过于庞大,可能看不到内嵌调用带来的任何性能提高(如今的 Java 版本已经不须要使用 final 方法进行这些优化了)。类中全部的private 方法都隐式地指定为 final 。

6.1.2 finally

在异常处理时提供 finally 块来执行任何清除操做。若是抛出一个异常,那么相匹配的 catch 子句就会执行,而后控制就会进入 finally 块(若是有的话)。

在如下 4 种特殊状况下,finally块不会被执行:

  • 在 finally 语句块中发生了异常。
  • 在前面的代码中用了 System.exit() 退出程序。
  • 程序所在的线程死亡。
  • 关闭 CPU 。

6.1.3 finalize

finalize ,是方法名。

Java 容许使用 #finalize() 方法,在垃圾收集器将对象从内存中清除出去以前作必要的清理工做。这个方法是由垃圾收集器在肯定这个对象没有被引用时对这个对象调用的。

它是在 Object 类中定义的,所以全部的类都继承了它。
子类覆盖 finalize() 方法,以整理系统资源或者执行其余清理工做。

finalize() 方法,是在垃圾收集器删除对象以前对这个对象调用的。
通常状况下,咱们在业务中不会本身实现这个方法,更可能是在一些框架中使用。

6.1.4 String 类能被继承吗,为何?

不能,由于 String 是 final 修饰。

6.2 static

6.2.1 static特色

  • static是一个修饰符,用于修饰成员。(成员变量,成员函数)static修饰的成员变量 称之为静态变量或类变量。
  • static修饰的成员被全部的对象共享。
  • static优先于对象存在,由于static的成员随着类的加载就已经存在。
  • static修饰的成员多了一种调用方式,能够直接被类名所调用,(类名.静态成员)。
  • static修饰的数据是共享数据,对象中的存储的是特有的数据

6.2.2 是否能够在 static方法中访问非 static 变量?

static 变量在 Java 中是属于类的,它在全部的实例中的值是同样的。当类被 Java 虚拟机载入的时候,会对 static 变量进行初始化。若是你的代码尝试不用实例来访问非 static 的变量,编译器会报错,由于这些变量尚未被建立出来,尚未跟任何实例关联上。

因为静态方法能够不经过对象进行调用,所以在静态方法里,不能调用其余非静态变量,也不能够访问非静态变量成员。

若是你的代码尝试不用实例来访问非 static 的变量,编译器会报错,由于这些变量尚未被建立出来,尚未跟任何实例关联上。

6.2.3 成员变量和静态变量的区别:

  • 生命周期的不一样:

    • 成员变量随着对象的建立而存在随着对象的回收而释放。
    • 静态变量随着类的加载而存在随着类的消失而消失。
  • 调用方式不一样:

    • 成员变量只能被对象调用。
    • 静态变量能够被对象调用,也能够用类名调用。(推荐用类名调用)
  • 别名不一样:

    • 成员变量也称为实例变量。
    • 静态变量称为类变量。
  • 数据存储位置不一样:

    • 成员变量数据存储在堆内存的对象中,因此也叫对象的特有数据。
    • 静态变量数据存储在方法区(共享数据区)的静态区,因此也叫对象的共享数据。

6.2.4 static 关键字修饰的加载顺序

->父类静态变量

​ ->父类静态代码块

​ ->子类静态变量

​ ->子类静态代码块

​ ->父类普通变量

​ ->父类普通代码块

​ ->父类构造函数

​ ->子类普通变量

​ ->子类普通代码块

​ ->子类构造函数

6.3 transient 关键字

transient声明一个实例变量,当对象存储时,它的值不须要维持。换句话来讲就是,用transient关键字标记的成员变量不参与序列化过程,

transient 只能修饰变量,不能修饰类和方法。

6.3.1 Java 序列话中,若是有些字段不想进行序列化怎么办?

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

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

6.4 volatile关键词

volatile 关键字用在多线程同步中,可保证读取的可见性,JVM只是保证从主内存加载到线程工做内存的值是最新的读取值,而非 cache 中

6.4.1 volatile关键字是否能保证线程安全?

不能 , 多个线程对 volatile 的写操做,没法保证线程安全。例如假如线程 1,线程 2 在进行 read,load 操做中,发现主内存中 count 的值都是 5,那么都会加载这个最新的值,在线程 1 堆 count 进行修改以后,会 write 到主内存中,主内存中的 count 变量就会变为 6;线程 2 因为已经进行 read,load 操做,在进行运算以后,也会更新主内存 count 的变量值为 6;致使两个线程及时用 volatile 关键字修改以后,仍是会存在并发的状况

7. Java IO

7.1 Java IO 相关的类

Java IO 相关的类,在 java.io 包下,具体操做分红面向字节(Byte)和面向字符(Character)两种方式。以下图所示:

img

7.2 什么是 Java 序列化?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。

能够对流化后的对象进行读写操做,也可将流化后的对象传输于网络之间。
序列化是为了解决在对对象流进行读写操做时所引起的问题。
反序列化的过程,则是和序列化相反的过程。

咱们不能将序列化局限在 Java 对象转换成二进制数组,好比,将一个 Java 对象转换成 JSON 字符串等,这也能够理解为是序列化。

7.2.1如何实现 Java 序列化?

将须要被序列化的类,实现 Serializable 接口,该接口没有须要实现的方法,implements Serializable 只是为了标注该对象是可被序列化的。

  • 序列化

    • 首先使用一个输出流(如:FileOutputStream)来构造一个 ObjectOutputStream(对象流)对象
    • 接着,使用 ObjectOutputStream 对象的 #writeObject(Object obj) 方法,就能够将参数为 obj 的对象写出(即保存其状态)。
  • 反序列化

    • 要恢复的话则用输入流。

7.3 如何实现对象克隆(浅克隆和深克隆)?

  • 实现 Cloneable 接口,并重写 Object 类中的 #clone() 方法。能够实现浅克隆,也能够实现深克隆。
  • 实现 Serializable 接口,经过对象的序列化和反序列化实现克隆。能够实现真正的深克隆。
实际场景下,咱们使用的克隆比较少,更可能是对象之间的属性克隆。例如说,将 DO 的属性复制到 DTO 中,又或者将 DTO 的属性复制到 VO 中。此时,咱们通常使用 BeanUtils 工具类。

8.异常

8.1 异常机制的概述

​ 异常机制是指当程序出现错误后,程序如何处理。具体来讲,异常机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。

程序错误分为三种:

  • 编译错误:由于程序没有遵循语法规则,编译程序可以本身发现而且提示咱们错误的缘由和位置,这个也是你们在刚接触编程语言最常遇到的问题。
  • 运行时错误:由于程序在执行时,运行环境发现了不能执行的操做。
  • 逻辑错误:由于程序没有按照预期的逻辑顺序执行。异常也就是指程序运行时发生错误,而异常处理就是对这些错误进行处理和控制。

8.2 Throwable

Throwable 类图

img

Throwable有两个重要的子类 :

  • Exception(异常)
  • Error(错误)

两者都是 Java 异常处理的重要子类,各自都包含大量子类

8.2.1 Exception(异常)和 Error(错误)

  • Error(错误),表示系统级的错误和程序没必要处理的异常,是 Java 运行环境中的内部错误或者硬件问题。

    • 例如:内存资源不足等。
    • 对于这种错误,程序基本无能为力,除了退出运行外别无选择,它是由 Java 虚拟机抛出的
  • Exception(异常),表示须要捕捉或者须要程序进行处理的异常,它处理的是由于程序设计的瑕疵而引发的问题或者在外的输入等引发的通常性问题,是程序必须处理的。Exception 又分为运行时异常,受检查异常。

    • RuntimeException(运行时异常),表示没法让程序恢复的异常,致使的缘由一般是由于执行了错误的操做,建议终止逻辑,所以,编译器不检查这些异常。
    • CheckedException(受检查异常),是表示程序能够处理的异常,也即表示程序能够修复(由程序本身接受异常而且作出处理),因此称之为受检查异常

8.3 error 和 exception 有什么区别?

  • Error:Error类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操做无关 。好比:

    • OutOfMemoryError
    • NoClassDefFoundError
    • LinkageError
  • Exception : 在Exception分支中有一个重要的子类RuntimeException(运行时异常),该类型的异常自动为你所编写的程序定义 异常,好比:

    • ArrayIndexOutOfBoundsException(数组下标越界)
    • NullPointerException(空指针异常)
    • ArithmeticException(算术异常)
    • MissingResourceException(丢失资源)
    • ClassNotFoundException(找不到类)
    • BufferOverflowException
    • ClassCastException

8.4 CheckedException 和 RuntimeException 有什么区别?

  • RuntimeException运行异常:表示程序运行过程当中可能出现的非正常状态,运行时异常表示虚拟机的一般操做中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题一般就不会发生。
  • CheckedException受检异常:跟程序运行的上下文环境有关,即便程序设计无误,仍然可能因使用的问题而引起。Java编译器要求方法必须声明抛出可能发生的受检异常,可是并不要求必须声明抛出未被捕获的运行时异常
Effective Java中对异常的使用给出了如下指导原则 :

不要将异常处理用于正常的控制流(设计良好的API不该该强迫它的调用者为了正常的控制流而使用异常)
对能够恢复的状况使用受检异常,对编程错误使用运行时异常
避免没必要要的使用受检异常(能够经过一些状态检测手段来避免异常的发生)
优先使用标准的异常
每一个方法抛出的异常都要有文档
保持异常的原子性
不要在catch中忽略掉捕获到的异常

8.5 Throwable 类经常使用方法?

  • getMessage() 方法:返回异常发生时的详细信息。
  • getCause() 方法:得到致使当前 Throwable 异常的 Throwable 异常。
  • getStackTrace() 方法:得到 Throwable 对象封装的异常信息。
  • printStackTrace() 方法:在控制台上打印。

8.6 throw 与 throws 的区别 ?

  • throw ,用于在程序中显式地抛出一个异常。
  • throws ,用于指出在该方法中没有处理的异常。每一个方法必须显式指明哪些异常没有处理,以便该方法的调用者能够预防可能发生的异常。最后,多个异经常使用逗号分隔。

8.7 异常处理中 finally 语句块的重要性?

无论程序是否发生了异常, finally 语句块都会被执行,甚至当没有catch 声明但抛出了一个异常时, finally 语句块也会被执行。

finally 语句块一般用于释放资源, 如 I/O 缓冲区, 数据库链接等等。

8.8 UnsupportedOperationException 是什么?

UnsupportedOperationException ,是用于代表操做不支持的异常。

在 JDK 类中已被大量运用,在集合框架java.util.Collections.UnmodifiableCollection 将会在全部 add 和 remove 操做中抛出这个异常。

9.反射

9.1 反射简介

当程序运行时,容许改变程序结构或变量类型,这种语言称为动态语言。咱们认为 Java 并非动态语言,可是它却又一个很是突出的动态相关的机制

9.2 反射的用途及实现?

Java 反射机制主要提供了如下功能:

  • 在运行时构造一个类的对象。
  • 判断一个类所具备的成员变量和方法。
  • 调用一个对象的方法。
  • 生成动态代理。
反射的主要用途, 开发各类通用框架
  • Spring 框架的 IoC 基于反射建立对象和设置依赖属性。
  • Spring MVC 的请求调用对应方法,也是经过反射。
  • JDBC 的 Class#forName(String className) 方法,也是使用反射。

9.3 反射中,Class.forName 和 ClassLoader 区别?

  • Class#forName(...) 方法,除了将类的 .class 文件加载到JVM 中以外,还会对类进行解释,执行类中的 static 块。
  • ClassLoader 只干一件事情,就是将 .class 文件加载到 JVM 中,不会执行 static 中的内容,只有在 newInstance 才会去执行 static 块。
  • Class#forName(name, initialize, loader) 方法,带参函数也可控制是否加载 static 块,而且只有调用了newInstance 方法采用调用构造函数,建立类的对象。

9.4 何时用断言(assert)?

断言,在软件开发中是一种经常使用的调试方式,不少开发语言中都支持这种机制。

通常来讲,断言用于保证程序最基本、关键的正确性。断言检查一般在开发和测试时开启。为了保证程序的执行效率,在软件发布后断言检查一般是关闭的。
断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为true;若是表达式的值为 false ,那么系统会报告一个AssertionError 错误。断言的使用以下面的代码所示:

assert(a > 0); // throws an AssertionError if a <= 0

断言能够有两种形式:

assert Expression1; 。
assert Expression1 : Expression2;
Expression1 应该老是产生一个布尔值。
Expression2 能够是得出一个值的任意表达式;这个值用于生成显示更多调试信息的字符串消息。

要在运行时启用断言,能够在启动 JVM 时使用 -enableassertions 或者 -ea 标记。要在运行时选择禁用断言,能够在启动 JVM 时使用 -da 或者 -disableassertions 标记。要在系统类中启用或禁用断言,可以使用 -esa 或 -dsa 标记。还能够在包的基础上启用或者禁用断言。

也欢迎关注微信公众号【Ccww笔记】,原创技术文章第一时间推出
若是此文对你有帮助、喜欢的话,那就点个赞呗,点个关注呗!

相关文章
相关标签/搜索