Java和C++在細(xì)節(jié)上的差異:枚舉與反射
繼上篇文章:Java和C++在細(xì)節(jié)上的差異:程序設(shè)計(jì)結(jié)構(gòu)
四、枚舉:
枚舉的是在Java 1.5SE 中開始支持的,以下為Java枚舉的基本概念和應(yīng)用技巧:
1. 所有的enum對象均是由class對象作為底層支持的,該對象繼承自JDK中的Enum<E>,但是該底層類確實(shí)final類,既不能再被其他的類繼承。
2. 枚舉的出現(xiàn)完全替代了原有的"public static final"常量表示法,枚舉以一種更加合理、優(yōu)雅和安全的方式替換了原有的方案。其最基本的聲明方式如下:
- public enum Color {
- RED, BLUE, BLACK, YELLOW
- }
3. Enum<E>中構(gòu)造函數(shù)的原型為protected Enum(String name, int ordinal),自定義的枚舉對象會將自身的名字以字符串的形式,同時(shí)將自己在整個(gè)常量從聲明的順序作為超類構(gòu)造函數(shù)的兩個(gè)參數(shù)傳給超類并由超類完成必要的初始化,如:RED枚舉常量將調(diào)用super("RED",0)。
4. 枚舉中可以定義構(gòu)造函數(shù)、域方法和域字段,但是枚舉中的構(gòu)造器必須是私有(private)的,如果自定義枚舉中有了自定義的構(gòu)造函數(shù),那么每個(gè)枚舉常量在聲明時(shí)必須按照自定義構(gòu)造函數(shù)的規(guī)則傳入?yún)?shù)。枚舉對象的構(gòu)造函數(shù)只是在枚舉常量對象聲明的時(shí)刻才調(diào)用一次,之后再也不能像普通對象那樣通過new的方法創(chuàng)建,見如下代碼:
- public enum Size {
- SMALL(0.8),
- MEDIUM(1.0),
- LARGE(1.2);
- double pricingFactor;
- private Size(double p) {
- pricingFactor = p;
- }
- }
注:枚舉常量列表必須寫在最前面聲明,否則編譯器報(bào)錯(cuò)。
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的函數(shù)原型為<T extends Enum<T>> T valueOf(Class<T> enumType,String name)
- Size s2 = Enum.valueOf(Size.class, "SMALL");
- //Size(自定義枚舉)的valueOf方法是Java編譯器在生成字節(jié)碼的時(shí)候自動(dòng)插入的。
- Size s3 = Size.valueOf("MEDIUM");//1
- //結(jié)果同上,枚舉重載了equals方法
- System.out.println("Size.MEDIUM.equals(Enum.valueOf(Size.class, \"MEDIUM\")):"+
- Size.MEDIUM.equals(Enum.valueOf(Size.class, "MEDIUM")));
- //遍歷枚舉類型中所有的成員,這里應(yīng)用的Size.values方法和Size.valueOf方法
- //一樣均是編譯器在生成字節(jié)碼的時(shí)候自動(dòng)插入的。
- for(Size s:Size.values()){//2
- //ordinal()和name()方法均為Enum提供的方法,返回枚舉常量在聲明時(shí)的構(gòu)
- //造函數(shù)中自動(dòng)調(diào)用超類構(gòu)造函數(shù)時(shí)傳入的自身字符串名和在聲明列表中的序號
- 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內(nèi)部缺省實(shí)現(xiàn)的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);
- }
- }
- /* 結(jié)果如下:
- 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語句中引用枚舉常量時(shí)不需要再加上枚舉的類型名了。
- 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. 和枚舉相關(guān)的兩個(gè)容器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++枚舉的主要區(qū)別為兩點(diǎn),一是C++中的枚舉中只能定義常量,主要用于switch子句,二是C++中的枚舉常量可以直接和數(shù)值型變量進(jìn)行各種數(shù)學(xué)運(yùn)算。
#p#
五、反射:
1. Java的反射機(jī)制主要表現(xiàn)為四點(diǎn):
1) 在運(yùn)行中分析類的能力;
2) 在運(yùn)行中查看對象;
3) 實(shí)現(xiàn)數(shù)組的操作代碼;
4) 利用Method對象,這個(gè)對象很像C++中的函數(shù)指針。
注:Java的基于反射的應(yīng)用主要用于一些工具類庫的開發(fā),在實(shí)際的應(yīng)用程序開發(fā)中應(yīng)用的場景較少。
2. 獲取對象的名稱(字符串形式) vs 通過對象的名稱(字符串形式)創(chuàng)建對象實(shí)例,見如下代碼:
- 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. 通過字符串形式的名稱創(chuàng)建類實(shí)例。
- className = "java.util." + className;
- try {
- Class c2 = Class.forName(className);
- //這里用到的newInstance用于創(chuàng)建c2所表示的對象實(shí)例,但是必須要求待創(chuàng)建的類實(shí)例
- //具有缺省構(gòu)造函數(shù)(無參數(shù)),很明顯newInstance調(diào)用并未傳入任何參數(shù)用于構(gòu)造對象。
- Date d2 = (Date)c2.newInstance();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
如果需要通過調(diào)用帶有參數(shù)的構(gòu)造函數(shù)來創(chuàng)建對象實(shí)例,需要使用java.lang.reflect.Constructor對象來完成,見如下代碼:
- public static void main(String args[]) {
- String className = "java.util.Date";
- try {
- Class c2 = Class.forName(className);
- //找到只接受一個(gè)long類型參數(shù)的構(gòu)造器
- Constructor cc = c2.getConstructor(long.class);
- long ll = 45L;
- //將該Constructor期望的指定類型(long)的參數(shù)實(shí)例傳入并構(gòu)造Date對象。
- Date dd = (Date)cc.newInstance(ll);
- System.out.println("Date.toString = " + dd);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
3. 遍歷一個(gè)未知類型的所有域、構(gòu)造方法和域方法,見如下函數(shù)原型:
Field[] getFields(); 返回指定對象域字段數(shù)組,主要包含該類及其超類的所有公有(public)域。
Field[] getDeclaredFields();返回指定對象域字段數(shù)組,主要包含該類自身的所有域,包括private等。
Method[] getMethods(); 返回指定對象域方法數(shù)組,主要包含該類及其超類的所有公有(public)域方法。
Method[] getDeclaredMethods();返回指定對象域方法數(shù)組,主要包含該類自身的所有域方法,包括private等。
Constructor[] getConstructors(); 返回指定對象構(gòu)造函數(shù)數(shù)組,主要包含該類所有公有(public)域構(gòu)造器。
Constructor[] getDeclaredConstructors();返回指定對象構(gòu)造函數(shù)數(shù)組,主要包含該類所有域構(gòu)造器。
int getModifiers(); 返回一個(gè)用于描述構(gòu)造器、方法或域的修飾符的整型數(shù)值,使用Modifier類中的靜態(tài)方法可以協(xié)助分析這個(gè)返回值。
String getName(); 返回一個(gè)用于描述構(gòu)造器、方法和域名的字符串。
Class[] getParameterTypes(); 返回一個(gè)用于描述參數(shù)類型的Class對象數(shù)組。
Class[] getReturnType(); 返回一個(gè)用于描述返回值類型的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();
- }
- }
- /* 輸出結(jié)果如下:
- 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. 通過反射編寫泛型數(shù)組代碼,見如下代碼比較:
- static Object[] badArrayGrow(Object[] a) {
- int newLength = a.length * 11 / 10 + 10;
- //該對象數(shù)組的在創(chuàng)建時(shí)是基于Object的,所以返回后,
- //再裝回其他類型數(shù)組時(shí)將會拋出ClassCastException的異常。
- Object[] newArray = new Object[newLength];
- System.arraycopy(a,0,newArray,0,a.length);
- return newArray;
- }
- static Object goodArrayGrow(Object a) {//這里的參數(shù)務(wù)必為Object,而不是Object[]
- Class c1 = a.getClass();
- if (!c1.isArray())
- return null;
- //這里用于獲取數(shù)組成員的類型
- Class componentType = c1.getComponentType();
- //獲取數(shù)組的長度。
- int length = Array.getLength(a);
- int newLength = length * 11 / 10 + 10;
- //通過數(shù)組成員的類型和新的長度值來創(chuàng)建一個(gè)和參數(shù)類型相同的數(shù)組,
- //并增加他的空間,***再返回。
- Object newArray = Array.newInstance(componentType,newLength);
- System.arraycopy(a,0,newArray,0,length);
- return newArray;
- }
5. 在運(yùn)行時(shí)使用反射的對象或動(dòng)態(tài)調(diào)用反射之后的方法。
1) 獲取域字段和設(shè)置域字段:
- public void testField() {
- Employee harry = new Employee("Harry Hacker",35000,10);
- Class c1 = harry.getClass();
- Field f = c1.getDeclaredField("name");
- //由于name字段有可能是Employee類的私有域字段,如果直接調(diào)用會致使JVM
- //拋出安全異常,為了避免該異常的發(fā)生,需要調(diào)用下面的語句來得以保證。
- f.setAccessible(true);
- Object v = f.get(harry);
- System.out.println(v);
- }
2) 通過Method的invoke函數(shù)動(dòng)態(tài)調(diào)用反射后的方法:
該方式有些類似于C#的委托(delegate)和C++的函數(shù)指針。
- 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為靜態(tài)方法,這里的***個(gè)參數(shù)傳null
- Object result = addMethod.invoke(myTest, 100,200);
- System.out.println(result);
- }
6. C++自身并沒有提供像Java這樣完備的反射機(jī)制,只是提供了非常簡單的動(dòng)態(tài)類型信息,如type_info和typeid。然而在一些C++的第三方框架類庫中提供了類似的功能,如MFC、QT。其中MFC是通過宏的方式實(shí)現(xiàn),QT是通過自己的預(yù)編譯實(shí)現(xiàn)。在目前的主流開發(fā)語言中,也只有C#提供的反射機(jī)制可以和Java的相提并論。
原文鏈接:http://www.cnblogs.com/stephen-liu74/archive/2011/08/07/2129804.html
【系列文章】