Java反射及使用

一,概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

以下主要介绍通过反射获取私有的和公共的构造方法、成员变量、方法;

二,获取任意类的字节码对象的几种方式

Class类:
Class 类的实例对象表示正在运行的 Java 应用程序中的类和接口;也就是jvm中有很多的实例,每个类都有唯一的字节码对象(Class对象);通过唯一的字节码对象就可以反射任意一个类中的构造,成员变量和方法;
Class 类没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机自动构造的;我们不需要创建,JVM已经帮我们创建了;
Class 对象用于提供类本身的信息,比如有几种构造方法, 有多少属性,有哪些普通方法;

三种方式获取的字节码对象是同一个;


private static void demo1() throws ClassNotFoundException {
        //第一种
	Class clazz = Class.forName("com.ang.Teacher");
        //第二种
	Class clazz2 = Teacher.class;
	Teacher t = new Teacher();
        //第三种
	Class clazz3 = t.getClass();
		
	System.out.println(clazz == clazz2);
	System.out.println(clazz2 == clazz3);
}

被反射的对象:根据具体情况修改构造类型(私有的:private和公共的:public),各种情况都有注释;

public class Teacher {
	
	private String name;
	private int age;
	
	
//	private Teacher(){
//		
//	}
	
		
	public Teacher(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Teacher [name=" + name + ", age=" + age + "]";
	}

    private int add(int a,int b){
		return a+b;
	}

}

三,获取有参,无参构造

获取构造方法步骤

1,首先获取要反射类的字节码对象(Class)

2,通过字节码对象获取有参或无参构造器Constructor 

  • 获取public类型有参构造
private static void demo2() throws ClassNotFoundException,
			NoSuchMethodException, InstantiationException,
			IllegalAccessException, InvocationTargetException {
	Class clazz = Class.forName("com.ang.Teacher");
	//通过Teacher空参构造创建对象,如果没用空参构造就会报错;而且必须是public类型的空参构造方法;
	//Teacher t = (Teacher) clazz.newInstance(); //如果没有空参构造或者空参构造是私有的不能使用此方法获取对象
	//通过有参构造获取对象,有参构造必须是public类型的
	Constructor c = clazz.getConstructor(String.class,int.class); //获取有参构造器
	Teacher t = (Teacher) c.newInstance("李四",50);//通过有参构造创建对象
	System.out.println(t);
}
  • 获取public类型的无参构造 
private static void demo3() throws ClassNotFoundException,
			NoSuchMethodException, InstantiationException,
			IllegalAccessException, InvocationTargetException {
	Class clazz = Class.forName("com.ang.Teacher");
	Constructor c = clazz.getConstructor();//获取public类型的空参构造
	Teacher t = (Teacher) c.newInstance();//通过空参构造获取对象
	t.setAge(40);
	System.out.println(t);
}
  • 获取私有(private)有参构造
private static void demo6() throws ClassNotFoundException,
			NoSuchMethodException, InstantiationException,
			IllegalAccessException, InvocationTargetException {
	Class clazz = Class.forName("com.ang.Teacher");
	Constructor c = clazz.getDeclaredConstructor(String.class,int.class);//获取私有有参构造
	c.setAccessible(true);//去除私有权限
	Teacher t = (Teacher) c.newInstance("小高",50);
	System.out.println(t);
}

四,通过反射获取对象

步骤

1,首先获取要反射类的字节码对象(Class)

2,通过字节码对象的newInstanc()方法获取实例对象;(注意:需要类的无参构造是public或者默认生成的)

  • public构造方法
//通过空参构造获取对象
private static void demo5() throws ClassNotFoundException,
			InstantiationException, IllegalAccessException {
	Class clazz = Class.forName("com.ang.Teacher");
	//空参构造必须是public类型的
	Teacher t = (Teacher) clazz.newInstance();
	t.setName("赵敏");
	System.out.println(t);
}
  •  非public类型构造方法
private static void demo6() throws ClassNotFoundException,
			NoSuchMethodException, InstantiationException,
			IllegalAccessException, InvocationTargetException {
	Class clazz = Class.forName("com.ang.Teacher");
	Constructor c = clazz.getDeclaredConstructor(); //获取私有构造,方法参数是可变的,所以有参构造直接传入参数类型的字节码对象;
	c.setAccessible(true);  //去除私有权限
	Teacher t = (Teacher) c.newInstance();
	t.setAge(100);
	System.out.println(t);
}

五,获取私有成员变量(属性值)并修改和使用

步骤

1,首先获取要反射类的字节码对象(Class)

2,根据反射创建实例

3,通过字节码对象中的getField()或者getDeclaredField()方法获取Field(提供有关类或接口的单个成员变量的信息)

4,通过Field就可以修改和使用成员变量了

  • pubic类型的有参构造是,获取私有成员变量
private static void demo4() throws ClassNotFoundException,
			NoSuchMethodException, InstantiationException,
			IllegalAccessException, InvocationTargetException,
			NoSuchFieldException {
	Class clazz = Class.forName("com.ang.Teacher");
	//通过有参构造获取对象,有参构造必须为public类型
	Constructor c = clazz.getConstructor(String.class,int.class);//字节码阶段,构造方法的参数只能是字节码对象;
	Teacher t = (Teacher) c.newInstance("王艳",100); //创建实例对象
	Field field = clazz.getDeclaredField("name");//获取私有成员变量
	field.setAccessible(true);//去除私有权限
	field.set(t, "李代理"); //修改私有成员变量的值
	System.out.println(t);
}

六,获取私有方法

private static void demo7() throws ClassNotFoundException,
			NoSuchMethodException, InstantiationException,
			IllegalAccessException, InvocationTargetException {
	Class clazz = Class.forName("com.ang.Teacher");
		
		
	Constructor c = clazz.getDeclaredConstructor(); //获取私有无参构造
	c.setAccessible(true); //去除私有权限
	Teacher t = (Teacher) c.newInstance();//通过私有构造获取实例
		
	Method method = clazz.getDeclaredMethod("add", int.class,int.class);//字节码阶段反射有参方法是,需传入参数类型的字节码
	method.setAccessible(true);  //去除私有权限
	int num = (Integer) method.invoke(t, 20,8);
	System.out.println(num);
}

反射常用的几个类:

1,Class:反射的入口,类的实例表示正在运行的 Java 应用程序中的类和接口。

2,Constructor :提供关于类的单个构造方法的信息以及对它的访问权限。

3,Field :提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

4,Method :提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。

 

七,案例

1,通过反射越过泛型检查

private static void demo() throws NoSuchMethodException,
			IllegalAccessException, InvocationTargetException {
		List list = new ArrayList();
		list.add(11);
		list.add(22);
		
		Class clazz = list.getClass();
		Method m = clazz.getMethod("add", Object.class);
		m.invoke(list, "哈喽");
		
		System.out.println(list);
}

打印输出:

[11, 22, 哈喽]  //List泛型指定的是Integer,结果通过反射存入了String

2,通过反射写一个通用的设置某个对象的某个属性为指定的值

package com.ang;

import java.lang.reflect.Field;

public class Tool {
	
	
	public void setProperty(Object obj,String propertyName,Object values) throws Exception{
		Class clazz = obj.getClass();
		Field f = clazz.getDeclaredField(propertyName); //暴力反射获取属性

		f.setAccessible(true);                          //去除私有权限
		
		f.set(obj, values);   //修改属性值
	}
}

 

private static void demos() throws Exception {
		Student s = new Student("张无忌", 40);
		System.out.println(s);
		
		Tool t = new Tool();
		
		t.setProperty(s, "name", "赵敏");
        System.out.println(s);
	}

打印输出:可以看到,把同一个对象中的属性值修改了

Student [name=张无忌, age=40]
Student [name=赵敏, age=40]

package com.ang;

public class Student {
	private String name;
	private int age;
			
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

3, 写一个Properties格式的配置文件,配置指定类的完整名称。再写一个程序,读取这个Properties配置文件,获得类的完整名称并加载这个类,用反射的方式运行run方法。

指定的类:

package com.ang;
public class Animal {
	public void run(){
		System.out.println("欢迎来到动物世界。。。");
	}
}

properties:name.properties配置文件内容

com.ang.Animal

 测试类:

public class Test {

	public static void main(String[] args) {
		try {
			BufferedReader br = new BufferedReader(new FileReader("name.properties"));
			Class clz = Class.forName(br.readLine());
			Animal a= (Animal) clz.newInstance();
			a.run();
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
}

运行结果:

欢迎来到动物世界。。。

你可能感兴趣的:(JavaSE)