《从零开始的java从入门到入土的学习生活——JavaSE篇》Chapter11——Java基础加强篇学习记录——单元测试、反射、注解、代理
一、单元测试
1、定义
针对最小的功能单元:方法,编写测试代码对其进行正确性测试。
2、Junit单元测试框架
可以用来对方法进行测试,它是第三方公司开源出来的(很多开发工具已经集成了Junit框架,比如IDEA)
优点:可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立。不需要程序员去分析测试的结果,会自动生成测试报告出来。
使用步骤:

代码如下:
public class StringUnit {
//字符串工具类
public static void PrintNumber(String name) {
System.out.println("名字长度是:" + name.length());
}
//求字符串的最大索引
public static int PrintIndex(String data) {
if(data==null||"".equals(data)){
return -1;
}
return data.length()-1;
}
}
public class StringUnitTest {
//测试方法:必须是公开public,无参,无返回值
//必须加上@Test注解
@Test
public void testPrintNumber() {
StringUnit.PrintNumber("张三abc");
StringUnit.PrintNumber("");
//StringUnit.PrintNumber(null);
}
@Test
public void testPrintIndex() {
int index = StringUnit.PrintIndex("abcdefg");
int index2 = StringUnit.PrintIndex("");
int index3 = StringUnit.PrintIndex(null);
System.out.println(index);
System.out.println(index2);
System.out.println(index3);
//做断言,断言结果是否与预期结果正确
Assert.assertEquals("本轮测试失败,业务获取的1最大索引有问题,请检查",6, index);
Assert.assertEquals("本轮测试失败,业务获取的2最大索引有问题,请检查",-1, index2);
Assert.assertEquals("本轮测试失败,业务获取的3最大索引有问题,请检查",-1, index3);
}
}
二、反射
1、定义
反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)。

| 获取类的字节码:Class对象 |
| 获取类的构造器:Constructor对象 |
| 获取类的成员变量:Field对象 |
| 获取类的成员方法:Method对象 |
| ①Class c1 =类名.class |
| ②调用Class提供方法:public static Class forName(String package); |
| ③Object提供的方法:public Class getClass():Class c3 =对象.getClass(); |
代码如下:
public static void main(String[] args) throws Exception {
//1、获取类本身:类名.class
Class c1=Student.class;
System.out.println(c1);
//2、获取类本身:对象.getClass()
Student s1=new Student();
Class c2=s1.getClass();
System.out.println(c2);
//3、获取类本身:对象.forName()605575422@qq.com
Class c3=Class.forName("com.genshin.demo2Reflect.Student");
System.out.println(c3);
System.out.println(c1==c2);
System.out.println(c1==c3);
}
运行结果如下:

2、发射获取类中的成分并操作
先创建一个类:
public class Mygo {
private String name;
private int age;
private String hobby;
private Mygo() {
System.out.println("无参构造器");
}
private Mygo(String name) {
System.out.println("一个有参构造器");
this.name = name;
}
public Mygo(String name, int age) {
System.out.println("两个有参构造器");
this.name = name;
this.age = age;
}
public void playmusic(){
System.out.println("正在演奏春日影");
}
public String playmusic(String name){
System.out.println(name + "正在演奏春日影");
return "长崎素世:为什么要演奏春日影!!!";
}
/*getter 与 setter 方法 */
@Override
public String toString() {
return "Mygo{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
①Class提供了从类中获取构造器的方法

获取类构造器的作用:依然是初始化对象返回
代码如下:
@Test
public void getclassInfo(){
//一、获取类的信息
//1、反射第一步:获取Class对象,代表拿到类
Class c1=Mygo.class;
System.out.println(c1.getName());//类名的全类名 com.genshin.demo2Reflect.Mygo
System.out.println(c1.getSimpleName());//类名 Mygo
}
运行结果如下:

@Test
public void getConstructorInfo() throws Exception{
//二、获取构造器对象并对其进行操作
//1、反射第一步:获取Class对象
Class c1=Mygo.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()+")");
Constructor con2= c1.getDeclaredConstructor(String.class,int.class);//获取有参构造器
System.out.println(con2.getName()+"("+con2.getParameterCount()+")");
//4、获取构造器的作用依然是创建对象
//暴力反射:暴力反射可以访问私有的构造器、方法、属性
con.setAccessible(true);
Mygo m1= (Mygo) con.newInstance();
System.out.println(m1);
Mygo m2= (Mygo) con2.newInstance("丰川祥子",16);
System.out.println(m2);
}
运行结果如下:

②Class提供从类中获取成员变量的方法

获取到成员变量的作用:

方法代码如下:
@Test
//三、获取类的成员变量对象并对其进行操作
public void getFieldInfo() throws Exception{
//1、获取Class对象
Class c1=Mygo.class;
//2、获取成员变量对象
Field[] fields=c1.getDeclaredFields();
for (Field f:fields) {
System.out.println(f.getName()+"("+f.getType().getName()+")");
}
//3、获取单个成员变量对象
Field f=c1.getDeclaredField("hobby");
System.out.println(f.getName()+"("+f.getType().getName()+")");
//4、获取成员变量的目的依然是取值和赋值
Mygo m1=new Mygo("丰川祥子",16);
f.setAccessible(true);
f.set(m1,"弹钢琴");
System.out.println(m1);
String hobby= (String) f.get(m1);
System.out.println(hobby);
}
运行结果:

③Class提供了从类中获取成员方法的API

成员方法的作用:依然是执行

代码如下:
@Test
//四、获取类的成员方法对象并对其进行操作
public void getMethodInfo() throws Exception{
//1、获取Class对象
Class c1=Mygo.class;
//2、获取成员方法对象
Method[] methods=c1.getDeclaredMethods();
for (Method m:methods) {
System.out.println(m.getName()+"("+m.getParameterCount()+")");
}
//3、获取单个成员方法对象
Method playmusic1 = c1.getDeclaredMethod("playmusic");
Method playmusic2 = c1.getDeclaredMethod("playmusic", String.class);
System.out.println(playmusic1.getName()+"("+playmusic1.getParameterCount()+")");
System.out.println(playmusic2.getName()+"("+playmusic2.getParameterCount()+")");
//4、获取成员方法的目的依然是调用方法
Mygo m1=new Mygo("丰川祥子",16);
playmusic1.setAccessible(true);
Object rs1=playmusic1.invoke(m1);//唤醒对象m1的playmusic方法,相当于m1.playmusic()
System.out.println(rs1);//null
Object rs2=playmusic2.invoke(m1,"要乐奈");
System.out.println(rs2);
运行结果:

3、反射的基本作用
| ①可以得到类的全部成分并操作 |
| ②可以破坏封装性 |
| ③可以绕过泛型的约束 |
代码如下:
public static void main(String[] args) throws Exception {
List<String> list = new ArrayList<>();
list.add("Sakiko");
list.add("Anno");
list.add("Soyorin");
Class c = list.getClass();//c==ArrayList.class
//获取ArrayList的add方法
Method add=c.getDeclaredMethod("add", Object.class);
add.invoke(list, 9.9);//翻墙
add.invoke(list, true);//翻墙
System.out.println(list);
运行结果:

反射的用途:适合拿来做java的高级框架
三、注解
1、注解的定义
Java代码里的特殊标记,比如:@Override、@Test等,作用是:让其他程序根据注解信息来决定怎么执行该程序。可以用在类上、构造器上、方法上、成员变量上、参数上、等位置处。
2、自定义注解
pubilc @interface 注解名称{
pubilc 属性类型 属性名() default 默认值;
}
注意:对于特殊属性名-value,如果注解中只有一个value属性,使用注解时,value名称可以不写
3、注解的原理
注解本质是一个接口,Java中所有注解都是继承了Annotation接口的。@注解(...):其实就是一个实现类对象,实现了该注解以及Annotation接口。
代码如下:
public @interface A {
String value();
String hobby() default "弹吉他";
}
public @interface Mybook {
String name();
int age() default 18;
String[] address();
}
@Mybook(name="二见原莉莉子",age=18,address = {"霓虹","日本"})
@A("delete")
public class AnnotationDemo1 {
@Mybook(name="千早爱音",age=18,address = {"霓虹","日本"})
public static void main(@A("delete") String[] args) {
//自定义注解
@A("delete")
int a;
}
}
4、元注解
意思:注解 注解 的注解
| @Target | @Retention |
| 作用:声明注解只能在哪个位置使用 | 作用:声明注解的保留周期 |
| 1.TYPE,类,接口 |
1. SOURCE |
| 2. FIELD,成员变量 | |
| 3.METHOD,成员方法 |
2. CLASS(默认值) 保留到字节码文件阶段,运行阶段不存在。 |
| 4.PARAMETER,方法参数 | |
| 5. CONSTRUCTOR,构造器 |
3.RUNTIME(开发常用) 一直保留到运行阶段。 |
| 6.LOCAL_VARIABLE,局部变量 |
代码如下:
@Target(ElementType.METHOD) //表示注解的作用目标为方法
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest1 {
}
//@MyTest1//报错
public class AnotationDemo2 {
@MyTest1 //成员变量-报错
private int age;
@MyTest1 //构造器-报错
public AnotationDemo2() {}
@MyTest1
public static void main(String[] args) {}
@MyTest1
public void getagetest() {}
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
//注解的目标是方法
public @interface MyTest2 {
}
5、注解的解析
就是判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来。
如何解析注解?
指导思想:要解析谁上面的注解,就应该先拿到谁:比如要解析类上面的注解,则应该先获取该类的Class对象,再通过Class对象解析其上面的注解。要解析成员方法上的注解,则应该获取到该成员方法的Method对象,再通过Method对象解析其上面的注解。
Class、Method、Field,Constructor、都实现了AnnotatedElement接口,它们都拥有解析注解的能力。

代码如下:
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest2 {
String value();
double height() default 160.5;
String[] address();
}
@MyTest2(value = "丰川祥子",address = {"日本","霓虹"})
public class Demo {
@MyTest2(value = "长崎素世",address = {"日本","霓虹"})
public void go(){
}
}
@Test
public void parseClass() throws Exception {
//获取Class对象
Class c1=Demo.class;
//获取方法对象
Method m=c1.getMethod("go");
//判断类上是否有@MyTest2注解(使用isAnnotationPresent)
if(c1.isAnnotationPresent(MyTest2.class)){
//获取注解对象
MyTest2 mt= (MyTest2) c1.getDeclaredAnnotation(MyTest2.class);
//获取注解属性值
String[] address=mt.address();
double height=mt.height();
String value=mt.value();
//打印注解属性值
System.out.println(Arrays.toString(address));
System.out.println(height);
System.out.println(value);
}
}
}
运行结果:

四、代理
对象如果嫌身上干的事太多,可以通过代理来转移部分职责,对象有什么方法想被代理,代理就要有对应的方法。
public interface StarService {
void sing(String name);
String dance();
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Star implements StarService {
private String name;
@Override
public void sing(String name) {
System.out.println(this.name + "表演唱歌" + name);
}
@Override
public String dance() {
System.out.println(this.name + "表演跳舞");
return "";
}
}
创建代理对象:
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(ProxyUtil.class.getClassLoader(),
s.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理要做的事情
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;
}
}
public class Test {
public static void main(String[] args) {
//准备一个明星对象,创建明星类
Star star=new Star("羊宫妃娜");
//为其创建一个代理类
StarService proxy=ProxyUtil.createProxy(star);
proxy.sing("《春日影》");
System.out.println(proxy.dance());
}
}
运行结果如下:

更多推荐




所有评论(0)