一、面向对象
1.1 面向对象与面向过程
是两种不同的编程范式
面向对象:先抽象出具有状态、行为的对象,然后用对象执行方法的方式解决问题,强调封装、继承、多态,更容易扩展和维护。因为修改一个对象不会影响到其他对象,适合处理复杂的系统
面向过程:将系统视为一系列的过程,通过调用这些过程来完成任务。强调的是算法和流程,如果需要修改一个过程,可能会影响到调用这个过程的其他地方
1.2 面向对象的三大特征
1.2.1 封装
为了提高代码的安全性,隐藏对象的内部细节,将对象的内部状态隐藏起来,并通过定义的公共的方法(接口)来操作对象,外部代码只知道如何使用这些方法而无需了解内部实现
1.2.2 继承
允许一个类(子类)继承另一个类(父类)的属性和方法的机制。子类可以重用父类的代码,并且可以通过添加新的方法或修改(重写)已有的方法来扩展或改进功能,提高了代码的可重用性和可扩展性
1.2.3 多态
是指相同的操作或方法可以在不同的对象上产生不同的行为,通过方法的重载和重写实现。多态允许以一致的方式处理不同类型的对象,提高了代码的灵活性。
编译时多态
是指在编译阶段,编译器就能够确定调用哪个方法,这是通过方法的重载来实现的。编译时多态主要依赖于方法名和参数列表来确定调用的方法。
public class Example {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
public static void main(String[] args) {
Example example = new Example();
int resultInt = example.add(2, 3); // 编译时确定调⽤add(int, int)
double resultDouble = example.add(2.0, 3.0); // 编译时确定调⽤add(double, double)
}
}
运行时多态
是指在程序运行时,根据实际对象的类型来确定调用的方法,这是通过方法的重写来实现的。运行时多态主要依赖于对象的实际类型,而不是引用类型。
class Animal {
void makeSound() {
System.out.println("sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Bark");
}
}
public class PolymorphismExample {
public static void main(String[] args) {
Animal myDog = new Dog(); // 运⾏时多态,myDog的实际类型是Dog
myDog.makeSound(); // 运⾏时确定调⽤Dog类的makeSound⽅法
}
}
总结
1、编译时多态:通过方法的重载在编译阶段确定调用的方法
2、运行时多态:通过方法的重写在程序运行时确定调用的方法,实现动态绑定
二、接口和抽象类
2.1 定义区别
接口是一种抽象类型,它定义了一组方法,但没有实现任何方法的具体代码。
抽象类是一个类,可以包含抽象方法和具体方法。抽象类中的抽象方法是没有实现的方法,而具体方法则包含实现代码。抽象类不能直接实例化,通常需要子类继承并实现其中的抽象方法。
2.2 继承区别
接口支持多继承,一个类可以实现多个接口
Java中不支持多继承,一个类只能继承一个抽象类
2.3 构造器区别
接口不能包含构造器,因为接口不能被实例化
抽象类可以包含构造器,用于初始化抽象类的实例。当子类实例化时,会调用父类的构造器
2.4 访问修饰符
接口中的方法默认是public abstract的。接口中的变量默认是public static final的。
抽象类中的抽象方法默认是protected的,具体方法可以是public、protected或private
2.5 实现限制
类可以同时实现多个接口,实现接口的类必须提供接口中定义的所有方法
一个类只能继承一个抽象类,继承抽象类的子类必须提供抽象类中定义的所有抽象方法的实现
三、Java访问修饰符
访问权限:public > protected > 缺省 > private
3.1 public
表示对所有类可见
3.2 protected
表示对同一个包下的类和所有子类可见
子类可以访问父类中声明为protected的成员,而不管子类与父类是否在同一个包中
3.3 缺省
表示只有同一个包中的类可以访问
3.4 private
表示对同一类内可见
四、static关键字
4.1 静态变量
使用static关键字声明的变量,也叫类变量。它们属于类而不是实例,所以所有实例共享相同的静态变量
4.2 静态方法
使用static关键字声明的方法。静态方法属于类而不属于实例,可以通过类名调用,而不需要创建类的实例
4.3 静态代码块
使用static关键字声明的代码块。它在类加载时执行,并且只执行一次
4.4 静态内部类
在类中使用static关键字声明的内部类
五、static和final
static用于修饰成员时,该成员成为类级别的,而不是实例级别的。静态成员属于类,而而不是属于类的实例。
public class MyClass {
static int staticVariable; // 静态变量
static void staticMethod() { // 静态⽅法
// ...
}
}
int value = MyClass.staticVariable;
MyClass.staticMethod();
当final用于修饰变量、方法或类时,表示它是不可变的。对于变量,一旦复制后不能再修改;对于方法,表示方法不能被子类重写;对于类,表示类不能被继承。
public class Example {
final int constantValue = 42; // 不可变的变量
final void finalMethod() { // 不可变的⽅法
// ...
}
}
final class FinalClass { // 不可变的类
// ...
}
六、final、finally、finalize的区别
6.1 final
就是不可变的意思,可以修饰变量、方法和类。修饰变量时,这个变量必须初始化,所以也称为常量
6.2 finally
是异常处理的一部分,只能用在try/catch中,并且附带一个语句块表示这段语句一定会被执行,无论是否抛出异常
6.3 finalize
是java.lang.Object中的方法,也就是每个对象都有这个方法,一个对象的finalize方法只会调用一次,调用了也不一定被回收,因为只有对象被回收的时候才会被回收,就会导致前面调用,后面回收的时候出现问题,不推荐使用
七、方法的重载和重写
7.1 重载
是指在同一个类中,可以有多个方法具有相同的名称,但它们的参数列表不同(参数的类型、个数、顺序),具有以下特点。
1、方法的返回类型可以相同也可以不同
2、方法的重载与方法的访问修饰符和返回类型无关
3、编译器根据方法的参数列表来区分不同的重载方法
7.2 重写
是指在子类中重新定义父类中已经定义的方法,方法名、参数列表和返回类型都必须相同。
1、重写(子类)的方法不能比被重写(父类)的方法有更低的访问权限
2、子类方法不能抛出比父类方法更多的异常
7.3 总结
1、重载是在同一个类中定义多个方法,方法名相同,但参数列表不同
2、重写是在子类中重新定义父类中已有的方法,方法名和参数列表必须相同
3、重载与返回类型和访问修饰符无关,而重写要求方法签名相同(即方法名、返回值类型、参数列表)
4、重载是编译时多态,重写是运行时多态
八、深拷贝和浅拷贝
8.1 浅拷贝
创建一个新对象,然后将原对象的非静态字段复制到新对象。如果字段是基本数据类型,那么就复制其值;如果字段是引用类型,复制的就是引用而不是实际对象
// 浅拷⻉
class Person implements Cloneable {
String name;
Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Address {
String city;
}
public class ShallowCopyExample {
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person();
person1.name = "John";
person1.address = new Address();
person1.address.city = "New York";
Person person2 = (Person) person1.clone();
System.out.println(person1 == person2); // false
System.out.println(person1.address == person2.address); // true,引⽤对象共享
}
}
8.2 深拷贝
创建一个新对象,复制原对象中所有引用类型的字段指向的对象,而不是共享引用。因此,新对象和原对象中的引用类型字段引用的是两组不同的对象
// 深拷⻉
import org.apache.commons.lang3.SerializationUtils;
class Person implements Serializable {
String name;
Address address;
// 通过序列化和反序列化实现深拷⻉
public Person deepCopy() {图源:javaguide
总结:
浅拷⻉复制对象,包括对象的引⽤,两者共享引⽤对象。
深拷⻉复制对象及其引⽤的对象,两者不共享引⽤对象,即使引⽤对象也会被复制。
在Java中,浅拷⻉通常通过 clone ⽅法实现,深拷⻉可以通过⼿动实现 clone ⽅法、序列化和反序列化,或
者使⽤第三⽅库来实现。
⾯向对象【笔记】
本部分为⾯向对象的⼀些笔记整理,如不需要可以跳过。
return SerializationUtils.clone(this);
}
}
class Address implements Serializable {
String city;
}
public class DeepCopyExample {
public static void main(String[] args) {
Person person1 = new Person();
person1.name = "John";
person1.address = new Address();
person1.address.city = "New York";
Person person2 = person1.deepCopy();
System.out.println(person1 == person2); // false
System.out.println(person1.address == person2.address); // false,引⽤对象不共享
}
}
8.3 总结
1、浅拷贝复制对象,包括对象的引用,两者共享引用对象
2、深拷贝复制对象及其引用的对象,两者不共享引用对象
注意区分对象的引用和引用对象!
3、Java中,浅拷贝通常通过clone方法实现,深拷贝可以通过手动实现clone方法、序列化和反序列化,或者使用第三方库来实现