类加载器的使用场景
ClassLoader : 类加载器
类加载器的使用场景: 根据类加载器对象快速找到当前模块src根目录下的文件
//成员方法
static ClassLoader getSystemClassLoader() : 拿到系统类加载器
InputStream getResourceAsStream(String name) : 快速的创建一个输入流对象指向源文件文件(name参数的地址指向的文件)
String name: 文件的路径(相对路径:起点是当前模块的src的根目录)
static InputStream getSystemResourceAsStream(String name) : 直接获取流指向文件(文件的相对路径起点也是src的根)
单元测试
测试 : 一项开发很重要的工作;
黑盒测试和白盒测试:
黑盒测试: 看不到代码的测试(不用操作代码的测试)
白盒测试: 能看到源代码的测试(需要操作代码的测试) -> 单元测试,debug
使用单元测试的步骤:
1. 建测试类 MyTest.java
2. 建测试方法
public void testXxxx(){
//需要测试的代码
}
3. 在测试方法的声明上加测试注解 @Test
//测试方法就可以单独执行
断言操作
Assert.assertEquals(long expected, long actual)
expected: 预期值
actual:实际值
@Before : 测试方法之前需要执行的方法
@After :测试方法之后需要执行的方法
反射
反射 : 是一种技术手段,一种编写代码的思维方式 -> reflect
技术手段的区别:
"正射" : 我们之前写代码的方式都是以 "正射" 的方式写代码
1. 创建类的对象 Student stu = new Student();
2. 对象调用方法 stu.study();
反射 :
1. 获取类的字节码对象 (因为字节码对象中封装着类的成员) Student.class
2. 获取类中要启动的成员对象 study方法对象 = Student的字节码对象.获取study方法对象();
3. 使用成员的对象启动,传入要启动方法的本类对象 study方法对象.invoke(stu)
思想方面的区别:
"正射": //面向需求
1. 代码中必须有Student类
2. 必须中的学生对象如何创建
3. 必须知道学生对象需要调用哪个方法
反射: //面向解决需求的人
写一个通用代码 例如: 某个类的对象要调用某个类的方法
只需要别人告诉你 类名是什么,方法名是什么 你就可以帮助这个人把方法调用
反射的第一步: 获取字节码对象
反射的第一步 : 获取类的字节码对象
第一种: 类名.class ;
每个类型都有class属性(数组也有,基本数据类型也有)
第二种: 对象.getClass()
所有的对象都有getClass()(数组也有,基本数据类型没有)
第三种: Class类中有静态方法: forName()
Class.forName("全类名"); //全类名: 包名.类名
一个类只可能有一个字节码对象!!
反射的第二步: 获取类中的成员
构造方法-Constructor
Class类中成员方法:
//虽然只能拿public修饰的,但是不加Declared可以拿到父类的成员!
Constructor<?>[] getConstructors() : 获取类中所有的public修饰的构造方法对象
Constructor<T> getConstructor(Class<?>... parameterTypes)
//Declared可以拿本类所有的成员 但是拿不到父类的成员!
Constructor<?>[] getDeclaredConstructors() :获取类中所有的构造方法对象
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取指定的某个构造方法对象
Class<?>... parameterTypes: 形参类型的字节码对象
成员变量-Field
//这一组方法能只能拿到本类public修饰的成员变量和父类public的成员变量
Field[] getFields() :
Field getField(String name)
//这一组方法能拿到本类所有的成员变量不能拿父类的成员变量
Field[] getDeclaredFields()
Field getDeclaredField(String name)
String name : 传入指定的变量名
成员方法-Method
//这一组方法能只能拿到本类public修饰的成员方法和父类public的成员方法
Method[] getMethods() :
Method getMethod(String methodName,Class...parameterTypes)
//这一组方法能拿到本类所有的成员方法不能拿父类的成员方法
Method[] getDeclaredMethods() :
Method getDeclaredMethod(String methodName,Class...parameterTypes)
反射的第三步: 使用类中的成员
构造方法: 创建对象
有了构造方法对象: 调用构造方法(创建对象)
Constructor类中:
T newInstance(Object... initargs)
Object... initargs : 启动方法传入的实际参数
遇到没有权限访问的成员,虽然能拿到成员的对象,但是真正使用时必须开启暴力反射开关:
Constructor,Field,Method类:
void setAccessible(boolean flag)
有一个快捷调用无参构造的方法,来自于Class类(如果类中没有无参构造,报错)
T newInstance() : 快速的创建一个本类对象,但是必须保证本类中有无参构造
成员变量: 获取属性值/设置属性值
拿到成员变量的对象,就可以对该变量进行获取值和设置值的操作
Field类:
Object get(Object obj) : 获取
Object obj: 获取哪个对象的该属性值
返回值Object : 拿到的值
void set(Object obj, Object value) : 设置
Object obj: 获取哪个对象的该属性值
Object value: 你要设置的值
以前:
student.getName()
类对象.get方法() 获取某个属性值
现在反射:
name.get(student)
属性对象.get方法(传入哪个对象) 传入哪个对象就获取哪个对象的属性值
成员方法:调用方法
Method类:
Object invoke(Object obj, Object... args)
Object obj : 哪个对象启动方法
Object... args : 启动方法的实参
Object : 接收方法的返回值的
//void 无返回值方法invoke的返回值是null
以前:
返回值 = 对象.方法(实参);
反射:
返回值 = 方法对象.invoke(调用方法的对象,实参);
注解
注释 : 用文字对代码进行解释说明的文字
注解 : 用代码对代码进行控制,操作的 代码
什么是注解
注解英文是annotation,是一种代码级别的说明(注释: 文字对代码的解释说明;注解:代码对代码的解释说明),注解和类 接口平级关系。相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上定义.
注解的作用
- 执行编译期的检查 例如:@Override
- 注解开发 例如@Test
JDK提供的三个基本的注解
@Override
:描述方法的重写.@SuppressWarnings
:压制警告.@Deprecated
:标记过时
自定义注解(重点)
自定义注解语法
语法: @interface 注解名{}
示例
/**
* 定义了注解
*
*/
public @interface Annotation01 {
}
定义新的Annotation类型使用关键字@interface关键字;
自定义注解自动继承了java.lang.annotation.Annotation接口;
注解属性
注解属性的作用
注解属性可以让注解具备携带存储数据的功能
注解属性的类型
-
基本类型
-
String
-
枚举类型
-
注解类型
-
Class类型
-
以上类型的一维数组类型
属性的定义格式: 属性的数据类型 属性名();
注意:
一旦注解有属性了,使用注解的时候,属性必须有值
- 示例代码
/**
*注解的属性; 格式和接口的方法很类似
* 1.基本类型
2.String
3.枚举类型
4.注解类型
5.Class类型
6.以上类型的一维数组类型
*/
public @interface Annotation02 {
int a();//基本类型
String b();//String
Color c();//枚举类型
Annotation01 d();//注解类型
Class e();//Class类型
String[] f();//一维数组类型
}
使用注解时给属性赋值(重要)
-
格式
@注解名(属性名=值,属性名2=值2) eg:@MyAnnotation3(i = 0,s="23")
属性赋值的特殊情况(重要)
-
若属性类型的一维数组的时候,当数组的值只有一个的时候可以省略{}
@MyAnnotation4(ss = { "a" }) @MyAnnotation4(ss = "a")
-
注解属性可以有默认值
属性类型 属性名() default 默认值; //属性有默认值后,在使用注解的时候就可以选择性的给属性赋值
-
若属性名为value的时候,且只有这一个属性需要赋值的时候可以省略value【重点】
元注解
注解: 用代码约束代码
元注解: 用元注解来约束注解的
元注解的作用
元注解是使用在自定义的注解上,为自定义的注解提供支持的
常用的元注解
@Target
: 用于指定被修饰的注解Annotation能用在修饰哪些程序元素,默认注解可以在任何程序元素. 值为:ElementType
的枚举值
METHOD
:方法
TYPE
:类 接口
FIELD
:字段
CONSTRUCTOR
:构造方法声明
@Retention
:定义该注解保留到那个代码阶段, 值为:RetentionPolicy
类型,默认只在源码阶段保留
SOURCE
:只在源码上保留(默认)
CLASS
:在源码和字节码上保留
RUNTIME
:在所有的阶段都保留
java (源码阶段) ----编译—> .class(字节码阶段) ----加载内存–> 运行(RUNTIME)
@Target(value = {ElementType.METHOD,ElementType.TYPE })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyAnnotation03 {
int a();
String b();
}
- @Documented: 被修饰的注解将保留在文档工具类(API)中
- @Inherited: 标注的注解是否能被继承
注解解析
注解解析目的
- 获取类、成员方法、成员变量、方法属性、构造方法等等上面的注解对象
- 获取注解对象的属性
- 判断某个类、成员变量、成员方法等等上面是否有某个注解
Method、Field、Constructer、Class都实现了AnnotationElement的接口
java.lang.reflect.AnnotatedElement
- ****** T getAnnotation(ClassannotationType)**:得到指定类型的注解引用。没有返回null。
- ****boolean isAnnotationPresent(Class<?extends Annotation> annotationType):判断指定的注解类型是否存在于某个元素上
- Annotation[] getAnnotations():得到所有的注解,包含从父类继承下来的。
- Annotation[] getDeclaredAnnotations():得到自己身上的注解。
public @interface Annotation01(){
}
@Annotation01
class Demo01(){
@Annotation01
public void fun01(){
}
public void fun02(){
}
}
//1.获得Demo01字节码对象
Class clazz = Demo01.class;
//2. 获得Demo01上面的注解对象
Annotation01 annotation01 = clazz.getAnnotation(Annotation01.class);
//3.反射获得fun01()方法对象
Method method = clazz.getMethod("fun01");
//4.判断fun01()方法上面是否有@Annotation01注解
boolean flag = method.isAnnotationPresent(Annotation01.class);