目录
一、反射
反射指的是允许以编程方式访问已加载类的成分(成员变量、方法、构造器等);
1.1 获取类
反射的第一步都是先得到加载后的类,然后才可以去那类的其他成分。
方式一:Class c2 = 类名.class
方式二:Class c1 = Class.forName(“全类名”);
方式三:Class c3 = 对象.getClass();
示例:创建一个Student类
public class Test1 {
public static void main(String[] args) throws Exception {
//1.方式1
Class c1 = Student.class;
System.out.println(c1.getName());//全类名
System.out.println(c1.getSimpleName());//简名:Student
//2.forName("全类名")
Class c2 = Class.forName("reflect.Student");
System.out.println(c1 == c2);
//3.对象.getClass
Student s = new Student();
Class c3 = s.getClass();
System.out.println(c3 == c2);
}
}
1.2 获取构造器对象
- 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
- Class类中用于获取构造器的方法
方法 | 说明 |
Constructor<?>[] getConstructors() | 返回所有构造器对象的数组(只能拿public的) |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造器对象的数组,存在就能拿到 |
Constructor<T> getConstructor(Class<?>... parameterTypes) | 返回单个构造器对象(只能拿public的) |
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 返回单个构造器对象,存在就能拿到 |
1.2.1 获取所有构造器
示例:
@Test
public void testGetConstructors(){
//1.先得到这个类的Class对象
Class c = Student.class;
//2.获取类的全部构造器
Constructor[] constructors = c.getConstructors();
//3.遍历数组中的每个构造器对象
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() +"---->" +
constructor.getParameterCount());
}
}
1.2.2 获取单个构造器
示例:
@Test
public void testGetConstuctor() throws Exception {
Class c = Student.class;
//2.获取某个构造器,例如无参构造器,只能public的
//Constructor constructor = c.getConstructor();
//System.out.println(constructor.getName()+"---->" +
constructor.getParameterCount());
Constructor constructor = c.getDeclaredConstructor();
System.out.println(constructor.getName()+"---->" +
constructor.getParameterCount());
}
1.2.3 获取有参数构造器
示例:
@Test
public void testGetConstuctor() throws Exception {
Class c = Student.class;
//3.获取有参数构造器
Constructor constructor = c.getDeclaredConstructor(String.class,int.class);
System.out.println(constructor.getName() +"---->" +
constructor.getParameterCount());
}
获取构造器的作用依然是初始化一个对象返回。
1.2.4 Constructor类中用于创建对象的方法
符号 | 说明 |
T newInstance(Object... initargs) | 根据指定的构造器创建对象 |
public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
示例:访问私有无参构造器
@Test
public void testGetConstuctor() throws Exception {
Class c = Student.class;
Constructor constructor1 = c.getDeclaredConstructor();
System.out.println(constructor1.getName()+"---->" +
constructor1.getParameterCount());
constructor1.setAccessible(true);//禁止检查访问权限
Student s =(Student) constructor1.newInstance();
System.out.println(s);
}
示例:访问有参构造器
//3.获取有参数构造器
Constructor constructor2 = c.getDeclaredConstructor(String.class,int.class);
System.out.println(constructor2.getName() +"---->" +
constructor2.getParameterCount());
constructor2.setAccessible(true);//禁止检查访问权限
Student s2 = (Student) constructor2.newInstance("hhh",3);
System.out.println(s2);
1.3 获取类的成员变量
使用反射技术获取成员变量对象并使用
- 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
- Class类中用于获取成员变量的方法
方法 | 说明 |
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
示例:
@Test
public void testGetFields() throws Exception {
//1.反射第一步,先得到类的Class对象
Class c = Student.class;
//2.获取类的全部成员变量
Field[] fields = c.getDeclaredFields();
//3.变量成员变量数组
for (Field field : fields) {
System.out.println(field.getName() +" -----> "+ field.getType());
}
//4.定位某个成员变量
Field fName = c.getDeclaredField("name");
System.out.println(fName.getName() + " ===> " +fName.getType());
Field fAge = c.getDeclaredField("age");
System.out.println(fAge.getName() +" ===> "+fAge.getType());
}
符号 | 说明 |
void set(Object obj, Object value): | 赋值 |
Object get(Object obj) | 获取值。 |
示例:
//赋值
Student student = new Student();
fName.setAccessible(true);
fName.set(student,"hhh");
System.out.println(student);
//取值
String name = (String) fName.get(student);
System.out.println(name);
1.4 获取类的成员方法
使用反射技术获取方法对象并使用
- 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
- Class类中用于获取成员方法的方法
方法 | 说明 |
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
示例:
@Test
public void testGetMethod() throws Exception {
//1.得到Class对象
Class c = Student.class;
//2.获取类的全部成员方法
Method[] methods = c.getDeclaredMethods();
//3.遍历这个数组中的每个方法对象
for (Method method : methods) {
System.out.println(method.getName() +"---->"
+ method.getParameterCount() + "---->"
+ method.getReturnType());
}
//4.获取某个方法对象
Method run = c.getDeclaredMethod("run");
System.out.println(run.getName() +"---->"
+ run.getParameterCount() + "---->"
+ run.getReturnType());
Method eat = c.getDeclaredMethod("eat",String.class);
System.out.println(eat.getName() +"---->"
+ eat.getParameterCount() + "---->"
+ eat.getReturnType());
}
Method类中用于触发执行的方法
符号 | 说明 |
Object invoke(Object obj, Object... args) | 运行方法 参数一:用obj对象调用该方法 参数二:调用方法的传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写) |
示例:
Student student = new Student();
run.setAccessible(true);
run.invoke(student);
eat.setAccessible(true);
System.out.println(eat.invoke(student, "ice"));
1.5 反射的作用
- 可以在运行时得到一个类的全部成分然后操作。
- 可以破坏封装性。(很突出)
- 也可以破坏泛型的约束性。(很突出)
- 更重要的用途是适合:做Java高级框架
- 基本上主流框架都会基于反射设计一些通用技术功能。
示例:给你任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件中去。
二、注解
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。
public class UserServiceTest {
@Test//注解
public void testLogin(){ }
@Test//注解
public void testChu(){ }
}
2.1 自定义注解
public @interface 注解名称 {
public 属性类型 属性名() default 默认值 ;
}
示例:
- value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写!!
- 但是如果有多个属性, 且多个属性没有默认值,那么value名称是不能省略的。
public @interface Mytest2 {
String value();//特殊属性
}
@Mytest(aaa = "牛魔王",ccc = {"html","css"})
//@Mytest2(value = "孙悟空")
@Mytest2("孙悟空")
public class AnnotationTest {
@Mytest(aaa = "红孩儿",bbb = false,ccc={"web","java"})
public void test1(){
}
public static void main(String[] args) {
}
}
2.2 注解的原理
public @interface Mytest {
String aaa();
boolean bbb() default true;
String[] ccc();
}
本质上是
public interface MyTest1 extends Annotation{
public abstract String aaa();
public abstract boolean bbb();
public abstract String[] ccc();
}
- 注解本质上是一个接口,Java中所有注解都是继承了Annotation接口的;
- @注解():其实就是一个实现类对象,实现了该注解以及Annotation接口。
2.3 元注解
指的是:修饰注解的注解
元注解有两个:
@Target: 约束自定义注解只能在哪些地方使用,
@Retention:申明注解的生命周期
例如:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Test{
}
2.3.1 @Target
作用:声明被修饰的注解只能在哪些位置使用;
@Target中可使用的值定义在ElementType枚举类中,常用值如下
- TYPE,类,接口
- FIELD, 成员变量
- METHOD, 成员方法
- PARAMETER, 方法参数
- CONSTRUCTOR, 构造器
- LOCAL_VARIABLE, 局部变量
示例:
@Target({ElementType.TYPE,ElementType.METHOD})//当前注解可以用在类上和方法上
public @interface Mytest3 {
}
2.3.2 @Retention
作用:声明注解的保留周期
@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下
- SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在
- CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.
- RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
示例:
@Target({ElementType.TYPE,ElementType.METHOD})//当前注解可以用在类上和方法上
@Retention(RetentionPolicy.RUNTIME)// 控制下面的注解一直保留到运行时
public @interface Mytest3 {
}
2.4 注解的解析
注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
2.4.1 解析注解的方法
- 注解在哪个成分上,我们就先拿哪个成分对象。
- 比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解
- 比如注解作用在类上,则要该类的Class对象,再来拿上面的注解
- 比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解
- 所有的类成分Class, Method , Field , Constructor,都实现了AnnotatedElement接口他们都拥有解析注解的能力:
2.4.2 与注解解析相关的接口
- Annotation: 注解的顶级接口,注解都是Annotation类型的对象
- AnnotatedElement:该接口定义了与注解解析相关的解析方法
方法 | 说明 |
Annotation[] getDeclaredAnnotations() | 获得当前对象上使用的所有注解,返回注解数组。 |
T getDeclaredAnnotation(Class<T> annotationClass) | 根据注解类型获得对应注解对象 |
boolean isAnnotationPresent(Class<Annotation> annotationClass) | 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false |
2.4.3 示例
需求:注解解析的案例。分析:
- 包含属性:String value()
- 包含属性:double aaa() 价格,默认值为 100
- 包含属性:String[] bbb()
- 限制注解使用的位置:类和成员方法上
- 指定注解的有效范围:RUNTIME
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Mytest4 {
String value();
double aaa() default 100;
String[] bbb();
}
@Mytest4(value = "web",aaa = 99.5,bbb = {"html","css"})
public class Demo {
@Mytest4(value = "ml",aaa = 99.5,bbb = {"cv","nlp"})
public void test1(){
}
}
③定义AnnotationDemo01测试类获取Mytest4注解上的数据
类上的注解:
@Test
public void parseClass(){
//1.先得到Class对象
Class c = Demo.class;
//2.解析类上的注解
//判断类上是否包含某个注解
if(c.isAnnotationPresent(Mytest4.class)){
Mytest4 mytest4 = (Mytest4) c.getDeclaredAnnotation(Mytest4.class);
System.out.println(mytest4.value());
System.out.println(mytest4.aaa());
System.out.println(Arrays.toString(mytest4.bbb()));
}
}
方法上的注解:
@Test
public void parseMethod() throws Exception {
Class c = Demo.class;
Method m = c.getDeclaredMethod("test1");
if(m.isAnnotationPresent(Mytest4.class)){
Mytest4 mytest4 = (Mytest4) m.getDeclaredAnnotation(Mytest4.class);
System.out.println(mytest4.value());
System.out.println(mytest4.aaa());
System.out.println(Arrays.toString(mytest4.bbb()));
}
}
三、动态代理
代理思想就是被代理者没有能力,或者不愿意去完成某件事情,需要找个人(代理)代替自己去完成这件事。
动态代理主要是对被代理对象的行为进行代理。
3.1 创建代理对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
为被代理对象返回一个代理对象。
参数一:类加载器 加载代理类,产生代理对象。,。
参数二:真实业务对象的接口。(被代理的方法交给代理对象)
参数三:代理的核心处理程序
3.2 示例
public interface Star {
String sing(String name);
void dance();
}
public class BigStar implements Star{
private String name;
public BigStar(String name){
this.name = name;
}
public String sing(String name){
System.out.println(this.name + " 正在唱 " + name);
return "thanks";
}
public void dance(){
System.out.println(this.name + "正在优美的跳舞");
}
}
public class ProxyUtil {
public static Star createProxy(BigStar bigStar){
/*
* public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
* 参数1:用于指定一个类加载器
* 参数2:指定生成的代理长什么样,也就是有哪些方法
* 参数3:指定生成的代理对象要干什么事情
* */
// Star starProxy = ProxyUtil.createProxy(s)
// starProxy.sing();starProxy.dance();
Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
new Class[]{Star.class}, new InvocationHandler() {
@Override//回调方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理对象要做的事情
if (method.getName().equals("sing")) {
System.out.println("准备话筒~~");
//return method.invoke(bigStar, args);
} else if (method.getName().equals("dance")) {
System.out.println("准备跳舞,收钱");
//method.invoke(bigStar, args);
}
return method.invoke(bigStar, args);
}
});
return starProxy;
}
}
测试:
public class Test {
public static void main(String[] args) {
BigStar s = new BigStar("hhh");
Star starProxy = ProxyUtil.createProxy(s);
String rs = starProxy.sing("goast");
System.out.println(rs);
}
}
运行结果:
代理执行过程:
3.3 代理的优缺点
- 可以在不改变方法源码的情况下,实现对方法功能的增强,提高了代码的复用。
- 简化了编程工作、提高了开发效率,同时提高了软件系统的可扩展性。
- 可以为被代理对象的所有方法做代理。
- 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理。