高级特性
1.Junit
Junit单元测试框架:用于对方法进行测试
注意测试用例需要全面。并做断言测试:断言结果是否与预期结果一致。
2.反射
反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等),是底层框架的通用技术
常见示例:
- 获取类的信息,操作他们,获得类中的成员变量、方法、构造器等
- 1.加载类,获取类的字节码:class对象
- 2.获取类的构造器:Constructor对象
- 3.获取类的成员变量: Field对象
- 4.获取类的成员方法: Method对象
1.获取class对象
1.类名.class
Class c1 = 类名.class
2.调用静态方法:forName
public static Class forName(String package);
3.使用Object提供的方法
Class c3 = 对象.getClass();
package com.reflect.reflectDemo;
import lombok.Data;
public class reflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
//1.获取类本身: 类名.class
Class c1 = Animal.class;
//2.调用static方法:forName
Class c2 = Class.forName("com.reflect.reflectDemo.Animal");
//3.使用object提供的getclass方法
Animal a = new Animal();
Class c3 = a.getClass();
System.out.println(c1==c2); //true;
System.out.println(c2==c3); //true
}
}
@Data
class Animal {
private String name;
private int age;
}
2.获取各种成分
1.获取构造器
2.获取成员变量
3.获取成员方法
package com.reflect.reflectDemo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class reflectDemo2 {
public static void main(String[] args) throws Exception {
Class c1 = Student.class;
//获取构造器
Constructor[] cs = c1.getDeclaredConstructors(); //拿到全部构造器
for (Constructor c : cs) {
System.out.println(c.getName() + "(" + c.getParameterCount() + ")");
}
//获取无参构造器
Constructor con = c1.getDeclaredConstructor();
//利用构造器创建对象
con.setAccessible(true);//私有方法暴力攻破访问权限
Student s = (Student) con.newInstance();
System.out.println(s);
//获取两个参数的构造器
Constructor con1 = c1.getDeclaredConstructor(String.class, int.class);
Student s1 = (Student) con1.newInstance("小明", 12); //关系反转,先得到构造器,通过给构造器传入参数从而创建对象
System.out.println(s1);
//////////////////////////////////
//获取成员变量
Field f1 = c1.getDeclaredField("hobby");
System.out.println(f1.getName() + "(" + f1.getType().getSimpleName() + ")");
Field f2 = c1.getDeclaredField("age");
System.out.println(f2.getName() + "(" + f2.getType().getSimpleName() + ")");
//获取成员变量的目的依然是取值和赋值
Student s2 = new Student("小红", 23);
f1.setAccessible(true);
f1.set(s2,"写作业"); //赋值
System.out.println(s2);
String hobby = (String) f1.get(s2);
System.out.println(hobby);
//获取成员方法的目的依然是调用方法
Student s3 = new Student("小贝", 43);
Method m1 = c1.getDeclaredMethod("study");
System.out.println(m1.getName() + "(" + m1.getParameterCount() + ")");
m1.setAccessible(true);
Object res1 = m1.invoke(s3); //唤醒对象s3的study方法执行,相当于s3.study(); -> 小贝学习
System.out.println(res1); //res1表示方法的返回值
Method m2 = c1.getDeclaredMethod("study", String.class);
System.out.println(m2.getName() + "(" + m2.getParameterCount() + ")");
Object res2 = m2.invoke(s3, "小黄");
System.out.println(res2);
}
}
@Data
class Student {
@Getter
private String name;
private int age;
private String hobby;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
private Student() {}
public String study(String name) {
System.out.println(name + "在学习");
return "别打扰我学习";
}
private void study() {
System.out.println(this.name + "学习");
}
}
3.反射的作用
- 1.得到类的全部成分,然后操作
- 2.可以破坏封装性
- 3.可以绕过泛型的约束
通过获取class文件(运行时),此时的泛型已经被擦除了,不再受泛型的约束。
再通过反射,获取class里面的方法,通过方法去操作对象,从而到达翻墙(绕过泛型约束)的目的。
反射的含义:之前是通过new创建对象,再通过对象去获取构造器、成员变量、方法。现在可以通过先加载类.class,获取到构造器之后,通过获取的构造器去创建对象。获取成员变量之后,给对象赋属性值
简易框架
- 实现传入一个对象,将它的所有字段以及对应的值保存到文件中去
package com.reflect.reflectDemo;
import lombok.Data;
import lombok.Getter;
import java.io.*;
import java.lang.reflect.Field;
public class Frame {
public static void main(String[] args) throws Exception {
Student1 s = new Student1();
saveObject(s);
Animal1 a = new Animal1();
saveObject(a);
}
public static void saveObject(Object obj) throws Exception {
OutputStream os = new FileOutputStream("object.txt", true);
PrintStream ps = new PrintStream(os);
//obj 可能是老师类,学生类
//只有反射可以知道对象有多少个字段
//1.获取class对象
Class c = obj.getClass();
//2.获取Class对象的所有字段
Field[] fields = c.getDeclaredFields();
String simpleName = obj.getClass().getSimpleName();
ps.println("===============" + simpleName + "=============");
//遍历字段
for (Field field : fields) {
//获取字段的值
String fieldName = field.getName();
field.setAccessible(true); //暴力反射
Object fieldValue = field.get(obj) + "";
//打印到文件中
ps.println(fieldName + "=" + fieldValue);
System.out.println(fieldName + "=" + fieldValue);
}
}
}
@Data
class Animal1 {
private String name;
private int age;
}
@Data
class Student1 {
@Getter
private String name;
private int age;
private String hobby;
public Student1(String name, int age) {
this.name = name;
this.age = age;
}
public Student1() {}
public String study(String name) {
System.out.println(name + "在学习");
return "别打扰我学习";
}
private void study() {
System.out.println(this.name + "学习");
}
}
3.注解
**注解就是Java代码中的特殊标记,比如: @Override、@Test
- 作用是让其他程序根据注解信息来决定怎么执行程序
- 注意:注解可以用在类上、构造器、方法、成员变量、参数等位置
1.自定义注解
自己定义注解
2.注解原理
本质是一个接口,继承了Anotation类
3.元注解
注解注解的注解
@Target
@Retention
@Target
:被它标记的注解只能在指定的位置使用,可以使用逗号隔开声明多个位置
@Retention
:约束注解的存活范围,一般为运行时(一直都在),否则没有意义
4.注解的解析
- 判断类上、方法上、成员变量上是否存在注解,并把注解的内容解析出来。
5.注解的应用场景
使用注解开发出一个简易的Junit框架
动态代理
形象展示:
示例:
package com.itheima.proxyDemo;
import jdk.jfr.DataAmount;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class proxyDemo {
public static void main(String[] args) {
//目标:创建代理对象
//1.准备一个明星对象:设计明星类
Start start = new Start("章若楠");
//2.为明星对象创建代理
StartServer proxy = ProxyUtil.createProxy(start);
proxy.sing("罗生门");
System.out.println(proxy.dance());
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Start implements StartServer {
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 "谢谢谢谢";
}
}
class ProxyUtil {
//创建一个明星对象的代理对象返回
public static StartServer createProxy(Start start) {
/*
参数一:用于执行哪个类加载器去加载生成的代理类
参数二:用于指定代理类需要实现的接口:明星类实现的接口,代理类就需要实现
参数三:用于指定代理类需要如何去代理(代理需要做的事情)
*/
StartServer proxy = (StartServer) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
start.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("准备话筒,收钱10万!");
} else if ("dance".equals(methodName)) {
System.out.println("准备跳舞场地,收钱100万!");
}
//真正干活(把真正的明星对象叫过来正式干活)
//调用真正的明星对象来执行被代理的行为
Object result = method.invoke(start, args);
return result;
}
});
return proxy;
}
}
interface StartServer {
void sing(String name);
String dance();
}
解决实际问题
- 可以简化代理的对象的重复操作,代理对象只需要专心处理专门的任务即可
示例:比如要分析方法的性能,需要对每个方法的执行时间进行统计,在方法内部写性能分析代码会显得重复并且导致业务逻辑不清晰
那么可以将性能分析任务交给代理来做,使用代理测试每个方法的执行时间。
*上述内容均来自黑马程序员B站视频的学习笔记以及截图,仅为学习交流,不作为商业用途,如有侵权,联系删除。