反射:java提供的一种机制,可以在类的运行时期动态的获取类中所有的信息(任意字段、构造方法,普通成员方法。。。。)
缺点:会破坏封装性,损失性能。 尽量少用反射
安卓应用场景:安卓中一些私有的类通常是系统相关的比如调用系统硬件设备等类,设备管理器。开发需要,就只能通过反射去调用这些私有的方法
Java应用场景:解耦的。在开发中,做一个项目是很多人一起开发,a(用户登录注册相关) b(列表,加入购物车) c(订单结算) 三个程序员共同开发
1.java.lang.Class
2.java.lang.reflect.Constructor
3.java.lang.reflect.Method
4.java.lang.reflect.Field
…
//获取字节码对象的第一种方式: 类名.class*/
Class clazz = User.class;
//获取字节码对象的第二种方式: Class.forName(); 解耦合了 jdbc
Class clazz1 = Class.forName("cn.itsource.reflect.User");
//获取字节码对象的第三种方式 对象.getClass()
Class clazz2 = new User().getClass();
//用字节码对象调用相关方法获取Field,Constructor,Method所有对象。(加s代表所有)
Class clazz = User.class;
Method[] ms = clazz.getMethods();
Field[] fs = clazz.getFields();
Constructor[] cs = clazz.getConstructors();
Class clazz = User.class;
Method[] ms = clazz.getMethods(); //获取本类公有的方法和父类继承过来的方法
Method[] ms2 = c2.getDeclaredMethods(); //获取本类公有和私有的所有方法
//获取指定的方法 (不加s代表指定)(没有参数类型就不写)
Method m = clazz.getMethod("method",int.class); //获取本类和父类指定公有方法
Method m2 = clazz.getDeclaredMethod("method",int.class);//获取本类指定可以是私有方法
Class<User> clazz = User.class;
//获取指定的方法,("方法名",参数类型.class)
Method m = clazz.getMethod("method",int.class);
User user = new User();
//返回值表示调用方法本身的返回内容
// obj :表示方法所属的对象 ,args:表示方法的实际参数。
Object r = m.invoke(user, 10);
System.out.println(r);
private编译时期有效,反射是在运行时期
Class clazz = User.class;
Method m = clazz.getDeclaredMethod("methodPrivate"); //2.获取指定的方法
m.setAccessible(true); //暴力反射。破坏了封装,设置可以访问。
//通过反射获取类的对象。但是private构造创建对象无效。需要先用反射去暴力获取私有构造方法再造对象
Object newInstance = clazz.newInstance(); //底层反射。或者还用User user = new User();
m.invoke(newInstance);
//获取Runtime构造器对象
Constructor<Runtime> c = Runtime.class.getDeclaredConstructor();
//设置可以访问
c.setAccessible(true);
//invoke --> newInstance 通过反射创建Runtime类对象
Runtime newInstance = c.newInstance();
System.out.println(newInstance);
//通过单例模式创建对象
Runtime runtime = Runtime.getRuntime();
System.out.println(newInstance);
Field f = User.class.getDeclaredField("name");
//设置可以访问
f.setAccessible(true);
User user = new User();
f.set(user, "jack");
System.out.println(f.get(user));
System.out.println(user.getName());
1.找到字节码对象
2.通过字节码对象找到具体对象(Method,Constructor,File)。
3.找具体要反射的方法
4.要反射的方法是私有的需要设置可以访问,setAccessible
5.调用,invoke,newInstance,set
使用注解语法:@注解名字
注解(annotation):用来标记代码的,简单理解就是一种标记,给代码看的.
注释:给程序员看的,用来说明解释代码.
作用:1.可以用来检验是否符合规定。2.使代码看起来简洁.清晰
@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Override
public String toString() {
// TODO Auto-generated method stub
return super.toString();
}
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
有了这个标记的方法,表示该方法已经过时,但是还可以调用,不推荐使用它,因为有了新的方法去替代它。
jdk更新换代,会替换到以前过时的一些功能,升级了,旧的也不会删掉
@Deprecated
public static void test(){}
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
//抑制黄色波浪线的警告.
@SuppressWarnings({ "rawtypes", "unused" })
ArrayList arrayList = new ArrayList();
从 Java 7 开始,额外添加了 3 个注解:
@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次
元注解:在自定义注解的时候,用来标记注解的注解。
@Documented 如果使用的注解中包含这个元注解,那么我们在生成文档api的时候,会有这个注解的描述
@Target 表示标记自定义注解可以用在什么地方
@Retention 标记自定义注解的生命周期 source class runtime
@Inherited 标记注解可以被继承.
/**
* @author Administrator
* @see 参考xxxx
* @since 从jdk1.8
* @version 1.0
*/
@Documented
public @interface 注解名字{
}
/**
* @author Administrator
*自定义注解语法:
* public @interface 注解名字{
* 类型 属性名();
* //类型:8种,String,数组,枚举,其他注解类型.
* 类型 属性名() default 值;
* }
*/
@Documented
@Target(value={ElementType.TYPE,ElementType.PARAMETER})
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
int a() ;
String b() ;
int[] c() ;
TT d();
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**Type parameter declaration */
TYPE_PARAMETER,
/**Use of a type */
TYPE_USE
}
@Documented
@Target(value={ElementType.TYPE,ElementType.PARAMETER})
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
int a() default 1;
String b() default "abc";
String[] value() ;
// TT d();
}
1.如果一个注解中只有一个属性需要赋值,属性名字叫value,那么可以省略value,直接给对应类型的值.
2.如果属性是一个数组,赋值的时候,只有一个值,可以省略花括号{}
@MyAnnotation(value={"发发发"},a=2)
public class MyAnnotationTest2 {
@TT
public static void test(){
System.out.println("test......");
}
}
1.找到类的字节码对象
2.找到有哪些方法
3.通过方法对象找到哪个方法上有注解
4.执行某方法
public class DemoTT {
public static void main(String[] args) throws Exception {
//获取类上的注解
/* Annotation[] as = MyAnnotationTest2.class.getAnnotations();
for (Annotation annotation : as) {
System.out.println(annotation);
} */
System.out.println("-------------");
//获取指定类的所有方法.
Method[] ms = MyAnnotationTest2.class.getDeclaredMethods();
for (Method m : ms) {
//获取方法上面的TT注解对象
TT tt = m.getAnnotation(TT.class);
//判断有TT注解对象的方法
if (tt instanceof TT) {
//调用有TT注解对象的方法.
m.invoke(new MyAnnotationTest2());
}
}
}
}
@MyAnnotation(value={"发发发"},a=1)
public class MyAnnotationTest2 {
@TT
public static void test(){
System.out.println("test......");
}
@TT
public static void test2(){
System.out.println("test2......");
}
public static void test3(){
System.out.println("test3......");
}
}