JAVA面向对象(下 )(一、继承和方法重写)

news2024/11/24 7:33:28

一、继承

1.1 什么是继承

生活中继承是指:

  • 继承财产=>延续财产

  • 继承/遗传 父母的长相,基因 => 延续上一代的基因

  • 继承技能、专业、职位 =>延续

  • 继承中华民族的传统文化 => 延续

  • 青出于蓝而胜于蓝 或 长江后浪推前浪,前浪被拍在沙滩上 => 下一代比上一代更厉害

Java中继承是指:

  • 代码的复用:子类可以继续使用父类已经声明过的代码

  • 代码的扩展:子类比父类对事物的描述更具体,更丰富,(属性或功能更多)

  • is-a 的关系:子类是父类事物的一个分支

示例代码
父类Person
package com.atguigu.inherited;

public class Person {//父类
    //属性
    public String name;
    public int age;

    //方法
    public void eat(){
        System.out.println(name+"现在是" + age +"岁,在吃东西");
    }
}
子类Student
package com.atguigu.inherited;

//Student是子类
//Person是父类
//子类继承了父类的成员,代码的复用
//子类和父类不同,子类的成员比父类多,子类更具体
public class Student extends Person{
    //属性
    public int score;//成绩

    //方法
    public void exam(){
        System.out.println(name+"现在是" + age +"岁,在在考试");
    }
}
测试类TestStudent
package com.atguigu.inherited;

public class TestStudent {
    public static void main(String[] args) {
        //Person对象只能访问name,age属性,调用eat()方法
        Person p = new Person();
        p.name = "李四";
        p.age = 24;
        p.eat();

        System.out.println("=========================");

        //Student对象可以访问name,age,score属性,调用eat()和exam()方法
        Student s = new Student();
        s.name = "张三";
        s.age = 23;
        s.score = 99;//父类已经声明过了,从父类继承的

        s.eat();//父类已经声明过了,从父类继承的
        s.exam();
    }
}

1.2 如何继承?

语法格式:

【修饰符】 class 父类名{
    
}
【修饰符】 class 子类名 extends  父类名{ //extends是关键字
    
}

父类:SuperClass,又称为超类,基类。

子类:SubClass,又称为派生类。

extends:扩展

1.3 继承有什么特点或要求

1、Java中只支持单继承

比喻:每一个人只有一个亲生父亲。

【修饰符】 class 子类名 extends  父类名1, 父类名2{ //错误
    
}

2、Java中支持多层继承

比喻:代代相传。

解释:父类也可以有父类,父类的父类对于这个子类来说也是父类。

public class A{
    
}
public class B extends A{
    
}
public class C extends B{
    
}
//B是C的父类,它是C的直接父类。
//A也是C的父类,它是C的间接父类。
//B会继承A的成员,然后C会继续继承B的成员。C的成员最多。

3、同一个父类可以同时有多个子类

比喻:支持多胎。

public class A{
    
}
public class B extends A{
    
}
public class C extends A{
    
}
//B和C同时都是A的子类,而且是并列关系。B和C是兄弟类关系。

4、父类的所有成员变量、成员方法会继承到子类吗?

父类的所有成员变量、成员方法,都会继承到子类中。但是,父类中私有的成员变量,成员方法,子类不能直接使用,如果子类需要使用,直接通过间接的方式使用,例如:通过get/set方式。

Father类
package com.atguigu.inherited;

public class Father {//父类
    //这四个属性的权限修饰符是不同的
    public int a;
    int b;
    protected int c;
    private int d;

    public int getD(){
        return d;
    }

    public void setD(int d) {
        this.d = d;
    }
}
Son类
package com.atguigu.inherited;

//Son是子类,Father是父类
public class Son extends Father {
    private int e;

    public void setE(int e) {
        this.e = e;
    }

    public String getInfo() {
//        return "a = " + a + ",b = " + b + ",c= " + c + ",d = " + d + ",e = " + e;
        //d因为在父类中是private修饰,所以在子类中无法直接使用
        return "a = " + a + ",b = " + b + ",c= " + c + ",d = " + getD() + ",e = " + e;
    }
}
测试类TestSon
package com.atguigu.inherited;

public class TestSon {
    public static void main(String[] args) {
        Son s = new Son();
        s.a = 1;
        s.b = 1;
        s.c = 1;
//        s.d = 1;
//        s.e = 1;
        s.setD(1);
        s.setE(1);
        System.out.println(s.getInfo());
    }
}

5、父类的构造器会继承到子类吗?

父类的构造器不会继承到子类中,但是,子类的构造器中必须调用父类的构造器。

  • 不会继承的原因:父类的构造器是用来创建父类的对象的

  • 必须调用的原因:父类的构造器中 编写了 为父类中声明的这些属性初始化的代码,那么子类会继承这些属性,就需要“复用”父类构造器的这些代码为它们初始化。

说明:

  • 子类的构造器==默认==去找父类的无参构造。

  • 当然,子类构造器也可以通过 super(); 或 super(实参列表); 来明确子类找父类的哪个构造器

    • super(); 明确调用父类的无参构造

    • super(实参列表); 明确调用父类的有参构造

父类Animal
package com.atguigu.inherited;

public class Animal {//父类,Animal:动物
    private String name;
    private int age;

    public Animal() {
        System.out.println("父类Animal的无参构造");
    }

    public Animal(String name, int age) {
        System.out.println("父类Animal的有参构造");
        this.name = name;
        this.age = 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;
    }
}
子类Dog
package com.atguigu.inherited;

//Dog是子类
//Animal是父类
public class Dog extends Animal{//Dog:狗
    private double weight;//重量

    public Dog() {
        super();//这句话可以省略,表示默认调用父类的无参构造
        //super("小黑",1);
        System.out.println("子类Dog的无参构造");
    }

    public Dog(String name, int age, double weight) {
        super(name, age);//明确说明调用父类的有参构造,为name和age属性初始化
        //super();
        System.out.println("子类Dog的有参构造");
        this.weight = weight;
    }
}
测试类TestDog
package com.atguigu.inherited;

public class TestDog {
    public static void main(String[] args) {
        Dog d1 = new Dog();
        Dog d2 = new Dog("小白",3,5.6);
    }
}
思考题1

问:子类的无参构造一定找父类的无参构造,子类的有参构造一定找父类的有参构造?没有

思考题2

问:父类没有无参构造,会怎么样?

6、子类可以重写父类的方法

请看$1.4小节。

1.4 方法的重写

如果父类的某个成员方法,继承到子类后,子类认为该方法的方法体功能实现不适合子类,子类可以选择对其进行重写(Override)。

1.4.1 重载与重写的区别

方法的重载(Overload)方法的重写(Override)
2个或多个方法的位置在同一个类中 或 父子类中父子类中
修饰符不看必须满足> 或 =的关系
返回值类型不看void:必须相同 基本数据类型:必须相同 引用数据类型:必须满足 < 或 = 的关系
方法名必须相同必须相同
(形参列表)必须不同。个数、类型、顺序不同,和形参名无关。必须相同。个数、类型、顺序相同,和形参名无关。
{方法体}不看必须重写实现

示例代码1
父类Fu1
package com.atguigu.override;

public class Fu1 {//父类
    public void m1(){
        System.out.println("Fu.m1方法");
    }

    protected void m2(){
        System.out.println("Fu.m2方法");
    }

     void m3(){
        System.out.println("Fu.m3方法");
    }

    private void m4(){
        System.out.println("Fu.m4方法");
    }
}
子类Zi1
package com.atguigu.override;

//Zi是子类,Fu是父类
public class Zi1 extends Fu1{
    //因为父类Fu1的m1方法的权限修饰符是public
    //子类Zi1的m1方法,权限修饰符必须是public
    @Override
    public void m1(){
        System.out.println("Zi.m1方法");
    }

    //因为父类Fu1的m2方法的权限修饰符是protected
    //子类Zi1的m2方法,权限修饰符可以是public 或 protected
    @Override
    public void m2(){
        System.out.println("Zi.m2方法");
    }

    //因为父类Fu1的m3方法的权限修饰符是缺省
    //子类Zi1的m3方法,权限修饰符可以是public 或 protected 或缺省
    @Override
    void m3(){
        System.out.println("Zi.m3方法");
    }

    //不是重写,因为父类Fu1的m4方法,在子类中不可见
    //@Override //加它会报错,因为现在的m4方法不是重写,它只是子类Zi1自己定义的一个方法
    private void m4(){
        System.out.println("Zi.m4方法");
    }
}
父类Fu2
package com.atguigu.override;

public class Fu2 {
    public void m1(){
        System.out.println("Fu.m1的方法");
    }

    public int m2(){
        System.out.println("Fu.m1的方法");
        return 0;
    }

    public Object m3(){
        System.out.println("Fu.m3的方法");
        return new Object();
    }

    public String m4(){
        System.out.println("Fu.m4的方法");
        return new String();
    }
}
子类Zi2
package com.atguigu.override;

public class Zi2 extends Fu2{
    //因为Fu2类的m1方法的返回值类型是void
    //Zi2的m1方法的返回值类型只能是void
    public void m1(){
        System.out.println("Zi.m1的方法");
    }

    //因为Fu2类的m2方法的返回值类型是int
    //Zi2的m2方法的返回值类型只能是int
    public int m2(){
        System.out.println("Zi.m2的方法");
        return 1;
    }

    //因为Fu2类的m3方法的返回值类型是Object
    //Zi2的m3方法的返回值类型可以是Object,或 Object的子类
    //String < Object
    public String m3(){
        System.out.println("Zi.m3的方法");
        return  new String();
    }

    //因为Fu2类的m4方法的返回值类型是String
    //Zi2的m4方法的返回值类型可以是String,或String的子类
    //很遗憾,String没有子类,它是太监类
    public String m4(){
        System.out.println("Zi.m4的方法");
        return new String();
    }
}
父类Fu3
package com.atguigu.override;

public class Fu3 {
    public void m1(){
        System.out.println("Fu.m1");
    }

    public void m2(int a, int b){
        System.out.println("Fu.m2");
    }

    public void m3(int a, int b){
        System.out.println("Fu.m3");
    }
}
子类Zi3
package com.atguigu.override;

public class Zi3 extends Fu3{
    //因为Fu3的m1方法的形参列表是(),
    //那么Zi3的m1方法如果是()就是重写,如果是(非空)就是重载
    public void m1(int a){
        System.out.println("Zi.m1");
    }

    //因为Fu3的m2方法的形参列表是(int a, int b),
    //那么Zi3的m2方法的形参列表(int x, int y)
    //它们是重写关系,不看形参的名字,
    //只看类型,个数,顺序
    public void m2(int x, int y){
        System.out.println("Zi.m2");
    }

    //因为Fu3的m3方法的形参列表是(int a, int b),
    //那么Zi3的m3方法的形参列表(int x)
    //它们是重载关系,不看形参的名字,
    //只看类型,个数,顺序
    public void m3(int x){
        System.out.println("Zi.m3");
    }
}

1.4.2 @Override有什么用

@Override是一个注解。是对代码进行注释。这个注释是可以给编译器看的,
编译器看到某个方法上面加了@Override,就会对这个方法按照重写的要求,
进行格式检查,看他是否满足重写的要求,不满足就编译报错,如果满足就
不报编译错。
但是,如果一个重写的方法,没有违反重写的要求,那么加@Override
和不加它没有任何区别。

总结:@Override只是对重写方法起到一个格式检查的作用,不影响重写的本质。

建议:重写的方法都加上@Override。

1.5 super关键字

super可以调用父类声明的xxx成员。

1、super() 或 super(实参列表)

它们会出现在子类构造器的首行。表示调用父类的构造器。它只能找直接父类的。

  • super():找父类无参构造。

  • super(实参列表):找父类的有参构造。

示例代码
GrandFather爷爷类
package com.atguigu.keyword;

public class GrandFather {//爷爷类
    private int a;

    public GrandFather() {
        System.out.println("GrandFather的构造器");
    }

    public GrandFather(int a) {
        this.a = a;
    }
}

 

Father父类
package com.atguigu.keyword;

public class Father extends GrandFather{//父类
    public Father(){
        System.out.println("Father的构造");
    }
}

 

GrandSon孙子类

 


public class GrandSon extends Father{
    public GrandSon() {
        super();
        System.out.println("GrandSon的构造器");
    }

    public GrandSon(int a){
//        super(a);//报错,因为Father类没有有参构造
    }
}

测试类TestGrandSon

package com.atguigu.keyword;

public class TestGrandSon {
    public static void main(String[] args) {
        GrandSon g = new GrandSon();

        /*
        从构造器的角度来说,
        孙子要先找爸爸,爸爸再找爷爷。

        但是不能孙子直接找爷爷的构造器。
         */
    }
}

2、super.方法

当子类重写了父类的某个方法,但是在子类中又要调用父类被重写的方法,就必须用“super.被重写的方法”,否则就会自己调用自己的。

如果子类没有重写父类的某个方法,在子类中需要调用这个父类的方法,加不加super.都可以。

3、super.成员变量

super.成员变量是表示使用父类的成员变量。

当父类的成员变量,与子类的成员变量重名了,可以用"super.成员变量"表示使用父类的成员变量。

如果父类的成员变量,与子类的成员变量没有重名问题,可以直接使用父类的成员变量。

虽然我们讲了这种使用方式,但是开发中一定要避免父子类的成员变量重名。

示例代码

父类Fu

package com.atguigu.keyword;

public class Fu {
    public int a = 1;
    public  int b = 1;
}

 

子类Zi

package com.atguigu.keyword;

public class Zi extends Fu{
    public int a = 2;

    public void test(int a){
        System.out.println("a = " + a);//有重名问题,就近原则,使用局部变量
        System.out.println("this.a = " + this.a);//有重名问题,明确说明使用本类的
        System.out.println("super.a = " + super.a);//有重名问题,明确说明使用父类的

        System.out.println("b = " + b);//没有重名问题,直接使用父类的
    }
}

 

测试类TestZi

package com.atguigu.keyword;

public class TestZi {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.test(3);
    }
}

4、注意

无论在子类中调用父类的构造器,还是父类的方法,还是父类的成员变量,都要求被调用的父类成员不能是private。就算加super也不能调用。

1.6 根父类Object类

1.6.1 根父类的概念

java.lang.Object类是所有Java类的父类,它是老祖宗。所有Java类都是它的子类。

如果一个类没有明确说明它继承(extends)哪个类,那么它的父类就是Object。

1.6.2 toString方法

Object类中声明了public String toString()方法,所有Java类(或者说所有引用数据类型)都会从Object继承这个方法。

如果子类不重写toString方法,默认返回的是 对象的实际类型(new它的类型) @ 对象的hashCode值的十六进制形式。

官方API文档说明了,建议所有子类都重写toString方法,用于返回对象的详细信息,一般都是返回对象的属性值拼接。等价于我们之前做练习用的getInfo方法。

package com.atguigu.api;

//Person的直接父类就是Object
public class Person {
}

 

package com.atguigu.api;

public class TestPerson {
    public static void main(String[] args) {
        Person p = new Person();
        System.out.println(p.toString());
        //这里toString方法,就是从Object类继承的
        //默认打印的是 com.atguigu.api.Person@4eec7777

        System.out.println(p.hashCode());//1324119927
        //1324119927是p对象的hash值的十进制形式
        //4eec7777是 hash值的十六进制形式
        //关于hash值是什么,干什么用的,今天先不讨论,等后面讲哈希表等数据结构的时候再说。
    }
}

toString()非常特殊,当我们用System.out.println() 或 System.out.print()方法打印对象时,默认就会调用这个对象的toString,不用程序员手动调用。或者当我们把一个Java对象与字符串进行拼接时,也会自动调用这个对象的toString。

toString()方法的重写有两个快捷键: 

  • Alt + Insert:属性拼接的模板

  • @Override
    public String toString() {
        return "Rectangle{" +
            "length=" + length +
            ", width=" + width +
            '}';
    }

  • Ctrl + O:重写父类的哪些方法。使用调用父类toString方法的模板。

@Override
public String toString() {
    return super.toString();
}

1.6.3 示例代码1

Rectangle矩形类
package com.atguigu.api;

public class Rectangle {
    private double length;
    private double width;

    public Rectangle() {
    }

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    //下面的toString方法,使用快捷键Alt + Insert自动生成
    @Override
    public String toString() { 
        return "Rectangle{" +
                "length=" + length +
                ", width=" + width +
                '}';
    }
}

测试类
package com.atguigu.api;

public class TestRectangle {
    public static void main(String[] args) {
        Rectangle r = new Rectangle(8,4);
        System.out.println(r.toString());
        System.out.println(r);
        //打印r对象时,自动会调用r的toString
    }
}

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

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

相关文章

es安装中文分词器

下载地址&#xff0c;尽量选择和自己本地es差不多的版本 https://github.com/infinilabs/analysis-ik/releases 下载好&#xff0c;解压&#xff0c;把里面的文件放到es的plugins/ik目录下 把plugin-descriptor.properties文件里的es版本改成自己对应的 再启动es&#xff0c;能…

十、OOP面向对象程序设计(五)

1、什么是接口以及接口的运用 1)接口定义 Java接口(Interface),是一些列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能。) 2)接口定义的一般形式 修饰符:…

抖音小店怎么选品?这些超级容易爆单的选品方法,很少有人告诉你!

哈喽~我是电商月月 抖音小店的运营过程中&#xff0c;选品是非常重要的&#xff0c;好的商品不用宣传&#xff0c;就有人看 今天我就来给大家分享几个选品技巧&#xff0c;学会后商品一上架就有流量&#xff01; 利用数据选品 1.“蝉妈妈”的数据排行榜选品 “蝉妈妈”能看…

在Ubuntu中如何查看NASM -f选项支持的输出格式

2024年4月19日&#xff0c;周五下午 用下面这条指令就可以了&#xff1a; nasm -hf

v1.9.2-httpsok快速申请免费SSL证书

v1.9.2-&#x1f525;httpsok快速申请免费SSL证书 介绍 httpsok 是一个便捷的 HTTPS 证书自动续签工具&#xff0c;专为 Nginx 、OpenResty 服务器设计。已服务众多中小企业&#xff0c;稳定、安全、可靠。 一行命令&#xff0c;一分钟轻松搞定SSL证书自动续期 更新日志 V1…

超越现实的展览体验,VR全景展厅重新定义艺术与产品展示

随着数字化时代的到来&#xff0c;VR全景展厅成为了企业和创作者展示作品与产品的新兴选择。通过结合先进的虚拟现实技术&#xff0c;VR全景展厅不仅能够提供身临其境的观展体验&#xff0c;而且还拓展了传统展示方式的界限。 一、虚拟现实技术的融合之美 1、高度沉浸的观展体验…

Pytorch-自动微分模块

&#x1f947;接下来我们进入到Pytorch的自动微分模块torch.autograd~ 自动微分模块是PyTorch中用于实现张量自动求导的模块。PyTorch通过torch.autograd模块提供了自动微分的功能&#xff0c;这对于深度学习和优化问题至关重要&#xff0c;因为它可以自动计算梯度&#xff0c…

行人属性AI识别/人体结构化属性AI识别算法的原理及应用场景介绍

行人属性AI识别技术是一种基于人工智能技术的图像识别技术&#xff0c;通过对行人的图像或视频进行处理和分析&#xff0c;提取出其中的结构化信息&#xff0c;如人体姿态、关键点位置、行人属性&#xff08;性别、年龄、服装等&#xff09;等。 行人结构化数据分析的方法包括…

什么是边缘计算?它为何如此重要?-天拓四方

随着信息技术的快速发展&#xff0c;数据处理和计算的需求日益增大&#xff0c;特别是在实时性要求极高的场景中&#xff0c;传统的云计算模式面临着巨大的挑战。在这样的背景下&#xff0c;边缘计算作为一种新兴的计算模式&#xff0c;正逐渐受到业界的广泛关注。那么&#xf…

【创建型模式】单例模式

一、单例模式概述 单例模式的定义&#xff1a;又叫单件模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。&#xff08;对象创建型&#xff09; 要点&#xff1a; 1.某个类只能有一个实例&#xff1b;2.必须自行创建这个实例&#xff1b;3.必须自行向整…

【nginx代理和tengine的启动-重启等命令】

在nginx成功启动后[任务管理器有nginx.exe进程]&#xff0c;运行vue项目&#xff0c;在浏览器访问http://localhost:10001/&#xff0c;提示&#xff1a;访问拒绝&#xff08;调试中network某些地址403&#xff09;&#xff1b; 解决方案&#xff1a; localhost改为ip&#xff…

自动化测试Selenium(4)

WebDriver相关api 定位一组元素 webdriver可以很方便地使用findElement方法来定位某个特定的对象, 不过有时候我们需要定位一组对象, 这时候就要使用findElements方法. 定位一组对象一般用于一下场景: 批量操作对象, 比如将页面上的checkbox都勾上. 先获取一组对象, 再在这组…

【代码随想录】【回文子串】day57:● 647. 回文子串 ● 516.最长回文子序列 ● 动态规划总结篇

回文子串 def countSubstrings(self, s):# 动态规划解法# dp[i][j] s[i-j]区间的回文子串的数目 dp[i][j]取决于dp[i1]和dp[j-1]count0dp[[False]*len(s) for _ in range(len(s))]for i in range(len(s)-1,-1,-1):for j in range(i,len(s)):if s[i]s[j] :if j-i<1:count1dp[…

全新升级轻舟知识付费系统引流变现至上利器

知识付费系统&#xff1a;引流变现至上利器 本系统参考各大主流知识付费系统&#xff0c;汇总取其精华&#xff0c;自主研发&#xff0c;正版授权系统。 我们给你搭建搭建一个独立运营的知识付费平台&#xff0c;搭建好之后&#xff0c;你可以自由的运营管理。网站里面的名称…

嵌入式软件考试——网络基础知识

1 主要知识点 OSI/RMTCP/IPIP地址与网络划分DNS与DHCP网络规划与设计网络故障诊断 2 OSI/RM 2.1 OSI七层模型 OSI七层模型 Bit流&#xff1a;物理层(集中器/中继器) 帧&#xff1a;数据链路层(网桥/交换机) 包&#xff1a;网络层(路由器) 段&#xff1a;传输层 报文&#xf…

SpringBoot框架——7.整合MybatisPlus

这篇主要介绍Springboot整合MybatisPlus&#xff0c;另外介绍一个插件JBLSpringbootAppGen,以及一个经常用于测试的基于内存的h2数据库。 Mybatisplus是mybatis的增强工具&#xff0c;和tk-mybatis相似&#xff0c;但功能更强大&#xff0c;可避免重复CRUD语句&#xff0c;先来…

uniapp_微信小程序_预约时间组件的使用

一、官方文档 DatetimePicker 选择器 | uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架 (uviewui.com) 二、完成的效果 之前使用的是Calendar 日历 这个太耗性能了&#xff0c;直接页面卡顿&#xff0c;所以就换成以上选择器了 三、代码 <u-datetime-p…

graphviz使用

安装 brew install graphviz测试 https://github.com/martisak/dotnets?tabreadme-ov-file

图文教程 | Git安装配置、常用命令大全以及常见问题

前言 因为多了一台电脑&#xff0c;平时写一些代码&#xff0c;改一些文件&#xff0c;用U盘存着转来转去特别麻烦。于是打算用Git管理我的文件&#xff0c;方便在两个终端之间传输数据啥的。也正好给新电脑装好Git。 &#x1f4e2;博客主页&#xff1a;程序源⠀-CSDN博客 &…

3d模型渲染怎么会没材质---模大狮模型网

在进行3D模型渲染时&#xff0c;有时会遇到材质丢失的问题&#xff0c;这可能会给设计师们带来一些困扰。材质是渲染的重要组成部分&#xff0c;它们赋予了模型真实感和视觉吸引力。然而&#xff0c;当模型在渲染过程中出现没有材质的情况时&#xff0c;可能会导致最终效果不如…