当前位置: 首页 > article >正文

Java基础知识(四) -- 面向对象(中)

1.包

1.3.1 包的作用
  • (1)可以避免类重名:有了包之后,类的全名称就变为:包.类名【便于使用】
  • (2)分类组织管理众多的类【便于管理类】
  • (3)可以控制某些类型或成员的可见范围【控制访问范围】

Java中常见的包:

包名作用
java.langJava语言的核心类,如String、Math、Integer、 System和Thread等,提供常用功能
java.net执行与网络相关的操作的类和接口
java.io提供多种输入/输出功能的类
java.util实用工具类,如集合框架类、日期时间、数组工具类Arrays,文本扫描仪Scanner,随机值产生工具Random
java.textJava格式化相关的类

包的本质就是创建不同的文件夹/目录来保存类文件

1.3.2 如何声明包?
package 包名;

注意:

(1)必须在源文件的代码首行

(2)一个源文件只能有一个声明包的语句

包名命名规范与习惯:

    1. 所有单词都小写,每一个单词之间使用.分割
    1. 习惯用公司的域名倒置
1.3.3 如何跨包使用类?

前提:被使用的类或成员的权限修饰符是>缺省的,即可见的。

    1. 使用类型的全名称
    java.util.Scanner input = new java.util.Scanner(System.in);
    
    1. 使用import 语句之后,代码中使用简名称
    import.类名;
    import.*;
    import static.类名.静态成员;
    

2.访问修饰符

2.1 概述

  Java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):

  1. 公开级别:用 public 修饰,对外公开

  2. 受保护级别:用 protected 修饰,对子类和同一个包中的类公开

  3. 默认级别:没有修饰符号,向同一个包的类公开.

  4. 私有级别:用 private 修饰,只有类本身可以访问,不对外公开.

2.2 访问修饰符的访问范围

访问级别访问修饰控制符同类同包子类不同包
公开public
受保护protected×
默认没有修饰符××
私有private×××

## 3.面向对象编程-封装

3.1 概述

  面向对象编程语言是对客观世界的模拟,客观世界里每一个事物的内部信息都是隐藏在对象内部的,外界无法直接操作和修改,只能通过指定的方式进行访问和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。正如现实生活中,每一个个体与个体之间是有边界的,每一个团体与团体之间是有边界的,而同一个个体、团体内部的信息是互通的,只是对外有所隐瞒。

  随着业务系统越来越复杂,类会越来越多,那么类之间的访问边界必须把握好,面向对象的开发原则要遵循“高内聚、低耦合”,而“高内聚,低耦合”的体现之一:

  • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
  • 低耦合:仅对外暴露少量的方法用于使用

  隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

Q:封装就是把该隐藏的隐藏起来,该暴露的暴露出来。那么暴露的程度如何控制呢?

A: 依赖访问控制修饰符,也称为权限修饰符来控制。访问控制修饰符来控制相应的可见边界,边界有如下:类、包、子类、模块(Java9之后引入)。权限修饰符:public,protected,缺省,private。

3.2 封装的实现

    1. 将属性私有化private
    1. 提供一个公共的(public)set方法, 用于对属性判断并赋值
    public void setXxx(类型 参数名){
        //加入业务逻辑判断
        属性 = 参数名;
    }
    
    1. 提供一个公共的(public)get方法, 用于获取属性值
    public void getXxx(){
        return xx;
    }
    

3.3 成员变量/属性私有化问题

  成员变量(field)私有化之后,提供标准的get/set方法,我们把这种成员变量也称为属性(property)。 或者可以说只要能通过get/set操作的就是事物的属性,哪怕它没有对应的成员变量。

3.3.1 成员变量封装目的
  • 隐藏类的实现细节
  • 保证对象信息的完整性,提高代码的安全性。让使用者只能通过事先预定的方法来访问数据,从而可在该方法里面加入控制逻辑,限制对成员变量的不合理访问。还可以进行数据检查,从而有利于保证对象信息的完整性。
  • 便于修改,提高代码的可维护性。主要体现在隐藏的部分,在内部修改了,如果其对外可以的访问方式不变,外部根本感觉不到它的修改。例如:Java8->Java9,String从char[]转为byte[]内部实现,而对外的方法不变,使用者根本感觉不到它内部的修改。
3.3.2 成员变量封装实现
  1. 使用 private 修饰成员变量

    public class Chinese{
        private static String Country;
        private String name;
        private int age;
        private boolean marry;
    }
    
  2. 提供 getXxx方法 / setXxx 方法,可以访问成员变量

    package JavaBase;
    
    public class Chinese {
        private static String country;
        private String name;
        private int age;
        private boolean marry;
    
        public static void setCountry(String c) {
            country = c;
        }
    
        public static String getCountry() {
            return country;
        }
    
        public void setName(String n) {
            name = n;
        }
    
        public String getName() {
            return name;
        }
    
        public void setAge(int a) {
            age = a;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setMarry(boolean m) {
            marry = m;
        }
        
         public boolean isMarry(){
            return marry;
        }
    }
    
3.3.3 如何解决局部变量与成员变量同名问题

当局部变量与类变量(静态成员变量)同名时,在类变量前面加“类名.";

当局部变量与实例变量(非静态成员变量)同名时,在实例变量前面加“this.”。

public class Chinese {
    private static String country;
    private String name;
    private int age;

    public static void setCountry(String country) {
        Chinese.country = country;
    }

    public static String getCountry() {
        return country;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}

4.面向对象编程-继承

4.1 概述

  多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类中无需再定义这些属性和行为,只需要和抽取出来的类构成某种关系。如图所示:
在这里插入图片描述
在这里插入图片描述

其中,多个类可以称为子类,也叫派生类;多个类抽取出来的这个类称为父类超类(superclass)或者基类。继承描述的是事物之间的所属关系,这种关系是:is-a 的关系。例如,图中猫属于动物,狗也属于动物。可见,父类更通用,子类更具体。通过继承,可以使多种事物之间形成一种关系体系。

  继承可以解决代码复用,让编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可。
在这里插入图片描述

4.2 继承的好处

  • 提高代码的复用性
  • 提高代码的扩展性
  • 类与类之间产生了关系,是学习多态的前提

4.3 继承的格式

【修饰符】 class 父类 {
	...
}

【修饰符】 class 子类 extends 父类 {
	...
}

示例1:

父类:

package JavaBase.extend.extend01;

// 父类:Pupil 和 Graduate 的父类
public class Student {
    // 公共属性
    public String name;
    public int age;
    private double score;

    // 公共方法
    public void setScore(double score) {
        this.score = score;
    }

    public void showInfo() {
        System.out.println("学生名 " + name + " 年龄 " + age + " 成绩 " + score);
    }
}

子类:

package JavaBase.extend.extend01;

public class Pupil extends Student {
    public void testing() {
        System.out.println("小学生" + name + "正在考小学数学..");
    }
}

package JavaBase.extend.extend01;

public class Graduate extends Student {
    public void testing() {
        System.out.println("大学生" + name + "正在考大学数学..");
    }
}

测试类:

package JavaBase.extend.extend02;

import JavaBase.extend.extend01.Graduate;
import JavaBase.extend.extend01.Pupil;

public class Extends01 {
    public static void main(String[] args) {
        Pupil pupil = new Pupil();
        pupil.name = "银角大王~";
        pupil.age = 11;
        pupil.testing();
        pupil.setScore(50);
        pupil.showInfo();


        System.out.println("===========================");
        Graduate graduate = new Graduate();
        graduate.name="金角大王~";
        graduate.age = 23;
        graduate.testing();
        graduate.setScore(80);
        graduate.showInfo();
    }
}
/*
小学生银角大王~正在考小学数学..
学生名 银角大王~ 年龄 11 成绩 50.0
===========================
大学生金角大王~正在考大学数学..
学生名 金角大王~ 年龄 23 成绩 80.0
*/

4.4 理解继承

    1. 子类继承父类所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问。
    1. 子类必须调用父类的构造器, 完成父类的初始化。
    1. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不通过。
    1. 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)。
    1. super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)。
    1. super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器。
    1. Java 所有类都是 Object 类的子类, Object 是所有类的基类。
    1. 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)。
    1. 子类最多只能继承一个父类(指直接继承),即 Java 中是单继承机制。
    1. 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系。

4.5 继承的本质分析

示例:

public class GrandPa {
    String name = "大头爷爷";
    String hobby = "旅游";
}

public class Father extends GrandPa {
    String name = "大头爸爸";
    private int age = 39;

    public int getAge(){
        return age;
    }
}

public class Son extends Father{
    String name = "大头儿子";
}

public class ExtendTheory {
    public static void main(String[] args) {
        Son son = new Son();
        System.out.println(son.name);// 大头儿子
        System.out.println(son.getAge()); //39
        System.out.println(son.hobby);//旅游
    }
}

从以上的案例可以看出来:

  • (1) 首先看子类是否有该属性
  • (2) 如果子类有这个属性,并且可以访问,则返回信息
  • (3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息…)
  • (4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object…

在这里插入图片描述

5.super关键字

5.1 概述

super 代表父类的引用,用于访问父类的属性、方法、构造器

5.2 基本语法

  • 1.访问父类的属性,但不能访问父类的private属性

    super.属性名;
    
  • 2.访问父类的方法,不能访问父类的private方法

     super.方法名(参数列表);
    
  • 3.访问父类的构造器

    super(参数列表)//只能放在构造器的第一句,只能出现一句!
    

5.3 super的好处

  • 1.调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
  • 2.当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果!
  • 3.super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。

5.4 super 与 this 比较

编号区别点thissuper
1访问属性访问本类中的属性,如果本类中没有此属性则从父类中继续查找从父类开始查找属性
2调用方法访问本类中的方法,如果本类中没有此方法则从父类中继续查找从父类开始查找方法
3调用构造器调用本类构造器,必须放在构造器的首行调用父类的构造器,必须放在子类构造器的首行
4特殊表示当前对象子类中访问父类对象

6.方法重写/覆盖(override)

6.1 概述

  简单的说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法。

示例

public class Animal {
    public void cry() {

        System.out.println("动物叫唤..");
    }

    public Object m1() {

        return null;
    }

    public String m2() {

        return null;
    }

    protected void eat() {
    }
}

public class Dog extends Animal {
    //1. 因为 Dog 是 Animal 子类
    //2. Dog 的 cry 方法和 Animal 的 cry 定义形式一样(名称、返回类型、参数)
    //3. 这时我们就说 Dog 的 cry 方法,重写了 Animal 的 cry 方法
    public void cry() {

        System.out.println("小狗汪汪叫..");
    }
}

public class Override01 {
    public static void main(String[] args) {
//演示方法重写的情况
        Dog dog = new Dog();
        dog.cry();// 小狗汪汪叫..
    }
}

6.2 方法重写的注意事项

方法重写也叫方法覆盖,需要满足下面的条件:

  • 1.子类的方法的形参列表,方法名称,要和父类方法的形参列表、方法名称完全一样。
  • 2.子类方法的返回值类型和父类方法的返回值类型完全一样,或者是父类返回值类型的子类。
  • 3.子类方法不能缩小父类方法的访问权限(public>protected>默认>private)。

6.3 方法重写与重载的区别

名称发生范围方法名形参列表返回值类型修饰符
重载(overload)本类必须一样类型、个数或顺序至少有一个不同无要求无要求
重写(overwride)父子类必须一样相同子类重写的方法,返回值类型和父类返回值类型一致,或者其子类子类不能缩小父类的方法的访问范围

7.面向对象编程-多态

7.1 概述

  在Java中,多态是面向对象编程中的一个重要概念,它允许不同类型的对象对同一方法进行不同的实现。具体来说,多态性指的是通过父类的引用变量来引用子类的对象,从而实现对不同对象的统一操作。方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

例如:狗和猫都是动物,动物共同的行为都有吃这个动作,而狗可以表现为啃骨头,猫则可以表现为吃老鼠。这就是多态的表现,即同一件事情,发生在不同对象的身上,就会产生不同的结果。

public class Animal {
    public void cry(){
        System.out.println("Animal Cry() 动物在叫...");
    }
}

public class Dog extends Animal{
    public void cry(){
        System.out.println("Dog Cry() 小狗汪汪叫...");
    }
}

public class Cat extends Animal{
    public void cry(){
        System.out.println("Cat cry() 小猫喵喵叫...");
    }
}

public class PolyObject {
    public static void main(String[] args) {
        //animal1 编译类型就是 Animal , 运行类型 Dog
        Animal animal1 = new Dog();
        animal1.cry(); // Dog Cry() 小狗汪汪叫...
        //animal2 编译类型就是 Animal , 运行类型 Cat
        Animal animal2 = new Cat();
        animal2.cry(); // Cat cry() 小猫喵喵叫...
    }
}

注:

    1. 一个对象的变编译类型和运行类型可以不一致。
    1. 编译类型在定义对象时, 就确定了,不能改变。
    1. 运行类型是可以变化的。
    1. 编译类型看定义时 = 的左边, 运行类型看 = 号的右边。

7.2 多态实现的条件

在Java中,要实现多态性,就必须满足以下条件:

    1. 继承关系
    • 存在继承关系的类之间才能够使用多态性。多态性通常通过一个父类用变量引用子类对象来实现。
    1. 方法重写
    • 子类必须重写(Override)父类的方法。通过在子类中重新定义和实现父类的方法,可以根据子类的特点行为改变这个方法的行为,如猫和狗吃东西的独特行为。
    1. 父类引用指向子类对象
    • 使用父类的引用变量来引用子类对象。这样可以实现对不同类型的对象的统一操作,而具体调用哪个子类的方法会在运行时多态决定

多态的向上转型

  1. 本质:父类引用指向了子类的对象

  2. 语法:父类类型 引用名 = new 子类类型();

  3. 特点:① 编译类型看左边,运行类型看右边

​ ② 可以调用父类中所有的成员(遵守访问权限)

​ ③ 不能调用子类中特有成员

public class Animal {
    String name = "动物";
    int age = 10;

    public void sleep() {
        System.out.println("睡");
    }

    public void run() {
        System.out.println("跑");
    }

    public void eat() {
        System.out.println("吃");
    }

    public void show() {
        System.out.println("hello,你好");
    }
}

public class Cat extends Animal {
    @Override
    public void eat() {//方法重写
        System.out.println("猫吃鱼");
    }

    public void catchMouse() {//Cat 特有方法
        System.out.println("猫抓老鼠");
    }
}

public class PolyObject {
    public static void main(String[] args) {
        //向上转型: 父类的引用指向了子类的对象
        //语法:父类类型引用名 = new 子类类型();
        Animal animal = new Cat();
        animal.eat();
        animal.sleep();
        animal.show();
        animal.run();
    }
}

多态的向下转型

  1. 语法:子类类型 引用名 = (子类类型) 父类引用

  2. 特点:① 只能强转父类引用,但是不能父类对象

​ ② 要求父类的引用必须指向的是当前目标类型的对象

​ ③ 当向下转型后,可以调用子类类型中所有的成员

// 现在想调用Cat类中的catchMouse 方法--> 向下转型
public class PolyObject {
    public static void main(String[] args) {
        //向上转型: 父类的引用指向了子类的对象
        //语法:父类类型引用名 = new 子类类型();
        Animal animal = new Cat();
        animal.eat();
        animal.sleep();
        animal.show();
        animal.run();
        Cat cat = (Cat) animal;
        cat.catchMouse();
    }
}

8.Java的动态绑定机制

    1. 当调用对象方法时, 该方法会和该对象的内存地址/运行类型绑定。
    1. 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。

示例如下:

public class A {
    public int i = 10;

    public int sum() {
        return getI() + 10;
    }

    public int sum1() {
        return i + 10;
    }

    public int getI() {
        return i;
    }
}


public class B extends A {
    public int i = 20;

    public int sum() {
        return getI() + 20;//20+20
    }

    public int sum1() {
        return i + 10;// 20+10
    }

    public int getI() {
        return i;
    }
}

public class DynamicBinding {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum());// 40
        System.out.println(a.sum1());// 30
    }
}


public class A {
    public int i = 10;

    public int sum() {
        return getI() + 10;//20+10
    }

    public int sum1() {
        return i + 10;//10+10
    }

    public int getI() {
        return i;
    }
}


public class B extends A {
    public int i = 20;

    public int getI() {
        return i;//20
    }
}

public class DynamicBinding {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum());// 30
        System.out.println(a.sum1());// 20
    }
}

9.Object类详解

9.1 equals方法

  ==和 equals 的对比

  • == 是一个比较运算符, 既可以判断基本类型, 又可以判断引用类型。如果判断基本类型,判断的是值是否相等。如果判断引用类型,判断的是地址是否相等,判定是不是同一个对象。
  • equals是Object类中的方法,只能判断引用数据类型。默认是判断地址是否相等,子类往往重写该方法,用于判断内容是否相等。

示例:

/***
 *
 * 判断两个 Person 对象的内容是否相等,
 * 如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false
 */
public class Person {
    private String name;
    private int age;
    private String gender;

    @Override
    public boolean equals(Object obj) {
        //判断如果比较的两个对象是同一个对象,则直接返回 t
        if (this == obj) {
            System.out.println("同一个对象");
            return true;
        }

        //类型判断
        if (obj instanceof Person) {
            //进行 向下转型, 因为需要得到obj的各个属性
            Person person = (Person) obj;
            System.out.println("不是同一个对象");
            return this.name.equals(person.name) && this.age == person.age && this.gender.equals(person.gender);
        }
        //如果不是 Person ,则直接返回 false
        return false;
    }

    public Person(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}



public class EqualObj {
    public static void main(String[] args) {
        Person person1 = new Person("jack", 10, "男");
        Person person2 = new Person("jack", 20, "男");
        Person person3 = new Person("jack", 10, "男");
        System.out.println(person1.equals(person1));
        System.out.println("=================================");
        System.out.println(person1.equals(person2));
        System.out.println("=================================");
        System.out.println(person1.equals(person3));
    }
}

9.2 hashCode方法

  hashCode方法返回该对象的哈希码值。实际上, 由Object类定义的hashCode方法会针对不同的对象返回不同的整数[一般通过将该对象的内部地址转换成一个整数来实现]。该方法是提高具有哈希结构的容器效率。两个引用, 如果指向的是同一个对象, 则哈希值肯定是相同的, 反之哈希值是不同的。

示例:

public class HashCode {
    public static void main(String[] args) {
        Person person1 = new Person();
        Person person2 = new Person();
        Person person3 = person1;
        System.out.println("person1的哈希值: " + person1.hashCode());
        System.out.println("person2的哈希值: " + person2.hashCode());
        System.out.println("person3的哈希值: " + person3.hashCode());
    }
}
/*
person1的哈希值: 1163157884
person2的哈希值: 1956725890
person3的哈希值: 1163157884
*/

9.3 toString方法

  该方法返回对象的字符串表示形式。Object类的toString方法返回一个包含该类的对象是一个实例的名称字符串的符号` @ ',和符号进制表示的对象的哈希码[全类名+@+哈希值的十六进制]。换句话说,此方法返回一个等于值的字符串:getClass().getName() + ‘@’ + Integer.toHexString(hashCode())。子类往往重写toString方法,用于返回对象的属性信息, 重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式。

public class Monster {
    private String name;
    private String job;
    private double salary;

    //重写toString方法,输出对象的属性
    @Override
    public String toString() {
        return "Monster {name=" + name + ", job=" + job + ", salary=" + salary + "}";
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("fin....");
    }

    public Monster() {
    }

    public Monster(String name, String job, double salary) {
        this.name = name;
        this.job = job;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
}


public class ToString_ {
    public static void main(String[] args) {
        Monster monster = new Monster("小妖怪", "巡山", 1000);
        System.out.println(monster.toString() + "\nhashcode=" + monster.hashCode());

        System.out.println("==当直接输出一个对象时,toString方法会被默认的调用==");
        System.out.println(monster);//等价monster.toString()
    }
}

10.总结

在这里插入图片描述


http://www.kler.cn/a/442029.html

相关文章:

  • 解锁辅助驾驶新境界:基于昇腾 AI 异构计算架构 CANN 的应用探秘
  • 二叉搜索树(TreeMapTreeSet)
  • C++第十五讲:异常
  • WPS数据分析000004
  • nginx 配置防爬虫
  • 【青蛙过河——思维】
  • 量化交易实操入门
  • SQLite 安装与使用
  • vue的elementUI 给输入框绑定enter事件失效
  • 【C语言】指针数组和数组指针
  • 25上半年软考《电子商务设计师》,备考大纲已出!
  • 为什么 Teams 中搜索不到 Power Automate
  • 电脑开机提示error loading operating system怎么修复?
  • 新手谷歌浏览器的使用(使用国内的搜索引擎)
  • lc238除自身以外数组的乘积——动态规划前缀积
  • Java全栈项目 - 智能小区物业管理平台开发实践
  • 新知DAC维修,换牛,
  • Rust操作符和符号全解析
  • Java对集合的操作方法
  • 面试小札:闪电五连鞭_7
  • opencv # Sobel算子、Laplacian算子、Canny边缘检测、findContours、drawContours绘制轮廓、外接矩形
  • Sentry日志管理thinkphp8 tp8 sentry9 sentry8 php8.x配置步骤, tp8自定义异常处理类使用方法
  • NSDT 3DConvert:高效实现大模型文件在线预览与转换
  • 关于llama2:从原始llama-2-7b到llama-2-7b-hf的权重转换教程
  • cesium 与 threejs 对比
  • attack xv6