
文章目录
- 4.Java基础重要知识(面试题)
- 4.1基本数据类型
- 4.1.1 八种基本数据类型
- 4.1.2 基本类型和包装类型的区别
- 4.1.3包装类型的缓存机制
- 4.1.4自动装箱和拆箱?原理?
- (1)定义
- (2)原理
 
 
- 4.2变量
- 4.2.1成员变量与局部变量的区别
- 4.2.2静态变量的作用
 
- 4.3面向对象基础
- 4.3.1面向对象三大特征
- (1)封装
- (2)继承
- (3)多态
 
- 4.3.2辨别接口和抽象类的异同
- (1)共同点
- (2)不同点
 
- 4.3.3辨别深拷贝、浅拷贝、引用拷贝
- (1)浅拷贝
- (2)深拷贝
- (3)引用拷贝
 
 
- 4.4 Object类
- 4.4.1==和equals的区别
- (1)==
- (2)equals
 
 
- 4.5 String
- 4.5.1 String、StringBuffer、StringBuilder的区别
- 4.5.2 String为什么是不可变的?
 
 
4.Java基础重要知识(面试题)
4.1基本数据类型
4.1.1 八种基本数据类型
Java 中有 8 种基本数据类型,分别为:
- 6 种数字类型: - 4 种整数型:byte、short、int、long
- 2 种浮点型:float、double
 
- 4 种整数型:
- 1 种字符类型:char
- 1 种布尔型:boolean。
这 8 种基本数据类型的默认值以及所占空间的大小如下:
| 基本类型 | 位数 | 字节 | 默认值 | 取值范围 | 
|---|---|---|---|---|
| byte | 8 | 1 | 0 | -128 ~ 127 | 
| short | 16 | 2 | 0 | -32768(-2^15) ~ 32767(2^15 - 1) | 
| int | 32 | 4 | 0 | -2147483648 ~ 2147483647 | 
| long | 64 | 8 | 0L | -9223372036854775808(-2^63) ~ 9223372036854775807(2^63 -1) | 
| float | 32 | 4 | 0f | 1.4E-45 ~ 3.4028235E38 | 
| double | 64 | 8 | 0d | 4.9E-324 ~ 1.7976931348623157E308 | 
| char | 16 | 2 | ‘u0000’ | 0 ~ 65535(2^16 - 1) | 
| boolean | 1 | false | true、false | 
4.1.2 基本类型和包装类型的区别

- 用途: - 除了定义一些常量和局部变量之外,我们在其他地方比如方法参数、对象属性中很少会使用基本类型来定义变量
- 包装类型可用于泛型,而基本类型不可以。
 
- 存储方式: - 基本数据类型的 局部变量存放在 Java 虚拟机栈中的局部变量表中,基本数据类型的成员变量(未被 static修饰 )存放在 Java 虚拟机的堆中。
- 包装类型属于对象类型,我们知道几乎所有对象实例都存在于堆中。
 
- 基本数据类型的 局部变量存放在 Java 虚拟机栈中的局部变量表中,基本数据类型的成员变量(未被 
- 占用空间: - 相比于包装类型(对象类型), 基本数据类型占用的空间往往非常小。
 
- 默认值: - 成员变量包装类型不赋值就是 null
- 基本类型有默认值且不是 null。
 
- 成员变量包装类型不赋值就是 
- 比较方式: - 对于基本数据类型来说,==比较的是值。
- 对于包装数据类型来说,==比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用equals()方法。
 
- 对于基本数据类型来说,
- 注意注意:基本数据类型存放在栈中是一个常见的误区! 基本数据类型的存储位置取决于它们的作用域和声明方式。如果它们是局部变量,那么它们会存放在栈中;如果它们是成员变量,那么它们会存放在堆中
public class Test {// 成员变量,存放在堆中int a = 10;// 被 static 修饰,也存放在堆中,但属于类,不属于对象// JDK1.7 静态变量从永久代移动了 Java 堆中static int b = 20;public void method() {// 局部变量,存放在栈中int c = 30;static int d = 40; // 编译错误,不能在方法中使用 static 修饰局部变量}
}
4.1.3包装类型的缓存机制
- Byte,- Short,- Integer,- Long这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据
- Character创建了数值在 [0,127] 范围的缓存数据
- Boolean直接返回- Trueor- False。
- 如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。
- 举例一:
Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 输出 trueFloat i11 = 333f;
Float i22 = 333f;
System.out.println(i11 == i22);// 输出 falseDouble i3 = 1.2;
Double i4 = 1.2;
System.out.println(i3 == i4);// 输出 false
- 举例二:注意新建包装类型对象
Integer i1 = 40;
Integer i2 = new Integer(40);
System.out.println(i1==i2);//Integer i1=40 这一行代码会发生装箱,也就是说这行代码等价于 Integer i1=Integer.valueOf(40) 。因此,i1 直接使用的是缓存中的对象。而Integer i2 = new Integer(40) 会直接创建新的对象
//结果为fasle
4.1.4自动装箱和拆箱?原理?
(1)定义
- 装箱:将基本类型用它们对应的引用类型包装起来;
- 拆箱:将包装类型转换为基本数据类型;
(2)原理
- 举例
Integer i = 10;  //装箱
int n = i;   //拆箱
- 字节码文件:
   L1LINENUMBER 8 L1ALOAD 0BIPUSH 10INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;PUTFIELD AutoBoxTest.i : Ljava/lang/Integer;L2LINENUMBER 9 L2ALOAD 0ALOAD 0GETFIELD AutoBoxTest.i : Ljava/lang/Integer;INVOKEVIRTUAL java/lang/Integer.intValue ()IPUTFIELD AutoBoxTest.n : IRETURN
- 装箱其实就是调用了 ==包装类的valueOf()==方法
- 拆箱其实就是调用了 xxxValue()方法。
Integer i = 10等价于Integer i = Integer.valueOf(10)
int n = i等价于int n = i.intValue();
4.2变量
4.2.1成员变量与局部变量的区别

-  语法形式: - 从语法形式上看,成员变量是属于类的,成员变量可以被 public,private,static等修饰符所修饰
- 局部变量是在代码块或方法中定义的变量或是方法的参数;,局部变量不能被访问控制修饰符及 static所修饰;
- 成员变量和局部变量都能被 final所修饰。
 
- 从语法形式上看,成员变量是属于类的,成员变量可以被 
-  存储方式:从变量在内存中的存储方式来看 - 如果成员变量是使用 static修饰的,那么这个成员变量是属于类的
- 如果没有使用 static修饰,这个成员变量是属于实例的。
- 而对象存在于堆内存 ,局部变量则存在于 栈内存。
 
- 如果成员变量是使用 
-  生存时间:从变量在内存中的生存时间上看 - 成员变量是对象的一部分,它随着对象的创建而存在
- 局部变量随着方法的调用而自动生成,随着方法的调用结束而消亡。
 
-  默认值:从变量是否有默认值来看 - 成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值(一种情况例外:被 final修饰的成员变量也必须显式地赋值)
- 而局部变量则不会自动赋值。
 
- 成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值(一种情况例外:被 
4.2.2静态变量的作用
- 静态变量也就是被 static关键字修饰的变量。它可以被类的所有实例共享,无论一个类创建了多少个对象,它们都共享同一份静态变量。
- 静态变量只会被分配一次内存,即使创建多个对象,这样可以节省内存。
- 静态变量是通过类名来访问的,例如StaticVariableExample.staticVar(如果被private关键字修饰就无法这样访问了)。
public class StaticVariableExample {// 静态变量public static int staticVar = 0;
}
通常情况下,静态变量会被 final 关键字修饰成为常量。
public class ConstantVariableExample {// 常量public static final int constantVar = 0;
}
4.3面向对象基础
4.3.1面向对象三大特征
封装、继承、多态
(1)封装
- 封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。
- 举例:我们看不到挂在墙上的空调的内部的零件信息(也就是属性),但是可以通过遥控器(方法)来控制空调。
(2)继承
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能
核心点:
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是 无法访问,只是拥有。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法
(3)多态
多态,顾名思义,表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。
多态的特点:
- 对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
- 引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
- 多态不能调用“只在子类存在但在父类不存在”的方法;
- 如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法。
4.3.2辨别接口和抽象类的异同
(1)共同点
- 都不能被实例化。
- 都可以包含抽象方法。
- 都可以有默认实现的方法(Java 8 可以用 default关键字在接口中定义默认方法)
(2)不同点
-  单继承、多实现 
-  成员变量修饰符不同、赋值不同: - 接口中的成员变量只能是 public static final 类型 的,不能被修改且必须有初始值
- 抽象类的成员变量默认 default,可在子类中被重新定义,也可被重新赋值
 
-  用途不同: - 接口主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。
- 抽象类主要用于代码复用,强调的是所属关系。
 
4.3.3辨别深拷贝、浅拷贝、引用拷贝
- 浅拷贝: - 浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点)
- 如果原对象内部的属性是 引用类型 的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
 
- 深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
(1)浅拷贝
举例说明浅拷贝,实现Cloneable接口,并重写clone()方法,直接调用父类Obejct的clone()方法
public class Address implements Cloneable{private String name;// 省略构造函数、Getter&Setter方法@Overridepublic Address clone() {try {return (Address) super.clone();} catch (CloneNotSupportedException e) {throw new AssertionError();}}
}public class Person implements Cloneable {private Address address;// 省略构造函数、Getter&Setter方法@Overridepublic Person clone() {try {Person person = (Person) super.clone();return person;} catch (CloneNotSupportedException e) {throw new AssertionError();}}
}
- 测试:
Person person1 = new Person(new Address("武汉"));
Person person1Copy = person1.clone();
// true
System.out.println(person1.getAddress() == person1Copy.getAddress());
从输出结构就可以看出,
person1的克隆对象和person1使用的仍然是同一个Address对象。
(2)深拷贝
这里我们简单对 Person 类的 clone() 方法进行修改,连带着要把 Person 对象内部的 Address 对象一起复制。
@Override
public Person clone() {try {Person person = (Person) super.clone();person.setAddress(person.getAddress().clone());return person;} catch (CloneNotSupportedException e) {throw new AssertionError();}
}
- 测试:
Person person1 = new Person(new Address("武汉"));
Person person1Copy = person1.clone();
// false
System.out.println(person1.getAddress() == person1Copy.getAddress());
从输出结构就可以看出,显然 person1的克隆对象和 person1 包含的 Address 对象已经是不同的了。
(3)引用拷贝
简单来说,引用拷贝就是两个不同的引用指向同一个对象。
以下图来描述浅拷贝、深拷贝、引用拷贝:

4.4 Object类
4.4.1==和equals的区别
==是运算符,equals是方法
(1)==
-  若比较的对象是基本数据类型,则比较数值是否相等 
-  若比较的对象是引用数据类型,则比较的是对象的内存地址是否相等 
-  不管是比较基本数据类型,还是引用数据类型的变量,其比较的都是值,只是引用类型变量存的值是对象的地址。引用类型对象变量其实是一个引用,它们的值是指向对象所在的内存地址。 
(2)equals
equals方法比较的是对象的内容是否相同
equals()方法存在于Object类中,而Object类是所有类的父类。在Object类中定义了equals方法:
public boolean equals(Object obj) {return (this == obj);
}
-  如果类未重写equals方法 调用equals时,会调用Object中的equals方法(实际使用的也是 == 操作符) 
-  如果类重写了equals方法 调用equals时,会调用该类自己的equals方法(一般是比较对象的内容是否相同)。比如: - String:比较字符串内容是否相同;
- Integer:比较对应的基本数据类型int的值是否相同。
 
4.5 String
4.5.1 String、StringBuffer、StringBuilder的区别
三者的区别可以从可变性、线程安全性、性能三个角度去分析:
-  可变性: -  String是不可变的
-  StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,不过没有使用final和private关键字修饰,最关键的是这个AbstractStringBuilder类还提供了很多修改字符串的方法比如append方法abstract class AbstractStringBuilder implements Appendable, CharSequence {char[] value;public AbstractStringBuilder append(String str) {if (str == null)return appendNull();int len = str.length();ensureCapacityInternal(count + len);str.getChars(0, len, value, count);count += len;return this;}//... }
 
-  
-  线程安全性: - String中的对象是不可变的,也就可以理解为常量,线程安全。
- AbstractStringBuilder是- StringBuilder与- StringBuffer的公共父类,定义了一些字符串的基本操作,如- expandCapacity、- append、、- insert、- indexOf等公共方法。- StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
- StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
 
 
-  性能: - 每次对 String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对
- StringBuffer每次都会对- StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。
- 相同情况下使用 StringBuilder相比使用StringBuffer仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
 
- 每次对 
- 总结: - 操作少量的数据: 适用 String
- 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
- 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer
 
- 操作少量的数据: 适用 
4.5.2 String为什么是不可变的?
- String类中使用 private 和 final 两个关键字修饰字符数组(作用:保存字符串)
- 被 final关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象
- 但是当数组存放的对象是引用类型时,这个数组保存的字符串的内容是可变的(虽然不能再指向其他对象,但是可以修改里面对象的数据)
- 真正不可变的原因: - 保存字符串的数组被 final修饰且为私有的,并且String类没有提供/暴露修改这个字符串的方法。
- String类被- final修饰导致其不能被继承,进而避免了子类破坏- String不可变。
 
- 保存字符串的数组被 
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {private final char value[];//...
}
