java8-初识注解和反射

 

转载:https://www.cnblogs.com/opendragonhuang/p/11225026.html

一、注解

1、注解的定义

注解就是源代码的元数据,通熟的讲就是代码中的标签。注解就有如下的特点:

(1)注解是一个附属品,依赖于其他元素(包、类、方法、属性等等)存在。

(2)注解本身没有作用,在恰当的时候由外部程序进行解析才会发生作用。

2、注解的分类

(1)按来源分

  • JDK 自带注解,例如:@Override, @Deprecated, @SuppressWornings 。
  • 第三方注解。
  • 自定义注解。

(2)按生命周期划分

  • SOURCE:只存在于源代码中,编译成 class 文件就不存在了。
  • Class:存在于源代码中和 class 文件中。
  • RUNTIME:注解保留到运行时。

3、元注解

元注解指的是用于修饰注解的注解,包括如下几个:

(1)@Retention:指明 Annotation 的生命周期,传入的值是一个枚举类型,可选值为

  • RetentionPolicy.SOURCE
  • RetentionPolicy.CLASS
  • RetentionPolicy.RUNTIME

(2)@Target:指明 Annotation 可以修饰程序哪些元素,传入的值为ElemetType[] 类型,值可为:

  • ElementType.CONSTRUCTOR :构造器
  • ElementType.FIELD:属性
  • ElementType.LOCAL_VARIABLE:局部变量
  • ElementType.METHOD:方法
  • ElementType.PACKAGE:包
  • ElementType.PARAMETER:参数
  • ElementType.TYPE:类、接口(包括注解类型和 enum 声明)

(3)@Documented:使用此修饰的注解将会被 javadoc 工具提取成文档,使用此注解,其 @Retention 必须被设置为 RetentionPolicy.RUNTIME 。

(4)@Inherited:具有继承性。

4、自定义注解

自定义注解需要注意的问题:

(1)使用 @interface 关键字定义。

(2)自动继承 java.lang.annotation.Annotation 接口。

(3)配置参数的类型只能是八大基本类型、String、Class、enum、Annotation 和对应的数组类型。

(4)配置参数声明的语法格式如下([] 表示可省略):

(5)类型 变量名() [default 默认值];

(6)如果只有一个配置参数,其参数名必须为 value。

(7)如果定义的注解含有配置参数,那在使用注解时,必须指定参数值,指定形式为:“参数名=参数值”。如果只有一个参数,直接写参数值即可,定义中指定了默认值的参数可以不指定值,但没有的一定要指定值

(8)没有成员的注解为标记,包含成员的称为元数据

二、反射

1、什么是反射

  反射指的是程序在运行期间借助反射 API 取得任何类的内部信息,并通过这些内部信息去操作对应对象的内部属性和方法。

  任何一个类,在第一次使用时,就会被 JVM 加载到堆内存的方法区中。JVM 加载类成功后,就会在方法区中产生一个对应的 Class 对象(一个类只要一个 Class 对象),这个 Class 对象包含被加载类的全部结构信息。

2、获取class对象的常用方式

(1)类的属性

  每一个类都有一个class静态属性,这个class静态属性就是类对应的class对象

  Class<Person> cl1 = Person.class;

(2)Object对象的getClass()方法

  Person p1 = new Person();
  Class<Person> class = (Class<Person>) p1.getClass();

(3)通过forName()方法获取class对象(常用)

  try {
       Class class = Class.forName("com.adf.ccxx.Person");
  } catch (ClassNotFoundException e) {
       e.printStackTrace();
  }

(4)通过ClassLoader类(不常用)

  ClassLoader cl = Person.class.getClassLoader();
  try {
      Class class = cl.loadClass("com.adf.ccxx.Person");
  } catch (ClassNotFoundException e) {
      e.printStackTrace();
  }

3、反射的基本使用

反射的基本使用包括创建对象,设置属性和调用方法。Class 对象中大多数 get 方法有 Declared 和无 Declared,他们的区别是:

(1)方法名中有Declared:获取到当前类所有的(含有 private),但不包括其父类。

(2)方法命中没有Declared:只能获取到 public 修饰的,包括当前类和所有父类。

基本用法: 

//Person

public class Person {
    private String name;
    private int age;
    public String habbit;
​
    public Person() {
    }
​
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    private Person(String name, int age, String habbit) {
        this.name = name;
        this.age = age;
        this.habbit = habbit;
    }
​
    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;
    }
​
    public String getHabbit() {
        return habbit;
    }
​
    public void setHabbit(String habbit) {
        this.habbit = habbit;
    }
​
    private String say(String str){
        String str1 = name+"说:"+str;
        System.out.println(str1);
        return str1;
    }
​
    public void eat(String food){
        System.out.println(name+"吃"+food);
    }
​
    @Override
    public String toString() {
        return "["+name+","+age+","+habbit+"]";
    }
}
//测试

public class PersonTest {
    @Test
    public void ReflexTest() throws Exception{
        System.out.println("---------- new 方式创建对象 ----------");
        // 普通方式创建 Person 对象
        Person p1 = new Person("adf", 22);
​
        // 直接设置属性
        p1.habbit = "编程";
        // 调用方法
        System.out.println(p1);
​
        //无法直接设置私有属性和调用私有方法
        //p1.name = "adf";
        //p1.say(""Hello);
//反射方式创建对象
        System.out.println("---------- 反射方式创建对象 ----------");
        Class<Person> clazz = Person.class;
​
        // 调用无参构造器参加对象
        Constructor<Person> constructor1 = clazz.getConstructor();
        Person p2 = constructor1.newInstance();
        System.out.println(p2);
​
        // 通过有参构造器
        Constructor<Person> constructor2 = clazz.getConstructor(String.class, int.class);
        Person p3 = constructor2.newInstance("adf", 22);
        System.out.println(p3);
​
        // 通过私有的构造器
        Constructor<Person> constructor3 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
        constructor3.setAccessible(true);
        Person p4 = constructor3.newInstance("adf", 22, "编程");
        System.out.println(p4);
​
        //通过反射设置公有属性
        Field personFeildHabbit = clazz.getDeclaredField("habbit");
        personFeildHabbit.set(p2, "编程");
​
        //通过反射设置私有属性
        Field personFeildAge = clazz.getDeclaredField("age");
        personFeildAge.setAccessible(true);
        personFeildAge.set(p2, 18);
​
        //通过反射调用方法
        Method personMethodToString = clazz.getDeclaredMethod("toString");
        // 方法的返回值为调用方法的返回值
        String str = (String)personMethodToString.invoke(p2);
        System.out.println(str);
​
        // 通过反射调用私有方法
        Method personMethodSay = clazz.getDeclaredMethod("say", String.class);
        personMethodSay.setAccessible(true);
        String str2 = (String)personMethodSay.invoke(p2, "Hello");
        System.out.println(str2);
    }
}

4、设计模式:代理模式

使用一个代理对象将原始对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理对象,代理对象决定是否以及何时将方法调用转到原始对象上。

(1)静态代理 

代理类和原始对象在编译期间就确定下来了,不利于程序的扩展,且每一个代理只能为一个接口服务,这就会在开发的过程中产生多大的代理类。

静态代理的实现:

  • 代理类和原始对象实现相同的接口。
  • 代理类保持原始对象的引用。
  • 代理类里调用原始对象的方法。
//ClothFactory​
public interface ClothFactory {
    String producer();
}
//ClothFactoryProxy 
public class ClothFactoryProxy implements ClothFactory{
    private ClothFactory clothFactory;
​
    public ClothFactoryProxy(ClothFactory clothFactory) {
        this.clothFactory = clothFactory;
    }
​
    @Override
    public String producer() {
        System.out.println("代理对象做一些准备");
        clothFactory.producer();
        System.out.println("代理对象做一些收尾工作");
​
        return null;
    }
}
//NikeFactory 
public class NikeFactory implements ClothFactory{
​
    public NikeFactory() {
    }
​
    @Override
    public String producer() {
        System.out.println("Nike 正在生产衣服");
        return null;
    }
}
//测试

@Test
public void staticProxyTest(){
    // 创建被代理对象
    NikeFactory nikeFactory = new NikeFactory();
​
    // 创建代理对象
    ClothFactoryProxy clothFactoryProxy = new ClothFactoryProxy(nikeFactory);
​
    // 通过代理对象调用被代理对象的方法
    clothFactoryProxy.producer();
}

(2)动态代理模式

动态代理是通过 Java 的反射机制实现的。通过动态代理,只需一个代理对象就可以代理所有的对象。

//Humen

public interface Human {
     String belief();
     void eat(String food);
}
//SuperMan
public class SuperMan implements Human {
    @Override
    public String belief() {
        return "我相信,我能行!";
    }
​
    @Override
    public void eat(String food) {
        System.out.println("正在吃"+food);
    }
}
//DynamicProxy 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
​
​
public class DynamicProxy {
    // 获取代理对象
    public static Object getInstance(Object obj){
        MyInvocation h = new MyInvocation();
        h.bind(obj);
        // 动态的创建对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), h);
    }
}
​
class MyInvocation implements InvocationHandler{
    private Object obj;
​
    /**
     * 绑定被代理对象
     * @param obj
     */
    public void bind(Object obj){
        this.obj = obj;
    }
​
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 代理对象调用被代理对象的方法
        Object ret = method.invoke(obj, args);
        return ret;
    }
}
//测试

@Test
public void dynamicProxyTest(){
    SuperMan sm = new SuperMan();
    Human h = (Human)DynamicProxy.getInstance(sm);
    System.out.println(h.belief());;
    h.eat("麻辣烫");
​
    NikeFactory nikeFactory = new NikeFactory();
    ClothFactory clothFactory = (ClothFactory) DynamicProxy.getInstance(nikeFactory);
    clothFactory.producer();
}

三、使用注解与反射小练习

//自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DriveAccess {
    public boolean canDrive() default false;
}
//实体类

public class Tesla extends Car implements Runnable {

    public Tesla(int velocity) {
        this.velocity = velocity;
    }

    @Override
    public String getName() {
        return "Tesla";
    }

    @Override
    @DriveAccess(canDrive = true)
    public void run() {
        String simpleName = this.getClass().getSimpleName();
        System.out.println(simpleName + " run " + velocity + "km/h");
    }
}
public class CarFactory {
    public static void test(String car) throws Exception {
        //获取class对象
        Class<?> name = Class.forName(car);
        //获取class对象中的方法
        for (Method method : name.getDeclaredMethods()) {
            //判断方法是否被DriveAccess注解标记
            if (method.isAnnotationPresent(DriveAccess.class)) {
                //返回方法上被标记的DriveAccess注解
                DriveAccess annotation = method.getAnnotation(DriveAccess.class);
                //获取方法的全名
                String methodName  = method.toGenericString();
                if (annotation.canDrive()) {
                    System.out.println(methodName  + " method can be accessed... ");
                    //获取类实例对象
                    Object c = name.getDeclaredConstructor(int.class).newInstance(100);
                    //调用方法
                    method.invoke(c);
                } else {
                    System.out.println(methodName  + " method can not be accessed... ");
                }
            } else {
                System.out.println(method.toGenericString() + " don't have DriveAccess Annotation...");
            }
        }
    }
}

 

posted @ 2020-12-29 17:04  ADF_CcXx  阅读(82)  评论(0)    收藏  举报