在JAVA中,獲取一個類的全部父類是比較簡單的,只需要通過反射(Class的getSuperclass()方法)即可。然而,如果想獲得一個類的所有子類,或者獲得實現某一個接口的所有實現類,相對比較麻煩。
用過Eclipse的開發人員都知道,通過F4鍵或(Ctrl+T組合鍵)可以查到指定類的類層次結構。仔細想一下該快捷鍵的實現原理,或許可以找到一個可行的設計思路。
首先,需要確定一個前提是,尋找所有子類,必須先指定搜索的文件范圍。打個比方,要尋找一個古人的所有后代成員,必須設置查找的地理范圍是在中國內,否則就無從入手。
結合實際的項目部署環境,查找子類的方法需要有兩種方式。第一種,在開發環境,可以直接遍歷指定范圍下的所有源代碼文件,再結合反射的知識;第二種,假設項目已經打成jar包,則只能通過jar包獲得所有類文件。
下面給出具體的代碼實現
1.若是開發環境,則通過遞歸查找指定目錄下的類文件的全路徑,代碼如下
1 /** 2 * 遞歸查找指定目錄下的類文件的全路徑 3 * 4 * @param baseFile 查找文件的入口 5 * @param fileList 保存已經查找到的文件集合 6 */ 7 public static void getSubFileNameList(File baseFile, List<String> fileList) { 8 if (baseFile.isDirectory()) { 9 File[] files = baseFile.listFiles(); 10 for (File tmpFile : files) { 11 getSubFileNameList(tmpFile, fileList); 12 } 13 } 14 String path = baseFile.getPath(); 15 if (path.endsWith(".java")) { 16 String name1 = path.substring(path.indexOf("src") + 4, path.length()); 17 String name2 = name1.replaceAll("\\\\", "."); 18 String name3 = name2.substring(0, name2.lastIndexOf(".java")); 19 fileList.add(name3); 20 } 21 }
獲取根路徑方法 final static File rootFolder = new File(SuperClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
2.若是jar包環境,則可以通過JarFile這個工具類,獲得所有全部類信息
1 /** 2 * 從jar包讀取所有的class文件名 3 */ 4 private static List<String> getClassNameFrom(String jarName) { 5 List<String> fileList = new ArrayList<String>(); 6 try { 7 JarFile jarFile = new JarFile(new File(jarName)); 8 Enumeration<JarEntry> en = jarFile.entries(); // 枚舉獲得JAR文件內的實體,即相對路徑 9 while (en.hasMoreElements()) { 10 String name1 = en.nextElement().getName(); 11 if (!name1.endsWith(".class")) {//不是class文件 12 continue; 13 } 14 String name2 = name1.substring(0, name1.lastIndexOf(".class")); 15 String name3 = name2.replaceAll("/", "."); 16 fileList.add(name3); 17 } 18 } catch (IOException e) { 19 e.printStackTrace(); 20 } 21 22 return fileList; 23 }
3.從前兩步可以得到所有子類或所有接口實現類的類路徑信息,有了類的全路徑,就可以通過反射拿到類的信息,用來判斷是否滿足條件
1 /** 2 * 判斷一個類是否繼承某個父類或實現某個接口 3 */ 4 public static boolean isChildClass(String className, Class parentClazz) { 5 if (className == null) return false; 6 7 Class clazz = null; 8 try { 9 clazz = Class.forName(className); 10 if (Modifier.isAbstract(clazz.getModifiers())) {//抽象類忽略 11 return false; 12 } 13 if (Modifier.isInterface(clazz.getModifiers())) {//接口忽略 14 return false; 15 } 16 } catch (Exception e) { 17 e.printStackTrace(); 18 return false; 19 } 20 return parentClazz.isAssignableFrom(clazz); 21 22 }
4.寫幾個簡單的測試類,用來說明問題
1 package bean; 2 3 public abstract class Animal { 4 public abstract void eat(); 5 public abstract void walk(); 6 }
1 package bean; 2 3 public class Cat extends Animal{ 4 5 @Override 6 public void eat() { 7 System.err.println("小貓吃東西"); 8 } 9 10 @Override 11 public void walk() { 12 System.err.println("小貓走路"); 13 } 14 15 }
1 package bean; 2 3 public class Dog extends Animal{ 4 5 @Override 6 public void eat() { 7 System.err.println("小狗吃東西"); 8 } 9 10 @Override 11 public void walk() { 12 System.err.println("小狗走路"); 13 } 14 15 }
1 package bean; 2 3 public class Person { 4 private String name; 5 private int age; 6 7 public Person(){ 8 9 } 10 public Person(String name, int age) { 11 super(); 12 this.name = name; 13 this.age = age; 14 } 15 16 public void sayHello(){ 17 System.err.println("hello,i am " + this.name); 18 } 19 }
5.入口方法,打印輸出結果(jar包可直接使用Eclipse導出可執行jar文件)
1 List<String> fileList = new ArrayList<String>(); 2 3 File baseFile = new File(getSrcPath() + File.separator + "src" + File.separator + "bean"); 4 if(baseFile.exists()){//開發環境,讀取源文件 5 getSubFileNameList(baseFile,fileList); 6 }else{//jar包環境 7 fileList=getClassNameFrom("server.jar"); 8 } 9 System.err.println("Animal類的所有子類有"); 10 for(String name:fileList){ 11 if(isChildClass(name,Animal.class)) 12 System.err.println(name); 13 }
