java反射以及Class类类型

java的反射机制:

在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法,

对于任意一个对象,都能调用它的任意一个方法和属性,

这种动态获取信息,以及动态调用对象的方法的功能称为java语言的反射机制

 

1.Class类的使用

 

在面向对象的世界中,万事万物都是对象

普通数据类型不是面向对象 比如int long

但是它们的包装类型 integer Long是对象

 

静态的成员 不是对象 是属于类的

 

 

 

什么是类

具有相同特性(数据元素)和行为(功能)的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象,类实际上就是一种数据类型。

 

类是对象的模版

对象是类的实例

 

但是 类也是对象 类是java.lang.Class类的实例对象

 

比如:

Class Foo{}

 

Foo的对象 是 Foo f1=new Foo();

 

但是 Foo类 也是一个对象 它是Class类的对象

只有java的虚拟机 可以访问Class.java的构造方法 从而构造出任何Class

 

任何一个类都是Class的实例对象 这个实例对象有三种表示方式

 

第一种:

Class c1=Foo.class

//任何一个类都有一个隐含的静态成员变量

 

第二种:

Class c2=f1.getClass()

//已经该类的对象 通过getClass方法

 

// 官网的解释 c1 c2 此时表示的是Foo类的 类类型 (class type)

类也是对象 是Class类的实例对象

这个对象 我们称为该类的类类型

 

 

第三种

 

Class c3=Class.forName("Foo");

 

这里 c1==c2==c3 true

 

 

我们完全可以通过该类的类类型创建该类的对象实例

 

Class c1=Foo.class

Foo foo=(Foo)c1.newInstance(); //需要做强制类型转换

 

 

 

动态加载类

 

Class.forName("类的全称")

 

不仅表示了类的Class type 还代表了动态加载类

编译时刻加载类是静态加载

运行时刻加载类是动态加载

 

 

比如说

java反射以及Class类类型_第1张图片

 

这样直接去进行编译 会报错,因为Word和Excel类均不存在

java反射以及Class类类型_第2张图片

 

new 创建对象 是静态加载必须在编译期的时候就将所有类都加载进去

java反射以及Class类类型_第3张图片

 

如果是动态加载类

那么在编译期是不会报错的

new 创建对象 是静态加载类 再编译时刻就

需要加载所有的可能使用到的类

比如 Foo foo=new Foo();

如果 Foo类不存在的话 是不能通过编译的

 

但是 动态加载类是在运行时刻进行加载

需要用到的时候才会去加载

比如

 

//运行时刻加载

Class c= Class.forName(args[0]);

//通过类类型 创建该类对象

Object oa=(Object)c.newInstance();

 

是可以在编译的时候通过,在运行的时候

输入参数args[0]去加载对象

 

这里如果是要加载Foo 那么Foo 实现 Object接口就行

先定义 interface Object

然后 foo implement Object

 

这样即可实现动态加载任何实现了Object接口的类 而不需要重新编译该程序

这样也可以实现热部署

 

因此功能性的类 尽可能的使用动态加载类 而不是静态加载类

这样方便重新升级的时候不用重新编译所有的代码

 

 

获取方法信息

Class c1=int.class //int的类类型

Class c2=String.class //String 的类类型

Class c3=void.class

 

拿到类类型之后

 

c1.getName(); //结果是 int

c2.getName() //结果是java.lang.String

c2.getsimpleName //结果是 String

 

//getName 基本数据类型直接拿到名字

// 类拿到是带包名的名字

 

 

Class类的基本API

 

获取方法的信息

//要获取类的信息 首先要获取类的类类型

 

Class c=obj.getClass();

 

//获取类的名称

 

c.getName();

 

//获取类的方法

 

//Method类 方法对象 所有的方法 都是这个类的对象

 

//一个成员方法 就是一个Method对象

 

//getMethods()方法 获取的事的所有public的函数 包括父类继承来的

 

//getDeclareMethods() 获取的是所有该类自己声明的方法 不论访问权限 父类继承来的没有

 

 

Method[] ms=c.getMethods(); //获取这个类里所有的public方法 存到一个method的数组中

 

ms[i].getReturnType();//获取方法返回值类型的Class type

//比如说返回值是String 我们拿到的是String.class

//所以返回值类型是 ms[i].getReturnType().getName()

 

ms[i].getName();//获取方法名字

 

//获取参数类型 得到的是参数列表的类型的Class type

Class[] paramTypes=ms[i].getParameterTypes();

 

paramTypes[i].getName();//拿到参数名字

 

获取成员变量的信息

 

成员变量也是对象

java.lang.reflect.Field 的对象

FieId类封装了关于成员变量的操作

 

getFieIds()方法获取的是所有的public的成员变量的信息

getDeclaredFields()获取的是该类自己声明的成员变量的信息

 

Field[] fs=c.getDeclareFields();

 

//得到成员变量的类类型

Class fieldType =fs[i].getType();

//得到成员变量的类型名字

String typeName=fieldType.getName();

//得到成员变量的名字

String fieldName=fs[i].getName();

 

 

获取构造函数信息

 

Class c=obj.getClass();

//构造函数也是对象

//是 java.lang.Constructor中封装了构造函数的信息

//getConstructors获取所有的public的构造函数

//getDeclaredConstructors 获取所有的构造方法 包括私有

Constructor[] cs=c.getDeclareConstructors();

cs[i].getName() //获取构造函数的名字

 

//获取构造函数的参数的类类型

Class[] paramTypes = cs[i].getParameterTypes();

paramTypes[i].getName()// 获取类型名字

 

2.方法的反射

 

1.如何获取某个方法

 

方法的名称和方法的参数列表才能唯一决定某个方法

 

方法的反射操作

method的对象有个invoker方法 ,这个方法通过参数列表确定某个方法

method.invoke(对象,参数列表)

 

 

 

public static void main(String[] args) {

//要获取print(int ,int) 方法

//要获取一个方法,就是要获取类的信息,获取类的信息,首先要获取类的类类型

 

A a1 = new A();

Class c = a1.getClass();

///获取方法:名称和参数列表来决定

//getMethod 获取的是public方法

//getDelcareMethod自己声明的方法

try {

Method m = c.getMethod("print", int.class, int.class);

//此时获取到了method的方法的对象

//方法的反射操作:是用Method对象来进行方法的调用操作

//方法如果没有返回值则返回null,如果有返回值 则返回具体的返回值

Object o = m.invoke(a1, 1, 2);

//等同于

a1.print(1, 2);

 

//通过反射调用方法的私有对象

//先获取声明的所有方法

Method m2 = c.getDeclaredMethod("print",int.class);

System.out.println("开始获取方法");

System.out.println(m2.getName());

System.out.println("参数列表为");

for (Class aClass : m2.getParameterTypes()) {

System.out.println(aClass.getName());

}

System.out.println("调用私有方法");

// m2.invoke(a1, 1);

//会报异常

//java.lang.IllegalAccessException: Class classDemo.demo3.MethodDemo1 can not access a member of class

// classDemo.demo3.A with modifiers "private"

//但是手动将该方法改为公有的即可

m2.setAccessible(Boolean.TRUE);// 将私有访问限制改为public

m2.invoke(a1, 1);

} catch (Exception ex) {

ex.printStackTrace();

}

}

 

 

输出如下

java反射以及Class类类型_第4张图片

 

 

泛型:

java集合种的泛型 是防止错误输入的,只有在编译阶段有效

绕过编译 ,就无效了

比如说 通过反射来操作,绕过编译

反射是在运行期起作用的

 

 

public class MethodDemo4 {

public static void main(String[] args) {

ArrayList list=new ArrayList();

 

ArrayList list1=new ArrayList();

list1.add("hello");

// list1.add(2); //直接放是错误的 会报错 因为编译时限制了类型是String

 

Class c1=list.getClass();

Class c2=list1.getClass();

System.out.println(c1==c2);

 

//c1==c2 编译之后集合的泛型是去泛型化的

try {

Method m=c2.getMethod("add",Object.class);

m.invoke(list1,2); //绕过泛型的限制 将2这个整型放进去了 ,说明在运行时候 去没有限制的

System.out.println(list1.size());

} catch (NoSuchMethodException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (InvocationTargetException e) {

e.printStackTrace();

}

 

}

 

}

java反射以及Class类类型_第5张图片

 

 

 

对象除了可以通过new创建,也可以反射创建,class.instant 或者反序列化创建

 

 

 

你可能感兴趣的:(java)