------------javaSE基础加强d6---01-Java高级技术-Junit单元测试---------------------------------------------
反射、注解、动态代理;
属于源码、框架、架构师层面的技术;
前置知识:单元测试:
- 针对最小的功能单元:方法,编写测试代码对其进行正确性测试;
- 之前是如何进行单元测试的?有什么问题?
- 在main方法里调用,去调用其他方法进行测试,
- 无法实现自动化测试,一个方法测试失效,可能影响其他方法的测试;
- 无法得到测试的报告,需要程序员自己去观察测试是否成功;
- Junit单元测试框架:
- 可以用来对方法进行测试,它是第三方公司开源出来的(很多开发工具已经集成了Junit框架,比如IDEA)
- 优点:
- 可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立;
- 不需要程序员去分析测试的结果,会自动生成测试报告出来;
- Junit单元测试的使用步骤:
- 需求:某个系统,有多个业务方法,请使用Junit单元测试框架,编写测试代码,完成对这些方法的正确性测试;
- 具体步骤:
- 将Junit框架的jar包导入到项目中(注意:IDEA集成了Junit框架,不需要我们自己手工导入了)
- 为需要测试的业务类,定义对应的测试类,并为每个业务方法,编写对应的测试方法(必须:公共、无参、无返回值);
- 测试方法上必须声明@Test注解;然后在测试方法中,编写代码调用被测试的业务方法进行测试;
- 开始测试:选中测试方法,右键选择"Junit运行",如果测试通过则是绿色,测试失败是红色;
小结:
- Junit单元测试是做什么的?
- 测试类中方法的正确性;
- Junit单元测试的优点是什么?
- Junit可以选择执行哪些测试方法,可以一键执行全部测试方法的测试;
- Junit可以生成测试报告,如果测试良好则是绿色,如果测试失败则是红色;
- 单元测试中某个方法测试失败了,不会影响其他测试方法的测试;
- Junit单元测试的实现过程?
- 必须导入Junit框架的jar包;
- 定义的测试方法必须是无参数无返回值,且公开的方法;
- 测试方法使用@Test注解标记;
- Junit测试某个方法,测试全部方法怎么处理?成功的标志是什么?
- 测试某个方法直接右键该方法启动测试;
- 测试全部方法,可以选择类或模块启动;
- 红色失败、绿色代表通过;
------------javaSE基础加强d6---02-Java高级技术-反射概述---------------------------------------------------
反射:(Reflection):加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)。
反射具体学什么?
学习获取类的信息,操作他们;
- 反射第一步:加载类,获取类的字节码:Class对象;
- 获取类的构造器:Constructor对象;
- 获取类的成员变量:Field对象;
- 获取类的成员方法:Method对象;
- 全部认识完后,再看反射的应用场景;
获取Class对象的三种方式:
- Class c1 = 类名。class
- 调用Class提供方法:public static Class forName(String package);
- Object提供的方法:public Class getClass(); Class c3 = 对象。getClass();
小结:
- 反射指的是什么?
- 反射主要学什么?
- 反射第一步是什么?
- 如何获取Class?
------------javaSE基础加强d6---03-Java高级技术-反射获取类的成分并对其进行操作------------------
Class提供了从类中获取构造器的方法:
|-----------------------------------------------------------------------|-------------------------|
| 方法 | 说明 |
| Constructor<?>[ ] getConstructors() | 获取全部的构造器(只能获取public修饰的) |
| Constructor<?> [ ] getDeclaredConstryctors() | 获取全部构造器(只要存在就能拿到) |
| Constructor<T> getConstructor(Class<?> ...parameterTypes) | 获取某个构造器(只能public修饰的) |
| Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 获取某个构造器(只要存在就能拿到) |
获取类构造器的作用:依然是初始化对象返回:
|-----------------------------------------|----------------------------------|
| Constructor提供的方法 | 说明 |
| T newInstance(Object... initargs) | 调用此构造器对象标识的构造器,并传入参数,完成对象的初始化并返回 |
| public void setAccessible(boolean flag) | 设置为ture,标识进制检查访问控制(暴力反射) |
- 反射第一步:加载类,获取类的字节码:Class对象;
- 构造器:Constructor对象
- 成员变量:Field对象
- 成员方法:Method对象
java
package com.itheima.demo2reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//使用方法
public class Demo2_2Reflect {
//目标:掌握反射第二步:获取类的对象;并对其进行操作;
//1、反射第一步:获取类本身;
@Test
public void getClassInfo() {
Class c1 = Dog.class;
System.out.println(c1.getName()); //类名的全类名;
System.out.println(c1.getSimpleName()); //类名;
}
@Test
public void getConstructorInfo() throws Exception {
//目标:获取类的构造方法;
Class c1 = Dog.class;
//2、获取构造器对象;
Constructor[] cons = c1.getDeclaredConstructors();
for (Constructor con : cons) {
System.out.println(con.getName() + "2、获取构造器对象 (" + con.getParameterCount() + ")");
}
//3、获取单个构造器:
Constructor con1 = c1.getDeclaredConstructor();
System.out.println(con1.getName() + "3、获取单个构造器 (" + con1.getParameterCount() + ")");
//4、获取有参构造器
Constructor con2 = c1.getDeclaredConstructor(String.class,int .class); //定位到有参构造器
System.out.println(con2.getName() + "4、获取有参构造器 (" + con2.getParameterCount() + ")");
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//拿到构造器的作用依然是创建对象:
//ok报错没关系,因为前面设置的无参构造器是私有的,这里可以暴力反射,可以访问私有的构造器、方法、属性;
con1.setAccessible(true);
Dog d1 = (Dog)con1.newInstance(); //它也不知道这是什么构造器,所以要强转为狗构造器;
System.out.println(d1);
Dog d2 = (Dog) con2.newInstance("小花", 10);
System.out.println(d2);
}
//3、获取类的成员变量并对其进行操作:
@Test
public void testGetFieldInfo() throws Exception {
//目标:获取类的成员变量并操作;
//1、反射第一步,获取Class对象,代表拿到类:
Class c1 = Dog.class;
Field[] fields = c1.getDeclaredFields();
for (Field field : fields){
System.out.println(field.getName() + " " + field.getType().getName());
}
System.out.println("------------------------------------------------------------------------------------------------------------------------------------------");
//2、获取单个成员变量并操作
Field field = c1.getDeclaredField("hobby");
System.out.println(field.getName() + " " + field.getType().getName());
Field field1 = c1.getDeclaredField("age");
System.out.println(field1.getName() + " " + field1.getType().getName());
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//获取成员变量的目的依然是取值和赋值;
Dog d = new Dog("泰迪" , 5);
field.setAccessible( true); //暴力反射;;
field.set(d,"社交");
//d.setHobby("社交");
System.out.println(d);
String hobby = (String) field.get(d);
System.out.println(hobby);
}
//4、获取成员变量并操作
@Test
public void testGetFields() throws Exception {
Class c1 = Dog.class;
Method[] methods = c1.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName() + "(" + method.getParameterCount() + ")");
//获取方法的名称 + 参数个数
}
System.out.println("------------------------------------------------------------------------------------------------------------------------------------------");
Method m = c1.getDeclaredMethod("eat");
System.out.println(m.getName() + "(" + m.getParameterCount() + ")");
Method m2 = c1.getDeclaredMethod("eat" , String.class);
System.out.println(m2.getName() + "(" + m2.getParameterCount() + ")");
System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------");
//获取方法的作用也是为了调用方法;
Dog dog = new Dog("社交" , 3);
m.setAccessible( true); //这里的m就代表上边的eat方法;;
// m.invoke(dog); //唤醒对象dog的eat方法执行,相当于dog.eat();
Object rs1 = m.invoke(dog); //
System.out.println("返回值: ---" + rs1); //没有返回值
//m2方法: public void eat(String name){
// System.out.println(name + "狗子吃吃吃");
Object rs2 = m2.invoke(dog,"三十斤胖");
System.out.println("返回值: ---" + rs2); //因为它是对象dog带String参数类型的eat方法执行,相当于dog.eat("三十斤胖");
}
}
小结:
- 如何去获取类的构造器、成员变量、方法?
- 获取到他们、各自有啥作用?
- 如何突破访问权限?
- 对象。setAccessible(ture);暴力反射;
------------javaSE基础加强d6---04-Java高级技术-反射的作用------------------------------------------------
- 反射的作用?
- 基本作用:可以得到一个类的全部成分然后操作;
- 破坏封装性;
- 可以绕过泛型的约束;
- 最重要的用途是:适合做java的框架,
- 基本上,主流的框架都会基于反射设计出一些通用的技术功能;
- 使用反射做一个简易版的框架:
- 需求:
- 对于任意一个对象,该框架都可以把对象的字段名和对应的值,保存到文件中去;
- 实现步骤:
- 定义一个方法,可以接收任意对象;
- 每收到一个对象后,使用反射获取该对象的Class对象,然后获取全部的成员变量;
- 遍历成员变量,然后提取成员变量在该对象中的具体值;
- 把成员变量名,和其值,写出到文件中即可;
- 需求:
简易框架代码:
java
package com.itheima.demo2reflect;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
public class SaveObjectFrameWork {
//保存任意对象的静态方法:
public static void saveObject(Object obj)throws Exception{
// PrintStream ps = new PrintStream("day06-junit-reflect-annotation-proxy\\src\\com\\itheima\\demo2reflect\\demo2_Save.txt");
PrintStream ps = new PrintStream(new FileOutputStream("day06-junit-reflect-annotation-proxy\\src\\com\\itheima\\demo2reflect\\demo4_Save.txt",true));
//obj 可能是老师、学生、狗
//只有反射可以知道对象有多少个字段;
//1、获取Class对象
Class c1 = obj.getClass();
String simpleName = c1.getSimpleName();
ps.println("======================" + simpleName + "======================");
//2、获取所有字段
Field[] fields = c1.getDeclaredFields();
//3、遍历所有的字段;
for (Field field : fields){
//4、获取字段值
//4.1、获取字段名称:
field.setAccessible(true);
String fieldName = field.getName();
//4.2、获取字段值
Object fieldValue = field.get(obj) + "";
//5、打印到文件中;
ps.println(fieldName + "=" + fieldValue);
}
ps.close();
}
}
测试代码:
java
package com.itheima.demo2reflect;
public class demo4Reflect {
public static void main(String[] args) throws Exception{
//目标:搞清楚反射的作用,做框架的通用技术:
Dog dog = new Dog("二蛋","睡大觉",10);
SaveObjectFrameWork.saveObject(dog);
Student s = new Student("小王",18,"睡觉");
SaveObjectFrameWork.saveObject(s);
Teacher t = new Teacher("小李",18,"女","爱自己在家玩",dog,60000,"123456");
SaveObjectFrameWork.saveObject(t);
}
}
(需要提前定义好着几个类)
------------javaSE基础加强d6---05-Java高级技术-注解-自定义注解-元注解---------------------------------
注解:相对抽象;;
-
注解概述:
- 就是java代码里的特殊标记,比如@Override、@Test等;
- 作用:让其他程序根据注解信息来决定怎么执行该程序;
- 注意:注解可以用在类、构造器、方法、成员变量、参数上等位置;
-
自定义注解:
- 就是自己定义注解:
javapublic @interface 注解名称{ public 属性类型 属性名() default 默认值; }特殊属性名:value;如果注解中只有一个value属性,使用注解时,value名称可以不写;
-
注解的原理:
-
反编译后发现,注解本质是接口,这个接口继承了Annotation类(所有的注解都继承了这个类)
-

-
所有的属性都是抽象方法;
-
@注解(...):其实就是一个实现类对象,实现了该注解以及Annotation接口;
-
-
注解的作用:
-
对Java中类、方法、成员变量做标记,然后进行特殊处理;
-
例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,没用标记的就不能当测试方法执行;
-
元注解:
- 元注解指的是:注解注解的注解;
-
@Target:目标位置:
- 声明被修饰的注解只能在哪些位置使用:
java@Target(ElementType.TYPE) 1、TYPE;类、接口 2、FIELE; 成员变量 3、METHOD; 成员方法 4、PARAMETER; 方法参数 5、CONSTRUCTOR ;构造器 6、LOCAL_VARIABLE ; 局部变量1
-
@Retention:保留周期;
-
作用:声明注解的保留周期;
java@Retention(RetentionPolicy.RUNTIME) 1、SOURCE;;只作用在源码阶段;字节码中不存在; 2、CLASS(默认);保留到字节码文件阶段,运行阶段不存在; 3、RUNTIME(开发常用); 一直保留到运行阶段; -
-
------------javaSE基础加强d6---06-Java高级技术-注解的解析------------------------------------------------
-
注解的解析:
- 就是判断类、方法、成员变量上是否存在注解,并把注解里的内容给解析出来;
-
如何解析注解?
-
指导思想:要解析谁上边的注解,就应该先拿到谁;
-
比如要解析类上边吗注解,就应该先拿到该类的Class对象,再通过Class对象在解析其上边的注解;
-
Class、Method、Field、Constructor、都实现了AnnotatedElement接口,他们都有解析注解的能力
|-------------------------------------------------------------------------|-----------------|
| AnnotatedElement接口提供了解析注解的方法 | 说明 |
| public Annotation[ ] getDeclaredAnnotations( ) | 获取当前对象上边的注解 |
| public T getDeclaredAnnotation(Class<T> annotationClass) | 获取指定的注解对象 |
| public boolean isAnnotationPresent(Class<Annotation> annotationClass) | 判断当前对象上是否存在某个注解 |1
-
-
解析注解的案例:
-
定义注解MyTest4,要求:
-
包含属性:String value()
-
包含属性:double aaa(),默认值为100;
-
包含属性:String[ ] bbb()
-
限制注解使用的位置:类和成员方法上
-
指定注解的有效范围:一直到运行时;
-
-
定义一个类叫:Demo,在类中定义一个test1方法,并在该类和其方法上使用MyTest4注解;
-
定义AnnotationTest3测试类,解析Demo类中的全部注解;
-
------------javaSE基础加强d6---07-Java高级技术-注解的应用场景------------------------------------------
注解的作用:进行特殊标记,方便别人特殊使用;
注解的属性:比如属性int count() default 1; 然后有一个方法注解count = 5就是这个方法要测五次;
使用注解开发出一个简易版的Junit框架:
- 需求:定义若干个方法,只要加了MyTest注解,就会触发该方法执行;
- 分析:
- 定义一个自定义注解MyTest,只能注解方法,存货范围一直都在;
- 定义若干个方法,部分方法加上@MyTest注解修饰,部分方法不加;
- 模拟一个junit程序,可以触发加了@MyTest注解的方法执行;
注解类:
java
package com.itheima.demo3annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
int count() default 1;
}
测试类:
java
package com.itheima.demo3annotation;
import java.lang.reflect.Method;
public class Demo5Annnotation {
public static void main(String[] args) {
//目标:搞清楚注解的应用场景:模拟Junit框架,有MyTest注解的方法就执行;
//1、获取类对象
Class c1 = Demo5Annnotation.class;
//2、获取所有方法
Method[] methods = c1.getDeclaredMethods();
//3、遍历方法,判断方法上是否有MyTest注解
for (Method method : methods) {
if (method.isAnnotationPresent(MyTest.class)){
// System.out.println("执行方法:"+method.getName());
try {
int count = method.getAnnotation(MyTest.class).count();
for (int i = 0; i < count; i++) {
// method.invoke(new Demo5Annnotation()); //需要有对象才能触发;
Demo5Annnotation d = new Demo5Annnotation();
method.invoke(d); //跟上边一样;
}
}catch (Exception e){e.printStackTrace();}
}
}
}
public void test1(){
System.out.println("test1方法执行了...");
}
@MyTest
public void test2(){
System.out.println("test2方法执行了...");
}
public void test3(){
System.out.println("test3方法执行了...");
}
@MyTest
public void test4(){
System.out.println("test4方法执行了...");
}
@MyTest(count = 5)
public void test5(){
System.out.println("test5方法执行了...");
}
}
------------javaSE基础加强d6---08-Java高级技术-代理概述-动态代理的代码实现------------------------
动态代理:一种设计模式;
相当于中介,提前为对象准备好需要的资源,然后传给对象;
-
如何为java对象创建一个代理对象?
- java.lang.reflect.Proxt类:提供了为对象产生代理对象的方法:
javapublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) //参数一:用于指定用哪个类加载器,去加载生成的代理类; //参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法? //参数三:用来指定生成的代理对象要干什么事情
1
- 程序为什么需要代理?代理长什么样子?
- 后期很多
小结:
- Java提供了什么API帮我们创建代理?
- Proxy。newproxyInstance()
- Proxy.newProxyInstance方法在创建代理时,需要接几个参数?每个参数的含义是什么?
- 参数一:用于执行用哪个类加载器去加载生成的代理类;
- 参数二:代理对象要代理的接口;明星类实现了哪些接口,代理类就实现了哪些接口;
- 参数三:用于指定代理类需要如何去代理(代理要做的事情);
- 通过invokehandler的invoke方法指定代理干的事时,这个invoke会被谁调用,要接哪几个参数?
1.
------------javaSE基础加强d6---09-Java高级技术-动态代理-解决实际问题-优势---------------------------
解决实际问题;掌握使用代理的好处:
案例:使用代理优化用户管理类:
- 场景:
- 某系统有一个用户管理类:包含用户登录;删除用户,查询用户等功能,系统要求统计每个功能的执行耗时情况,以便后期观察程序性能;
- 需求:
- 现在,某个初级程序员已经开发好了该模块,请观察该模块的代码,找出目前存在的问题,并对其进行改造;
Java中的王者框架:Spring框架;可以理解为一个代理加工厂,可以处理所有的方法;
一种思想:
AOP切面编程:在调我方法的时候,方法前植入了一段代码;方法执行之后植入了一段代码;