JAVA反射(高新技术)

JAVA反射

一、知识准备

现在我们首先来看三个问题

1.什么是Class 类

类描述:描述JAVA类,众多的人可以用person类来表示,众多的JAVA类可以用一个类来描述,这个类就是Class。

 2.怎么得到Class的对象。

如得到Person对象,可以这样做: Person p=new Person();

现在我们要知道一个概念:Class的实例对象就是内存中的一份字节码,或者说内存中的一份字节码对应Class的一个实例对象。现在解释一下什么是字节码?

我们常说JAVA编译后,会变成.class文件,而这里所说的字节码就是一个类编译而成的二进制代码,比如:


要得到Person对象时,是要先得到Person类的一份字节码(如果JVM中没有,需要先加载,如果有,可以直接返回);

要得到Set对象时,是要先得到Set类的一份字节码;

要得到Math对象时,是要先得到Math类的一份字节码。

注意:每个类的字节码,在内存中只有一份,每一份字节码就是一个Class的实例对象,比如要得到Person的字节码,可以有下面三种写法

A.   Class p=Person.class;  “Person.class”就代表Person的字节码,“Person.class”的所属类型的Class

B.   Class.forName("类的全路径名");

C.   对象名.getClass();

例子:
public class ReflectTest1 {

		 public static void main(String[] args) throws ClassNotFoundException {
						
			String str1="abc";
						
			//第一种方式:用类名.class
		        Class class1=String.class;
						
			//第二种方式:用String的对象获取String的字节码
			Class class2=str1.getClass();
						
			 //第二种方式:用String的对象获取String的字节码
			Class class3=Class.forName("java.lang.String");
						   				  
			System.out.println("结果如下");
			System.out.println(class1==class2);
			System.out.println(class1==class3);
		}
}
这个类的结果输出是两个true,说明每个类的字节码,在内存中只有一份,无论你用三种方式的哪一种方式去取,得到的都是同一份字节码。</span>
3.基本类型的字节码
      Java中有八种基本类型,所对应的字节码是基本数据类型的字节码,在程序中,可以种Class的isPrimitive()方法来判断它是不是基本数据类型的字节码。
值得一提的是,int[].class这不是基本数据类型的字节码,因为这是数组类型,当调用isArray的时候返回true。

二、反射深入分析

1.什么是反射?

定义:反射就是把JAVA类中的各种成分映射成映射成相应的JAVA类。

这句话怎么理解呢?

我们知道,一个类中可以有成员变量,成员方法,构造方法等信息,这些信息就用相应的类的实例对象来表示。

在反射中,有一些类用来表示反射以后类中的成分,比如:Filed,Method,Constructor,Package。

比如:System类中,有System.exit(),System.getProperties(),不管你的类中有什么方法,都可以用反射中Method来表示。

我们知道,一个类中可以有成员变量,成员方法,构造方法等信息,这些信息就用相应的类的实例对象来表示。

在反射中,有一些类用来表示反射以后类中的成分,比如:Filed,Method,Constructor,Package。


2.类构造函数的反射  Constructor

 Constructor代表某一个类的构造方法 

那么怎么得到一个类的构造方法?
可以先拿到这个类的CLass实例对象(这个类的字节码),实例对象中有两个方法。
得到类的一个构造函数的方法

public Constructor<T> getConstructor(Class<?>... parameterTypes)  throws NoSuchMethodException,SecurityException

通过参数类型,得到想要的构造函数,因为接收的是可变参数,所以可以传多个,比如:想得到String的String(StringBuffer buf)的构造方法,可以这么写:StringString.class.getConstructor(StringBuffer.class);

例子:
public class ConstructorReflect {
		public static void main(String[] args) throws Exception {		
			//通过字节码的方法得到Constructor对象
			 Constructor<String> constructor = String.class.getConstructor(StringBuffer.class);
			 String instance = constructor.newInstance(new StringBuffer("abc"));
			System.out.println(instance);
		}
}


注意在上面程序有两个地方用到StringBuffer,第一个StringBuffer指定得到哪个构造方法,第二个StringBuffer表示在用constructor实例化对象的时候
还要传递一个StringBuffer的对象,两个必须完全一致。
如果你这么写 String.class.newInstance();那就是得到这个类中不带任何参数的构造方法。
得到一个类的所有构造函数的方法 
public Constructor<?>[] getConstructors() throws SecurityException


3.类成员变量的反射 Filed

怎么通过反射得到类中字段的值呢?
例子1:下面的程序可以通过反射得到公有成员y,和私有成员x。

class Point{
	private int x;
	public int y;
	public Point(int x, int y) {
		super();
		this.x = x;
		this.y = y;
		}
}
public class FiledReflect {
	public static void main(String[] args) throws Exception {
				
		Point p1=new Point(5, 39);
		Point p2=new Point(3, 9);
					</span>
		<span style="font-size:14px;">//对公有成员变量
		Field fieldY = p1.getClass().getField("y");	
		int y = (int) fieldY.get(p1);//取对应p1对象的y字段的值,必须要有对象。
		System.out.println(y);
				
		//对私有成员变量,可以进行暴力反射
		Field fieldX = p1.getClass().getDeclaredField("x");
		fieldX.setAccessible(true);//暴力反射
		int x = (int) fieldX.get(p1);//取对应p1对象的x字段的值
		System.out.println(x);				
		}
}


例子2:需求:对一个Person类中的成员变量,把所有String类型的变量值字母a改成A;

import java.lang.reflect.Field;
class Person{
	private String name;
	private int age ;
	private String nickName;
	public Person(String name, int age, String nickName) {
		 super();
		this.name = name;
		this.age = age;
		this.nickName = nickName;
		}
					
		@Override
		public String toString() {
			 return "Person [name=" + name + ", age=" + age + ", nickName="+ nickName + "]";
		}
}
 public class FiledReflect2 {

	public static void main(String[] args) throws Exception {
						
		Person p=new Person("zhangsan", 25, "Amao");		
		//得到字节码,通过字节码得到这个类的所有方法
		Field[] fields = p.getClass().getDeclaredFields();
			for(Field field:fields){
			//因为一个类的字节码在内存中只有一份,所以用==比较更专业,此处用==,不用equals()
				if(field.getType()==String.class){
					field.setAccessible(true);//进行暴力反射
					String oldValue = (String) field.get(p);//获得字段的值
					String newValue =oldValue.replace("a", "A");
					field.set(p, newValue);
					System.out.println(p);
				}
			}
					
		}
}

执行结果是:Person [name=zhAngsAn, age=25, nickName=AmAo]


4.类成员方法的反射 Method

比如:想调用String类中的chatAt(int i)这个方法,该怎么么办呢?
public Method getMethod(String name, Class<?>... parameterTypes)hrows NoSuchMethodException,SecurityException

参数说明:

name:这个表示方法的名字
parameterTypes:这个参数的作用表示调用哪个方法,因为重载的原因,一个类中同名的方法可能不止一个
对于上面的问题,我们可以这样做。
Method  myStrCharAt=String.class.getMethod("charAt",int.class);

例子:

public class MethodReflect {

	public static void main(String[] args) throws Exception{
		String str="abcdef";
				
		Method strMethod = str.getClass().getMethod("charAt", int.class);
				
		//得到方法之后,调用对象str的chatAt方法;
		char result = (char) strMethod.invoke(str, 1);
			
		System.out.println(result);//结果是b
	}
 }

当一个类XXX,我们已经通过反射得到它的方法xxxMethod,那么看下面一行代码
xxxMethod.invoke(null, 1);

这表示不知道谁的xxxMethod方法,因为传递的是null,说明调用的这个方法是属于类的,就是静态方法,所以就不需要传递参数了。

5.数组与反射

数组在 Java 语言中是一种特殊的类类型,一个数组的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的:

import java.lang.reflect.*;  
public class Array1 { 
   public static void main(String args[]) { 
      try { 
           Class cls = Class.forName("java.lang.String"); 
           Object arr = Array.newInstance(cls, 10); 
           Array.set(arr, 5, "this is a test"); 
           String s = (String) Array.get(arr, 5); 
           System.out.println(s); 
      } 
      catch (Throwable e) { 
           System.err.println(e); 
      } 
   } 
}
例中创建了 10 个单位长度的 String 数组,为第 5 个位置的字符串赋了值,最后将这个字符串从数组中取得并打印了出来。


这篇博客上的所写,写的不对不好的地方,还请大家指正,互相学习,共同进步。



  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值