Java 语言介绍
Java 发展简史
1991 年,James Gosling 在 SUN 公司的工程师小组想要设计这样一种小型计算机语言。该语言主要用于像电视盒这样的消费类电子产品。另外,由于不同的厂商选择不同的CPU 和操作系统,因此,要求该语言不能和特定的体系结构绑在一起,要求语言本身是中立的,也就是跨平台的。所以,将这个语言命名为“Green”,类似于绿色软件的意思。后来,改名为 Oak,橡树的意思。改名后发现已经有一种语言叫这个名字了,再改名叫 Java。
Java 语言发展到今天经历了一系列的过程:
1991 年,SUN 公司的 Green 项目,Oak
1995 年,推出 Java 测试版
1996 年,JDK1.0
1997 年,JDK1.1
1998 年,JDK1.2,大大改进了早期版本缺陷,是一个革命性的版本,更名为 Java2。
2004 年,J2SE 5.0 (1.5.0) Tiger 老虎
2006 年,J2SE 6.0 (1.6.0) Mustang 野马
2011 年,JavaSE7.0 Dolphin 海豚
2014 年,JavaSE8.0
2017 年,JAVA 9.0
2018 年 3 月,JAVA 10
2018 年 9 月,JAVA 11
2019 年 3 月,JAVA 12
2019 年 9 月,JAVA 13
2020 年 3 月,JAVA14
SUN 公司已经被 oracle 公司收购,目前每半年更新一次 java 的版本。但是,企业中的主流仍然以 8 为主。对于初学者,应该以企业主流应用版本为核心进行学习,没有必须在此处追求最新版本。
Java 的核心优势
Java 为消费类智能电子产品而设计,但智能家电产品并没有像最初想象的那样拥有大的发展。然而 90 年代,Internet 却进入了爆发式发展阶段,一夜之间,大家都在忙着将自己的计算机连接到网络上。这个时候,遇到了一个大的问题。人们发现连接到 Internet 的计算机各式各样,有 IBM PC、苹果机、各种服务器等等,不仅硬件 CPU 不同,操作系统也不同,整个的网络环境非常复杂。这个时候,程序员们希望他们编写的程序能够运行在不同的机器,不同的环境中,这需要一种体系中立的语言(即跨平台)。Java 的研发小组户然发现他们用于小范围的语言也可以适应 Internet 这个大环境。
跨平台是 Java 语言的核心优势,赶上最初互联网的发展,并随着互联网的发展而发展,建立了强大的生态体系,目前已经覆盖 IT 各行业的“第一大语言”,是计算机界的“英语”。虽然,目前也有很多跨平台的语言,但是已经失去先机,无法和 Java 强大的生态体系抗衡。Java 仍将在未来几十年成为编程语言的主流语言。
Java 各版本的含义
JavaSE(Java Standard Edition):标准版,定位在个人计算机上的应用这个版本是 Java 平台的核心,它提供了非常丰富的 API 来开发一般个人计算机上的应用程序,包括用户界面接口 AWT 及 Swing,网络功能与国际化、图像处理能力以及输入输出支持等。在上世纪 90 年代末互联网上大放异彩的 Applet 也属于这个版本。Applet 后来为 Flash 取代,Flash 即将被 HTML5 取代。
JavaEE(Java Enterprise Edition):企业版,定位在服务器端的应用
JavaEE 是 JavaSE 的扩展,增加了用于服务器开发的类库。如:JDBC 是让程序员能直接在 Java 内使用的 SQL 的语法来访问数据库内的数据;Servlet 能够延伸服务器的功能,通过请求-响应的模式来处理客户端的请求;JSP 是一种可以将 Java 程序代码内嵌在网页内的技术;
JavaME(Java Micro Edition):微型版,定位在消费性电子产品的应用上
JavaME 是 JavaSE 的内伸,包含 J2SE 的一部分核心类,也有自己的扩展类,增加了适合微小装置的类库:javax.microedition.io.*等。该版本针对资源有限的电子消费产品的需求精简核心类库,并提供了模块化的架构让不同类型产品能够随时增加支持的能力。
Java 的特性和优势
跨平台/可移植性
这是 Java 的核心优势。Java 在设计时就很注重移植和跨平台性。比如:Java 的 int 永远都是 32 位。不像 C++可能是 16,32,可能是根据编译器厂商规定的变化。这样的话程序的移植就会非常麻烦。
安全性
Java 适合于网络/分布式环境,为了达到这个目标,在安全性方面投入了很大的精力,使 Java 可以很容易构建防病毒,防篡改的系统。
面向对象
面向对象是一种程序设计技术,非常适合大型软件的设计和开发。由于 C++为了照顾大量 C 语言使用者而兼容了 C,使得自身仅仅成为了带类的 C 语言,多少影响了其面向对方象的彻底性!Java 则是完全的面向对象语言。
简单性
Java 就是 C++语法的简化版,我们也可以将 Java 称之为“C++-”。跟我念“C 加加减”,指的就是将军 C++的一些内容去掉;比如:文件,指针运算,结构,联合,操作符重载,虚基类等等。同时,由于语法基于 C 语言,因此学习起来完全不费力。
高性能
Java 最初发展阶段,总是被人诟病“性能低”;客观上,高级语言运行效率总是低于低级语言的话,这个无法避免。Java 语言本身在发展中通过虚拟机的优化提升了几十倍的运行效率。比如,通过 JIT(JUST IN TIME)即时编译技术提高运行效率。 将一些“热点”字节码编译成本地机器码,并将结果缓存起来,在需要的时候重新调用。这样的话,使 Java 程序执行效率大大提高,某些代码甚至可以接待 C++的效率。
因此,Java 低性能的短腿,已经被完全解决了。业界发展上,我们也看到很多 C++应用转到 Java 开发,很多 C++程序员转型为 Java 程序员。
分布式
Java 是为 Internet 分布式环境设计的,因为它能够处理 TCP/IP 协议。事实上,通过 URL 访问一个网络资源和访问本地文件是一样的。Java 还支持远程方法调用(RMI,Remote Method Invocation),使程序能够通过网络调用方法。
多线程
多线程的使用可以带来更好的交互响应和实时行为。 Java 多线程的简单性是 Java 成为主流服务器端开发语言的主要原因之一。
健壮性
Java 是一种健壮的语言,吸收了 C/C++ 语言的优点,但去掉了其影响程序健壮性的部分(如:指针、内存的申请与释放等)。Java 程序不可能造成计算机崩溃。即使 Java 程序列也可能有错误。如果出现某种出乎意料之事,程序也不会崩溃,而是把该异常抛出,再通异常处理机制加以处理
不知道什么是Java注解?莫慌,十分钟一篇文章就能深度学习
不知道什么是Java注解?莫慌,十分钟一篇文章就能深度学习!
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
- 编写文档: 通过代码里标识的元数据生成文档【生成文档doc文档】
- 代码分析: 通过代码里标识的元数据对代码进行分析【使用反射】
- 编译检查: 通过代码里标识的元数据让编译器能够实现基本的编译检查【Override等】
编写文档
首先,我们要知道Java中是有三种注释的,分别为单行注释、多行注释和文档注释。而文档注释中,也有@开头的元注解,这就是基于文档注释的注解。我们可以使用javadoc命令来生成doc文档,此时我们文档的内元注解也会生成对应的文档内容。这就是编写文档的作用。
代码分析
我们频繁使用之一,也是包括使用反射来通过代码里标识的元数据对代码进行分析的,此内容我们在后续展开讲解。
编译检查
至于在编译期间在代码中标识的注解,可以用来做特定的编译检查,它可以在编译期间就检查出“你是否按规定办事”,如果不按照注解规定办事的话,就会在编译期间飘红报错,并予以提示信息。可以就可以为我们代码提供了一种规范制约,避免我们后续在代码中处理太多的代码以及功能的规范。比如,@Override注解是在我们覆盖父类(父接口)方法时出现的,这证明我们覆盖方法是继承于父类(父接口)的方法,如果该方法稍加改变就会报错;@FunctionInterface注解是在编译期检查是否是函数式接口的,如果不遵循它的规范,同样也会报错。
- @Override: 标记在成员方法上,用于标识当前方法是重写父类(父接口)方法,编译器在对该方法进行编译时会检查是否符合重写规则,如果不符合,编译报错。
- @Deprecated: 用于标记当前类、成员变量、成员方法或者构造方法过时如果开发者调用了被标记为过时的方法,编译器在编译期进行警告。
- @SuppressWarnings: 压制警告注解,可放置在类和方法上,该注解的作用是阻止编译器发出某些警告信息。
标记在成员方法上,用于标识当前方法是重写父类(父接口)方法,编译器在对该方法进行编译时会检查是否符合重写规则,如果不符合,编译报错。
这里解释一下@Override注解,在我们的Object基类中有一个方法是toString方法,我们通常在实体类中去重写此方法来达到打印对象信息的效果,这时候也会发现重写的toString方法上方就有一个@Override注解。如下所示:
于是,我们试图去改变重写后的toString方法名称,将方法名改为toStrings。你会发现在编译期就报错了!如下所示:
那么这说明什么呢?这就说明该方法不是我们重写其父类(Object)的方法。这就是@Override注解的作用。
用于标记当前类、成员变量、成员方法或者构造方法过时如果开发者调用了被标记为过时的方法,编译器在编译期进行警告。
我们解释@Deprecated注解就需要模拟一种场景了。假设我们公司的产品,目前是V1.0版本,它为用户提供了show1方法的功能。这时候我们为产品的show1方法的功能又进行了扩展,打算发布V2.0版本。但是,我们V1.0版本的产品需要抛弃吗?也就是说我们V1.0的产品功能还继续让用户使用吗?答案肯定是不能抛弃的,因为有一部分用户是一直用V1.0版本的。如果抛弃了该版本会损失很多的用户量,所以我们不能抛弃该版本。这时候,我们对功能进行了扩展后,发布了V2.0版本,我们给予用户的通知就可以了,也就是告知用户我们在V2.0版本中为功能进行了扩展。可以让用户自行选择版本。
但是,除了发布告知用户版本情况之外,我们还需要在原来版本的功能上给予提示,在上面的模拟场景中我们需要在show1方法上方加@Deprecated注解给予提示。通过这种方式也告知用户“这是旧版本时候的功能了,我们不建议再继续使用旧版本的功能”,这句话的意思也就正是给用户做了提示。用户也会这么想“奥,这版本的这个功能不好用了,肯定有新版本,又更好用的功能。我要去官网查一下下载新版本”,还会有用户这么想“我明白了,又更新出更好的功能了,但是这个版本的功能我已经够用了,不需要重新下载新版本了”。
那么我们怎么查看我上述所说的在功能上给予的提示呢?这时候我需要去创建一个方法,然后去调用show1方法,并查看调用时它是如何提示的。
图已经贴出来了,你是否发现的新旧版本功能的异同点呢?很明显,该方法中的提示是在调用的方法名上加了一道横线把该方法划掉了。这就体现了show1方法过时了,已经不建议使用了,我们为你提供了更好的。
回想起来,在我们的api中也会有方法是过时的,比如我们的Date日期类中的方法有很多都已经过时了。如下图:
如你所见,是不是有很多方法都过时了呢?那它的方法上是加了@Deprecated注解吗?来跟着我的脚步,我带你们看一下。
我们已经知道的Date类中的这些方法已经是过时的了,如果我们使用该方法并执行该程序的话。执行的过程中就会提示该方法已过时的内容,但是只是提示,并不影响你使用该方法。如下:
OK!这也就是@Deprecated注解的作用了。
压制警告注解,可放置在类和方法上,该注解的作用是阻止编译器发出某些警告信息,该注解为单值注解,只有 一个value参数,该参数为字符串数组类型,参数值常用的有如下几个。
- unchecked:未检查的转化,如集合没有指定类型还添加元素
- unused:未使用的变量
- resource:有泛型未指定类型
- path:该类路径,原文件路径中有不存在的路径
- deprecation:使用了某些不赞成使用的类和方法
- fallthrough:switch语句执行到底没有break关键字
- rawtypes:没有写泛型,比如: List list = new ArrayList();
- all:全部类型的警告
压制警告注解,顾名思义就是压制警告的出现。我们都知道,在Java代码的编写过程中,是有很多黄色警告出现的。但是我不知道你的导师是否教过你,程序员只需要处理红色的error,不需要理会黄色的warning。如果你的导师说过此问题,那是有原因的。因为在你学习阶段,我们认清处理红色的error即可,这样可以减轻你学习阶段在脑部的记忆内容。如果你刚刚加入学习Java的队列中,需要大脑记忆的东西就有太多了,也就是我们目前不需要额外记忆其他的东西,只记忆重点即可。至于黄色warning嘛,在你的学习过程中慢慢就会有所了解的,而不是死记硬背的。
那为了解释@SuppressWarnings注解,我们还使用上一个例子,因为在那个例子中就有黄色的warning出现。
而每一个黄色的warning都会有警告信息的。比如,这一个图中的警告信息,就告知你show2()方法没有被使用,简单来说,你创建的show2方法,但是你在代码中并没有调用过此方法。以后你便会遇到各种各样黄色的warning。然后, 我们就可以使用不同的注解参数来压制不同的注解。但是在该注解的参数中,提供了一个all参数可以压制全部类型的警告。而这个注解是需要加到类的上方,并赋予all参数,即可压制所有警告。如下:
我们加入注解并赋予all参数后,你会发现use方法和show2方法的警告没有了,实际上导Date包的警告还在,因为我们Date包导入到了该类中,但是我们并没有创建Date对象,也就是并没有写入Date在代码中,你也会发现那一行是灰色的,也就证明了我们没有去使用导入这个包的任何信息的说法,出现这种情况我们就需要把这个没有用的导包内容删除掉,使用Ctrl + X删除导入没有用到的包即可。还有一种办法就是在包的上方修饰压制警告注解,但是我认为在一个没有用的包上加压制注解是毫无意义的,所以,我们直接删除就好。
然后,我们还见到上图,注解那一行出现了警告信息提示。这一行的意思是冗余的警告压制。这就是说我们压制以下的警告并没有什么意义而造成的冗余,但是如果我们使用了该类并做了点什么的话,压制注解的冗余警告就会消失,毕竟我们使用了该类,此时就不会早场冗余了。
上述解释@SuppressWarnings注解也差不多就这些了。OK,继续向下看吧。持续为大家讲解。
@Repeatable 表明标记的注解可以多次应用于相同的声明或类型,此注解由Java8版本引入。我们知道注解是不能重复定义的,其实该注解就是一个语法糖,它可以重复多次使用,更适用于我们的特殊场景。
首先,我们先创建一个可以重复使用的注解。
你会发现注解要求传入的只是一个类对象,此类对象就需要传入另外一个注解,这里也就是另外一个注解容器的类对象。我们去创建一下。
其实,这两个注解的套用,就是将一个普通的注解封装了一个可重复使用的注解,来达到注解的复用性。最后,我们创建一下测试类,随后带你去看一下源码。
测试类,是一个工人测试类,该工人使用注解记录早中晚的工作时间。测试结果如下:
然后我们进入到源码一探究竟。
我们发现进入到源码后,就只看见一个返回值为类对象的抽象方法。这也就验证了该注解只是一个可实现重复性注解的语法糖而已。
注解可以根据注解参数分为三大类:
- 标记注解: 没有参数的注解,仅用自身的存在与否为程序提供信息,如@Override注解,该注解没有参数,用于表示当前方法为重写方法。
- 单值注解: 只有一个参数的注解,如果该参数的名字为value,那么可以省略参数名,如 @SuppressWarnings(value = “all”),可以简写为@SuppressWarnings(“all”)。
- 完整注解: 有多个参数的注解。
说到@Override注解是一个标记注解,那我们进入到该注解的源码查看一下。从上往下看该注解源码,发现它继承了导入了java.lang.annotation.*,也就是有使用到该包的内容。然后下面就又是两个看不懂的注解,其实发现注解的定义格式是public修饰的@Interface,最终看到该注解中方法体并没有任何参数,也就是只起到标记作用。
在上面我们用到的@SuppressWarnings注解就是一个单值注解。那我们进入到它的源码看一下是怎么个情况。其实,和标记注解比较,它就多一个value参数而已,而这就是单值注解的必要条件,即只有一个参数。并且这一个参数为value时,我们可以省略value。
上述两个类型注解讲解完,至于完整注解嘛,这下就能更明白了。其中的方法体就是有多个参数而已。
格式: public @Interface 注解名 {属性列表/无属性}
注意: 如果注解体中无任何属性,其本质就是标记注解。但是与其标注注解还少了上边修饰的元注解。
如下,这就是一个注解。但是它与jdk自定义注解有点区别,jdk自定义注解的上方还有注解来修饰该注解,而那注解就叫做元注解。元注解我会在后面详细的说到。
这里我们的确不知道@Interface是什么,那我们就把自定义的这个注解反编译一下,看一下反编译信息。反编译操作如下:
反编译后的反编译内容如下:
首先,看过反编译内容后,我们可以直观的得知它是一个接口,因为它的public修饰符后面的关键字是interface。
其次,我们发现MyAnno这个接口是继承了java.lang.annotation包下的Annotation接口。
所以,我们可以得知注解的本质就是一个接口,该接口默认继承了Annotation接口。
既然,是继承的Annotation接口,那我们就去进入到这个接口中,看它定义了什么。以下是我抽取出来的接口内容。我们发现它看似很常见,其实它们不是很常用,作为了解即可。
最后,我们的注解中也是可以写有属性的,它的属性不同于普通的属性,它的属性是抽象方法。既然注解也是一个接口,那么我们可以说接口体中可以定义什么,它同样也可以定义,而它的修饰符与接口一样,也是默认被public abstract修饰。
而注解体中的属性也是有要求的。其属性要求如下:
属性的返回值类型必须是以下几种:
- 基本数据类型
- String类型
- 枚举类型
- 注解
- 以上类型的数组
- 注意: 在这里不能有void的无返回值类型和以上类型以外的类型
定义的属性,在使用时需要给注解中的属性赋值
- 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时可以不为属性赋值,它取的是默认值。如果为它再次传入值,那么就发生了对原值的覆盖。
- 如果只有一个属性需要赋值,并且属性的名称为value,则赋值时value可以省略,可以直接定义值
- 数组赋值时,值使用{}存储值。如果数组中只有一个值,则可以省略{}。
属性返回值既然有以上几种,那么我就在这里写出这几种演示一下是如何写的。
首先,定义一个枚举类和另外一个注解备用。
其次,我们来定义上述几种类型,如下:
这里我们演示一下,首先,我们使用该注解来进行演示。
随后创建一个测试类,在类的上方写上注解,你会发现,注解的参数中会让你写这两个参数(int、String)。
此时,传参是这样来做的。格式为:名称 = 返回值类型参数。如下:
上述所说,如果使用default关键字给属性默认初始化值,就不需要为其参数赋值,如果赋值的话,就把默认初始化的值覆盖掉了。
当然还有一个规则,如果只有一个属性需要赋值,并且属性的名称为value,则赋值时value可以省略,可以直接定义值。那么,我们的num已经有了默认值,就可以不为它传值。我们发现,注解中定义的属性就剩下了一个value属性值,那么我们就可以来演示这个规则了。
这里,我并没有写属性名称value,而是直接为value赋值。如果我将num的default关键字修饰去掉呢,那意思也就是说在使用该注解时必须为num赋值,这样可以省略value吗?那我们看一下。
结果,就是我们所想的,它报错了,必须让我们给num赋值。其实想想这个规则也是很容易懂的,定义一个为value的值,就可以省略其value名称。如果定义多个值,它们可以省略名称就无法区分定义的是哪个值了,关键是还有数组,数组内定义的是多个值呢,对吧。
这里我们演示一下,上述的多种返回值类型是如何赋值的。这里我们定义这几个参数来看一下,是如何为属性赋值的。
num是一个int基本数据类型,即num = 1
value是一个String类型,即value = \”str\”
lamp是一个枚举类型,即lamp = Lamp.RED
myAnno2是一个注解类型,即myAnno2 = @MyAnno2
values是一个String类型数组,即values = {\”s1\”, \”s2\”, \”s3\”}
values是一个String类型数组,其数组中只有一个值,即values = \”s4\”
注意: 值与值之间是,隔开的;数组是用{}来存储值的,如果数组中只有一个值可以省略{};枚举类型是枚举名.枚举值
元注解就是用来描述注解的注解。一般使用元注解来限制自定义注解的使用范围、生命周期等等。
而在jdk的中java.lang.annotation包中定义了四个元注解,如下:
@Target 指定被修饰的注解的作用范围。其作用范围可以在源码中找到参数值。
由此可见,该注解体内只有一个value属性值,但是它的类型是一个ElementType数组。那我们进入到这个数组中继续查看。
进入到该数组中,你会发现它是一个枚举类,其中定义了上述表格中的各个属性。
了解了@Target的作用和属性值后,我们来使用一下该注解。首先,我们要先用该注解来修饰一个自定义注解,定义该注解的指定作用在类上。如下:
而你观察如下测试类,我们把注解作用在类上时是没有错误的。而当我们的注解作用在其他地方就会报错。这也就说明了,我们@Target的属性起了作用。
注意: 如果我们定义多个作用范围时,也是可以省略该参数名称了,因为该类型是一个数组,虽然能省略名称但是,我们还需要用{}来存储。
@Retention 指定了被修饰的注解的生命周期
注意: 我们常用的定义即是RetentionPolicy.RUNTIME,因为我们使用反射来实现的时候是需要从JVM中获取class类对象并操作类对象的。
首先,我们要了解反射的三个生命周期阶段,这部分内容我在Java反射机制中也是做了非常详细的说明,有兴趣的小伙伴可以去看看我写的Java反射机制,相信你在其中也会有所收获。
这里我再次强调一下这三个生命周期是源码阶段 – > class类对象阶段 – > Runtime运行时阶段。
那我们进入到源码,看看@Retention注解中是否有这些参数。
我们看到该注解中的属性只有一个value,而它的类型是一个RetentionPolicy类型,我们进入到该类型中看看有什么参数,是否与表格中的参数相同呢?
至于该注解怎么使用,其实是相同的,用法如下:
这就证明了我们的注解可以保留到Runtime运行阶段,而我们在反射中大多数是定义到Runtime运行时阶段的,因为我们需要从JVM中获取class类对象并操作类对象。
@Documented 指定了被修饰的注解是可以Javadoc等工具文档化
@Documented注解是比较好理解的,它是一个标记注解。被该标记注解标记的注解,生成doc文档时,注解是可以被加载到文档中显示的。
还拿api中过时的Date中的方法来说,在api中显示Date中的getYear方法是这样的。
正如你看到的,注解在api中显示了出来,证明该注解是@Documented注解修饰并文档化的。那我们就看看这个注解是否被@Documented修饰吧。
然后,我们发现该注解的确是被文档化了。所以在api中才会显示该注解的。如果不信,你可以自己使用javadoc命令来生成一下doc文档,看看被该注解修饰的注解是否存在。
@Inherited 指定了被修饰的注解修饰程序元素的时候是可以被子类继承的
首先进入到源码中,我们也可以清楚的知道,该注解也是一个标记注解。而且它也是被文档化的注解。
其次,我们去在自定义注解中,标注上@Inherited注解。
演示@Inherited注解,我需要创建两个类,同时两个类中有一层的继承关系。如下:
我们在Person类中标记了@MyAnno注解,由于该注解被@Inherited注解修饰,我们就可以得出继承于Person类的Student类也同样被@MyAnno注解标记了,如果你要获取该注解的值的话,肯定获取的也是父类上注解值的那个\”1\”。
自定义注解
Cat
准备好,上述代码后,我们就可以开始编写使用反射技术来解析注解的测试类。如下:
首先,我们先通过反射来获取注解中的methodName和className参数。
此时的打印结果证明我们已经成功获取到了该注解的两个参数。
注意: 获取类对象中的注解对象时,其原理实际上是在内存中生成了一个注解接口的子类实现对象并返回的字符串内容。如下:
继续编写我们后面的代码,代码完整版如下:
完整版代码
执行结果
执行后成功的调用了eat方法,并打印了猫吃鱼的结果,如下:
首先,我们在使用JDBC的时候是需要通过properties文件来获取配置JDBC的配置信息的,这次我们通过自定义注解来获取配置信息。其实使用注解并没有用配置文件好,但是我们需要了解这是怎么做的,获取方法也是鱼使用反射机制解析注解,所谓“万变不离其宗”,它就是这样的。
自定义注解
数据库连接工具类
为了代码的健全我也在里面加了properties文件获取连接的方式。
测试类
测试结果
为了证明获取的连接是由注解的配置信息获取到的连接,我将properties文件中的所有配置信息删除后测试的。
我不清楚小伙伴们是否了解,Junit单元测试。@Test是单元测试的测试方法上方修饰的注解。此注解的核心原理也是由反射来实现的。
作者:Ziph
原文链接:https://blog.csdn.net/weixin_44170221/article/details/106590823
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。