1、 文章背景
工作已有五年之久,回望过去,没有在一线城市快节奏下学习成长,只能自己不断在工作中学习进步,最近一直想写写属于自己的文章,记录学习的内容和知识点,当做一次成长。
2、 面向对象概述
摘要:Java是一种面向对象的编程语言,它的设计理念是基于对象的概念。面向对象编程(OOP)是一种编程范式,它将程序组织成对象的集合,这些对象通过相互之间的交互来完成任务。
在Java中,一切都是对象。Java中的对象是类的实例,类是对象的蓝图或模板。通过定义类,我们可以创建多个对象,并使用它们来执行特定的任务。
3、 面向对象的三大特性
面向对象编程的核心思想是封装、继承和多态。
- 封装(Encapsulation):封装是将数据和代码封装在一个单元中,使其成为一个独立的实体。类通过将数据和方法封装在一起来实现封装。这样可以隐藏实现的细节,并提供对外部的安全访问接口。
- 继承(Inheritance):继承是一种机制,它允许一个类继承另一个类的属性和方法。通过继承,子类可以重用父类的代码,并可以添加或修改自己的行为。
- 多态(Polymorphism):多态是指一个对象可以具有多种形态。在Java中,多态可以通过方法重写和方法重载来实现。多态性使得我们可以使用统一的接口来处理不同类型的对象,提高代码的灵活性和可扩展性。
-
封装:
Java封装是一种面向对象编程的概念,它指的是将数据和相关的操作封装在一个类中,通过定义访问修饰符来控制对于类的成员的访问权限。
在Java中,封装通过使用访问修饰符(public、private、protected)来实现。通常情况下,类的成员变量被声明为私有(private),这意味着只有类内部的方法可以直接访问这些变量。为了允许其他类访问这些私有变量,可以提供公共(public)的访问方法(getter和setter方法)。
封装的目的是隐藏类的内部实现细节,只暴露必要的操作接口给外部使用。这样做有助于提高代码的安全性和可维护性,同时也减少了对类内部实现的依赖,从而提供了更好的封装性。
以下是一个简单的Java封装示例:
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
在上面的示例中, name 和 age 成员变量被声明为私有,通过公共的getter和setter方法来访问和修改这些变量。这样,外部类就无法直接访问和修改 name 和 age ,只能通过提供的公共方法进行操作。
希望这个简单的示例能够帮助你理解Java中的封装概念。
-
继承:
Java继承是面向对象编程中的一个重要概念,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。通过继承,子类可以获得父类的特性,并且可以在其基础上添加自己的特性或修改已有的特性。
在Java中,使用关键字
extends来实现继承。子类通过继承父类来获取父类的属性和方法,并且可以通过重写(override)方法来改变或扩展父类的行为。以下是一个简单的Java继承示例:
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name + " is eating.");
}
}
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
public void bark() {
System.out.println(name + " is barking.");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("Max");
dog.eat(); // 继承自父类 Animal
dog.bark(); // 子类 Dog 独有的方法
}
}
在上面的示例中, Animal 类是父类, Dog 类是子类。子类 Dog 通过继承父类 Animal ,获得了父类的属性和方法。子类 Dog 还添加了自己的方法 bark() 。在 Main 类的 main 方法中,我们创建了一个 Dog 对象,并调用了继承自父类的 eat() 方法和子类独有的 bark() 方法。
继承的好处在于代码的重用和扩展性。通过继承,我们可以避免重复编写相似的代码,并且可以在子类中添加额外的功能。
注意:1、子类不能继承父类的构造方法,只能隐式或显式地调用。 2、父类中的私有属性不能被子类继承,公开的属性是可以的。 3、final类不能被继承
-
多态:
Java多态(Polymorphism)是面向对象编程中的一个重要概念,它允许使用一个父类类型的引用来引用子类对象,从而实现不同子类对象的统一处理。
多态的实现依赖于继承和方法重写。当一个子类继承自父类并重写了父类的方法时,可以通过父类类型的引用来调用子类的方法,实现不同子类对象的不同行为。
以下是一个简单的Java多态示例:
class Animal {
public void makeSound() {
System.out.println("Animal is making a sound.");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog is barking.");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat is meowing.");
}
}
public class Main {
public static void main(String[] args) {
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.makeSound(); // Output: Dog is barking.
animal2.makeSound(); // Output: Cat is meowing.
}
}
在上面的示例中, Animal 是父类, Dog 和 Cat 是子类。子类 Dog 和 Cat 都重写了父类 Animal 的 makeSound() 方法。在 Main 类的 main 方法中,我们创建了一个 Dog 对象和一个 Cat 对象,并将它们分别赋值给父类类型的引用 animal1 和 animal2 。通过这种方式,我们可以使用父类类型的引用来调用子类的方法,实现了多态性。
多态性使得代码更加灵活和可扩展,可以根据具体的对象类型来调用相应的方法,而无需显式地判断对象的类型。
4、 super()关键字
super() 是Java中的一个关键字,用于调用父类的构造方法。当子类继承父类时,可以使用 super() 来调用父类的构造方法,以便在子类中初始化从父类继承的成员变量或执行父类的一些特定操作。
super() 的使用有以下几点要注意:
super()必须作为子类构造方法的第一条语句出现,用于显式调用父类的构造方法。- 如果子类的构造方法没有显式调用
super(),则会默认调用父类的无参构造方法。 - 如果父类没有无参构造方法,而子类又没有显式调用父类的其他构造方法,则会编译错误。
super()可以传递参数,用于调用父类的有参构造方法。
以下是一个简单的示例来演示 super() 的使用:
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name + " is eating.");
}
}
class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // 调用父类Animal的构造方法
this.breed = breed;
}
public void bark() {
System.out.println(name + " is barking.");
}
public void displayBreed() {
System.out.println(name + " is a " + breed);
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("Max", "Labrador");
dog.eat(); // 继承自父类Animal
dog.bark(); // 子类Dog独有的方法
dog.displayBreed(); // 子类Dog独有的方法
}
}
在上面的示例中, Dog 类继承自 Animal 类。子类 Dog 的构造方法中使用 super(name) 来调用父类 Animal 的构造方法,以初始化从父类继承的 name 成员变量。然后,子类 Dog 可以通过自己的方法 bark() 和 displayBreed() 来实现特定于狗的行为。
5、 对象的创建
在Java中,创建对象的方式有以下几种常见的方法:
- 使用构造方法:每个类都可以定义一个或多个构造方法,用于创建对象并初始化其属性。你可以通过调用类的构造方法来创建对象。
ClassName objectName = new ClassName(); // 调用无参构造方法
例如,创建一个 Person 类的对象:
Person person = new Person(); // 调用Person类的无参构造方法
- 使用静态工厂方法:类可以提供静态方法作为工厂方法,用于创建并返回类的实例。
ClassName objectName = ClassName.factoryMethod(); // 调用静态工厂方法
例如,通过静态工厂方法创建一个 Person 类的对象:
Person person = Person.createPerson(); // 调用Person类的静态工厂方法
- 使用反射:Java的反射机制允许在运行时动态地创建对象。你可以使用
Class类的newInstance()方法来创建对象。
ClassName objectName = ClassName.class.newInstance(); // 使用反射创建对象
例如,使用反射创建一个 Person 类的对象:
Person person = Person.class.newInstance(); // 使用反射创建Person类的对象
4.使用对象克隆:Java提供了一种通过克隆现有对象来创建新对象的机制。为了使用克隆,类必须实现 Cloneable 接口并重写 clone() 方法。
类名 对象名 = (类名) 已有对象.clone();
例如,创建一个现有 Person 对象的克隆:
Person person2 = (Person) person1.clone();
需要注意的是,对象克隆创建的是浅拷贝,意味着只有对象本身被克隆,而内部引用仍然是共享的。
深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是在对象复制过程中的两种不同方式。 浅拷贝是指创建一个新对象,新对象的属性值和原始对象的属性值相同,但是它们共享相同的引用类型的对象。也就是说,新对象仍然引用原始对象中的相同对象,而不是创建它们的副本。 深拷贝是指创建一个新对象,并且新对象的属性值和原始对象的属性值相同,但是它们引用的对象也是全新的副本,而不是共享原始对象中的对象。
这些是在Java中常见的对象创建方式。选择哪种方式取决于你的需求和设计。默认情况下,本对话的语言为英语。如果需要,你可以指定其他语言。
6、 this关键字
this 关键字是Java中的一个特殊关键字,它表示当前对象的引用。 this 关键字用于引用当前对象的实例变量和方法。
以下是关于 this 关键字的几个重要点:
-
访问实例变量:当存在与实例变量同名的局部变量时,可以使用
this关键字来引用实例变量。这有助于区分局部变量和实例变量。 -
构造方法链:在构造方法中,可以使用
this关键字调用同一类中的另一个构造方法,这称为构造方法链。 -
传递当前对象:可以使用
this关键字将当前对象作为参数传递给另一个方法或构造方法。 -
返回当前对象:方法可以使用
return this;语句来返回当前对象。这允许方法链式调用,即在单行中调用同一对象的多个方法。
下面是一个示例,用于说明 this 关键字的使用方式:
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name; // 使用'this'引用实例变量
this.age = age;
}
public void printDetails() {
System.out.println("姓名:" + this.name + ",年龄:" + this.age); // 在此处使用'this'是可选的
}
public Person getOlderPerson() {
this.age++; // 增加当前对象的年龄
return this; // 返回当前对象
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person("张三", 25);
person.printDetails();
Person olderPerson = person.getOlderPerson();
olderPerson.printDetails();
}
}
在这个示例中, this 关键字用于引用 Person 类中的实例变量 name 和 age 。它有助于将它们与构造方法的参数区分开来。 this 关键字还用于从 getOlderPerson() 方法返回当前对象。
7、 对象的销毁
在Java中,对象的销毁是由Java虚拟机(JVM)的垃圾回收器(Garbage Collector)负责的。当对象不再被引用或不可达时,垃圾回收器会自动回收并释放其占用的内存空间。
对象不再被引用时,可以通过以下几种方式来触发垃圾回收器对其进行销毁:
-
对象的引用被设置为
null:当一个对象的所有引用都被设置为null,即不再指向该对象时,该对象就成为不可达对象,可以被垃圾回收器回收。 -
对象超出作用域:当一个对象超出了其作用域(例如,方法结束或代码块结束),它将不再可访问,也就成为不可达对象,可以被垃圾回收器回收。
-
显式调用
System.gc()方法:虽然调用System.gc()方法不能确保立即销毁对象,但可以建议垃圾回收器尽快执行垃圾回收操作。
需要注意的是,垃圾回收器的运行是由JVM自动管理的,具体的回收时机和方式是由JVM根据内存管理策略和算法来决定的,我们无法直接控制对象的销毁。
在大多数情况下,我们不需要手动销毁对象,垃圾回收器会自动处理不再被引用的对象的销毁。我们只需要确保及时释放对对象的引用,让垃圾回收器可以识别和回收不再使用的对象,从而释放内存空间。
8、 static静态修饰符
static 是Java中的一个关键字,用于修饰类的成员变量和方法。它可以用于静态变量、静态方法、静态代码块和静态内部类。
以下是 static 修饰符的一些特点和用法:
-
静态变量:使用
static修饰的变量称为静态变量或类变量。静态变量属于类,而不是属于类的实例。它在所有类的实例之间共享,并且可以通过类名直接访问。 -
静态方法:使用
static修饰的方法称为静态方法。静态方法属于类,而不是属于类的实例。它可以直接通过类名调用,无需创建类的实例。静态方法只能访问静态变量和调用其他静态方法。 -
静态代码块:使用
static修饰的代码块称为静态代码块。静态代码块在类加载时执行,并且只执行一次。它通常用于初始化静态变量或执行一些静态的初始化操作。 -
静态内部类:使用
static修饰的内部类称为静态内部类。静态内部类不依赖于外部类的实例,可以直接通过外部类名访问。它可以拥有静态成员和实例成员。
静态修饰符的使用可以提供一些特定的功能和行为,例如在不创建类的实例的情况下访问变量或调用方法,或者在类加载时执行一些初始化操作。
重点介绍静态变量和静态方法:
-
静态变量和实例变量
静态变量(Static Variables)和实例变量(Instance Variables)是Java中两种不同类型的变量。 静态变量是使用
static关键字修饰的变量,也称为类变量。它们属于整个类,而不是类的实例。静态变量在类加载时被初始化,并且在整个程序运行期间保持相同的值。它们可以通过类名直接访问,无需创建类的实例。实例变量是定义在类中但不使用
static关键字修饰的变量。它们属于类的每个实例,每个实例都有自己的一组实例变量。实例变量在创建类的实例时被初始化,并且每个实例都有自己的独立副本。 -
静态方法和非静态方法
静态方法(Static Methods)和非静态方法(Non-Static Methods)是Java中两种不同类型的方法。 静态方法是使用
static关键字修饰的方法。它们属于整个类,而不是类的实例。静态方法可以直接通过类名调用,无需创建类的实例。静态方法只能访问静态变量和调用其他静态方法。非静态方法是没有使用
static关键字修饰的方法,也称为实例方法。它们属于类的每个实例,每个实例都有自己的一组非静态方法。非静态方法必须通过类的实例来调用。
9、 final修饰符
在Java中, final 修饰符用于限制类、方法和变量的行为。
-
Final类:当一个类被声明为
final时,它不能被其他类继承,也就是不能被其他类扩展。 -
Final方法:当一个方法被声明为
final时,它不能被子类重写。通常在你希望阻止对某个方法的特定实现进行修改时使用。 -
Final变量:当一个变量被声明为
final时,它的值在赋值后不能被改变。它成为一个常量,只能在初始化时赋值一次。Final变量必须在声明时或构造函数中进行初始化。
10、 构造方法
构造方法(Constructor)是一种特殊的方法,用于创建和初始化对象。它具有与类名相同的名称,并且没有返回类型(甚至没有 void )。构造方法在创建类的实例时被调用。 构造方法的主要目的是初始化对象的状态,例如给实例变量赋初值或执行其他必要的初始化操作。当使用 new 关键字创建对象时,构造方法会自动调用。
构造方法有以下特点:
-
构造方法的名称必须与类名完全相同。
-
构造方法没有返回类型,甚至没有
void关键字。 -
构造方法可以有参数,用于接收创建对象时传递的初始值。
-
如果没有显式定义构造方法,Java会提供一个默认的无参构造方法。但是,如果显式定义了一个有参构造方法,那么默认的无参构造方法将不再提供。
-
构造方法可以重载,即可以定义多个具有不同参数列表的构造方法。
11、 重载
重载(Overloading)是Java中的一个特性,它允许在同一个类中定义多个同名的方法,但这些方法的参数列表必须不同。 重载的方法具有相同的名称,但具有不同的参数类型、参数个数或参数顺序。Java编译器根据调用方法时提供的参数来确定要调用的具体方法。
以下是重载方法的几个特点:
-
方法名称必须相同,但参数列表必须不同。
-
重载方法可以具有不同的返回类型,但返回类型不能作为重载方法的区分标准。
-
重载方法可以在同一个类中定义,也可以在父类和子类之间定义。
-
重载方法可以有不同的访问修饰符。
12、 重写
重写(Overriding)是Java中的一个特性,它允许子类重新定义父类中已经存在的方法。 在继承关系中,当子类继承自父类时,子类可以通过重写父类的方法来提供自己的实现。重写的方法具有相同的名称、参数列表和返回类型。
以下是重写方法的几个特点:
-
方法名称、参数列表和返回类型必须与父类中被重写的方法相同。
-
重写方法不能比父类方法更严格地限制访问权限。例如,如果父类方法是
public,那么子类重写方法也必须是public。 -
重写方法必须具有相同或更宽松的异常抛出规则。也就是说,如果父类方法抛出了某些异常,那么子类重写方法可以不抛出这些异常,或者抛出相同的异常或其子类异常。
-
重写方法可以使用
@Override注解来标识,这样可以提醒编译器检查是否正确地重写了父类方法。
当子类重写一个方法时,需要遵循以下规则: 1. 子类中的方法必须与父类中的方法具有相同的名称、参数列表和返回类型。 2. 子类中重写的方法的访问修饰符不能比父类中的方法更严格。 3. 子类中的重写方法可以抛出与父类方法相同的异常,或者不抛出异常。
- Java中使用
static、final、private修饰的方法能被子类继承,但不会重写。此外,属性也能继承,但不能重写。
13、 接口和抽象类
接口(Interface)和抽象类(Abstract Class)是Java中用于实现类之间的继承和多态性的两种机制。
接口是一种完全抽象的类,它只包含方法的签名,没有方法的实现。接口定义了一组规范,表示类应该具有的行为。类可以实现(implement)一个或多个接口,通过实现接口中定义的方法来满足接口的规范。
抽象类是一个可以包含抽象方法的类。抽象方法是没有实现的方法,只有方法签名。抽象类可以包含具体的方法实现,也可以包含抽象方法。抽象类不能被实例化,只能被继承。子类继承抽象类后,必须实现抽象类中的所有抽象方法,或者将子类也声明为抽象类。
下面是接口和抽象类的一些特点和用法:
接口:
- 使用
interface关键字定义接口。 - 接口中的方法默认是公共的抽象方法,可以省略
public abstract关键字。 - 接口中的字段默认是公共的静态常量,可以省略
public static final关键字。 - 一个类可以实现多个接口,通过
implements关键字实现接口。 - 实现接口的类必须实现接口中定义的所有方法。
抽象类:
- 使用
abstract关键字定义抽象类。 - 抽象类可以包含具体的方法实现,也可以包含抽象方法。
- 抽象方法用
abstract关键字声明,没有方法体。 - 一个类只能继承一个抽象类,通过
extends关键字继承抽象类。 - 子类必须实现抽象类中的所有抽象方法,或者将子类也声明为抽象类。
接口和抽象类都用于实现类之间的继承和多态性,但它们在设计和使用上有一些区别。接口更加灵活,可以实现多继承,而抽象类可以提供一些通用的实现。选择使用接口还是抽象类取决于具体的需求和设计。