反射
.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);
}