欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

:2.反射、范型、注解,注解

来源: javaer 分享于  点击 39399 次 点评:242

:2.反射、范型、注解,注解


1.反射
反射(Reflection)能够让运行于 JVM 中的程序检测和修改运行时的行为
类加载器的基本概念  类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java )在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class )。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。 Java反射机制定义Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。为什么需要反射?通过反射,我们能够
  • 在运行时检测对象的类型;
  • 动态构造某个类的对象;
  • 检测类的属性和方法;
  • 任意调用对象的方法;
  • 修改构造函数、方法、属性的可见性。
反射的基础--Class类用来描述Java类的类就是Class这个类。
每个类在java虚拟机中占用一片内存空间,里面的内容就是对应这个类的字节码(Class)
Class字节码的获取:
类名.class
对象.getClass
Class.forName("类的全名");

其中,跟反射密切相关的就是forName这个方法,通过类名去获取字节码。前面两种都是虚拟机中已经加载过了。forName方法在当虚拟机中没有加载过对应字节码的时候,就会去动态地加载进来;当已经加载过的时候,直接复用加载过的。
九种预定义好的基本Class字节码
八种数据类型,外加void也有对应的字节码。下面给出一些例子:
Class<Integer> type = Integer.TYPE;
Class<Integer> integerClass = int.class;
Class<Void> voidClass = Void.class;

反射就是把Java类中的各种成分通过java的反射API映射成相应的Java类,得到这些类以后就可以对其进行使用。比如方法,构造方法,成员变量,类型,包等。
创建对象String s = "java.util.Date";
Object m = Class.forName(s).newInstance();
反射的基本方法
在java.lang.reflect包中有三个重要的类:
  • Field:描述类的域(成员变量)

  • Method:描述类的方法(方法)

  • Constructor:描述类的构造器(构造器)
对于public域(包括超类成员):

  • getFields

  • getMethods

  • getConstructors


对于其它域(包括私有和受保护的成员,不包括超类成员):

  • gettDeclaredFields

  • gettDeclaredMethods

  • gettDeclaredConstructors

实例见demo,以Person类为例@MyAnotation(value = "class")public class Person {    public String name = "kson";    private int age = 100;
    @MyAnotation(value = "value",hi = "hhhh")    private Person(String name) {        this.name = name;        System.out.println("privateName:"+name);    }

    public Person(){        System.out.println("name"+name);    }    @MyAnotation(value = "method value",hi = "methodhi")    public void fun(){        System.out.println("fun");    }
    public void fun(String name){
        System.out.println(name);
    }} //加载类的三种方式        Class c1 = Person.class;        Class c2 = new Person().getClass();        Class c3 = Class.forName("com.kson.reflectandpatternandannotation.Person");
        //获取无参构造函数,并实例化类        Constructor constructor = c3.getConstructor(null);        Person p = (Person) constructor.newInstance(null);        p.fun();
        //获取含参数私有构造函数,并实例化//        Constructor     constructor1 = c3.getDeclaredConstructor(String.class);        Constructor     constructor1 = c3.getDeclaredConstructor(new Class[]{String.class});        constructor1.setAccessible(true);        Person p1 = (Person) constructor1.newInstance("hello");//        Person p1 = (Person) constructor1.newInstance(new Object[]{"kson"});        p1.fun();
        //获取并调用类的无参方法        Method method1 = c3.getMethod("fun",null);        method1.invoke(c3.newInstance(),null);
        //获取并调用类的含参方法        Method method2 = c3.getMethod("fun",String.class);        method2.invoke(c3.newInstance(),"hhhhhh");
        //获取类的字段        Field field = c3.getField("name");        Object o = c3.newInstance();        field.set(o,"hahahah");
        Class type = field.getType();        System.out.println("type:"+type);        System.out.println(field.get(o));
        Field field1 = c3.getDeclaredField("age");        field1.setAccessible(true);        field1.set(o,99);        System.out.println(field1.get(o));
2.注解从   jdk5开始,Java增加了对元数据的支持,也就是Annotation,Annotation其实就是对代码的一种特殊标记,这些标记可以在编译,类加载和运行时被读取,并执行相应的处理。当然刚刚说了,Annotation只是一种标记,所以要是在代码里面不用这些标记也是能完成相应的工作的,只是有时候用注解能简化很多代码,看起来非常的简洁。
基本的Annotation
  • @Override——限定重写父类方法
  • @Deprecated——标示已过时
  • @SuppressWarning——抑制编译器警告
  • @SafeVarargs——这货与Java7里面的堆污染有关,具体想了解的,传送到这里
JDK的元Annotation,元注解
JDK除了提供上述的几种基本的Annotation外,还提供了几种元注解Annotation,用于修饰其他的Annotation定义

@Retention 这个是决定你Annotation存活的时间的,它包含一个RetationPolicy的value成员变量,用于指定它所修饰的Annotation保留时间,一般有:
    • Retationpolicy.CLASS:编译器将把Annotation记录在Class文件中,不过当java程序执行的时候,JVM将抛弃它。

    • Retationpolicy.SOURCE : Annotation只保留在原代码中,当编译器编译的时候就会抛弃它。

    • Retationpolicy.RUNTIME : 在Retationpolicy.CLASS的基础上,JVM执行的时候也不会抛弃它,所以我们一般在程序中可以通过反射来获得这个注解,然后进行处理。

@Target 这个注解一般用来指定被修饰的Annotation修饰哪些元素,这个注解也包含一个value变量:
    • ElementType.ANNOTATION_TYPE : 指定该Annotation只能修饰Annotation。
    • ElementType.CONSTRUCTOR: 指定只能修饰构造器。

    • ElementType.FIELD: 指定只能成员变量。

    • ElementType.LOCAL_VARIABLE: 指定只能修饰局部变量。

    • ElementType.METHOD: 指定只能修饰方法。

    • ElementType.PACKAGE: 指定只能修饰包定义。

    • ElementType.PARAMETER: 指定只能修饰参数。

    • ElementType.TYPE: 指定可以修饰类,接口,枚举定义。



  1. @Document 这个注解修饰的Annotation类可以被javadoc工具提取成文档

  1. @Inherited 被他修饰的Annotation具有继承性
自定义Anotation
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD,ElementType.TYPE,ElementType.CONSTRUCTOR})@Inherited@Documentedpublic @interface MyAnotation {    String value();    String hi() default "hello";}
声明:@MyAnotation(value = "class")public class Person {    public String name = "kson";    private int age = 100;
    @MyAnotation(value = "value",hi = "hhhh")    private Person(String name) {        this.name = name;        System.out.println("privateName:"+name);    }

    public Person(){        System.out.println("name"+name);    }    @MyAnotation(value = "method value",hi = "methodhi")    public void fun(){        System.out.println("fun");    }
    public void fun(String name){
        System.out.println(name);
    }}
实例://类注解Class c = Class.forName("com.kson.reflectandpatternandannotation.Person");MyAnotation myAnotation = (MyAnotation) c.getAnnotation(MyAnotation.class);System.out.println(myAnotation.value());System.out.println(myAnotation.hi());
//私有构造函数注解Constructor constructor = c.getDeclaredConstructor(String.class);constructor.setAccessible(true);MyAnotation myAnotation1 = (MyAnotation) constructor.getAnnotation(MyAnotation.class);System.out.println("constructor:"+myAnotation1.hi());
//public方法注解Method method = c.getMethod("fun",null);MyAnotation myAnotation2 = method.getAnnotation(MyAnotation.class);System.out.println(myAnotation2.hi());
应用场景:实现简单的依赖注解框架@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface ContentView {    int value();}
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface ViewInject {    int value();}
public class ViewInjectUtils {

    public static void inject(Activity activity) {        injectContentView(activity);        injectView(activity);    }
    private static void injectView(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if (viewInject != null) {                int id = viewInject.value();                View view = activity.findViewById(id);                try {                    field.setAccessible(true);                    field.set(activity, view);                } catch (IllegalAccessException e) {                    e.printStackTrace();                }            }
        }
    }
    private static void injectContentView(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();        ContentView contentView = clazz.getAnnotation(ContentView.class);
        if (contentView != null) {            int layoutId = contentView.value();
            try {                Method method = clazz.getMethod("setContentView", int.class);                method.invoke(activity, layoutId);            } catch (Exception e) {                e.printStackTrace();            }        }

    }}

EventInject@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
   
int[] value();
}
public class ViewInjectUtils {
    public static void inject(Activity activity) {
        injectContentView(activity);
        injectView(activity);
        injectEvent(activity);
    }

    private static void injectEvent(final Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        for (final Method method2 : methods) {
            OnClick click = method2.getAnnotation(OnClick.class);
            if (click != null) {

                int[] viewId = click.value();
                method2.setAccessible(true);
                Object listener = Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader(),
                        new Class[]{View.OnClickListener.class}, new InvocationHandler() {
                            @Override                             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                return method2.invoke(activity, args);
                            }
                        });

                try {
                    for (int id : viewId) {
                        View v = activity.findViewById(id);
                        Method setClickListener = v.getClass().getMethod("setOnClickListener", View.OnClickListener.class);
                        setClickListener.invoke(v, listener);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }范型:
1. 认识泛型
  • 泛型是在JDK1.5之后增加的新功能.
  • 泛型可以解决数据的安全性问题, 主要的原理是在类声明的时候通过一个标识表示类中某个属性的类型或者是某个方法的返回值参数类型.
  • 格式:
访问权限 class 类名称<泛型, 泛型...>{
    属性
    方法
}
  • 对象的创建:
类名称<具体类型> 对象名称 = new 类名称<具体类型>();
  • 示例
/**
* 经纬度
*
* @author dixin
*
*/
class Point<T,A,B,C,D,NININI,HELLO> {
    private T x;
    private T y;

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public T getY() {
        return y;
    }

    public void setY(T y) {
        this.y = y;
    }
}

public class GenericDemo01 {

    public static void main(String[] args) {

        Point<String> p1 = new Point<String>();
        p1.setX("经度为10");
        p1.setY("纬度为100");

        Point<Integer> p2 = new Point<Integer>();
        p2.setX(10);
        p2.setY(100);

        System.out.println(p1.getX() + ", " + p1.getY());
        System.out.println(p2.getX() + ", " + p2.getY());
    }
}

// 执行结果
经度为10, 纬度为100
10, 100
2. 构造方法中使用泛型class Con<T> {

    private T value;
    // 类定义中已经定义泛型T, 方法中可以直接使用, 不用加<>
    public Con(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

public class GenericDemo02 {

    public static void main(String[] args) {
        Con<String> c = new Con<String>("构造方法中使用泛型");
        System.out.println(c.getValue());
    }
}
3. 设置多个泛型class Gen<K, T> {

    private K key;
    private T value;

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

}

public class GenericDemo03 {

    public static void main(String[] args) {
        Gen<String, Integer> gen = new Gen<String, Integer>();
        gen.setKey("key");
        gen.setValue(10);

        System.out.println(gen.getKey() + ", " + gen.getValue());
    }
}4. 通配符
  • 类型不统一问题
class Info<T> {

    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    @Override
    public String toString() {
        return this.getValue().toString();
    }
}

public class GenericDemo04 {

    public static void main(String[] args) {
        Info<String> i = new Info<String>();
        i.setValue("类型不统一");
        tell(i); // 编译报错
        // The method tell(Info<Object>) in the type GenericDemo04 is not applicable for the arguments
(Info<String>)
    }

    public static void tell(Info<Object> i) {
        System.out.println(i);
    }
}原因:泛型是不可变的, 对于任意两个不同的类型Type1和Type2, List<Type1>既不是List<Type2>的子类型, 也不是List<Type2>的父类型. 所以这里不能将<String>转换成<Object>.解决方式:
  • public static void tell(Info<Object> i)中去掉<Object>, 使用raw类型, 但这样就失去了泛型的安全性检查意义.
  • 更好的方式, 采用通配符.
修改为public static void tell(Info<?> i)5. 泛型接口
  • 声明泛型接口和声明泛型类的语法类似, 也是在接口名称后面加上<T>.
  • 格式:
interface 接口名称<泛型标识>
  • 示例:
interface IGen<T> {
    public  void say();
}

class GenImpl<T> implements IGen<T> {

    private String info;

    public GenImpl(String info) {
        this.info = info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    public String getInfo() {
        return info;
    }

    @Override
    public void say() {
        System.out.println(this.info);
    }
}

public class GenericDemo05 {

    public static void main(String[] args) {
        IGen<String> g = new GenImpl<String>("泛型接口");
        g.say();
    }
}
6. 泛型方法
  • 泛型方法中可以定义泛型参数, 此时, 参数的类型就是传入数据类型.
  • 格式:
访问权限 <泛型标识> 泛型标识 方法名称([泛型标识 参数名称])
  • 示例:
public class GenericDemo06 {

    public static void main(String[] args) {
        String str = tell("Hello");
        System.out.println(str);
        int i = tell(10);
        System.out.println(i);
    }
   
    // <T>是第一泛型参数, 写在访问权限和static后面
    public static <T> T tell(T t) {
        return t;
    }
}

// 执行结果
Hello
107. 泛型数组泛型数组的使用要和泛型方法搭配使用.
  • 在使用泛型方法的时候, 也可以传递或返回一个泛型数组.
public class GenericDemo07 {

    public static void main(String[] args) {

        String arrStr[] = { "A", "B", "C" };
        tell(arrStr);

        Integer arrInt[] = { 1, 2, 3 };
        tell(arrInt);
    }

    public static <T> void tell(T arr[]) {
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

相关文章

    暂无相关文章
相关栏目:

用户点评