Java基础(中):
- 面向对象编程(OOP)和面向过程编程(POP)的区别?
两者的主要区别在于解决问题的方式不同:
- 面向过程编程(POP):面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。
- 面向对象编程(OOP):面向对象会先抽象出对象,然后用对象执行方法的方式解决问题。
相比较于 POP,OOP 开发的程序一般具有下面这些优点:
- 易维护:由于良好的结构和封装性,OOP 程序通常更容易维护。
- 易复用:通过继承和多态,OOP 设计使得代码更具复用性,方便扩展功能。
- 易扩展:模块化设计使得系统扩展变得更加容易和灵活。
- 创建一个对象用什么运算符?对象实体与对象引用有何不同?
new 运算符,new 创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。
- 一个对象引用可以指向0个或1个对象
- 一个对象可以有多个引用指向它
- 对象的相等一般比较的是内存中存放的内容是否相等。
- 引用相等一般比较的是他们指向的内存地址是否相等。
String str1 = "hello";
String str2 = new String("hello");
String str3 = "hello";
// 使用 == 比较字符串的引用相等
System.out.println(str1 == str2); //false
System.out.println(str1 == str3); //true
// 使用 equals 方法比较字符串的相等
System.out.println(str1.equals(str2)); //true
System.out.println(str1.equals(str3)); //true
- == 比较的是比较的是字符串的引用是否相等,equals比较的是内容是否相等
- 如果一个类没有声明构造方法,该程序能正确执行吗?
- 构造方法是一种特殊的方法,主要作用是完成对象的初始化工作。
如果一个类没有声明构造方法,也可以执行!因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。如果我们自己添加了类的构造方法(无论是否有参),Java 就不会添加默认的无参数的构造方法了。
- 构造方法有哪些特点?是否可以被override?
- 构造方法名和类名相同
- 没有返回值
- 自动执行:生成类对象时,构造方法自动执行
- 构造方法不能被重写,可以被重载
- 面向对象三大特征:
- 封装:封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法(get、set)来操作属性。
- 继承:继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。
à 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有
à 子类可以拥有自己属性和方法,即子类可以对父类进行扩展
à 子类可以用自己的方式实现父类的方法
- 多态:表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。
- 接口和抽象类有什么共同点和区别?
共同点:
1.接口和抽象类不可以直接实例化,只能被继承或实现后才能创建实例对象
2.接口和抽象类都可以包含抽象方法。抽象方法没有方法体,必须在子类或实现类中实现。
区别:
设计目的、继承和实现(1vs1、1vs多)、成员变量(接口的成员变量只能用private static final类型,不能修改且必须有初值,而抽象类的成员变量可以有任何的修饰符)
- 深拷贝与浅拷贝?什么是引用拷贝?
- 浅拷贝:创建一个新对象,复制原对象的所有值。如果原对象的属性是基本类型,则这些值会被复制,如果是引用类型(如数组或其他对象),则仅复制这些引用,原对象和新对象共享这些引用指向的对象。
- 深拷贝:创建一个全新的对象,并且递归地复制所有引用类型的成员变量,确保新对象和原对象的所有数据完全独立,包括引用类型数据。
- 引用拷贝(地址拷贝):只复制对象的引用,不复制对象本身,两个对象引用指向同一个内存地址。
例:Person person1 = new Person(“Alice”);
Person person2 = person1;
- Object类的常见方法有哪些?
Object类是所有类的父类,主要提供了11种常见方法。
- toString():返回对象的字符串表示
- equals():比较两个对象是否相等
- hashCode():返回对象的哈希码
- clone():浅拷贝
- getClass():获取对象的运行时类
- == 和 equals()的区别?
== 对于基本类型和引用类型的作用效果是不同的:
对于基本数据类型来说,== 比较的是值。
对于引用数据类型来说,== 比较的是对象的内存地址。
equals():默认情况下,Objects的equals()比较的是对象的内存地址,但是如果被重写过,比如String类和Integer类,这时,比较的就是对象的内容
- hashCode()有什么作用?
hashCode() 的作用是获取哈希码(int 整数),也称为散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。
- 为什么要有hashCode?
- 当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashCode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashCode 值作比较,如果没有相符的 hashCode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashCode 值的对象,这时会调用 equals() 方法来检查 hashCode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
其实, hashCode() 和 equals()都是用于比较两个对象是否相等。
- 为什么不只提供hashCode()方法呢?(详细原因见JavaGuide)
- 为什么重写equals()时必须重写hashCode()方法?
- 因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。
如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。
- String、StringBuffer、StringBuilder的区别?
- 可变性
- 线程安全性:AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
- 性能:每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
- String为什么是不可变的?
1. 保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。
2. String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。
16. 字符串拼接用”+”还是StringBuilder?
- 字符串对象通过“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。
- 不过,在循环内使用“+”进行字符串的拼接的话,存在比较明显的缺陷:编译器不会创建单个 StringBuilder 以复用,会导致创建过多的 StringBuilder 对象。
- String的equals()和Object的equals()有何区别?
String 中的 equals 方法是被重写过的,比较的是 String 字符串的值是否相等。 Object 的 equals 方法是比较的对象的内存地址。
- 字符串常量池的作用了解吗?
字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。
例:
// 在堆中创建字符串对象”ab“
// 将字符串对象”ab“的引用保存在字符串常量池中
String aa = "ab";
// 直接返回字符串常量池中字符串对象”ab“的引用
String bb = "ab";
System.out.println(aa==bb);// true