【JavaSE---03】面向对象编程「类与对象 | 关键字 | 访问修饰符 | main方法 | 作用域 | 重载 | 构造器 | 封装 | 继承 | 多态 | Object类」

0. 前言

  1. 面向过程:强调的是功能。
  2. 面向对象:把功能封装进对象,强调具备了功能的对象。
  3. 面向对象是基于面向过程的,但是他们是侧重点不同的思想。其最大的区别在于:
    1. 面向过程只在乎哪些功能实现目标
    2. 而面向对象是对象可以实现目标,再去考虑该对象具备哪些功能去实现目标。
      在这里插入图片描述

1. 类与对象

1.1 类与对象关系

类与对象关系:类是共性,是一系列个体的抽象;对象是个性,是某个实际的个体。

比如:张三,李四等是一个个具体的对象,而这些个体的抽象叫做人。

1.2 类

1.2.1 类的组成(5大组成部分)

1.2.1.1 成员变量(或成员字段Field)与成员方法 / 普通属性与普通方法
  1. 成员变量定义: 访问修饰符 + 数据类型 + 变量名 eg:private int age;

    注意:可以赋初始值,也可以不赋值,因为成员变量有初始值。但是局部变量必须赋值后才能使用(除了数组),因为其没有默认值。

  2. 成员方法
    1. 定义: 访问修饰符 + 返回值类型 + 方法名 + (形参列表) + { 方法体 }
    2. 同一个类中的方法可以直接相互使用,不用创建对象后再使用
    3. return返回值类型 与 方法定义中的返回值类型 要一致
      在这里插入图片描述
    4. 若要返回多个值,则返回值类型可以用数组
      【则数组一般定义在被调用的方法中,这样就不用将数组传入,且由于数组被引用会导致数组不会被回收】
      在这里插入图片描述
1.2.1.2 类变量(或成员字段Field)与类方法 / 静态属性与静态方法
  1. 类变量
    1. 定义:【比成员变量多了static】访问修饰符 + static + 数据类型 + 变量名 eg:private static int count;【推荐】
    2. 特点:
      1. 所有对象会共享该变量
      2. 在类加载的时执行【关于类加载的详细内容】
    3. 使用:
      1. 类名.类变量名eg: Person.count【推荐】
      2. 对象名.类变量名eg: person1.count
    4. 若在定义时初始化:
      1. 直接赋确定值。eg:public static int count = 100;
      2. 调用方法。则只能调用静态方法,不能调用普通方法。
        在这里插入图片描述
  2. 类方法
    1. 定义:【比成员方法多了static】访问修饰符 + static + 返回值类型 + 方法名 + (形参列表) + { 方法体 }
    2. 特点: 调用时执行
    3. 使用:
      1. 类名.方法名()【推荐】
      2. 对象名.方法名()
    4. 使用场景:当方法中不涉及任何和对象相关的成员变量或方法,则可以设计为静态方法,提高开发效率。比如:Utils工具类。
    5. 细节:
      1. 静态方法中不能含有thissuper(因为此时还无对象)
      2. 静态方法中只能访问静态变量和静态方法,不能访问成员变量和成员方法
  3. 练习
    在这里插入图片描述
    在这里插入图片描述
1.2.1.3 代码块
  1. 代码块:是对构造器的补充,可以做初始化操作。
  2. 语法:
    在这里插入图片描述
  3. 应用场景:多个构造器中都有重复的语句,可以抽取到代码块中,提高代码的重用性。
  4. 细节:
    1. 静态代码块是随着类的加载而执行,由于类只加载一次,所以只执行一次;而普通代码块是创建对象时才执行。
    2. 和静态方法一样。静态代码块只能调用静态属性静态方法、不能用superthis;普通代码块则都可以。
1.2.1.4 构造器 / 构造方法

构造器 / 构造方法:new对象时,系统会自动调用该方法,完成对新对象的初始化。

细节:

  1. 构造器没有返回值
  2. 构造器方法名必须与类名一样
    在这里插入图片描述
  3. 一旦定义了构造器,默认的无参数构造器方法就会失效。如果需要,则需要重新写上
  4. 一个类可以定义多个不同的构造器,即构造器重载
1.2.1.5 内部类

内部类知识点详解

1.2.2 类加载

【关于类加载的详细内容】

1.3 对象

1.3.1 创建对象

  1. 直接分配:Person p = new Person();
  2. 先声明,再分配。
    1. Person p:在栈中开辟空间,内容为null,p指向该地址。
    2. p = new Person();:在堆中开辟空间,并将该地址存放到p所指向的空间中,替换掉null。

1.3.2 使用对象

1.3.2.1 对象属性、方法的使用
  1. 使用对象属性:对象名.属性名 如:p.name
  2. 使用对象方法:对象名.方法名(实参列表) 如:int a = p.getSum(10, 20)**
1.3.2.2 方法调用的细节
1.3.2.2.1 调用方法时的空间分配

调用方法时,会在jvm的栈中再开辟一块独立的小栈,方法调用完后将结果返回,并释放空间:

  1. 释放该小栈空间
  2. 释放其用到且没有被引用的堆空间
  1. 调用方法的空间分配
    在这里插入图片描述
  2. 递归调用方法的空间分配
    在这里插入图片描述
1.3.2.2.2 传参机制与值返回的本质

传参机制值返回 的本质是赋值,而赋值是复制所在栈地址里的内容。

表现为:实参是基本数据类型时,是值拷贝,不会影响实参;实参是引用数据类型时,是传递地址,会影响实参。

值返回同理。

在这里插入图片描述

1.3.3 创建对象的流程分析

1.3.3.1 从类的角度看new对象操作
  1. 首先是 类加载 。执行 静态代码块静态属性,他们的初始化优先级一样。故看编写的先后顺序。
    在这里插入图片描述
  2. 执行对应的构造器,构造器中隐含表示了两个东西
    在这里插入图片描述

    注意:

    1. super()内的执行步骤,可以看作new父类。其父类中的静态属性、静态代码块、构造器、普通属性、普通代码块也是一样的执行步骤,有点递归的感觉。
    2. 普通代码块普通属性的初始化优先级一样。故看编写的先后顺序。
  3. 例子:
    在这里插入图片描述

当父类也有静态属性、静态方法、普通属性、普通方法时:

记住一点:先把父类的静态执行完,再执行子类静态。后续步骤与上面一致。
在这里插入图片描述
例子:
在这里插入图片描述

练习:
在这里插入图片描述
在这里插入图片描述

1.3.3.2 从jvm的角度看new对象操作

在这里插入图片描述

  1. 在jvm栈中开辟独立的小栈main
    在这里插入图片描述
  2. Person类加载:完成 加载、连接、初始化 三个操作。类加载详细情况
    在这里插入图片描述
  3. 在jvm堆中为实例化对象分配空间,并为属性设置默认值
    在这里插入图片描述
  4. 调用父类构造器
  5. 执行普通属性 和 普通代码块
    在这里插入图片描述
  6. 执行子类构造器
    在这里插入图片描述
  7. 将实例化对象的地址赋给p
    在这里插入图片描述

1.3.4 对象的空间分配

在这里插入图片描述

数据的存放模式有两点需要注意

  1. 大端模式、小端模式;
  2. 边界对齐和边界不对齐(访问某变量:前者访存一次,后者可能一次或多次)。
    在这里插入图片描述

1.3.5 instanceof关键字

可以用instanceof,判断某运行对象是否是某类其子类的对象。
在这里插入图片描述

运行对象:指该对象变量名所指向的堆中的对象。
在这里插入图片描述

1.3.6 this关键字

  1. 作用
    1. 访问当前对象的属性:this.属性名
    2. 访问当前对象的方法:this.方法名(参数列表)
    3. 访问当前对象构造器:this(参数列表)。两个注意点:
      1. 只能在构造器中访问另外一个构造器,不能在非构造方法中使用。
      2. 使用this访问构造器,必须在所在构造器的首行
      3. this关键字在构造函数中不能出现相互调用 的情况,因为是一个死循环。
        在这里插入图片描述
        在这里插入图片描述
  2. 细节
    1. this 在定义的类的除了静态方法和静态代码块之外的地方使用。
    2. this 所在单元的内容为该对象的首地址,故this代表当前对象

1.3.7 final关键字

  1. final可修饰 类、属性、方法、局部变量。
  2. 应用场景:
    1. 当不希望该类被继承时。eg:public final class Person { }
    2. 当不希望类的某个属性值被修改时。eg:private final long TAX_NUM = 100L;
    3. 当不希望父类的某个方法被子类 覆盖或者重写时.eg:public final void say() { }
    4. 当希望某个局部变量的值被修改时。eg:final int MAX_SIZE = 10;
  3. 细节:
    1. final修饰属性或者局部变量,命名规范为:XX_XX(大写字母)
    2. final修饰普通属性时的赋值:
      1. 直接赋值
      2. 在构造器中赋值
      3. 在代码块中赋值
        在这里插入图片描述
    3. final修饰静态属性时
      1. 赋值:直接赋值、代码块中赋值。不能在构造器中赋值。
      2. final和static同时修饰的属性被其他类调用时,不会导致该类的加载。
        在这里插入图片描述
    4. final修饰的方法,子类虽然不能覆盖或者重写,但是可以继承
    5. final不能修饰构造器
  4. 练习题
    在这里插入图片描述

2. 访问修饰符号

  1. 作用:访问修饰符用于修饰属性方法对于类只能用public和默认两种
  2. 种类
    1. public:所有包的类公开
    2. protected:对同一个包中的类,及子类公开
    3. 默认:对同一个包中的类公开
    4. private:只对本类公开
      在这里插入图片描述

3. 理解main方法

  1. main方法由Java虚拟机调用。
  2. Java虚拟机与main方法不再同一个包下,所以要用public
  3. Java虚拟机在执行main方法时不需要创建对象,所以用static
  4. doc下使用java指令运行程序时,若在后面添加了实际参数,会将这些参数转为字符串数组,即args数组。
    在这里插入图片描述

4. 作用域

  1. 全局变量:也就是属性
    1. 作用域为整个类。
      在这里插入图片描述
    2. 全局变量不赋值就可以使用,因为有默认值
    3. 可以加修饰符,也可以不加
  2. 局部变量:除了属性之外的其他变量
    1. 方法中的变量,其作用域在该方法内;控制结构中的变量,其作用域在该代码块中。
    2. 局部变量必须赋值才可以使用,因为其没有默认值。
    3. 不能加修饰符
  3. 细节
    1. 属性和局部变量作用域相交时可以重名,访问时遵循就近原则。
    在这里插入图片描述
    2. 局部变量与局部变量作用域相交时不可以重名
    在这里插入图片描述

5. 包

  1. 包的作用
    1. 区分相同名字的类
    2. 管理类
    3. 控制访问范围
  2. 包的本质:创建不同的文件夹来保存类文件
  3. 包的命名
    1. 规则
      1. 字母、数字、_,但是不能以数字开头【比变量命名少个$】
      2. 不含空格、不能用关键字和保留字
        在这里插入图片描述
    2. 规范:
      1. com.公司名.项目名.业务模块名
      2. 包名一般用小写字母
  4. 导入包
    1. 导入com.a包下的一个Person类:import com.a.Person
    2. 导入com.a包下的所有类:import com.a.*

      注意:同包下的类,不用导入

  5. 细节:类在某包下,则第一行必须使用package声明,且该声明必须放在第一行。
    如:com.a包下有Person类,则Person类的第一行必须写package com.a

6. 可变参数

  1. 语法:数据类型...形参名称 eg:public double getSum(int...nums){ 方法体 }
  2. 细节点
    1. 可变参数和数据类型可以一起放在形参列表中,但是可变参数必须放在最后
      eg:错误:public double getSum(int...nums, double b){ }
    2. 形参列表只允许有一个可变参数
      eg:错误:public double getSum(int...nums, double...nums2){ }
    3. 可变参数对应的实参个数可以为0个、1个、多个,但必须是同一类型且与可变参数类型相同
    4. 可变参数的本质是数组,故当作数组使用
      在这里插入图片描述

7. overload / 方法重载

overload / 方法重载:同一个类中,方法名相同,但是形参列表不一致。

形参列表是否一致只考虑2个点:【形参名称返回值类型无关

  1. 参数个数;
  2. 对应位置的参数类型。与
    在这里插入图片描述

9. 面向对象的三大特征

9.1 封装

  1. 封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
  2. 封装的原则:将不需要对外提供的内容都隐藏起来,提供公共方法对其访问。
  3. 作用:提高安全性、隐藏实现细节
  4. 实现封装的步骤
    1. 对于属性,一般都需要隐藏起来,只通过授权的方法访问。实现步骤:
      1. 用private修饰成员变量
      2. 设置public的setter和getter方法
    2. 对于实现具体功能的方法,一般要隐藏起来,实现步骤:
      1. 用private修饰方法
      2. 设置public的方法调用这些方法以实现外部者所需的功能
  5. 细节:将属性封装后,构造器为属性初始化值也应该调用setter来赋值,而不是直接赋值。【可以防止别人用构造器去修改属性值】
    在这里插入图片描述

9.2 继承

9.2.1 继承的使用

  1. 继承:子类可以继承父类的所有,并进行扩展。
  2. 应用场景:当多个类的属性和方法有很多是相同时,且可以抽象出父类,提高代码的复用性。
  3. 语法:在定义子类时使用extends关键字。如:有 父类Person,子类Graduate,则:class Graduate extends Person { 成员变量及方法 }
  4. 细节
    1. 所有类都继承了Object类,因为Object是祖先,万物皆对象。
    2. 子类最多只能继承一个父类。
    3. B继承A,则B有A的所有属性和方法,同时A可以定义自己特有的属性和方法
      在这里插入图片描述
    4. 父类的非私有属性和方法可以在子类中直接被访问,但是私有属性和方法不能在子类中访问,要遵循访问控制。
      1. 想访问父类的私有属性,可以在父类中定义非私有方法访问私有属性,如getter、setter方法,然后子类调用该方法。
      2. 想访问父类的私有方法,也类似处理
    5. 子类的每个构造器都必须 直接使用this(参数列表) 间接调用父类的构造器,完成父类的初始化。
      1. 默认在子类构造器的第一行调用父类的默认构造器,即无参构造器,等同于supper();
      2. 父类没有无参构造器,则必须在子类的构造器中用super指定父类的构造器。语法:super(参数列表)
        在这里插入图片描述
      3. 使用super关键字,必须写在构造器的第一行。
        在这里插入图片描述
      4. 由于在构造器中 superthis(参数列表) 都必须在构造器的首行,故不能共存,只能用一个。
        在这里插入图片描述

练习:
在这里插入图片描述

9.2.2 super关键字

  1. super代表父类,作用:
    1. 访问父类的构造器(上面已经提到),只能在子类的构造器中使用。语法:super(参数列表)
    2. 访问父类的属性,遵循访问修饰符规则。语法:super.属性名
    3. 访问父类的方法,遵循访问修饰符规则。语法:super.方法名(参数列表)
  2. 细节
    1. 当子类和父类的属性或方法重名,用super.属性名访问父类属性,用this.属性名访问子类属性;成员方法同理。
      【注意:若不使用super、this关键字加以区分,直接使用,则子类访问某属性或方法时,会先查看子类:若子类有该属性或方法,则访问;否则向上追溯父类】
    2. 子类的每个构造器都必须 直接this(参数列表) 间接调用父类的构造器,完成父类的初始化。

练习:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

9.2.3 继承的流程分析

在这里插入图片描述

  1. 将类加载到jvm的方法区。先加载父类,再加载子类
    在这里插入图片描述
  2. 在jvm栈中开辟独立的小栈main
  3. 在jvm堆中为实例化对象分配空间,并为属性设置默认值
  4. 为实例化对象的属性赋值
  5. 调用构造器,为实例化对象进行初始化
    在这里插入图片描述

    注意:父类与子类属性重名时,其分别分配了空间,故属性没有重写之说,由于方法在方法区中,故有重写之说。

  6. 将实例化对象的地址赋给son
  7. 访问son对象的属性或方法
    1. 子类和父类的属性或方法重名:首先看子类是否有该属性或方法,有则访问;没有则向上追溯父类有没有这个属性或方法
      如:输出son.age为30,而不是60
    2. 父类的私有属性,子类不能直接访问,但是在推中还是有分配空间。
      如:图中的age->60

9.2.4 overwrite / 方法重写

9.2.4.1 方法重写的使用
  1. 方法重写/覆盖:子类可以对父类的方法进行重写
  2. 方法重写的判断:
    1. 方法名、参数列表一致
    2. 返回值一致,或者是父类的子类型。
      如:父类返回Object类型,子类可以返回String类型。
    3. 访问修饰符可以一致或扩大,但不能缩小。
      如:public不能改成protected
  3. 细节:默认和private的方法,不能够被重写。(根据访问修饰符范围)
9.2.4.2 重写 vs 重载

在这里插入图片描述

9.3 多态

多态:指方法对象有多种状态,是OOP编程的第三大特征。

9.3.1 方法多态、对象多态、参数多态

  1. 方法多态:
    1. 重载体现多态。
    2. 重写体现多态。
  2. 对象多态:基于继承实现,指一个父类变量可以接受多个子类对象。表现为对象的编译类型和运行类型不一致。编译类型是确定的,运行类型是可以通过向上转型、向下转型动态改变的。
    3个细节:
    1. 向上转型:可以用父类变量接受子类对象,即向上转型。
      如:Animal dog = new Dog();Animal 是父类,Dog是子类。

      向上转型注意3点:

      1. 父类变量:编译类型是父类类型,但运行类型是子类对象(可以使用dog.getClass()方法验证)。
        在这里插入图片描述
      2. 使用该父类变量访问与子类重名的方法时:访问的是子类的方法。原因见下面的动态绑定机制。
        使用该父类变量访问与子类重名的属性时:访问的是父类的属性。因为在堆中分别为其分配了空间。
        在这里插入图片描述
    2. 由于编译器的限制,导致dog只能调用Animal的属性和方法(遵循访问控制权限,因为其本身是子对象)。尽管Dog专有的属性和方法是存在在于方法区的,但是不能调用。
      在这里插入图片描述
    3. 向下转型:由于Dog专有的属性和方法是存在,如果想访问,可以使用向下转型:将父类变量强转为子类变量。
      在这里插入图片描述

      向下转型注意2点:

      1. 只能强转向上转型后的父类变量,不能强转直接new的父类对象。
      2. 若父类有多个子类,且子类间无继承关系,则父类变量只能强转为其指向堆中的对象类型。
        如:Object是String、Integer的父类,且String、Integer之间无继承关系
        在这里插入图片描述
  3. 参数多态:利用对象多态体现参数多态。应用场景:形参为父类类型,实参为子类型,这样有个默认向上转型。表现为父类类型的形参可以接受多种类型的子类对象。
  4. 对象多态的应用:当某方法重载次数很多时,可使用对象多态简化为一个方法。

9.3.2 动态绑定机制

动态绑定机制与多态是密切相关的,正是由于多态,才引出动态绑定机制。

动态绑定机制:调用向上转型的对象变量a的方法时,该方法、以及该方法所调用的所有方法,都与运行类型绑定。比如:

  1. 调用对象变量a的方法H,如何找H?
  2. 调用对象变量a的方法H 且 H方法中再调用方法M时,如何找M???

答:都会优先在a所指向的堆中对象所属类中去找,若没有再到其父类找。

注意:访问对象变量的属性时,没有动态绑定。故:哪个类的方法访问属性,就用哪个类的属性。

在这里插入图片描述
在这里插入图片描述

9.3.3 多态数组

  1. 调用数组内对象统一的重名方法
    在这里插入图片描述
  2. 调用数组内对象特有的方法:使用instanceof向下转型
    在这里插入图片描述

10. Object类

10.1 标识对象是否相等

hashCode()和equals()用来标识对象,两个方法协同工作用来判断两个对象是否相等。 对象通过调用 Object.hashCode()生成哈希值,由于不可避免地会存在哈希值冲突的情况。因此hashCode相同时,还需要再调用 equals 进行一次值的比较。但是若hashCode不同,将直接判定两个对象不同,跳过 equals ,这加快了冲突处理效率。

注意:

  1. 任何时候重写equals,都必须同时重写hashCode。
  2. 如果 Object.equal () 相等,Object.hashCode () 也必然相等。重写时也建议保证此特性。

10.1.1 hashCode方法

  1. 哈希码(hashCode):Object的hashCode方法是根据内部地址压缩得到的一个整数
  2. 作用:提供该方法是为了支持哈希表,例如HashMap,HashTable等
  3. 特点:
    1. 相同对象的哈希值完全一样
    2. 不同对象的哈希值几乎不一样
  4. 继承Object类的类,重写hashCode方法可以指定hashCode值由什么决定。
    在这里插入图片描述

10.1.2 equals方法

  1. == vs equals方法
    1. ==是比较运算符。可用基本类型和引用类型。基本类型是比较值是否相等;引用类型是比较所指对象(即地址)是否相同,而不是hashCode值。
      1. 两边都是同类型的变量
        在这里插入图片描述
      2. 一边是变量,另一边是非变量
        在这里插入图片描述
    2. equals是Object类的方法。只用于引用类型。默认比较对象(即地址)是否相同;重写方法后先比较对象(即地址),不相等再比较对象内容(即属性)是否相等。
  2. 重写equal方法三步:
    ① 比较是否是相同对象
    ② 比较是否是该对象的实例
    ③ 将形参强转为该类型,再比较字段是否相同
    在这里插入图片描述
  3. 练习
    在这里插入图片描述
    在这里插入图片描述

10.3 toString方法

  1. toString:默认返回类型:全类名 + @ + 哈希值的十六进制
    在这里插入图片描述
  2. 重写toString方法:一般返回类名、属性名及属性值 。【可使用快捷键Alt + insert
    在这里插入图片描述
  3. 细节:直接输出对象名,会自动调用toString方法。
    在这里插入图片描述

10.4 finalize方法

  1. finalize方法:当垃圾回收器确定不存在对该对象的任何引用时,由垃圾回收器调用此方法。默认方法是什么也不做。
  2. 重写finalize方法:加上一些业务逻辑代码,如比如数据库连接释放、关闭文件等等。【在实际开发中一般不重写该方法,了解即可】
  3. 细节:不是对象一不被任何对象引用就调用finalize方法,是根据jvm的GC算法。

10.5 getClass方法

getClass方法:获取运行对象的类。
通常用对象名.getClass().getName()获取运行对象的类名

封装和继承目的都是为了代码重用,多态目的是为了接口重用

单例模式

  1. 单例模式:指在软件运行过程中,某类只有一个实例。
  2. 实现步骤:
    1. 设置私有静态属性
    2. 构造器私有化:防止类外new对象
    3. 提供一个公共的静态的getInstance()方法:用于获取实例对象
    4. 实例化对象,有两种情况:
      1. 饿汉式:在属性处创建对象。类加载时就会创建对象
        在这里插入图片描述
      2. 懒汉式:在getInstance方法中创建对象。使用方法时,才创建对象。
        在这里插入图片描述
  3. 应用场景:
    1. 需要生成唯一序列的环境
    2. 需要频繁实例化然后销毁的对象。
    3. 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
    4. 方便资源相互通信的环境
  4. 饿汉式 vs 懒汉式
    1. 创建对象的时机不同。
    2. 饿汉式不存在线程安全问题;懒汉式存在线程安全问题
    3. 饿汉式存在资源浪费的可能,因为对象不被使用可能会被回收;而懒汉式不存在资源浪费
  5. 例子:Runtime类就是用的单例模式
    在这里插入图片描述

在这里插入图片描述

冒泡排序:

1. 几趟:length-12. 每一趟确定一个位置

public static void main(String[] args) {
    int[] a = {4, 2, 1, 3, -1, 9, 8};
    int temp = 0;
    // 1. length-1趟
    for (int i = 0; i < a.length-1; i++) {
        // 2. 依此确定0,1,2...号位置。使用冒泡依次交换确定
        for (int j = a.length-1; j > i; j--) {
            if (a[j-1] > a[j]) {
                temp = a[j-1];
                a[j-1] = a[j];
                a[j] = temp;
            }
        }
    }
    for (int i = 0; i < a.length; i++)
        System.out.print(a[i] + " ");
}

public static void main(String[] args) {
    int[] a = {4, 2, 1, 3, -1, 9, 8};
    int temp = 0;
    // 1. length-1趟
    for (int i = 0; i < a.length-1; i++) {
        // 2. 依此确定0,1,2...号位置。使用比较非依次交换确定
        for (int j = i+1; j < a.length; j++) {
            if (a[i] > a[j]) {
                temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }
        }
    }
    for (int i = 0; i < a.length; i++)
        System.out.print(a[i] + " ");
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ElegantCodingWH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值