Java基础
1.面向过程和面向对象的区别
- 面向过程:是分析问题的解决步骤,然后用函数把这些步骤一步步实现,最后在使用的时候一一调用即可。性能较高,
- 面向对象:把构成问题的事务分解成各个对象,而建立这些对象目的也不是为了完成一个个步骤,而是为了描述某个事务在解决整个问题的过程中所发生的行为。面向对象有三大特点:封装、继承、多态。所以相对面向过程来说易维护、易复用、易扩展,但从 性能上来说,比面向对象的性能低。
- 封装:隐藏对象的属性和实现细节,只对外公开一些操作内部属性的方法。其目的就是为了增强安全性、简化编程,使用者不需要了解具体的实现细节,只需要通过外部接口的调用,就可以完成对类的内部成员的操作。
- 继承:把已存在类的定义作为基础创建类,新类继承旧类的所有属性和方法,可以增加新的属性和方法,也可以重写父类的方法,但不能选择性的继承父类,其目的是能够非常方便地服用以前的代码,大大提高开发效率。
- 多态:一个方法根据对象的不同会有不同的行为方式,常用在通过指向父类的指针来调用在不同子类中的方法,其中继承是多态的基础。重写和重载就是典型的多态的实现
- 重写:是父类与子类之间多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Override)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。
- 重载:重载(Overload)是一个类中多态性的一种表现。如果在一个类中定义了多个同名的方法,它们参数列表不同,则称为方法的重载(Overload)。
2.重载和重写的区别
- 重写必须继承;重载则不用。
- 重写的方法名、参数数目相同、参数类型兼容;重载的方法名相同,参数列表不同
- 重写的方法修饰符大于等于父类的方法;重载和修饰符无关
- 重写是子类的方法覆盖父类的的方法,要求方法名和参数都相同;重载是在同一个类中的两个或两个以上的方法,拥有相同的方法名,但是参数却不相同,方法体也不相同。
3.equals和==的区别
- ==:比较的变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指向同一个对象。比较的是真正意义上的指针操作。
- equals:用来比较的是两个对象的内容是否是同一个对象。即比较的是值是否相同。
4.String、StringBuffer和StringBuilder的区别
- String是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个final类型的字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对String的操作都会生成新的String对象。每次+操作相当于隐式在堆上new了一个跟源字符串相同的StringBuilder对象,再调用append方法拼接+后面的字符。
- StringBuffer和StringBuilder他们都继承了AbstractStringBuilder抽象类,从该抽象类中可以看出,他们的底层都是可变的字符数组,所以再进行频繁的字符串操作时,建议使用StringBuffer和StringBuilder来进行操作。另外StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
5.ArrayList和LinkedList的区别
List有两个重要的实现类就是ArrayList和LinkedList
- ArrayList从底层可以看出是一个能够自动增长容量的数组,因为数组是基于索引的数据结构,它在使用索引在数组中搜索和读取数据很快,但是删除数据却是开销很大,因为这需要重排数组中的所有数据,即删除数据以后将后面的所有数据前移。
- LinkedList是一个双链表,在添加和删除元素时具有比ArrayList更好的性能,但在get和set方面弱于ArrayList,当然这些对比都是基于数据量很大或者操作很频繁的时候。
6.HashMap和HashTable的区别
- 两者父类不同:HashMap继承于AbstractMap类,而HashTable继承与Dictionary类。不过它们都同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口
- 对外提供的接口不同:HashTable比HashMap多提供了elements()和contains()两个方法。elements()方法继承Dictionary类,用于返回HashTable中value的枚举;contains()方法判断该HashTable是否包含传入的value。作用与containsValue()一致。事实上containsValue()就只是调用了contains()方法。
- 对null的支持不同:HashTable中key和value都不可为null。HashMap中key可以为null,但这样的key只能有一个,因为必须保证key的唯一性;而且可以有多个key值对应的value为null
- 安全性不同:HashMap是线程不安全的,在多线程并发的环境下,可能会产生死锁,所以需要开发人员自己处理多线程的安全问题。HashTable是线程安全的,它每个方法上都有synchronized关键字,可直接用于多线程中,所以在单线程场景下它的效率远低于HashMap,当需要多线程操作时可以使用线程安全的ConcurrentHashMap,因为ConcurrentHashMap由于使用了分段锁的结构,并不对整个数据进行锁定,所以效率比HashTable要高。
7.Collection和Collections的区别
- Collection:集合类的上级接口,子接口有Set、List、LinkedList、ArrayList、Vector、Stack等
- Collections:集合类的一个帮助类。包含各种有关集合操作的静态多态方法,用于实现对各种集合的搜索、排序、线程安全化等操作。不可实例化,就像一个工具类,服务于Collection框架。
8.List,Set,Map三者的区别
- List:接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
- Set:不允许重复的集合,不会有多个元素引用相同的对象
- Map:使用键值对存储,Map会维护与key有关联的值。两个key可以引用相同的对象,但key不可以重复。
9.抽象类和接口的区别
- 默认方法实现:抽象类可以有默认的方法实现,接口完全是抽象的,不存在方法的实现。
- 实现:子类使用extends关键字来继承抽象类,如果子类不是抽象类,它需要提供抽象类中所有声明的方法的实现;子类使用关键字implements来实现接口,他需要提供接口中的所有声明的方法的实现。
- 构造器:抽象类可以有构造器,接口不能有构造器
- 访问修饰符:抽象类可以有public、protected和default这些修饰符;接口只能使用public,不可以使用其他的修饰符。
- 添加新方法:抽象类中添加新的方法,你可以给他提供默认的实现,不需要改变现有的代码。接口中添加方法,那么必须改变实现该接口的类。
- 多继承:抽象类可以继承一个类和实现多个接口,接口只可以实现一个或多个接口。
10.HashMap底层原理
JDK1.7的底层原理
HashMap对象在实例化后,底层从创建一个长度为16的一维数组Entry[],当执行put(key1,value1)方法添加数据时,首先调用key1所在类的hashcode()方法计算key1的哈希值,此哈希值经过底层算法的计算之后可以得到对应Entry数组中的存放位置。
- 如果该位置上数据为空,此时key1-value1添加成功
- 如果此位置上数据不为空(意味着此位置上存在一个或多个数据(多个数据以链表的形式存在)),则比较key1和已存在的数据的哈希值,如果key1的哈希值与已存在的哈希值都不相同,此时key1-value1添加成功,即key1-value1和原来的数据会以链表的形式存储;如果key1的哈希值和某个已经存在的数据(如key2-value2)的哈希值相同,则调用key1所在类的equals()方法,返回key1和key2的比较结果,如果返回为false,此时key1-value1添加成功,同样也是和原来的数据以链表的形式存储;如果返回的时true,则会使用value1替换value2.
在不断添加数据的过程中,会涉及到数组扩容问题。当添加数据超出临界值(且数据要存放的位置非空时),需要对数组扩容。默认扩容方式:扩容为原来容量的2倍,并将原来的数据复制过来。
JDK1.8及以后相较于JDK1.7底层实现方面的不同
- 使用空参构造器创造HashMap对象时,底层没有直接创建一个长度为16的数组,而是在首次调用put()方法时才会创建。
- JDK1.8底层存储数据的数组变为Node[].
- JDK1.7底层为数据+链表,JDK1.8底层数据结构为:数组+链表+红黑树
- 当存储数据形成链表时,链表的结构也不相同,JDK1.7的链表结构是新的数据指向旧的数据。JDK1.8的链表结构是:旧的数据指向新的数据
- 当底层数组的某个索引上的元素以链表形式存在的数据个数大于8,且当前数组的长度大于64时,此时这个索引位置上的数据改为红黑树存储
11.final、finally、finalize 有什么区别?
- final可以修饰类,变量,方法,修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写
- finally用于抛异常,finally代码块内语句无论是否发生异常,都会在执行finally,常用于一些流的关闭。
- finalize方法用于垃圾回收
一般情况下不需要我们实现finalize,当对象被回收的时候需要释放一些资源,比如socket链接,在对象初始化时创建,整个生命周期内有效,那么需要实现finalize方法,关闭这个链接。
但是当调用finalize方法后,并不意味着gc会立即回收该对象,所以有可能真正调用的时候,对象又不需要回收了,然后到了真正要回收的时候,因为之前调用过一次,这次又不会调用了,产生问题。所以,不推荐使用finalize方法。
12.this和 super的区别
this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。
this的用法:
- 普通的直接引用,this相当于是指向对象本身
- 形参与成员名字重名,用this来区分
- 引用本类的构造函数
super可以理解为是指向自己父类对象的一个指针,而这个父类指的是离自己最近的一个父类
super的用法:
- 普通的直接引用,相当于指向当前对象父类的引用,这样就可以用super.xxx来引用父类的成员
- 子类中的成员变量与父类的成员变量或方法同名时,用super进行区分
- 引用父类的构造函数
二者的区别:
- super:它引用当前对象的直接父类中的成员,this:他代表当前对象名
- super()和this()类似,区别是super()在子类中调用父类的构造方法,二者均需放在构造方法内的第一行
13.反射
13.1.什么是反射机制
简单来说,反射机制指的是程序在运行时能够获取自身的信息。
Java反射机制是在运行状态中,对于任意一个类/对象,都能够知道这个类/对象的所有属性和方法,这种动态获取的信息以及动态调用对象的方法的功能称为Java的反射机制
13.2.反射机制的优缺点
- 优点:运行期类型的判断,动态加载类,提高代码灵活度。
- 缺点:性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。
13.3.反射机制的应用场景
- 我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;
- Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:
- 将程序内所有 XML 或 Properties 配置文件加载入内存中;
- Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息;
- 使用反射机制,根据这个字符串获得某个类的Class实例;
- 动态配置实例的属性
13.4.Java获取反射的三种方法
- 通过new对象实现反射机制:对象名.getClass()
- 通过路径实现反射机制:Class.forName("类的路径")
- 通过类名实现反射机制:类名.class
14.动态代理
14.1.什么是动态代理
代理类在程序运行时创建的代理方式被成为 动态代理。也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
14.2.动态代理的两种实现方法
- JDK动态代理是由Java内部的反射机制来实现的
- cglib动态代理底层则是借助asm来实现的。
浙公网安备 33010602011771号