Java類型信息與用反射機制編寫通用的Excel導入導出

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、前端電子書籍
這裏寫圖片描述

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