• Java刷题大全(笔试题)【大厂必备】(基础)


    Java刷题

    标识符的规范

    • 26个字母,数字0-9,_ , $

    • 数字不要开头

    类与文件名

    1. 有一个类中的类名字前面没有public的情况下,类名和文件名没有任何约束

    2. 一个java文件中可以有多个类,编译后都会生成类.class文件

    3. 如果一个类的前面有public,那么这个类必须与文件名字一致

    4. 一个类中只能有一个public类,其余的类都不能有public

    Java基本数据类型中,表输范围最大的是(D)

    A. short

    B. long

    C. int

    D. float

    题解

    byte 1个字节

    short 2个字节

    int 4个字节

    long 8个字节

    float 4个字节

    double 8个字节

    上述的数据类型的表述范围:从上向下一次递增

    循环结构是否合法

    循环条件要以表达式的方式来体现,结果是boolean类型,不能在条件里面定义变量

    循环条件必须是boolean类型,int不能转成boolean

    计数器如果是多个变量,用逗号分割就可以了

    我们可以在循环前面加上标签,来通过break或continue来跳出当次循环或整个循环,只需要在后面指定标签名

    例子:outer:for( ; ; ) break outer;

    do-while循环

    do{
       //循环体 
    }while(循环条件);
    
    • 1
    • 2
    • 3

    局部变量初始化

    下面的程序输出什么()

    public class Conditional{
        public static void main(String args[]){
            int c;
            sout(c);
            sout(c++);
            sout(c)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    A. 0 1 1 B. 0 0 1 C. 000 D. 编译不通过

    题解

    正确答案为D

    容易混淆答案:C这是决定错误的,因为局部变量在使用时,必须要初始化

    考点

    局部变量在使用之前是必须初始化的

    三元运算符

    在三元运算符的使用与遇见double类型与int类型在运算的时候结果会像double来转换

    ++与–问题

    执行语句int i=1,j=++i;后i与j的值分别为()

    i=2,j=2

    题解

    答案为2 2

    因为++i的含义是在当前行i=i+1;

    i++的含义是在表达式执行完毕后,i=i+1;

    考点

    如果多个变量定义在同一行,用逗号分隔开

    注意++与–的坑

    main方法是Java应用程序执行的入口点,关于main方法的方法头一下哪些是合法的?

    public static void main(String[] args) 正确

    public static int main(String[] args) 错误,返回类型为void❌

    public void main(String[] args) 错误缺少static❌

    public static void main(String args[]) 正确,这个要注意

    题解

    第一个与第四个都是对的

    敲击psvm

    为什么第四个是对的呢,那是因为在java中也可以使用 String args[]方法来定义数组,虽然是不推荐的,但是也提供了这个方式,所以第四个也是对的

    考点

    数组的另一种写法

    • String[] args
    • String args[]

    死循环

    在不考录极值的情况下,下面哪个循环会导致死循环、

    for(int k=0;k<0;k++)

    for(int k=10;k>0;k–)

    for(int k=0;k<10;k–)

    for(int k=0;k>0;k++)

    题解

    for(int k=0;k<0;k++) 因为for循环执行顺序,我们都不会进入循环,也就不会导致死循环

    for(int k=10;k>0;k–) 这个循环是可以中止的

    for(int k=0;k<10;k–) 此循环是死循环,但是注意不是真正意义上的死循环,因为题目上说不考虑极值的情况下,如果到属性极值也是会自动结束循环的

    for(int k=0;k>0;k++) 因为for循环执行顺序,我们都不会进入循环,也就不会导致死循环

    主要注意循环执行各个部分的顺序

    考点

    各种循环的执行顺序,以及考虑极值问题

    不同数据类型加法

    System.out.println(‘5’+2);的输出结果应该是(D)

    A. 52 B. 7 C. 2 D. 55

    题解

    我们的‘5’,是单引号所以为char类型,我们需要将char类型转换为int类型,通过ASCII码表来进行转换所以为55

    基础的变量定义

    指出正确的表达式(B,C,D)

    A. byte a=128; 128超过byte的范围所以为int类型了,无法自动转换,❌

    B. Boolean b=null; 在boolean类型中只能有false和true两种类型,但是Boolean是boolean的包装类,可以为null ✔

    C. long l= 0xfffL; 0x代表16进制可以保存✔

    D. double d=0.9239d; 正确的,我们平时不加,是因为浮点型默认是double类型所以d可以省略✔

    下面语句中在编译时候不会出现警告或错误的是(A)

    A. float f=3L; 3L长度是Long类型,因为float的范围大于long所以可以直接赋值 ✔

    B. char c=“c”; 双引号表示字符串,字符串无法存入字符中,因为字符串底层是数组,引用数据类型❌

    C. boolean b=null; boolean只有两个false和true❌

    D. int i=10.0; 10.0为默认浮点类型即为double类型,int类型无法赋值,需要强转❌

    switch

    下面的方法,当输入i为2的时候返回值是多少?()

    public static getValue(int i){
        long result=0;
        switch(i){
            case 1:
                result=result+i;
            case 2:
                result=result+i*2;
            case 3:
                result=result+i*3;
        }
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    答案:编译异常

    题解

    本题有两个题解一个是返回值类型不匹配,一个是switch中case没有加break的情况

    在我们一般人来说,刚刚看到case这个考题点的时候就有可能会感觉知道考点了,直接就会选错

    一句话:骄兵必败

    注意如果case中没有添加break,那么将符合的case以及下面全部的case全部执行完毕

    前++与后++

    下面的代码中,执行之后i和j的值是什么

    int i=1;int j;
    j=i++*2+3*--i
    
    • 1
    • 2

    答案:1,5

    题解

    我们要注意在i++*2这个表达式执行完毕之后,i会变成什么只需要记住

    i++在执行完表达式之后会变为i=i+1,所以在3*–i这个表达刚刚开始时候i为2

    JVM题目

    下面程序运行的结果正确的是

    public class Demo1{
        public static void main(String[] args){
            int num=0;
            for(int i=0;i<100;i++){
                num=num++;
            }
            System.out.println("num="+num);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    答案:num=0

    题解

    我们需要结合JVM来去理解

    我们只需要记住:num=num++时,num还是原本的值,只有表达式执行结束后的加一,暂时不要管

    短路或

    下面程序运行结果正确的是()

    public class Demo1{
        public static void main(String[] args){
            int num=100;
            int m=20;
            if(m++<20||n==n++)
            System.out.println(n);
          Sout(m)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    答案:100,21

    题解

    本题主要考察两个方面

    一:if没有加{}的情况下,采取就近原则输出最近的一条执行语句

    二:||为短路字符,顾名思义就是当前面为真的时候,短路或后面的表达式将不会执行了

    值传递

    题解:java值传递问题;

    int i=10;

    method(i)

    public void method(int i){

    ​ i=i+10;

    }

    结果为i=10;

    因为当主方法调用函数时候,就相当于将i的值赋给方法的形参i所以不会改变i原本的值

    基本数据类型问题

    题目:

    float a=5;
    sout(a%2);//1.0
    sout(a%2==1);//true,虽然是浮点型,但是数组相等,这个要牢记
    sout(a/2);//2.5,如果a为int类型那么就是2.5取整,不会四舍五入的大家要牢记
    
    • 1
    • 2
    • 3
    • 4

    局部变量作用域

    public class Demo1{
        public static void main(String[] args){
            for(int i=0;i<3;i++){
            System.out.println(i);
            }
          Sout(i)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    题解

    会报错的,因为在for循环定义的i只能作用在for循环中,当循环结束局部变量也会随之消亡,所以属于没有变量i,就不会编译时报错啦

    短路运算符

    &&和||

    &&当有假的时候后面的表达式就不运行了

    ||当有真的时候后面的表达式就不运行了

    数据类型编译错误

    boolean b=true;

    int a=(int)1.23;

    double x=1.1f;

    char c=10;会将10在ASCII表中找到对应的字符,付给c

    全对,数值会根据范围判断类型

    =与==

    注意if里面的条件,是=还是==

    =代表赋值

    ==代表比较

    方法返回值

    当方法返回类型不为void,则必须有返回值,否则报错

    如果一个方法有返回值,那么必须要有一个return值来执行

    return在void方法中为一个 第一个含义就是中止这个方法的调用

    如果数据类型之间可以自动的类型转换也可以

    方法定义下面错误的是

    A 所有的具体类中方法必须有方法体 因为是具体类所以是正确,抽象类的抽象方法可以没有方法体 ✔

    B 参数传递是值传递的方式 ✔

    C 如果方法没有返回值必须声明返回为void 特例:构造器❌

    D 如果方法定义返回为void,则方法中不能出现return语句 ❌

    &的另一种含义

    Java表达式4&5的结果是什么

    答案:4

    & 第一种含义逻辑运算符,可以做位运算,做二进制的数的位运算

    4=0100

    & 5=0101

    ​ 0100=4

    数组

    当编译并运行下面代码会出现什么结果

    public class MyArr{
        public static void main(String[] args){
          int[] i =new int[5];
          Sout(i[5]);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    运行时错误

    数组没有给每个元素值,那么他们默认值是数组类型的默认值

    此题,在运行时抛出一个运行时异常(数组下标越界异常)

    数组定义方式哪个是正确的

    int[] arr = new int[3];✔

    int[] arr = {1,2,3}✔ 存在局限性,必须先定义再使用

    int[] arr = new int[]{1,2,3}✔

    int[] arr = new int[3]{1,2,3}❌ 静态定义数组不需要确定数组长度

    方法重载

    • 两同一不同

      • 同一个类,方法名相同
    • 参数列表不同

      • 参数列表的参数类型
      • 顺序(类型顺序)
      • 个数

    切记与返回值类型没有关系!!!!!!!

    下面哪个方法是重载

    方法:public void cal(int x,int y,int z){}

    public int cal(int x,int y,float z){return 0;} 方法名相同,参数列表中类型不同 ✔

    public int cal(int x,int y,int z){return 0;} 方法名相同,参数列表相同,返回类型不同,与返回值类型有个毛关系❌

    public void cal(int x,int y){} 方法名相同,参数列表中参数个数不同 ✔

    public void cal(int z,int y,float x){} 全相同❌

    构造方法下面正确的是

    构造方法不能被继承 不能继承,子类构造器可以调用✔

    构造方法不能被重写 ✔

    构造方法不能被重载 可以重载,无参构造器,有参构造器 ❌

    构造方法不能声明为private 特例:单例模式 ❌

    关于类说法不正确的是

    类是同种属性和方法的抽象 ✔

    类属于Java语言中的引用数据类型✔

    对象是类的实例✔

    类就是对象❌

    Java语言中哪个包是自动被导入的

    java.util 需要

    java.lang 不需要

    java.default 需要

    java.io 需要

    下面构造器定义正确的是

    有一个类MyClass,它的构造方法声明正确的是

    void MyClass(int x){…}❌

    MyClass(int x){…}✔

    public MyClass(int x){…}✔

    public void MyClass(int x){…}❌

    抽象方法的定义

    在Java中,如果父类中的某些方法不包含任何逻辑,并且需要由子类重写,应该使用(abstract)关键字类声明父类的这些方法

    接口玩转

    下面关于接口说法不正确的是()

    接口的所用方法都是抽象的 ❌默认实现default与静态static方法都是由方法体的,也可以在接口中定义

    接口的所有方法一定都是public的 ✔

    用于定义接口的关键字是implements ❌是interface

    接口中可以定义属性 ❌定义的是常量不是属性。(记住)

    修饰符可以放在哪里

    不能在类(非内部类)上的修饰符是()

    final ✔

    public ✔

    abstract ✔

    protected ❌

    private ❌

    一个类实现多个接口

    例子

    class A implements B,C

    继承

    下面关于继承的叙述正确的是()

    在Java里只允许单一继承 ❌ 因为虽然类是单继承的但是接口是多继承的

    在Java中一个类只能实现一个接口 ❌一个类可以实现多个接口

    Java可以多继承✔

    Java的类单一继承避免了菱形继承的危险✔

    所谓菱形继承就是,当两个类继承了同一个父类,并且重写了相同的方法,后又有一个类继承这两个类,那么就会报错,因为会出现两个方法名字一样,方法参数列表相同,这在Java中是不允许出现的。

    super

    super是调用父类的构造器的,而且必须要出现子类构造器的第一行

    当父类中的构造器为无参构造器,子类会自动默认调用super()

    但是如果父类没有写无参构造器或只写了有参构造器那么,我们就必须调用父类的有参构造器

    子类必须要调用父类的构造器,当我们不写就是调用默认父类的无参构造器

    一个类可以定义多少构造器

    无数个

    类属性和类方法

    关于实例方法和类方法,一下描述正确的是()

    A 实例方法只能访问实例变量❌也可以访问类变量

    B 类方法既可以访问类变量,也可以访问实例变量❌类方法只能访问类变量

    C 类方法只能通过类名来调用❌类方法还可以通过实例对象来调用

    D 实例方法只能通过对象来调用✔

    题解

    实例变量就是对象属性

    类方法就是类的静态方法

    类变量与类方法在类加载的时候就会被创建

    实例变量与实例方法在类实例化对象的之后被创建

    解题关键

    声明周期

    关于接口的定义和实现,下列描述正确的是

    接口定义中的方法都只有定义没有实现❌在jdk8中接口的默认方法与静态方法是可以写方法体的

    接口定义中的变量都必须写明final和static❌不是必须要写的,在默认状态下也是public static final

    如果一个接口由多个类来实现,则这些类在实现该接口中的方法时应采用统一的代码❌因为如果写统一的代码,那接口还有啥意义,失去了多态的体现

    如果类实现一个接口,则必须实现该接口中的所有抽象方法,接口方法未必声明为public✔

    关于抽象类的说法正确的是

    某个抽象类的父类是抽象类,则这个子类必须重写父类的所有抽象方法❌不是必须重写父类的抽象方法,如果没有重写完全那么子类也是必须是抽象类

    接口和抽象类是一回事❌当然不是一回事,抽象类可以有属性、方法、构造器,接口没有构造器,且接口的方法只能为抽象方法或者默认静态方法

    绝对不能用抽象类去创建对象✔不要与new 抽象类名(){}这个方法混淆,这个方法是创建抽象类的匿名内部类

    抽象类中不能有非抽象方法❌可以有非抽象方法

    关于final下面描述不正确的是

    final class不能被继承✔

    final 变量不能被修改✔

    final成员变量不可以在构造方法中赋值❌final修饰成员变量是必须要初始化的,我们可以显性赋值,也可以在构造器中赋值来初始化

    final方法不能被覆盖(override)✔

    抽象类与接口

    抽象类不一定有抽象方法

    定义抽象类的目的是为被继承,因为抽象类是不能被实例化的,只能被继承

    interface中定义的方法不一定全是抽象方法,还有静态方法与默认方法

    interface中定义的变量全部都是final static变量,也就是常量

    关于接口常量定义正确的是

    在Java接口中,使用以下声明语句来定义公有的int型常量MAX

    public int MAX=100;

    final int MAX=100;

    public static int MAX=100;

    public static final int MAX=100;

    上述四种都是可以的

    关于构造器下面说法正确的是

    类必须显示定义构造函数❌当类没有声明有参构造器,默认声明的是无参构造器

    构造函数的返回值类型是void❌构造函数没有返回值、

    构造函数和类有相同的名字,并且不能带任何参数❌可以携带参数,有参构造器

    一个类可以定义多个构造函数✔

    注意构造器

    我们要注意构造器是什么,是没有参数的,这就是一些题的坑

    父类的私有属性不会被继承

    在子类构造器第一行必须要调用的父类构造器,要么默认调用无参构造器,要么主动调用有参构造器(在父类没有无参构造器的情况下)

    多态里面的父子类的转换

    class A{}
    class B extend A{}
    class C extend A{}
    psvm{
         A p0=new A();
         B p1=new B();
         C p2=new C();
         A p3=new B();
         A p4=new C();
        //插入代码
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    p0=p1✔p0是父类,p1是子类B,子类可以自动转化为父类

    p1=p2❌二者都是A的子类,无法赋值

    p2=p4❌p2是子类,p4是父类,父类转子类需要强制类型转换

    p2=©p1❌二者都是子类无法进行强制类型转换,类型转换只存在于子父类中

    类中的属性是有默认值的

    在多态我们要记得一件事情那就是

    父类 a=子类b();

    这样类似的情况下,属性是不会被继承的,如果二者都有同名属性,那么a依旧会调用父类的属性

    属性没有继承

    构造器this和super

    当构造器中使用this调用了本类的其他构造器那么就不会再调用父类构造器

    上面这句话是相对的,其实在你调用本类的其他构造器的情况下,所引用的构造器一样会调用父类构造器的,所以子类构造器一定会调用父类构造器这句话是没有错的

    在构造器中使用this调用了本类的其他构造器要将this.()放在构造器第一行哦

    子类的优先级高,有限匹配子类,没有依次向上转型

    对象创建的属性赋值流程

    new一个对象的时候

    1.创建对象

    2.赋值属性后面有等号的属性值

    3.执行构造器方法体

    类属性加载流程和单例综合

    public class Test1{
        psvm{
            Singleton s=Singleton.getSingleton();
            sout("count1="+count1);
            sout("count2="+count2);
        }
    }
    class Singleton{
        private static Singleton singleton = new Singleton()//1
        private static int count1;//2
        private static int count2=0;//3
        private Singleton(){//6
            count1++;//4
            count2++;//5
        }
        public static Singleton getSingleton(){
            return singleton;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    答案:count1=1

    ​ count2=0

    题解

    当在主函数调用类的静态方法的时候,会在JVM中加载类,在类加载过程中,static的属性会添加,流程如下:

    会先执行1:创建类的实例化,也就是调用类的构造器

    执行6:使用无参构造器后依次执行里面的内容

    执行4:在这个适合静态属性已经存在,但是没有赋值,默认值所以count1为1

    执行5:在这个适合静态属性已经存在,但是没有赋值,默认值所以count2为1

    执行2:构造器执行完毕,因为count1后面没有等号也就没有再次赋值

    执行3:在count2中进行了重新赋值为0

    所以结果为:count1=1

    ​ count2=0

    所有的异常皆继承哪个类

    java.lang.Throwable

    java.lang.Exception

    java.lang.Error

    多个catch的排列方式

    子类异常在前面,父类异常在后面

    因为父类异常在前面那么子类异常也就没有存在的意义了

    关于异常问题

    public class MultiCatch{
        public static void main(String args[]){
            try{
                int a=args.length;
                int b=42/a;
                int c[]={1};
                c[42]=99;
                sout("b="+b);
            }catch(ArithmeticException e){
                sout("除0异常"+e);//1
            }catch(ArrayIndexOutOfBoundsException e){
                sout("数组超越边界异常:"+e);//2
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    答案:程序将输出第一行或第二行的异常信息

    题解

    我们可以认为的通过外面向args传递数据,当数据不为空也就不会报计算异常就会报第二个数据越界异常

    当我们没有向外面传递值的时候报除0异常

    当抛出一个异常后,其余异常不会抛出

    异常程序题输出什么

    我们要先观察try中抛出的异常与catch中的异常相比是否符合异常类型,若不符合会将异常自动向上抛,如果一直向上抛也没有复合类型的,那么最后将会将由JVM抛出异常

    在异常处理中,如释放资源、关闭文件、关闭数据库等由finally来完成

    关于finally下面程序返回结果是

    return用于形成方法的返回值,后续(finally)修改返回的变量无法改变方法的返回值

    finally是唯一一个可以覆盖掉之前的返回值

    String说法正确的是

    String是引用数据类型

    String是java.lang包下的不需要引用就可以使用

    String不可以被继承 String底层代码是由final修饰的

    下面比较说法正确的是

    ==可以比较两个基本数据类型的变量值是否相等

    ==可以比较两个对象是是否相等

    equals可以比较两个基本数据类型变量是否相等

    equals可以比较两个对象是否相等

    ==:可以比较基本数据类型变量的值

    ​ 比较类的地址

    equals:不能比较基本数据类型变量

    ​ 如果没有重写默认的是比较地址值

    ​ 如果重写比较的是内容

    关于字符串长度下面争取的是

    数组有length()这个方法❌数组的length是数组的属性

    数组没有length()这个方法✔

    String没有length()这个方法❌length()在String是方法

    String有length()这个方法✔

    字符串创建个数哪个正确

    String s=new String(“xyz”);创建了几个String Object?

    关于字符串创建个数哪个正确

    String s=new String(“xyz”);创建了几个String Object?(D)

    1个或2个

    要观察前面的情况而定

    如果前面创建xyz那么就是一个String Object(在对内存中)

    如果前面没有创建,那就创建两次,一个在堆内存,一个在共享区

    关于字符串拆分下面哪个正确

    psvm{
        String str="aa.bb.cc";
        String[] arr=str.split(".");
    }
    
    • 1
    • 2
    • 3
    • 4

    题目中这个分割条件是不会生效的,因为“.”属于正则表达式中的代表任意字符

    我们要将它取消他的含义

    String[] arr=str.split(“\\.”);

    这样才可以去除.中正则表达式的含义

    subString()方法

    String str="aa.bb.cc";
    str.substring(2,6);
    sout(str);
    
    • 1
    • 2
    • 3

    结果:aa.bb.cc

    题解

    含义是在字符串中截取

    左闭右开

    字符串常量是不可变的

    subString会产生一个新的子字符串,不会在原本的字符串上进行修改

    关于可变字符串下列说法正确的是

    StringBuffer中没有重写equals方法,调用的依旧是父类Object的equals方法,比较内存地址

    StringBuffer中的方法实在本身进行修改的

    非运行时异常

    IOException

    FileNotFoundException

    下面关于final,finally,finalize错误的是

    final用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承

    finally是异常处理语句结构的一部分,表示总是执行❌话过于绝对,当我们在finally执行之前使用System.exit(-1),退出JVM

    finalize是Object类的一个方法,在垃圾收集器执行时候会调用被回收对象的此方法✔

    final修饰的属性必须有初始值❌

    我们可以在构造器中进行final属性初始化

    例如

    public class Test{
        final int age;
        Test{
            age=10;
        }
        psvm{
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Math.round()

    加0.5向下取整

    关于包装类说法正确的是

    关于Integer 与 int 说法错误的是

    Integer是java为int提供的包装类,提供了很多关于数值的计算方法✔

    Integer是引用数据类型,int是基本数据类型✔

    int是基本数据类型,不能获得Class类对象❌

    Class c=int.class;
    
    • 1

    int的默认值为0,而Integer的默认值为null✔

    char的包装类是Character

    int的包装类是Integer

    关于随机数哪个是正确的

    Random r=new Random();
    int i=r.nextInt(10);
    System.out.println(i);
    
    • 1
    • 2
    • 3

    题解

    依旧是左闭右开

    默认0-1

    r.nextInt(10) 代表 默认*10

    所以为【0,10)===>也就是0-9之间的随机数

  • 相关阅读:
    B031-网络编程 Socket Http TomCat
    Linux定时任务调度
    Java InputStram/OutputStream
    图书馆自习教室管理系统分析与设计
    字节跳动八进八出,offer到手,发现项目不重要算法才最重要
    nn.functional.sigmoid
    本地笔记同步到博客
    nodejs在pdf中绘制表格
    [Spring Cloud] (4)搭建Vue2与网关、微服务通信并配置跨域
    「C++」简单模拟
  • 原文地址:https://blog.csdn.net/m0_47711130/article/details/126593896