Java和C++在细节上的差异(四)

  接Java和C++在细节上的差异(三)

  4、内部类:Java中内部类可以为私有内部类,既只有外部类可以访问该内部类,而Java外部类的可见性只有包可见和public两种。C++中的内部类比较类似于Java中的静态内部类,只是一种作用域限制的行为,以下为Java非静态内部类的说明:

  1)内部类可以访问外部类的所有域成员和域字段,这也同样包括私有的字段和成员。

  2)Java的编译器在构造外部类调用内部类构造方法时,自动将外部类的this变量作为一个隐式参数传给了内部类的构造函数,内部类则在构造函数中保留了this变量的引用,该行为为编译器隐式行为。

  1publicclassEmployee {2publicclassInnerClass {3bool test() {4//这里的_jobYears为外部类域字段。5return_jobYears>10;6}7}89publicEmployee(intjobYears,String name) {10_name=name;11_jobYears=jobYears;12_salary=0;13}1415publicvoidraiseSalary() {16//编译器的会将以下构造隐式替换为InnerClass inner = new InnerClass(this);17//因为Java在为其编译的时候发现InnerClass为非静态内部类,则自动添加了以下构造:18//public InnerClass(Employee e)19InnerClass inner=newInnerClass();20if(test())21_salary+=1000;22}23privateString _name;24privateint_jobYears;25privateint_salary;26}

  注:针对以上事例,内部类InnerClass可以通过Employee.this._jobYears的全称来显式的代替_jobYears > 10 中的_jobYears。反过来在raiseSalary方法中可以通过this.new InnerClass()语法格式更加明确的创建InnerClass的对象。

  1publicclassEmployee {2publicclassInnerClass {3bool test() {4//这里的_jobYears为外部类域字段。5returnEmployee.this._jobYears>10;6}7}89publicEmployee(intjobYears,String name) {10_name=name;11_jobYears=jobYears;12_salary=0;13}1415publicvoidraiseSalary() {16//这里也可以不使用this作为内部该内部类对象的外部类对象17//引用,可以根据需要替换为其他外部类对象的引用,如:18//Employee other = new Employee();19//InnerClass innser = other.new InnerClass();20InnerClass inner=this.newInnerClass();21if(test())22_salary+=1000;23}24……25}

  注:在外部类的作用域之外调用public内部类的语法为 OutClass.InnerClass。

  3)局部内部类的可见范围仅仅限于声明该局部类的函数内部,见如下代码:

  1 public void start() {2 class TimePrinter implements ActionListener {3 public void actionPerformed(ActionEvent e) {4 Date now = new Date();5 System.out.println(“At the tone,the time is ” + now);6 //beep为外部类的域字段7 if (beep)8 Tookkit.getDefaultToolkit().beep();9 }10 }11 ActionListener l = new TimePrinter();12 new Timer(interval,l).start();13 }

  局部类同样可以访问函数内部的局部变量,但是要求该变量必须是final的。

  1 public void start(final bool beep) {2 class TimePrinter implements ActionListener {3 public void actionPerformed(ActionEvent e) {4 Date now = new Date();5 System.out.println(“At the tone,the time is ” + now);6 //beep为外部函数的局部变量。7 if (beep)8 Tookkit.getDefaultToolkit().beep();9 }10 }11 ActionListener l = new TimePrinter();12 new Timer(interval,l).start();13 }

  为了规避局部类只能访问final局部变量的限制,既一次赋值之后不能再被重新赋值。但是我们可以通过数组的方式进行巧妙的规避,在下例中数组counter对象本身是final的,因此他不可以被重新赋值,然而其引用的数组元素则可以被重新赋值,见下例:

  1 public void test() {2 final int[] counter = new int[1];3 for (int i = 0; i < dates.length; ++i) {4 dates[i] = new Date() {5 public int compareTo(Date other) {6 //这里如果counter不是数组,而是被定义为final int counter,7 //则会导致编译失败。8 counter[0]++;9 return pareTo(other);10 }11 }12 }13 }

  C++中同样可以做到这些,其规则和Java的主要差异为C++的内部类无法直接访问外部类的任何成员。

  1 class OuterClass {2 public:3 void testOuter() {4 class FunctionInnerClass {5 public:6 void test() {7 printf(“This is FunctionInnerClass.\n”);8 }9 };10 FunctionInnerClass innerClass;11 innerClass.test();12 }13 };14 15 int main()16 {17 OuterClass outer;18 outer.testOuter();19 return 0;20 }

  4)匿名内部类,其基本规则和局部内部类相似,差别在于该内部类不能有声明构造函数,这主要是因为Java要求类的构造函数和类名相同,而匿名内部类自身没有类名,因此在new新对象的时候,传入的构造函数参数为超类的构造函数参数。C++中不支持匿名类。见下例:

  1 public void start(final bool beep) {2 ActionListener l = new ActionListener() {3 public void actionPerformed(ActionEvent e) {4 Date now = new Date();5 System.out.println(“At the tone,the time is ” + now);6 //beep为外部函数的局部变量。7 if (beep)8 Tookkit.getDefaultToolkit().beep();9 }10 }11 new Timer(interval,l).start();12 }

  5)静态内部类,其功能和C++中的嵌套类非常相似,但是和Java自身的非静态内部类之间还是存在一些差异,如静态内部类不能直接访问外围类的对象引用域字段,但是可以访问外部类的static域字段(包括private)。在Java中只有内部类可以被定义为static的,外围类是不可以这样定义的。

  1 public class TestMain {2 private static boolean classField = false;3 private boolean objectField = false;4 static class InnerClass {5 public void test() {6 //这里由于classField是静态域字段,所以静态内部类可以直接访问,7 //但是对于objectField对象域字段而言,由于静态内部类中没有包含8 //外部类的引用,因此不能直接访问objectField.9 if (classField) 10 System.out.println(“Hello.”);11 }12 }13 14 public static void main(String[] args) {15 classField = true;16 new InnerClass().test();17 }18 }

  以下示例中的内部类只能是静态内部类,因为该外部类的静态方法在返回内部类的实例时,无法将一个外部类的对象引用传递给该内部类,因为必须要求该内部类为静态内部类,否则将会报编译错误。

  1 public class TestMain {2 static class InnerClass {3 public void test() {4 System.out.println(“Hello.\n”);5 }6 }7 8 private static InnerClass createInnerClass() {9 return new InnerClass();10 }11 public static void main(String[] args) {12 createInnerClass().test();13 }14 }

  如果InnerClass不是静态内部类,则需要将上例改写为:

  1 public class TestMain {2 class InnerClass {3 public void test() {4 System.out.println(“Hello.\n”);5 }6 }7 8 private static InnerClass createInnerClass() {9 //为了确保InnerClass可以得到外部类的对象引用。10 return new TestMain().new InnerClass();11 }12 public static void main(String[] args) {13 createInnerClass().test();14 }15 }

  6)代理类:通过以下代码step by step解释代理类的机制

  1 import java.lang.reflect.InvocationHandler;2 import java.lang.reflect.Proxy;3 import java.util.Arrays;4 import java.util.Random;5 6 public class TestMain {7 public static void main(String[] args) {8 Object[] elements = new Object[1000];9 for (int i = 0; i < elements.length; ++i) {10 Integer v = i + 1;11 //h(调用处理接口)是代理类的核心处理单元。由于代理类对象只是包含了InvocationHandler12 //这样一个对象实例,并且是存放于超类Proxy中的,而实际的被代理实例必须存放于InvocationHandler13 //的实现类中,如这里的Integer对象v。其中的核心代理代码也是在InvocationHandler子类的14 //invoke方法中完成的。15 InvocationHandler h = new TraceHandler(v);16 //1. 第一个参数表示ClassLoader,这里使用缺省加载器,因此传入null即可。17 //2. 第二个参数表示该代理类需要implement的接口数组(Java中可以实现多个接口)。18 //3. 调用处理器接口,是代理类如果实现代理的核心,后面会介绍该类。19 //4. 将该代理类作为Integer的代理存入数组。20 elements[i] = Proxy.newProxyInstance(null, new Class[] {Comparable.class}, h);21 }22 Integer key = new Random().nextInt(elements.length) + 1;23 //1. 由于代理类也都实现Comparable接口,因此可以用于Arrays.binarySearch中。24 //2. 对代理类进行二分查找的比较时,将会直接调用代理类的compareTo方法。25 //3. 该自动生成的Proxy的子类,其中的compareTo方法会将所有外部调用的信息,连同26 // 方法名一并传给其内部调用处理器对象的invoke方法,并调用该方法(invoke).27 //4. 这里Proxy子类会将所有实例化时指定接口(Comparable)的方法(compareTo),以及28 // Object中toString、equals和hashCode方法的调用都会传递给调用处理器的invoke方法。29 //5. 因此在输出结果中不仅可以看到compareTo方法的调用被打印出,toString也可打印。30 int result = Arrays.binarySearch(elements, key);31 if (result >= 0)32 System.out.println(elements[result]);33 }34 }35 36 class TraceHandler implements InvocationHandler {37 //由于Proxy的子类是动态生成的,其具体的实现也是编译器动态生成后传给JVM的。38 //因此这里是整个代理机制中唯一存放被代理对象的地方。39 public TraceHandler(Object t) {40 target = t;41 }42 43 //在此例中,该方法是被Comparable接口中的compareTo方法调用的,该实现逻辑是位于该44 //动态生成的Proxy子类中,如 45 //public MyProxy extends Proxy implements Comparable {46 // int compareTo(Object other) { h.invoke(…); }47 @Override48 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {49 //打印出实际被调用方法的名称和参数值。50 System.out.print(target);51 System.out.print(“.” + method.getName() + “(“);52 if (args != null) {53 for (int i = 0; i < args.length; ++i) {54 System.out.print(args[i]);55 if (i < args.length – 1)56 System.out.print(“, “);57 }58 }59 System.out.println(“)”);60 //交给被代理类做实际的比较。61 return method.invoke(target, args);62 }63 private Object target = null;64 }65 /* 输出结果如下:66 pareTo(128)67 pareTo(128)68 pareTo(128)69 pareTo(128)70 pareTo(128)71 pareTo(128)72 pareTo(128)73 pareTo(128)74 128.toString()75 128 */

  相关链接:

  Java和C++在细节上的差异(一)

  Java和C++在细节上的差异(二)

  Java和C++在细节上的差异(三)

思念带着一种默默地忧伤,

Java和C++在细节上的差异(四)

相关文章:

你感兴趣的文章:

标签云: