java基础

01:数据类型

一、Java 数据类型总览

Java 是强类型语言,每个变量必须先声明类型,且类型不能随意改变,这也是Java语言安全性和规范性的重要体现。

Java中的数据类型整体分为两大类:

  1. 基本数据类型:共计8种,是Java内置的基础类型,存储的是具体数值

  2. 引用数据类型:除8种基本类型外,其余所有类型都属于引用类型,存储的是对象的内存地址

二、基本数据类型(Primitive Types)

Java的基本数据类型一共有8种,按照用途可以划分为4组,分别是整数类型、浮点类型、字符类型、布尔类型,下面逐一详细讲解。

(一)整数类型(4种)

整数类型用于存储整数数值,按照占用内存大小和取值范围不同,分为4种,具体特性如下:

byte类型:占用1个字节,取值范围在-128 ~ 127之间,适用于存储极小范围的整数

short类型:占用2个字节,取值范围在-32768 ~ 32767之间,日常开发中较少使用

int类型:占用4个字节,取值范围在-2147483648 ~ 2147483647之间,是Java中整数的默认类型,也是开发中最常用的整数类型

​long类型:占用8个字节,取值范围极大,适合存储超大整数,声明该类型的变量时,数值末尾必须添加大写 L 作为标识(小写l容易和数字1混淆,不推荐使用)

代码示例:

java 复制代码
 // 不同整数类型变量声明

byte a = 10;

short b = 100;

int c = 1000;

long d = 10000000000L;

(二)浮点类型(2种)

浮点类型用于存储带有小数的数值,分为单精度浮点型和双精度浮点型两种:

float类型:占用4个字节,属于单精度浮点数,声明该类型变量时,数值末尾必须添加大写 F 作为标识

​double类型:占用8个字节,属于双精度浮点数,精度比float更高,是Java中浮点数的默认类型,开发中处理小数优先使用该类型

代码示例:

java 复制代码
 // 不同浮点类型变量声明

float f = 3.14F;

double d = 3.1415926;

(三)字符类型(1种)

字符类型只有char一种,占用2个字节,专门用于存储单个字符。

字符类型的值必须使用单引号包裹

​可以存储英文字符、中文字符,也可以存储Unicode编码对应的数字,编码会自动转为对应字符

代码示例:

java 复制代码
 // char类型变量声明

char c1 = 'A';

char c2 = '中';

char c3 = 97; // Unicode编码97对应字符'a'

(四)布尔类型(1种)

布尔类型只有boolean一种,主要用于逻辑判断,占用内存大小不固定。

该类型只有两个取值: true (真)和 false (假)

​常用于条件判断、循环控制等逻辑场景

代码示例:

java 复制代码
 // boolean类型变量声明

boolean flag1 = true;

boolean flag2 = false;

三、基本数据类型默认值

在Java中,成员变量的基本数据类型会有默认初始值,具体如下:

整数类型(byte、short、int、long):默认值均为0

​浮点类型(float、double):默认值均为0.0

​字符类型(char):默认值为 \u0000 (空字符)

​布尔类型(boolean):默认值为false

四、基本数据类型转换

Java支持基本数据类型之间的转换,分为自动类型转换和强制类型转换两种,转换需遵循数据范围规则。

1. 自动类型转换(隐式转换)

当小范围数据类型赋值给大范围数据类型时,Java虚拟机会自动完成类型转换,无需手动处理。

转换规则:byte → short → int → long → float → double,char类型可自动转换为int、long、float、double类型

代码示例:

java 复制代码
 int num = 100;

// int类型自动转换为double类型

double decimal = num;

2. 强制类型转换(显式转换)

当大范围数据类型需要赋值给小范围数据类型时,必须手动进行强制类型转换,否则编译报错。

转换格式:(目标数据类型) 变量名/数值

注意:强制类型转换可能会出现精度丢失、数据溢出的问题,需谨慎使用

代码示例:

java 复制代码
 double num = 3.99;

// 强制转换为int类型,直接舍弃小数部分,结果为3

int integer = (int) num;

五、引用数据类型

引用数据类型是基于基本数据类型构建的复杂类型,存储的是对象在堆内存中的地址,而非具体数值,主要包含以下几类:

  1. 类(Class):比如String字符串类、自定义的实体类、Object基类等,其中String是开发中最常用的引用类型

  2. 接口(Interface):使用interface关键字定义的接口类型

​3. 数组([]):同一类型数据的集合,比如int[]、String[]都属于引用类型

​4. 除此之外,枚举、注解、Lambda表达式等也都属于引用数据类型

String类型代码示例:

java 复制代码
 // String是引用类型,使用双引号存储字符串

String name = "Java开发者";

六、基本类型与包装类

Java为8种基本数据类型分别提供了对应的包装类(引用类型),方便在集合、泛型等场景中使用,对应关系如下:

byte对应Byte、short对应Short、int对应Integer、long对应Long

​float对应Float、double对应Double

​char对应Character

​boolean对应Boolean

JDK 1.5及之后版本,支持自动装箱和自动拆箱,简化基本类型和包装类的转换:

自动装箱:基本数据类型自动转换为对应的包装类

​自动拆箱:包装类自动转换为对应的基本数据类型

代码示例:

java 复制代码
 // 自动装箱:int → Integer

Integer num = 10;

// 自动拆箱:Integer → int

int value = num;

七、基本类型与引用类型核心区别

  1. 存储方式:基本类型存储具体数值,存放在栈内存;引用类型存储内存地址,对象实体存放在堆内存

​2. 默认值:基本类型有固定默认值,引用类型默认值为null

​3. 比较方式:基本类型用 == 比较数值大小;引用类型用 == 比较内存地址,比较内容需使用equals()方法

​4. 空值:基本类型不能赋值为null,引用类型可以赋值为null

八、高频面试题

  1. 声明long类型变量时,为什么必须加L?

答:因为整数默认是int类型,超过int范围的数值不加L会编译报错,小写l易混淆,推荐用大写L。

​2. float f = 3.14; 这句代码是否正确?

答:不正确,浮点数默认是double类型,double转float需要强制转换,正确写法是float f = 3.14F;

​3. char类型可以存储中文字符吗?

答:可以,Java中char采用Unicode编码,能够覆盖所有中文字符。

​4. 基本类型和引用类型的区别是什么?

答:核心区别是存储内容、存储位置、比较方式、空值支持不同,详细可参考上文区别总结。

02:数据类型扩展及面试题讲解

一、基本数据类型细节扩展

1. 整数类型底层与字面量

Java 整数默认是 int 类型,若数值超过 int 范围(-2147483648 ~ 2147483647),必须在末尾加 L 声明为 long 类型,否则编译报错。

整数支持多种进制字面量:

十进制: 100 (默认写法)

​二进制: 0b100 (JDK7+,前缀 0b )

​八进制: 0144 (前缀 0 ,易混淆,不推荐使用)

​十六进制: 0x64 (前缀 0x ,大小写均可)

代码示例:

java 复制代码
int dec = 100; // 十进制

int bin = 0b1100100; // 二进制,值为100

int oct = 0144; // 八进制,值为100

int hex = 0x64; // 十六进制,值为100

long bigNum = 10000000000L; // 超过int范围,必须加L

2. 浮点类型精度问题

float 和 double 是近似存储,无法精确表示部分小数(如 0.1 ),这是二进制浮点的固有特性。

金融、货币等高精度场景,禁止直接使用 float / double ,推荐使用 java.math.BigDecimal 保证计算精度。

代码示例:

java 复制代码
// 浮点精度问题示例

double a = 0.1;

double b = 0.2;

System.out.println(a + b); // 输出0.30000000000000004,而非0.3



// 高精度计算推荐使用BigDecimal

BigDecimal num1 = new BigDecimal("0.1");

BigDecimal num2 = new BigDecimal("0.2");

System.out.println(num1.add(num2)); // 输出0.3,结果精确

3. char 类型本质与 Unicode

char 本质是16位无符号整数,对应 Unicode 编码表中的字符编号。

可直接赋值为 Unicode 编码,也可存储中文字符(Java 采用 UTF-16 编码)。

代码示例:

java 复制代码
char c1 = 'A'; // 字符字面量

char c2 = 65; // 十进制编码,对应'A'

char c3 = '\u4e2d'; // Unicode转义,对应'中'

char c4 = '国'; // 直接存储中文字符

4. boolean 类型与 JVM 实现

Java 语法层面 boolean 只有 true 和 false 两个值,JVM 底层通常用 int (1表示true,0表示false)或 byte 实现。

boolean 不能与其他基本类型进行强制转换,也不能用 1/0 代替 true/false 。

二、高频面试题汇总

  1. 为什么 long 类型要加 L,不加会怎样?

答:整数默认是 int ,若数值超过 int 最大值却不加 L ,编译器会报「整数过大」错误;小写 l 易与数字 1 混淆,规范要求用大写 L 。

​2. float f = 3.14; 编译报错吗?为什么?

答:会报错。浮点数默认是 double 类型, double 范围大于 float ,需手动加 F 声明为 float ,或进行强制转换。

​3. char 类型可以存储中文吗?为什么?

答:可以。Java 中 char 采用 UTF-16 编码,覆盖了所有 Unicode 字符,包括中文字符;但生僻字或 emoji 可能占用 2 个 char (代理对)。

​4. 为什么不能用 float/double 处理金钱?

答:因为二进制浮点无法精确表示 0.1 等小数,会导致精度丢失,引发金额计算错误。

03:类型转换

一、自动类型转换(隐式转换)

定义:小范围类型 → 大范围类型,JVM 自动完成转换,无需手动干预。

转换优先级: byte → short → int → long → float → double ; char 可直接转换为 int / long / float / double ,但不能与 byte / short 自动转换。

代码示例:

java 复制代码
int num = 100;

double d = num; // int自动转double,结果100.0



char c = 'A';

int i = c; // char自动转int,结果65

二、强制类型转换(显式转换)

定义:大范围类型 → 小范围类型,必须手动添加 (目标类型) 语法,否则编译报错。

风险:可能出现精度丢失(浮点转整数)或数据溢出(整数转小范围整数)。

代码示例:

java 复制代码
// 浮点转int,精度丢失

double pi = 3.14159;

int intPi = (int) pi; // 结果为3,直接舍弃小数部分



// 整数溢出示例

int maxInt = 2147483647;

byte b = (byte) maxInt; // 结果为-1,高位被截断,发生溢出

三、表达式中的类型提升

当表达式中包含多种基本类型时,Java 会自动将所有操作数提升为表达式中范围最大的类型。

规则: byte / short / char 会先提升为 int ,再与其他类型按优先级提升。

代码示例:

java 复制代码
byte a = 10;

short b = 20;

int c = a + b; // a和b先提升为int,结果为int



long d = 100L;

double e = 1.1;

double f = d + e; // d提升为double,结果为double

四、包装类与自动装箱拆箱

自动装箱:基本类型 → 包装类(如 int → Integer ),JVM 自动调用 valueOf() 方法。

自动拆箱:包装类 → 基本类型(如 Integer → int ),JVM 自动调用 intValue() 等方法。

注意:包装类对象比较需用 equals() , == 会比较内存地址(缓存池内的小数值除外)。

代码示例:

java 复制代码
// 自动装箱

Integer num = 10; // 等价于 Integer num = Integer.valueOf(10);

// 自动拆箱

int i = num; // 等价于 int i = num.intValue();



// 包装类比较

Integer a = 127;

Integer b = 127;

System.out.println(a == b); // true,127在缓存池内,地址相同

Integer c = 128;

Integer d = 128;

System.out.println(c == d); // false,128超出缓存池,地址不同

System.out.println(c.equals(d)); // true,比较数值

04:变量、常量、作用域

一、变量(Variable)

1. 变量的声明与初始化

语法: 数据类型 变量名 = 初始值; (初始化可延后,但使用前必须赋值)。

Java 是强类型语言:变量类型一旦声明,无法修改,只能存储对应类型的值。

代码示例:

java 复制代码
int age; // 声明变量

age = 18; // 初始化

String name = "张三"; // 声明并初始化

2. 变量的分类(按作用域)

局部变量:定义在方法/代码块内部,作用域仅限当前方法/代码块,生命周期随方法/代码块执行结束而销毁,无默认值,必须手动初始化后才能使用。

​成员变量(实例变量):定义在类内、方法外,作用域覆盖整个类的非静态方法,生命周期随对象创建而诞生、对象销毁而销毁,默认值与数据类型默认值一致。

​类变量(静态变量):定义在类内、方法外,用 static 修饰,作用域覆盖整个类,被所有对象共享,生命周期随类加载而诞生、类卸载而销毁,默认值与数据类型默认值一致。

代码示例:

java 复制代码
public class Student {

    // 类变量(静态变量)

    public static String school = "XX大学";

    // 成员变量(实例变量)

    private String name;

    private int age;



    public void study() {

        // 局部变量

        String subject = "Java";

        System.out.println(name + "在学习" + subject);

    }

}

二、常量(Constant)

定义:值不可修改的变量,用 final 关键字修饰。

命名规范:全部大写,单词间用下划线分隔(如 MAX_VALUE )。

分类:

静态常量: static final 修饰,属于类,全局共享,推荐使用。

​实例常量: final 修饰,属于对象,创建后不可修改。

​局部常量:方法内用 final 修饰,作用域内不可修改。

代码示例:

java 复制代码
public class MathConst {

    // 静态常量(推荐)

    public static final double PI = 3.1415926;

    // 实例常量

    private final int MAX_AGE = 120;



    public void test() {

        // 局部常量

        final int MIN_AGE = 0;

        // MIN_AGE = 1; // 编译报错,常量不可修改

    }

}

三、作用域(Scope)

作用域:变量可被访问的代码范围,由 {} 决定。

就近原则:当局部变量与成员变量同名时,局部变量优先(可通过 this / 类名 访问成员变量)。

生命周期:变量在作用域开始时创建,作用域结束时销毁,无法在作用域外访问。

代码示例:

java 复制代码
public class ScopeDemo {

    private int num = 10; // 成员变量



    public void test() {

        int num = 20; // 局部变量,与成员变量同名

        System.out.println(num); // 输出20,就近原则

        System.out.println(this.num); // 输出10,通过this访问成员变量

    }

}

05:基本运算符

一、算术运算符

包括: + 、 - 、 * 、 / 、 % 、 ++ 、 --

/ :整数相除只保留整数部分,小数部分直接舍弃。

​% :取模运算,结果符号与被除数一致。

​++ / -- :自增/自减运算符,分前缀(先运算后取值)和后缀(先取值后运算)。

代码示例:

java 复制代码
int a = 10;

int b = 3;

System.out.println(a / b); // 输出3

System.out.println(a % b); // 输出1



int c = 5;

System.out.println(c++); // 输出5,后自增

System.out.println(++c); // 输出7,先自增

二、赋值运算符

包括: = 、 += 、 -= 、 *= 、 /= 、 %=

复合赋值运算符会自动进行类型转换,避免手动强转。

代码示例:

java 复制代码
 short s = 10;

s += 5; // 等价于 s = (short)(s + 5),自动强转

System.out.println(s); // 输出15

三、比较运算符

包括: == 、 != 、 > 、 < 、 >= 、 <=

结果均为 boolean 类型,用于条件判断。

​注意: == 比较基本类型时比较数值,比较引用类型时比较内存地址。

代码示例:

java 复制代码
 int x = 10;

int y = 20;

System.out.println(x > y); // 输出false

System.out.println(x != y); // 输出true

四、逻辑运算符

包括: && (短路与)、 || (短路或)、 ! (非)

短路特性: && 左侧为 false 时,右侧不执行; || 左侧为 true 时,右侧不执行,可提升效率。

代码示例:

java 复制代码
int a = 5;

System.out.println(a > 10 && a++ > 0); // 输出false,a++未执行

System.out.println(a); // 输出5



System.out.println(a < 10 || a++ > 0); // 输出true,a++未执行

System.out.println(a); // 输出5

五、位运算符(了解)

包括: & (与)、 | (或)、 ^ (异或)、 ~ (取反)、 << (左移)、 >> (右移)、 >>> (无符号右移)

位运算符直接操作二进制位,常用于高效计算。

代码示例:

java 复制代码
 int a = 60; // 二进制 0011 1100

int b = 13; // 二进制 0000 1101

System.out.println(a & b); // 输出12(0000 1100)

System.out.println(a << 2); // 输出240,等价于乘以4

六、三元运算符

语法: 条件表达式 ? 表达式1 : 表达式2

条件为 true 时执行表达式1,为 false 时执行表达式2。

代码示例:

java 复制代码
 int score = 85;

String result = score >= 60 ? "及格" : "不及格";

System.out.println(result); // 输出"及格"

七、运算符优先级

优先级从高到低大致为:

() > 单目运算符( ++ / -- / ! )> 算术运算符 > 位移运算符 > 比较运算符 > 逻辑运算符 > 三元运算符 > 赋值运算符

实际开发中建议用 () 明确优先级,避免歧义。

Java 基础 08 至 12 阶段聚焦运算符进阶、包机制与文档生成三大核心模块,是夯实 Java 语法基础、规范代码编写的关键环节。以下结合 CSDN 博客排版需求,对核心知识点进行结构化总结,兼顾专业性与可读性。

06:自增自减运算符 + 初识 Math 类

1. 自增自减运算符(核心考点)

自增(++)、自减(--)是单目运算符,分为前置后置 两种形式,核心区别在于运算顺序与返回值

形式 语法规则 执行特点 示例(int a=1;)
前置自增 ++变量 先自增,后取值 int b=++a; → a=2,b=2
后置自增 变量++ 先取值,后自增 int b=a++; → a=2,b=1
前置自减 --变量 先自减,后取值 int b=--a; → a=0,b=0
后置自减 变量-- 先取值,后自减 int b=a--; → a=0,b=1

⚠️ 避坑要点

  • 运算符仅作用于变量,不可用于常量(如5++编译报错);
  • 嵌套使用时逻辑易混乱,建议拆分代码分步计算,提升可读性。

2. 初识 Math 类(常用工具类)

Math 类是 Java 提供的数学运算工具类,位于java.lang包下,无需导包即可直接使用,所有方法均为静态方法,核心常用 API 如下:

方法 功能 示例
Math.abs(double a) 取绝对值 Math.abs(-5) → 5.0
Math.ceil(double a) 向上取整 Math.ceil(3.2) → 4.0
Math.floor(double a) 向下取整 Math.floor(3.8) → 3.0
Math.round(double a) 四舍五入 Math.round(3.5) → 4
Math.random() 生成 [0.0,1.0) 随机数 (int)(Math.random()*10) → 0-9 随机整数
Math.pow(double a, double b) 计算 a 的 b 次幂 Math.pow(2,3) → 8.0
Math.sqrt(double a) 开平方 Math.sqrt(16) → 4.0

07:逻辑运算符 + 位运算符

1. 逻辑运算符(布尔运算核心)

用于处理布尔值之间的逻辑关系,分为短路逻辑普通逻辑两类,是条件判断的基础:

运算符 名称 运算规则 短路特性 示例(boolean a=true, b=false)
&& 短路与 两边为 true 则结果为 true 左边为 false,右边不执行 a&&b → false(b 不执行)
` ` 短路或 两边为 false 则结果为 false 左边为 true,右边不执行 `a b` → true(b 不执行)
! 取反(true 变 false,false 变 true) !a → false
& 普通与 两边为 true 则结果为 true 无短路,两边都执行 a&b → false(b 执行)
` ` 普通或 两边为 false 则结果为 false 无短路,两边都执行 `a b` → true(b 执行)
^ 异或 相同为 false,不同为 true a^b → true

2. 位运算符(二进制底层运算)

直接操作整数的二进制位,效率极高,适用于底层性能优化场景,核心运算符如下:

运算符 名称 运算规则(以 int a=3 (0011), b=5 (0101) 为例) 结果
& 按位与 对应位都为 1,结果为 1,否则为 0 a&b=1(0001)
` ` 按位或 对应位有 1,结果为 1,否则为 0 `a b=7(0111)`
^ 按位异或 对应位不同为 1,相同为 0 a^b=6(0110)
~ 按位取反 0 变 1,1 变 0(补码运算) ~a=-4
<< 左移 各二进制位全部左移,高位丢弃,低位补 0 a<<1=6(0110)(等价于 * 2)
>> 右移 各二进制位右移,正数高位补 0,负数补 1 a>>1=1(0001)(等价于 / 2)
>>> 无符号右移 右移后高位统一补 0 负数右移结果为正数

💡 实用技巧:左移 / 右移运算效率远高于乘除 2,底层优化优先使用位运算。

08:三元运算符 + 语法小结

1. 三元运算符(简化条件判断)

三元运算符是if-else的简化形式,语法简洁,适用于简单的二选一逻辑:

  • 语法格式条件表达式 ? 表达式1 : 表达式2

  • 执行逻辑 :条件为true,执行表达式 1;为false,执行表达式 2。

  • 示例

    复制代码
    int score = 85;
    String result = score >= 60 ? "及格" : "不及格";
    System.out.println(result); // 输出:及格

⚠️ 注意:表达式 1 和 2 需为同类型(或可自动转换),不可嵌套过深,否则降低代码可读性。

2. 运算符语法小结(易混点梳理)

  • 运算符优先级:单目运算 > 算术运算 > 关系运算 > 逻辑运算 > 三元运算 > 赋值运算
  • 括号()可强制提升优先级,是解决运算混乱的核心手段;
  • 赋值运算(=)与比较运算(==)需严格区分,避免赋值符误用于条件判断。

09:包机制(代码组织规范)

包机制是 Java 用于分类管理类、避免命名冲突的核心机制,是大型项目开发的基础规范。

1. 包的定义与命名

  • 定义 :使用package关键字声明包,必须放在 Java 文件的第一行(注释除外);

  • 命名规范 :遵循小写字母 + 公司域名倒序 原则,避免使用关键字,如com.xxx.utilcn.xxx.entity

  • 示例

    复制代码
    package com.javabase.operator; // 声明当前类所属包

2. 包的导入与使用

  • 导入包 :使用import关键字导入指定类或整个包,简化类名引用;
    • 导入单个类:import java.util.Scanner;
    • 导入整个包:import java.util.*;(不推荐过度使用,易引发类名冲突)
  • 静态导入 :导入静态成员(方法 / 常量),可直接调用(需谨慎使用);
    • 示例:import static java.lang.Math.*; → 可直接调用abs()random()
  • 包的访问权限 :结合public/default/protected/private,控制类、成员的访问范围。

3. 包的本质

包在物理上对应文件系统的目录结构 ,如com.xxx.Operator类,对应文件路径为com/xxx/Operator.java

10:JavaDoc 生成文档(代码规范化必备)

JavaDoc 是 Java 官方提供的文档生成工具,可通过代码中的注释自动生成标准化 API 文档,是专业开发的必备技能。

1. 注释规范(文档生成基础)

JavaDoc 仅识别多行注释/** ... */ 文档注释/** ... */,需按规范编写:

  • 类注释:放在类上方,描述类功能、作者、版本等;
  • 方法注释:放在方法上方,描述功能、参数、返回值、异常等;
  • 成员注释:放在成员变量上方,描述变量含义。

2. 常用注释标签

标签 作用 示例
@author 标注作者 @author 张三
@version 标注版本 @version 1.0
@param 描述方法参数 @param num 输入的整数
@return 描述返回值 @return 计算结果
@throws 描述抛出异常 @throws Exception 异常说明
@since 标注支持的 JDK 版本 @since JDK 1.8

3. JavaDoc 生成步骤

  1. 编写规范的文档注释;
  2. 使用命令行或 IDE 生成文档:
    • 命令行方式:

      复制代码
      javadoc -d doc -author -version com.javabase.Operator.java
      • -d doc:指定生成文档的输出目录为doc
      • -author/-version:保留作者和版本信息;
    • IDE 方式(IDEA):右键类 → GenerateJavaDoc → 配置输出路径,点击OK

4. 生成文档效果

生成的文档为 HTML 格式,包含类结构、方法详情、参数说明等,可直接在浏览器打开,与 JDK 官方 API 文档格式一致。

相关推荐
splage2 小时前
Java进阶之泛型
java·开发语言
Meepo_haha2 小时前
python爬虫——爬取全年天气数据并做可视化分析
java
xiaohe072 小时前
JAVA系统中Spring Boot 应用程序的配置文件:application.yml
java·开发语言·spring boot
羊小蜜.2 小时前
C++17: map & multimap—— 键值映射容器
开发语言·c++·stl
Eternity_GQM2 小时前
【CMake入门】
java·开发语言
糖猫猫cc2 小时前
Kite 实现逻辑删除
java·kotlin·orm·kite
Memory_荒年2 小时前
Dubbo调优实战:从QPS 1000到10000的惊险过山车之旅
java·后端·dubbo
Cosolar2 小时前
别再羡慕 Python 了!Java 开发者的 AI Agent 全指南:四大框架从选型到实战
java·人工智能·后端
xw-busy-code2 小时前
npm 包管理笔记整理
前端·笔记·npm