反射、注解、泛型


反射

.java文件,通过编译,生成.class文件,再通过类加载器,加载到jdk内存中并且生成字节码对象class。

所有的反射功能都是基于我们字节码(class),一个类的class在内存中只有一份,而且class其实也是一个对象Class。

获取class对象的三种方式

方式一: 通过Object继承来的方法(getClass)获取对象对应的字节码文件对象

Person p = new Person();//创建Peron对象
Class clazz = p.getClass();//1

方式二:每一个类型都具备一个class静态属性,通过该属性即可获取该类的字节码文件对象。

Class clazz = Person.class;

方式三:必须类全名,使用最多。

Class clazz = Class.forName("cn.bean.Person");//必须类全名

通过构造函数,获取对象

默认构造函数

public class Person {
    public Person() {
        System.out.println("无参构造---");
    }
}

try{
    Class clazz = Person.class;
    Person person = (Person)clazz.newInstance();
} catch (Exception e) {
    e.printStackTrace();
}

有参公共构造函数

public class Person {
    private String name;
    public Person(String name) {
        this.name = name;
        System.out.println("有参构造---" + name);
    }
}

try{
    Class clazz = Person.class;
    Constructor constructor = clazz.getConstructor(String.class);
    Person person = (Person)constructor.newInstance("张三");
} catch (Exception e) {
    e.printStackTrace();
}

无参私有构造

public class Person {
    private Person() {
        System.out.println("无参私有构造---" );
    }
}

try{
    Class clazz = Person.class;
    Constructor constructor = clazz.getDeclaredConstructor();
    constructor.setAccessible(true);
    Person person = (Person)constructor.newInstance();
} catch (Exception e) {
    e.printStackTrace();
}

有参私有构造

public class Person {
    private String name;
    private Person(String name) {
        this.name = name;
        System.out.println("有参私有构造---" + name);
    }
}

try{
    Class clazz = Person.class;
    Constructor constructor = clazz.getDeclaredConstructor(String.class);
    constructor.setAccessible(true);
    Person person = (Person)constructor.newInstance("张三");
} catch (Exception e) {
    e.printStackTrace();
}

获取属性

public class Person {
    private String name = "李四";
    public int age = 18;
}

try {
    Class clazz = Person.class;
    Person person = (Person) clazz.newInstance();

    Field field = clazz.getField("age");
    int age = (int) field.get(person);

    Field fieldDeclared = clazz.getDeclaredField("name");
    fieldDeclared.setAccessible(true);
    String name = (String) fieldDeclared.get(person);

    System.out.println(name + "----" + age);
} catch (Exception e) {
    e.printStackTrace();
}

方法调用

获取,私有有参方法和公有无参方法

public class Person {
    
    private String name = "李四";

    public String getName() {
        System.out.println("get:" + name);
        return name;
    }

    private void setName(String name) {
        this.name = name;
        System.out.println("set:" + name);

    }
}
    
try{
    Class clazz = Person.class;
    Person person = (Person)clazz.newInstance();

    Method methodDeclared = clazz.getDeclaredMethod("setName", String.class);
    methodDeclared.setAccessible(true);
    methodDeclared.invoke(person,"张三");

    Method method = clazz.getMethod("getName");
    method.invoke(person);

} catch (Exception e) {
    e.printStackTrace();
}

实例:调用静态属性

Class clazz = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThreadField =  clazz.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThreadField.setAccessible(true);
//sCurrentActivityThread是静态的,可以传null
Object sCurrentActivityThread = sCurrentActivityThreadField.get(null);

注解

注解和反射没有什么必然联系。注解只是一个标识(标记),没有具体的功能逻辑代码。注解作用:给jvm看的,给机器看的。注解优点:开发效率高,成本低。注解缺点:耦合性大,并且不利于后期维护。

jdk5提供的注解

@Override:告知编译器此方法是覆盖父类的 
@Deprecated:标注过时 
@SuppressWarnings:压制警告

编写一个注解

关键字:@interface。属性:返回值 名称()

注意:如果属性的名字是value,并且注解的属性值有一个,那么在使用注解时可以省略value。

注解属性类型只能是以下几种:基本类型、String、枚举类型、注解类型、Class类型、以上类型的一维数组类型。

元注解:代表修饰注解的注解。作用:限制定义的注解的特性。

@Retention//代表注解什么时候起作用 
SOURCE: 注解在源码级别可见,编程阶段
CLASS:注解在字节码文件级别可见,编译时(打包的时候) 
RUNTIME:注解在整个运行阶段都可见 

@Target //代表注解修饰的范围:类上使用,方法上使用,字段上使用 
FIELD:字段上可用此注解 
METHOD:方法上可以用此注解 
TYPE:类/接口上可以使用此注解

例如

@Target(ElementType.FIELD)
@Target({ElementType.METHOD,ElementType.TYPE})

注解例子

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Anno {
    int age() default 28;
    String[] value();
}

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Anno2 {
    String value();
}

@Anno2("张三")
public class Test{
    @Anno(value = { "aaa","bbb","ccc"},age = 18)
    public void test(){
    }
}

default作用是设置默认值,如果设置了default,注解时候可以不传。

butterknife 用了反射没有? 一个地方用了Class.fromName().newInstance();(new 对象的时候)。

补充@Target注解的作用目标

@Target(ElementType.TYPE)   //接口、类、枚举、注解

@Target(ElementType.FIELD) //字段、枚举的常量

@Target(ElementType.METHOD) //方法

@Target(ElementType.PARAMETER) //方法参数

@Target(ElementType.CONSTRUCTOR)  //构造函数

@Target(ElementType.LOCAL_VARIABLE)//局部变量

@Target(ElementType.ANNOTATION_TYPE)//注解

@Target(ElementType.PACKAGE) ///包   

反射加注解实现

要想解析使用了注解的类,那么该注解的Retention必须设置成Runtime。关于注解解析的实质:从注解中解析出属性值。字节码对象存在于获得注解相关的方法。

运行时注解,仿xutil获取Id(编译时注解-ButterKnife注解):

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewById {
    int value();
}

public class ViewUtils {
    public static void inject(Activity activity) {
        // 1.获取所有的属性
        Field[] fields = activity.getClass().getDeclaredFields();
        // 2.过滤关于 ViewById 属性
        for (Field field : fields) {
            ViewById viewById =  field.getAnnotation(ViewById.class);
            if(viewById != null){
                // 3.findViewById
                View view = activity.findViewById(viewById.value());
                // 4.反射注入
                field.setAccessible(true);
                try {
                    //activity 属性所在类,view 代表的是属性的值
                    field.set(activity,view);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

@ViewById(R.id.tv)
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewUtils.inject(this);
    tv.setText("ViewById");
}

泛型

泛型的好处:将运行时期的ClassCastException,转移到了编译时期变成了编译失败;避免了类型强转的麻烦。

缺点:泛型不能用于显性地引用运行时类型的操作之中,例如转型,instanceof和new操作。

多泛型

class MorePoint<T,U,A,B,C>{
}

任意一个大写字母都可以。他们的意义是完全相同的,但为了提高可读性,大家还是用有意义的字母比较好,一般来讲,在不同的情境下使用的字母意义如下:

 E — Element,常用在java Collection里,如:List<E>,Iterator<E>,Set<E>
 K,V — Key,Value,代表Map的键值对
 N — Number,数字
 T — Type,类型,如String,Integer等等

类泛型

在任何地方出现的,代表的是统一类型。创建对象时,确定泛型的类型

class ArrayList<T> {
    public boolean add(T e) {
        return false;
    }

    public T get(int index) {
        return null;
    }
}

方法泛型

定义格式:修饰符 <代表泛型的变量> 返回值类型 方法名(参数){泛型类型 变量名}

例如,API中的ArrayList集合中的方法:

//该方法,用来把集合元素存储到指定数据类型的数组中,返回已存储集合元素的数组
public <T> T[] toArray(T[] a){  }  

//使用格式:调用方法时,确定泛型的类型
ArrayList<String> list = new ArrayList<String>();
String[] arr = new String[100];
String[] result = list.toArray(arr);

方法泛型最好与类的泛型一致,如果不一致,需要在方法上声明该泛型静态方法必须声明自己的泛型

泛型的接口

//带有泛型的接口
public interface List <E>{
    abstract boolean add(E e);
}
//一种实现类,先实现接口,不理会泛型
public class ArrayList<E> implements List<E>{
}  
//另一种实现类,实现接口的同时,也指定了数据类型
public class XXX implements List<String>{
}

泛型的通配

匹配所有的数据类型:<?>

泛型的上限和下限

// 上限,限制的是父类,接收BaseActivity和它的子类
public void startActivity(Class<? extends BaseActivity> clazz) {
    Intent intent = new Intent(this,clazz);
    startActivity(intent);
}

//下限,限制的是子类,接收BaseActivity和它的父类
public void startActivity1(Class<? super BaseActivity> clazz) {
    Intent intent = new Intent(this,clazz);
    startActivity(intent);
}

文章作者:
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 !
  目录