1. 字符串 String 相关
1.1String 、StringBuffer 和 StringBuilder 的区别
- String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
- StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
1.2 String 为什么要设计成不可变类
常量池的需要
字符串常量池是Java堆内存的一个特殊区域,当创建一个String对象时,假如字符串已经存在于常量池中,则不会创建新的对象,而是直接引用已经存在的对象。
String s1 = "abc";
String s2 = "abc";
s1 和 s2 指向常量池中的同一个对象 abc ,如果 String 是可变类, s1 对其的修改将会影响到 s2 。
HashCode 缓存的需要
因为字符串不可变,在创建的时候HashCode就被缓存,不需要重新计算。
多线程安全
由多个线程之间共享,不需要同步处理。
如何实现不可变
1、不要提供任何会修改对象状态的方法。
2、保证类不会被拓展(一般声明为 final 即可)。
3、使所有的域都是 private final的。
4、确保对于任何可变组件的互斥访问(可以理解如果中存在可变对象的域,得确保客户端无法获得其引用,并且不要使用客户端提供的对象来初始化这样的域)。
1.3 new String 和直接赋值的区别:
- String str1 = “ABC”,可能创建一个或者不创建对象,如果ABC这个字符串在常量池中已经存在了,那么str1直接指向这个常量池中的对象。
- String str2 = new String(“ABC”),至少创建一个对象。一定会在堆中创建一个 str2 中的 String 对象,它的 value 是 ABC,如果 ABC 这个字符串在常量池中不存在,会在池中创建一个对象。
2. Java 中 ==equals 和 hashCode 的区别
==
在Java中,分为基本数据类型和复合数据类型,基本数据类型包括 byte、short、char、int、long、float、double、boolean 这八种。
- 对于基本数据类型,== 比较的是它们的值。
- 对于复合数据类型(类、接口、数组)),比较的是它们在内存中的存放地址,即比较的是否是同一个对象。
在复合数据类型中,除非是同一个 new 出来的对象,他们 == 结果是 true,其他的都为 false。
equals
- 默认情况(没有覆盖equals方法)
Object的equals方法默认主要用于判断对象内存地址引用是不是同一个地址(是不是同一个对象),此时定义的 equals 与 == 等效
public boolean equals(Object obj) {
return (this == obj);
}
- 重写覆盖 equals 方法
有些时候,对于两个不同的对象,我们又需要提供逻辑上是否相等的判断方法,这时候就需要重写 equals 方法。Java 提供的某些类已经重写了 equals 方法,用于判断"相等"的逻辑,例如 Integer。
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
hashCode
定义
public native int hashCode();
hashCode() 方法返回的就是一个 hash 码( int 类型)。
hash 码的主要用途就是在对对象进行散列的时候作为 key 输入,我们需要每个对象的 hash 码尽可能不同,这样才能保证散列的存取性能(将数据按特定算法指定到一个地址上)。事实上,Object 类提供的默认实现确实保证每个对象的 hash 码不同(在对象的内存地址基础上经过特定算法返回一个 hash 码)。
作用
hashCode 只有在集合中用到,相当于集合的 key ,利用下标访问可以提高查找效率
Java 中的集合(Collection)有两类,一类是 List,再有一类是 Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?
Java 采用了哈希表的原理。当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。
如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;(放入对象的 hashcode 与集合中任一元素的 hashcode 不相等)
如果这个位置上已经有元素了( hashcode 相等),就调用它的 equals 方法与新元素进行比较,相同的话就不存,不相同就散列其它的地址。
所以这里存在一个冲突解决的问题(即 hash 冲突)。这样一来实际调用 equals 方法的次数就大大降低了,几乎只需要一两次。
人话:解决 set 集合当中元素是否重复的问题,添加元素时先调用元素的 hashCode() 方法来确定在集合中的位置,如果该位置没有元素直接插入,如果该位置有元素再进行 equals 判定二者是否相同,同就不存,不同就存到别的地方。
equals和hashcode之间的关系:
- 默认情况下,equals 相等,hashcode 必相等,hashcode 相等,equals 不是必相等。hashcode 基于内存地址计算得出,可能会相等,虽然几率微乎其微。
3. 基础类与封装类,以 int 与Integer 为例
传递方式不同
- 基本类型(原始数据类型)在传递参数时都是按值传递。
- 封装类型是引用类型,按引用传递的(其实“引用也是按值传递的”,传递的是对象的地址)。由于包装类型都是不可变量,因此没有提供改变它值的方法,增加了对“按引用传递”的理解难度。
int是基本类型,直接存放数值;Integer是类,产生对象时用一个引用指向这个对象。
封装类可以有方法和属性
封装类可以有方法和属性,利用这些方法和属性来处理数据,如 Integer.parseInt(Strings) 。基本数据类型都是 final 修饰的,不能继承扩展新的类、新的方法。
默认值不同
基本类型跟封装类型的默认值是不一样的。如 int i, i 的预设为0; Integer j,j 的预设为 null ,因为封装类产生的是对象,对象默认值为null。
存储位置
基本类型在内存中是存储在栈中,引用类型的引用(值的地址)存储在栈中,而实际的对象(值)是存在堆中。
虽然基本类型在栈上分配内存效率高,但是在堆栈上分配内存可能有内存泄漏的问题。
基础类型与封装类型对比
基本数据类型的好处就是速度快(不涉及到对象的构造和回收),封装类的目的主要是更好的处理数据之间的转换。
面试题:int 与 Integer 不同
1、Integer 是 int 的包装类,int 则是 Java 的一种基本数据类型
2、Integer 变量必须实例化后才能使用,而 int 变量不需要
3、Integer 实际是对象的引用,当 new 一个 Integer 时,实际上是生成一个指针指向此对象;而 int 则是直接存储数据值
4、Integer 的默认值是 null ,int 的默认值是0
4. 序列化&反序列化
序列化是什么
- 对象序列化是一个用于将对象状态转换为字节流并存储到一个存储媒介或通过网络发送到任何其他程序的过程;反序列化则是把字节序列恢复为Java对象的过程,但它们仅处理Java变量而不处理方法
- 序列化以特定的方式对类实例的瞬时状态进行编码保存的一种操作.序列化作用的对象是类的实例.对实例进行序列化,就是保存实例当前在内存中的状态.包括实例的每一个属性的值和引用等.反序列化的作用便是将序列化后的编码解码成类实例的瞬时状态.申请等同的内存保存该实例.
- 序列化的作用就是为了保存 Java 的类对象的状态,并将对象转换成可存储或者可传输的状态,用于不同 JVM 之间进行类实例间的共享。
简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存 object states ,但是 Java 给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。
为神马需要对 Java 对象实现序列化
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。我们可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间(注:要想将对象传输于网络必须进行流化)因为在对对象流进行读写操作时会引发一些问题,而序列化机制正是用来解决这些问题的。
人话:若对象不进行序列化,则无法跨平台传输,安全性也无法得到保证
神马情况下需要序列化(也可以算是原因)
- 永久性保存对象,保存对象的字节序列到本地文件中。
Serializable
- 通过序列化对象在网络中传递对象。
Serializable
- 通过序列化在进程间传递对象。
Parcelable
序列化的方式:Serialzable和Parcelable
两种序列化的区别:
- Serializable 只需要对某个类以及它的属性实现 Serializable 接口即可,它的缺点是使用了反射,序列化的过程比较慢,这种机制会在序列化的时候创建许多的临时对象,容易引发频繁的 GC 。
- Parcelable 是Android 平台特有的,在使用内存的时候性能更好,但 Parcelable 不能使用在要将数据存储在磁盘的情况下,因为 Parcelable 不能很好的保证数据的持续性在外界有变化的情况。
Parcelable 在 Android 中的应用场景:
通过 Intent 传递复杂对象
Intent 支持传递的数据类型包括:
- 基本类型的数据、及其数组。
- String/CharSequence 类型的数据、及其数组。
- Parcelable/Serializable ,及其数组/列表数据。
** SharePreference存储复杂对象**
相关资源来源于网络,仅供学习交流使用,如有侵权烦请站内联系。