数据类型
💡 Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean。
简介
- 数据类型分为基本类型(primitive type)与引用类型(reference type)。
- 基本数据类型:数据直接存储在栈上。
- 引用数据类型区别:数据存储在堆上,栈上只存储引用地址。
基本数据类型
-
整数类型
整型 占用字节空间大小 取值范围 默认值 byte 1字节 -128 ~ 127 0 short 2字节 -32768 ~ 32767 0 int 4字节 -2^31 ~ (2^31) - 1 0 long 8字节 -2^63 ~ (2^63) - 1 0L -
浮点类型
浮点型 占用字节空间大小 取值范围 默认值 float 4字节 10^38 0.0F double 8字节 10^308 0.0 -
字符类型
字符型 占用字节空间大小 取值范围 默认值 char 2字节 0 ~ 65535 ‘\u0’ -
布尔类型
布尔型 占用字节空间大小 取值范围 默认值 boolean 视情况而定 true、false false 被编译为
int类型
,等于是说JVM里占用字节和int
完全一样,int
是 4字节 ,于是boolean
也是 4字节
被编译为byte数组
,每个boolean
元素占用 8位 = 1字节
引用数据类型:
接口(interface)、数组([ ])、类(class)。
⭐ 所有引用类型的默认值都为 null 。
包装类型
1. 包装类和基本数据类型的区别
- 声明方式不同,基本类型不适用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间;
- 存储方式及位置不同,基本类型是直接将变量值存储在堆栈中,而包装类型是将对象放在堆中,然后通过引用来使用;
- 初始值不同,基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null
- 使用方式不同,基本类型直接赋值直接使用就好,而包装类型在集合如Collection、Map时会使用到。
- 包装类都是继承 Number 接口实现 Compareble 接口的
2. 什么是自动拆装箱?原理是什么?
-
装箱:将基本类型用它们对应的引用类型包装起来;
-
拆箱:将包装类型转换为基本数据类型;
举例:
Integer i = 10; //装箱 int n = i; //拆箱
上面这两行代码,对应的字节码为:
L1 LINENUMBER 8 L1 ALOAD 0 BIPUSH 10 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; PUTFIELD AutoBoxTest.i : Ljava/lang/Integer; L2 LINENUMBER 9 L2 ALOAD 0 ALOAD 0 GETFIELD AutoBoxTest.i : Ljava/lang/Integer; INVOKEVIRTUAL java/lang/Integer.intValue ()I PUTFIELD AutoBoxTest.n : I RETURN
从字节码中,我们发现装箱其实就是调用了 包装类的valueOf()方法,拆箱其实就是调用了 xxxValue()方法。因此,
Integer i = 10 // 等价于 Integer i = Integer.valueOf(10) int n = i // 等价于 int n = i.intValue();
注意
:如果频繁拆装箱的话,也会严重影响系统的性能。我们应该尽量避免不必要的拆装箱操作。
3. Integer a= 127 与 Integer b = 127 相等吗
-
对于对象引用类型:== 比较的是对象的内存地址。
-
对于基本数据类型:== 比较的是值。
如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象。
超过范围 a1==b1的结果是false
Object类
1. Object类中常见的方法,为什么把wait方法和notify方法放在Object里边?
- 因为Java提供的锁是对象级的,每个对象都有对象头,用来存储。
2. Object类的方法并简要说明
- Object()默认构造方法
- clone()创建并返回此对象的一个副本
- equals(Object obj) 当前对象是否与obj对象相同
- finalize()当垃圾收集器确定该对象可以回收时,由垃圾收集器调用此方法
- getClass返回一个对象的运行时类
- hashCode()返回该对象的哈希码值
- notify()唤醒此对象监视器上等待的单个线程
- notifyAll()唤醒在此对象监视器上等待的所有线程
- toString()返回该对象的字符串表示
- wait()使当前线程等待,直到其他线程调用此对象的notify()或者notifyAll()方法
- wait(long timeout)导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。wait(long timeout, int nanos) 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
String类
1. String有哪些特性?
- 不变性:String 是只读字符串,是一个典型的 immutable 对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,可以保证数据的一致性。
- 常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。
- final:使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性。
2. String s = new String("xyz") 创建了几个字符串对象?
- 一个或两个 。如果字符串常量池已经有“xyz”,则是一个;否则,两个。
- 当字符创常量池没有 “xyz”,此时会创建如下两个对象:
- 一个是字符串字面量 "xyz" 所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,此时该实例也是在堆中,字符串常量池只放引用。
- 一个是通过 new String() 创建并初始化的,内容与"xyz"相同的实例,也是在堆中。
3.String s = "xyz" 和 String s = new String("xyz") 区别?
- 两个语句都会先去字符串常量池中检查是否已经存在 “xyz”,如果有则直接使用,如果没有则会在常量池中创建 “xyz” 对象。
- 另外,String s = new String("xyz") 还会通过 new String() 在堆里创建一个内容与 "xyz" 相同的对象实例。
4. String 和 StringBuilder 、StringBuffer 的区别?
-
String:String 的值被创建后不能修改,任何对 String 的修改都会引发新的 String 对象的生成。
-
StringBuffer:跟 String 类似,但是值可以被修改,使用 synchronized 来保证线程安全。
-
StringBuilder:StringBuffer 的非线程安全版本,没有使用 synchronized,具有更高的性能,推荐优先使用。
-
可变性
String类中使用字符数组保存字符串,private final char value[],所以string对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。
-
线程安全性
String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
-
性能
每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
-
对于三者使用的总结
如果要操作少量的数据用 = String
单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
long
1.超过 long 整型的数据应该如何表示?
- 基本数值类型都有一个表达范围,如果超过这个范围就会有 数值溢出 的风险。
- 在 Java 中,64 位 long 整型是最大的整数类型。
- BigInteger 内部使用 int[] 数组来存储任意大小的整形数据。
- 相对于常规整数类型的运算来说,BigInteger 运算的效率会相对较低。
float
1. float f=3.4; 是否正确
- 不正确 。3.4 是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成 精度丢失 。
- 因此需要强制类型转换float f =(float)3.4; 或者写成 float f=3.4F。
short
1. short s1 = 1; s1 = s1 + 1;有错吗? short s1 = 1; s1 += 1;有错吗?
- short s1 = 1; s1 = s1 + 1;由于 1 是 int 类型,因此 s1+1 运算结果也是 int型,需要强制转换类型才能赋值给 short 型。
- short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short(s1 + 1); 其中有 隐含的强制类型转换。
应用
1. == 、 equals 、hashcode
- == :对于基本类型来说是值比较,对于引用类型来说是比较的是引用
- equals :默认情况下是引用的比较,对于重写了 equals 方法,比如 String 、Integer 等 ,变成了值的比较。
- hashcode :
- 如果两个对象相同,那么它们的 hashCode 值一定要相同
- 如果两个对象的 hashCode 相同,它们并不一定相同(这里说的对象相同指的是用eqauls方法比较)。
2. 什么时候需要重写 equals 方法 和 hashcode 方法
-
当我们需要重新定义两个对象是否相等的条件时,需要进行重写。比如通常情况下,我们认为两个不同对象的某些属性值相同时,就认为这两个对象是相同的。
-
比如我们比较两个用户是否相等,只需要比较用户id是否相同就行了。
@Override public boolean equals(Object obj) { // 首先判断传进来的obj是否是调用equals方法对象的this本身,提高判断效率 if (obj == this) {return true;} // 判断传进来的obj是否是null,提高判断效率 if (obj == null) {return false;} // 判断传进来的obj是否是User对象,防止出现类型转换的异常 if (obj instanceof User) { User user = (User) obj; boolean flag = (this.id == user.id); return flag; } // 如果没有走类型判断语句说明两个比较的对象它们的类型都不一样,结果就是false了 return false; }
3. 为什么重写equals时,一定需要重写hashCode()方法?
- 程序先进行 hashcode 的比较,如果不同,那没就不必在进行 equals 的比较了,这样就大大减少了 equals 比较的次数。
- 为了保证是同一个对象,在 equals 比较相同的情况下 hashcode值必定相同。
4. & 和 && 的区别
-
&&:逻辑与运算符。
- 当运算符左右两边的表达式都为 true,才返回 true。同时具有短路性,如果第一个表达式为 false,则直接返回 false。
-
&:逻辑与运算符、按位与运算符。
- 按位与运算符:用于二进制的计算,只有对应的两个二进位均为1时,结果位才为1 ,否则为0。
- 逻辑与运算符:& 在用于逻辑与时,和 && 的区别是不具有短路性。所在通常使用逻辑与运算符都会使用 &&,而 & 更多的适用于位运算。
5. 深拷贝和浅拷贝区别是什么?
- 数据分为基本数据类型和引用数据类型。基本数据类型:数据直接存储在栈中;引用数据类型:存储在栈中的是对象的引用地址,真实的对象数据存放在堆内存里。
- 浅拷贝 :对于基础数据类型:直接复制数据值;对于引用数据类型:只是复制了对象的引用地址,新旧对象指向同一个内存地址,修改其中一个对象的值,另一个对象的值随之改变。
- 深拷贝 :对于基础数据类型:直接复制数据值;对于引用数据类型:开辟新的内存空间,在新的内存空间里复制一个一模一样的对象,新老对象不共享内存,修改其中一个对象的值,不会影响另一个对象。深拷贝相比于浅拷贝速度较慢并且花销较大
6. Java序列化和反序列化,如何实现Java序列化?或者请解释Serializable 接口的作用
- 序列化是一种用来处理对象流的机制,也就是将对象的内容转化成二进制流,可以将对象持久化或者网络传输。
- 反序列化是将二进制流还原为对象的过程。
- 实现Java序列化,通过实现Serializable即可。
7. Math.round(11.5) 等于多少?Math.round(-11.5)等于多少
- Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。四舍五入的原理 是在参数上加 0.5 然后进行下取整。