Java面向对象

面向对象

面向对象编程(Object-Oriented Programming, OOP)

其本质是:以类的方式组织代码,以对象的方式组织(封装)数据。

抽象

三大特性:封装、继承、多态

回顾方法

  1. 非静态方法不能直接调用
    在这里插入图片描述
    在这里插入图片描述
  2. 如果ab都不是static方法或者都是,a可以直接调用b
    但是如果只是a是,b不是,则不能。
    static和类一起加载的,而b是实例化后才调用。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  3. 一个类中只能有一个public class,可以有多个class
    在这里插入图片描述
    在这里插入图片描述
  4. 一个项目中应该只存在一个main方法
    在这里插入图片描述

对象的创建

使用new关键字创建对象,创建的时候除了分配内存空间之外,还会给创建好的对象进行默认的初始化 及 类中构造器的调用。

类中的构造器也称构造方法,是在进行创建对象时候必须调用的,构造器有两个特点:

  1. 必须和类的名字相同
  2. 必须没有返回类型,不能写void

.class文件进行反编译,可以看到默认的类的构造方法,即一个类即使什么都不写,也会存在一个方法。

在Java中,当一个类被声明但尚未实例化时,它仍然具有构造方法。每个类都至少有一个构造方法,即使你没有明确地为类定义构造方法,Java也会为其提供一个默认的无参数构造方法。

显式定义构造器

使用new关键字本质就是在调用构造器。

在Java中,你可以显式地定义构造器(也称为构造方法)来初始化对象。构造器的名称必须与类名完全相同,它没有返回类型,甚至不是void。 以下是定义构造器的一般步骤:

  • 构造器的声明:构造器的声明与方法类似,但没有返回类型,并且与类名完全相同。

  • 提供构造器的参数列表:你可以为构造器提供参数列表,以便在创建对象时传递参数进行初始化。

  • 构造器的主体:在构造器的主体中,你可以编写初始化对象时需要执行的代码。

显式定义构造器的主要目的在于允许你在创建对象时进行必要的初始化操作。以下是显式定义构造器的几个重要用途:

  • 对象初始化:构造器允许你在对象被创建时初始化对象的状态,设置初始值,并执行任何必要的初始化操作。这有助于确保对象在创建后处于一个合适的状态。

  • 参数传递:通过构造器,你可以定义接受参数的初始化过程,使得在创建对象时可以传递必要的参数来定制对象的初始化状态。

  • 约束条件:构造器可以用于确保对象的创建满足特定的约束条件,例如初始化必要的属性、建立必要的连接、或执行其他必要的设置。

  • 灵活性:显式定义构造器使得你能够自定义对象的初始化过程,根据需要执行不同的初始化操作。这有助于提高代码的灵活性和可重用性。

补充说明:
在Java中,静态方法(使用了static关键字修饰的方法)无法访问非静态成员,因为静态方法是与类关联的,而非静态成员是与类的实例(对象)关联的。因此,this 关键字在静态方法中是不可用的,因为它指向调用该方法的对象,而静态方法没有对象实例。

在这里插入图片描述

更改为:

  1. name 字段设置为静态:如果你希望在静态方法中访问 name 字段,你可以将 name 字段设置为静态,这样它就可以在静态方法中直接访问。
    在这里插入图片描述
  2. 将成员方法改为非静态方法:如果你希望在 print 方法中访问非静态成员 name,你可以将 print 方法设置为非静态方法,这样它就可以访问 name
    在这里插入图片描述

在Java中,如果你显式地定义了有参构造函数,而没有显式地定义无参构造函数,Java编译器会自动提供一个默认的无参构造函数。因此,从语言规范上来说,你不需要显式地定义无参构造函数。

但是,有一种情况下你需要注意:如果你在类中显式地定义了有参构造函数,而又希望在某些情况下使用无参构造函数,那么你就需要自己显式地提供一个无参构造函数。 这是因为一旦你提供了有参构造函数,Java编译器就不会再自动提供默认的无参构造函数了。

在Java中,构造函数(也称为构造器)不一定要在成员变量和方法的声明之前。事实上,构造函数可以出现在类中的任何地方。

MacOS中声明构造器的快捷键是command+N
Windows中声明构造器的快捷键是alt+Insert

在这里插入图片描述

内存分析

结合上一个博客进行分析:https://blog.csdn.net/qq_43606119/article/details/134900927

在这里插入图片描述

在这里插入图片描述

在Java中,内存可以分为以下几个主要部分:

  • 堆内存(Heap Memory):
    堆内存用于存储对象实例和数组。在堆内存中分配的内存需要手动进行垃圾回收(Garbage Collection)来释放不再使用的对象和数组占用的内存空间。Java的自动内存管理系统负责管理堆内存。

  • 栈内存(Stack Memory):
    栈内存用于存储局部变量和方法调用。每当你调用一个方法时,系统都会在栈内存中创建一个栈帧(Stack Frame),用于存储方法的参数、局部变量以及方法返回后的结果。当方法执行结束时,对应的栈帧会被销毁,释放其占用的空间。
    在方法中创建一个对象时,这个对象实际上是存储在堆内存中的,而对这个对象的引用则会存储在栈内存中。因此,堆内存和栈内存是相互关联的。

  • 方法区(Method Area):
    方法区存储类的结构信息,例如类的字段、方法信息,静态变量,常量等。在方法区中也会存储运行时常量池,包括类中的常量、字面量等。在较新的Java版本中,方法区被元空间(Metaspace)所取代。
    在Java中,方法区通常被认为是堆的一部分,但它不是堆的一部分。
    方法区是堆的逻辑部分,但在物理上它通常是在堆的外部。

  • 本地方法栈(Native Method Stack):
    本地方法栈与栈内存类似,但它用于执行本地方法(Native Method)的调用。

  • 程序计数器(Program Counter):
    程序计数器是当前线程执行的字节码的行号指示器。它并不是一个内存区域,而是线程私有的,每个线程都有自己的程序计数器。

Java的内存管理是由Java虚拟机(JVM)负责的,JVM负责分配、管理和释放内存。其中,堆内存和栈内存是Java程序中最常用的内存区域。理解这些内存区域对于编写高效、健壮的Java程序至关重要。

类中成员的默认值

  • 数值类型:
    byte, short, int, long 类型的默认值为 0。
    float, double 类型的默认值为 0.0。

  • 布尔类型:
    boolean 类型的默认值为 false。

  • 字符类型:
    char 类型的默认值为 ‘\u0000’(null字符)。

  • 引用类型:
    对象引用的默认值为 null。

面向对象三大特性

封装

程序设计追求“高内聚、低耦合”

封装是面向对象编程中的一种重要概念,它指的是将对象的状态(数据)和行为(方法)包装在一起,并对外部提供访问这些数据和方法的接口,同时隐藏对象的内部实现细节。封装可以帮助实现信息隐藏和保护数据的完整性,同时提供良好的接口以供其他对象进行交互。

为什么封装?封装的主要目的包括:

  • 隐藏对象的内部细节:
    封装可以隐藏对象的内部状态和实现细节,防止外部直接访问和修改对象的数据,从而避免意外的修改或破坏。

  • 保护数据的完整性:
    通过封装,对象可以对数据的访问进行控制,确保数据在被外部访问时不会被意外修改,从而维护数据的完整性。

  • 提供良好的接口:
    封装可以提供良好的接口,使得对象的使用者只需关注如何使用对象的接口,而无需关心对象的内部实现细节,从而降低了对象之间的耦合度。

如何实现封装?在Java中,封装主要通过以下几种方式来实现:

  • 访问控制符:
    使用访问控制符(如 private、protected、public)来限制对类的成员的访问。通常,对象的数据域会被声明为 private,只能通过类内部的方法(即 getter 和 setter 方法)来访问和修改。

  • Getter 和 Setter 方法:
    通过公共的 getter 和 setter 方法来允许外部访问和修改对象的状态。这样可以在方法中加入一些逻辑判断,实现对数据的控制和保护。

  • 类:
    将数据和方法封装在一个类中,通过类的实例化对象来访问和操作数据,从而隐藏了数据的具体实现细节。

在这里插入图片描述

同样,可以使用快捷键快速声明getter和setter方法

MacOS中声明构造器的快捷键是command+N
Windows中声明构造器的快捷键是alt+Insert

在这里插入图片描述

可以通过封装保护数据,提高程序的安全性。

在这里插入图片描述

继承

如果说封装是对一类对象的抽象,那么继承就是对一批类的抽象。

类和类之间有很多关系,比如组合、依赖等,继承是其中一种关系。

组合关系:

在这里插入图片描述

继承关系:

在这里插入图片描述

什么是继承?
在Java中,继承是一种重要的面向对象编程概念,它允许一个类(称为子类)可以继承另一个类(称为父类)的属性和方法。这意味着子类可以重用父类的特性,并且可以添加自己的特性或修改父类的特性。

为什么使用继承?

  • 代码重用:继承允许子类重用父类的代码,避免了重复编写相同的代码。

  • 层次化组织:通过继承,可以建立类之间的层次关系,使得代码更加有组织、易于理解。

  • 多态性:继承是实现多态性的一种重要方式,允许将不同的子类对象当作父类对象来对待,从而增加灵活性。

如何实现继承?在Java中,继承可以通过以下方式来实现:

  • 定义父类:首先定义一个父类(也称为基类或超类),其中包含一些通用的属性和方法。

  • 定义子类:然后定义一个子类(也称为派生类),通过使用关键字 extends 来指示子类继承自父类。子类可以继承父类的属性和方法,并且可以添加自己的属性和方法。

  • 访问父类方法:在Java中,子类可以通过使用 super 关键字来访问父类的方法或构造函数。

// 定义父类  
class Animal {  
    String name;  
  
    public Animal(String name) {  
        this.name = name;  
    }  
  
    public void makeSound() {  
        System.out.println("Some generic sound");  
    }  
}  
  
// 定义子类  
class Dog extends Animal {  
    public Dog(String name) {  
        super(name);  
    }  
  
    @Override  
    public void makeSound() {  
        System.out.println("Woof!");  
    }  
}  
  
// 创建子类对象  
public class Main {  
    public static void main(String[] args) {  
        Dog dog = new Dog("Buddy");  
        dog.makeSound();  // 输出: Woof!  
    }  
}  

Dog类重用了Animal类的属性和方法,同时也添加了自己的特性。

Java中只有单继承没有多继承,然而,存在接口多继承接口可以看作是一种抽象类,其中可以包含方法的签名但不包含方法的实现。一个类可以实现多个接口,从而获得多个接口的特性。

查看一个类的层次结构的快捷键:

Windows系统是Ctrl+H
MacOS系统是^H,在键盘上就是Ctrl+H

在这里插入图片描述

所有的类默认直接或者间接继承Object类。

子类继承了父类,就会拥有父类的所有方法.

在这里插入图片描述

除了私有方法。

在这里插入图片描述

super的用法:调用父类的构造方法和访问父类的成员。

super只能出现在子类的方法或者构造方法中。

  1. 调用父类的构造方法:在子类的构造方法中,可以使用 super() 调用父类的构造方法。这对于子类需要执行父类的初始化操作非常有用。如果子类构造方法中没有显式地调用 super(),则会隐式地调用父类的无参构造方法。

    public class Parent {  
        public Parent(int value) {  
            // 父类构造方法  
        }  
    }  
    
    public class Child extends Parent {  
        public Child(int value) {  
            super(value); // 调用父类的构造方法  
            // 其他子类初始化操作  
        }  
    }  
    
  2. 访问父类的成员:在子类中,可以使用 super 关键字访问父类中的方法和属性。这对于子类需要在重写方法中调用父类的方法或访问父类的属性非常有用。

    public class Parent {  
        public void print() {  
            System.out.println("Parent's print method");  
        }  
    }  
    
    public class Child extends Parent {  
        public void print() {  
            super.print(); // 调用父类的方法  
            System.out.println("Child's print method");  
        }  
    }  
    

子类调用父类的无参构造,并且是先执行父类的构造方法。

在这里插入图片描述

等效上面的代码,但是super()语句一定要写在子类重写的构造方法体的前面,可以不写,默认有。

在这里插入图片描述

在 Java 中,this()super() 都是用于调用构造函数的关键字。this() 用于调用本类的其他构造函数,而 super() 用于调用父类的构造函数。在同一个构造函数中,不能同时出现 this()super(),因为两者都是用于调用构造函数的,而且 Java 不允许在一个构造函数中同时存在两个调用其他构造函数的语句,一个构造函数中可以使用 this()super() 其中之一。

在这里插入图片描述

在这里插入图片描述

方法重写(涉及到多态性)

在Java中,当你使用多态性(Polymorphism)时,即通过父类的引用指向子类的对象,调用被子类重写的方法时,实际上会调用子类中的重写方法。

在这段代码中,当你执行 Parent p = new Son(); p.print(); 时,即使 p 的类型是 Parent,但实际上它指向的是 Son 类的一个实例。由于 print 方法在 Son 类中被重写了,因此无论通过 Son 类的引用还是 Parent 类的引用,调用 print 方法都会执行 Son 类中的 print 方法。

这是因为 Java 中的方法调用是动态绑定的,也就是说,实际调用的方法是在运行时确定的,根据对象的实际类型来决定调用哪个方法,并不是根据引用变量的类型来决定要调用的方法。这就是多态性的体现。

在这里插入图片描述

虽然是否使用@override都不影响子类对父类的方法进行重写,但是使用的话会有两个好处:

  1. 帮助自己检查是否正确的重写父类方法
  2. 明显地提示看代码的人,这是重写的方法

在这里插入图片描述

方法重写只涉及非静态方法。

当子类定义一个与父类具有相同签名的静态方法时,实际上是在子类中隐藏了父类的静态方法。这意味着无论使用父类的引用还是子类的引用,调用静态方法时,实际上会根据引用类型来决定要调用的方法,而不是根据对象的实际类型

这与实例方法的动态绑定不同。因此,静态方法不具备多态性,它们是与类关联的,而不是与对象的实际类型关联的。

在这里插入图片描述

方法重写不可以重写私有方法!

在这里插入图片描述

总结:

当子类定义一个与父类中具有相同方法名、参数列表和返回类型的方法时,就称为方法重写。

重写方法的特点包括:

  1. 方法名称、参数列表和返回类型必须与父类中的方法完全相同。
  2. 子类中重写的方法不能拥有比父类中的方法更严格的访问权限(比如,如果父类中的方法是public,那么子类中重写的方法也必须是public)。
  3. 重写的方法不能比父类中的方法抛出更多、更宽泛的异常,但可以不抛出异常或者抛出更少、更具体的异常。

通过方法重写,子类能够为继承自父类的方法提供特定的实现,从而使得程序能够根据对象的实际类型来动态地调用适当的方法(针对的是非静态方法)。这种特性也称为多态性,它允许程序根据对象的实际类型来决定要调用的方法,而不是根据引用的类型。

和构造方法、gettersetter,同样的快捷键。

一个对象能执行哪些方法看的是左边的引用类型(Object类中没有print方法不能执行)
方法执行的效果看的是右边的对象是否重写(p.print()执行的是子类Son重写后的print方法,因此输出son

在这里插入图片描述

但是可以通过强制类型转换用子类的独有方法(高类型向低类型转换需要强制类型转换)

在这里插入图片描述

不能重写(即不存在多态性):(1)静态方法;(2)常量;(3)private方法

访问控制符

访问控制符(Access Control Modifiers)在Java中用于控制类、变量、方法以及构造函数的访问权限。Java提供了四种不同的访问控制符:

  1. private:私有访问控制符,被声明为私有的成员只能在定义它们的类内部访问。这意味着其他任何类,包括该类的子类,都无法访问这些私有成员。

  2. default (package-private):默认访问控制符,如果没有指定访问控制符,则成员具有默认访问控制符。默认访问控制符的成员只能被同一包内的其他类访问。

  3. protected:受保护的访问控制符,受保护的成员可以被同一包内的其他类访问,也可以被其他包中的子类访问。

  4. public:公共访问控制符,公共成员可以被任何类访问,无论是否属于同一包或者是子类。

范围依次扩大

多态

多态是面向对象编程中的一个重要概念,它允许不同类的对象对同一消息做出响应,即同样的方法调用会根据对象的不同类型而表现出不同的行为(运行的时候才知道)。

一个对象的实际类型是确定的,但可以指向对象的引用类型有很多(父类,有关系的类)

例如:
Animal这个父类有很多子类(Dog类和Cat类)。
myDogmyCat 都是 Animal 类型的引用,但它们实际上指向了不同的子类对象。
当调用 makeSound 方法时,根据对象的实际类型,会执行相应子类中的方法,这就是多态性的体现。

Animal myDog = new Dog();  
Animal myCat = new Cat();  
  
myDog.makeSound(); // 这里会调用 Dog 类中的 makeSound 方法  
myCat.makeSound(); // 这里会调用 Cat 类中的 makeSound 方法  

如何实现多态:

实现多态性通常需要依赖继承和重写(覆盖)两个特性。具体来说,多态性通常通过以下方式实现:

  1. 继承:子类继承父类,可以继承父类的方法。
  2. 方法重写:子类可以重写(覆盖)父类中的方法,从而根据子类对象的实际类型来执行不同的方法实现。
  3. [父类引用指向子类]

注意,多态是方法的多态,属性没有多态性。

instanceof

instanceof 是 Java 中的一个关键字,用于判断一个对象是否是某个特定类的实例,或者是其子类的实例。其语法形式为 object instanceof class,其中 object 是要检查的对象,class 是要检查的类或接口。

具体来说,instanceof 的作用是检查一个对象是否是一个特定类的实例,或者是其子类的实例。如果是,则返回 true,否则返回 false。这个操作通常用于避免在进行类型转换时出现异常,或者在运行时根据对象的实际类型执行相应的操作。

在上面的例子中,myDog instanceof Dog就会返回true。

匿名代码块

匿名代码块通常指的是在Java中的一种特殊的代码块,它没有名字,用于初始化实例。

这些代码块在创建对象时执行,并且优先于构造函数执行。

特点:

  • 没有名字:匿名代码块没有方法名或标识符,只是一段用花括号括起来的代码。
  • 初始化实例:用于在创建对象时执行一些初始化操作,比如初始化实例变量。
  • 优先于构造函数:匿名代码块的执行优先于构造函数,无论是调用无参构造函数还是带参构造函数。
package lamda;

public class Demo01 {
    {
        System.out.println("匿名代码块");
    }

    static {
        System.out.println("静态代码块");
    }

    public Demo01() {
        System.out.println("构造方法");
    }

    public static void func() {
        System.out.println("静态方法");
    }

    public static void main(String[] args) {
        Demo01 d = new Demo01();
        d.func();
        System.out.println("==================");
        Demo01 m = new Demo01();
        m.func();
    }
}

输出结果:

在这里插入图片描述

静态代码块

静态代码块是在Java中用来初始化静态成员变量或执行静态方法的一种代码块。
它在类加载的过程中执行,只会执行一次,并且优先于构造函数和其他实例化操作。

作用:

  • 静态成员变量的初始化:静态代码块可以用来初始化类的静态成员变量,这些变量在类加载时就会被初始化。
  • 执行静态方法:静态代码块也可以用来执行一些静态方法,这些方法可能需要在类加载时进行初始化或其他操作。
  • 加载驱动程序:在Java中,静态代码块经常用来加载数据库驱动程序,因为它们在类加载时只会执行一次,适合这样的初始化操作。

静态代码块和静态函数(方法)的区别:

  • 执行时机:
    • 静态代码块:在类加载时执行,且只执行一次,用于初始化类的静态成员变量或执行其他静态操作。
    • 静态函数:可以在任何时候通过类名直接调用,也可以在对象实例上调用,用于执行一段特定的功能或操作。
  • 初始化作用:
    • 静态代码块:主要用于类的初始化操作,例如对静态成员变量的初始化。
    • 静态函数:用于定义可以直接通过类名调用的静态方法,执行特定的功能。
  • 语法:
    • 静态代码块:使用 static 关键字,不需要方法名,用花括号包裹代码块。
    • 静态函数:使用 static 关键字,需要方法名和参数列表,并包含方法体内的代码。

抽象类

抽象类是一种在面向对象编程中使用的概念。它是一种不能被实例化的类,即不能创建该类的对象。抽象类通常用于定义一些通用的特征和行为,但它本身并不包含足够的信息来进行实例化。

在面向对象编程中,abstract修饰符通常用于定义抽象类和抽象方法。

抽象类中可以没有抽象方法,但是包含抽象方法的类一定要声明为抽象类。

抽象类不能用new关键字来创建对象,是用来让子类继承的。
抽象方法只有方法的声明,没有方法的实现,是用来让子类来实现的。
抽象类中可以有普通方法,但是抽象方法必须在抽象类中。

子类继承抽象类,必须实现抽象类中没有实现的抽象方法,否则子类也应该声明为抽象类。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

抽象类本质上还是单继承。

接口

在Java中,接口是一种抽象类型,它定义了一组方法的签名,但没有具体的实现。接口是通过关键字interface来声明的。接口中的方法默认是抽象的,不需要使用abstract关键字进行标识。

声明类的关键字是class,声明接口的关键字是interface

在Java中,接口里的所有定义确实都是抽象的,但并不是所有定义都是public的。让我们逐一解释:

抽象性:是的,接口中的所有方法都是抽象的,也就是说不包含方法的具体实现。在接口中声明方法时,不需要加上 abstract 关键字,因为接口中的方法默认就是抽象的。

访问修饰符:在Java 8之前,接口中的方法都是公共的(public),因为接口中的方法默认是公共的,你可以在接口中的方法定义中显式地加上 public 关键字,但这并不是必需的。然而,从Java 9开始,Java引入了私有方法和私有静态方法,因此接口中可以包含私有方法和私有静态方法,这些方法不再要求是公共的。

所以,综上所述,这个说法在Java 8之前是正确的,但在Java 9及以后需要做出修正。

在这里插入图片描述

在IDEA中声明一个接口:

在这里插入图片描述

在这里插入图片描述

抽象类的本质其实还是单继承,子类只能继承一个父类,而在接口中,一个类可以实现多个接口。
实现了接口的类,必须实现接口中所有方法。

在这里插入图片描述
接口不能被实例化,没有构造函数。

内部类

在Java中,内部类是定义在另一个类内部的类。内部类的定义允许类的作者将一类的定义放在另一个类的定义内部,这通常是为了更好地封装和组织代码。

在Java中,内部类可以分为四种类型:成员内部类、静态内部类、局部内部类和匿名内部类。

  1. 成员内部类
  • 可以访问外部类的成员,包括私有成员。
  • 与外部类实例相关联,需要通过外部类的实例来访问
  • 可以拥有自己的成员变量和方法。
    在这里插入图片描述
  1. 静态内部类
  • 不需要依赖外部类的实例。
  • 不能直接访问外部类的实例变量和方法,但可以访问外部类的静态成员。
    在这里插入图片描述
  1. 局部内部类
  • 作用域仅限于所在的方法或代码块。
  • 可以访问外部类的成员。
  • 不能有访问修饰符(即不能是 publicprivateprotected)。
    在这里插入图片描述
  1. 匿名内部类

    匿名内部类是没有名字的内部类,通常用于创建只需使用一次的类。
    比如某个接口我只用一次,没必要为了这个接口再声明一个类。

  • 没有显式的类名。

  • 可以实现接口或继承抽象类。

  • 通常用于创建事件处理器、线程等。

    在 Java 中,代码需要在类的方法中执行,而不能直接放在类的主体中。当类被加载时,它的字段和方法会被初始化,但是直接执行语句(比如 i.func();)是不被允许的。
    在这里插入图片描述

  • 17
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值