目录
- 1.2 注解反射与动态代理
- 1.2.1 反射
- 1.获取class 对象 3种 方式
- (1)对象**.**getClass() 方法
- (2)类**.**class 语法
- (3)Class**.**forName()
- 2.class 对象的方法
- 3. 类的实例化
- 4.Constructor
- 5.Field 成员变量的介绍
- 6.Method反射
- (1)获取成员方法的函数
- (2)调用成员方法
- (3)无参数成员方法调用
- (4)有一个参数的成员方法调用
- (5)有多个参数的成员方法调用
- (6)关于内部类的调用
- (7)关于成员变量的调用
- (8).invoke返回值判断
- 1.2.2 动态代理
- (1)静态代理:一对一的关系
- (2)动态代理:一对多
- 1.2.3 注解
- 1.2.3.1 注解分类
- 1.2.3.1 注解的使用
- (1)定义注解
- (2)注解的属性
- (3)注解解析
- (4)注解使用注意点
- (5) 注解使用举例
- (6) android中注解的使用
1.2 注解反射与动态代理
1.2.1 反射
反射的入口java.lang.Class
日常开发中的对象,分为两种,基本类型和引用类型
java">基本类型,(固定的 8 种)
整数:byte, short, int, long
小数:float, double
字符:char (2字节 65536个字符)
布尔值:boolean(1位)
引用类型
所有的引用类型都继承自 java.lang.Object
类,枚举,数组,接口都是引用类型
java.io.Serializable 接口,基本类型的包装类(比如 java.lang.Double)也是引用类型
引用类型占4字节
1.获取class 对象 3种 方式
(1)对象**.**getClass() 方法
已经有1个对象的情况下,使用对象.getClass() 返回这个对象的class
java">String str = "abcd";
Class<?> clazz = str.getClass(); //返回的对象 clazz 是 String 类型。
(2)类**.**class 语法
没有创建对象的情况下,使用 类名后加上 .class
java">Class<?> clazz = String.class;
Class<?> clazz1 = int.class;
String str = c.newInstance(); //获取这个类,并new出对象
//这种方式不仅能用于引用类型,基本类型也可以。
(3)Class**.**forName()
如果是隐藏类的情况下,Class.forName(“包名.类名”) 方式返回class
java">//一般使用
Class<?> cl = Class.forName("com.android.auto");
Class<?> c2 = Class.forName(String.class.getName()); //完整的包名+类名
//内部类
Class<?> c3 = Class.forName("com.example.auto$Phone");
注意:
class 对象的打印:
java">Class<?> cl = String.class;
System.out.println(cl);
System.out.println(cl.getName()); //包名+类名
System.out.println(cl.getSimpleName());//类名
//打印:
class java.lang.String
java.lang.String
String
获取class 对象 举例:
java">//第一种方式获取Class对象
Student stu1 = new Student(); //这一new 产生一个Student对象,一个Class对象。
Class<?> stuClass = stu1.getClass();//获取Class对象
//第二种方式获取Class对象
Class<?> stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);//第一种方式获取的Class对象和第二种方式获取的是同一个
//第三种方式获取Class对象
try {
//方式3 第一种 注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
Class<?> stuClass3 = Class.forName("com.Student");
Class<?> stuClass3 = Class.forName(Student.class.getName()); //方式3 第二种
System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
2.class 对象的方法
常用方法 | 说明 |
---|---|
getName():String | 返回由该 Class对象表示的实体(类,接口,数组类,原始类型或无效)的Class,作为String。 |
getPackage() | 获取此类的包 |
getSuperClass():Class | 返回表示由 Class 表示的实体的直接超类(类,接口,原语类型或空白)的 Class 。 |
getInterfaces():Class[] | 返回由该对象表示的类或接口直接实现的接口。 |
isArray():boolean | 确定此 Class 对象是否表示数组类 |
isEnum():boolean | 且仅当该类在源代码中被声明为枚举时才返回true。 |
isInterface():boolean | 确定指定的 Class 对象是否表示接口类型。 |
isPrimitive():boolean | 确定指定的Class 对象是否表示基本类型 |
构造常用方法 | 说明 |
Constructor [] getConstructors() | 获取所有public构造方法 |
Constructor [] getDeclaredConstructors() | .获取所以构造方法,不区分访问修饰符 |
getDeclaredConstructor(Class<?>… parameterTypes) | 获取指定构造方法,里面的参数类.class 如果是基本类型 例如:int.class float.class |
获取Method常用方法 | 说明 |
getMethods() | 获取所有public 方法 |
getDeclaredMethod(String name, Class<?>… parameterTypes)) | 获取无限定符方法 |
获取Filed常用方法 | 说明 |
getFields() | 获取所有public 变量 |
getDeclaredField() | 获取无限定符号变量 |
常用方法举例
java">Class<?> clazz1 =Student.class;
String className = clazz.getName(); //获取全类名
System.out.println("className: "+className);
if(clazz.isArray()) { //判断是否是数组
System.out.println("isArray");
}else if (clazz.isInterface()) { //判断是否是接口
System.out.println("isInterface");
}else if (clazz.isPrimitive()){ //判断是否是基本类型
System.out.println("Primitive");
}
if(clazz.isInstance(servant)) //判断是否是类型
{
System.out.println("isInstance");
}
3. 类的实例化
XXX.class.newInstance() 一定要获取权限
java">//jdk8
String str = String.class.newInstance()
//jdk9 先获取构造器,然后通过构造器new
Constructor constructor = String.class.getDeclaredConstructor()
constructor.setAccessible(true);
String str = constructor.newInstance();//无参数构造
constructor.setAccessible(false);
4.Constructor
1.获取所有public构造方法
java">public Constructor [] getConstructors()
2.获取所以构造方法,不区分访问修饰符
java">public Constructor [] getDeclaredConstructors()
3.获取指定构造方法
java">public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
// Class<?>... parameterTypes 里面的参数类.class 如果是基本类型 例如:int.class float.class
例如:
java">Class clazz = Class.forName("fanshe.Student");
//获取所有公有构造方法
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
//所有的构造方法(包括:私有、受保护、默认、公有)
Constructor[]conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
5.Field 成员变量的介绍
每个成员变量有类型和值。 java.lang.reflect.Field 为我们提供了获取当前对象的成员变量的类型,和重新设值的
(1)获取变量名字
java">Field.getName():返回这个变量的名称
(2)获取变量的类型
java">Field.getType():返回这个变量的类型
Field.getGenericType():如果当前属性有签名属性类型就返回,否则就返回 Field.getType()
(3)获取成员变量的修饰符
java">Field.getModifiers() 方法获取当前成员变量的修饰符
成员变量可以被以下修饰符修饰:
访问权限控制符:public, protected, private
限制只能有一个实例的:static
不允许修改的:final
不会被序列化:transient
线程共享数据的一致性:volatile
(4)获取和修改成员变量的值
获取变量:
java">public Field getField(String name) 公共成员字段,name 字段的名称。
public Field[] getFields() 所有公共字段数组
public Field getDeclaredField(String name) //常用可以获取当前类的publi、protecte、缺省 、privat修饰的字段
public Field[] getDeclaredFields()
返回一个Field[]数组 公共、保护、默认(包级私有)和私有字段,但不包括继承的字段。
修改变量:
java">field.setAccessible(true)
field.setAccessible(false)
Field.set(Object obj, Object value) //将value对象设置到obj里面,final 定义的变量field.set改变不了
Object Field.get(Object obj) //从Object中获取file变量
常见错误
1 :无法转换类型导致的 java.lang.IllegalArgumentException f.set(ft, new Integer(43));
2:反射非 public 的变量导致的 NoSuchFieldException
3 :修改 final类型的变量导致的 IllegalAccessException
举例:
java">例如:
//1.获取Class对象
Class stuClass = Class.forName("fanshe.field.Student");
//2.获取字段
System.out.println("************获取所有公有的字段********************");
Field[] fieldArray = stuClass.getFields();
for(Field f : fieldArray){
System.out.println(f);
}
System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
fieldArray = stuClass.getDeclaredFields();
for(Field f : fieldArray){
System.out.println(f);
}
注意:final 定义的变量field.set改变不了
6.Method反射
继承的方法(包括重载、重写和隐藏的)会被编译器强制执行,这些方法都无法反射。因此,反射一个类的方法时不考虑父类的方法,只考虑当前类的方法每个方法都由修饰符、返回值、参数、注解和抛出的异常组成
(1)获取成员方法的函数
java">Method getMethod(String name, Class… parameterTypes) 公共成员方法,parameterType参数 Method[] getMethods() 公共方法,返回Method 对象的数组,(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)
Method getDeclaredMethod(String name, Class… parameterTypes) 常用
返回一个 Method 对象,名字指定为name,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 Method[] getDeclaredMethods()
返回 Method 对象的一个数组,获取自己包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法
(2)调用成员方法
java">//1.设置setAccessible属性为true:
method.setAccessible()来改变
//2.invoke调用
method.invoke(Object obj,arg...)
例:
public class Test {
private void fun() {}
private static void fun1() {}
}
Class<?> clazz = Test.class;
Constructor<T> con = clazz.getDeclaredConstructor();
Test t = con.newInstance();
Method method = clazz.getDeclaredMethod("fun",null);
method.setAccessible(true);
method.invoke(t,null);
Method method = clazz.getDeclaredMethod("fun1",null);
method.invoke(null,null)
method.setAccessible(false);
注意:当调用的方法是静态成员方法是method.invoke(null,xxx); 传入的对象可以是null
(3)无参数成员方法调用
java">例:
private void setButtonListener(){}
//反射调用
Methodmethod = mockBtAddPhone.getClass().getDeclaredMethod("setButtonListener",null);
method.setAccessible(true);
Object result = method.invoke(mockBtAddPhone,null);
//或者Object result = method.invoke(mockBtAddPhone);
method.setAccessible(false); //用完设置为false
(4)有一个参数的成员方法调用
java">例:
private void set ButtonListener(int i) {}
//反射调用
Method method = mockBtAddPhone.getClass().getDeclaredMethod("setButtonListener",int.class);
method.setAccessible(true);
Object result = method.invoke(mockBtAddPhone,1);//第二个参数是,正式代码接口的参数
method.setAccessible(false); // 用完设置为false
(5)有多个参数的成员方法调用
java">public void BTAdapterOnConnectionChangedUpdate(byte index, long devAdress, String devName, int func, byte result){}
//反射调用
Class[] argTypes1=new Class[5];
argTypes1[0]=byte.class;
argTypes1[1]=long.class;
argTypes1[2]=String.class;
argTypes1[3]=int.class;
argTypes1[4]=byte.class;
Method method1 = MyBTAddPhoneListenerClazz.getDeclaredMethod("BTAdapterOnConnectionChangedUpdate", argTypes1);//传递的argTypes1是代码接口的参表
method1.setAccessible(true);
Objectresult1 = method1.invoke(obj,newObject[{(byte)0,1,null,1(byte)0});
method.setAccessible(false); // 用完设置为false
(6)关于内部类的调用
内部非静态类,默认持有外部类的this
Sceen测试类中包含BlueTooth的类,调用BlueTooth类中的onChange方法
java">public class Screen {
class BlueTooth {
public void onChange(String _str,int _num){
System.out.println("调用内部类成功 :"+_str+" "+_num);
}
}
}
调用方式:
1.先获取外部类
2.获取内部类,通过构造函数,new出对象时,传入外部类this
3.Method调用
注意:
内部类的构造函数参数,默认有外部类
//获取外部类
Class<?> clazz5 = Screen.class;
Screen screen1 = (Screen) clazz5.newInstance();
//获取内部类,通过构造函数,new出对象
Class<?> clazz6 = Class.forName("com.Main.Screen$BlueTooth");
Constructor<?>[] constructors = clazz6.getDeclaredConstructors();
for (Constructor<?> con: constructors){
System.out.println(con);//com.Main.Screen$BlueTooth(com.Main.Screen)
}
constructors[0].setAccessible(true);
Object objects = constructors[0].newInstance(clazz5.newInstance());
constructors[0].setAccessible(false);
//method调用
Class<?>[] args1 = new Class[2];
args1[0]=String.class;
args1[1]=int.class;
Method method = clazz6.getDeclaredMethod("onChange",args1);
method.setAccessible(true);
method.invoke(objects,"HH",123);
method.setAccessible(false);
(7)关于成员变量的调用
调用 Screen类中的fun3()接口,fun3()接口中需要对MMvvM中的num进行判断
java">调用方式:
1.类.class 获取类(Screen)。
2.Field 获取成员变量(MvvM)
3.field1.set 修改MvvM中num的值
4.field.set 将MvvM变量设置到类(Screen)里面
5.method.invo Method调用
--------------
public class Screen {
private MVVM mvm;
public Screen(){
System.out.println("Screen() ");
mvm = MVVM.getInstance();
}
public void fun3(int _num){
if (mvm.getNum()==_num){
System.out.println("调用条件分支成功 mvm.getNum()==_num");
}else {
System.out.println("调用条件分支成功 mvm.getNum()!=_num");}
}
}
------------
public class MVVM {
private int num;
private static MVVM mvvm;
public static MVVM getInstance(){
if (mvvm==null){
mvvm = new MVVM();
}
return mvvm;
}
public void setNum(int _num){this.num=_num;}
public int getNum(){return this.num;}
}
//条件分支调用
try {
//获取Screen类
Class<?> clazz3 = Screen.class;
Screen screen1 = (Screen) clazz3.newInstance();
Class<?>[] args1 = new Class[1];
args1[0]=int.class;
Method method = clazz3.getDeclaredMethod("fun3",args1);
Field field = clazz3.getDeclaredField("mvm");
//获取MVVM类 将100设置到num变量
Class<?> inner = MVVM.class;
MVVM mvvm = (MVVM) inner.newInstance();
Field field1 = inner.getDeclaredField("num");
field1.setAccessible(true);
field1.set(mvvm,100);
field1.setAccessible(false);
//将mvvm设置到screen1里面的screen
field.setAccessible(true);
field.set(screen1,mvvm);
field.setAccessible(false);
method.setAccessible(true);
method.invoke(screen1,100);
method.setAccessible(false);
} catch (NoSuchMethodException |NoSuchFieldException |IllegalAccessException |InstantiationException |InvocationTargetException e) {
e.printStackTrace();
}
(8).invoke返回值判断
java">如果是string类型返回函数
Class<> clazz= InvokeObj.class;
Method method4 = clazz.getMethod("StringShow", String.class);
String result = (String) method4.invoke(clazz.newInstance(), "Thinking in java"); Assert.assertEquals("Thinking in java",result);
如果是void函数,返回为空
Assert.assertEquals(null,result);
public Object invoke(Objectobj,Object...args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。
如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。
如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。 如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。
如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素不 被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null
1.2.2 动态代理
Git
代理概念:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用。
目的:
(1)通过引入代理对象的方式来间接访问目标对象,防止之间访问目标对象给系统带来不必要的复杂性。
(2)通过代理对象对原有的业务逻辑增强。
(1)静态代理:一对一的关系
java">静态代理:例子
接口: Start
Start ->实现 RealStart
Start ->实现 ProxyStart
ProxyStart 是代理类包含RealStart
public interface Start {
public void bookTicket();
public void Config();
public void CollectMoney();
public void sing();
}
public class RealStart implements Start{
@Override
public void bookTicket() {
System.out.println("RealStart bookTicket");
}
@Override
public void Config() {
System.out.println("RealStart Config");
}
@Override
public void CollectMoney() {
System.out.println("RealStart CollectMoney");
}
@Override
public void sing() {
System.out.println("RealStart sing");
}
}
public class ProxyStart implements Start {
private Start start;
public ProxyStart(Start _start) {
start=_start;
}
@Override
public void bookTicket() {
System.out.println("ProxyStart bookTicket");
}
@Override
public void Config() {
System.out.println("ProxyStart Config");
}
@Override
public void CollectMoney() {
System.out.println("ProxyStart CollectMoney");
}
@Override
public void sing() {
start.sing();
}
}
public static void main(String[] args) {
System.out.println("-------------简单代理--------------");
Start realStart = new RealStart();
Start proxyStart = new ProxyStart(realStart);
proxyStart.CollectMoney();
proxyStart.bookTicket();
proxyStart.sing();
}
(2)动态代理:一对多
代理类在程序运行时创建的代理方式被成为动态代理,相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
动态代理步骤:
1.真实对象接口
2.创建真实对象 实现接口
3.创建对象 实现 InvocationHandler接口
4.通过 Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Shop.class},invocationHandler); 实现代理
1.2.3 注解
注解是JDK1.5的新特性。
注解相当一种标记,是类的组成部分,可以给类携带一些额外的信息。
标记(注解)可以加在包,类,字段,方法,方法参数以及局部变量上。
注解是给编译器或 JVM 看的,编译器 或 JVM 可以根据注解来完成对应的功能。
注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开 发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种 标记,看你的程序有什么标记,就去 干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。
1.2.3.1 注解分类
注解分为:标准注解 和 元注解
标准注解:
@Override: 对覆盖超类中的方法进行标记,如果被标记的方法并没有实际覆盖超类中的方法,则编译器会发出错误警告。
@Deprecated: 对不鼓励使用或者已过时的方法添加注解,当编程人员使用这些方法时,将会在编译时显示提示信息。
@SuppressWarnings: 选择性的取消特定代码端中的警告。
@SafeVarargs: JDK 7新增,则用来声明使用了可变长度参数的方法,其在与泛型类一起使用时不会出现类型安全问题。
元注解:
元注解用来注解其他注解,从而创建新的注解
@Repeatable:JDK1.8新增,允许1个注解在同一个声明类型(类,属性,或者方法上)
1.2.3.1 注解的使用
(1)定义注解
定义新的类型注解使用 @interface 关键字
java">public @interface test {
....
}
(2)注解的属性
可以让用户在使用注解时传递参数,让注解的功能更加强大
格式1:数据类型 属性名();
格式2:数据类型 属性名() default 默认值;
属性适用的数据类型 八种基本数据类型(int , float , boolean , byte , double , char , long , short)
String类型,Class类型,枚举类型,注解类型
以上所有类型的一维数组
java">@Target(ElementType.METHOD)
public @interface test {
String name();
int age() default 100;
}
//定义使用范围为方法上,注解有2个属性 name,age
//使用:
class Test {
@Anno(name = "12312")
public void fun() {
}
}
使用注意事项
如果属性有默认值,则使用注解的时候,这个属性可以不用赋值。
如果属性没有默认值,那么在使用注解时一定要给属性赋值。
注解不加限定可以用在任何地 方,比如类,成员方法,构造方法,成员变量等地方。
(3)注解解析
注解解析相关的接口 Anontation:所有注解类型的公共接口,类似所有类的父类是Object。
AnnotatedElement:定义了与注解解析相关的方法,常用方法以下四个.
java">//如果此元素上 存在指定类型的注释,则返回true,否则返回false。
isAnnotationPresent(Class<? extends Annotation> annotationClass)
getAnnotations() //返回此元素上 存在的注释。
getDeclaredAnnotations() //返回 直接存在于此元素上的注释。
(4)注解使用注意点
使用注解时一定要声明: @Retention,@Target
否则会报:java.lang.NullPointerException
(5) 注解使用举例
java">//Anno.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Anno {
String name();
int age() default 200;
double rank();
}
//Anno1.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Anno1 {
String [] arg();
}
//Test.java
public class Test {
@Anno(name = "12312",rank = 200)
public void fun() {
}
@Anno1(arg = {"123456","1","1234"})
public void fun1() {
}
}
//Main.java
public class Main {
public static void main(String[] args) {
Class<?> clazz = Test.class;
try {
System.out.println("--------------BEGIN-----------------");
System.out.println("---------注解练习1---------");
Method method = clazz.getDeclaredMethod("fun");
method.setAccessible(true);
System.out.println("---");
Anno anno = method.getAnnotation(Anno.class);
//通过反射拿到注解的值
System.out.println(anno.age()+" "+anno.rank()+" "+anno.name());
Method method1 = clazz.getDeclaredMethod("fun1");
method1.setAccessible(true);
Anno1 annotation = method1.getAnnotation(Anno1.class);
String [] strings = annotation.arg();
for (String str:strings) {
System.out.println(str);
}
}catch (Exception e) {
System.out.println(e);
}
}
}
输出:
--------------BEGIN-----------------
---------注解练习1---------
---
200 200.0 12312
123456
1
1234
(6) android中注解的使用
Lifecycle