Java基础知识面试题

122 篇文章 2 订阅
117 篇文章 0 订阅

主题

链接

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开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值