一、Class类
1.Class类对象:
Class类也是类的一种,只是名字和class关键字高度相似。Java是大小写敏感的语言。Class类的对象内容是你创建的类的类型信息,比如你创建一个shapes类,那么,Java会生成一个内容是shapes的Class类的对象(也就是Class类的对象是一个类的类型信息)。Class类的对象不能像普通类一样,以 new shapes() 的方式创建,它的对象只能由JVM创建,因为这个类没有public构造函数。
每一个类都有一个Class类的对象与之对应,一个类一个Class对象,这个类生成的对象,都会有个字段记录该对象所属类在CLass类的对象的所在位置。
2…获得一个Class对象的方法:
a.使用Object.getClass ()方法----引用类型的对象的获取方式
如果我们已经拿到了一个对象,可以使用这个对象的 getClass 方法获得一个 Class 对象(不过这仅限于引用类型的对象),甚至可以直接用new的对象来调用getClass()方法:
String string=new String();
//使用已经存在的对象的getClass()方法获取Class对象
Class class1=string.getClass();
Class class2=new String().getClass();
b.使用类的class成员属性
如果我们当前没有某个类的对象,无法使用 getClass() 方法来获取Class对象,那还可以使用 类名.class 来获取 Class对象:
//使用 类名.class来获取Class对象
Class class2=String.class;
string.getClass()==String.class//类的class成员和对象的getClass返回值都是
//这个类所对应的Class对象
其实这种方式不仅能用于引用类型,基本类型也可以。数组也是可以的:
Class class3=int.class;
Class class4=int[][].class
c.使用Class类的forName(“类完整路径”)方法获取,这是一个静态方法,直接用Class.forName()调用:
如果我们有一个类的完整路径,就可以使用 Class.forName(“类完整的路径”) 来得到相应的 Class,这个方法只能用于引用类型,所谓类的完整路径是:包名.类名 例如:java.lang.String。
Class<?> strClass=Class.forName("java.lang.String");
d.使用包装类的TYPE属性来获取包装类对应的Class类:
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
二、反射
1.与反射相关的类和方法
主要需要的方法:
Class类:
getName() 获得类的完整路径名字
newInstance() 创建类的实例
forName(String className) 根据类名返回类的对象
getSuperclass() 获得当前类继承的父类的名字
getFields() 获得所有公有的属性对象
getConstructors() 获得该类的所有公有构造方法
getMethod(String name, Class...<?> parameterTypes) 获得该类某个公有的方法
getMethods() 获得该类所有公有的方法
Field类:
equals(Object obj) 属性与obj相等则返回true
get(Object obj) 获得obj中对应的属性值
set(Object obj, Object value) 设置obj中对应属性值
Method类:
invoke(Object obj, Object... args) 传递object对象及参数调用该对象对应的方法
Constructor类:
newInstance(Object... initargs) 根据传递的参数创建类的对象
2.反射的意义
反射就是运行时获取一个类的所有信息,可以获取到.class的任何定义的信息(包括成员 变量,成员方法,构造器等)可以操纵类的字段、方法、构造器等部分。
反射的作用如下:
(1)、通过反射运行配置文件内容
通过修改配置文件,实现程序灵活性,不必写死。就相当于配置文件是一个接口,随便写什么类都能跑。类似于多态,但是多态只能在继承的类之间多态,而反射则可以在相同的功能的类之间。
(2)、通过反射越过泛型检查
(3)、实现内省
3、反射的实现:
补充: 一般Class的方法都有个.class的参数,用来表示这个函数的参数是什么类型的,
Method m = h.getClass().getMethod(“setName”, String.class); 这里String.class就是String的class对象,表示这个是函数setName()是String类型的参数。
例子1:通过反射来创建对象(通过反射拿到类的class对象,然后通过class对象拿到构造器对象,然后用构造器对象来创建对象)
package test;
public class ConstructorTest {
/*
* 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
* 2).获取单个的方法,并调用:
* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
*
* 2.创建对象
* Constructor对象调用newInstance(Object... initargs)
*/
public static void main(String[] args) throws Exception {
//1.加载Class对象
Class clazz = Class.forName("pojo.Hero");
//2.获取所有公有构造方法
System.out.println("**********************所有公有构造方法*********************************");
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("获取私有构造方法,并调用");
con = clazz.getDeclaredConstructor(float.class);//获取参数类型是float的构造方法
System.out.println(con);
//调用构造方法
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
obj = con.newInstance(100);
System.out.println("*获取公有、无参的构造方法");
Constructor con = clazz.getConstructor(null);//获取无参的构造方法
//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
//2>、返回的是描述这个无参构造函数的类对象。
System.out.println("con = " + con);
//调用构造方法
Object obj = con.newInstance();
}
}
例子2:获取成员方法并使用
package test;
public class MethodTest {
public static void main(String[] args) {
HeroPlus h = new HeroPlus();
try {
// 获取这个名字叫做setName,参数类型是String的方法
//一般参数都有个.class的参数,String.class就是String的class对象
//表示这个是String类型的参数
//
Method m = h.getClass().getMethod("setName", String.class);
// 对h对象,调用这个方法
m.invoke(h, "盖伦");
// 使用传统的方式,调用getName方法
System.out.println(h.getName());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
例子3:反射的作用1:利用配置文件来让程序更灵活
首先准备两个业务类
package service;
public class Service1 {
public void doService1(){
System.out.println("业务方法1");
}
}
package service;
public class Service2 {
public void doService2(){
System.out.println("业务方法2");
}
}
当需要从第一个业务方法切换到第二个业务方法的时候,使用非反射方式,必须修改代码,并且重新编译运行(太耗时间),才可以达到效果。而用反射的方式就能用配置文件来切换业务。
首先准备一个配置文件,就叫做spring.txt吧, 放在src目录下。
里面存放的是类的名称,和要调用的方法名。首先准备一个配置文件,就叫做spring.txt吧, 放在src目录下。里面存放的是类的名称,和要调用的方法名。
在测试类Test中,首先取出类名称和方法名,然后通过反射去调用这个方法。
当需要从调用第一个业务方法,切换到调用第二个业务方法的时候,不需要修改一行代码,也不需要重新编译,只需要修改配置文件spring.txt,再运行即可。
spring.txt内容
class=reflection.Service1
method=doService1
测试类
package service;
public class ReflectTest {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) throws Exception {
//从spring.txt中获取类名称和方法名称
File springConfigFile = new File("H:\\reflect-demo\\src\\spring.txt");
Properties springConfig= new Properties();
springConfig.load(new FileInputStream(springConfigFile));
String className = (String) springConfig.get("class");
String methodName = (String) springConfig.get("method");
//根据类名称获取类对象
Class clazz = Class.forName(className);
//根据方法名称,获取方法对象
Method m = clazz.getMethod(methodName);
//获取构造器
Constructor c = clazz.getConstructor();
//根据构造器,实例化出对象
Object service = c.newInstance();
//调用对象的指定方法
m.invoke(service);
}
}
例子4:反射的作用2(越过泛型检查)
泛型是在编译期间起作用的。在编译后的.class文件中是没有泛型的。所有比如T或者E类型啊,本质都是通过Object处理的。所以可以通过使用反射来越过泛型。
以下的代码,通过反射,越过了泛型检查,来让String类型的ArrayList成功添加了int类型变量。
package test;
public class GenericityTest {
public static void main(String[] args) throws Exception{
ArrayList<String> list = new ArrayList<>();
list.add("this");
list.add("is");
// strList.add(5);报错
/********** 越过泛型检查 **************/
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = list.getClass();
//获取add()方法
Method m = listClass.getMethod("add", Object.class);//参数是Object类型
//调用add()方法
m.invoke(list, 5);
//遍历集合
for(Object obj : list){
System.out.println(obj);
}
}
}