面向对象
类和对象
什么是类,什么是对象
类(抽象):类是具有相同的状态和相同的行为的一组对象的集合。用属性表示对象的状态,用方法表示对象的行为。类是对象的模版,对象是类的具体实例。
对象(具体):万物皆是对象。
类和对象的关系
一个类给出它的全部对象的一个统一的定义,而它的每个对象则是符合这种定义的一个实体(也称为实例),因此类和对象的关系是抽象和具体的关系。
如何创建对象
可以使用关键字 new
来创建一个对象。创建对象的过程包括两个步骤:声明一个对象引用和实例化对象:
-
声明对象引用:你需要声明一个对象引用变量来保存对象的引用。对象引用变量的类型应该与你要创建的对象的类型相匹配。
-
实例化对象:使用
new
关键字和构造函数来实例化对象。构造函数用来初始化对象的属性(数据成员)和执行一些其他的初始化操作。
类里面有什么?
成员
Java中的成员变量分为两种:类的成员变量和对象的成员变量(又称为实例变量)。 类的成员变量定义在类中,而对象的成员变量定义在对象中。
字段
在Java中,字段(Field)是类或接口中定义的变量,用于存储对象的状态。
一个类可以有多个字段,每个字段都有一个名称和一个类型。
Java中的字段可以分为两种:实例字段和静态字段。
实例字段是定义在类中且在方法体之外的变量,每个对象都有自己的实例字段副本,可以通过对象来访问。
静态字段也称为类变量,是定义在类中且在方法体之外的变量,它们属于整个类而不是单个对象,可以通过类名来访问。
方法
方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集,可以被重复调用。
方法包含一个方法头和一个方法体。下面是一个方法的所有部分:
- 修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
- 返回值类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作不需要返回值,这时返回类型为void。
- 方法名:方法名是一个标识符,用于标识该方法。方法名应该以小写字母开头,后面可以跟大写字母、数字和下划线。
- 参数列表:参数列表包括参数声明和参数类型。参数声明指定了每个参数的名称和数据类型。参数类型是参数接受的数据类型。
- 方法体:方法是一组语句,它们组成了一个代码块。方法体中的语句必须以分号结尾。
成员类/接口【后边了解】
成员类和接口都是类的一种。
1.成员类(Member Class)
成员类是定义在一个类内部的类,它可以访问外部类的成员变量和方法。成员类可以继承外部类的成员变量和方法,也可以添加自己的成员变量和方法。成员类通常用于实现面向对象编程中的封装、继承和多态等特性。
2.接口(Interface)
接口是一种抽象类型,它定义了一组方法的规范,但没有具体的实现。一个类可以实现一个或多个接口,从而获得接口中定义的方法的规范。实现接口的类必须实现接口中定义的所有方法。接口通常用于实现面向对象编程中的多态特性。
静态初始化器
Java静态初始化器是一种特殊的初始化器,用于在类加载时执行一次性的初始化操作。它通常用于初始化静态变量或执行其他需要在类加载时进行的操作。
Java静态初始化器的语法如下:
static {
// 初始化代码
}
其中,static
关键字表示该代码块仅在类加载时执行一次,并且只执行一次。可以在静态初始化器中定义静态变量、执行其他静态初始化操作或执行其他需要在类加载时进行的操作。
注意
静态初始化器中的代码会在类加载时按照它们在代码中出现的顺序依次执行。如果多个静态初始化器存在,则它们的执行顺序与它们在代码中出现的顺序相同。
实例初始化器
Java实例初始化器是一种特殊的初始化器,用于在创建对象时执行一次性的初始化操作。它通常用于初始化实例变量或执行其他需要在对象创建时进行的操作。
Java实例初始化器的语法如下:
public class MyClass {
private int myInt;
public MyClass() {
myInt = 10;
}
}
其中,MyClass
类中的构造方法是一个实例初始化器,它在创建MyClass
对象时被调用。在构造方法中,可以定义实例变量并对其进行初始化。
注意
实例初始化器只会在对象创建时执行一次,并且只执行一次。如果多个实例初始化器存在,则它们的执行顺序与它们在代码中出现的顺序相同。
构造方法(函数)
Java构造方法是一种特殊的方法,用于创建对象并初始化对象的成员变量。它通常用于实现面向对象编程中的封装特性。
Java构造方法的语法如下:
public class MyClass {
private int myInt;
public MyClass() {
myInt = 10;
}
public MyClass(int i) {
myInt = i;
}
}
其中,MyClass类中的构造方法是一个实例初始化器,它在创建MyClass对象时被调用。在构造方法中,可以定义实例变量并对其进行初始化。
注意
Java构造方法没有返回值类型,因此不能使用void作为返回值类型。另外,Java构造方法可以重载,即在同一个类中定义多个构造方法,但它们的参数列表必须不同。
除了无参构造方法外,还可以定义有参构造方法。有参构造方法需要与类中定义的相同数量和类型的参数匹配,以便在创建对象时传递参数。
类的成员
static成员
Java
中的static
成员变量是类的所有对象共享的成员变量。它们被所有对象共享,而不是为每个对象单独分配一个副本。因此,当一个对象修改了static
成员变量的值时,其他对象也会看到这个变化。
在Java
中,static
成员变量必须在类加载时进行初始化,并且只能被初始化一次。可以在声明static
成员变量时直接进行初始化,也可以在静态代码块中进行初始化。
实例成员
Java实例成员是定义在类中且在方法体之外的变量,每个对象都有自己的实例成员副本,可以通过对象来访问。
Java实例成员包括以下几种类型:
- 实例变量:也称为对象变量或成员变量,用于存储对象的状态。它们被所有对象共享,而不是为每个对象单独分配一个副本。
- 实例方法:也称为对象方法或成员方法,用于操作对象的状态。它们必须与类中定义的相同数量和类型的参数匹配,以便在调用时传递参数。
- 构造方法:也称为对象构造方法,用于创建对象并初始化对象的成员变量。它们通常用于实现面向对象编程中的封装、继承和多态等特性。
static 作用
Java中的static关键字可以用于修饰成员变量、方法、代码块和内部类,具有不同的作用。
-
修饰成员变量:当一个成员变量被声明为static时,它就变成了静态成员变量。静态成员变量是类的所有对象共享的成员变量,而不是为每个对象单独分配一个副本。静态成员变量必须在类加载时进行初始化,并且只能被初始化一次。
-
修饰方法:当一个方法被声明为static时,它就变成了静态方法。静态方法不能访问非静态成员变量和非静态成员方法,因为它们在调用时不依赖于类的实例。
-
修饰代码块:当一个代码块被声明为static时,它就变成了静态代码块。静态代码块在类加载时执行一次,且只执行一次。通常用于初始化静态成员变量或执行其他需要在类加载时进行的操作。
-
修饰内部类:当一个内部类被声明为static时,它就变成了静态内部类。静态内部类与普通内部类类似,但它们不能访问外部类的非静态成员变量和非静态成员方法。静态内部类通常用于实现与类相关的功能,而不是与特定实例相关的功能。
实例化的过程
注意:类只有在第一次使用时进行加载静态初始化。
静态变量—>静态代码块—>变量—>代码块—>构造
在继承中(有父才有子):静态变量(父类)—>静态代码块(父类)—>静态变量(子类)—>静态代码块(子类)—>变量(父 类)—>代码块(父类)—>构造(父类)—>变量(子类)—>代码块(子类)—>构造(子类)
方法重载
一个类、同名、不同参(数量、类型、顺序不同)
在一个类中定义多个同名的方法,但要求这些方法具有不同的参数类型或参数个数或参数顺序。
访问修饰符
public
公共的,被修饰的成员变量和成员方法可以在所有的类中访问。
protected
受保护的,被修饰的成员变量和成员方法可以在声明它的类中访问,在该类的子类中访问,也可以在与该类同一个包中访问,但不能在位于其他包的非子类中访问。
(不写) package-access / package-private
缺省指不使用修饰访问符。可以在声明它的类中访问,也可以在与该类位于同一个包中的类访问,但不能在位于其它包的类中访问。
private
只能在声明它们的类中访问,而不能在其他类中(包括子类)中访问。
修饰类的访问修饰符
只有两个,分别是public和package-private
同一包中 | 不同包中 | |
---|---|---|
public | 可以使用 | 可以使用 |
package-private | 可以使用 | 不可以使用 |
修饰成员、构造的访问修饰符
位置 | public | protected | 包访问修饰符 | private |
---|---|---|---|---|
同类访问 | √ | √ | √ | √ |
同包其他类访问 | √ | √ | √ | × |
同包子类访问 | √ | √ | √ | × |
不同包子类访问 | √ | √ | × | × |
不同包非子类访问 | √ | × | × | × |
面向对象特征
封装
将数据和操作数据的方法包装在一起,形成一个类。外部只能通过类的接口来访问数据,不能直接访问数据,保证了数据的安全性。
继承
子类可以继承父类的属性和方法,并且可以在其基础上进行扩展和重写。
多态
同一个方法名、参数列表不同的方法可以有不同的实现,提高了程序的灵活性和可扩展性。
封装
什么是封装
封装是指将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的访问和操作。封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制。适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性 。
在Java中,封装可以通过以下几个关键字实现:private、protected、public。
getter、setter
getter:直接返回属性的值(获取);
setter:将传入的参数赋值给属性。
如何实现
- 将类的属性设置为private,即只能在类内部访问,外部无法直接访问。
- 提供public的getter和setter方法来获取和设置属性的值,这样可以控制属性的访问权限,同时可以在getter和setter方法中添加逻辑处理代码。
例如,下面的代码演示了如何通过封装实现对一个Person类的属性的保护:
java复制代码public class Person {
private String name; // 私有属性
private int age; // 私有属性
// 公共的getter和setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
// 在setter方法中添加逻辑处理代码
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
this.age = age;
}
}
继承
什么是继承
继承就是通过从现有类继承其属性和方法,来实现自身内容,这种现象或行为就称为继承。(一个类里面有4种东西:属性(包括类属性和实例属性)、方法(包括类方法和实例方法)、构造器/构造方法初始化块(包括类的初始化块和实例的初始化块))。
继承了什么
- 子类可以继承父类的非私有属性和方法(只有不是private修饰的都可以继承);
- 子类可以添加自己的属性和方法;
- 子类的构造方法可以调用父类的构造方法(构造方法不能继承);
- Object类是所有类的根类;
- java中的类只支持单一继承,每个类只能有一个父类(直接父类);
- 子类可以成为其他类的父类,从而建立多级继承关系。
类单继承
Java中类只能单继承,即一个类只能继承自一个父类。这是Java面向对象编程的一条基本规则。
但是,如果一个类同时继承了多个父类,那么它必须使用super关键字来指定要访问哪个父类的属性或方法。
重写(三同,一大)
如果从父类中继承的方法不能满足子类的需求,可以在子类中对父类同名方法进行重写(覆盖),以符合子类的需求。
final
在Java中,final
关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。当用final
修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final
进行修饰。final
类中的成员变量可以根据需要设为final
,但是要注意final
类中的所有成员方法都会被隐式地指定为final
方法。
当用final
修饰一个方法时,表明这个方法不能被重写。如果子类中有相同的方法,那么子类的方法会覆盖父类的方法。
当用final
修饰一个变量时,表明这个变量的值只能被赋值一次,即常量。一旦赋值后,不可再修改其值。而且必须在初始化对象的时候赋值。
字段不能重新赋值
在Java中,如果一个字段被声明为final,那么它的值就不能被重新赋值。也就是说,一旦给这个字段赋了值,就不能再改变它的值了。
虽然字段不能重新赋值,但是可以修改其值。
方法不能重写
在Java中,如果一个方法被声明为final,那么它就不能被子类重写(override)。也就是说,如果一个方法被声明为final,那么它的实现只能存在于父类中,而不能在子类中被覆盖。
类不能被继承
在Java中,如果一个类被声明为final,那么它就不能被继承。也就是说,如果一个类被声明为final,那么它的子类只能扩展这个类,而不能继承它。
遮蔽
遮蔽(Shadowing)是指子类中定义了与父类中同名的成员变量或方法,导致子类中的成员变量或方法会隐藏父类中的成员变量或方法。当使用子类对象访问该成员时,只能访问到子类中的成员,而无法访问到父类中的成员。
- 遮蔽只对实例变量有效,与静态变量无关。
- 子类中声明的实例变量会遮蔽父类中同名的实例变量,这意味着在子类中通过实例访问该变量时,访问的是子类的实例变量。
- 在子类中可以使用
super
关键字来显式地访问父类的实例变量。
隐藏
隐藏(Hiding)是指子类定义了一个与父类同名但签名不同的方法,导致子类的方法会隐藏父类的方法。当使用子类对象调用该方法时,只能调用到子类中的方法,而无法调用到父类中的方法。
- 隐藏只对静态变量有效,与实例变量无关。
- 子类中声明的静态变量会隐藏父类中同名的静态变量,这意味着通过类名访问该变量时,访问的是子类的静态变量。
- 在子类中可以使用
super
关键字来显式地访问父类的静态变量。
字段
在Java中,字段是类的一个成员,主要用来存储对象的状态(如同某些编程语言中的变量),所以有时也可称为成员字段或成员变量 。字段可以分为实例字段和类字段两种类型。实例字段是指每个对象都有自己的一份拷贝,而类字段则是所有对象共享一份拷贝 。
static 方法
在Java中,static方法是属于类的方法而不是属于对象的方法。因此,它们不能访问非静态成员变量或调用非静态方法,只能访问静态成员变量或调用其他静态方法 。
使用static关键字修饰的方法称为静态方法,也称为类方法。静态方法可以直接通过类名来调用,而不需要创建类的实例对象。
注意:
静态方法不能访问非静态成员变量或调用非静态方法,因为它们没有所属的对象实例。
this
在Java中,this关键字代表当前对象的引用。它可以用于访问实例变量和调用实例方法。当使用this关键字访问实例变量时,可以省略this,但在某些情况下,为了明确表示是访问当前对象的变量,可以使用this关键字。例如,在一个方法中,访问实例变量name时,可以使用this.name表示当前对象的name变量。此外,this关键字还可以在实例方法中调用其他实例方法,并表示当前对象。在静态方法中,无法使用this关键字,因为静态方法没有当前对象的引用。
super
super关键字在Java中用于调用父类的构造方法,访问父类的属性和方法,以及表示当前对象的父类型特征。
- 用父类的构造方法:在子类的构造方法中使用super关键字可以调用父类的构造方法,并完成对从父类继承的属性的初始化。这通常发生在子类需要在构造自己的特定属性之前,先完成父类属性的初始化的情况下。
- 调用父类的成员属性和方法:在子类中,如果出现了与父类继承的属性或方法同名的情况,可以使用super关键字来访问父类的属性和方法。这样可以区分子类和父类的同名属性或方法。
- 表示当前对象的父类型特征:super关键字代表了当前对象所继承的父类的特征。可以将其理解为子类对象中父类部分的属性和方法。这种情况下,super关键字通常用于在子类中访问父类的特征。
注意:super关键字只能在构造方法和实例方法(非静态方法)中使用,并且必须遵循一定的语法规则。例如,super关键字只能出现在构造方法的第一行,并且super关键字可以省略不写的情况下,当访问子类属性或方法与父类属性或方法同名时,必须使用super关键字来显式指定访问父类的特征。
this和super的区别
this和super不能同时出现,它们都出现在构造方法的第一行。关键字this用于引用当前对象,可以用于隐式参数的引用,以及调用该类的其他构造器。当使用this关键字时,它表示当前对象的引用,可以用于区分成员变量和局部变量的命名冲突,或者在一个构造器中调用其他构造器。关键字super用于调用超类(父类)的方法和构造器。它可以用于调用超类的方法,从而与子类的方法进行区分,也可以用于调用超类的构造器。在子类中重写了父类的方法时,如果需要调用父类的方法可以使用super关键字。
实例化过程
注意:类只有在第一次使用时进行加载静态初始化。
在继承中(有父才有子):静态变量(父类)—>静态代码块(父类)—>静态变量(子类)—>静态代码块(子类)—>变量(父 类)—>代码块(父类)—>构造(父类)—>变量(子类)—>代码块(子类)—>构造(子类)
Object
Object是Java中所有类的父类,也就是说Java的所有类都继承了Object,子类可以使用Object的所有方法。Object类位于java.lang包中,编译时会自动导入,我们创建一个类时,如果没有明确继承一个父类,那么它就会自动继承Object,成为Object的子类。
Object类的地位
Object类在Java中具有特殊的地位和重要的作用,它作为所有类的根类,提供了通用的方法、多态性基础和线程同步支持等功能,为Java编程提供了基础的功能和特性。
Object类的方法
Object类是Java中所有类的父类,包括基础类型都继承自Object类。因此,Object类中的方法是所有类都可以使用的,这些方法包括:
- equals(Object obj)方法:用于比较两个对象是否相等,即判断两个对象的内容是否相同。
- hashCode()方法:用于获取对象的散列值。
- toString()方法:返回对象的字符串表示形式。
- getClass()方法:返回对象的运行时class对象,class对象就是描述对象所属类的对象。
多态
什么是多态
多态是指同一个行为具有多个不同表现形式或形态的能力。多态是对象多种表现形式的体现。
在Java中,多态性是面向对象的三大特征之一,它允许同一个方法调用,由于对象不同可能会有不同的行为。
如何实现多态
Java中多态的实现有两种方式:编译时多态和运行时多态。
编译时多态是指在编译时就已经确定了对象的执行行为,而不是在运行时确定。这种情况下,我们使用接口来实现编译时多态。
运行时多态是指在运行时才确定对象的执行行为,这种情况下,我们使用继承或接口来实现运行时多态。
向上转型
子类向父类转换称为向上转型。
父类类型 引用变量名 = new 子类类型();
调用static方法时,调用的是父类方法。
提示:实现多态的3个条件
子类继承父类,并且子类重写父类的方法。
父类引用指向子类对象(父类引用可以指向任意一个子类的对象)。
父类引用调用方法时,实际上调用的是子类的方法(父类指向哪个子类就调用的哪个子类的方法),不同的子类有不同的实现方法,体现出同一个方法在不同子类中的不同形态的表现。
调用时应当注意:
(1)父类只能调用子类从父类继承的方法或重写的方法。
(2)父类不能调用子类新增的方法。
向下转型
向下转型是指将父类对象转换为子类类型的操作。在向下转型后,父类特有的属性和方法会被丢弃,只有子类特有的属性和方法才能被访问。
子类类型 引用变量名 = (子类类型) 父类类型的引用变量;
instanceof (JDK16的新写法)
instanceof是Java的一个二元操作符,用于测试对象是否是指定类型(类或子类或接口)的实例。它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean的数据类型。
向下转型前,为了确保可以强制类型转换我们先使用instanceof进行判断。(instanceof在有继承关系的类之间使用)
父类变量引用子类对象
调用实例时,看等号右边(对象是谁)
调static时,看等号左边(类是谁)
里式替换
子类对象应该能够替换其父类对象,而不会影响程序的正确性
父类能出现的地方,子类都可以出现
对象数组的使用
在Java中,对象数组是一种存储多个相同类型对象的集合。每个对象在数组中都有一个索引位置,可以通过索引来访问和操作这些对象。
创建对象数组的语法格式如下:
ClassName[] objectArray = new ClassName[arraySize];
注意:
对象数组的长度是固定的,不能改变。如果需要添加或删除元素,需要创建一个新的数组,并将原数组中的元素复制到新数组中。
使用对象作为参数/返回值
在Java中,可以将对象作为方法的参数或返回值。当一个对象被传递给方法时,它被称为方法的参数;当一个对象被从方法中返回时,它被称为方法的返回值。
使用对象作为参数:
public void printStudentInfo(Student student) {
System.out.println("Name: " + student.getName());
System.out.println("Age: " + student.getAge());
}
在上面的例子中,我们将一个Student对象作为参数传递给了printStudentInfo方法。在方法内部,我们可以使用student对象的属性和方法来访问学生的信息。
使用对象作为返回值:
public Student getStudentById(int id) {
// 根据id查询学生信息
Student student = new Student();
student.setId(id);
student.setName("Tom");
student.setAge(18);
return student;
}
在上面的例子中,我们定义了一个getStudentById方法,该方法接受一个整数类型的id参数,并返回一个Student对象。在方法内部,我们创建一个新的Student对象,设置其属性,并将其返回。
注意:
当将对象作为参数传递时,实际上是传递对象的引用。这意味着在方法内部对参数进行的任何修改都会影响到原始对象。因此,如果需要在方法内部修改对象的属性,应该使用对象的副本而不是引用。可以使用Object类的clone()方法来创建一个对象的副本。