大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。
当前系列: Java语法 修改讲义

复习:J&C:反射和特性(注释)

说明:以下演示大量使用F3和Ctrl+T理清继承结构


Class类

两种获取方式

  • .getClass():通过实例(在运行时)获取
  • .class:通过类型(在编译时)获取
以及:通过字符串类名获得(复习)
Class<? extends Person> ci = Person.class;

@想一想@:为什么是<? extends Person>,说明什么?

//假如有:Class<Person> ciByObject = null;
Class<? extends Person> ciByObject = Teacher.class;		
Person p = ciByObject.newInstance();
同一个类型,无论通过何种方式获得的Type对象,其实都是同一个对象:

类成员

都可以通过Class对象获得:

  • getConstructors():返回构造函数
  • getField():返回字段
    Field name = Person.class.getField("name");
    //读写字段
    name.set(fg, "大飞哥");
    System.out.println(name.get(fg));
    
  • getMethod():返回方法,可使用invoke()调用该方法
    Method eat = Person.class.getMethod("eat", Double.class);
    //调用方法
    eat.invoke(new Person("飞哥"), 18.6);

Java对其进行了很好的整理(值得借鉴):

  • interface Member:类成员
    • final class Field:字段(不可执行),可调用set()/get()方法赋值取值
    • abstract class Executable:可执行的
      • final class Method:方法
      • final class Constructor<T>:构造函数

PS:Class实现的是interface Type,Type下面还有四个和泛型相关的接口(仅供了解)

所有反射相关的类,都放在两个package里面:

package java.lang;
package java.lang.reflect;

突破访问限制

以调用私有方法为例:

Method eat =Person.class.getDeclaredMethod("eat");
eat.setAccessible(true);
eat.invoke(new Person());

关键:

  • getDeclaredMethod():获取所有的,而不仅仅是私有的方法
  • setAccessible():重新设置访问权限

继承和多态

继承和多态,在反射中仍然起作用。

首先,子类类型信息对象,就直接继承(包含)着她父类成员:

//Teacher中并没有声明eat()方法,eat()来自基类
Teacher.class.getMethod("eat").invoke(new Teacher());    

其次,也可以由子类对象调用其父类成员:

//Teacher对象可以调用基类Person的eat()方法
Person.class.getMethod("eat").invoke(new Teacher());  

比较复杂一点的是如果子类覆盖(override)了父类方法:

//类似于:new Person().eat(),OK
Person.class.getMethod("eat").invoke(new Person());
//类似于:new Teacher().eat(),OK
Person.class.getMethod("eat").invoke(new Teacher());

//运行时错误:Person对象不能调用Teacher中的eat()方法
Teacher.class.getMethod("eat").invoke(new Person());


注解

自定义声明

我们可以使用@interface自己定义一个注解。

很像一个接口,嗯……,里面还可以有方法声明一样的东东(正式名称attribute:属性),比如:

@interface guru{ //注意这个@符号
	int level();
}

这样,标记时就需要指定level的值:

@guru(level = 2)
class Person {
如果level命名为value的话,value=可以省略,比如:

反射获取

要能通过反射获取到注解,需要在注解上添加一个
@Retention(RetentionPolicy.RUNTIME)

标记该注解会在一直保留(retention)到运行时(runtime)

然后,通过AnnotatedElement接口实例(比如:Class<T>/Member/Field)的:

  • getAnnotations():获取其上标记的所有注解:
    Annotation[] annotations = Person.class.getAnnotations();
    for (int i = 0; i < annotations.length; i++) {
    	System.out.println(annotations[i]);
    }
  • getAnnotation(Class<T>)方法,获取Class<T>指定的一个注解:
    Guru annotation = Person.class.getAnnotation(Guru.class);
    System.out.println(annotation.value());

内置注解

记JDK/JRE中自带的注解,简单分类:

代码用的:

  • @Override - 标记该方法重写父类方法。如果发现其父类中并没有该方法,会报编译错误。
  • @Deprecated - 标记过时成员。如果使用该成员,会报编译警告。
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告,带字符串value属性,常用的有:all、unused、unchecked……

注解用的:

  • @Retention - 标识这个注解怎么保存,带一个RetentionPolicy类型(枚举)value,可选:
    • SOURCE:只在代码中存在,编译时丢弃
    • CLASS(默认):编译后保存在类文件中,但运行时会被JVM丢弃
    • RUNTIME:一直保留,在运行时可以通过反射访问。
  • @Documented - 标记这些注解是否包含在用户文档中。
  • @Target - 标记这个注解应该是哪种 Java 成员。

其他:

  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。


package:包

复习java基础eclipse)+讲解+控制台演示

  • .java文件的编译,需要使用到JDK中的javac.exe工具,编译完成,生成包含二进制的、字节码的.java文件。
  • .java文件,可以直接用java.exe运行

所以,单个.class文件是可以执行的,但是,项目中通常都会有多个.java源代码文件,编译后就会形成多个.class文件,这时候咋办

通常我们会把它们打包(package)成.jar(Java Archive)文件,打包实际上包含两个过程:

  • 压缩:以流行的 ZIP 文件格式为基础(演示,使用7zip直接解压.jar文件)
  • 插入一些配置文件,比如:/META-INF/MANIFEST.MF,演示:
    • 记事本打开
    • 解压其他更复杂的jar包
PS:除了.jar,还有.war(Web),.ear(Enterprise)等

class loader

通过Class可以直接获取package:
Main.class.getPackage();

但不能从这个Package中得到所有的类,为啥呢?

JVM 运行并不是一次性加载所需要的全部类的,它是按需加载(又被称之为延迟加载):在程序在运行的过程中,需要用什么类,就加载什么类。

所谓加载,就是把Class 的字节码形式( *.class)转换成内存形式的 Class 对象,这个过程是通过 ClassLoader 来完成的。

F3演示:forName()调用ClassLoader……


JUnit

演示:eclipse上新建一个JUnit项目

官方定义JUnit5由三部分组成:

  • Platform:位于架构的最底层,是JVM上执行单元测试的基础平台,对接了各种IDE(例如IDEA、eclipse),定义了引擎层对接的API;
  • Jupiter:位于引擎层,支持5版本的编程模型、扩展模型;
  • Vintage:位于引擎层,用于执行低版本的测试用例

尝试着理解:Platform定义接口(API),所以是开放的

有用的注解

测试方法:

  • @Test:表示方法是测试方法。
  • @Disabled:表示测试类或测试方法不执行,类似于JUnit4中的@Ignore

测试环境:

  • @BeforeAll :表示在所有单元测试之前执行
  • @AfterAll :表示在所有单元测试之后执行
    注意:上述两个注解只能是在静态方法上标记
  • @BeforeEach :表示在每个单元测试之前执行
  • @AfterEach :表示在每个单元测试之后执行

断点演示:执行顺序


作业

  1. 写一段代码演示.class和.getClass()的区别,以及他们和forName()的区别。
  2. 将当前项目打包成.jar文件,用命令行运行
  3. 用内置注解完善之前的作业

其他见:J&C:反射 / 特性(注释)/ 单元测试工具

学习笔记
源栈学历
今天学习不努力,明天努力找工作

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 Java语法 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码