您好,登錄后才能下訂單哦!
這篇文章主要介紹“Java和C++的枚舉與反射有什么不同”,在日常操作中,相信很多人在Java和C++的枚舉與反射有什么不同問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java和C++的枚舉與反射有什么不同”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
一、枚舉:
枚舉的是在Java 1.5SE 中開始支持的,以下為Java枚舉的基本概念和應用技巧:
1. 所有的enum對象均是由class對象作為底層支持的,該對象繼承自JDK中的Enum<E>,但是該底層類確實final類,既不能再被其他的類繼承。
2. 枚舉的出現完全替代了原有的"public static final"常量表示法,枚舉以一種更加合理、優雅和安全的方式替換了原有的方案。其最基本的聲明方式如下:
public enum Color { RED, BLUE, BLACK, YELLOW }
3. Enum<E>中構造函數的原型為protected Enum(String name, int ordinal),自定義的枚舉對象會將自身的名字以字符串的形式,同時將自己在整個常量從聲明的順序作為超類構造函數的兩個參數傳給超類并由超類完成必要的初始化,如:RED枚舉常量將調用super("RED",0)。
4. 枚舉中可以定義構造函數、域方法和域字段,但是枚舉中的構造器必須是私有(private)的,如果自定義枚舉中有了自定義的構造函數,那么每個枚舉常量在聲明時必須按照自定義構造函數的規則傳入參數。枚舉對象的構造函數只是在枚舉常量對象聲明的時刻才調用一次,之后再也不能像普通對象那樣通過new的方法創建,見如下代碼:
public enum Size { SMALL(0.8), MEDIUM(1.0), LARGE(1.2); double pricingFactor; private Size(double p) { pricingFactor = p; } }
注:枚舉常量列表必須寫在最前面聲明,否則編譯器報錯。
5. 可以給自定義枚舉添加域方法,見如下代碼:
public enum Size { SMALL(0.8), MEDIUM(1.0), LARGE(1.2); private double pricingFactor; Size(double p) { pricingFactor = p; } public double getPricingFactor() { return pricingFactor; } }
6. 枚舉中常用域方法:
public enum Size{ SMALL, MEDIUM, LARGE; } public static void main(String[] args){ //兩種獲得枚舉類型的方法 Size s1 = Size.SMALL; //valueOf的函數原型為<T extends Enum<T>> T valueOf(Class<T> enumType,String name) Size s2 = Enum.valueOf(Size.class, "SMALL"); //Size(自定義枚舉)的valueOf方法是Java編譯器在生成字節碼的時候自動插入的。 Size s3 = Size.valueOf("MEDIUM");//1 //結果同上,枚舉重載了equals方法 System.out.println("Size.MEDIUM.equals(Enum.valueOf(Size.class, \"MEDIUM\")):"+ Size.MEDIUM.equals(Enum.valueOf(Size.class, "MEDIUM"))); //遍歷枚舉類型中所有的成員,這里應用的Size.values方法和Size.valueOf方法 //一樣均是編譯器在生成字節碼的時候自動插入的。 for(Size s:Size.values()){//2 //ordinal()和name()方法均為Enum提供的方法,返回枚舉常量在聲明時的構 //造函數中自動調用超類構造函數時傳入的自身字符串名和在聲明列表中的序號 System.out.println(s.ordinal()+" "+s.name()+" "+s.toString()); } //compareTo方法缺省比較的是枚舉常量的ordinal()的返回值。 if (s1.compareTo(s3) < 0) System.out.println("Size.SMALL is less than Size.MEDIUM"); }
7. 在枚舉中可以聲明基于特定常量的類主體,見如下代碼:
public enum Size { //Small、ExtraLarge和ExtraExtraLarge均使用自定義的getPricingFactor //方法覆蓋Size提供的缺省getPricingFactor方法。 Small { @Override public double getPricingFactor() { return 0.8; } }, //Medium和Large將使用Size內部缺省實現的getPricingFactor方法。 Medium, Large, ExtraLarge { @Override public double getPricingFactor() { return 1.2; } }, ExtraExtraLarge { @Override public double getPricingFactor() { return 1.2; } }; public double getPricingFactor() { return 1.0; } } public static void main(String args[]) { for (Size s : Size.values()) { double d = s.getPricingFactor(); System.out.println(s + " Size has pricing factor of " + d); } } /* 結果如下: Small Size has pricing factor of 0.8 Medium Size has pricing factor of 1.0 Large Size has pricing factor of 1.0 ExtraLarge Size has pricing factor of 1.2 ExtraExtraLarge Size has pricing factor of 1.2 */
8. 枚舉在switch語句中的用法,見如下代碼:
public enum Color { RED, BLUE, BLACK, YELLOW } public static void main(String[] args) { Color m = Color.BLUE; //case語句中引用枚舉常量時不需要再加上枚舉的類型名了。 switch (m) { case RED: System.out.println("color is red"); break; case BLACK: System.out.println("color is black"); break; case YELLOW: System.out.println("color is yellow"); break; case BLUE: System.out.println("color is blue"); break; default: System.out.println("color is unknown"); break; } }
9. 和枚舉相關的兩個容器EnumMap和EnumSet,聲明和主要用法如下。
EnumMap<K extends Enum<K>, V> EnumSet<E extends Enum<E>> //Code Example 1 public enum State { ON, OFF }; public static void main(String[] args) { //EnumSet的使用 EnumSet stateSet = EnumSet.allOf(State.class); for (State s : stateSet) System.out.println(s); //EnumMap的使用 EnumMap stateMap = new EnumMap(State.class); stateMap.put(State.ON, "is On"); stateMap.put(State.OFF, "is off"); for (State s : State.values()) System.out.println(s.name() + ":" + stateMap.get(s)); } //Code Example 2 public enum Size { Small,Medium,Large } public static void main(String args[]) { Map<Size, Double> map = new EnumMap<Size, Double>(Size.class); map.put(Size.Small, 0.8); map.put(Size.Medium, 1.0); map.put(Size.Large, 1.2); for (Map.Entry<Size, Double> entry : map.entrySet()) helper(entry); } private static void helper(Map.Entry<Size, Double> entry) { System.out.println("Map entry: " + entry); }
10. Java枚舉和C++枚舉的主要區別為兩點,一是C++中的枚舉中只能定義常量,主要用于switch子句,二是C++中的枚舉常量可以直接和數值型變量進行各種數學運算。
二、反射:
1. Java的反射機制主要表現為四點:
1) 在運行中分析類的能力;
2) 在運行中查看對象;
3) 實現數組的操作代碼;
4) 利用Method對象,這個對象很像C++中的函數指針。
注:Java的基于反射的應用主要用于一些工具類庫的開發,在實際的應用程序開發中應用的場景較少。
2. 獲取對象的名稱(字符串形式) vs 通過對象的名稱(字符串形式)創建對象實例,見如下代碼:
public static void main(String args[]) { //1. 通過對象獲取其字符串表示的名稱 Date d = new Date(); //or Class<? extends Date> c1 = d.class; Class<? extends Date> c1 = d.getClass(); String className = c1.getName(); //2. 通過字符串形式的名稱創建類實例。 className = "java.util." + className; try { Class c2 = Class.forName(className); //這里用到的newInstance用于創建c2所表示的對象實例,但是必須要求待創建的類實例 //具有缺省構造函數(無參數),很明顯newInstance調用并未傳入任何參數用于構造對象。 Date d2 = (Date)c2.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }
如果需要通過調用帶有參數的構造函數來創建對象實例,需要使用java.lang.reflect.Constructor對象來完成,見如下代碼:
public static void main(String args[]) { String className = "java.util.Date"; try { Class c2 = Class.forName(className); //找到只接受一個long類型參數的構造器 Constructor cc = c2.getConstructor(long.class); long ll = 45L; //將該Constructor期望的指定類型(long)的參數實例傳入并構造Date對象。 Date dd = (Date)cc.newInstance(ll); System.out.println("Date.toString = " + dd); } catch (Exception e) { e.printStackTrace(); } }
3. 遍歷一個未知類型的所有域、構造方法和域方法,見如下函數原型:
Field[] getFields(); 返回指定對象域字段數組,主要包含該類及其超類的所有公有(public)域。
Field[] getDeclaredFields();返回指定對象域字段數組,主要包含該類自身的所有域,包括private等。
Method[] getMethods(); 返回指定對象域方法數組,主要包含該類及其超類的所有公有(public)域方法。
Method[] getDeclaredMethods();返回指定對象域方法數組,主要包含該類自身的所有域方法,包括private等。
Constructor[] getConstructors(); 返回指定對象構造函數數組,主要包含該類所有公有(public)域構造器。
Constructor[] getDeclaredConstructors();返回指定對象構造函數數組,主要包含該類所有域構造器。
int getModifiers(); 返回一個用于描述構造器、方法或域的修飾符的整型數值,使用Modifier類中的靜態方法可以協助分析這個返回值。
String getName(); 返回一個用于描述構造器、方法和域名的字符串。
Class[] getParameterTypes(); 返回一個用于描述參數類型的Class對象數組。
Class[] getReturnType(); 返回一個用于描述返回值類型的Class對象。
private static void printConstructors(Class c1) { Constructor[] constructors = c1.getDeclaredConstructors(); for (Constructor c : constructors) { String name = c.getName(); System.out.print(" "); String modifiers = Modifier.toString(c.getModifiers()); if (modifiers.length() > 0) System.out.print(modifiers + " "); System.out.print(name + "("); Class[] paramTypes = c.getParameterTypes(); for (int j = 0; j < paramTypes.length; ++j) { if (j > 0) System.out.print(","); System.out.print(paramTypes[j].getName()); } System.out.println(");"); } } private static void printMethods(Class c1) { Method[] methods = c1.getDeclaredMethods(); for (Method m : methods) { Class retType = m.getReturnType(); String name = m.getName(); System.out.print(" "); String modifiers = Modifier.toString(m.getModifiers()); if (modifiers.length() > 0) System.out.print(modifiers + " "); System.out.print(retType.getName() + " " + name + "("); Class[] paramTypes = m.getParameterTypes(); for (int j = 0; j < paramTypes.length; ++j) { if (j > 0) System.out.print(", "); System.out.print(paramTypes[j].getName()); } System.out.println(");"); } } private static void printFields(Class c1) { Field[] fields = c1.getDeclaredFields(); for (Field f : fields) { Class type = f.getType(); String name = f.getName(); System.out.print(" "); String modifiers = Modifier.toString(f.getModifiers()); if (modifiers.length() > 0) System.out.print(modifiers + " "); System.out.println(type.getName() + " " + name + ";"); } } public static void main(String args[]) { String name = "java.lang.Double"; try { Class c1 = Class.forName(name); Class superc1 = c1.getSuperclass(); String modifier = Modifier.toString(c1.getModifiers()); if (modifier.length() > 0) System.out.print(modifier + " "); System.out.print("class " + name); if (superc1 != null && superc1 != Object.class) System.out.print(" extends " + superc1.getName()); System.out.print("\n{\n"); printConstructors(c1); System.out.println(); printMethods(c1); System.out.println(); printFields(c1); System.out.println("}"); } catch (Exception e) { e.printStackTrace(); } } /* 輸出結果如下: public final class java.lang.Double extends java.lang.Number { public java.lang.Double(java.lang.String); public java.lang.Double(double); public boolean equals(java.lang.Object); public java.lang.String toString(); public static java.lang.String toString(double); public int hashCode(); public static native long doubleToRawLongBits(double); public static long doubleToLongBits(double); public static native double longBitsToDouble(long); public int compareTo(java.lang.Double); public volatile int compareTo(java.lang.Object); public byte byteValue(); public short shortValue(); public int intValue(); public long longValue(); public float floatValue(); public double doubleValue(); public static java.lang.Double valueOf(double); public static java.lang.Double valueOf(java.lang.String); public static java.lang.String toHexString(double); public static int compare(double, double); public boolean isNaN(); public static boolean isNaN(double); public boolean isInfinite(); public static boolean isInfinite(double); public static double parseDouble(java.lang.String); public static final double POSITIVE_INFINITY; public static final double NEGATIVE_INFINITY; public static final double NaN; public static final double MAX_VALUE; public static final double MIN_NORMAL; public static final double MIN_VALUE; public static final int MAX_EXPONENT; public static final int MIN_EXPONENT; public static final int SIZE; public static final java.lang.Class TYPE; private final double value; private static final long serialVersionUID; } */
4. 通過反射編寫泛型數組代碼,見如下代碼比較:
static Object[] badArrayGrow(Object[] a) { int newLength = a.length * 11 / 10 + 10; //該對象數組的在創建時是基于Object的,所以返回后, //再裝回其他類型數組時將會拋出ClassCastException的異常。 Object[] newArray = new Object[newLength]; System.arraycopy(a,0,newArray,0,a.length); return newArray; } static Object goodArrayGrow(Object a) {//這里的參數務必為Object,而不是Object[] Class c1 = a.getClass(); if (!c1.isArray()) return null; //這里用于獲取數組成員的類型 Class componentType = c1.getComponentType(); //獲取數組的長度。 int length = Array.getLength(a); int newLength = length * 11 / 10 + 10; //通過數組成員的類型和新的長度值來創建一個和參數類型相同的數組, //并增加他的空間,***再返回。 Object newArray = Array.newInstance(componentType,newLength); System.arraycopy(a,0,newArray,0,length); return newArray; }
5. 在運行時使用反射的對象或動態調用反射之后的方法。
1) 獲取域字段和設置域字段:
public void testField() { Employee harry = new Employee("Harry Hacker",35000,10); Class c1 = harry.getClass(); Field f = c1.getDeclaredField("name"); //由于name字段有可能是Employee類的私有域字段,如果直接調用會致使JVM //拋出安全異常,為了避免該異常的發生,需要調用下面的語句來得以保證。 f.setAccessible(true); Object v = f.get(harry); System.out.println(v); }
2) 通過Method的invoke函數動態調用反射后的方法:
該方式有些類似于C#的委托(delegate)和C++的函數指針。
public int add(int param1, int param2) { return param1 + param2; } public static void main(String[] args) throws Exception { Class classType = MyTest.class; Object myTest = classType.newInstance(); Method addMethod = classType.getMethod("add",int.class,int.class); //如果add為靜態方法,這里的***個參數傳null Object result = addMethod.invoke(myTest, 100,200); System.out.println(result); }
6. C++自身并沒有提供像Java這樣完備的反射機制,只是提供了非常簡單的動態類型信息,如type_info和typeid。然而在一些C++的第三方框架類庫中提供了類似的功能,如MFC、QT。其中MFC是通過宏的方式實現,QT是通過自己的預編譯實現。在目前的主流開發語言中,也只有C#提供的反射機制可以和Java的相提并論。
到此,關于“Java和C++的枚舉與反射有什么不同”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。