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

  • 面向过程:是指分析出解决问题的步骤,然后用函数将这些步骤一步步实现,然后在使用的时候一一调用即可。性能较高,所以单片机嵌入式等开发一般使用面向过程开发。
  • 面向对象:是指把构成问题的事务分解成各种对象,建立对象的目的不是为了完成一个个步骤,而是为了描述该对象在解决整个问题的过程中所发生的行为。面向对象有封装继承多态的特性,所以易维护、易复用、易扩展。可以设计出低耦合的系统。但是性能比面向过程低。

重载和重写的区别

  1. 重载就是在一个类中同名的方法具有不同的参数列表(参数类型、参数个数以及参数顺序不同)都被视为重载,但是重载对返回类型没有要求,可以相同也可以不同。

  2. 重写就是指子类继承了父类中原有的方法,但是有时候子类不想原封不动的继承父类中的某个方法,所以对方法体进行修改或者重写。

    1. 重写,方法名、参数列表和返回值必须相同,抛出的异常范围要小于等于父类,访问权限要大于等于父类。
    2. 如果父类中的方法访问修饰符为private/final/static,则子类就不能重写该方法。

String、StringBuffer和StringBuilder的区别是什么?String为什么是不可变的?

可变性:

  1. String类中使用final关键字修饰字符数组来保存字符串,所以String对象是不可变的。
  2. StringBuilder和StringBuffer都继承了AbstractStringBuilder类,在AbstractStringBuilder类中也是使用了字符数组进行保存字符串,但是没有使用final关键字修饰,所以这两种对象是可变的。

线程安全及性能

  1. String中对象是不可变的,可以理解为常量,线程安全。

  2. StringBuilder和StringBuffer都继承了AbstractStringBuilder类,而AbstractStringBuilder类中定义了一些字符串的基本操作方法。

    1. 而StringBuffer对这些方法或者对调用的方法加了同步锁,所以是线程安全的,效率相对较低。一般在多线程中使用。
    2. StringBuilder没有对这些方法加同步锁,所以是非线程安全的,效率相对较高。一般在单线程中使用。

自动装箱和拆箱

每个基本数据类型都对应了一个包装类。

  1. 自动装箱:是指将基本数据类型包装为一个包装类对象,例如像一个泛型为Integer的集合中添加int类型的变量。
  2. 自动拆箱:将一个包装类对象转换为一个基本数据类型,例如将一个包装类对象赋值给一个基本数据类型的变量。

接口和抽象类的区别是什么?

  1. 接口的方法默认是public,且只能包含抽象方法和静态方法,所有方法在接口中不能有实现(Java8之后接口方法可以有默认实现),而抽象类中可以包含普通方法。
  2. 接口中除了static、final变量,不能有其他的变量,而抽象类中的成员变量可以是各种类型的。
  3. 一个类可以实现多个接口,但是只能继承一个抽象类。接口自身也可以通过extends关键字来扩展多个接口。
  4. 是否有构造器)接口不能包含构造器,抽象类可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器完成属于抽象类的初始化操作
  5. 从设计层面来说,抽象是对类的抽象,是一种模板设计;而接口是对行为的抽象,是一种行为的规范。

创建一个对象用什么运算符? 对象实体和对象引用有何不同?

使用new运算符,new用来创建对象实例,对象实例存在于堆内存中,对象引用存在于栈内存中,对象引用指向对象实例。


一个类的构造方法的作用是什么?如果一个类没有声明构造方法,该程序能正确执行吗?为什么?

类的构造方法的主要作用是完成对类对象的初始化工作;可以执行;因为一个类即使没有声明构造方法也会有默认不带参数的构造方法。


静态方法和实例方法

  1. 在外部调用静态方法时,可以使用类名.方法名的方式也可以使用对象名.方法名的方式;而调用实例方法只能使用对象名.方法名的方式。
  2. 静态方法在访问本类内部的成员时,只能访问静态成员(即静态变量和静态方法),而不允许访问实例成员变量和实例成员方法;实例方法没有这个限制。

==和equals

  • == 的作用:

    • 基本类型:比较值是否相等
    • 引用类型:比较的是对象的内存地址值是否相等
  • equals 的作用:

    • 基本类型:不能用于判断基本数据类型的变量
    • 引用类型:用于比较两个对象的内容是否相等,默认况下,因为所有的类都继承了Object类,Object类中的equals方法使用的是==,所以是比较内存地址值是否相等。也可以按照需求逻辑,重写覆盖掉对象的equals方法。

HashCode的作用

  • Java中的集合有两类,一类是List,一类是Set。前者有序可重复,后者无序不可重复。当在Set集合汇总插入元素时怎么判断元素是否存在呢?可以通过equals方法。但是如果元素太多,就要使用很多次equals方法,就会比较慢。

  • HashCode就是根据对象的内存地址换算出来的一个值。这样当集合添加新元素时,先调用这个元素的hashCode方法,就能定位到他应该放置的物理位置上。

    • 如果该位置上没有元素,就不用进行任何比较,直接在这个位置上进行存储。
    • 如果该位置上有元素,才调用equals方法与该元素进行比较,相同的话就不进行存储,不相同就散列到其他地址。
  • 这样调用equals方法次数就大大降低了,提升了效率。


有没有可能两个不相等的对象有相同的HashCode

有可能

两个对象具有相同的hashCode时代表发生了哈希冲突,一般有以下三种解决方式:

  • 拉链法:每个哈希表节点都有一个next指针,多个哈希表节点可以通过next指针相连构成一条单链表,这样,发生哈希冲突的对象就可以使用这条单链表进行存储。
  • 开发定址法:一旦发生了哈希冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
  • 双哈希法:有多个哈希函数,当发生哈希冲突时,使用第二个、第三个哈希函数进行继续计算地址,直到没有冲突

为什么重写equals时必须重写hashcode方法?

  • 因为如果两个对象相等,则HashCode也一定是相同的。
  • 但是两个对象有相同的hashcode值,他们不一定是相同的。
  • 因此重写equals方法时必须重写hashcode方法。

Java中是值传递还是引用传递

  1. 按值调用时值方法接收的是调用者提供的值;按引用调用是指方法接收的是调用者提供的变量地址。
  2. Java总是按值调用的,方法得到的是所有参数值的副本,传递对象时实际上方法接收到的是对象引用的副本。

浅拷贝和深拷贝

  • 浅拷贝:只复制当前对象的基本数据类型和引用变量,但是引用变量指向的那些对象不进行复制,引用变量指向的仍然是原对象中引用变量所指向的值。修改克隆对象可能回影响原对象,不安全。
  • 深拷贝:完全拷贝了基本数据类型和引用数据类型以及引用数据类型指向的对象,也进行了复制。也就是说深拷贝把要复制的对象所引用的对象也全部进行了复制。

final关键字

final关键字主要用在三个地方:变量、方法、类

  1. 对于一个final变量,如果是基本数据类型的变量,则其数值一旦初始化之后就不能改变;如果是引用类型的变量,则在对其进行初始化之后就不能再让其指向另一个对象。
  2. final修饰的方法不能被重写,但是子类可以使用父类中final修饰的方法;
  3. 当用final秀是一个类时,表明这个类不能被继承,final类中的所有成员方法都会被隐式的指定为final方法。

static关键字

  • 可以修饰变量和方法,被static关键字修饰的变量和方法被称为静态变量和静态方法,

    • 都属于类的静态资源
    • 是被类的实例所共享的
    • 可以通过类名.对象或者方法名的方式直接调用
  • static还可以用于静态代码块,一般用于一些初始化操作