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<Integer> 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();
		}
	}
	
}

運行結果:

歡迎來到動物世界。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章