文章目录

  • 零、本讲学习目标
    • 1、掌握类的封装
    • 2、学会使用方法的重载
    • 3、学会方法的递归
  • 一、类的封装
    • 1、为什么需要封装
      • (1)案例演示
      • (2)案例分析
      • (3)解决方案
    • 2、如何实现封装
      • (1)封装的定义
      • (2)封装的实现
      • (3)案例演示
    • 3、课堂练习:创建三角形类Triangle
    • 4、小结:封装的具体实现步骤
  • 二、方法的重载
    • 1、为什么要引入重载
      • (1)假设场景
      • (2)困境所在
      • (3)解决办法
    • 2、案例演示方法重载
      • 任务1、编程解决数字求和问题
        • (1)不采用重载进行处理
        • (2)采用重载进行处理
      • 任务2、编程解决喂养不同宠物的问题
        • (1)创建三个类:Cat、Dog、Bird
        • (2)创建学生类Student,重载喂养方法
        • (3)创建测试类TestStudent,调用重载的喂养方法
  • 三、方法的递归
    • 1、利用递推法与递归法计算阶乘
    • 课堂练习:利用递推法和递归法计算1 + 2 + …… +100
    • 2、递归案例:汉诺塔游戏
  • 四、课后作业
    • 1、创建动物类Animal
    • 2、创建复数类Complex
    • 3、采用递归方法获取斐波拉契数列第n项的值
    • 4、采用面向对象方式计算三角形面积
  • 五、杂感:“一题多解”与“一诗多译”

零、本讲学习目标

1、掌握类的封装

2、学会使用方法的重载

3、学会方法的递归

封装是面向对象的三大特征之一,理解并掌握封装对于学习Java面向对象的内容十分重要。本节将从类的封装开始讲解,同时在前面方法定义和调用的基础上来进一步学习方法的重载和递归操作。

一、类的封装

1、为什么需要封装

  • Java是面向对象的编程语言,因为面向对象中所有的实体都是以对象为基本单位的,以宏观世界的实体来映射到计算机世界中,每个对象有它自己的属性和自己的行为,我们用类来将这些属性和行为封装到一起,当它实例化出来一个对象的时候,我们可以说这个对象具有这些属性性和行为。

  • 对象的数据封装特性彻底消除了传统结构方法中数据与操作分离所带来的种种问题,提高了程序的可复用性和可维护性,降低了程序员保持数据与操作内容的负担。

  • 对象的数据封装特性还可以把对象的私有数据和公共数据分离开,保护了私有数据,减少了可能的模块间干扰,达到降低程序复杂性、提高可控性的目的。

(1)案例演示

  • 在net.hw.lesson10包里创建Person类
package net.hw.lesson10;/*** 功能:Person类* 作者:华卫* 日期:2020年4月28日*/
public class Person {/*** 姓名(default访问权限)*/String name;/*** 人品(default访问权限)*/int character;/*** 自我介绍方法*/public void speak() {System.out.println("嗨,我叫" + name + ",人品值:" + character);}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", character=" + character +'}';}
}
  • 在net.hw.lesson10包里创建TestPerson类
package net.hw.lesson10;/*** 功能:测试Person类* 作者:华卫* 日期:2020年4月28日*/public class TestPerson {public static void main(String[] args) {// 实例化对象Person p = new Person();      // 设置对象属性p.name = "李晓红";p.character = -5;// 调用对象方法p.speak();}
}

运行程序,查看结果:

(2)案例分析

上述案例将人品赋值为一个负数-5,在语法上不会有任何问题,因此程序可以正常运行,但在现实生活中明显是不合理的,一般而言,人品值应该是某个范围内的正数,比如[1, 10]内的某一个整数值。

(3)解决方案

为了避免出现上述不合理的问题,在设计一个Java类时,应该对成员变量的访问作出一些限定,不允许外界随意访问,这就需要实现类的封装。

2、如何实现封装

(1)封装的定义

类的封装,是指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息,而是通过该类所提供的方法来实现对内部信息的操作访问。

(2)封装的实现

在定义一个类时,将类中的属性私有化,即使用private关键字来修饰,私有属性只能在它所在类中被访问,如果外界想要访问私有属性,需要提供一些使用public修饰的公有方法,其中包括用于获取属性值的getXXX()方法(简称getter)和设置属性值的setXXX()方法(简称setter)。

(3)案例演示

  • 修改Person类,将name与character属性设置为private属性
  • 可以利用IntelliJ IDEA提供的快捷方式帮助我们自动生成访问私有属性的getters和setters:

说明:按组合键<Alt> + <Ins>弹出Generate菜单,选择Getter and Setter,在弹出的对话框里选中需要生成getter与setter的属性,单击【OK】按钮即可自动生成访问私有属性的公共方法。

  • 修改setCharacter()方法,保证传入的人品值必须在[1, 10]范围之内

    通过这个方式就可以保护私有属性,不允许给私有属性设置不符合要求的数值。

此时,Person类代码如下:

package net.hw.lesson10;/*** 功能:Person类* 作者:华卫* 日期:2020年4月28日*/
public class Person {/*** 姓名(private访问权限)*/private String name;/*** 人品(private访问权限)*/private int character;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getCharacter() {return character;}public void setCharacter(int character) {if (character >= 1 && character <= 10) {this.character = character;} else {System.err.println("温馨提示:人品值必须在[1, 10]范围内!");}}/*** 自我介绍方法*/public void speak() {System.out.println("嗨,我叫" + name + ",人品值:" + character);}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", character=" + character +'}';}
}
  • 修改TestPerson类,通过setXXX()方法给私有属性赋值

  • 运行程序TestPerson,查看结果


Person对象调用setCharacter(-5),该方法会对参数character进行检查,由于当前传入的值不在[1, 10]范围内,因此会输出“温馨提示:人品值必须在[1, 10]范围内!”的信息。此时,character属性没有赋值,因此保持其初始值0,从而输出“人品值:0”。

  • 如果设置的人品值不符合要求,那么调用自我介绍方法时不输出任何内容。

  • 运行TestPerson程序,查看结果

  • 修改TestPerson,设置符合要求的人品值,再运行程序,查看效果

人品值8在[1, 10]范围内,符合要求,属性设置成功,因此输出了“人品值:8”.

3、课堂练习:创建三角形类Triangle

创建三角形类Triangle,包含三个私有属性a、b、c,提供对私有属性的存取方法,对于setXXX()方法,必须要求传入的参数值要大于0,否则输出“温馨提示:边长必须为正数!”;创建一个方法:getArea(),用于计算三角形面积,然后再编写测试类TestTriangle来测试(创建对象、设置属性,调用方法)。

4、小结:封装的具体实现步骤

  • 修改属性的可见性来限制对属性的访问。
  • 为每个属性创建一对赋值方法和取值方法,用于对这些属性的访问。
  • 在赋值和取值方法中,加入对属性的存取限制。

对于我们在Python里创建的Person类,怎样才能将name与character属性设置为私有属性,只能用setXXX()来设置,而不能直接通过对象名.属性名的方式来访问呢?

此时运行程序,结果如下:

但是可以通过setXXX()方法来设置对象的私有属性,如下图所示:

当然还有一种方式也可以访问对象的私有属性,在私有属性之前加一个_类名,如下图所示:

要保证人品值在[1, 10]之间,我们修改setCharacter()方法:

"""
类的封装
"""class Person:def setName(self, name):self.__name = namedef setCharacter(self, character):if character >= 1 and character <= 10:self.__character = characterelse:print("温馨提示:人品值必须在[1, 10]范围内!")self.__character = 0def speak(self):print("嗨, 我叫" + self.__name + ",人品值:" + str(self.__character))def __str__(self):return "Person {name='" + self.__name + "', character=" + str(self.__character) + "}"    if __name__ == "__main__":p = Person()    p.setName("李晓红")p.setCharacter(-1);print(p)p.speak()

运行程序,结果如下:

二、方法的重载

1、为什么要引入重载

(1)假设场景

编写一个对数字求和的方法,参与求和的数字个数不确定,类型也不确定。

(2)困境所在

由于参与求和数字的个数和类型都不确定,因此要针对不同的情况去设计不同的方法,这样的话非常麻烦,参数个数和类型搭配着变化,导致方法个数实在太多,并且也不便于命名和区分。

(3)解决办法

Java允许在一个程序中定义多个名称相同,但是参数的类型或个数不同的方法,这就是方法的重载。

2、案例演示方法重载

任务1、编程解决数字求和问题

(1)不采用重载进行处理

package net.hw.lesson10;/*** 功能:实现数字求和*      不采用重载* 作者:华卫* 日期:2020年4月28日*/
public class Example1001 {/*** 两个整数求和** @param x* @param y* @return*/public static int add01(int x, int y) {return x + y;}/*** 三个整数求和** @param x* @param y* @param z* @return*/public static int add02(int x, int y, int z) {return x + y + z;}/*** 三个实数求和** @param x* @param y* @param z* @return*/public static double add03(double x, double y, double z) {return x + y + z;}public static void main(String[] args) {// 调用方法int sum1 = add01(100, 150);int sum2 = add02(20, 30, 50);double sum3 = add03(45.5, 56.2, 89.4);// 输出结果System.out.println("sum1 = " + sum1);System.out.println("sum2 = " + sum2);System.out.println("sum3 = " + sum3);}
}

针对参数个数或类型不同的情况,分别定义了三个不同的求和方法add01、add02与add03,在调用它们时,千万不能将参数个数和类型给搞错了,用起来显得有点麻烦。

运行程序,查看结果:

(2)采用重载进行处理

package net.hw.lesson10;/*** 功能:实现数字求和*      采用重载* 作者:华卫* 日期:2020年4月28日*/
public class Example1002 {/*** 两个整数求和** @param x* @param y* @return*/public static int add(int x, int y) {return x + y;}/*** 三个整数求和** @param x* @param y* @param z* @return*/public static int add(int x, int y, int z) {return x + y + z;}/*** 三个实数求和** @param x* @param y* @param z* @return*/public static double add(double x, double y, double z) {return x + y + z;}public static void main(String[] args) {// 调用方法int sum1 = add(100, 150);int sum2 = add(20, 30, 50);double sum3 = add(45.5, 56.2, 89.4);// 输出结果System.out.println("sum1 = " + sum1);System.out.println("sum2 = " + sum2);System.out.println("sum3 = " + sum3);}
}

定义了三个同名的add()方法,但是参数个数或参数类型不同,从而实现方法的重载。调用add()方法时,系统会通过传入的参数情况决定调用哪个重载的方法,比如,add(100, 150),自然就会调用add(int x, int y)方法。

运行程序,查看结果:

注意:方法重载与返回值类型无关,只需满足两个条件:(1)方法名相同;(2)参数个数或参数类型不同。

任务2、编程解决喂养不同宠物的问题

(1)创建三个类:Cat、Dog、Bird

每个类都要求两个属性:名字和年龄,还包含一个吃的方法 - eat()。

  • 创建鸟类(Bird)
package net.hw.lesson10;/*** 功能:鸟类* 作者:华卫* 日期:2020年4月28日*/
public class Bird {private String name;private int 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;}public void eat() {System.out.println(name + "爱吃虫。");}
}
  • 创建猫类(Cat)
package net.hw.lesson10;/*** 功能:猫类* 作者:华卫* 日期:2020年4月28日*/
public class Cat {private String name;private int 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;}public void eat() {System.out.println(name + "爱吃鱼。");}
}
  • 创建狗类(Dog)
package net.hw.lesson10;/*** 功能:狗类* 作者:华卫* 日期:2020年4月28日*/
public class Dog {private String name;private int 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;}public void eat() {System.out.println(name + "爱吃骨头。");}
}

(2)创建学生类Student,重载喂养方法

package net.hw.lesson10;/*** 功能:学生类*      演示重载* 作者:华卫* 日期:2020年4月28日*/
public class Student {private String name;private int character;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getCharacter() {return character;}public void setCharacter(int character) {if (character >= 1 && character <= 10) {this.character = character;} else {System.err.println("人品值必须介于[1, 10]之间!");}}public void speak() {if (character >= 1 && character <= 10) {System.out.println("嗨, 我叫" + name + ",人品值:" + character);}}/*** @param cat*/public void feed(Cat cat) {System.out.println(name + "喂养了一只猫,名叫" + cat.getName() + ",现在" + cat.getAge() + "岁了。");cat.eat();}/*** @param dog*/public void feed(Dog dog) {System.out.println(name + "喂养了一条狗,名叫" + dog.getName() + ",现在" + dog.getAge() + "岁了。");dog.eat();}/*** @param bird*/public void feed(Bird bird) {System.out.println(name + "喂养了一只鸟,名叫" + bird.getName() + ",现在" + bird.getAge() + "岁了。");bird.eat();}
}

(3)创建测试类TestStudent,调用重载的喂养方法

package net.hw.lesson10;/*** 功能:测试Student类*      主要测试方法的重载* 作者:华卫* 日期:2020年4月28日*/
public class TestStudent {public static void main(String[] args) {// 实例化Cat类Cat cat = new Cat();cat.setName("欢欢");cat.setAge(2);// 实例化Dog类Dog dog = new Dog();dog.setName("瑞瑞");dog.setAge(3);// 实例化Bird类Bird bird = new Bird();bird.setName("灵灵");bird.setAge(1);// 实例化Student类Student student = new Student();student.setName("杨过");student.setCharacter(10);// 自我介绍student.speak();// 调用重载的feed方法student.feed(cat);student.feed(dog);student.feed(bird);}
}

运行程序,查看结果:

三、方法的递归

1、利用递推法与递归法计算阶乘

  • 递推法是由已知向未知拓展,采用循环结构完成,时间复杂度高而空间复杂度低。
  • 递归法是由未知向已知溯源,采用选择结构完成,时间复杂度低而空间复杂度高。

说明:方法的递归是指在一个方法的内部调用自身的过程。递归必须要有结束条件,不然就会陷入无限递归的状态,永远无法结束调用。

package net.hw.lesson10;import java.util.Scanner;/*** 功能:计算阶乘* 作者:华卫* 日期:2020年4月28日*/
public class Factorial {public static void main(String[] args) {int n, jc;Scanner sc = new Scanner(System.in);System.out.print("n = ");n = sc.nextInt();System.out.println("递推法计算:" + n + "! = " + fact01(n));System.out.println("递归法计算:" + n + "! = " + fact02(n));}/*** 递推计算阶乘方法** @param n* @return*/public static int fact01(int n) {int jc = 1;for (int i = 1; i <= n; i++) {jc = jc * i;}return jc;}/*** 递归计算阶乘方法** @param n* @return*/public static int fact02(int n) {if (n == 0) {return 1;} else {return fact02(n - 1) * n;}}
}

运行程序,查看结果:

方法的调用过程很复杂,下面我们说明整个调用过程。
当n = 10时,对于fact02(10),fact02()将被调用10次,n的值都会递减。当n的值为1时,所有递归调用的方法都会以相反的顺序相继结束,所以的返回值进行累乘,最终得到结果3628800。

(1)调用fact02(10),返回fact02(9) * 10
(2)调用fact02(9),返回fact02(8) * 9
(3)调用fact02(8),返回fact02(7) * 8
(4)调用fact02(7),返回fact02(6) * 7
(5)调用fact02(6),返回fact02(5) * 6
(6)调用fact02(5),返回fact02(4) * 5
(7)调用fact02(4),返回fact02(3) * 4
(8)调用fact02(3),返回fact02(2) * 3
(9)调用fact02(2),返回fact02(1) * 2
(10)调用fact02(1),返回fact02(0) * 1
(11)调用fact01(0),根据递归方法的定义,返回1

于是从下而上,依次可以得到fact02(1)、fact02(2)、……、fact02(10)。

大家可以参看完成同样任务的Python代码:

课堂练习:利用递推法和递归法计算1 + 2 + …… +100

2、递归案例:汉诺塔游戏


游戏规则:
(1)一次只能移动一个环
(2)小环必须在大环之上
(3)所有环从A柱移到C柱

package net.hw.lesson07;/*** 功能:汉诺达游戏* 作者:华卫* 日期:2020年4月28日*/
public class HanoiGame {private static long step;public static void main(String[] args) {hanoi(3, 'A', 'B', 'C');}/*** @param n 铁饼个数* @param x 起点位置* @param y 中间位置* @param z 终点位置*/public static void hanoi(int n, char x, char y, char z) {if (n == 1) {move(x, z);} else {hanoi(n - 1, x, z, y);move(x, z);hanoi(n - 1, y, x, z);}}public static void move(char p1, char p2) {step++;System.out.println("Step " + step + ": " + p1 + " -> " + p2);}
}

运行程序,查看结果:

当铁饼数n=3n=3n=3,移动次数=23−1=7=2^3-1=7=23−1=7

可以测试一下,当铁饼数n=10n=10n=10时,移动次数有多大。

可以看到,移动次数1023=210−1。1023=2^{10}-1。1023=210−1。

四、课后作业

1、创建动物类Animal

创建一个Animal类,包含三个私有属性:名字(name)、类别(category)和年龄(age);编写对三个属性的存取方法,注意年龄必须大于0;创建两个方法:自我介绍(speak)和行走(walk),然后再编写测试类TestAnimal来测试(创建对象、设置属性、调用方法)。

2、创建复数类Complex

创建复数类Complex,包含实部与虚部两个私有属性,定义复数的加减乘除四则运算方法add()、subtract()、multiply()、divide()。

package net.hw.lesson10;public class Complex {/*** 实部*/private double a;/*** 虚部*/private double b;public double getA() {return a;}public void setA(double a) {this.a = a;}public double getB() {return b;}public void setB(double b) {this.b = b;}public Complex add(Complex c) {return null;}public Complex subtract(Complex c) {return null;}public Complex multiply(Complex c) {return null;}public Complex divide(Complex c) {return null;}
}

大家知道Python语言里有complex数据类型,演示如下:

3、采用递归方法获取斐波拉契数列第n项的值

斐波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……0、1、1、2、3、5、8、13、21、34、……0、1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以递推的方法定义:F(0)=0,F(1)=1,F(n)=F(n−1)+F(n−2),(n≥2,n∈N∗)F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2) ,(n≥2,n∈N*)F(0)=0,F(1)=1,F(n)=F(n−1)+F(n−2),(n≥2,n∈N∗)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从1963起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。

4、采用面向对象方式计算三角形面积

创建三角形类Triangle,包含a、b、c三个私有属性,一个公共方法getArea()。然后创建一个测试类TestTriangle,在其主方法里创建Triangle对象,设置对象属性,调用对象方法得到三角形面积。

五、杂感:“一题多解”与“一诗多译”

俗话讲,殊途同归,条条道路通罗马。

数学课上,常常要求学生,开动脑筋,尽量一题多解;不同的解法往往能反映出解题者不同的着眼点与思维风格,这样做,可以让我在学生的不同解法中体验到多样性的乐趣,而且还可以评判选择比较好的解题方法。程序设计课上,也常常要求学生,针对同一个问题,尽量设计不同算法编写程序来实现,有时看到学生设计出巧妙的算法,心里会有说不出的喜悦。

这学期又讲Java程序设计,觉得蛮好玩的。其中,“多态”是面向对象编程三大支柱之一,分为横向多态与纵向多态。

横向多态,即同一个类中方法的重载(overload),方法名称相同,但是参数的个数或类型不同。那感觉有点类似于同一个厨师,给他不同的食材,可以做出千姿百态的菜品。

纵向多态,即子类对父类方法进行改写或覆盖(override),方法名称与参数完全相同,但是方法的代码不同。那感觉有点类似于长久后浪推前浪,青出于蓝而胜于蓝。

由“一题多解”,自然想到“一诗多译”。横向多态对应横向多译,即不同人对同一首诗进行翻译,给出不同译诗,可以比较不同译者的水平与风格,拓展译者的视野;纵向多态对应纵向多译,即同一个人在不同时期对同一首诗进行翻译,给出不同版本,可以比较该译者水平与风格的变化,挖掘译者的深度。我喜欢一首诗的横向多译,通过比较不同译诗,明白自己的不足,学习别人的长处;同时,我也喜欢一首诗的纵向多译,也许过一段时间,我会重新翻译自己译过的某首诗,希望能译出一点不同的风味来,也算是一种小小的自我突破,感觉蛮有乐趣的。

Java讲课笔记10:类的封装相关推荐

  1. 多重继承_Python 和 Java 基础对比 10 —— 类的封装、继承和多态

    @Python大星 一.Python 类的封装.继承和多态 封装 继承 Python 支持多父类的继承机制,所以需要注意圆括号中基类的顺序,若是基类中有相同的方法名,并且在子类使用时未指定,Pytho ...

  2. Java学习:类的封装、继承和多态

    [3.1]声明日期类及使用日期对象 public class MyDate{ //类声明 int year,month,day; //成员变量,表示年月日 void set(int y,int m,i ...

  3. Java小白StudyDay06---oop类 对象 封装

    使用IDEA 面向对象 类与对象 封装 一.使用IDEA 二.面向对象 2.1 面向对象的三大特征 三. 类 3.1 类 3.2 对象 3.3 类与对象的关系 3.4 类的使用 3.5对象在内存中的存 ...

  4. Java讲课笔记13:类的继承

    文章目录 零.本讲学习目标 1.理解继承的概念 2.掌握方法的重写 3.掌握super关键字 4.熟悉Object类 一.类的继承 1.继承示意图 2.继承的定义 3.继承的语法格式 4.继承案例演示 ...

  5. Java讲课笔记09:类与对象

    文章目录 零.本讲学习目标 1.理解面向对象的概念 2.熟悉面向对象的三个特点 3.掌握类和对象的定义和使用 4.熟悉访问控制符 一.面向对象概述 1.什么是面向对象? 2.面向过程 vs. 面向对象 ...

  6. 2020年Java讲课笔记目录

    一.讲课笔记 Java讲课笔记01:搭建Java开发环境 Java讲课笔记02:Java集成开发环境 Java讲课笔记03:Java基本语法 Java讲课笔记04:变量与常量 Java讲课笔记05:运 ...

  7. Java中dimension类详解

    dimension - Java的一个类 dimension是Java的一个类,封装了一个构件的高度和宽度,这个类与一个构件的许多属性具有相关性,因此在Component类中定义多个与之有关的方法,L ...

  8. 【JAVA SE】第六章 面向对象、对象和类以及封装

    第六章 面向对象.对象和类以及封装 文章目录 第六章 面向对象.对象和类以及封装 一.面向对象 1.概念 2.面向对象的三大特征 二.对象和类 1.基本概念 2.Java中的对象 3.Java 中的类 ...

  9. Java基础-学习笔记(六)——类的封装性

    1.类是模板,对象是具体的实例 2.如果成员函数中的局部变量与成员变量名一致,则该方法对这个变量名的访问是局部变量 class lesson1 {int age=9;void talk(){int a ...

最新文章

  1. pandas 或者字段值_pandas用法总结
  2. pytorch拼接函数:torch.stack()和torch.cat()--详解及例子
  3. .NET 的一点历史往事:和 Java 的恩怨
  4. 《此生未完成》痛句摘抄(3)
  5. nginx 判断手机端跳转_nginx基本功能和工作原理
  6. Android 要收费?周鸿祎:这是迟早的事!
  7. axure7 地址选择_Axure教程:省市县三级联动选择(全国省市区数据)
  8. PS抠图方法[photoshop中文教程]
  9. 2022强网杯web方向wp
  10. android电视安全模式,电视安全模式怎么解除
  11. boren - 飞机大战9之战机爆炸
  12. Directsound 与 Waveout 有何不同
  13. LA 7456 Least Crucial Node
  14. waf服务器部署位置,【原】WAF 防火墙 部署
  15. 兔年首文迎新春-Cesium橘子洲烟花礼赞
  16. servlet,filer,listener 推荐阅读
  17. 20、CSS中单位:【px和%】【em和rem】【vw|vh|vmin|vmax】的区别
  18. 如何安装cnpm和检查cnpm是否已经安装成功
  19. 接地电阻测试仪常用的5种接线方法介绍
  20. springboot毕设项目小区停车场管理系统zvzay(java+VUE+Mybatis+Maven+Mysql)

热门文章

  1. 【并发技术16】线程同步工具Exchanger的使用
  2. 大前端的自动化工厂(1)——Yeoman
  3. Python提取 Gprmax 3.0 的out文件数据
  4. day6 java成员变量与局部变量以及对象的建立
  5. Typora入门基本教程
  6. c#写图像tif gdal_Gdal系列 (二)读取图像基本操作b + 简单波段合成
  7. 二维动态规划降维误差一般为多少_动态规划--5道题入门
  8. 可以自定义公式的计算器_Excel万能个税计算器,税率对比显示,自定义增税点自动计算结果...
  9. Collection(单列集合)
  10. 台式电脑蓝牙关了之后就开不了_台式电脑如何通过iPhone上网?