Loading...

# Java 基础

# Java 跨平台原理

Java 对于不同系统、不同版本、不同位数的 Java 虚拟机来屏蔽不同的系统指令集差异而对外提供统一的接口

# JDK、JRE、JVM 的关系

JDK (java development toolkit):Java 开发工具包

是 java 的核心,包括了 java 运行环境,一堆 java 工具(javac、java、jdb)和 java 基础类库(javaAPI 包括 rt.jar)

JRE (java runtime environment):Java 运行环境

所有的 java 程序都要在 JRE 下才能运行,包括 JVM 和 Java 核心类库和支持文件,与 JDK 相比不包含开发工具(编译器、调试器和其他工具)

JVM (java virtual mechinal):Java 虚拟机

JVM 是 JRE 的一部分,他是一个虚拟出来的计算机,JVM 的主要工作是解释自己的指令集(即字节码)并映射到本地的 CPU 指令集或 OS 的系统调用

# 面向对象的特征

四个:封装、抽象、继承、多态

封装:将客观的事物封装成抽象的类,并且将自己类中的属性私有化,只对外提供 set 和 get 方法来进行属性的赋值和取值

继承:子类继承父类,子类可以使用父类的所有功能,并且在无需改变父类的情况下能对这些功能进行扩展

多态:允许相同或不同子类型的对象对同一消息作出不同的响应,如重载和重写

# 访问修饰符 public,private,protected, 以及默认时的区别

修饰符当前类同包子类其他包
public
protect×
default××
private×××

# 基本数据类型,包装类型

基本数据类型有八种

byte、short、int、long、char、boolean、float、double

数据类型占用字节取值范围默认值包装类型
boolean只有 true 和 falsetrue、falsefalseBoolean
byte1 (8 位)-128~1270Byte
short2 (16 位)-32768~327670Short
int4 (32 位)-231~231-10Integer
long8-263~263-10.0lLong
float43.4E-45~1.4E380.0fFloat
double84.9E-324~1.8E3080Double
char20~65535\u0000 (空格)Character

包装类型:每一个基本数据类型都一一对于一个包装类型

java 是一个面向对象的语言,而基本数据类型不具备面向对象的特征

# 拆箱和装箱

装箱:把基本数据类型转化为对应的包装类型

Integer i = 1;

自动装箱实际上会在编译时会调用 Integer.valueOf () 方法来装箱

拆箱:把包装类型转换为基本数据类型

int j = i;

实际上在编译时会调用 intValue () 方法来拆箱

# == 和 equals 的区别

== 用来判断两个变量之间的值是否相等,变量可分为基本数据变量和引用类型,如果比较的是基本数据类型,那么就是比较他们的值是否相等,如果比较的是引用类型,那么比较的是他们引用的内存地址

equals 不能用于作用与基本数据类型的变量,他继承至 Object 类,比较的是是否是同一对象,如果没有对 equals 方法进行重写,则比较的是引用类型变量所指向对象的地址

# 重写 equals 为何要重写 hashcode

  1. 使用 hashcode 方法提前校验,可以避免每一次对比都调用 equals 方法,提高效率(因为 hashcode 不等,equals 一定不等)
  2. 为了保证是同一对象,如果重写了 equals 方法,而没有重写 hashcode 方法,会出现 equals 相等,hashcode 不相等的情况,重写 hashcode 方法就是为了避免这种情况发生

# final 的作用

  • 修饰类:表示类不可被继承
  • 修饰方法:表示方法不能被子类覆盖(重写),但是可以重载
  • 修饰变量:表示变量一旦被赋值就不可更改它的值
    • 修饰类变量,只能在静态初始化块中指定初始值或声明该类变量时指定初始值
    • 修饰成员变量,可以在非静态初始块、声明该变量或者构造器中执行初始值
    • 修饰局部变量,系统不会为局部变量进行初始化,局部变量必须由程序显示初始化,因此使用 final 修饰局部变量时,即可以在定义时指定默认初始值(后面的代码不能对变量进行赋值),也可以不指定默认值,而在后面的代码中对 final 变量赋初值(仅一次)
    • 修饰基本数据类型变量,则其数值一旦在初始化之后便不能更改
    • 修饰引用类型变量,则在初始化之后便不能让其指向另一个对象,但引用的值是可变的

# String 属于基础的数据类型吗?

String 不属于基础类型,基础类型有八种:byte、short、int、long、char、boolean、float、double,而 String 属于对象。

# String,StringBuilder,StringBuffer 的区别

String 是字符串常量,其值不能改变,每次操作都会产生新的 String 对象,底层是使用了一个不可变的数组对象 (final char [])

StringBulider 是线程不安全的,其值可以改变,速度快,底层是使用了一个可变的数组对象(没有用 final 修饰)

StringBuffer 是线程安全的,其值可以改变,速度慢。

使用场景

经常需改改变字符串内容时使用后两个

优先使用 StringBuilder,多线程使用共享变量时使用 StringBuffer

# 拼接字符串

String s = "a" + "b";

开辟了三个内存空间

StringBuilder sb = new StringBuilder();
sb.append("a").append("b");

只开辟了一个内存空间

拼接字符串不能使用 String,要用 StringBuilder 或 StringBuffer

# 如何实现字符串的反转及替换?

可以使用 String 或 StringBuffer/StringBuilder 中的 reverse () 方法,以下使用递归的方法进行反转:

public static String reverse(String originStr) {
    if(originStr == null || originStr.length() <= 1) 
        return originStr;
    return reverse(originStr.substring(1)) + originStr.charAt(0);
}

# 重载和重写的区别

  • 重载:发生在同一个类中,方法名必须相同,参数类型不同,参数个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时
  • 重写:发生在父子类中,方法名和参数必须相同,返回值范围小于父类、抛出的异常范围小于等于父类,访问修饰符大于等于父类,如果父类的访问修饰符为 private,则子类不能重写该方法

# 数组有没有 length () 方法?String 有没有 length () 方法?

数组没有 length () 方法,有 length 的属性。String 有 length () 方法。JavaScript 中,获得字符串的长度是通过 length 属性得到的,这一点容易和 Java 混淆。

# char 型变量中能不能存贮一个中文汉字,为什么?

char 类型可以存储一个中文汉字,因为 Java 中使用的编码是 Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个 char 类型占 2 个字节(16 比特),所以放一个中文是没问题的。

# 抽象类(abstract class)和接口(interface)有什么异同?

抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。

抽象类可以有构造函数;接口不能有。

类可以实现多个接口;但是只能继承一个类。

接口中的方法默认使用 public 修饰;抽象类中的方法可以时任意修饰符。

抽象类的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的

# 接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?

接口可以继承接口,而且支持多重继承。抽象类可以实现 (implements) 接口,抽象类可继承具体类也可以继承抽象类。

# 集合

# Map

可分为 HashMap 和 TreeMap

HashMap:hash 表无序,不能放重复键,允许放空键空值

TreeMap:数据结构是树,有序

# Collection

分为 List 和 Set

List:有序的,可以重复的

Set:无序的,不可重复的,需要重写 equals 和 hashcode 方法

List 接口:ArrayList、LinkedList

Set 接口:HashSet、TreeSet

HashSet:底层数据结构是哈希链表,保证元素的唯一,不保证元素顺序不变,需要使用 equals 方法和 hashcode 方法

TreeSet:底层数据结构是二叉树,保证元素唯一,并对元素按自然排序进行排序,可以实现 Compareable 接口重写 compareTo () 实现自定义排序

# ArrayList、LinkedList 的区别

ArrayLsit 和 LinkedList 都是实现了 List 接口

ArrayList 是基于动态数组的数据结构,LinkedList 是基于链表的

ArrayList:查询快,增加删除慢,由于数组在内存中是一块连续的内存,查询根据索引就能找到,所以快,而添加和删除需要移动内存,所以慢。

LinkedList:增加删除快,查询慢,由于链表在内存中不是连续的,查找时,需要从头部开始,挨着找,所以查询慢,而添加删除时,只需要改变引用指向即可,所以增加删除快

# ArrayList 和 vector 区别

ArrayList:默认容量 10,每次扩容时为之前的 1.5 倍,是线程不安全的,效率高

vector:默认容量 10,每次扩容是为之前的 2 倍,是线程安全的,效率低

# HashMap 和 HashTable 的区别

相同点

HashMap 和 HashTable 都是通过键值对来存储值的

不同点

HashMap:可以把 null 作为键或值,是线程不安全的,效率较高,默认容量 16,每次扩容时为原来的两倍

HashTable:不能把 null 作为键或值,是线程安全的,效率较低,默认容量为 11,每次 扩容为原来的两倍加一

创建时,如果给定容量初始值,那么 HashTable 就是给定的初始值,而 HashMap 会自动扩充为 2 的幂次方大小

# 如何决定使⽤ HashMap 还是 TreeMap?

对于 map 中插入、删除、定位一个元素这类操作,HashMap 时最好的选择,因为相对而言,HashMap 的插入更快,但如果要对 key 集合进行有序的遍历,那 TreeMap 是更好的选择。

# 说⼀下 HashMap 的实现原理?

HashMap 基于 Hash 算法实现的,可通过 put (key,value) 存储,get (key) 来获取,当传入 key 时,HashMap 会根据 key.hashCode () 计算出 hash 值,根据 hash 值将 value 保存到 bucket 里,当计算出的 hash 值相同时,我么称之为 hash 冲突,HashMap 的做法时采用链表和红黑树存储相同的 hash 值的 value,当 hash 冲突个数比较少时,使用链表或者红黑树。

# HashMap 与 concurrentHashMap 的区别

concurrentHashMap 对整个 Map 进行了分段分割,分为了 N 个 Segment,默认提升 16 倍,相对于 HashTable 的 synchronized 锁粒度更精细了一些,并发性能更好,而 HashMap 没有锁机制,不是线程安全的,JDk1.8 之后 concurrentHashMap 摒弃了 Segment 的数据结构,直接采用数组 + 链表 + 红黑树的数据结构实现,并发控制使用 synchronized 和 CAS (compare and swap) 来操作

# Collection 和 Collections 的区别?

Collection 是一个接口,它是 Set、List 等容器的父接口;Collections 是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。

# 如何实现数组和 List 之间的转换?

数组转 List:使用 Arrays.asList (array) 进行转化。

List 转数组:使用 List 自带的 toArray () 方法。

# 在 Queue 中 poll () 和 remove () 有什么区别?

相同点:都是返回第一个元素,并在队列中删除返回的对象。

不同点:如果没有元素 poll () 会返回 null,而 remove () 则会直接抛出 NoSuchElementException 异常。

# 迭代器 Iterator 是什么?

Iterator 接口提供遍历任何 Collection 的接口,我们可以从一个 Collection 中使用迭代器方法来迭代容器实例,迭代器取代了 java 集合框架中的 Enumeration。迭代器允许调用者在迭代的过程中移除元素。

# Iterator 怎么使用?有什么特点?

List<String> list = new ArrayList<>();
Iterator<String> it = list.iterator();
while(it.hasNext()){
    String obj = it.next();
    System.out.println(obj);
}

iterator 的特点是更加安全,因为它可以确保,在当遍历的集合元素中当元素被更改时,就会抛出 ConcurrentModificationException 异常。

# Iterator 和 ListIterator 有什么区别?

Iterator 可以遍历 Set 和 List,ListIterator 只能遍历 List。

Iterator 只能单向遍历,而 ListIterator 可以双向遍历。

ListIterator 从 Iterator 接口继承,并且添加了一些新的功能,比如:添加一个新元素,替换一个元素,获取前面或后面元素的索引。

# 怎么确保⼀个集合不能被修改?

可以使用 Collections.unmodifiableCollection (Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会被抛出 UnsupportOperationException 异常。

# IO

# Java 中有几种类型的流?

按功能分为:输入流和输出流。

按类型分为:字节流和字符流。

字节流继承 InputStream、OutPutStream,字符流继承 Reader、Writer。

区别:字节流是以 8 位传输以字节为单位输入输出数据,而字符流是以 16 位传输以字符位单位输入输出数据。

# BIO、NIO、AIO 的区别

BIO:同步阻塞

用户发起一个 IO 操作请求后,必须等待 IO 操作的完成,只有当真正完成了 IO 操作之后,用户进程才能运行

NIO:同步非阻塞

用户发起一个 IO 操作请求后,后边可以做其他事情,但是用户进程需要时不时的询问 IO 操作是否就绪,从而引起不必要的 CPU 资源浪费

AIO:异步非阻塞

用户发起一个 IO 操作请求后立即返回,等 IO 操作真正完成之后,应用程序会得到 IO 操作完成的通知。

# 实现拷贝文件的工具类使用字符流还是字节流

我们拷贝的文件不确定是只包含字符流,又可能是字节流(图片、声音、图像等),为保证通用性,要是有字节流

# 多线程

# 实现线程的方式

  1. 继承 Thread 类
  2. 实现 Runnable 接口
  3. 实现 Callable 接口

继承拓展性不强,Java 总是单继承,如果一个类继承了 Thread 类就不能继承其他类了

# 线程的启动方式

启动线程调用 start 方法,而启动以后执行的是 run 方法

# 区分线程

调用 setName 方法,设置一个线程名称,只是一种规范,在线程创建完成后,都需要设置名称

# 并行和并发有什么区别?

并行:多个处理器或多核处理器同时处理多个任务。

并发:多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行。

# 线程和进程的区别?

进程是资源分配的最小单位,线程是 CPU 调度的最小单位。

一个程序至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来提供程序的执行速度。

# 守护线程是什么?

守护线程是运行在后台的一种特殊的进程,它独立于控制终端并且周期性的执行某种任务或者等待处理某些发生的事件。

在 java 中垃圾回收线程就是特殊的守护线程。

# 什么是线程安全和线程不安全?

通俗来说,加锁的就是线程安全的,不加锁的就是线程不安全的

  • 线程安全:在多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能访问,直到该线程读取完,其他线程才能使用,不会出现数据不一致或者数据污染
  • 线程不安全:不提供数据的访问保护,有可能出现多个线程先后更改数据造成所得到的数据就是脏数据

# 什么是自旋锁?

当线程 A 想要获取一把自旋锁而该锁又被其他线程锁持有时,线程 A 在一个循环中自旋以检测锁是不是已经可用了。

自旋锁需注意:

  • 由于自旋时不释放 CPU,因而持有自旋锁的线程应该尽快释放自旋锁,否则等待该自旋锁的线程会一直在那里自旋,会浪费 CPU 时间
  • 持有自旋锁的线程在 sleep 前应该释放自旋锁以便其他线程可以获取自旋锁

# 什么是 CAS?

CAS(compare and swap)的缩写,比较并交换

CAS 不通过 JVM,直接利用 Java 本地方法 JNI(Java Native Interface 为 Java 本地调用),直接调用 CPU 的 cmpxchg(是汇编指令)。

利用 CPU 的 CAS 指令,同时借助 JNI 来完成 Java 的非阻塞算法,实现原子操作,其他原子操作都是利用类似的特性完成的。

整个 java.util.concurrent 都是建立在 CAS 之上的,因此对于 synchronized 阻塞算法,JUC 在性能上有很大提升。

CAS 是乐观锁技术,当多个线程使用 CAS 同时更新同一个变量时,只有一个线程能更新变量的值,其他线程都失败,失败的线程并不会挂起,而是被告知这次竞争失败,并可以再次尝试。

CAS 有三个操作数,内存值 V,旧的预期值 A,要修改的新值 B,当且仅当预期值 A 和内存值 V 相同时,将内存值修改为 B,否则什么都不做。

优点

确保对内存的读 - 改 - 写操作都是原子操作的

缺点

CAS 虽然很高效的解决原子操作,但是 CAS 仍然存在三大问题。ABA 问题,循环时间长开销大和只能保证一个共享变量的原子操作。

# 什么是乐观锁和悲观锁?

  • 乐观锁:是一种思想,相对于悲观锁而言,乐观锁假设认为数据一般不会造成冲突,所以在数据提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。
  • 悲观锁:JDK5 之前靠 synchronized 关键字保证同步的,这种通过使用一致的锁定协议来协调对共享状态的访问,可以保证无论哪个线程持有共享变量的锁,都采用独占的方式来访问这些变量,独占锁其实就是一种悲观锁,所以说 synchronized 是悲观锁。

# 什么是原子操作?

原子操作是指一个不受其他操作影响的操作任务单元,原子操纵是在多线程的环境下避免数据不一致必须的手段。

int++ 并不是一个原子操作,当一个线程读取它的值并加 1 时,另外一个线程有可能会读到之前的值,这就会引发错误。

# 什么是多线程的上下文切换?

即使是单核 CPU 也支持多线程执行代码,CPU 通过给每个线程分配 CPU 时间片来实现这个机制,时间片是 CPU 分配给各个线程的时间,因此时间片非常短,所以 CPU 会不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒。

上下文切换过程中,CPU 会停止处理当前运行的程序,并保证当前线程运行的具体位置以便之后继续运行。

CPU 通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务,但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以加载这个任务的状态。

从任务保存到再加载的过程就是一次上下文切换。

# 说一下 runnable 和 callable 有什么区别?

runnable 没有返回值,callable 可以拿到返回值,callable 可以算是 runnable 的补充。

# 线程有哪些状态?

NEW:尚未启动

RUNABLE:正在执行中

BLOCKED:阻塞的(被同步锁或 IO 锁阻塞)

WAITED:永久等待状态

TIME_WAIT:等待指定时间重新被唤醒的状体

TERMINATED:执行完成

# sleep 和 wait 的区别

  1. sleep 定义在 Thread 类上,不会释放锁,使用在任何地方
  2. wait 定义在 object 类上,会释放锁,必须在同步方法或同步代码块中执行
  3. 超时或调用 interrupt 方法唤醒 sleep 线程
  4. notify 随机唤醒一个 wait 线程,notifyall 唤醒所有 wait 线程

# notify () 和 notifyAll () 的区别

notify () 唤醒一个线程,具体唤醒哪一个线程有虚拟机控制。

notify () 唤醒所有线程,会将全部线程有等待池移到锁池,然后参与竞争,竞争成功则执行,如果不成功则留在锁池等到锁释放后再次竞争。

# 线程的 run () 和 start () 有什么区别?

start () 是启动线程,run () 是执行线程的运行时代码,run () 可以重复调用,start () 只能执行一次。

# 线程的 sleep () 方法和 yield () 方法有什么区别?

  1. sleep () 方法给其他线程机会时不考虑线程的优先级,因此会给优先级低的线程已运行的机会;yield () 方法只会给相同优先级或者更高优先级的线程以运行的机会;
  2. 线程执行 sleep () 方法后进行阻塞(blocked)状态,而执行 yield () 放在则转入就绪(ready)状态;
  3. sleep () 方法需要声明抛出 InterruptedException,而 yield () 没有任何的异常声明;
  4. sleep () 方法比 yield () 方法具有更高的移植性;

# synchronized 详解

synchronized 是常见的线程同步手段之一,它是如何保证同一时刻只有一个线程可以进入临界区呢?

synchronized 是对对象加锁,在 JVM 中,对象在内存中分为三块区域:对象头、实例数据和对齐填充。在对象头中保存了锁标志位和指向 Monitor 对象的起始地址。当 Monitor 被线程持有后,就会处于锁定状态,Owner 部分会指向持有 Monitor 对象的线程,另外 Monitor 中还有两个队列,用来存放进入及等待获取锁的线程。

synchronized 应用在方法上时,在字节码中是通过方法的 AccCC_Synchronized 标志实现的,Synchronized 应用在同步代码块上时,在字节码中是通过 Monitorenter 和 Monitorexit 实现的。

针对 Synchronized 获取锁的方式,JVM 使用了锁升级的优化方式,就是使用偏向锁优先同一线程再次获取锁,如果失败,就升级为 Cas 轻量级锁,如果再失败会短暂自旋,防止线程被系统挂起,最后如果以上都失败就升级为重量级锁。

# synchronized 和 lock 的区别

synchronized 时一个关键字,lock 是一个接口

synchronized 可以给类、方法和同步代码块加锁,lock 只能给同步代码块加锁

synchronized 无需手动获取和释放锁,发生异常时会自动解锁,不会出现死锁,lock 需要自己手动加锁和释放锁,如 lock ()、unlock (),如果忘记使用 unlock (),则会出现死锁,所以一般在 finally 里面加上 unlock ()

# synchronized 和 volatile 的区别

  1. 作用位置不同

    synchronized 修饰类、方法、代码块

    volatile 修饰变量

  2. 作用不同

    synchronized 可以保证变量修改的原子性和可见性,可能会造成线程阻塞

    volatile 仅能保证变量修改的可见性,无法保证原子性,不会造成线程阻塞

# 什么是死锁,如何解决

死锁

线程 1 独占资源 a 并且尝试获取独占资源 b,而线程 2 独占资源 b 并尝试获取独占资源 a,两个线程在等待另一个资源的同时不释放资源,就形成了死锁

形成死锁的四个必要条件

  1. 互斥条件:一个资源每次只能被一个进程使用
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺资源:进程以获得的资源,在未完成之前,不能强行剥夺
  4. 循环等待条件:若干个进程形成一种头尾相接的循环等待资源关系

预防死锁

  1. 破坏请求和保存条件

    一次性的申请所有资源,之后不再申请资源,如果不满足资源条件则得不到资源分配。

    只获得初期资源运行,之后将运行完的资源释放,请求新的资源

  2. 破坏不可抢占条件

    当一个进程获取某种不可抢占资源,提出新的资源申请,若不能满足,则释放所有资源,以后需要,再次重新申请

  3. 破坏循环等待条件

    对资源进行排号,按照序号递增的顺序请求资源,若进程获得序号高的资源想要获取序号低的资源,就需要先释放序号高的资源

# 线程并发库

创建线程池的四种方式

方法名作用
newFixedThreadPool()创建固定数量的线程池
newCachedThreadPool()创建缓存的线程池
newSingleThreadExecutor()创建单个线程
newScheduledThreadPool()创建定时器线程池

# 线程池中 submit () 和 execute () 方法有什么区别?

execute ():只能执行 Runnable 类型的任务。

submit ():可以执行 Runnable 和 Callable 类型的任务。

Callable 类型的任务可以获取执行的返回值,而 Runnable 执行无返回值。

# 在 java 程序中怎么保证多线程的运行安全?

  1. 使用安全类,如 java.util.current 下的类
  2. 使用自动锁 synchronized
  3. 使用手动锁 Lock

# 线程池的作用

  1. 限定线程的个数,提高线程的可管理性
  2. 提高响应速度
  3. 降低资源消耗

# 请说出与线程同步以及线程调度相关的方法。

wait ():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;

sleep ():是一个正在运行的状态处于睡眠状态,是一个静态方法,调用此方法需要处理 InterruptedException 异常;

notify ():唤醒一个正在处于等待的线程,调用此方法时,并不能确切的唤醒某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,与优先级无关;

notifyAll ():唤醒所有处于等待状态的线程,该方法并不是把所有对象的锁给所有线程,而是让他们竞争,只有获得锁的线程才能进入就绪状态。

# 反射

# 什么是反射

程序在运行时可以通过类名获取类的所有信息

# 反射的实现方式

  1. Class.forName()
  2. 类名.class
  3. 对象.getClass ()

# 如何通过反射创建对象?

  1. 通过类对象调用 newInstance () 方法
  2. 通过类对象的 getConstructor 或 getDeclaredConstructor () 方法获得构造器(constructor)对象并调用 newInstance () 方法创建对象

# 简述一下面向对象的 "六原则一法则"。

单一职责原则:一个类只做它该做的事情。

开闭原则:软件实体应当对拓展开放,对修改关闭。

依赖倒装原则:面向接口编程。

接口隔离原则:接口要小而专,绝不能大而全。

合成聚合复用原则:优先使用聚合或合成关系复用代码。

迪米特法则:又名最小知识原则,一个对象应当对其他对象有尽可能少的了解。

# 反射的优缺点

优点:在运行期间绑定对象,提高了灵活性

缺点:有性能有影响,他的操作总是慢于直接代码

# 什么时动态代理?怎么实现动态代理?

动态代理是运行时动态生成代理对象。

实现方式:JDK 动态代理和 Cglib 代理

JDK 代理是基于接口实现的

Cglib 代理是基于继承实现的

# 什么是 java 序列化?什么情况下需要序列化

Java 序列化是为了保存各种对象在内存中的状态,并且可以把保存的对象状态再读取出来。

以下情况需要使用序列化:

  • 想把内存中的对象状态保存到一个文件中或者数据库中的时候。
  • 想用套接字在网络中传输对象的时候。
  • 想通过 RMI(远程调用)传输对象的时候。

# 对象拷贝

# 为什么要使用克隆?

克隆的对象可能包含一些已近修改过的属性,而 new 出来的对象的属性都还是初始化时候的值,所有当需要一个新的对象来保存当前对象的 “状态” 就需要靠克隆方法了。

# 如何实现对象克隆?

  1. 实现 Cloneable 接口并重写 Object 类中的 clone () 方法;
  2. 实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆;

# 深拷贝和浅拷贝区别是什么?

浅克隆:当对象被复制时只复制它本身和其中包含值类型的成员变量,而引用类型的成员变量并没有复制。

深克隆:对了对象本身被复制外,对象包含的所有成员变量也将被复制。

# 异常

# throw 和 throws 的区别?

throw:是真实抛出一个异常。

throws:是声明可能会抛出一个异常。

# 阐述 final、finally、finalize 的区别

final:有三种用法,如果一个类被声明 final,该类将不可被继承,将变量声明为 final,必须在声明时赋予初值,在以后的引用中不可被修改,将方法声明为 final,该方法不可被重写。

finally:通常放在 try...catch... 的后面,无论程序是正常执行还是发生异常,都会执行 finally 里面的操作,一般将释放外部资源的代码写在 finally 中。

finalize:Object 中的方法,Java 中允许使用 finalize () 方法在垃圾收集器将对象从内存中清除除去之前做必要的清理工作,这个方法是由垃圾收集器在销毁对象时调用的,通过重写 finalize () 方法可以整理系统资源或者执行其他清理工作。

# try-catch-finally 中哪个部分可以省略?

catch 或 finally 可以被省略,不能同时省略,也就是有 try 的时候,必须后面跟一个 catch 或者 finally。

# try {} 里有一个 return 语句,那么紧跟在这个 try 后的 finally {} 里的代码会不会被执行,什么时候被执行,在 return 前还是后?

会执行,在方法返回调用者前执行。

# Error 和 Exception 有什么区别?

Error 表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的情况;Exception 表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示如果程序运行正常,从不会发生的情况。

# 列出一些你常见的运行时异常?

ClassCastException:类转换异常

IndexOutOfBoundException:下标越界异常

NullPointerException:空指针异常

IIIegalArgumentException:非法参数异常

SecurityException:安全异常

IOException:IO 异常

更新于

请我喝[茶]~( ̄▽ ̄)~*

七音 微信支付

微信支付

七音 支付宝

支付宝