目录
String,StringBuffer, StringBuilder 的区别是什么
面向对象和面向过程的理解
- 面对对象
- 面向对象是把业务逻辑或者问题分解抽象为对象,分析需求中有哪些对象,分析每个对象各自需要做什么事情,通过对象完成各自部分功能实现,从而实现整体的一个业务逻辑,易于复用、扩展和维护
- 面对过程
- 面向过程分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了,需要做什么直接就开始干,比较直接高效,只思考事情的本身
面向对象三大特性
- 封装,也就是把客观事物封装成抽象的类,隐藏对象的属性和实现细节,仅对外提供公共访问方式,便于使用,提高复用性和安全性
- 继承,存在于子父类关系中,主要不满足父类的功能实现,可以对父类的功能进行扩展,继承是多态的前提
- 多态性,父类引用指向子类对象,提高了程序的拓展性,基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同
抽象类和接口的对比
- 从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范
- 备注:Java8中接口中引入默认方法和静态方法,以此来减少抽象类和接口之间的差异
- 现在,我们可以为接口提供默认实现的方法了,并且不用强制子类来实现它
参数 | 抽象类 | 接口 |
声明 | 抽象类使用abstract关键字声明 | 接口使用interface关键字声明 |
实现 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现 | 子类使用implements关键字来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
访问修饰符 | 抽象类中的方法可以是任意访问修饰符,默认修饰符是default | 接口方法默认修饰符是public。并且不允许定义为 private 或者 protected,1.8之后可以是default和static |
多继承 | 一个类最多只能继承一个抽象类 | 一个类可以实现多个接口 |
字段声明 | 抽象类的字段声明可以是任意的 | 接口的字段默认都是 static 和 final 的 |
成员变量与局部变量的区别有哪些
- 定义
- 变量:在程序执行的过程中,在某个范围内其值可以发生改变的量。从本质上讲,变量其实是内存中的一小块区域
- 成员变量:方法外部,类内部定义的变量
- 局部变量:类的方法中的变量。
- 成员变量和局部变量的区别
- 作用域
- 成员变量:针对整个类有效。
- 局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)
- 存储位置
- 成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。
- 局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放
- 生命周期
- 成员变量:随着对象的创建而存在,随着对象的消失而消失
- 局部变量:当方法调用完,或者语句结束后,就自动释放
- 初始值
- 成员变量:有默认初始值。
- 局部变量:没有默认初始值,使用前必须赋值
重载(Overload)和重写(Override)的区别
- 重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分
- 重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写
hashCode 与 equals
- hashcode的作用是获取哈希码,返回的是一个int整数,就是确定的对象在哈希表中的索引位置,特点就是能够快速根据key检索出value,对象是在堆中,实际上就是维护了一张hash表,索引就是hashcode,这个方法属于object类中
- 先通过比较hash值,然后比较equals方法,这个哈希码的作用是确定该对象在哈希表中的索引位置,这样提高了执行速度,以及工作效率
- 散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
- 如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度
== 和 equals 区别是什么
==
常用于相同的基本数据类型之间的比较,也可用于相同类型的对象之间的比较;- 如果
==
比较的是基本数据类型,那么比较的是两个基本数据类型的值是否相等; - 如果
==
是比较的两个对象,那么比较的是两个对象的引用,也就是判断两个对象是否指向了同一块内存区域;
- 如果
- equals方法主要用于两个对象之间,检测一个对象是否等于另一个对象
- 它的作用也是判断两个对象是否相等,般有两种使用情况:
- 情况1,类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
- 情况2,类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)
String,StringBuffer, StringBuilder 的区别是什么
- 可变性
- String类中使用字符数组保存字符串,private final char value[],所以string对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的,都是在原对象上进行操作,string每次操作都是生成新的一个对象
- 线程安全性
- String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的
- 性能
- 每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险
- 对于三者使用的总结
- 如果要操作少量的数据用 = String单线程操作字符串缓冲区 下操作大量数据 = StringBuilder多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
String有哪些特性
- 不变性:String 是只读字符串,是一个典型的 immutable 对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,可以保证数据的一致性。
- 常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用
- final:使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性
String为什么要设计成不可变的
1.便于实现字符串池(String pool)
在Java中,由于会大量的使用String常量,如果每一次声明一个String都创建一个String对象,那将会造成极大的空间资源的浪费。Java提出了String pool的概念,在堆中开辟一块存储空间String pool,当初始化一个String变量时,如果该字符串已经存在了,就不会去创建一个新的字符串变量,而是会返回已经存在了的字符串的引用。
2.使多线程安全
在并发场景下,多个线程同时读一个资源,是安全的,不会引发竞争,但对资源进行写操作时是不安全的,不可变对象不能被写,所以保证了多线程的安全。
3.避免安全问题
在网络连接和数据库连接中字符串常常作为参数,例如,网络连接地址URL,文件路径path,反射机制所需要的String参数。其不可变性可以保证连接的安全性。如果字符串是可变的,黑客就有可能改变字符串指向对象的值,那么会引起很严重的安全问题。
4.加快字符串处理速度
由于String是不可变的,保证了hashcode的唯一性,于是在创建对象时其hashcode就可以放心的缓存了,不需要重新计算。这也就是Map喜欢将String作为Key的原因,处理速度要快过其它的键对象。所以HashMap中的键往往都使用String
什么是字符串常量池
字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串,在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用