java的反射机制

在最近的学习中,越来越多的接触到了反射机制,找资料学习了一下,将学习过程以及知识点记录下来,方便以后学习

(1)Class类的使用

在面向对象的世界中,万物皆对象。

我们平常定义的类,类中的属性,类中的方法都可以看成是对象,是java.lang.Class类的实例对象

这里先介绍类类型:Class类的对象就是类类型(叫类类型只是为了区别是类的对象还是是Class的对象), 一个类只可能是Class类的一个对象。(这里感觉和单例模式挺相像的)

首先看一段代码:在代码中了解概念:

package com.feng.reflect;

public class ClassDemo1 {

	public static void main(String[] args)
	{
		//我们可以创建一个Student类的对象
		Student student = new Student();
		//Student这个类其实也是一个对象,是java。lang.Class类的实例对象
		//这里介绍三种可以获得类类型的方式
		
		//(1)类名。class,说明任何一个类都有一个隐含的static成员变量
		Class c1 = Student.class;   
		//(2)类对象。getClass()
		Class c2 = student.getClass();
		//(3)Class.forName("全名包含报名");
		try {
			Class c3 = Class.forName("com.feng.reflect.Student");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println(c1 == c2);  //这里是相等的,因为每个类的类类型只能有一个对象
		//其中Class类的对象就叫做类类型,上面定义的c1, c2, c3都是类类型
		
		//我们可以通过类类型来创建类的对象
		try {
			//newInstance调用的是类的无参的构造方法
			Student s = (Student)c1.newInstance();
			s.show();
		} catch (InstantiationException | IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

class Student
{
	public void show()
	{
		System.out.println("我是一个学生");
	}
}
注意的几点:

(1)获取类类型的三种方法

(2)通过类类型创建类对象,创建出来的对象需要强制类型转换

(2)动态加载类

在我们以往的学习中,一般都是用IDE去写程序,直接点击运行,不会去关注编译,运行的问题。但是在这里我们就要分清编译和运行了

首先,我们要区分静态加载类和动态加载类的区别

静态加载类:在编译时就需要加载所有可能使用到的类。(换句话说,如果要用的类没有的话,在编译阶段就会报错),使用new方法都是静态加载

动态加载类:在运行的时候才关注加载那些类,用到哪个类加载哪个类。用不到的类不用加载(换句话说,用不到的类没有也可以)

如果到这里还看不懂,没有关系,看一个例子:

这里我们为了区分编译和运行,不再使用IDE了,而是使用命令行来执行类

在编辑工具中写好程序:

public class Office {

	public static void main(String[] args)
	{
		if(args.equals("Word"))
		{
			Word word = new Word();
			word.start();
		}
		else if(args.equals("Excel"))
		{
			Excel excel = new Excel();
			excel.start();
		}
	}
}

我们现在只有Office这一个类,功能很简单,只要写过程序的人都能看懂,大家也都能知道这个程序时错误的,因为我们没有Word,Excel类,更没有里面的start方法

我们执行命令 javac Office.java  这是对类文件进行编译,new是静态加载,编译时加载所需要的类,因为没有Word,Excel类,所以报错了

但是现在就有问题了,我们真的需要Word,Excel这些类吗,比如我们传参数,传一个Word那我只需要一个Word类就可以了,Excel根据没有用到啊,按常理说我只要有一个Word类就应该让我运行啊。除此之外,如果我有100个类难道我要写上100个else if?????

答案是当然不需要,如果我们改为动态加载,用到哪一个类就加载哪一个类不就得了。

对程序进行修改如下:

public class Office
{
	public static void main(String[] args) 
	{
		try
		{
			//动态加载类
			Class c1  = Class.forName(args[0]);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	
	}
}
这样我们使用命令 javac Office.java编译时是不会报错的。

但是使用命令java Office Word 运行时,会提示找不到Word类。所以动态加载类是在运行时用到了这个类才会去找

创建出Word类,在Office中穿件Word实例,并调用Word的方法

Word类代码

public class Word 
{
	public void start()
	{
		System.out.println("开始写Word。。。");
	}
}
Office类代码

public class Office
{
	public static void main(String[] args) 
	{
		try
		{
			//动态加载类
			Class c1  = Class.forName(args[0]);
			//创建实例
			Word word = (Word)c1.newInstance();
			word.start();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	
	}
}

javac *.java

java Office Word   就可以打印输出结果了

这样编译就运行都可以了。我们用什么类,写什么类就可以。用Word的时候,不需要Excel类的存在

这还是不够完美,如果我想换成构造Excel,那我还要去更改main函数中的代码,很不方便,那怎么办。

这个好解决,我们可以给在Office类中实现的类建立一个统一的接口Tool

代码如下:

public interface Tool
{
	void start();
}
Word ,Excel实现这个接口

public class Word implements Tool
{
	public void start()
	{
		System.out.println("开始写Word。。。");
	}
}
public class Excel implements Tool
{
	public void start()
	{
		System.out.println("开始写Excel。。。");
	}
}
Office类

public class Office
{
	public static void main(String[] args) 
	{
		try
		{
			//动态加载类
			Class c1  = Class.forName(args[0]);
			//创建实例
			Tool tool = (Tool)c1.newInstance();
			tool.start();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	
	}
}
执行结果:

实现了我们要的功能。
(3)获取方法信息
基本的数据类型也有类类型:

看一段简单的代码就知道了:

package com.feng.reflect;

public class ClassDemo2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Class c1 = double.class;
		Class c2 = Double.class;
		System.out.println(c1.getName());
		System.out.println(c2.getSimpleName());
	}

}
如何获取方法信息,直接看代码

package com.feng.reflect;

import java.lang.reflect.Method;

public class ClassUtil {

	/*
	 * 打印对象对应的类的方法
	 */
	public static void printClassMethodMessage(Object object)
	{
		//首先获取类类型
		Class c1 = object.getClass();   //传进来的类型,获取到的是子类的类类型。换句话说会获取到具体类的类类型
		//获取类名
		System.out.println("类名为:"+c1.getSimpleName());
		
		//获取方法对象,Method类
		Method[] methods = c1.getMethods(); //获取的是所有的public的函数,包括继承来的函数
		//Method[] methods = c1.getDeclaredMethods();  //获取的是自己的方法,不管权限
		
		//获取方法的信息
		for(Method m:methods)
		{
			//获取方法的返回值
			Class c2 = m.getReturnType();  //返回的是类类型
			System.out.print(c2.getName()+"(");
			//获取参数类型
			Class[] cs = m.getParameterTypes();
			for(Class c: cs)
			{
				System.out.print(c.getName()+", ");
			}
			System.out.println(")");
		}
		
	}
}
测试类代码如下:

package com.feng.reflect;

public class ClassDemo2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Class c1 = double.class;
		Class c2 = Double.class;
		System.out.println(c1.getName());
		System.out.println(c2.getSimpleName());
	}

}
都是写类类型中的方法,只要了解会用即可。

(4)获取成员变量构造函数信息

获取成员变量的信息如下所示,测试就不写了

/**
	 * 打印成员变量的信息
	 */
	public static void printClassFieldMessage(Object object)
	{
		//获取类对象
		Class c = object.getClass();
		//获取成员变量
		System.out.println("类类型:"+c.getSimpleName());
		Field[] field = c.getDeclaredFields();
		for(Field f: field)
		{
			System.out.println(f.getType()+" "+f.getName());
		}
	}
获取构造函数的信息
/**
	 * 获取构造函数的信息
	 */
	
	public static void printConstructMessage(Object object)
	{
		//获取类对象
		Class c = object.getClass();
		//获取构造函数
		System.out.println("类类型:"+c.getSimpleName());
		Constructor[] cs = c.getDeclaredConstructors();
		for(Constructor c1: cs)
		{
			System.out.print(c.getSimpleName()+ "(");
			//获得参数列表
			Class[] cls = c1.getParameterTypes();
			for(Class cl: cls)
			{
				System.out.print(cl.getSimpleName()+", ");
			}
			System.out.println(")");
		}
	}
都是一样道理的东西,非常简单

(5)方法的反射操作
方法是由方法的名称和方法的参数列表唯一确定的

方法反射的操作 method.invoke(对象,参数)

/**
	 * 获取构造函数的信息
	 */
	
	public static void printConstructMessage(Object object)
	{
		//获取类对象
		Class c = object.getClass();
		//获取构造函数
		System.out.println("类类型:"+c.getSimpleName());
		Constructor[] cs = c.getDeclaredConstructors();
		for(Constructor c1: cs)
		{
			System.out.print(c.getSimpleName()+ "(");
			//获得参数列表
			Class[] cls = c1.getParameterTypes();
			for(Class cl: cls)
			{
				System.out.print(cl.getSimpleName()+", ");
			}
			System.out.println(")");
		}
	}
方法的执行必须和具体的对象相联系的,因为方法输出的值是和对象有关的。

(6)集合范型的实质
先看一段程序:

package com.feng.reflect;

import java.util.ArrayList;

public class ClassDemo5 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ArrayList list = new ArrayList();
		ArrayList<String> list1 = new ArrayList<String>();
		
		Class c1 = list.getClass();
		Class c2 = list1.getClass();
		
		System.out.println(c1 == c2);

	}

}
定义了两个ArrayList对象,这两个对象的类类型是不是一样呢,答案是肯定的,不管定义多少个不同的ArrayList独享,他们的类类型都是一个。

范型在这的作用只是在编译的时候加以控制,在最终的运行的时候其实是没有范型的事情的,范型只是用来防止输入错误的,绕过编译就无效了。

下面看一个例子:

package com.feng.reflect;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class ClassDemo5 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		ArrayList<String> list = new ArrayList<String>();
		list.add("nihao");
		//list.add(20);  //这肯定是错的
		//我们可不可以跳过程序的编译阶段,在list中添加一个整形数据呢,我们使用反射来试一下
		Class c = list.getClass();
		Method m;
		try {
			m = c.getMethod("add", Object.class);
			m.invoke(list,20);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
			
		//不能使用foreach进行遍历
		System.out.println(list);
	}

}
输出结果为:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值