Jnint单元测试



1、在导包@Test的时候,鼠标先放到"@Test"上,然后按住"Alt+Enter"键选择Junit4导包
2、可能会出现测试没有报错但实际出错的情况,这时就要做断言(判断断言结果是否与预期结果一致),使用API:Assert.assertEquals("本轮测试失败,业务获取***有问题,请检查",预期结果值,预期结果只值给的变量)
Jnint单元测试小结


反射
属于框架的底层原理
反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)
当在IDEA中使用 "某个对象."的时候, IDEA会自动的显示出该对象属于的类中有哪些成员变量和方法,将类解剖显示出里面的东西就叫做反射

万物皆对象,类本身也是一个对象。
反射具体学什么?
1、反射第一步:加载类,获取类的字节码:class对象
2、获取类的构造器:Constructor对象
3、获取类的成员变量:Field对象
4、获取类的成员方法:Methon对象

反射第一步:加载类,获取类的字节码:class对象

java
package com.itheima.demoreflect;
public class ReflectDemo1 {
public static void main(String[] args) throws Exception {
//目标:掌握反射第一步操作:或者类的Class对象(获取类本身)
//1、获取类本身:类本名.class
Class c1 = Student.class;//类文件就是个对象
System.out.println(c1);
//2、获取类本身:类对象.forName("类的全类名") 通过类全类名获取Class对象
Class c2 = Class.forName("com.itheima.demoreflect.Student");
System.out.println(c2);
//3、获取类本身:对象.getClass() 通过学生new对象再获取对象对应的类本身
Student s = new Student();
Class c3 = s.getClass();//getClass()是Object类中的方法,谁都可以调用
System.out.println(c3);
System.out.println(c1 == c2);//true
System.out.println(c2 == c3);//true
}
}
//=================分界线=================
package com.itheima.demoreflect;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
}
获取类中的成分、并对其进行操作
构造器、成员方法、成员变量需要的API分别来自Constructor类、Method类、Field类
1、只能拿public修饰的构造器,一般不用
2、私有的private修饰的构造器也可以拿,经常用
3、也只能拿public修饰的构造器,且一般根据类型,一般不用
4、私有的private修饰的构造器也可以拿,且一般根据类型,经常用(...代表可变参数)
注意:获取单个构造器和单个成员方法需要区别有参构造器与无参构造器和有参方法与无参方法
暴力反射:暴力反射可以访问私有的构造器、方法、成员变量(con.setAccessible(true);//绕过访问权限直接访问)
反射的作用:
学习反射之前是拿对象直接调构造器、成员变量、成员方法,有了反射之后是拿到对象的方式不同了,作用还是调构造器、成员变量、成员方法

定位方法需要两个参数,一个是方法名(因为同一个方法名可能有两个方法,一个有参,一个无参),为了区分两个方法,还需要一个为参数类型


java
package com.itheima.demoreflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectDemo2 {
@Test
public void getClassInfo() {
//目标:获取类的信息
//1、反射第一步:获取Class对象,代表拿到类
Class c1 = Student.class;
System.out.println(c1.getName());//类名的全类名
System.out.println(c1.getSimpleName());//类的简名
}
//2、获取类的构造器对象并对其进行操作
@Test
public void getConstructor() throws Exception {
//目标:获取类的构造器对象并对其进行操作
//1、反射第一步:获取Class对象,代表拿到类
Class c1 = Dog.class;
//2、获取全部构造器对象
Constructor[] cons = c1.getDeclaredConstructors();
for (Constructor c : cons) //遍历得到的构造器数组中的每个构造器对象
{
System.out.println(c.getName() + "(" + c.getParameterCount() + ")");
}
//3、获取单个无参构造器
Constructor con = c1.getDeclaredConstructor();//无参数构造器
System.out.println(con.getName() + "(" + con.getParameterCount() + ")");
//4、获取单个有参构造器
Constructor con2 = c1.getDeclaredConstructor(String.class, int.class);//有参构造器(括号中String和int分别代表第一个和第二个参数的类型,为什么要.class,因为规定)
System.out.println(con2.getName() + "(" + con2.getParameterCount() + ")");
//5、获取构造器的作用依然是创建对象:创建对象。(感觉脱裤子放屁,但有其道理)
//暴力反射:暴力反射可以访问私有的构造器、方法、成员变量
con.setAccessible(true);//绕过访问权限直接访问
Dog d1 = (Dog) con.newInstance();//该无参构造器为私有报错,要用到暴力反射
System.out.println(d1);//通过ToString重写了 Dog{name='null', age=0}
Dog d2 = (Dog) con2.newInstance("旺财", 5);//该有参构造器本身就是公开的,不需要暴力反射
System.out.println(d2);//通过ToString重写了 Dog{name='旺财', age=5}
}
//3、获取类的成员变量对象并对其进行操作
@Test
public void getField() throws Exception {
//目标:获取类的成员变量对象并对其进行操作
//1、反射第一步:获取Class对象,代表拿到类
Class c1 = Dog.class;
//2、获取全部成员变量对象
Field[] fields = c1.getDeclaredFields();
for (Field f : fields) {
System.out.println(f.getName() + "(" + f.getType().getName() + ")");//name(java.lang.String)
//age(int)
}
//3、获取单个成员变量对象
Field f1 = c1.getDeclaredField("name");
System.out.println(f1.getName() + "(" + f1.getType().getName() + ")");//name(java.lang.String)
Field f2 = c1.getDeclaredField("age");
System.out.println(f2.getName() + "(" + f2.getType().getName() + ")");//age(int)
//4、获取成员变量的作用仍然是取值和赋值
Dog d1 = new Dog("旺财", 5);//为了方便直接new一个而不反射获得成员变量
f1.setAccessible(true);//暴力反射:绕过访问权限直接访问
f1.set(d1, "小花");
System.out.println(d1);//Dog{name='小花', age=5}
String name = (String) f1.get(d1);
System.out.println(name);//小花
}
//4、获取类的成员方法对象并对其进行操作
@Test
public void getMethod() throws Exception {
//目标:获取类的成员方法对象并对其进行操作
//1、反射第一步:获取Class对象,代表拿到类
Class c1 = Dog.class;
//2、获取全部成员方法对象
Method[] methods = c1.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m.getName() + "(" + m.getParameterCount() + ")");
}
//3、获取单个成员方法对象
Method m1 = c1.getDeclaredMethod("eat");//1个参数代表获取的是无参数的eat方法
System.out.println(m1.getName() + "(" + m1.getParameterCount() + ")");
Method m2 = c1.getDeclaredMethod("eat", String.class);//2个参数代表获取的是有参数的eat方法
System.out.println(m2.getName() + "(" + m2.getParameterCount() + ")");
//4、获取成员方法的作用仍然是调用方法
Dog d = new Dog("旺财", 5);//方法不能单独调需要依赖于对象,所以这里为了简便直接new一个对象而不是反射一个对象
m1.setAccessible(true);//暴力反射:绕过访问权限直接访问
Object rs1 = m1.invoke(d);//唤醒对象d的eat方法执行,相当于d.eat()
System.out.println(rs1);//null
Object rs2 = m2.invoke(d,"小花");//唤醒对象d的eat方法执行,相当于d.eat("小花")
System.out.println(rs2);
}
}
//=================分界线=================
package com.itheima.demoreflect;
public class Dog {
private String name;
private int age;
private Dog(){
System.out.println("无参构造器执行了");
}
public Dog(String name){
System.out.println("一个参数的有参构造器执行了");
this.name = name;
}
public Dog(String name, int age){
System.out.println("两个参数的有参构造器执行了");
this.name = name;
this.age = age;
}
private void eat(){
System.out.println("吃吃吃");
}
public String eat(String name){
System.out.println("吃吃" + name);
return "狗说:谢谢";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
作用、应用场景
只有反射可以知道对象有多少个成员变量、构造器、成员方法



java
package com.itheima.demoreflect;
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
//目标:搞清楚反射的应用,做框架的通用技术
Dog dog = new Dog("小黑",5);
SaveObjectFrame.saveObject(dog);
//创建学生对象
Student student = new Student("小王",18,"football");
SaveObjectFrame.saveObject(student);
//创建老师对象
Teacher teacher = new Teacher("小红",18,"football",5000,"Java",'女',"12345678");
SaveObjectFrame.saveObject(teacher);
}
}
//=================分界线=================
package com.itheima.demoreflect;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
public class SaveObjectFrame {
//保存任意对象的静态方法
public static void saveObject(Object obj) throws Exception {
PrintStream ps = new PrintStream(new FileOutputStream("D:\\JAVA\\code\\javaseprojectaimax\\day06-junit-reflect-annotation/src/object.txt", true));//追加需用低级管道包装 打印流
//obj可能是学生 老师 狗
//只有反射可以知道对象有多少个字段
//1、获取Class对象
Class c = obj.getClass();
String simpleName = c.getSimpleName();
ps.println("=========" + simpleName + "========");
//2、获取Class对象的所有字段
Field[] fields = c.getDeclaredFields();
//3、遍历字段
for (Field f : fields) {
//获取字段的名称
String fieldName = f.getName();
//获取字段的值
f.setAccessible(true);//暴力反射
Object fieldvalue = f.get(obj) + "";
//打印到文件中去
ps.println(fieldName + "=" + fieldvalue);
}
ps.close();
}
}
//=================分界线=================
package com.itheima.demoreflect;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private String name;
private int age;
private String hobby;
private double salary;
private String className;
private char sex;
private String phone;
}
//=================分界线=================
package com.itheima.demoreflect;
public class Dog {
private String name;
private int age;
private Dog(){
System.out.println("无参构造器执行了");
}
public Dog(String name){
System.out.println("一个参数的有参构造器执行了");
this.name = name;
}
public Dog(String name, int age){
System.out.println("两个参数的有参构造器执行了");
this.name = name;
this.age = age;
}
private void eat(){
System.out.println("吃吃吃");
}
public String eat(String name){
System.out.println("吃吃" + name);
return "狗说:谢谢";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//=================分界线=================
package com.itheima.demoreflect;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
private String hobby;
}
//=================分界线=================
//编译后使用反射构建出的文件
=========Dog========
name=小黑
age=5
=========Student========
name=小王
age=18
hobby=football
=========Teacher========
name=小红
age=18
hobby=football
salary=5000.0
className=Java
sex=女
phone=12345678
注解
注解就是对java中类、方法、成员遍历做标记,然后进行特殊处理


注解原理
每一个注解都继承了Annotation接口 ,并且内部的属性都是抽象方法

java
package com.itheima.demo3annotation;
@MyBook(name="张三",age=20,address={"西安","北京"})
//@A(value="delete")
@A("delete")//特殊属性,在使用时如果只有一个value属性,则value可以省略
public class AnnotationDemo1 {
@MyBook(name="罗峰",age=2000000,address={"虚拟宇宙","乾乌宇宙国"})
public static void main(String[] args) {
//目标:自定义注解
}
}
//=================分界线=================
package com.itheima.demo3annotation;
//自定义注解
public @interface MyBook {
String name();
int age() default 18;
String[] address();
}
//=================分界线=================
package com.itheima.demo3annotation;
public @interface A {
String value();//特殊属性,在使用时如果只有一个value属性,则value可以省略,或者其余值有默认值也可以省略
String hobby() default "看电影";
}
元注解
元注解作用:用于给注解 注解
1、@Target(ElementType.METHOD)// 声明注解作用范围
2、@Retetion一般用第三种方法会一直保留到运行阶段

java
package com.itheima.demo3annotation;
public class AnnotationDemo2 {
//@MyTest1 报错
private int age;
//@MyTest1 报错
public AnnotationDemo2(){
}
public static void main(String[] args) {
//目标:搞清楚元注解的作用
}
@MyTest1
public void getAgeTest(){
}
}
//=================分界线=================
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,ElementType.FIELD})// 声明注解作用范围
@Retention(RetentionPolicy.RUNTIME)// 声明注解的保留策略:编译器运行时一直保留到运行时
public @interface MyTest1 {
}

注解的解析

解析注解案例

复习:内容是数组会显示内存地址,打印需用toString。而其他基本类型/字符串直接打印就会显示内容
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,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)//表示注解的保留策略
public @interface MyTest2 {
String value();
double height() default 169.5;
String[] address();
}
//=================分界线=================
package com.itheima.demo3annotation;
@MyTest2(value = "hello",height = 165,address = {"北京","上海"})
public class Demo {
@MyTest2(value = "xxxx",height = 164,address = {"北京","西安"})
public void go(){
}
}
//=================分界线=================
package com.itheima.demo3annotation;
import org.junit.Test;
import java.util.Arrays;
public class AnnotationDemo3 {
@Test
//目标:解析注解
public void parseClass() throws Exception {
//1、获取Class对象
Class c = Demo.class;
//2、isAnnotationPresent判断是否有注解
if (c.isAnnotationPresent(MyTest2.class)) {
//3、获取注解对象
MyTest2 mt = (MyTest2) c.getDeclaredAnnotation(MyTest2.class);//获取某个注解对象
//4、获取注解的属性值
String[] address = mt.address();
double height = mt.height();
String value = mt.value();
//5、打印注解属性值
System.out.println(Arrays.toString( address));//内容是数组会显示内存地址,需用toString.而其他基本类型/字符串则会显示内容
System.out.println(height);
System.out.println(value);
}
}
}
注解的作用、应用场景

通过main方法模拟,让有自定义注解的执行,没有的就不执行
注解的属性的作用,可以告诉带注解的让他执行几次
main方法模拟@Test测试:

动态代理


java
package com.itheima.demo4proxy;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//目标:创建代理对象
//1、准备一个明星对象,设计明星类
Star star = new Star("圣采儿");
//2、为圣采儿创建一个专属于她的代理对象
StarService proxy = ProxyUtil.createProxy(star);
proxy.sing("《夜空中最亮的星》");//代理对象调用代理方法第三个参数执行
System.out.println(proxy.dance());
}
}
//=================分界线=================
package com.itheima.demo4proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工具类,中介公司,专门负责创建代理对象并返回给别人使用
*/
public class ProxyUtil {
public static StarService createProxy(Star s){
/**
* 参数一:用于指定哪个类加载器,去加载生成的代理类(固定代码)
* 参数二:指定代理类需要实现哪些接口(直接拿明星对象接口,她两实现接口一样)(固定代码)
* 参数三:指定代理类需要如何去代理(代理要做的事情)
*/
StarService proxy = (StarService)Proxy.newProxyInstance(Proxy.class.getClassLoader(),
s.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
//用来声明代理要做的事情
//用于声明代理对象要干的事情
//参数一:proxy接收到代理对象本身(暂时用处不大)
//参数二:method代表正在被代理的方法(sing或dance)
//参数三:args代表正在被代理的方法的参数(《夜空中最亮的星》或空)
String methodName = method.getName();
if ("sing".equals(methodName)){
System.out.println("开始代理,收钱20w");
}else if("dance".equals(methodName)){
System.out.println("开始代理,收钱100w");
}
//真正干活(调用明星对象方法)
Object result = method.invoke(s, args);
return result;
}
});
return proxy;
}
}
//=================分界线=================
package com.itheima.demo4proxy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Star implements StarService{
private String name;
@Override
public void sing(String name) {
System.out.println(this.name+"正在唱歌..."+ name);//this.name是对象名,name是局部变量歌名
}
@Override
public String dance() {
System.out.println(this.name+"正在跳舞..."+ name);
return "谢谢";
}
}
//=================分界线=================
package com.itheima.demo4proxy;
//明星行为接口
public interface StarService {
void sing(String name);
String dance();
}
动态代理解决实际问题

代码中需要代码同时实现业务和性能分析,这时可以使用动态代理把把性能分析交给代理来做,主代码只需要关注业务,使代码更简介