Java基础:面向对象(二)

news2024/11/18 11:37:14

Java基础:面向对象(二)


文章目录

  • Java基础:面向对象(二)
    • 1. 面向对象编程思想
    • 2. 类与对象
      • 2.1 类
        • 2.1.1 类的定义
        • 2.1.2 成员变量
        • 2.1.3 局部变量
      • 2.2 对象
        • 2.2.1 对象的定义
        • 2.2.2 对象的使用
        • 2.2.3 对象创建的原理
    • 3. 封装
    • 4. 构造方法
    • 5. 对象作为方法的参数或返回类型
    • 6. 对象数组
      • 6.1 声明数组变量
      • 6.2 创建数组对象
    • 7. static关键字
      • 7.1 static可以修饰属性
      • 7.2 static可以修饰方法
      • 7.3 static可以修饰代码块
      • 7.4 单例模式
        • 7.4.1 饿汉式单例模式
        • 7.4.2 懒汉式单例模式
    • 8. 使用访问修饰符实现封装
      • 8.1 包的概念
      • 8.2 导包操作
      • 8.3 静态导入
    • 9. 代码封装
    • 10. this关键字
    • 11. 继承关系
      • 11.1 继承的概念
      • 11.2 继承类型
      • 11.3 继承的特性
      • 11.4 子类实例化过程
      • 11.5 属性的隐藏
      • 11.6 方法重写(方法覆盖)
      • 11.7 抽象方法和抽象类
      • 11.8 final关键字的用法
    • 12. 多态
      • 12.1 多态的概念
      • 12.2 向上转型(上溯造型)
      • 12.3 向下转型(下溯造型)
      • 12.4 里式代换原则
      • 12.5 instanceof运算符
    • 13. 组合关系
    • 14. 接口
        • 14.1 接口的概念
        • 14.2 语法格式
        • 14.3 接口的应用
          • 14.3.1 接口的多态
          • 14.3.2 接口与抽象类
          • 14.3.3 简单工厂模式
          • 14.3.4 工厂方法模式
          • 14.3.5 缺省适配器模式
    • 15. 内部类
      • 15.1 简介
      • 15.2 分类
        • 15.2.1 成员内部类(普通内部类)
        • 15.2.2 静态内部类
        • 15.2.3 局部内部类
        • 15.2.4 匿名内部类


1. 面向对象编程思想

  • 面向对象分析(Object Oriented Analysis,OOA):面向对象分析是软件开发过程的第一步,它主要关注于对问题域的理解和分析。
  • 面向对象设计(Object Oriented Design,OOD):面向对象设计是在面向对象分析的基础上进行的,它主要关注于如何将分析阶段得到的需求转化为具体的设计。
  • 面向对象编程(Object-Oriented Programming,OOP):面向对象编程是面向对象软件开发的实现阶段,它主要关注于使用面向对象编程语言来实现设计阶段的成果。面向对象编程的主要特性包括封装性继承性多态性,这些特性有助于提高代码的可重用性、可维护性和可扩展性。

2. 类与对象

2.1 类

2.1.1 类的定义

在Java中,类(Class)是对象(Object)的模板,它定义了一类对象的属性和行为。类的定义格式如下:

[访问修饰符] class 类名{
	成员变量(属性);
	成员方法(行为);
	内部类;
	代码块;
}
2.1.2 成员变量

成员变量(包括实例变量和静态变量)是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。Java中类的成员变量都有默认值。例如,整数类型为0,浮点数类型为0.0,布尔类型为false,字符类型为'\u0000',引用类型为null

2.1.3 局部变量

方法体方法参数或者代码块中定义的变量被称为局部变量。局部变量没有默认值,声明和初始化都是在方法中,方法结束后,变量就会自动销毁。

:成员变量有默认值,局部变量没有默认值,产生的原因?

:成员变量和局部变量在默认值行为上的差异主要源于它们在生命周期、存储位置、作用域和初始化机制上的不同。以下是具体的原因:

  1. 生命周期
    成员变量:随着类的实例化或类的加载而被创建,并在类的实例或类本身被销毁时销毁。
    局部变量:在方法、构造函数或初始化块执行时创建,并在执行完毕后销毁。
  2. 存储位置
    成员变量:实例变量存储在堆内存中(随着对象的创建而分配),静态变量存储在方法区(也称为静态存储区或类变量区)。
    局部变量:存储在栈内存中,每个方法执行时都会在栈内存中创建一个栈帧,局部变量就存储在这个栈帧中。
  3. 作用域
    成员变量:可以在类的任何方法中访问(对于实例变量)或在类的外部通过类的实例或类名(对于静态变量)访问。
    局部变量:只能在声明它们的方法、构造函数或初始化块内部访问。
  4. 初始化
    成员变量:如果没有显式地初始化,Java在创建类的实例或JVM(Java虚拟机)类加载时,会自动为成员变量赋予默认值。
    局部变量:使用前必须显式初始化,否则编译时会出现错误。

2.2 对象

2.2.1 对象的定义

对象是类的一个实例,有状态和行为。类和对象的关系,就是抽象和具体的关系。创建对象的基本语法格式如下:

类名 对象名 = new 类名();

实例化对象

2.2.2 对象的使用

调用对象的属性和方法,如果是在当前类中,可以直接访问属性和方法。如果在其他类中,通过点.运算符来访问其属性和方法。例如,如果有一个名为Person的类,需要创建per1per2per3三个对象,使用setget方法对属性进行赋值和取值操作。代码示例如下:

/* Test.java */
public class Test {
    public static void main(String[] args) {
        Person per1 = new Person();
        Person per2 = new Person();
        Person per3 = new Person("王麻子", 28);
        per1.setName("张三");
        per1.setAge(10);
        per2.setName("李四");
        per2.setAge(18);
        per1.init();
        System.out.println("姓名:" + per2.getName());
        System.out.println("年龄:" + per2.getAge());
        per3.init();
    }
}
/* Person.java */
public class Person {
    private String name;
    private int age;
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void init() {
        System.out.println("姓名:" + name);
        System.out.println("年龄:" + 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;
    }
}

🆚🆚运行结果:

姓名:张三
年龄:10
姓名:李四
年龄:18
姓名:王麻子
年龄:28
2.2.3 对象创建的原理
  • 在Java虚拟机(JVM)的内存模型中,程序计数器、Java栈、堆、本地方法栈和方法区是五个重要的组成部分。
  • 当使用new关键字创建一个对象时,同时操作了栈和堆,在栈内存中保存对象的引用(对象的首地址),在堆内存中保存对象的属性。
  • 编写.java文件(源文件)的时候,文件存储在硬盘上,这个文件只是包含了类的定义和源代码,但它并没有被JVM加载到内存中执行。当编译这个.java文件时,Java编译器(如javac命令)会检查源代码的语法和语义。编译成功后,它会生成一个或多个.class文件(类文件)。这些类文件包含了JVM执行程序所需的字节码(Byte-code)和其他信息。当运行(如java命令)包含main方法的类时,JVM会加载这个类文件到内存中。加载过程中,JVM会执行以下步骤:
    • 加载:JVM通过类加载器(ClassLoader)查找并加载指定的类文件到内存中。
    • 链接
      • 验证:确保被加载的类的正确性和安全性。
      • 准备:为类的静态变量分配内存,并将其初始化为默认值。
      • 解析:把类中的符号引用转换为直接引用。
    • 初始化:为类的静态变量赋予正确的初始值。
  • 操作对象都是通过引用完成,一旦引用出栈,没有被引用的对象就变成了垃圾,JVM的垃圾回收器(Garbage Collector,GC)会负责回收其占用的内存空间。

3. 封装

  • 定义:封装是将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来对隐藏的信息进行操作和访问。具体来说,封装通过定义私有变量(使用private作为访问修饰符)和公有方法(公共的gettersetter方法)来实现,私有变量只能在类的内部访问,而公有方法可以在类的外部访问和调用。
    公有方法
  • 作用:数据隐藏,提高代码的可维护性,实现接口和抽象。降低耦合性,实现高内聚、低耦合。

4. 构造方法

在Java中,构造方法(也称为构造函数或构造器)是一种特殊的方法,用于在创建对象时初始化该对象的状态。构造方法的基本语法格式如下:

public class ClassName {  
    // 类的成员变量  
    // ...  
  
    // 构造函数  
    public ClassName() {  
        // 构造函数的实现代码  
        // 这里可以初始化类的成员变量等  
    }  
    
    // 如果有需要,可以定义带参数的构造函数  
    public ClassName(parameterType1 parameter1, parameterType2 parameter2, ...) {  
        // 构造函数的实现代码  
        // 这里可以使用传入的参数来初始化类的成员变量等  
    }  
    
    // 类的其他方法  
    // ...  
}
  • 名称:构造方法的名称必须与类名完全相同。这是Java语言规定的一个特性,以便在创建对象时能够自动调用相应的构造方法。
  • 返回类型:构造方法没有返回类型,也不允许使用void关键字声明返回类型。这是因为构造方法的主要目的是初始化对象,而不是返回任何值。
  • 访问修饰符:构造方法可以使用访问修饰符(如publicprotectedprivate)来限制其可见性和可访问性。通常,构造方法被声明为public,以便可以从类的外部创建对象。但是,在某些情况下,你可能希望将构造方法声明为protectedprivate,以实现特定的设计目标(如单例模式、工厂方法等)。
  • 参数列表:构造方法可以具有任意数量和类型的参数。这些参数在创建对象时通过new关键字传递给构造方法,以便在初始化对象时使用。通过提供不同参数的构造方法,可以实现对象的多样化初始化。
  • 默认构造方法:在Java中,每个类至少有一个构造方法。如果你没有显式地定义任何构造方法,那么Java编译器会自动为你生成一个默认的无参构造方法。如果你定义了至少一个构造方法,默认的无参构造方法可能不会被自动生成。
  • 构造方法的调用:如果是在其他类中,通过new的方式来调用构造方法;如果是在自己的类中,可以在构造方法之间进行互相调用。使用this([参数]),必须写在构造方法中的第一行。
  • 继承:子类不会继承父类的构造方法,但子类构造方法可以通过super()关键字调用父类的构造方法。

5. 对象作为方法的参数或返回类型

创建一个类,然后创建该类的对象,并将这些对象作为参数传递给方法或作为方法的返回值。代码示例如下:

public class Person {  
    private String name;  
    private int age;  
  
    // 构造方法、getter和setter方法...  
  
    // 示例方法,接受Person对象作为参数  
    public void greet(Person otherPerson) {  
        System.out.println(this.name + " says hello to " + otherPerson.getName());  
    }  
  
    // 示例方法,返回Person对象  
    public Person getOlderPerson(Person person) {  
        if (this.age > person.getAge()) {  
            return this;  
        } else {  
            return person;  
        }  
    }  
}

6. 对象数组

对象数组是一种可以存储多个对象的数组。每个数组元素都可以是一个对象的引用,指向一个实际的对象。

6.1 声明数组变量

声明一个数组变量,指定数组将要存储的对象的类型,并使用方括号[]表示它是一个数组。

Student[] students; // 声明一个Student类型的数组变量

6.2 创建数组对象

使用new关键字和类的名称来创建数组对象,并指定数组的大小(即它可以存储的元素数量)。

students = new Student[3]; // 创建一个可以存储3个Student对象的数组。

或者,可以在声明变量的同时创建数组对象:

Student[] students = new Student[3]; // 类名[] 数组 = new 类名[数组长度];

代码示例如下:

// Test.java
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("小明", "男");
        Student student2 = new Student("小红", "女");
        Student student3 = new Student("小丽", "女");
        Teacher teacher = new Teacher();
        
        Student[] students = new Student[3];
        students[0] = student1;
        students[1] = student2;
        students[2] = student3;
        
        teacher.sign(students);
    }
}
// Teacher.java
public class Teacher {
    public void sign(Student[] students) {
        for (Student student : students) {
            String gender = student.getGender();
            if (gender.equals("男")) {
                System.out.println(student.getName() + "到");
            } else {
                System.out.println(student.getName() + "有");
            }
        }
    }
}
// Student.java
// ...

🆚🆚运行结果:

小明到
小红有
小丽有

7. static关键字

7.1 static可以修饰属性

  • 静态成员变量(类成员变量):使用static修饰的,作用范围在整个类的所有对象上。所有对象共享这个变量,在内存中只有一个副本。访问通过类名.变量名访问。在类加载时被初始化,存储在堆里。
  • 实例成员变量:没有static修饰,作用范围在某个实例对象上。在创建对象时被初始化,存在多个副本。各个对象不互相影响。通过实例对象名.变量名访问。

7.2 static可以修饰方法

  • 静态方法(类方法):使用static修饰,不能直接使用实例变量,只能直接使用静态变量。只能直接调用静态方法,不能直接调用实例方法。通过类名.方法名()访问。不允许使用this关键字(代表对象)。
  • 实例方法:没有static修饰,既可以使用静态变量,又可以使用实例变量。既可以直接访问实例方法,又可以直接访问静态方法。 通过实例对象名.方法名()访问。

代码示例如下:

public class Car {
    // 静态成员变量
    public static int count = 0;

    // 实例成员变量
    private String licenseNumber;
    private String brand;

    // 构造方法
    public Car(String licenseNumber, String brand) {
        this.licenseNumber = licenseNumber;
        this.brand = brand;
        // 每次创建Car对象时,都更新静态变量count
        count++;
    }

    // 静态方法,用于返回当前Car对象的数量
    public static int getCurrentNumberOfCars() {
        return count;
    }

    // 实例方法,用于显示Car对象的详细信息
    public void displayDetails() {
        System.out.println("第" + count + "辆车的车牌号是:" + licenseNumber + ",品牌是:" + brand);
    }

    public static void main(String[] args) {
        // 可以通过类名直接访问静态成员变量
        System.out.println("最初车的数量:" + Car.count);

        // 创建Car对象,通过对象引用访问实例成员变量、实例方法
        Car car1 = new Car("A12345", "问界M9");
        System.out.println("第" + count + "辆车的车牌号是:" + car1.licenseNumber + ",品牌是:" + car1.brand);
        Car car2 = new Car("B67890", "小米SU7");
        car2.displayDetails();

        // 可以通过类名访问静态方法,获取Car对象的数量
        System.out.println("目前车的数量:" + Car.getCurrentNumberOfCars());
    }
}

🆚🆚运行结果:

最初车的数量:01辆车的车牌号是:A12345,品牌是:问界M92辆车的车牌号是:B67890,品牌是:小米SU7
目前车的数量:2

7.3 static可以修饰代码块

执行顺序和优先级:静态代码块>构造代码块>构造方法。

  • 静态代码块:类加载的时候执行,并且只执行一次,优先于各种代码块和构造方法,最开始执行的。
  • 构造代码块:在创建对象时被调用,每创建一次对象,都会执行一次构造代码块。 执行顺序,优先于构造方法执行。一个类如果有多个构造方法时,每通过构造方法,创建一个对象,代码块都会被执行一次。但是对于构造方法来讲,只执行某一个构造方法(根据参数执行)。

代码示例如下:

public class TestInitialization {
    public TestInitialization() {
        System.out.println("Constructor executed.");
    }

    {
        System.out.println("Instance block executed.");
    }

    static {
        System.out.println("Static block executed.");
    }

    public static void main(String[] args) {
        TestInitialization obj1 = new TestInitialization();
        TestInitialization obj2 = new TestInitialization();
    }
}

🆚🆚运行结果:

Static block executed.
Instance block executed.
Constructor executed.
Instance block executed.
Constructor executed.

7.4 单例模式

单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只能创建一个实例对象,自行实例化,并向整个系统提供这个对象。

7.4.1 饿汉式单例模式

饿汉式单例模式(Eager Initialization Singleton)是一种在类加载时就立即创建单例实例的设计模式。这种模式相比于懒汉式单例模式(Lazy Initialization Singleton),它的优势在于其线程安全性,因为JVM在加载类时会对静态变量进行初始化,并且这个初始化过程是线程安全的。

// 饿汉式单例模式
public class Singleton {
    private Singleton() { // 私有构造函数,防止外部通过new创建实例
    }
    private static final Singleton single = new Singleton(); // 提供一个当前类的私有静态成员变量
    public static Singleton getInstance() { // 提供一个公有的静态方法,返回成员变量
        return single;
    }
}
7.4.2 懒汉式单例模式

非线程安全的懒汉式单例模式是最基本的懒汉式单例模式实现,但在多线程环境下是不安全的。

// 非线程安全的懒汉式单例模式
public class Singleton2 {
    private Singleton2() {
    }
    private static Singleton2 singleton;
    public static Singleton2 getInstance() {
        if (singleton == null)
            singleton = new Singleton2();
        return singleton;
    }
}

为了保证懒汉式单例模式的线程安全,通常有以下3种方法:

  1. 线程安全的懒汉式单例模式:使用synchronized关键字来保证线程安全,但这种方式在性能上较差,因为每次调用getInstance()方法时都需要进行同步;
  2. 双重检查锁定(Double-Check Locking,DCL)的懒汉式单例模式:DCL模式通过两次检查实例是否为null来避免不必要的同步,是线程安全且性能较好的实现方式。为了避免指令重排导致的问题,需要将instance声明为volatile
// 双重检查锁定的懒汉式单例模式
public class Singleton3 {
    private volatile static Singleton3 instance;
    private Singleton3() {
    }
    public static Singleton3 getInstance() {
        if (instance == null) { // 第一次检查  
            synchronized (Singleton3.class) {
                if (instance == null) { // 第二次检查  
                    instance = new Singleton3();
                }
            }
        }
        return instance;
    }
}
  1. 静态内部类的懒汉式单例模式:利用静态内部类的特性来实现懒汉式单例模式,这种方式既保证了线程安全,又避免了同步带来的性能开销。
// 静态内部类的懒汉式单例模式
public class Singleton4 {
    private Singleton4() {
    }
    // 静态内部类  
    private static class Singleton4Holder {
        private static final Singleton4 INSTANCE = new Singleton4();
    }
    // 提供全局访问点  
    public static Singleton4 getInstance() {
        return Singleton4Holder.INSTANCE;
    }
}

8. 使用访问修饰符实现封装

8.1 包的概念

包(Package)是Java中用于组织类的命名空间。它可以把相关的接口枚举注解组合在一起,形成一个逻辑上的单元。包的主要目的是为了解决类的命名冲突和进行更好的代码组织。
包
以下是一些在Java类库中比较常用的包:

包名说明
java.langjava语言包,不需要进行导包,就可以使用的
java.utiljava实用工具包
java.iojava输入输出流包
java.sql操作数据库相关的包
java.netjava网络包
java.text处理文本、日期、时间、数字、消息的类和接口
java.swing图形用户界面

包的用途:

  1. 避免命名冲突;
  2. 可以按功能将类进行组合;
  3. 可以保护类、数据和方法。

📌

  1. package打包语句必须在代码的第一条语句。前面只能有空白和注释;
  2. 使用package时,会在磁盘上生成对应的目录;
  3. 每个源文件只能有一条打包语句。

8.2 导包操作

在同一个包中,类之间互相访问,可以不需导包操作;如果类不在同一个包下面,需要使用import进行导包操作。

  • import可以出现多次。
  • 声明在package之后,在class之前。
  • 导包可以使用通配符*

8.3 静态导入

静态导入(Static Import)是一种导入静态成员(例如静态方法、静态字段或静态枚举常量)的方式,而无需通过类名来引用它们。要使用静态导入,需要使用import static关键字。代码示例如下:

import static com.coder.test.MathUtils.add;

9. 代码封装

代码封装通常指的是使用不同的访问修饰符(Access Modifiers)来控制类、成员变量(属性)和方法的可见性和可访问性。Java提供了四种访问修饰符:

privatedefaultprotectedpublic
同类TTTT
同包FTTT
子类FFTT
通用性FFFT
  • 属性和方法的访问修饰符
    • public:公有的,最高的访问权限,所有类都可以访问,不管是否同包。
    • protected:受保护的,可以被同包的类访问,以及不同包中的子类访问。
    • default(无修饰符,也称为包级私有或默认访问权限):不使用访问修饰符关键字来声明,表示默认,当前类和同包可以访问。
    • private:只能在当前类中访问,不对外公开。
  • 类的访问修饰符
    • public:所有类都可以访问。
    • default:只能当前包中访问。

10. this关键字

  • this.属性名:用于引用当前对象的该属性。
  • this.方法名():用于调用当前对象的该方法。
  • this([参数]):用于调用当前类的另一个构造器,这称为构造器的重载调用或构造器链。必须是构造器(构造方法)中的第一个语句,且只能调用一次。
  • this:用于返回当前对象的引用。

11. 继承关系

11.1 继承的概念

继承是一种机制,它允许一个类(称为子类、派生类或扩展类)继承另一个类(称为父类、超类或基类)的属性和方法。子类可以继承父类的所有非私有成员(即publicprotecteddefault),并且可以选择性地覆盖(@Override)父类的方法,或者添加新的属性和方法。继承关系通常被称为is-a关系。继承的语法格式如下:

class 父类 {
}

class 子类 extends 父类 {
}

11.2 继承类型

Java主要支持的是单一继承(Single Inheritance),即一个类只能直接继承自一个父类。
java继承类型

11.3 继承的特性

Java继承具有以下几个主要特性:

  • 单一继承:Java只支持单继承,即一个类只能有一个直接的父类。虽然直接继承是单一的,但可以通过多层继承来间接地继承多个类的特性。
  • 继承属性和方法:子类可以继承父类的非私有(publicprotected)属性和方法。子类可以访问和操作这些继承来的属性和方法。
  • 方法重写(Override):子类可以提供一个与父类方法签名相同(方法名、参数列表和返回类型相同)但实现不同的方法。当通过子类对象调用该方法时,将执行子类中的方法实现,而不是父类中的方法。
  • 构造方法不被继承:子类不继承父类的构造方法。但子类可以通过调用父类的构造方法来初始化从父类继承的属性。这通常通过super()关键字在子类构造方法中完成。
  • 访问权限:子类可以访问父类的公有(public)和保护(protected)成员,但不能直接访问父类的私有(private)成员,必须通过gettersetter方式进行访问。
  • 多态性:继承是实现多态性的基础之一。多态性允许在运行时根据对象的实际类型来调用相应的方法。例如,父类引用可以指向子类对象,并调用子类重写的方法。
  • super关键字:子类可以通过super关键字来引用父类的属性和方法。这允许子类在需要时访问父类的实现或数据。
  • 初始化顺序:在子类实例化时,首先执行父类的静态代码块,然后执行子类的静态代码块。接着,当创建子类对象时,会先调用父类的构造方法(如果有通过super()显式调用或者隐式调用),然后执行子类的构造方法中的代码。
  • 子类可以添加新的属性和方法:子类可以拥有父类所没有的属性和方法,这扩展了父类的功能。
  • final类和final方法:如果一个类被声明为final,则它不能被继承。同样,如果一个方法被声明为final,则它不能在子类中被重写。

11.4 子类实例化过程

子类实例化过程:加载类(包括父类和子类)——>初始化静态变量和静态初始化块——>分配内存——>调用父类构造函数——>初始化父类实例变量和实例初始化块——>执行父类构造函数体——>初始化子类实例变量和实例初始化块——>执行子类构造函数体——>对象创建完成。

❓问:在子类创建对象时,是否也创建了父类的对象?
✅答:不会创建父类对象,只是创建了父类空间,并进行了初始化操作。

📌

  1. 如果父类有无参的构造方法,子类super()可以省略;
  2. 如果父类中没有无参的构造方法,子类super(参数)不可以省略;
  3. 如果使用super()显式地调用父类的构造方法,要求必须写在子类构造方法中的第一行;
  4. 子类的构造方法中,不能同时出现super()this()

11.5 属性的隐藏

  • 在父类中定义一个属性,在子类中定义了一个同名的属性,在子类中访问,会隐藏父类的属性。
  • 在子类中,有两个同名属性,一个是继承自父类的,使用super属性名访问,一个是子类自己的,使用this属性名访问。
  • 在方法中,如果访问到属性,继承自父类的方法,使用的是父类的属性,子类自己的方法,使用的是子类属性。

代码示例如下:

public class TestPro {
    public static void main(String[] args) {
        Son son = new Son();
        son.showName();
        son.testName();
    }
}
class Father {
    String name = "父类的属性";
    public void testName() {
        System.out.println("3. " + name);
    }
}
class Son extends Father {
    String name = "子类的属性";
    public void showName() {
        System.out.println("1. " + name);
        System.out.println("2. " + super.name + "\t" + this.name);
    }
}

🆚🆚运行结果:

1. 子类的属性
2. 父类的属性	子类的属性
3. 父类的属性
  • 如果父类中有一个静态变量,则被所有子类所共享,其中一个子类修改了值,其他子类访问也是修改后的值。如果子类自己写了一个同名的静态变量,则子类中访问的是自己的变量,不再是父类变量。

代码示例如下:

public class TestPro2 {
    public static void main(String[] args) {
        Son son = new Son();
        son.showName(); // 打印父类的静态变量
        Son2 son2 = new Son2();
        son2.setName(); // 修改静态变量的值
        son.showName();
    }
}
class Father {
    static String name = "父类的属性";
    public void testName() {
        System.out.println("3. " + name);
    }
}
class Son extends Father {
    // static String name = "子类的属性";
    public void showName() {
        System.out.println("4. " + name);
    }
}
class Son2 extends Father {
    public void setName() {
        name = "新子类的属性";
    }
}

🆚🆚运行结果:

4. 父类的属性
4. 新子类的属性

11.6 方法重写(方法覆盖)

方法重写(Override)是面向对象编程的一个重要概念,它发生在子类与父类之间。方法重写的使用有以下要求:

  • 方法签名相同:方法名、参数列表必须相同;
  • 返回类型相同或子类:返回类型必须相同,或者是其子类;
  • 访问权限不能更严格:子类方法的访问权限(publicprotecteddefaultprivate)不能低于父类中被重写的方法的访问权限;
  • 不能重写final方法:如果父类中的方法被声明为final,那么它不能被重写;
  • 不能重写static方法:如果父类中的方法是static的,那么子类中的同名同参数列表的方法并不是覆盖父类方法,而是隐藏了父类方法,因为static方法是与类关联的,而不是与对象关联的,所以它们不参与多态;
  • 使用@Override注解:在Java中,建议在使用重写时加上@Override注解。这样,如果子类的方法不符合重写的要求(比如方法签名不匹配),编译器会报错;
  • 抛出异常不能更广泛:子类方法抛出的异常类型必须是父类方法抛出的异常类型的子集或者相同,子类方法不能抛出比父类方法更多的异常,但可以选择不抛出异常。

📌

  1. 使用方法重写的场景:子类和父类的方法要完成相同功能,但采用不同的算法或公式。
  2. 必须要重写的方法:抽象方法(由abstract修饰)必须在子类中被重写。
  3. 不能重写的方法:构造方法(constructor)、私有方法(由private修饰)、最终方法(由final修饰)、静态方法(由static修饰)。

11.7 抽象方法和抽象类

抽象(Abstraction)是实现设计和实现相分离的一种关键手段。这种分离是面向对象编程(OOP)的基本原则之一,它使得软件设计更加模块化、可维护和可扩展。

  • 抽象方法:使用abstract关键字修饰,只有方法的声明(方法头),没有方法的实现(方法体)。不能是静态方法。
  • 抽象类:使用abstract关键字修饰,可以有抽象方法,也可以有普通方法,也可以有构造方法,也可以没有抽象方法。如果一个类中有抽象方法,则这个类必须是抽象类。抽象类不能实例化。

11.8 final关键字的用法

final可以用来修饰类、方法、变量(包括实例变量、静态变量和局部变量)和常量表达式。以下是final关键字的用法:

  • 定义终结类(最终类)
    • 当一个类定义为final时,就表明不能继承这个类,表示不能产生子类。
    • final类里面的所有方法,都是隐式的final,不会产生子类,也不会覆盖方法,所以不需要在final类中为方法加final关键字。
  • 定义终结方法
    • 不能被子类重写。所有private方法都是隐式的final方法,在private方法中,不需要使用final关键字修饰。
    • final方法可以被重载(同名,参数列表不同)。

代码示例如下:

private int test1() {
    return 0;
}
//...
public final void test2() {
    //...
}
public final void test2(int x) {
    //...
}
  • 定义常量
    • 修饰引用类型:无法更改引用所指向的对象地址,但引用的对象属性可以修改。
    • 修饰基本数据类型:无论是编译期常量还是非编译期常量,一旦它们被初始化,它们的值就不能再被修改。
    • static final:static final的作用主要是用来声明全局常量,这些常量属于类并且其值不能被改变。这样的常量通常在类加载时初始化,并且可以在整个应用程序中通过类名来访问。

代码示例如下:

public class Test {
    // 编译期常量
    final int[] a = {10, 20, 30, 40}; // 引用类型
    final int i = 10; // 基本数据类型
    // 非编译期常量
    Random random = new Random();
    final int j = random.nextInt(10);
    
    public static void main(String[] args) {
        final Student student = new Student(); // 引用类型
        student.setName("张三");
        student.setGender("男");
        student.setName("李四");
        System.out.println(student);
        
        Test t = new Test();
        System.out.println(t.i);
        System.out.println(t.j);
    }
}
class Student {
	//...
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }
}

🆚🆚运行结果:

Student{name='李四', gender='男'}
10
6

📌final不可以和abstract放在一起对方法和类进行修饰。


12. 多态

12.1 多态的概念

在Java中,多态(Polymorphism)是面向对象编程的三大特性之一(封装、继承和多态),它允许我们以统一的方式处理不同类型的对象。以下是多态的必要条件和优点:

  • 多态的必要条件
    • 继承关系:多态需要存在继承关系。
    • 方法重写:子类必须重写父类的方法,以实现自己的特有行为。
    • 父类引用指向子类对象:在程序中使用父类类型的引用变量指向子类对象,即将子类对象赋给父类引用变量。
    • 父类引用调用重写方法:通过父类引用变量调用子类对象中重写的方法。
    • 运行时绑定:通过动态绑定机制,根据对象的实际类型来调用相应的方法。这是实现多态的关键机制之一。
  • 多态的优点
    • 简化代码。
    • 面向抽象编程,不面向具体编程(依赖倒转原则)。
    • 易于扩展,增强代码可读性。

12.2 向上转型(上溯造型)

向上转型(Upcasting)是指将子类的引用赋值给父类类型的变量。由于子类继承了父类的所有属性和方法(除了私有属性和方法),因此父类类型的变量可以引用子类对象。这种转换是自动的,不需要显式地进行类型转换。语法格式如下:

父类类型 对象名 = new 子类类型();

12.3 向下转型(下溯造型)

向下转型(Downcasting)是指将父类类型的引用转换为子类类型的变量。由于父类引用可能指向的是子类对象,也可能是其他子类对象或父类对象本身,因此这种转换是不安全的,因为它可能会导致ClassCastException。为了进行向下转型,我们需要显式地使用类型转换操作符,并且最好使用instanceof关键字进行检查,以确保转换是安全的。语法格式如下:

子类类型 对象名 = (子类类型)父类引用;

代码示例如下:

class Animal {
    void eat() {
        System.out.println("所有的动物都要吃东西");
    }
}
class Dog extends Animal {
    @Override
    void eat() {
        System.out.println("小狗爱吃骨头");
    }
}
class Cat extends Animal {
    @Override
    void eat() {
        System.out.println("小猫爱吃鱼");
    }
}
public class Main {
    public static void main(String[] args) {
        // 向上转型
        Animal animal1 = new Dog();
        animal1.eat();
        Animal animal2 = new Cat();
        // 向下转型
        if (animal2 instanceof Animal) { // 使用instanceof关键字进行检查
            Cat cat = (Cat) animal2;
            cat.eat();
        }
    }
}

🆚🆚运行结果:

小狗爱吃骨头
小猫爱吃鱼

12.4 里式代换原则

里式代换原则(Liskov Substitution Principle,LSP)是面向对象设计的基本原则之一。这个原则强调了在程序设计中,子类应当能够替换其父类并出现在父类能够出现的任何地方,而不会改变程序的行为。里式代换原则要求:

  1. 子类必须完全实现父类的方法;
  2. 父类出现的地方,子类一定可以出现;
  3. 子类出现的地方,父类不一定可以出现。

12.5 instanceof运算符

判断一个对象是否属于某个类(或其父类),返回boolean类型的值,如果属于就返回true,否则返回false

object instanceof Class

其中:object是要测试的对象,Class是要检查的类名。


13. 组合关系

组合(Composition)关系是一种表示“部分-整体”关系的设计概念,它表示一个对象(组合对象)包含另一个对象(被组合对象)作为其实例变量(字段、属性)。这种关系也称为has-a关系,它表示一个对象有另一个对象作为其一部分。代码示例如下:

class Engine {
    // Engine类的属性和方法
    private String type;
    public Engine(String type) {
        this.type = type;
    }
    public String getType() {
        return type;
    }
}
class Car {
    // Car类包含Engine类的实例变量,表示组合关系
    private Engine engine;
    public Car(Engine engine) {
        this.engine = engine;
    }
    public void start() {
        // 使用被组合对象的方法
        System.out.println("汽车开始启动" + engine.getType() + "引擎");
    }
}
public class Main {
    public static void main(String[] args) {
        Engine engine = new Engine("V8");
        Car car = new Car(engine);
        car.start();
    }
}

🆚🆚运行结果:

汽车开始启动V8引擎

📌:合成聚合复用原则(Composite/Aggregate Reuse Principle,CARP)是面向对象设计中的一个重要原则,它强调在复用代码时,应该优先使用对象的组合/聚合关系,而不是通过继承关系来达到复用的目的。


14. 接口

14.1 接口的概念

接口(Interface)是一个完全抽象的类,它不能被实例化,但可以被类实现(Implement)。实现关系通常被称为like-a关系。接口主要用于定义一组方法,这些方法被称为接口方法,这些方法不提供具体的实现,而是由实现接口的类来提供。

14.2 语法格式

接口的语法格式如下:

[修饰符] interface 接口名 [extends 接口名[, 接口名...]] {  
    // 常量(字段)  
    [public] [static] [final] 数据类型 常量名 =;  
    // 抽象方法  
    [public] [abstract] 返回类型 方法名(参数列表);  
    // 默认方法(从JDK 1.8开始)  
    [public] default 返回类型 方法名(参数列表) {  
        // 方法体  
    }  
    // 静态方法(从JDK 1.8开始)  
    [public] static 返回类型 方法名(参数列表) {  
        // 方法体  
    }  
    // 私有方法(从Java 9开始)  
    // 私有静态方法  
    private static 返回类型 方法名(参数列表) {  
        // 方法体  
    }  
    // 私有实例方法(从Java 9开始)  
    private 返回类型 方法名(参数列表) {  
        // 方法体  
    }  
}

类的语法格式如下:

[修饰符] class 类名 [implements 接口名[, 接口名...]] {  
    // 成员变量(字段)  
    [修饰符] 数据类型 变量名;  
    // 构造方法  
    [修饰符] 类名(参数列表) {  
        // 构造方法体  
    }  
    // 方法  
    [修饰符] 返回类型 方法名(参数列表) {  
        // 方法体  
    }  
    // 初始化块(可选)  
    {  
        // 初始化代码  
    }  
    // 内部类、接口等(可选)  
}

其中:

  • 接口的定义:[访问修饰符] interface 接口名{}
  • 接口中的数据成员(变量)默认都是public static final的,也就是公有的静态常量。
  • 接口中的方法默认是public abstract的,因此可以省略这些修饰符。
  • 从Java 8(JDK 1.8)开始,接口中可以包含带有具体实现的方法,称为默认方法,使用default来标记。
  • 从Java 8开始,接口中可以包含静态方法,使用static来标记,并且不能是抽象的。
  • 从Java 9开始,接口中可以包含私有方法(包括私有静态方法和私有实例方法),这些私有方法只能在接口内部使用,并且不能被实现类访问。
  • 接口不是类,没有构造方法,不能实例化。
  • 接口可以实现多继承。
  • 一个接口,可以被多个类实现。
  • 类和接口的关系:class 类名 implements 接口名{}
  • 一个类可以实现多个接口:class 类名 implements 接口1[,接口2...]{}
  • 一个类可以在继承一个父类的同时,实现一个或多个接口:class 类名 extends 父类 implements 接口1[,接口2...]{}
14.3 接口的应用
14.3.1 接口的多态

接口(Interface)是实现多态性(Polymorphism)的一种重要方式。接口的多态实现方式主要依赖于这三个关键概念:接口定义,类实现接口,接口引用指向实现类的对象。代码示例如下:

/* 接口定义 */
interface Shape { // 图形
    void draw();
    void calculateArea();
}
/* 类实现接口 */
class Circle implements Shape { // 圆
    @Override
    public void draw() {
        System.out.println("画个圆");
    }
    @Override
    public void calculateArea() {
        // 实现计算圆面积的代码
    }
}
class Rectangle implements Shape { // 矩形
    @Override
    public void draw() {
        System.out.println("画个矩形");
    }
    @Override
    public void calculateArea() {
        // 实现计算矩形面积的代码
    }
}
public class Main {
    public static void main(String[] args) {
        /* 接口引用指向实现类的对象 */
        Shape shape1 = new Circle(); // Circle 是 Shape 的一个实现
        Shape shape2 = new Rectangle(); // Rectangle 也是 Shape 的一个实现
        // 调用 draw 方法
        shape1.draw();
        shape2.draw();
        // 调用接口中定义的其他方法
        shape1.calculateArea();
        shape2.calculateArea();
    }
}

🆚🆚运行结果:

画个圆
画个矩形
14.3.2 接口与抽象类

接口(Interface)和抽象类(Abstract Class)是两种用于实现多态性和代码重用的重要机制。接口与抽象类的主要区别:

  • 目的:接口主要用于定义对象的行为规范,而抽象类则用于定义对象的共同属性和行为的模板。
  • 方法:接口只包含抽象方法,而抽象类可以包含抽象方法和非抽象方法。
  • 继承与实现:类通过extends关键字继承抽象类,通过implements关键字实现接口。
  • 多重继承:一个类可以实现多个接口,但只能继承一个抽象类(直接继承)。
  • 字段:接口中的字段都是常量(隐式staticfinal),而抽象类可以包含各种类型的字段。
14.3.3 简单工厂模式

简单工厂模式(Simple Factory Pattern)是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,它由一个工厂类根据传入的参数来决定创建哪一种产品类的实例。简单工厂模式是最简单的工厂模式,但它不是一种设计模式,因为它没有遵循开闭原则(Open-Closed Principle),即扩展开放,修改关闭。
简单工厂模式
代码示例如下:

// 定义一个产品接口
interface Product {
    void use();
}
// 实现产品接口的具体产品类
class ProductA implements Product {
    @Override
    public void use() {
        System.out.println("使用产品A");
    }
}
class ProductB implements Product {
    @Override
    public void use() {
        System.out.println("使用产品B");
    }
}
// 工厂类,负责创建产品对象
class SimpleFactory {
    // 静态方法,根据传入的参数创建对应的产品对象
    public static Product createProduct(String type) {
        if ("A".equals(type)) {
            return new ProductA();
        } else if ("B".equals(type)) {
            return new ProductB();
        } else {
            return null; // 或者抛出异常
        }
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 使用工厂类创建产品对象
        Product productA = SimpleFactory.createProduct("A");
        if (productA != null) {
            productA.use(); 
        }
        Product productB = SimpleFactory.createProduct("B");
        if (productB != null) {
            productB.use();
        }
        // 试图创建一个不存在的产品类型
        Product productC = SimpleFactory.createProduct("C");
        if (productC == null) {
            System.out.println("无法创建产品C");
        }
    }
}

🆚🆚运行结果:

使用产品A
使用产品B
无法创建产品C
14.3.4 工厂方法模式

工厂方法模式(Factory Method Pattern)定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建的任务交给它的子类来完成。这样做既保证了核心类的稳定,又使得子类可以根据自身需要来灵活创建对象。
工厂方法模式
代码示例如下:

// 产品接口
interface Product {
    void use();
}
// 具体产品类A
class ProductA implements Product {
    @Override
    public void use() {
        System.out.println("使用产品A");
    }
}
// 具体产品类B
class ProductB implements Product {
    @Override
    public void use() {
        System.out.println("使用产品B");
    }
}
// 工厂接口
interface Factory {
    Product createProduct();
}
// 具体工厂类A
class FactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ProductA();
    }
}
// 具体工厂类B
class FactoryB implements Factory {
    @Override
    public Product createProduct() {
        return new ProductB();
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        Factory factoryA = new FactoryA();
        Product productA = factoryA.createProduct();
        productA.use();
        Factory factoryB = new FactoryB();
        Product productB = factoryB.createProduct();
        productB.use();
    }
}

🆚🆚运行结果:

使用产品A
使用产品B
14.3.5 缺省适配器模式

缺省适配器模式(Default Adapter Pattern)是适配器模式(Adapter Pattern)的一个特殊形式,是一种结构型设计模式,用于处理接口中方法数量过多,或者大部分方法都有默认实现的情况。缺省适配器模式通过提供一个实现了目标接口的缺省适配器类,该类为接口中的每个方法都提供了默认实现,从而允许子类继承该适配器类时只需要关注自己感兴趣的方法,而不需要实现接口中的所有方法。
缺省适配器模式
代码示例如下:

// 目标接口
interface MyInterface {
    void method1();
    void method2();
    void method3();
    // ... 可能还有其他方法
}
// 缺省适配器类,实现了MyInterface接口并提供默认实现
class DefaultAdapter implements MyInterface {
    @Override
    public void method1() {
        System.out.println("Default implementation of method1");
    }
    @Override
    public void method2() {
        System.out.println("Default implementation of method2");
    }
    @Override
    public void method3() {
        System.out.println("Default implementation of method3");
    }
    // ... 其他方法的默认实现
}
// 具体实现类,继承自缺省适配器并覆盖部分方法
class MySpecificImplementation extends DefaultAdapter {
    @Override
    public void method1() {
        // 覆盖默认实现
        System.out.println("Specific implementation of method1");
    }
    // method2 和 method3 使用默认实现
}
// 客户端代码
public class Main {
    public static void main(String[] args) {
        MyInterface myImpl = new MySpecificImplementation();
        myImpl.method1();
        myImpl.method2();
        myImpl.method3();
    }
}

🆚🆚运行结果:

Specific implementation of method1
Default implementation of method2
Default implementation of method3

15. 内部类

15.1 简介

  • 在一个类中,定义另一个类,称作内部类(Inner Class)。
  • 内部类可以访问外部类中的属性和方法,不需要创建外部类的对象。
  • 外部类要访问内部类的属性民和方法,需要创建内部类的对象.
  • 如果内部类有和外部类同名变量和方法,则内部类的变量和方法优先级更高。
  • 外部类,修饰符publicdefault
  • 内部类,修饰符publicprotecteddefaultprivate

15.2 分类

15.2.1 成员内部类(普通内部类)

成员内部类(Member Inner Classes)可以直接访问外部类的变量和方法(包括私有和静态修饰的),不能使用static声明变量和方法的。编译后生成的class文件命名为 “外部类名$内部类名”。不能直接new一个内部类对象,必须使用外部类对象来创建内部类对象。
成员内部类
代码示例如下:

class Test {
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        OuterClass.MemberInner MemberInner = outer.new MemberInner();
        MemberInner.test();
    }
}
class OuterClass {
    private int x = 10;
    private static int y = 20;
    class MemberInner {
        int x = 30;
        public void test() {
            System.out.println(OuterClass.this.x + x + y);
        }
    }
}

🆚🆚运行结果:

60

📌:如果外部类和内部类具有相同名称的成员变量或方法,内部类可以直接访问内部类的成员变量和方法,如果内部类访问外部类的成员变量或方法时,需要this关键字。

15.2.2 静态内部类

静态内部类(Static Inner Classes)使用static修饰的内部类,不能直接访问外部类的非静态成员变量和方法。如果要访问外部类的实例变量和方法,需要创建外部类的实例对象。可以创建静态变量和方法。静态内部类创建对象时,不需要创建外部类的对象,直接可以创建内部类的对象。代码示例如下:
静态内部类
代码示例如下:

class Test1 {
    public static void main(String[] args) {
        OuterStatic.test1(); // 类名.方法名()
        OuterStatic.InnerStatic innerStatic = new OuterStatic.InnerStatic(); // 创建内部类的对象
        innerStatic.test(); // 实例对象名.方法名()
        OuterStatic.InnerStatic.test1();
    }
}
class OuterStatic {
    private int x = 10;
    public static void test1() { // 静态方法
        System.out.println("OuterStatic");
    }
    static class InnerStatic {
        private static int y = 20; // 静态变量
        public static void test1() { // 静态方法
            System.out.println("InnerStatic");
        }
        public void test() { // 实例方法
            test1();
            OuterStatic outer = new OuterStatic();
            System.out.println(outer.x);
        }
    }
}

🆚🆚运行结果:

OuterStatic
InnerStatic
10
InnerStatic
15.2.3 局部内部类

局部内部类(Local Inner Classes)定义在外部类的方法或代码块中。可以直接访问外部类的所有成员变量和方法(包括私有的、静态的),不能使用访问修饰符(地位相当于局部变量),可以使用final修饰,防止其他类去继承。作用范围就在当前的方法或代码块中。
局部内部类
代码示例如下:

class Test2 {
    public static void main(String[] args) {
        OuterLocal outerLocal = new OuterLocal();
        outerLocal.outerMethod();
    }
}
class OuterLocal {
    public void outerMethod() {
        final int localVar = 10; // 保证数据运行的一致性,使用final来修饰
        class InnerLocal {
            public void display() {
                System.out.println("Accessing localVar: " + localVar);
            }
        }
        InnerLocal inner = new InnerLocal();
        inner.display();
    }
}

🆚🆚运行结果:

Accessing localVar: 10
15.2.4 匿名内部类

匿名内部类(Anonymous Inner Classes)是没有类名的内部类,通常用于继承一个类或实现一个接口,并且只需要使用一次。常用于GUI编程中的事件监听器、线程等。匿名内部类不能定义构造函数。语法格式如下:

new <类或接口> <类的主体>

匿名内部类
代码示例如下:
例1:

class Test3 {
    public static void main(String[] args) {
        AddInterface inter = new AddInterface() {
            @Override
            public void add(int x, int y) {
                System.out.println(x + y);
            }
        };
        OuterAnony anony = new OuterAnony();
        anony.testAdd(inter, 10, 20);
    }
}
class OuterAnony {
    public void testAdd(AddInterface addInterface, int x, int y) {
        addInterface.add(x, y);
    }
}
interface AddInterface {
    void add(int x, int y);
}

例2:

class Test3 {
    public static void main(String[] args) {
        OuterAnony anony = new OuterAnony();
        anony.testAdd(new AddInterface() {
            @Override
            public void add(int x, int y) {
                System.out.println(x + y);
            }
        }, 10, 20);
    }
}

例3:

class Test3 {
    public static void main(String[] args) {
        OuterAnony anony = new OuterAnony();
        anony.testAdd((x, y) -> System.out.println(x + y), 10, 20); // Lambda表达式
    }
}

🆚🆚运行结果:

30

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨

以上内容是关于Java面向对象的基础知识,希望对初学者或再次学习者有所帮助,基础打扎实,不怕风吹雨打!如果以上内容有错误或者内容不全,望大家提出!我也会继续写好每一篇博文!
👍👍👍

待续未完
🙊🙊🙊

欢迎观看和提问!!!
👏👏👏

下一篇:Java基础:异常(三)
赞

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1713253.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

灯下黑”挖出国内知名安全平台某BUF的CSRF漏洞

漏洞复现&#xff1a; 漏洞点在删除文章的地方&#xff0c;首先为了测试先发布一篇文章 发布之后我们可以查看文章&#xff0c;注意url中的一串数字&#xff0c;就是这篇文章的id&#xff0c;如下如&#xff1a; 这里的文章id是“271825”&#xff0c;首先抓一下删除文章的数据…

装机数台,依旧还会心念i5-12600KF的性能和性价比优势:

近几个月的时间中&#xff0c; 装机差不多4台电脑&#xff0c;由于工作需要&#xff0c;计划年中再增添一台。 目前市场上英特尔CPU促销非常火爆&#xff0c;第12代、第13代以及第14代的产品在年中有适当的优惠。 年中也是装机的旺季&#xff0c;各种相关配件也相对便宜一些。…

新版IDEA没有办法选择Java8版本解决方法

2023年11月27日后&#xff0c;spring.io 默认不再支持创建jdk1.8的项目 解决方法就是把 Spring的Server URL 改为阿里的。 阿里的Server URL https://start.aliyun.com/ 默认的Server URL https://start.spring.io 阿里的Server URL https://start.aliyun.com/

如何使用宝塔面板搭建Tipask问答社区网站并发布公网远程访问

文章目录 前言1.Tipask网站搭建1.1 Tipask网站下载和安装1.2 Tipask网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3 Cpolar稳定隧道&#xff08;本地设置&#xff09; 3. 公网访问测试4.结语 前…

基于react native的图片放大旋转效果二

基于react native的图片放大旋转效果二 const TaskReceiveModal ({ onClick }) > {const spinValue useRef(new Animated.Value(0)).current;const scaleValue useRef(new Animated.Value(0)).current;const spinAnimation useRef(null);const spin spinValue.interpol…

【YOLOv10的使用】YOLOv10的训练/验证/预测/导出模型/ONNX模型的使用

&#x1f680;&#x1f680;&#x1f680; YOLOv10: 实时端到端的目标检测 性能 YOLOv10比最先进的YOLOv9延迟时间更低&#xff0c;测试结果可以与YOLOv9媲美&#xff0c;可能会成为YOLO系列模型部署的“新选择”。 目录 1 安装 2 训练 3 验证 4 预测 5 导出模型 6 ONNX…

股价飙升:AI PC大变革,联想的“联想时刻”正在缔造?

按照产业的传导逻辑&#xff0c;在颠覆式技术到来之时&#xff0c;当引发这场变革的最核心技术及产品真正进入了产品化、商业化阶段&#xff0c;此时直触需求端的终端厂商&#xff0c;其成长性估算将得到市场的重新预估。 眼下AI PC之于联想就是如此。 5月27日&#xff0c;联…

使用 CNN 训练自己的数据集

CNN&#xff08;练习数据集&#xff09; 1.导包&#xff1a;2.导入数据集&#xff1a;3. 使用image_dataset_from_directory()将数据加载tf.data.Dataset中&#xff1a;4. 查看数据集中的一部分图像&#xff0c;以及它们对应的标签&#xff1a;5.迭代数据集 train_ds&#xff0…

高维数组到向量的转换:两种方法的深度解析

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;高维数组的挑战与需求 二、方法一&#xff1a;使用NumPy库进行展平 示…

HTML+CSS 圆形菜单

效果演示 实现了一个圆形菜单的效果,点击菜单按钮后,菜单项会从菜单按钮中心点向外展开,并且菜单项上有文字链接。可以将这段代码的效果称为“圆形菜单展开效果”。 Code <!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8…

word 替换全部字母和数字为新罗马

步骤1&#xff0c;准备好一份测试文档 Adfafdafdafdafdsafdsafasdfdsa 汇总的时光发生的尬的算法的萨法asdfasfsafda大法师短发沙发上对方阿福的萨法的算法大法大方发达舒服打发到沙发上对方说 打发打发打发的负担啊大方阿道夫大法东方大厦发大水Ameti 1. Adafe我直打大噶特区…

Vue开发者工具安装

通过谷歌应用商店安装&#xff08;国外网站&#xff09; 极简插件下载&#xff08;推荐&#xff09;&#xff1a;下载 → 解压 → 点击左上角的三个小点 → 开发者模式 → 拖拽安装 → 插件详情允许访问文件 https://chrome.zzzmh.cn/index 安装步骤&#xff1a; 安装之后可…

集合的综合练习

自动点名器1&#xff1a;班级里有N个学生&#xff0c;实现随机点名器 public class test {public static void main(String [] args) {ArrayList<String> listnew ArrayList<>();//创建一个集合//在集合中添加元素Collections.addAll(list, "李明",&quo…

618必买的数码好物有哪些?盘点兼具设计与实用的数码好物分享

随着618购物节的到来&#xff0c;数码爱好者们又开始跃跃欲试&#xff0c;期待在这个年度大促中寻找到自己心仪的数码好物&#xff0c;在这个数字化时代&#xff0c;数码产品不仅是我们日常生活的必需品&#xff0c;更是提升生活品质的重要工具&#xff0c;那么在众多的数码产品…

一行命令将已克隆的本地Git仓库推送到内网服务器

一、需求背景 我们公司用gitea搭建了一个git服务器&#xff0c;其中支持win7的最高版本是v1.20.6。 我们公司的电脑在任何时候都不能连接外网&#xff0c;但是希望将一些开源的仓库移植到内网的服务器来。一是有相关代码使用的需求&#xff0c;二是可以建设一个内网能够查阅的…

【数据结构和算法】-动态规划爬楼梯

动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;是运筹学的一个分支&#xff0c;主要用于解决包含重叠子问题和最优子结构性质的问题。它的核心思想是将一个复杂的问题分解为若干个子问题&#xff0c;并保存子问题的解&#xff0c;以便在需要时直接利用&am…

15.Redis之持久化

0.知识引入 mysql的事务,有四个比较核心的特性. 1. 原子性 2.一致性 3.持久性 >(和持久化说的是一回事)【把数据存储在硬盘 >持久把数据存储茌内存上>不持久~】【重启进程/重启主机 之后,数据是否存在!!】 4.隔离性~ Redis 是一个 内存 数据库.把数据存储在内存中的…

运维必备的 Linux文件系统

1 前言 我们来简单看一下Linux系统的磁盘、目录、文件。 2 Linux 文件系统 在 Linux 操作系统中&#xff0c;所有被操作系统管理的资源&#xff0c;例如网络接口卡、磁盘驱动器、打印机、输入输出 设备、普通文件或是目录都被看作是一个文件。 也就是说在 Linux 系统中有…

长文总结 | Python基础知识点,建议收藏

测试基础-Python篇 基础① 变量名命名规则 - 遵循PEP8原则 普通变量&#xff1a;max_value 全局变量&#xff1a;MAX_VALUE 内部变量&#xff1a;_local_var 和关键字重名&#xff1a;class_ 函数名&#xff1a;bar_function 类名&#xff1a;FooClass 布尔类型的变量名…

21天精通FL Studio21.2.8!中文汉化全攻略方法教程

在音乐制作的世界中&#xff0c;有一款软件以其强大的功能和易用性而广受好评&#xff0c;那就是FL Studio。而最新版本的FL Studio 21更是在原有的基础上进行了全面的升级&#xff0c;为我们带来了更多的惊喜。今天&#xff0c;我们就一起来了解一下这款最新的水果软件——FL …