• Java基础知识面试题


    主题

    链接

    Java基础知识

    面试题

    Java集合容器

    面试题

    Java并发编程

    面试题

    Java底层知识

    面试题

    Java常用框架

    面试题

    计算机网络

    面试题

    数据库

    面试题

    RabbitMQ

    面试题

    Redis

    面试题

    Elasticsearch

    面试题

    Zookeeper

    面试题

    系统设计

    面试题

    文章目录

    面向对象

    什么是面向对象?什么是面向过程?

    • 面向过程就是分析出实现需求所需要的步骤,通过函数一步一步实现这些步骤,接着依次调用即可。
    • 面向对象是把整个需求按照特点、功能划分,将这些存在共性的部分封装成对象,创建对象不是为了完成某一个步骤,而是描述某个事物在解决问题的步骤中的行为
    • 面向对象是将每一个步骤抽象为行为,便于复用和扩展

    面向对象的三大基本特征是什么?

    • 封装就是隐藏对象的属性和实现细节,封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节
    • 继承机制允许创建分等级层次的类。继承就是子类继承父类的特征和行为。
    • 多态是指一个对象的相同方法在不同情形有不同表现形式。

    多态存在的三个必要条件:

    • 继承
    • 重写(子类继承父类后对父类方法进行重新定义)
    • 父类引用指向子类对象

    面向对象的五大基本原则是什么?

    • 单一职责原则(SRP):一个类应该有且只有一个去改变它的理由,是指一个类的功能要单一
    • 开放封闭原则(OCP):对象或实体应该对扩展开放,对修改封闭。软件实体(类,模块,函数等等)应该是可扩展的,但是不可修改。因为修改程序有可能会对原来的程序造成错误。不能修改,但是可以添加功能,尽可能的在外边添加新的类。
    • 里氏替换原则(LSP):子类应当可以替换父类并出现在父类能够出现的任何地方。
    • 依赖倒置原则(DIP):高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。简单来说,解决耦合。一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。
    • 接口隔离原则(ISP):接口端不应该依赖它不需要的接口,接口要尽量的小,实现类不必实现不需要的方法。

    什么是JVM、JDK、JRE

    • JDK:Java Development Kit,是JAVA程序开发时用的开发工具包,其内部也有JRE运行环境。
    • JRE:Java Runtime Environment,是JAVA程序运行时需要的运行环境,就是只为了运行JAVA程序而不是去搞开发的话,只安装JRE就能运行已经存在的JAVA程序了。
    • JVM:Java Virtual Machine,JDK、JRE内部都包含JVM,JAVA虚拟机内部包含许多应用程序的类的解释器和类加载器等等。

    为什么Java是跨平台的?

    Java程序编译之后的代码不是能被硬件系统直接运行的代码,而是一种中间码——字节码。然后不同的硬件平台上安装有不同的Java虚拟机,由JVM来把字节码再翻译成所对应的硬件平台能够执行的代码。
    因此对于Java编程者来说,不需要考虑硬件平台是什么。所以Java可以跨平台。
    Java的平台无关性是建立在Java虚拟机的平台有关性基础之上的,是因为Java虚拟机屏蔽了底层操作系统和硬件的差异。

    值传递、引用传递的区别

    java中方法参数传递方式是按值传递。

    • 如果参数是基本类型,传递的是基本类型的字面量值的拷贝。
    • 如果参数是引用类型,传递的是该参量所引用的对象在堆中地址值的拷贝。

    访问修饰符public、private、protected,以及不写(默认)时的区别?

    在这里插入图片描述

    抽象类与抽象接口

    抽象类和普通类的区别:
    在Java语言中使用abstract class来定义抽象类,抽象类中并不是只能有抽象方法,和普通类一样,同样可以拥有成员变量和普通的成员方法

    • 抽象方法的访问修饰符必须为public和protected;
    • 抽象类不能被实例化;
    • 如果一个类继承于抽象类,则子类必须实现父类的抽象方法,如果子类没有实现父类的抽象方法,则子类必须也一个抽象类。

    抽象类和接口区别:

    • 抽象类内部可以有方法的实现细节,而接口中只能存在public abstract方法;
    • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
    • 接口中不能含有静态代码块以及静态方法,而抽象类中可以有静态代码旷和静态方法;
    • 一个类只能继承一个抽象类,而一个类可以实现多个接口。

    重写与重载的区别

    • 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
    • 重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

    基本数据类型

    Java的基本数据类型有哪些

    整型:byte, short, int, long
    字符型:char
    浮点型:float, double
    布尔型:boolean
    Java中最小的计算单元为字节,1字节=8位(bit)

    各个基本数据类型的取值范围

    在这里插入图片描述

    关键字

    • transient 声明不用序列化的成员域
    • native 用来声明一个方法是由与计算机相关的语言(如C/C++语言)实现的
    • throw 抛出一个异常
    • throws 声明在当前定义的成员方法中所有需要抛出的异常

    String

    为什么字符串是不可变的?

    String是引用类型,String变量储存一个地址,地址指向内存堆中的String对象。当我们说变量不可变,有两种不可变性:

    1. 变量储存的地址不可变
    2. 地址指向的对象内容不可变

    String变量指向的地址是可变的,他的不可变性是指第2种——地址指向的对象内容不可变。
    纵览String的方法,String类确实没有提供能从String外部修改对象的方法。我们熟悉的replace,substring等等方法都要返回一个String,其实都是在返回一个新的对象,而没有修改原有的对象。

    为什么要设计成对象内容不可变?

    • String常量池:便于实现String常量池。String存在于常量池中,当新创建一个字符串变量,如果字符串在内存中已经存在,那么就会把这个已经存在于常量池对象的地址赋给变量。这样可节省内存开销。但是通过构造函数new String()的并不是。
    • 线程安全:对象内容不可变,就不会有线程安全问题。
    • 代码安全:如果String可变,一旦代码某处改动了字符串,会对系统有安全和稳定性威胁。一个字符串对象被多个变量引用,直接修改对象内容,引起所有引用该对象的变量都发生变化,容易引起bug。

    String.valueOf和Integer.toString的区别

    • java.lang.Object类里已有public方法.toString(),所以对任何严格意义上的java对象都可以调用此方法。但在使用时要注意,必须保证object不是null值,否则将抛出NullPointerException异常。
    • valueOf(Object obj)对null值进行了处理,不会报任何异常。但当object为null 时,String.valueOf(object)的值是字符串”null”,而不是null。

    swtich对string的支持

    • Java1.7之前switch只能局限于int 、short 、byte 、char四类做判断条件。在JVM内部实际大部分字节码指令只有int类型的版本。在使用switch的时候,如果是非int型,会先转为int型,再进行条件判断。
    • Java1.7的switch增加了对String的支持,可String并不能直接转为int型,switch比较的是字符串常量的哈希值(int类型),但是hash值可能会有冲突,所以还需要再调用equals方法进行二次比较。

    intern

    • 对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。
    • 在调用”ab”.intern()方法的时候会返回”ab”,但是这个方法会首先检查字符串池中是否有”ab”这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用。
    • 可以利用String.intern方法来提高程序效率或者减少内存占用的情况

    == 和 equals 的区别是什么

    它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)

    equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

    情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。

    情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

    自动拆装箱

    什么是包装类型?什么是基本类型

    为什么存在这两种类型呢?
    主要是效率,基本数据类型基于值,对象类型基于对象的引用。对象类似存储在堆中,通过栈中的引用来使用这些对象,但是对于一些局部变量,这个变量直接存储“值”,并置于栈中,更加高效些。
    有了基本类型为什么还要有包装类型呢?
    我们知道Java是一个面相对象的编程语言,基本类型并不具有对象的性质,为了让基本类型也具有对象的特征,就出现了包装类型(如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型),它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
    另外,当需要往ArrayList,HashMap中放东西时,像int,double这种基本类型是放不进去的,因为容器都是装object的,这是就需要这些基本类型的包装器类了。

    二者的区别:

    1. 声明方式不同:
      基本类型不使用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间;
    2. 存储方式及位置不同:
      基本类型是直接将变量值存储在栈中,而包装类型是将对象放在堆中,然后通过引用来使用;
    3. 初始值不同:
      基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null;
    4. 使用方式不同:
      基本类型直接赋值直接使用就好,而包装类型在集合如Collection、Map时会使用到。

    什么是自动拆装箱

    有了基本数据类型和包装类,肯定有些时候要在他们之间进行转换。比如把一个基本数据类型的int转换成一个包装类型的Integer对象。
    我们认为包装类是对基本类型的包装,所以,把基本数据类型转换成包装类的过程就是打包装,英文对应于boxing,中文翻译为装箱。反之,把包装类转换成基本数据类型的过程就是拆包装,英文对应于unboxing,中文翻译为拆箱。

    在Java SE5中,为了减少开发人员的工作,Java提供了自动拆箱与自动装箱功能。
    Integer i =10; //自动装箱
    int b= i; //自动拆箱

    Integer的缓存机制是什么

    Integer里面默认的缓存数字是-128-127,
    1、Integer与Integer相互比较,数据在-128-127范围内,就会从缓存中拿去数据,使用== 比较就相等;如果不在这个范围,就会直接新创建一个Integer对象,使用 == 判断的是两个内存的应用地址,所以不相等。
    2、Integer和int类型相比,在jdk1.5会自动拆箱,然后==比较栈内存中的数据,所以都是相等的

    异常

    异常类型

    在这里插入图片描述

    自定义异常

    • 是否需要定义成 Checked Exception(检查性异常必须在编写代码时,使用 try catch 捕获),因为这种类型设计的初衷更是为了从异常情况恢复,作为异常设计者,我们往往有充足信息进行分类。
    • 在保证诊断信息足够的同时,也要考虑避免包含敏感信息,因为那样可能导致潜在的安全问题。如果我们看 Java 的标准类库,你可能注意到类似 java.net.ConnectException,出错信息是类似“ Connection refused (Connection refused)”,而不包含具体的机器名、IP、端口等,一个重要考量就是信息安全。类似的情况在日志中也有,比如,用户数据一般是不可以输出到日志里面的。

    Error 和 Exception

    • 首先 Exception 和 Error 都是继承于 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),Exception 和 Error 体现了JAVA 这门语言对于异常处理的两种方式。
    • Exception 是 Java 程序运行中可预料的异常情况,我们可以获取到这种异常,并且对这种异常进行业务外的处理。
      分为检查性异常和非检查性(RuntimeException)异常。两个根本的区别在于,检查性异常必须在编写代码时,使用 try catch 捕获,比如:IOException异常。非检查性异常在代码编写时,可以忽略捕获操作,比如:ArrayIndexOutOfBoundsException,这种异常是在代码编写或者使用过程中通过规范可以避免发生的。
    • Error 是 Java 程序运行中不可预料的异常情况,这种异常发生以后,会直接导致 JVM 不可处理或者不可恢复的情况。所以这种异常不可能抓取到,比如 OutOfMemoryError、NoClassDefFoundError等。

    finally和return的执行顺序

    • 不管有没有出现异常,finally块中代码都会执行;
    • 当try和catch中有return时,finally仍然会执行;
    • finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),所以函数返回值是在finally执行前确定的;
    • finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

    泛型

    泛型与继承

    泛型:参数化类型,将类型由原来的具体类型参数化,把类型也定义为行参。方法中均使用同一类型。属于编译期信息,无法提供动态绑定,当类型与方法无关时,使用泛型。使用泛型的类应该有共同的方法,为水平方法,而继承是垂直方向。

    类型擦除是什么

    List和List等类型,在编译后都会变成List

    泛型中 K T V R ? object等的含义

    K:key(键值);
    T:Type(Java类);
    V:Value(值);
    N:Number(数值类型);
    E:Element(元素,集合元素);
    :无限定
    Object:所有类的根类

    限定通配符和非限定通配符

    • 表示类型的上界:< Extends T>,类型必须为T或者其子类
    • 表示类型的下界:< Super T>,类型必须为T或者T的父类

    List<>和List之间的区别

    • List:可以添加任意类型的元素,不安全(编译通过),不便利(需要自己强制转换类型),不表述(可以直接看到实参就是其类型)。
    • List<>:通配符类型,接受List任意参数化类型,包括List,不能添加元素,保证安全和便利,但不保证表述。
    • List< Object> 参数为对象,可以添加List,可以添加元素,但不能接受除了本身外的任何参数化类型。

    序列化

    什么是序列化和反序列化

    序列化:对象存储转换为二进制,对象和元数据(属性)都存储为二进制。
    反序列化:把对象和元数据从二进制恢复。
    场景:持久化,存入数据库;远程传输,进程之间传输。

    为什么要序列化

    有些时候我们需要把应用程序中的数据以另一种形式进行表达,以便于将数据存储起来,并在未来某个时间点再次使用,或者便于通过网络传输给接收方。这一过程我们把它叫做序列化。

    序列化的底层原理

    实现的API:ObjectOutputStream中的writeObject(Object obj),ObjectInputStream中的readObject()
    只有实现了Serializable或者Externalizable接口的类的对象才可以被实例化。父类实现上述接口,子类就不需要显示的实现,静态类不可以实现序列化。

    序列化与单例模式

    正常的序列化会破坏单例模式,产生新的对象,保证单例时,需要添加private Object readResolve(){}方法

    为什么说序列化并不安全

    可以实现远程代码执行;序列化明文存储,可以根据对象序列化生产很多私有属性。通过反序列化产生非预期对象,根源在于ObjectInputSteam对生成的对象类型没有限制。
    不安全的反序列化

    单元测试

    junit

    单元测试各个注解执行的先后顺序:
    @BeforeClass -> @Before -> @Test -> @After -> @AfterClass;

    时间处理

    Java时间API

    • Util.Date:能够精确到毫秒级
    • Sql.Date:数据库日期API,操作日期,不能读取和修改时间
    • Sql.Time:数据库时间类,获取操作时间
    • Sql.Timestamp:纳秒级Util.Date
    • System.currentTimeMillis():当前系统时间,毫秒
    • System.nanoTime(); 当前系统时间,纳秒

    SimpleDateFormat线程安全问题

    主要因为SimpleDateFormat继承于DateFormat,而DateFormat使用成员变量传值,其Calendar在多个方法中调用,Format和subFormat都使用了DateFormat的成员变量Calendar

    Java 8 对时间的处理

    引入新类java.time:线程安全,不可变。主要类:

    • Instant:时间戳
    • LocalDate:不包含具体时间的日期,2014-01-01
    • LocalTime:不包含日期的时间
    • LocalDateTime:包含时间于日期,但无时区偏移
    • ZonedDateTime:完整时间,偏移量以GMT和UTC为准。

    注解

    什么是元注解

    注解的注解:@Retention、@Target、@Document、@Inherited
    @Retentiond:定义注解的保留策略,RetentionPolicy.SOURCE:在源码 .CLASS在字节码,运行时无效,.RUNTIME在字节码。反射可以获取
    @Target:定义注解的目标
    @Document:说明注解被包含在Javadoc中
    @Inherited:说明子类可以继承父类的该注解

    怎么自定义注解

    Public @interface xx{} java.lang.annotation

    Java中有哪些常用注解

    @Override:重写标志,标示覆盖父类的方法
    @Deprecated: 已过期,表示方法时不建议使用的。
    @SuppressWarnings: 压制告警,抑制告警

    什么是SPI?

    SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。
    这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。

    反射有什么用

    反射(Reflection)是其Java非常突出的一个动态相关机制。它可以于运行时加载、使用编译期间完全未知的classes(但是要知道类的名字)。也就是说,在运行时,我们还可以获取一个类中所有的方法和属性,可以实例化任何类的对象,还能判断一个对象所属的类。可以用于热部署。

    反射最重要的用途就是开发各种通用框架。
    很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。

    Class类

    Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象,这个Class对象是由JVM生成的,通过它能够获悉整个类的结构。要想使用反射,首先需要获得待操作的类所对应的Class对象。
    获取class对象方法:
    1.使用Class类的静态方法
    eg:Class.forName(“java.lang.String”);
    2.使用类的.class语法
    eg:Class c = Employee.class;
    3.使用对象的getClass()方法
    eg:Employee e = new Employee();
    Class c3 = e.getClass();
    原理
    所有的java类都是继承了object这个类,在object这个类中有一个方法:getclass().这个方法是用来取得该类已经被实例化了的对象的该类的引用,这个引用指向的是Class类的对象。我们自己无法生成一个Class对象(构造函数为private),而 这个Class类的对象是在当各类被调入时,由 Java 虚拟机自动创建 Class 对象,或通过类装载器中的 defineClass 方法生成。我们生成的对象都会有个字段记录该对象所属类在CLass类的对象的所在位置。如下图所示:
    在这里插入图片描述

    IO

    字符流、字节流、输入流、输出流的区别

    字节流:
    1.字节流在操作的时候不会用到缓冲区(也就是内存)
    2.字节流可用于任何类型的对象,包括二进制对象
    3.字节流处理单元为1个字节,操作字节和字节数组。
    InputStream是所有字节输入流的祖先,而OutputStream是所有字节输出流的祖先。

    字符流:
    1.而字符流在操作的时候会用到缓冲区
    2.而字符流只能处理字符或者字符串
    3.字符流处理的单元为2个字节的Unicode字符,操作字符、字符数组或字符串,
    Reader是所有读取字符串输入流的祖先,而writer是所有输出字符串的祖先。

    同步与异步的区别

    同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
    所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。 换句话说,就是由调用者主动等待这个调用的结果。
    而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。典型的异步编程模型比如Node.js
    举个通俗的例子:
    你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下”,然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
    而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。
    实质:访问数据的方式,同步需要当前线程读写数据,在读写数据的过程中还是会阻塞;异步只需要I/O操作完成的通知,当前进程并不主动读写数据,由操作系统内核完成数据的读写。

    阻塞与非阻塞的区别

    阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
    阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
    非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
    还是上面的例子,
    你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
    在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。

    Linux5种IO模型

    阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动IO模型以及异步IO模型。

    https://www.jianshu.com/p/486b0965c296

    BIO、NIO和AIO的区别

    同步阻塞IO(JAVA BIO):
    同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

    同步非阻塞IO(Java NIO):
    同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问。

    异步阻塞IO(Java NIO):
    此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄(如果从UNP的角度看,select属于同步操作。因为select之后,进程还需要读写数据),从而提高系统的并发性!

    异步非阻塞IO(Java AIO(NIO.2)):
    在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。

    三种IO的用法和原理

    • BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
    • NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
    • AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

    在这里插入图片描述

    在这里插入图片描述

    正则表达式

    • 用d可以匹配一个数字,w可以匹配一个字母或数字
    • 在正则表达式中,用*表示任意个字符(包括0个),用+表示至少一个字符,用表示0个或1个字符,用{n}表示n个字符,用{n,m}表示n-m个字符:

    要做更精确地匹配,可以用[]表示范围,比如:

    • [0-9a-zA-Z_]可以匹配一个数字、字母或者下划线;
    • [0-9a-zA-Z_]+可以匹配至少由一个数字、字母或者下划线组成的字符串,比如’a100’,‘0_Z’,'Py3000’等等;
    • [a-zA-Z_][0-9a-zA-Z_]*可以匹配由字母或下划线开头,后接任意个由一个数字、字母或者下划线组成的字符串,也就是Python合法的变量;
    • [a-zA-Z_][0-9a-zA-Z_]{0, 19}更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)。

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    如何给子DIV平均分配宽度
    备份和恢复Gitlab数据
    vue中三种for循环与高阶函数(filter、map、reduce)的使用
    D. Challenging Valleys
    定时任务-启动服务立即执行一次,再按照cron表达式等待下一次执行
    Text-based diagram tool
    玩家们最关心的绝地求生游戏作战秘籍,分享大神干货,助您轻松提升战斗力!
    2022 IDEA大会引领科技创新趋势 沈向洋团队重磅发布低空经济白皮书
    网络安全-HSRP协议
    Pod环境变量和initContainer
  • 原文地址:https://blog.csdn.net/m0_67401417/article/details/126072383