Java類文件編譯後,會在類中添加一個靜態屬性,這個屬性就是Class類的實例,用於描述類型信息。描述類信息的架構圖如:
Class對象提供了一組方法,讓我們可以方便獲取類的屬性,方法,構造方法等信息,並且用Field,Method,Constructor類來描述,可以用這些類來分析類型信息和運行類型信息來進行一些動態操作,例如反射,動態代理,依賴注入/控制反轉等
Class類
Classl類的對象用來表示運行時的類或接口信息,可以通過Class對象獲取類名,父類信息,還可以通過Class類獲取該類的屬性,方法,構造方法等
1. 通過.class屬性
2. 通過getClass()方法
3. 通過forName()方法
package xls;
public class ClassTest {
public void print(String str){
System.out.println("Hello "+str);
}
}
package xls;
public class ClassTestMain {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//通過.class屬性
Class<ClassTest> clazz1 = ClassTest.class;
System.out.println("通過.class屬性: "+clazz1.getCanonicalName());
//通過getClass()方法
ClassTest classTest = new ClassTest();
Class<? extends ClassTest> clazz2 = classTest.getClass();
System.out.println("通過getClass()方法: "+clazz2.getCanonicalName());
//通過forName(className)方法 className爲完整名字
Class<?> clazz3 = Class.forName("xls.ClassTest");
System.out.println("通過forName()方法: "+clazz3.getCanonicalName());
//用三種方法獲取的Class對象實例化ClassTest
ClassTest classTest1 = clazz1.newInstance();
classTest1.print("classTest1");
ClassTest classTest2 = clazz2.newInstance();
classTest2.print("classTest2");
ClassTest classTest3 = (ClassTest) clazz3.newInstance();
classTest3.print("classTest3");
}
}
運行結果:
獲取Constructor對象
有四種方式獲取構造方法的Constructor對象
1. getConstructor(Class,parameterTypes…)用於獲取指定參數類型的Constructor對象
但是不能獲取私有的構造方法的對象
2. getDeclaredConstructor(Class,paramerterTypes..) 用於獲取指定參數類型的Constructor對象
但是能獲取公有和私有的構造方法的Constructor對象
3. getConstructors()用於獲取所有公有構造方法描述對象,若沒有共有構造函數時返回長度爲0的數組
4. getDeclaredConstructors()用於獲取所有公有和被保護的構造方法描述對象
package xls;
import java.lang.reflect.Constructor;
public class ConstructorTest {
/**
* 構造函數發生重載時,下方的無參構造方法被註釋掉的話,則變成沒有無參構造方法,
* 當使用Constructor constructor1 = clazz1.getConstructor();就會報錯
* 因爲這種情況並沒有無參構造方法
* 但是當沒有寫構造方法時,則會有一個默認的無參構造方法使用
* Constructor constructor1 = clazz1.getConstructor();則沒有問題
*/
public ConstructorTest(){}
public ConstructorTest(String name){}
public ConstructorTest(String name,int age){}
protected ConstructorTest(int age){}
private ConstructorTest(double salary){}
}
package xls;
import java.lang.reflect.Constructor;
public class ConstructorTestMain {
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
/**
* 1.getConstructor(Class...parameterTypes)用於獲取指定參數類型的Constructor對象
* 但是不能獲取私有的構造方法的對象
*
* 2.getDeclaredConstructor(Class...paramerterTypes) 用於獲取指定參數類型的Constructor對象
* 但是能獲取公有和私有的構造方法的Constructor對象
*/
Class clazz1 = ConstructorTest.class;//有三種方式獲取Class對象
//獲取無參構造方法,要注意有沒有無參構造方法
Constructor constructor1 = clazz1.getConstructor();
System.out.println(constructor1.toString());
//獲取指定參數類型的Constructor對象
Constructor constructor2= clazz1.getConstructor(String.class);
System.out.println(constructor2.toString());
Constructor constructor3= clazz1.getConstructor(String.class,int.class);
System.out.println(constructor3.toString());
//下方的代碼會報錯,因爲getConstructor()不能獲取私有屬性的
//Constructor constructor4= clazz1.getConstructor(int.class);
//System.out.println(constructor4.toString());
//Constructor constructor5= clazz1.getConstructor(double.class);
//System.out.println(constructor5.toString());
System.out.println("<===============2===============>");
//獲取指定類型的被保護的構造方法的對象
Constructor constructor6 = clazz1.getDeclaredConstructor(int.class);
System.out.println(constructor6.toString());
Constructor constructor7 = clazz1.getDeclaredConstructor(double.class);
System.out.println(constructor7.toString());
System.out.println("<===============3===============>");
/**
* 3.getConstructors()用於獲取所有公有構造方法描述對象,若沒有共有構造函數時返回長度爲0的數組
* 4.getDeclaredConstructors()用於獲取所有公有和被保護的構造方法描述對象
*/
Constructor[] constructor8 = clazz1.getConstructors();
for(Constructor c :constructor8){
System.out.println(c.toString());
}
System.out.println("<===============4===============>");
Constructor[] constructor9 = clazz1.getDeclaredConstructors();
for(Constructor c :constructor9){
System.out.println(c.toString());
}
}
}
運行結果:
獲取Method對象
Method的對象用於描述類的單個方法(不包構造方法),可以通過它來獲取方法的訪問權限,參數類型,返回值的類型等信息,還可以根據Method的對象來動態執行。
package xls;
public class MethodTest {
public void hello(){}
private void student(String name){}
public void teacher(String name){
System.out.println("Hello "+name);
}
public final void school(){}
}
package xls;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MethodMain {
public static void main(String[] args) throws ClassNotFoundException,
NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
/**
* 獲取Method的四種方法與獲取Constructor的四種方法相似,用法相似
* 1.getMthod(String name,Class...parameterTypes)用於獲取指定名稱和參數類型的公有方法的描述對象,
* ,不能用來獲取私有和被保護的對象
* 2.getDeclaredMethod(String name,Class...parameterTypes)作用和第一個相同,
* 但可以獲取來獲取非公有的方法描述對象
*/
Class<?> clazz = Class.forName("xls.MethodTest");
Method method1 = clazz.getMethod("hello", null);
Method method3 = clazz.getMethod("teacher", String.class);
// Method method2 = clazz.getMethod("student", String.class);
Method method4 = clazz.getMethod("school", null);
System.out.println("<===================1===================>");
System.out.println(method1.toString());
// System.out.println(method2.toString());
System.out.println(method3.toString());
System.out.println(method4.toString());
Method method5 = clazz.getDeclaredMethod("hello", null);
Method method6 = clazz.getDeclaredMethod("teacher", String.class);
Method method7 = clazz.getDeclaredMethod("student", String.class);
Method method8 = clazz.getDeclaredMethod("school", null);
System.out.println("<===================2===================>");
System.out.println(method5.toString());
System.out.println(method6.toString());
System.out.println(method7.toString());
System.out.println(method8.toString());
/**
* 3.getMethods()獲取公有的方法描述對象列表,還包括繼承父類或接口的方法描述對象,
* 4.getDeclaredMethods()獲取公有和非公有的所有方法描述對象列表,但只能獲取
* 自己定義的方法描述對象
*/
Method[] method9 = clazz.getMethods();
System.out.println("<===================3===================>");
for(Method m : method9){
System.out.println(m.toString());
}
Method[] method10 = clazz.getDeclaredMethods();
System.out.println("<===================4===================>");
for(Method m : method10){
System.out.println(m.toString());
}
/**
* 獲取Method的對象,可以獲取它的方法名,返回類型,執行方法
*/
System.out.println("<======================================>");
System.out.println(method3.getName());//方法名
System.out.println(method3.getReturnType());//返回類型
//還可以通過Method的invoke(Object obj,Object...args)執行方法
//首先需要通過Class對象實例化一個對象
MethodTest metodTest = (MethodTest) clazz.newInstance();
//前面我們已經獲取了方法的對象,我們要執行teacher(String name)方法的話,可以用method3
method3.invoke(metodTest, "wto");//執行teacher方法打印出“Hello wto”
//注意我想要獲取非公有方法對象的方法名,返回類型,不能直接獲取,要先修改訪問權限
//method3.setAccessible(true);Method的對象可使用setAccessible修改訪問權限true爲可訪問
//因爲Method,Contructor,Field都實現了AccessibleObject的接口,所以Constructor和Field
//可以通過.setAccessible(true);來修改訪問權限
}
}
運行結果:
獲取Field對象
package xls;
public class FieldTest {
public String name = "luxi";
public int num =1202020;
private int age;
private double salary;
public String sit;
}
package xls;
import java.lang.reflect.Field;
public class FieldTestMain {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException,
SecurityException, InstantiationException, IllegalAccessException {
/**
* Class提供4種方式來獲取Field對象
* 這四種方式前面提到獲取Constructor和Method的4種方式類似
* 1.getField(String name) 獲取指定的公有的屬性,name爲屬性名
* 2.getDeclaredField(String name) 獲取指定的屬性包括非公有屬性,但必須定義在類內部
*/
System.out.println("========================1===================");
Class clazz = Class.forName("xls.FieldTest");
//使用getField(String name)獲取
Field field1 = clazz.getField("name");//獲取FieldTest名爲name的屬性對象
//獲取到屬性對象後,可以獲取屬性的類型,值,屬性名
System.out.println(field1.getType());//獲取屬性類型
//獲取屬性的值,必須要傳入一個實例化的對象,只有在實例化對象纔會有值
FieldTest obj = (FieldTest) clazz.newInstance();
System.out.println(field1.get(obj));
//修改屬性的值,不是通過類中的方法賦值,與Method中的invoke(obj,value)執行方法不同
field1.set(obj, "lcy");//把name的值修改爲lcy
System.out.println(field1.get(obj));
System.out.println("========================2===================");
//使用getDeclaredField()可以獲取非公有的屬性值
Field field2 = clazz.getDeclaredField("age");//獲取FieldTest名爲name的屬性對象
//獲取到屬性對象後,可以獲取屬性的類型,值,屬性名,但是非公有
System.out.println(field2.getType());//獲取屬性類型
//獲取屬性的值,必須要傳入一個實例化的對象,只有在實例化對象纔會有值
FieldTest obj1 = (FieldTest) clazz.newInstance();
field2.setAccessible(true);//修改訪問權限
System.out.println(field2.get(obj));
//修改屬性的值,不是通過類中的方法賦值,與Method中的invoke(obj,value)執行方法不同
field2.set(obj1, 3);//把name的值修改爲lcy
System.out.println(field2.get(obj1));
/**
* 3.getField()獲取所有公有屬性的對象
* 4.getDeclaredFields()獲取所有的屬性對象,包括非公有屬性
*/
System.out.println("========================3===================");
Field[] field3 = clazz.getFields();
for(Field f : field3){
System.out.println(f.toString());
}
System.out.println("========================4===================");
Field[] field4 = clazz.getDeclaredFields();
for(Field f : field4){
System.out.println(f.toString());
}
}
}
運行結果
用反射機制編寫通用的Excel導入導出
反射機制
反射機制是指在運行狀態中,對任意的一個類,都能知道這個類的所有屬性和方法;對任意的一個對象,都能調用它的任意一個方法,修改它的任意屬性,這種動態獲取的信息以及動態調用對象成員的功能就是java的反射機制。
編寫通用的Excel導入導出
假設當一個圖書關係管理系統項目中,需要導出圖書,成員等信息時,我們不可能爲每一個需要導出的對象都寫一個導出導入Excel的代碼,這會有很多重複性的代碼,也沒有必要,因此可以寫一個通用的Excel導入導出。我的思路是將要導出的數據封裝在每一個對象當中,因而我們就可以對對象進行操作,利用反射的機制在代碼運行時動態的對這些對象進行操作。
package xls;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;
import jxl.write.Label;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
public class Xls {
/**
* @param arrayList 把要導出的數據封裝到一個個對象中,把對象存儲到ArrayList中
* @param path 文件的路徑
* @param list 第一行爲數據的名稱
*/
public static void out(ArrayList<?> arrayList,String path,ArrayList<String> list) throws
IllegalArgumentException, IllegalAccessException, IOException, WriteException{
//打開文件
WritableWorkbook book = Workbook.createWorkbook(new File(path));
//創建選項卡
WritableSheet sheet = book.createSheet("sheet1", 0);
//excel中第一行爲數據名稱
for(int i=0;i<list.size();i++){
Label label = new Label(i,0,(String) list.get(i));
sheet.addCell(label);
}
//j爲list的元素個數,數據從第2行還是,第一行爲數據的名稱
for(int j = 1;j<=arrayList.size();j++){
//根據實例對象獲取該對象的Class對象,下表從0開始j-1
Class<? extends Object> clazz = arrayList.get(j-1).getClass();
//獲取所有屬性值,公有屬性和私有屬性
Field[] fields = clazz.getDeclaredFields();
int i=0;
for(Field ff : fields){
//私有屬性不能被訪問和修改,要進行權限的修改
ff.setAccessible(true);
/**
* ff.getType()獲取屬性類型
* ff.getName()獲取屬性名
* ff.get()獲取屬性值
* System.out.println(ff.getType()+" "+ff.getName()+" "+ff.get(list.get(j)));
*/
//Label(0(代表列),1(行), "hello") 代表第二行第一個的值爲hello
Label label = new Label(i++,j, String.valueOf(ff.get(arrayList.get(j-1))));
sheet.addCell(label);
}
}
book.write();
book.close();
}
/**
* @param clazz Class的對象
* @param path 文件路徑
* @return ArrayList
*/
public static ArrayList<?> in(Class<?> clazz,String path) throws BiffException, IOException, NoSuchMethodException,
SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{
ArrayList<Object> list = new ArrayList<Object>();
Workbook book = Workbook.getWorkbook(new File(path));
//獲取第一個選項卡
Sheet sheet = book.getSheet(0);
//根據Class的對象獲取所有屬性
Field[] fields = clazz.getDeclaredFields();
//獲取行數,每一行爲每一個對象的數據
int row = sheet.getRows();
for(int j=1;j<row;j++){
int i = 0;
//實例化一個對象
Object obj = clazz.newInstance();
for(Field ff : fields){
//ff.getName()獲取屬性名,把屬性名的首字母大寫。例如屬性名爲name,那麼它set的方法名爲setName
String methodName = "set"+ff.getName().substring(0, 1).toUpperCase()+ff.getName().substring(1);
//j爲行,i爲列,獲取文件中的值,類型爲String
String value;
Cell cell = sheet.getCell(i++,j);
value = cell.getContents();
//根據實例化的對象的Class對象獲取實例對象的方法 ,getMethod的參數爲要獲取的方法的方法名,和參數的類型也即是要賦值的屬性的類型
Method method = obj.getClass().getMethod(methodName,ff.getType());
//在excel中獲取的值都是String,所以要根據屬性的類型進行值的轉換
if(ff.getType().toString().equals("class java.lang.String")){
//調用實例對象obj的set方法進行對屬性的數值
method.invoke(obj, value);
}else if(ff.getType().toString().equals("int")||ff.getType().toString().equals("Integer")){
method.invoke(obj, Integer.valueOf(value));
}
}
list.add(obj);//把值賦值到對象中,加入到ArrayList中
}
book.close();
return list;
}
}
package xls;
public class Student {
private String name;
private int num;
private String className;
private String age;
private String major;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String toString() {
return "姓名:" + name + " 學號:" + num + " 班級:"
+ className + " 年齡:" + age + " 專業:" + major;
}
}
package xls;
public class Teacher {
private String name;
private String sex;
private String phone;
private int salary;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "姓名:"+name+" "+sex+" 手機:"+phone+" 工資:"+salary;
}
}
測試類
package xls;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import jxl.read.biff.BiffException;
import jxl.write.WriteException;
public class XlsTest {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws IllegalArgumentException,
IllegalAccessException, WriteException, IOException, BiffException,
NoSuchMethodException, SecurityException, InvocationTargetException,
InstantiationException {
//把數據導入excel中
ArrayList<Teacher> arrayList = new ArrayList<Teacher>();
Teacher teacher = new Teacher();
teacher.setName("張海");
teacher.setSex("男");
teacher.setPhone("13058993353");
teacher.setSalary(7000);
arrayList.add(teacher);
Teacher teacher1 = new Teacher();
teacher1.setName("張萍");
teacher1.setSex("女");
teacher1.setPhone("15013983453");
teacher1.setSalary(7340);
arrayList.add(teacher1);
Teacher teacher2 = new Teacher();
teacher2.setName("李依芬");
teacher2.setSex("女");
teacher2.setPhone("13550934483");
teacher2.setSalary(8000);
arrayList.add(teacher2);
ArrayList<String> list = new ArrayList<String>();
list.add("姓名");
list.add("性別");
list.add("手機");
list.add("工資");
Xls.out(arrayList,"f:/teacher.xls",list);
//把f:/teacher2.xls的數據導進來
ArrayList<Teacher> arrayList2 = new ArrayList<Teacher>();
arrayList2 = ((ArrayList<Teacher>) Xls.in(Teacher.class,"f:/teacher2.xls"));
for(Teacher t : arrayList2){
System.out.println(t.toString());
}
System.out.println("===================================================================");
Student student = new Student();
student.setName("詹三");
student.setNum(2013363232);
student.setClassName("3班");
student.setAge("21");
student.setMajor("計算機");
ArrayList<Student> arrayList3 = new ArrayList<Student>();
arrayList3.add(student);
ArrayList<String> list1 = new ArrayList<String>();
list1.add("姓名");
list1.add("學號");
list1.add("班級");
list1.add("年齡");
list1.add("專業");
Xls.out(arrayList3,"f:/student.xls",list1);//導出
//導入
arrayList3 = ((ArrayList<Student>) Xls.in(Student.class,"f:/student.xls"));
for(Student t : arrayList3){
System.out.println(t.toString());
}
}
}
導出到f:/teacher.xls的結果
把f:/teacher.xls表中的數據導入
把f:/student.xls表中的數據導入
導入的結果
深入理解動態代理和深入理解Java Proxy機制點擊這裏查看
——————————————————————————–
關注微信公衆號“閱享屋”可獲取900G IT教學視頻和JAVA、前端電子書籍