前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >反射反射 程序员的快乐

反射反射 程序员的快乐

原创
作者头像
房上的猫
发布2018-04-11 23:22:44
9130
发布2018-04-11 23:22:44
举报
文章被收录于专栏:个人随笔个人随笔

文章开始之前 提一下:

java反射操作其实就是主要围绕Class,Field,Methon,Constructor等几个类来操作其中的方法

Class类的使用

1)在面向对象的世界里,万事万物皆对象

A.Java语言中,普通数据类型,静态成员不是对象,其他皆对象

B.每一个类也是对象

C.类是java.lang.Class类的实例对象

There is a class named Class

对象的表示:

普通类对象表示:

Foo foo = new Foo();

Class类实例对象表示:

//Foo也是一个实例对象,是Class类的实例对象

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

1.任何一个类都有一个隐含的静态成员变量class:

Class c1 = Foo.class;

2.已经指定该类的对象通过getClass方法

Foo foo = new Foo();

Class c2 = foo.getClass();

官网:c1/c2 表示了Foo类的类类型(class type)

一个类,本身就是一个对象,它是Class类的对象

万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们称为该类的类类型

3.ForName(“类的全称”);

Class c3 = null;

c3=Class.forName(“包名+.某类名”);

不管是那种表达方式,都是Class类的对象,都是Foo类的类类型。所以:

C1=c2=c3

完全可以通过类的类类型创建该类的对象(创建Foo的实例对象,但要做强制类型转换,向下转型)

前提要求:需要有无参数的构造方法

Comont comont = new Comont();

Class c1 = comont.getClass();

Class c2 = Comont.class;

try {

Comont c = (Comont) c2.newInstance();

} catch (InstantiationException | IllegalAccessException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

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

comont.start();

动态加载类

Class.forName(“类的全称”);

1.不仅表示了类的类类型,还代表动态加载类

2.编译时刻加载类是静态加载类,运行时刻加载类是动态加载类

(1)New 对象是静态加载类,在变异时刻就需要加载所有的可能使用到的类

(2)动态加载类,在运行时刻加载

编译不会报错

运行时刻报错。找不到该类

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

共同实现接口类 cc = (共同实现接口类) c.newInstance();

3.使用记事本开发可明显区分

Java 类 要运行类:动态加载类,不需要重新编译测试类,直接运行即可

功能性的类:尽量使用动态加载

基本数据类型也有类类型

Class c1 = int.class;

Class c2 = String.class;//String类的字节码

数据类型和包装类的类类型不同

Void也是类

Class c3 = void.class;

基本数据类型

Void关键字

都存在类类型

方法也是对象,方法是Method的对象

反射:某类的字节码表示

获取方法信息

1.c.getName()

(1)基本数据类型返回类型名

(2)类返回包名+类名类的名称

2.c1.getSimpleName()

(1)返回不带包名的类的名称

栗子:通过反射可以获取到任何类的信息

需求:打印类的信息,获取类的 成员函数

package cn.pro;

import java.lang.reflect.Method;

/**

*

* @author: 房上的猫

*

* @time: 下午5:34:45

*

* @博客地址: https://www.cnblogs.com/lsy131479/

*

*/

public class mymain {

public static void printClassMessage(Object obj) {

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

// 形参obj 该对象所属类的信息

Class c = obj.getClass();// 传递的是哪个子类的对象 c就是该 子类的类类型

// getClass()方法:native修饰代表 存在不是java 代码 ,调度 外用 ///该方法java内调用底层c语言实现

// 获取累的名称

System.out.println("类的名称是:" + c.getName());

// Method类是方法对象

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

// getMethods()方法获取的是所有的public修饰的函数,包括父类 继承而来的

Method[] ms = c.getMethods();

// c.getDeclaredMethods()获取的是所有该类自己声明的方法,不 问访问权限.所有。所有。所有

String[] name = new String[ms.length];

for (int i = 0; i < ms.length; i++) {

// 得到方法的返回值类型--得到的是返回值类型的类类型

Class returnType = ms[i].getReturnType();

// 得到返回值名字

String returnName = returnType.getName();

// 得到方法的名称

name[i] = ms[i].getName();

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

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

Int params=parameterTypes.length;

String[] paramNames = new String[params];

for (int j = 0; j < params; j++) {

// 得到参数列表名

paramNames[j] = ms[j].getName();

}

}

}

}

通过反射可以获取任何类的类信息

比较牛逼

获取类的成员变量构造函数信息

成员变量也是对象

是Java.lang.reflect.Field的对象

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

栗子:通过反射可以获取到任何类的信息

需求:打印类的信息,获取类的成员变量

package cn.reflect;

import java.lang.reflect.Field;

/**

*

* @author: 房上的猫

*

* @time: 下午3:49:32

*

* @博客地址: https://www.cnblogs.com/lsy131479/

*

*/

public class myField {

public static void printFieldMessage(Object obj) {

Class c = obj.getClass();

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

Field[] fs = c.getFields();

// getDeclaredFields() 获取的是该类自己声明的成员信息 不问访问权限.所有。所有。所有

// Field[] fs = c.getDeclaredFields();

for (Field field : fs) {

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

Class fieldType = field.getType();

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

String typeName = fieldType.getName();

// 得到成员变量的名字

String fieldName = field.getName();

//

}

}

}

构造函数也是对象

是Java.lang.Constructor的对象 其中封装了构造函数的信息

栗子:通过反射可以获取到任何类的信息

需求:打印类的信息,获取类的构造函数信息

package cn.reflect;

import java.lang.reflect.Constructor;

/**

*

* @author: 房上的猫

*

* @time: 下午3:49:32

*

* @博客地址: https://www.cnblogs.com/lsy131479/

*

*/

public class myCon {

public static void printConMessage(Object obj) {

Class c = obj.getClass();

// getConstructors() 获得所有的共有的构造方法

Constructor[] cs = c.getConstructors();

// getDeclaredConstructors() 得到所有的构造方法(必须是自己声明的) 不问访问权限.所有。所有。所有

// Constructor[] cs = c.getDeclaredConstructors();

for (Constructor constructor : cs) {

// 获取构造函数名

String name = constructor.getName();

// 获取构造函数的参数列表------>得到的是参数列表的类类型

Class[] parameterTypes = constructor.getParameterTypes();

for (Class class1 : parameterTypes) {

// 获取构造函数参数列表名

String name2 = class1.getName();

}

}

}

}

其他

Class类还可以获取类的其他信息,这里将不再详细讲解

想要了解的可以创建Class类的实例对象

Class c = obj.getClass();

c.get...

自行查看并尝试

或阅读帮助文档,查看Class类的所有API

记住一点:在任何情况下想要获取一个类的信息,首先要得到这个类的类类型

得到类的类类型,得到这个类的类信息就轻而易举得到了

方法的反射

1.如何获取某个方法

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

2.方法反射的操作

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

栗子:

package cn.reflect;

import java.lang.reflect.Method;

/**

*

* @author: 房上的猫

*

* @time: 下午4:25:25

*

* @博客地址: https://www.cnblogs.com/lsy131479/

*

*/

public class MethodDemo1 {

public static void main(String[] args) throws Exception {

// 要获取print(intint)方法

A a = new A();

/*

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

*/

Class c = a.getClass();

/*

* 2.获取方法 名称和参数列表

*

* getMethod(name, parameterTypes)获取的是public的方法

*

* c.getDeclaredMethod(name, parameterTypes)获取的是所有自己声明的方法 不问访问权限

*

*

* 参数解析: 1.name:方法名 2.parameterTypes:参数列表类型(0或多个)

*/

Method m = c.getMethod("print", new Class[] { int.class, int.class });

/*

* 方法的反射操作

*

* a.print(10,20);方法的反射操作是用m对象来进行方法调用 和a.print调用的效果

*

* 方法如果没有返回值返回null 有返回值返回具体的返回值 返回object类型,需要做强制类型转换

*/

Object o = m.invoke(a, new Object[] { 10, 20 });

// 就等于调用了print(intint)方法

/*

* 如果方法无参数列表则:

*

* c.getMethod("print"); m.invoke(a);

*/

}

}

class A {

public void print(int a, int b) {

System.out.println(a + b);

}

public void print(String a, String b) {

// a的大写形式 and b的小写形式

System.out.println(a.toUpperCase() + "," + b.toLowerCase());

}

}

升华操作:

通过反射了解集合泛型的本质

通过Class,Method来认识泛型的本质

什么是泛型?

泛型什么时候有效?

那我们探讨一下这两个话题:

package cn.reflect;

import java.lang.reflect.Method;

import java.util.ArrayList;

/**

*

* @author: 房上的猫

*

* @time: 下午6:31:38

*

* @博客地址: https://www.cnblogs.com/lsy131479/

*

*/

public class MethodDemo2 {

public static void main(String[] args) {

// 普通集合

ArrayList list = new ArrayList<>();

// 泛型集合 集合内只能存放'<>'尖括号内类型的值

ArrayList<String> list1 = new ArrayList<>();

// 集合的泛型防止错误输入

// 利用反射了解集合泛型

Class c1 = list.getClass();

Class c2 = list1.getClass();

list1.add("1");

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

/*

* 反射的操作都是编译之后的操作(运行时)

*

* c1==c2结果返回true 表示两个集合类运行时都是同一类类型

*

* 说明编译之后集合的泛型是去泛型化的(编译完之后就没有泛型存在了)

*

* java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了

*

* 验证:可以通过方法的反射来操作,绕过编译

*/

try {

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

m.invoke(list1, 100);// 大胆猜想:绕过编译操作就绕过了泛型

// 集合大小返回2 .说明:绕过编译阶段后可以向泛型集合添加任何类型的值

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

// 尝试查看泛型集合内的值 发现值已经添加进去

System.out.println(list1);

/*

* 在这里如果使用for增强遍历将会抛异常

*

* 因为后面添加的100是int类型的值

*

* 而集合泛型是String类型

*

* 使用for增强遍历内部其实做出的操作是:集合的每个值都使用一个集合泛型类型的数据类型来接受遍历

* 而int类型使用String类型来接受,众所周知将会报错(这里的泛型类型是String)

*

* 会有类型转换错误

*/

} catch (NoSuchMethodException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

结论:

反射(Class,Method,Field ... )的操作都是绕过编译,都是在运行时刻来执行的

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Class类的使用
    • 动态加载类
      • 获取方法信息
        • 获取类的成员变量构造函数信息
          • 其他
          • 方法的反射
          • 升华操作:
          • 结论:
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
          http://www.vxiaotou.com