Java和C++在细节上的差异

更新时间:2023-04-19 04:51:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

::

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

1public void foo() {

2int[][] odds = new int[NMAX+1][];

3for (int n = 0; n <= NMAX; ++n)

4 odds[n] = new int[n + 1];

5

6for (int n = 0; n < odds.length; ++n) {

7for (int k = 0; k < odds[n].length; ++k)

8 odds[n][k] = n * k;

9 }

10 }

复制代码

C/C++中对应于Java的不规则二维数组的表示方式。

1void foo() {

2int** odds = new int*[10];

3for (int n = 0; n < 10; ++n) {

4if (n == 0)

5 odds[n] = new int;

6else

7 odds[n] = new int[n + 1];

8 }

9

10for (int n = 0; n < 10; ++n) {

11for (int k = 0; k < n + 1; ++k)

12 odds[n][k] = n * k;

13 }

14//注:C/C++代码部分需要自行释放分配的内存。

15for (int n = 0; n < 10; ++n) {

16if (n == 0)

17 delete odds[n];

18else

19 delete [] odds[n];

20 }

21 delete [] odds;

22 }

复制代码

二、对象与类:

1. Java对象实例的存储方式:

所有的Java对象实例都是通过new的方式创建的,如Employee employee = new Employee()。而此时创建的employee对象实例,实际是指向Employee

一个实例的引用,主要体现为实例之间基于等号的赋值,如:employee = employee2; 赋值后两个变量将指向同一个Employee对象实例。Java处理对象变量的

C++中的引用比较类似,但是还是存在一定的差异,首先C++不存在空引用,既引用变量定义时也必须被同时声明其所引用的对象实例,再者就是引用一旦定义时

后就不能再被重新赋值了。因此这里可以将Java的对象变量看做C++中的对象指针,如:BirthdayDate d; /*Java*/ 等同于BirthdayDate* d; /*C++*/。

与Java对象实例声明的方式相同,C++中的对象指针也是通过new的方式进行初始化的,如BirthdayDate* d = new BirthdayDate. 同样可以将C++中的

针赋值为NULL,也可以将其重新赋值指向另外一个对象实例。与Java相同,通过new操作符创建的对象实例是存储在堆中的,不同的是,Java的对象在创建后,

人员在去关注该对象实例需要合适被释放,所有的操作均有Java虚拟机中提供的垃圾回收机制自动完成。而C++中的该类对象,则需要开发人员通过调用delete

自行完成释放,如果忘记释放将会产生内存泄露。在C++中,不仅可以将对象存储在堆上,同样也可以定义并存储的栈上,如BrithdayDate d; 该对象实例不需要

放,在栈退出时将自动释放该对象的存储空间,同时也会调用该对象的析构函数。

2. Java对象方法的显式参数和隐式参数:

1public class Employee {

2public void raiseSalary(double byPercent) {

3double raise = salary + byPercent / 100;

4 salary += raise;

5 }

6private double salary;

7 }

复制代码

raiseSalary是Employee类的一个成员方法,该方法是由两个参数构成,一个是显式参数byPercent,另一个则是隐式参数this,既raiseSalary方法是实现

为:

1public void raiseSalary(double byPercent) {

2double raise = this.salary + byPercent / 100;

3this.salary += raise;

4 }

复制代码

这里的隐式参数this表示当前调用raiseSalary方法的对象实例的自身,该机制和C++基本相同。

注:静态方法中不存在该特征。

3. Java对象中定义的final实例域,如:public class Employee { ... private final String name; }, 该类型的field必须在对象构造函数中进行初始化

变量将不能再被重新赋值。和final字段相似,C++对象中的const成员变量也必须在对象构造函数的初始化列表中完成赋值任务,在之后的使用中该字段将不会再被

否则会产生编译错误。对于Java的final域而言,以便应用于基本数据类型,如int,double等,或者不可变类型,如String。对于可变类型而言,final修饰符可能

些预料之外的混乱,如private final Date hiredate; 当该field作为某个get方法的返回值返回给调用者之后,final的修饰作用只能保证返回后的date对象不能再

赋值并指向新的对象实例引用,但是可以通过直接修改返回值对象的自身数据来破坏对象的封装性,从而可能造成数据的非法性,或者状态的不一致性。

4. 函数参数传递的方式:传值和传引用。

在Java中调用函数是,参数都是通过传值的方式传递到函数内部,然而根据参数类型的不同,其表现仍然存在一定的差异。主要总结为以下3点:

被调用方法不能修改一个基本数据类型的参数,如:int,double,boolean等,见如下代码:

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

1private static void tripleValue(double x) {

2 x *= 3;

3 System.out.println("End of method: x = " + x);

4 }

5

6public static void testTripleValue() {

7 System.out.println("Test tripleValue");

8double percent = 10;

9 System.out.println("Before: percent = " + percent);

10 tripleValue(percent);

11 System.out.println("After: percent = " + percent);

12 }

13/* 结果如下:

14 Test tripleValue

15 Before: percent = 10.0

16 End of method: x = 30.0

17 After: percent = 10.0 */

复制代码

被调用方法可以改变一个对象参数的状态,见如下代码:

1private static void tripleSalary(Employee x) {

2 x.raiseSalary(200);

3 System.out.println("End of method: salary = " + x.getSalary());

4 }

5

6public static void testTripleSalary() {

7 System.out.println("Test tripleSalary");

8 Employee harry = new Employee("Harry",50000);

9 System.out.println("Before: salary = " + harry.getSalary());

10 tripleSalary(harry);

11 System.out.println("After: salary = " + harry.getSalary());

12 }

13/* 结果如下:

14 Test tripleSalary

15 Before: salary = 50000.0

16 End of method: x = 150000.0

17 After: salary = 150000.0 */

复制代码

被调用方法不能实现让对象参数引用一个新的对象,见如下代码:

1private static void swap(Employee a,Employee b) {

2 Employee temp = x;

3 x = y;

4 y = temp;

5 System.out.println("End of method: x = " + x.getName());

6 System.out.println("End of method: y = " + y.getName());

7 }

8public static void testSwap() {

9 System.out.println("Test Swap");

10 Employee a = new Employee("Alice",70000);

11 Employee b = new Employee("Bob",60000);

12 System.out.println("Before: a = " + a.getName());

13 System.out.println("Before: b = " + b.getName());

14 swap(a,b);

15 System.out.println("After: a = " + a.getName());

16 System.out.println("After: b = " + b.getName());

17 }

18/* 结果如下:

19 Test swap

20 Before: a = Alice

21 Before: b = Bob

22 End of method: x = Bob

23 End of method: y = Alice

24 After: a = Alice

25 After: b = Bob */

复制代码

C++有值调用和引用调用,引用参数标有&符号。如:void tripleValue(double& x)或void swap(Employee& x,Employee& y)方法实现修改他们引用参数

的,既该方法执行完成后,调用函数的参数变量的值将发生改变。

5. 对象的构造和构造函数:

在Java中如果一个class没有定义任何构造函数,Java编译器将自动生成一个缺省的构造函数,没有任何参数,其行为只是按照Java默认的方式初始化该类的所

量,如数值型为0,布尔为false,对象则为null。但是如果该class定义了自己的构造函数,那么缺省构造函数将不会被自动生成,再试图调用自动生成的缺省构造函

导致编译错误。该行为和C++完全一致。但是Java提供了另外一种域变量初始化方式,如下:

1public class Employee {

2 ...

3private String name = ""; //直接赋值

4private int id = assignId();//通过调用域方法完成初始化。

5 }

复制代码

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

在C++中不能直接在类的定义中以任何形式直接初始化成员变量。但是C++提供了在构造函数中以初始化列表的方式完成成员变量对象的初始化,特别是con

员,必须在这里赋值。

通过一个构造器调用另一个构造器从而完成域变量的初始化和部分代码复用。通过this关键字(或称隐式参数)作为函数名,然后传入参数调用你期望的另一个

数,注:this被调用之前不能执行任何其他的code。

1public Employee(double s) {

2//calls Employee(String,double)

3this("Employee #" + nextId,s);

4 ++nextId;

5 }

复制代码

在C++中如果打算完成此功能,必须将构造函数的部分逻辑抽取出来,以便让多个构造函数去调用,然后不同的构造函数之间不能直接调用。

在Java定义的子类中,如果子类的构造函数不是调用父类的缺省构造函数,则需要在子类构造函数的第一行代码中指定欲调用的父类构造函数,该调用需要通

关键字来完成。见如下代码:

1public class MyFirst {

2public static void main(String[] args) {

3 BaseClass bc1 = new SonClass();

4 BaseClass bc2 = new SonClass(5);

5 }

6 }

7

8class BaseClass {

9public BaseClass() {

10 System.out.println("This is BaseClass");

11 }

12

13public BaseClass(int i) {

14 System.out.println("This is BaseClass with i.");

15 }

16 }

17

18class SonClass extends BaseClass {

19public SonClass() {

20 System.out.println("This is SonClass");

21 }

22

23public SonClass(int i) {

24super(5);

25 System.out.println("This is SonClass with i");

26 }

27 }

28/* 结果如下:

29 This is BaseClass

30 This is SonClass

31 This is BaseClass with i.

32 This is SonClass with i */

复制代码

在C++中也可以完成该种类型的指定,但是必须在子类构造函数的初始化列表中完成对父类指定构造函数的调用。

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

1class BaseClass {

2public:

3 BaseClass() {

4 printf("This is BaseClass\n");

5 }

6

7 BaseClass(int i) {

8 printf("This is BaseClass with i\n");

9 }

10 };

11

12class SonClass : public BaseClass {

13public:

14 SonClass() {

15 printf("This is SonClass\n");

16 }

17

18 SonClass(int i) : BaseClass(i) {

19 printf("This is SonClass with i\n");

20 }

21 };

22

23int main()

24 {

25 BaseClass* bc1 = new SonClass;

26 BaseClass* bc2 = new SonClass(5);

27 delete bc1;

28 delete bc2;

29return0;

30 }

31/* 结果如下:

32 This is BaseClass

33 This is SonClass

34 This is BaseClass with i.

35 This is SonClass with i */

复制代码

在Java的域变量初始化方法中存在初始化块的方式,既除声明即初始化、构造函数初始化之外的第三种域变量初始化方式。在一个类的声明中可以存在多个代

要构造类的对象,这些块就会被执行,然后再运行类的构造函数。静态域变量可以在静态初始化块中完成初始化的工作,但是该初始化块只是在类第一次加载时被

次,之后都将不再被执行。见如下代码:

1class Employee {

2public Employee(String n,double s) {

3 name = n;

4 salary = s;

5 }

6

7 ...

8

9private static int nextId;

10private int id;

11private String name;

12private double salary;

13

14//object initialization block.

15 {

16 id = nextId;

17 nextId++;

18 }

19

20//static initialization block.

21static

22 {

23 Random generator = new Random();

24 nextId = generator.nextInt();

25 }

26 }

复制代码

6. C++的对象析构和Java对象的finalize方法:

C++是有显式的析构方法,其中放置一些当对象不再使用时需要执行的清理代码。在析构函数中,最常见的操作时回收分配给对象的存储空间,系统资源等。

自动的垃圾回收器,不需要人工回收内存,所以Java并不支持析构函数。如果打算在Java的代码中完成类似的工作,可以通过为该类添加finalize方法,该方法将会

收集器清除对象之前调用,在实际应用中,不要依赖于使用finalize方法回收任何短缺的资源,这是因为很难知道这个方法什么时候才能调用。如果某个资源确实

用完毕后立刻关闭,那么就需要由人工来管理。可以应用一个类似dispose或close的方法完成相应的清理操作。特别需要说明,如果一个类使用了这样的方法,当

再被使用时一定要调用它。

7. Java的包vs C++的名字空间

他们具有极为相同的只能,即防止名字污染。当一个应用程序中存在多个第三方组件,那么不同组件中命名了相同名称的类将是极为可能发生的,如Java中的

在java.util和java.sql中均存在该名称的类的声明。为了有效的防止名字污染,C++中采用了namespace和using namespace的指令来明确定义某个类具体所位

体位置,Java中则采用了package和import语句。

Java在Java SE5.0 开始,import语句不仅可以导入类,还增加了导入静态方法和静态域的功能。如import static 16436ed2c1c708a1284a442fng.System.*。在完成该静态导入之

就可以在剩下的代码中直接使用System类的静态方法和静态域了,如out.println();exit(0)。该技巧主要用于带有较长名称的常量,如if (d.get(DAY_OF_WEEK

MONDAY) ...,看起来比if (d.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) ...要容易的多。

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

三、继承:

1. Java和C++在对象继承方面的主要差异:

对象的继承性是所有面向对象语言都支持的面向对象特性之一,Java和C++作为两个重要的面向对象开发语言在此方面有着较多的相似性,但是在有些概念的

式上还是存在着一定的差异,先列举如下:

1) 对象继承的关键字,Java中采用extents关键字,如class DeriveClass extends BaseClass, 在C++中则使用(:)冒号表示类之间的继承,如class

DeriveClass : public BaseClass。

2) Java的继承方式中不存在public,protected和private,其表现行为和C++中的public继承完全一致。

3) 在有些情况下,子类中的方法需要显式的调用超类中的方法实现,特别是当子类中也存在同样方法签名的实现时,如果没有明确的指出需要调用超类的方

Java的编译器会将子类当前的方法列为本次调用的候选方法,见如下代码:

1class DeriveClass extends BaseClass {

2public double getSalary() {

3double baseSalary = getSalary();

4return baseSalary + bonus;

5 }

6 }

复制代码

以上代码中的getSalary()方法将会递归的调用其自身,而开发者的实际用意是调用超类中的getSalary方法,由于超类和子类中具有相同签名的该方法,因此

在此时选择了子类中的getSalary。其修改方式如下:

1class DeriveClass extends BaseClass {

2public double getSalary() {

3double baseSalary = super.getSalary();

4return baseSalary + bonus;

5 }

6 }

复制代码

加上关键字super明确的指出要调用超类中的getSalary方法。在C++中的实现方式为BaseClass::getSalary(),既在方法签名的前面加上父类的名字和两个

的冒号(::)。

1class DeriveClass : public BaseClass {

2public:

3double getSalary() {

4double baseSalary = BaseClass::getSalary();

5return baseSalary + bonus;

6 }

7 }

复制代码

4) Java中所有未声明为final的方法都视为可以继承的虚方法。在C++中,尽管没有此类限制,但是在实际的应用中还是存在一些潜在的技巧以达到此效果

C++类中声明的公有成员方法,如果该方法未声明为virtual,既虚函数,则暗示该类的子类实现者不要在子类中覆盖(override)该方法。

5) Java中不支持多重继承,不仅有效的避免了C++因多重继承而带来的一些负面影响,与此同时,在Java中可以通过继承(extends)单个父类和实现

(implements)多个接口的方式更好表达该类设计意愿。

6) Java中如果子类和超类同时包含具有相同签名的公有域方法,那么在子类中将覆盖超类中的域方法。这其中的方法签名只是包括方法名和参数列表,既

数和类型,函数的返回值不包含在方法签名中,但是在Java中针对该种方法覆盖的返回值还是存在一定的限制,既子类中的返回值的类型,或者与超类中该方法的返

型相同,或者为其返回类型的子类。C++中没有此类返回值类型的限制。但是Java的此类限制也会带来一些潜在的迷惑和危险,见如下代码:

1class Employee {

2public Employee[] getBuddies() { ... }

3 }

4

5class Manager extends Employee {

6public Manager[] getBuddies() { ... }

7 }

8

9public static void main(String[] args) {

10 Employee[] m = new Manager().getBuddies();

11//在Java中子类的数组在复制给超类的数组时不需要显式的转换,就像

12 //子类的实例赋值给超类的实例一样,也不需要任何显式的转换。

13 //赋值之后e和m指向相同的内存地址,同样e[0]和m[0]也指向相同的实例。

14 Employee[] e = m;

15//本次赋值合法也不会引发任何异常,但是会导致一个潜在的问题,既

16 //m[0]的对象已经被悄悄的改变了,指向了Employee的另外一个子类。

17 e[0] = new OtherEmployee();

18//此时再调用m[0]中Manager定义的域方法时将会引发Java的运行时异常。

19 m[0].setBonus(1000);

20 }

复制代码

7) Java中的final类,如果某个自定义类型被加入final关键字,则表示该类将不能被继承,否则会直接产生编译错误。在C++中没有特殊的关键字类完成此

制,然而在实际的应用中也同样存在一些潜在的技巧协助开发者来进行此类限制的甄别。如将父类中的析构函数不设置为虚函数,此方法则间接的暗示子类的实现

意,如果仍然继承该父类,那么在实现多态时,如BaseClass* c = new DeriveClass,如果之后需要释放c变量的内存资源时delete c, 此时由于父类中的析构

是虚函数,因此此次调用将只会执行父类的析构函数,而不会调用子类的析构函数,最终导致类分割所带来的一些潜在错误或资源泄漏。

8) 内联方法,在C++中有特殊的关键字inline用于帮助编译器来推断是否需要将该方法编译成内联方法,以提高运行时的效率。在Java中没有此类关键字

过编译器的一连串推演,最终决定该域方法是否可以编译成内联方法,主要候选方法为简短、被频繁调用且没有真正被子类覆盖的域方法。

9) 超类到子类的强制类型转换。在Java中可以通过直接强转的方式来转换,如Manager m = (Manager)e。如果装换失败将会引发运行时异常

ClassCastException,因此很多情况下为了避免此类异常的发生,需要在强转之前先进行判断,如if (e instanceof Manager) { ... }, 如果条件为真,装换将

成。在C++中也可以采用这样的直接强转方法,但是即使类型不匹配程序也不会在强转是引发任何异常,而是在后面针对该变量的使用时才会导致错误的发生。

存在dynamic_cast关键字,如dynamic_cast和dynamic_cast,前者为基于指针的转换,如果转换失败返回变量为NULL,而后者

发异常。

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

10) 抽象类:在Java中如果class被定义为abstract class,该类将不能被实例化,如果子类未能完全实现超类中所有的抽象方法,那么子类也将会被视为

C++中没有特殊的关键字来表示抽象类,而且通过将类中的一个或多个方法定义为纯虚方法来间接实现的,见如下C++代码,其中的first和second均为纯虚方法

法的尾部添加" = 0 "。

1class AbstractClass {

2public:

3virtual void first() = 0;

4virtual void second() = 0;

5virtual void third();

6 }

复制代码

11) protected关键字在Java和C++中针对域方法和域字段的访问方式存在着不同的限制级别,相同之处是protected的方法和字段都可以被子类直接访问

之处是Java中相同包中的类也可以直接他们。C++自身并不存在包的概念,然而即便是相同名字空间内的对象也不能直接访问。

2. Object:

Java是单根结构的框架,所有的对象都是Object的子类,即使在对象声明时没有进行直接的指定,Java的编译器将会自行搞定这些。C++中没有适当的类作

象的根类,然而在有些类库中可以自行定义,如MFC的CObject等。Java的Object中有3个非常重要的方法equals、hashCode和toString。如果子类中重载了他

任意一个方法,同时也建议重载另外两个域方法。

1) equals: 主要用于判定两个对象是否相等。类的实现者可以根据自己的真实逻辑来重新实现该方法,通用实现规则见下例:

1public class Employee {

2//1. 显式参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量。

3public boolean equals(Object otherObject) {

4//2. 检测this与otherObject是否引用同一个对象(一种优化)

5if (this == otherObject)

6return true;

7//3. 检测otherObject是否为null,如果null,则返回false。

8if (otherObject == null)

9return false;

10//4. 比较this与otherObject是否属于同一个类。

11 //如果子类中的equals语义各不相同,使用下面的getClass方式,精确定义类类型。

12if (getClass() != otherObject.getClass())

13return false;

14//如果子类中的equal语义和超类完全相同,可以使用instanceof检测即可。

15 //5. 将otherObject转换为相应的类类型变量

16 Employee other = (Employee)otherObject;

17//6. 现在开始对所有需要比较的域进行比较了。其中使用==比较基本类型,

//使用equals比较对象类型。

18return name.equals(16436ed2c1c708a1284a442f) && salary == other.salary;

19 }

20 }

复制代码

注:数组元素的比较可以调用Arrays.equals方法检测。如果子类中重新定义了equals方法,就要在其中包含调用super.equals(other).

Java在语言规范中给出了自定义equals方法需要遵守的规则:

自反性: 对于任何非空引用x,x.equals(x)应该返回true。

对称性: 对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true。

传递性: 对于任何引用x,y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true。

一致性: 如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果。

对于任意非空引用x,x.equals(null)应该返回false。

2) hashCode: 导出一个经过哈希计算后的整型值,Java对hashCode的缺省实现是返回当前对象的存储地址。一下列出String的hashCode实现方式:

1public int hashCode() {

2int hash = 0;

3for (int i = 0; i < length(); ++i)

4 hash = 31 * hash + charAt(i);

5return hash;

6 }

复制代码

注:自定义类型的equals和hashCode定义必须一致,如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。如果打算实现

的hashCode方法,推荐使用在对象构造初始化后就不会再改变的域字段作为hashCode的计算因子。否则一旦使用可变域资源作为hashCode计算因子的一部分

致一些隐藏的问题。比如当Employee对象实例存入HashMap中,但是使用者在存入集合之后,修改了某个参数hashCode计算的域字段的值,此后再在HashMap

原有对象时由于hashCode已经改变,因此即使该对象已经存入HashMap中,结果是仍然无法找到最初存入的对象了。数组类型的hashCode,可以通过

Arrays.hashCode方法计算得出。

3) toString: Java中比较推荐的实现方式为:

1public String toString() {

2return getClass().getName() +

3 "field1 = " + field1 +

4 "field2 = " + field2;

5 }

复制代码

注:C#的Framework中也存在一个类似的Object对象,作为C#所有对象(包括自定义对象)的唯一根类,其中也有对应的3个方法equals、hashCode和toSt

Effective C#中针对这3个方法提供了一个很好的建议,既如果自定义类重载了这3个方法中任何一个,那么强烈建议该类也重载另外两个域方法。如对equals和

而言,如果x.equals(y)返回true,那么x.toString.equals(y.toString)也将返回true,反之亦然。针对equals和hashCode域方法还有一种推荐的实现方式,PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

3. 包装类和自动打包: 1) 包装器对象均为不可变对象,如String ,既一

旦初始化之后其值将不会再被改变。包装器类是final 类,不能为继承。

2) 自动拆包和打包:Integer n = 3; n++; 在执行n++时,Java 编译器将自动插入一条拆包指令,然后进行自增计算,最后再将结果打入对象包内。

3) 自动打包的规范要求boolean, byte, char <= 127, 和介于-128--127之间的short 和int 被包装到固定的对象中,见如下代码:

4) 打包和拆包过程是编译器行为,不是虚拟机行为,是编译器在生成字节码的时候自动插入的指令。

5) 包装类在容器中的应用。对于Java 提供的泛型容器类其类型参数不能是primitive type ,如int 、float 等,如果确实需要添加类似的数据,需要将相应的包作为容器类型参数,之后在插入原始类型数据,但是在插入过程中Java 的编译器将自动插入打包指令,因此实际插入到容器中的仍然是包装类对象,见如下代码:

4. Java 函数的变参表示方式:

PrintStream printf(String fmt,Object...args),其效果相当于 PrintStream printf(String fmt,Object[] args)。在C++中变参的表示方式为int printf(c char* fmt, ...); 其后的缺省参数需要通过C 语言中提供的宏VA_LIST 来协助完成。

分类: Java 编程

? 博主前一篇:Makefile 的常用技术总结

? 博主后一篇:Java 和C++在细节上的差异(二) Feedback

#1楼 回复 引用 查看

1 public bool equals(Object other) {

2 return toString().equals(other.toString());

3 }

4

5 public int hashCode() {

6 return toString().hashCode();

7 }

复制代码

1 public void test() {

2 Integer a1 = 1000;

3 Ingeger a2 = 1000;

4 if (a1 == a2)

5 System.out.println(

6 "This won't be printed out because they are greater than 127.");

7

8 Integer a3 = 100;

9 Ingeger a4 = 100;

10 if (a3 == a4)

11 System.out.println(

12 "This will be printed out because they are less then 127.");

13 }

复制代码

1 public static void main(String args[]) {

2 ArrayList l = new ArrayList();

3 for (int i = 0; i < 10; ++i)

4 l.add(i);

5

6 for (int i = 0; i < l.size(); ++i) {

7 System.out.printf("The value is %d.\t",l.get(i));

8 System.out.printf("The class name is %s.\n"

9 , l.get(i).getClass().getName());

10 }

11 }

12 /* 结果如下:

13 The value is 0. The class name is 16436ed2c1c708a1284a442fng.Integer.

14 The value is 1. The class name is 16436ed2c1c708a1284a442fng.Integer.

15 The value is 2. The class name is 16436ed2c1c708a1284a442fng.Integer.

16 The value is 3. The class name is 16436ed2c1c708a1284a442fng.Integer.

17 The value is 4. The class name is 16436ed2c1c708a1284a442fng.Integer.

18 The value is 5. The class name is 16436ed2c1c708a1284a442fng.Integer.

19 The value is 6. The class name is 16436ed2c1c708a1284a442fng.Integer.

20 The value is 7. The class name is 16436ed2c1c708a1284a442fng.Integer.

21 The value is 8. The class name is 16436ed2c1c708a1284a442fng.Integer.

22 The value is 9. The class name is 16436ed2c1c708a1284a442fng.Integer.

23 */

复制代码

绿色通道:

好文要顶

关注我收藏该文与我联系+加关注

Stephen_Liu

关注 - 3

粉丝 - 142

(请您对文章做出评

5

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

Ads by Google

马上获得优惠PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

::

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

1public enum Size{

2 SMALL,

3 MEDIUM,

4 LARGE;

5 }

6public static void main(String[] args){

7//两种获得枚举类型的方法

8 Size s1 = Size.SMALL;

9//valueOf的函数原型为> T valueOf(Class enumType,String name)

10 Size s2 = Enum.valueOf(Size.class, "SMALL");

11//Size(自定义枚举)的valueOf方法是Java编译器在生成字节码的时候自动插入的。

12 Size s3 = Size.valueOf("MEDIUM");//1

13

14 //结果同上,枚举重载了equals方法

15 System.out.println("Size.MEDIUM.equals(Enum.valueOf(Size.class, \"MEDIUM\")):"+

16 Size.MEDIUM.equals(Enum.valueOf(Size.class, "MEDIUM")));

17

18//遍历枚举类型中所有的成员,这里应用的Size.values方法和Size.valueOf方法

19 //一样均是编译器在生成字节码的时候自动插入的。

20for(Size s:Size.values()){//2

21 //ordinal()和name()方法均为Enum提供的方法,返回枚举常量在声明时的构

22 //造函数中自动调用超类构造函数时传入的自身字符串名和在声明列表中的序号

23 System.out.println(s.ordinal()+" "+16436ed2c1c708a1284a442f()+" "+s.toString());

24 }

25//compareTo方法缺省比较的是枚举常量的ordinal()的返回值。

26if (16436ed2c1c708a1284a442fpareTo(s3) < 0)

27 System.out.println("Size.SMALL is less than Size.MEDIUM");

28 }

复制代码

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

1public enum Size {

2//Small、ExtraLarge和ExtraExtraLarge均使用自定义的getPricingFactor

3 //方法覆盖Size提供的缺省getPricingFactor方法。

4 Small {

5 @Override

6public double getPricingFactor() {

7return 0.8;

8 }

9 },

10//Medium和Large将使用Size内部缺省实现的getPricingFactor方法。

11 Medium,

12 Large,

13 ExtraLarge {

14 @Override

15public double getPricingFactor() {

16return 1.2;

17 }

18 },

19 ExtraExtraLarge {

20 @Override

21public double getPricingFactor() {

22return 1.2;

23 }

24 };

25public double getPricingFactor() {

26return 1.0;

27 }

28 }

29public static void main(String args[]) {

30for (Size s : Size.values()) {

31double d = s.getPricingFactor();

32 System.out.println(s + " Size has pricing factor of " + d);

33 }

34 }

35/* 结果如下:

36 Small Size has pricing factor of 0.8

37 Medium Size has pricing factor of 1.0

38 Large Size has pricing factor of 1.0

39 ExtraLarge Size has pricing factor of 1.2

40 ExtraExtraLarge Size has pricing factor of 1.2 */

复制代码

8. 枚举在switch语句中的用法,见如下代码:

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

1public enum Color {

2 RED, BLUE, BLACK, YELLOW

3 }

4public static void main(String[] args) {

5 Color m = Color.BLUE;

6//case语句中引用枚举常量时不需要再加上枚举的类型名了。

7switch (m) {

8case RED:

9 System.out.println("color is red");

10break;

11case BLACK:

12 System.out.println("color is black");

13break;

14case YELLOW:

15 System.out.println("color is yellow");

16break;

17case BLUE:

18 System.out.println("color is blue");

19break;

20default:

21 System.out.println("color is unknown");

22break;

23 }

24 }

复制代码

9. 和枚举相关的两个容器EnumMap和EnumSet,声明和主要用法如下。

1 EnumMap, V>

2 EnumSet>

3//Code Example 1

4public enum State {

5 ON, OFF

6 };

7public static void main(String[] args) {

8//EnumSet的使用

9 EnumSet stateSet = EnumSet.allOf(State.class);

10for (State s : stateSet)

11 System.out.println(s);

12

13//EnumMap的使用

14 EnumMap stateMap = new EnumMap(State.class);

15 stateMap.put(State.ON, "is On");

16 stateMap.put(State.OFF, "is off");

17for (State s : State.values())

18 System.out.println(16436ed2c1c708a1284a442f() + ":" + stateMap.get(s));

19 }

20

21//Code Example 2

22public enum Size {

23 Small,Medium,Large

24 }

25public static void main(String args[]) {

26 Map map = new EnumMap(Size.class);

27 map.put(Size.Small, 0.8);

28 map.put(Size.Medium, 1.0);

29 map.put(16436ed2c1c708a1284a442frge, 1.2);

30for (Map.Entry entry : map.entrySet())

31 helper(entry);

32 }

33private static void helper(Map.Entry entry) {

34 System.out.println("Map entry: " + entry);

35 }

复制代码

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

学运算。

五、反射:

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

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

2) 在运行中查看对象;

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

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

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

2. 获取对象的名称(字符串形式) vs 通过对象的名称(字符串形式)创建对象实例,见如下代码:

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

1public static void main(String args[]) {

2//1. 通过对象获取其字符串表示的名称

3 Date d = new Date();

//or Class c1 = d.class;

4 Class c1 = d.getClass();

5 String className = c1.getName();

6

7//2. 通过字符串形式的名称创建类实例。

8 className = "java.util." + className;

9try {

10 Class c2 = Class.forName(className);

11//这里用到的newInstance用于创建c2所表示的对象实例,但是必须要求待创建的类实例

12 //具有缺省构造函数(无参数),很明显newInstance调用并未传入任何参数用于构造对象。

13 Date d2 = (Date)c2.newInstance();

14 } catch (ClassNotFoundException e) {

15 e.printStackTrace();

16 } catch (InstantiationException e) {

17 e.printStackTrace();

18 } catch (IllegalAccessException e) {

19 e.printStackTrace();

20 }

21 }

复制代码

如果需要通过调用带有参数的构造函数来创建对象实例,需要使用16436ed2c1c708a1284a442fng.reflect.Constructor对象来完成,见如下代码:

1public static void main(String args[]) {

2 String className = "java.util.Date";

3try {

4 Class c2 = Class.forName(className);

5//找到只接受一个long类型参数的构造器

6 Constructor cc = c2.getConstructor(long.class);

7long ll = 45L;

8//将该Constructor期望的指定类型(long)的参数实例传入并构造Date对象。

9 Date dd = (Date)cc.newInstance(ll);

10 System.out.println("Date.toString = " + dd);

11 } catch (Exception e) {

12 e.printStackTrace();

13 }

14 }

复制代码

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对象。

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

1private static void printConstructors(Class c1) {

2 Constructor[] constructors = c1.getDeclaredConstructors();

3for (Constructor c : constructors) {

4 String name = c.getName();

5 System.out.print(" ");

6 String modifiers = Modifier.toString(c.getModifiers());

7if (modifiers.length() > 0)

8 System.out.print(modifiers + " ");

9 System.out.print(name + "(");

10

11 Class[] paramTypes = c.getParameterTypes();

12for (int j = 0; j < paramTypes.length; ++j) {

13if (j > 0)

14 System.out.print(",");

15 System.out.print(paramTypes[j].getName());

16 }

17 System.out.println(");");

18 }

19 }

20

21private static void printMethods(Class c1) {

22 Method[] methods = c1.getDeclaredMethods();

23for (Method m : methods) {

24 Class retType = m.getReturnType();

25 String name = m.getName();

26 System.out.print(" ");

27

28 String modifiers = Modifier.toString(m.getModifiers());

29if (modifiers.length() > 0)

30 System.out.print(modifiers + " ");

31 System.out.print(retType.getName() + " " + name + "(");

32 Class[] paramTypes = m.getParameterTypes();

33for (int j = 0; j < paramTypes.length; ++j) {

34if (j > 0)

35 System.out.print(", ");

36 System.out.print(paramTypes[j].getName());

37 }

38 System.out.println(");");

39 }

40 }

41

42private static void printFields(Class c1) {

43 Field[] fields = c1.getDeclaredFields();

44for (Field f : fields) {

45 Class type = f.getType();

46 String name = f.getName();

47 System.out.print(" ");

48 String modifiers = Modifier.toString(f.getModifiers());

49if (modifiers.length() > 0)

50 System.out.print(modifiers + " ");

51 System.out.println(type.getName() + " " + name + ";");

52 }

53 }

54

55public static void main(String args[]) {

56 String name = "16436ed2c1c708a1284a442fng.Double";

57try {

58 Class c1 = Class.forName(name);

59 Class superc1 = c1.getSuperclass();

60 String modifier = Modifier.toString(c1.getModifiers());

61if (modifier.length() > 0)

62 System.out.print(modifier + " ");

63

64 System.out.print("class " + name);

65if (superc1 != null && superc1 != Object.class)

66 System.out.print(" extends " + superc1.getName());

67

68 System.out.print("\n{\n");

69 printConstructors(c1);

70 System.out.println();

71 printMethods(c1);

72 System.out.println();

73 printFields(c1);

74 System.out.println("}");

75 } catch (Exception e) {

76 e.printStackTrace();

77 }

78 }

79/* 输出结果如下:

80 public final class 16436ed2c1c708a1284a442fng.Double extends 16436ed2c1c708a1284a442fng.Number

81 {

82 public 16436ed2c1c708a1284a442fng.Double(16436ed2c1c708a1284a442fng.String);

83 public 16436ed2c1c708a1284a442fng.Double(double);

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

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

5. 在运行时使用反射的对象或动态调用

反射之后的方法。

1) 获取域字段和设置域字段:

2) 通过Method 的invoke 函数动态调用反射后的方法:

该方式有些类似于C#的委托(delegate)和C++的函数指针。

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

分类: Java 编程

? 博主前一篇:Java 和C++在细节上的差异(一)

? 博主后一篇:Java 和C++在细节上的差异(三) 注册用户登录后才能发表评论,请 登录 或 注册,返回博客园首页。

1 static Object[] badArrayGrow(Object[] a) {

2 int newLength = a.length * 11 / 10 + 10;

3 //该对象数组的在创建时是基于Object 的,所以返回后,

4 //再装回其他类型数组时将会抛出ClassCastException 的异常。

5 Object[] newArray = new Object[newLength];

6 System.arraycopy(a,0,newArray,0,a.length);

7 return newArray;

8 }

9

10 static Object goodArrayGrow(Object a) {//这里的参数务必为Object ,而不是Object[]

11 Class c1 = a.getClass();

12 if (!c1.isArray())

13 return null ;

14 //这里用于获取数组成员的类型

15 Class componentType = c1.getComponentType();

16 //获取数组的长度。

17 int length = Array.getLength(a);

18 int newLength = length * 11 / 10 + 10;

19 //通过数组成员的类型和新的长度值来创建一个和参数类型相同的数组,

20 //并增加他的空间,最后再返回。

21 Object newArray = Array.newInstance(componentType,newLength);

22 System.arraycopy(a,0,newArray,0,length);

23 return newArray;

24 }

25

复制代码

1 public void testField() {

2 Employee harry = new Employee("Harry Hacker",35000,10);

3 Class c1 = harry.getClass();

4 Field f = c1.getDeclaredField("name");

5 //由于name 字段有可能是Employee 类的私有域字段,如果直接调用会致使JVM

6 //抛出安全异常,为了避免该异常的发生,需要调用下面的语句来得以保证。

7 f.setAccessible(true );

8 Object v = f.get(harry);

9 System.out.println(v);

10 }

复制代码

1 public int add(int param1, int param2) {

2 return param1 + param2;

3 }

4

5 public static void main(String[] args) throws Exception {

6 Class classType = MyTest.class ;

7 Object myTest = classType.newInstance();

8 Method addMethod = classType.getMethod("add",int .class ,int .class );

9 //如果add 为静态方法,这里的第一个参数传null

10 Object result = addMethod.invoke(myTest, 100,200);

11 System.out.println(result);

12 }

复制代码

绿色通道:

好文要顶

关注我收藏该文与我联系+加关注

Stephen_Liu

关注 - 3

粉丝 - 142

(请您对文章做出评

2

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

Ads by Google

Ads by Google PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

::

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

1//该实现类使用浅拷贝已经可以满足其需要了

2public class implements Cloneable {

3//这里已经提升了clone方法的级别为public。

4public Employee clone() throws CloneNotSupportedException {

5return (Employee)super.clone();

6 }

7 }

8//深拷贝clone方法,必须clone对象内部所有可变的实例域,其中这些可变类

9 //必须全部都实现了自己的clone方法,否则将会跑出异常。

10public class Employee implements Cloneable {

11public Employee clone() throws CloneNotSupportedException {

12//缺省clone完成了域字段的按位浅拷贝。

13 Employee cloned = (Employee)super.clone();

14 cloned.hireday = (Date)hireday.clone();

15 }

16private Date hireday;

17 }

复制代码

注:数组对象可以通过Array的clone(public)方法完成元素的拷贝。

在C++中由于并不存在Object这样的单根结构的框架,因此C++是以另外一种方式表现该问题的,既缺省拷贝构造和缺省等于操作符重载。和Java类似,这两

也是member bitwise拷贝的,但这是由编译器在生成对象模型时自动完成的缺省行为,如果该类重载了拷贝构造函数和等于操作符,在需要copy的时候则会调

的方法,类的实现者应该在这两个方法中完成深拷贝。C++中还可以通过将这两个方法显示的声明为private类型的方法来禁用这种对象之间的copy行为,一旦出

器将会在在编译器报错。在C++中还存在一个explicit的关键字,可以有效的防止编译器通过自行推演隐式的调用对象的拷贝构造函数和等于操作符函数,见如下代

1//该类将会使用缺省的copy constructor,因此也会出现两个对象

2 //引用相同_name变量地址的问题。

3class Employee {

4private:

5char* _name;

6 };

7//该类由于将这两个方法私有化,一旦出现对象的隐式拷贝构造,

8 //将会导致编译错误。

9class Employee {

10private:

11 Employee(Employee& other);

12const Employee& operator= (Employee& other);

13private:

14char* _name;

15 };

16//将会调用重载后的这两个函数

17class Employee {

18 Employee(Employee& other);

19const Employee& operator= (Employee& other);

20private:

21char* _name;

22 };

复制代码

注:C++中有一种被称为引用计数的技术,经常会用在这个地方,以便提高对象copy的效率。

3. 接口与回调:严格意义上讲,回调这个属于更多的应用于C/C++这些支持基于过程编程的语言,Java中的回调是通过接口的方式来实现的,由于在接口的

中可以附带更多的信息,因此其表达能力要由于C/C++中的函数指针,见如下代码:

1public class Thread {

2public Thread(Runnable r) {}

3 }

4

5public class MyTask implements Runnable {

6public MyTask(int taskID) {

7 _taskID = taskID;

8 }

9

10public void setOk(bool ok) {

11 _ok = ok;

12 }

13

14public void run() {}

15 }

16

17public static void main(String[] args){

18 MyTask t = new MyTask(5);

19 Thread thrd = new Thread(t);

20 t.setOk(true);

21 thrd.start();

22 }

复制代码

这里的Runnable参数既为接口,Thread对象在启动的时候会调用该接口实现对象的run方法,但是在调用之前可以给该实现类传入更多的状态等相关数据,以便

程类调用run方法时可以得到更多的信息。

以下为回调函数在C/C++中的实现:

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

1 typedef int(*TestCallback)(int,int);

2int testCaller(TestCallback cb,int a,int b) {

3return cb(a,b);

4 }

5

6int testCallback(int a,int b) {

7return a * b;

8 }

9

10int main() {

11 TestCallback cb = testCallback;

12return testCall(cb,5,6);

13 }

复制代码

在C++中还可以通过模板以更加松散的方式完成类似Java的基于接口的回调(Java的回调方式,C++完全可以做到),见如下代码:

1 template

2class Thread {

3public:

4 Thread(T* r) _r = r {}

5void start() { if (_r) _r->run(); }

6private:

7 T* _r;

8 }

复制代码

在以上的实现中,T无需是某个接口的实现类,只要保证该类型包含run()方法即可,注意:C++中的模板是引用才编译的方式,如果没有任何Thread

会导致任何编译错误,只有当声明的类型对象中不包含run()方法时才会导致编译错误。

4. 内部类:Java中内部类可以为私有内部类,既只有外部类可以访问该内部类,而Java外部类的可见性只有包可见和public两种。C++中的内部类比较类

中的静态内部类,只是一种作用域限制的行为,以下为Java非静态内部类的说明:

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

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

的引用,该行为为编译器隐式行为。

1public class Employee {

2public class InnerClass {

3 bool test() {

4//这里的_jobYears为外部类域字段。

5return _jobYears > 10;

6 }

7 }

8

9public Employee(int jobYears,String name) {

10 _name = name;

11 _jobYears = jobYears;

12 _salary = 0;

13 }

14

15public void raiseSalary() {

16//编译器的会将以下构造隐式替换为InnerClass inner = new InnerClass(this);

17 //因为Java在为其编译的时候发现InnerClass为非静态内部类,则自动添加了以下构造:

18 //public InnerClass(Employee e)

19 InnerClass inner = new InnerClass();

20if (test())

21 _salary += 1000;

22 }

23private String _name;

24private int _jobYears;

25private int _salary;

26 }

复制代码

注:针对以上事例,内部类InnerClass可以通过Employee.this._jobYears的全称来显式的代替_jobYears > 10 中的_jobYears。反过来在raiseSalary方法

通过this.new InnerClass()语法格式更加明确的创建InnerClass的对象。

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

1public class Employee {

2public class InnerClass {

3 bool test() {

4//这里的_jobYears为外部类域字段。

5return Employee.this._jobYears > 10;

6 }

7 }

8

9public Employee(int jobYears,String name) {

10 _name = name;

11 _jobYears = jobYears;

12 _salary = 0;

13 }

14

15public void raiseSalary() {

16//这里也可以不使用this作为内部该内部类对象的外部类对象

17 //引用,可以根据需要替换为其他外部类对象的引用,如:

18 // Employee other = new Employee();

19 // InnerClass innser = other.new InnerClass();

20 InnerClass inner = this.new InnerClass();

21if (test())

22 _salary += 1000;

23 }

24 ......

25 }

复制代码

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

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

1public void start() {

2class TimePrinter implements ActionListener {

3public void actionPerformed(ActionEvent e) {

4 Date now = new Date();

5 System.out.println("At the tone,the time is " + now);

6//beep为外部类的域字段

7if (beep)

8 Tookkit.getDefaultToolkit().beep();

9 }

10 }

11 ActionListener l = new TimePrinter();

12new Timer(interval,l).start();

13 }

复制代码

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

1public void start(final bool beep) {

2class TimePrinter implements ActionListener {

3public void actionPerformed(ActionEvent e) {

4 Date now = new Date();

5 System.out.println("At the tone,the time is " + now);

6//beep为外部函数的局部变量。

7if (beep)

8 Tookkit.getDefaultToolkit().beep();

9 }

10 }

11 ActionListener l = new TimePrinter();

12new Timer(interval,l).start();

13 }

复制代码

为了规避局部类只能访问final局部变量的限制,既一次赋值之后不能再被重新赋值。但是我们可以通过数组的方式进行巧妙的规避,在下例中数组counter对

final的,因此他不可以被重新赋值,然而其引用的数组元素则可以被重新赋值,见下例:

1public void test() {

2final int[] counter = new int[1];

3for (int i = 0; i < dates.length; ++i) {

4 dates[i] = new Date() {

5public int compareTo(Date other) {

6//这里如果counter不是数组,而是被定义为final int counter,

7 //则会导致编译失败。

8 counter[0]++;

9return 16436ed2c1c708a1284a442fpareTo(other);

10 }

11 }

12 }

13 }

复制代码

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

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

1class OuterClass {

2public:

3void testOuter() {

4class FunctionInnerClass {

5public:

6void test() {

7 printf("This is FunctionInnerClass.\n");

8 }

9 };

10 FunctionInnerClass innerClass;

11 innerClass.test();

12 }

13 };

14

15int main()

16 {

17 OuterClass outer;

18 outer.testOuter();

19return0;

20 }

复制代码

4) 匿名内部类,其基本规则和局部内部类相似,差别在于该内部类不能有声明构造函数,这主要是因为Java要求类的构造函数和类名相同,而匿名内部类自身

名,因此在new新对象的时候,传入的构造函数参数为超类的构造函数参数。C++中不支持匿名类。见下例:

1public void start(final bool beep) {

2 ActionListener l = new ActionListener() {

3public void actionPerformed(ActionEvent e) {

4 Date now = new Date();

5 System.out.println("At the tone,the time is " + now);

6//beep为外部函数的局部变量。

7if (beep)

8 Tookkit.getDefaultToolkit().beep();

9 }

10 }

11new Timer(interval,l).start();

12 }

复制代码

5) 静态内部类,其功能和C++中的嵌套类非常相似,但是和Java自身的非静态内部类之间还是存在一些差异,如静态内部类不能直接访问外围类的对象引用

段,但是可以访问外部类的static域字段(包括private)。在Java中只有内部类可以被定义为static的,外围类是不可以这样定义的。

1public class TestMain {

2private static boolean classField = false;

3private boolean objectField = false;

4static class InnerClass {

5public void test() {

6//这里由于classField是静态域字段,所以静态内部类可以直接访问,

7 //但是对于objectField对象域字段而言,由于静态内部类中没有包含

8 //外部类的引用,因此不能直接访问objectField.

9if (classField)

10 System.out.println("Hello.");

11 }

12 }

13

14public static void main(String[] args) {

15 classField = true;

16new InnerClass().test();

17 }

18 }

复制代码

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

态内部类,否则将会报编译错误。

1public class TestMain {

2static class InnerClass {

3public void test() {

4 System.out.println("Hello.\n");

5 }

6 }

7

8private static InnerClass createInnerClass() {

9return new InnerClass();

10 }

11public static void main(String[] args) {

12 createInnerClass().test();

13 }

14 }

复制代码

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

PDF created with pdfFactory Pro trial version 16436ed2c1c708a1284a442f

本文来源:https://www.bwwdw.com/article/ya4q.html

Top