Java基础--反射、注解、动态代理

目录

一、反射

1.1 获取类

1.2 获取构造器对象

1.2.1 获取所有构造器

1.2.2 获取单个构造器

1.2.3 获取有参数构造器

1.2.4 Constructor类中用于创建对象的方法

1.3 获取类的成员变量

1.4 获取类的成员方法

1.5 反射的作用

二、注解

2.1 自定义注解

2.2 注解的原理

2.3 元注解

2.3.1 @Target

2.3.2 @Retention

2.4 注解的解析

2.4.1 解析注解的方法

2.4.2 与注解解析相关的接口

2.4.3 示例

三、动态代理

3.1 创建代理对象

3.2 示例

3.3 代理的优缺点


一、反射

反射指的是允许以编程方式访问已加载类的成分(成员变量、方法、构造器等);

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对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值(如果没有就不写)

如果某成员方法是非 public 的,需要打开权限(暴力反射),然后再触发执行
setAccessible ( boolean )

示例:

            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 示例

需求:注解解析的案例。分析:

定义注解Mytest4,要求如下:
        - 包含属性: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();
}
定义Demo,在类和成员方法上使用Mytest4注解
@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 创建代理对象

Java 中代理的 代表类是: java.lang.reflect.Proxy ,它提供了一个静态方法,用于为被代理对象,产生一个代理对象返回。
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 代理的优缺点

  • 可以在不改变方法源码的情况下,实现对方法功能的增强,提高了代码的复用。
  • 简化了编程工作、提高了开发效率,同时提高了软件系统的可扩展性。
  • 可以为被代理对象的所有方法做代理。
  • 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值