面向对象

面向对象
Cyrex类 class
对具有相同属性和功能的一类事物的抽象描述,是创建对象的模板,例如可以定义一个“Person”类来描述人类的共同特征和行为,如姓名,身高等属性,吃饭,睡觉等行为。
类的组成
成员变量:用于描述类的属性,代表对象的状态。如“Person”类中的“name”和“height”
成员方法:用于描述类的行为,定义对象可以执行的操作。如“Person”类中的“eat”和“sleep”
构造方法:一种特殊的方法,用于在创建对象时初始化对象的属性,构造方法的名称与类名相同,没有返回值
内部类 Inner Class
顾名思义,是定义在另一个类(即外部类Outer Class)内部的类,内部类又称为嵌套类(Nested Class),外部类又称为封闭类(Enclosing Class)
大致分为四种内部类:
成员内部类 Member Inner Class
定义在外部类的成员位置上,与外部类的成员变量等同级(本质是外部类的实例成员),能访问外部类的所有成员(包括私有的),也能被外部类访问其所有成员(也包括私有的),可以添加任何访问修饰符,在其他类创建成员内部类的实例时,需要保证成员内部类在这个其他类中可见
1
2
3
4
5
6
7class OuterClass {
private String outerClassField = "outer class's field";
class MemberInnerClass { // 成员内部类
private String memberInnerClassField = "member inner class's field";
}
}静态嵌套类 static Nested Class
具体解析放在后文的static关键字内
局部内部类 Local Inner Class
局部内部类定义在外部类的方法,构造方法或代码块内部,地位类似于方法的局部变量,其中不能包含静态成员,它的作用域仅限于定义它的块中,可以访问外部类的所有成员,也可以被外部成员访问其所有成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class OuterClass {
public OuterClass() {
class LocalInnerClassA { // 在构造方法中定义局部内部类
}
}
{
class LocalInnerClassB { // 在实例代码块中定义局部内部类
}
}
static {
class LocalInnerClassC { // 在静态代码块中定义局部内部类
}
}
public void fun() {
int localVar;
class LocalInnerClassD { // 在成员函数中定义局部内部类
}
}
}匿名内部类 Anonymous Inner Class
匿名内部类是一种特殊的内部类,它没有显式的类名。匿名内部类通常用于创建一个类的实例,而这个类只需要使用一次。匿名内部类可以继承一个类或实现一个接口,并且可以在定义类的内容的同时创建对象
类什么时候会被加载?
- 运行类中的main方法
- 访问类中的静态成员变量/方法
- 创建类的实例对象
对象
对象是类的实例,以类为模板,通过使用new关键字在内存中创建出一个实际存在的实例
1 | public class Person{ |
重载 overload(外壳必须改变,核心可以改变)
含义:在一个类里面,方法名字相同而参数列表不同,返回类型可同可不同,这些方法被称为重载方法
(通俗理解的话就是下面这玩意)
目的:为了提供功能相似但输入参数不同的方法,以便于调用时根据参数选择合适的方法
使用场景:常用于创建一个操作不同类型的参数的多个版本
规则:
- 参数列表必须改变(个数/类型/顺序)
- 返回类型可以改变
- 实现过程可以改变
- 异常声明可以改变
- 访问限制可以改变
1 | public class overload{ |
重写 override(外壳不能改变,内核可以改变):
含义:当子类重写父类的方法时,称为override,此方法可以通过@Override注解来标记(非强制)。子类的方法必须与父类被重写的方法具有相同的名称,返回类型和参数列表
目的:通常是为了在子类中提供特定于子类的方法,或者为了子类拓展或修改父类的方法
使用场景:继承和多态
规则:
- 参数列表不能改变
- 返回类型可以为被重写方法的派生类
- 实现过程可以改变
- 异常声明不能比父类更加宽泛
- 访问限制不能比父类更加严格
- final修饰的方法不可被重写
- static修饰的方法不可被重写,但可以重新声明
- 构造方法不可重写
- 父类无法被子类访问的方法不可重写
1 | public class override { |
this关键字
什么是this?
this关键字是一个特殊引用变量,他持有对当前对象的引用。在类的非静态方法和构造函数中可以使用this关键字访问当前对象的成员变量和方法,即“当前自身对象“的代称。
基本用法:
1.访问当前对象的成员变量:
当局部变量与成员变量同名时,this关键字可用于区分它们
1 | public class Student { |
1 | public class Student{ |
运行结果:
我们发现返回出来的结果为null,说明在对student初始化时并未将”小明“赋值给name,这是因为没用使用this关键字时,java认为左侧的name也是局部变量而非成员变量,即将局部变量的值赋值给了局部变量,而成员变量因为没有受到任何改动而维持默认值null
2.调用当前对象的方法:
1 | public class Student { |
3.调用当前类的构造函数:
在类的构造函数中,可以使用this关键字来调用另一个构造函数以简化代码编写,这种方法也被称为”构造函数重载“。使用此方法需要注意:构造函数重载的语句必须是构造函数中的第一条语句。
1 | public class Student { |
4.返回当前对象本身:
this关键字可以作为方法的返回值,返回当前对象本身。这种用法在实现方法链的时候非常有用。
1 | public class Student { |
在上述例子中,setName和setAge方法返回this,使得调用可以链接在一起,实现了方法链
Final 关键字
final代表最终的,不可改变的,一般有四种使用方法
修饰类(被final修饰的类可以叫太监类,因为它不能具有子类)
一个类如果被final修饰的话,这个类便不能再拥有子类,并且其中的所有成员方法都无法被覆盖重写
1
2
3
4
5
6final class FinalClass{
public void display(){
System.out.println("this is a final class");
}
}
//尝试继承FinalClass会导致编译报错修饰方法
当final修饰一个方法后,该方法便不能被覆写
修饰局部变量
对于基本数据类型来说,final意味着变量中的数据内容不可改变;
对于引用数据类型来说,final意味着变量中的地址值不可改变,但数据内容是可以改变的(地址所指向的对象可以改变)。
修饰成员变量
对于成员变量来说,经过final修饰后同样不可变
由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再提供默认值
对于final修饰后的成员变量,要么直接复制,要么从构造方法赋值
必须保证类中所有重载的构造方法都会对final的成员变量进行复制
如果选择在构造方法中复制的话,为了确保线程安全,通常会把setname()方法取消掉
static关键字
static静态,用于修饰类的成员(包括方法与变量),表示这些成员属于类本身而非某个具体实例(非静态则属于某个具体实例,依赖于实例对象进行访问/使用)
静态变量,也被称为类变量,在内存中只有一份副本,被该类中的所有实例对象所共享,一般将所有对象都相同的属性定义为静态变量
1 | public class MyClass{ |
静态方法,也被称为类方法,可以直接通过类名调用,而无需构建实例来使用,静态方法只能使用静态变量与其他静态方法,无法访问非静态成员,而非静态方法可以访问静态变量及使用静态方法
1 | public class MathPlus{ |
静态内部类
它与外部类的实例无关,可以直接创建实例。静态内部类不能直接访问外部类的非静态成员,但可以访问外部类的静态成员
1 | public class OuterClass{ |
静态代码块,在类初始化的时候执行且仅执行一次(执行优先级高于非静态的初始化块),常用于对静态变量进行初始化
1 | static{ |
static关键字修饰的属性特点
- 随着类的加载而加载
- 优先于对象存在
- 静态成员被所有对象共享
- 可以直接使用类名访问
abstract关键字
abstract抽象的,主要用于修饰类和方法
修饰抽象类
当abstract用于修饰类时,这个类就被称为抽象类,抽象类不能被实例化,也就是不能使用new关键字来创建该类的实例对象。抽象类一般作为一个通用的模板,使得子类可以继承抽象类并实现其中的抽象方法
修饰抽象方法
抽象方法只有方法声明,没有方法体。抽象方法必须在抽象类中定义,子类继承抽象类时,必须实现父类中所有抽象方法,否则子类在声明时也必须声明为抽象类
访问权限修饰符
public 公共的
public是权限最大的修饰符,可以修饰类,成员变量,成员方法,构造方法。被其修饰后,可以在任何一个类,不管同不同包,任意使用
protected 受保护的
protected不能修饰外部类。被protected修饰后,只能被同包下的其他类访问。如果不同包下的类要访问被protected修饰的成员,这个类必须是其子类。
default 默认的
如果一个类、变量、方法或构造函数没有使用任何访问权限修饰符,那么它具有默认的访问权限(default),也称为包访问权限。只有同一个包内的类可以访问这些成员。
private 私有的
private不能修饰外部类。被private修饰后,只能在其修饰的本类中访问,在其它类中无法被调用,但可以通过set和get方法向外界提供访问方式
作用范围
同一个类 | 同一个包 | 不同包的子类 | 不同包非子类 | |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
面向对象的三大特征
封装 Encapsulation
指将数据(属性)和操作数据的方法(行为)封装在一起,形成一个不可分割的整体,通过访问权限修饰符来实现,其目的是保护对象的内部状态不被外部直接访问,只能通过公共接口(方法)来访问和修改
封装的实现
- 属性私有化
- 方法公开化
- 在类中定义构造方法
继承 Inheritance
指一个类可以继承另一个类的属性和方法,通过extends来实现
继承的实现
声明继承关系
1
2
3public class Dog extends Animal{
STATEMENT;
}继承属性和方法
子类将继承父类的所有变量,方法以及嵌套类,但不会继承父类的构造函数(但在创建子类对象时会默认调用父类无参构造方法),同时,父类的私有(private)成员也不能被子类直接访问
重写父类方法
子类可以重写(override)父类方法
1
2
3
4
5
6
7
8
9
10
11
12public class Animal{
public void makeSound(){
System.out.println("Animal make a sound");
}
}
public class Dog extends Animal{
public void makesound(){
System.out.println("bark!")
}
}添加新属性和方法
子类可以添加新的属性和方法以拓展父类方法
super关键字
super关键字可以用来引用当前对象的父类(超类)的成员变量或方法,主要用途如下:
访问父类的成员变量:
当子类与父类中有同名的成员变量时,可以使用super关键字访问父类的成员变量
1
2
3
4
5
6
7
8
9
10
11
12
13class Parent {
int x = 10;
}
class Child extends Parent {
int x = 20;
void display() {
System.out.println(x); // 输出子类的x,即20
System.out.println(super.x); // 输出父类的x,即10
}
}调用父类的构造方法:
在子类的构造方法中,可以使用super来调用父类的构造方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Parent {
int x;
Parent(int x) {
this.x = x;
}
}
class Child extends Parent {
int y;
Child(int x, int y) {
super(x); // 调用父类的构造方法
this.y = y;
}
}访问父类的方法
当子类和父类中有同名的方法时,可以使用super来调用父类的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class Parent {
public void display() {
System.out.println("Display method in Parent");
}
}
class Child extends Parent {
public void display() {
System.out.println("Display method in Child");
}
public void show() {
super.display(); // 调用父类的display方法
this.display(); // 调用当前类的display方法
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.show();
}
}在子类构造方法中调用父类的构造方法:
当子类的构造方法中没有使用super来调用父类的构造方法时,会默认调用父类的无参构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Parent {
int x;
Parent(int x) {
this.x = x;
}
}
class Child extends Parent {
int y;
Child(int y) {
this.y = y;
// super(); // 默认调用父类的无参构造方法
}
}
多态 Polymorphism
对象可以具有多种形式或类型(为不同类型的实体提供一个统一的接口),比如多种动物都具有“吃”这个动作,但具体下来,狗可以表现为啃骨头,猫可以表现为抓老鼠吃等等
多态的实现主要依靠接口和继承
静态类型与动态类型
在Java编程中,静态类型是指变量在声明时所确定的类型,而动态类型是指变量在运行时实际引用的对象类型。这一对概念对于理解Java的多态特性至关重要。
1 | List61B<String> lst = new SLList<String>(); |
比如在这行代码中,在声明过程中,lst的类型为List61B,这是他的静态类型(static type),在实例化过程中,它使用了SLList构造函数进行实例化,这是它的动态类型(dynamic type)
当java运行一个被覆盖的方法时,他会在其动态类型中搜索适当的方法签名以运行
接口 Interface
接口是一系列方法的声明(并不是所有接口内部都必须有方法),是一些方法特征的集合(是解决Java无法使用多继承的一种解决方式),按我的理解来看对象到类到接口就是一个不断抽象的过程,把接口当作一个特殊的类就好,由全局变量和抽象方法组成(100%的抽象类,接口中必须全是抽象方法)
定义接口时需要使用interface关键字
1 | interface NAME{ |
类与接口之间不是继承关系,而是实现关系,需要用到implements关键字(如果一个类需要实现某个接口的话,必须实现这个接口内的所有方法)
1 | className implements interfaceName{ |