java系列(四)反射和Class类

一、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);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值