文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java和C++的枚举与反射有什么不同

2023-06-17 12:08

关注

这篇文章主要介绍“Java和C++的枚举与反射有什么不同”,在日常操作中,相信很多人在Java和C++的枚举与反射有什么不同问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java和C++的枚举与反射有什么不同”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一、枚举:

枚举的是在Java 1.5SE 中开始支持的,以下为Java枚举的基本概念和应用技巧:

   所有的enum对象均是由class对象作为底层支持的,该对象继承自JDK中的Enum<E>,但是该底层类确实final类,既不能再被其他的类继承。

   枚举的出现完全替代了原有的"public static final"常量表示法,枚举以一种更加合理、优雅和安全的方式替换了原有的方案。其最基本的声明方式如下:

public enum Color { RED, BLUE, BLACK, YELLOW }

   Enum<E>中构造函数的原型为protected Enum(String name, int ordinal),自定义的枚举对象会将自身的名字以字符串的形式,同时将自己在整个常量从声明的顺序作为超类构造函数的两个参数传给超类并由超类完成必要的初始化,如:RED枚举常量将调用super("RED",0)。

   枚举中可以定义构造函数、域方法和域字段,但是枚举中的构造器必须是私有(private)的,如果自定义枚举中有了自定义的构造函数,那么每个枚举常量在声明时必须按照自定义构造函数的规则传入参数。枚举对象的构造函数只是在枚举常量对象声明的时刻才调用一次,之后再也不能像普通对象那样通过new的方法创建,见如下代码:

public enum Size { SMALL(0.8), MEDIUM(1.0), LARGE(1.2); double pricingFactor; private Size(double p) { pricingFactor = p; } }

注:枚举常量列表必须写在最前面声明,否则编译器报错。

   可以给自定义枚举添加域方法,见如下代码:

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; } }

   枚举中常用域方法:

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"); }

   在枚举中可以声明基于特定常量的类主体,见如下代码:

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); } } 

   枚举在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; } }

   和枚举相关的两个容器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); }

   Java枚举和C++枚举的主要区别为两点,一是C++中的枚举中只能定义常量,主要用于switch子句,二是C++中的枚举常量可以直接和数值型变量进行各种数学运算。

二、反射:

   Java的反射机制主要表现为四点:

1)    在运行中分析类的能力;

2)    在运行中查看对象;

3)    实现数组的操作代码;

4)    利用Method对象,这个对象很像C++中的函数指针。

注:Java的基于反射的应用主要用于一些工具类库的开发,在实际的应用程序开发中应用的场景较少。

   获取对象的名称(字符串形式) 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(); } }

   遍历一个未知类型的所有域、构造方法和域方法,见如下函数原型:

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(); } } 

   通过反射编写泛型数组代码,见如下代码比较:

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; }

   在运行时使用反射的对象或动态调用反射之后的方法。

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);  }

   C++自身并没有提供像Java这样完备的反射机制,只是提供了非常简单的动态类型信息,如type_info和typeid。然而在一些C++的第三方框架类库中提供了类似的功能,如MFC、QT。其中MFC是通过宏的方式实现,QT是通过自己的预编译实现。在目前的主流开发语言中,也只有C#提供的反射机制可以和Java的相提并论。

到此,关于“Java和C++的枚举与反射有什么不同”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯