Comparative Teaching of C++ and Java in Object-Oriented Programming

0 Introduction

C++ and Java are foundational courses in computer science, representing mainstream languages for object-oriented programming. They both teach programming through the lens of object-oriented principles, but their approaches to implementing these principles differ. This is a typical case of solving the same problem with different methodologies, illustrating multiple solutions to a single question. Once students clearly understand the different approaches C++ and Java take to implement object-oriented programming, they will find it much easier to grasp other object-oriented programming languages.

1 Teaching Challenges and Strategies for Encapsulation

Using an example akin to Windows, where the operating system hides its state information internally, ordinary users can only access it through the menus provided by Windows, while advanced users can manipulate and access internal information via functions offered by Windows. Thus, encapsulation hides the internal information of an object but allows access to this hidden information through methods provided by the class. Encapsulation is generally achieved through three keywords: private, protected, and public. Proper encapsulation makes programs easier to understand and maintain while enhancing their security. When teaching encapsulation, at least three objectives should be achieved: ① Hiding the implementation details of a class; ② Users can only access data through specified methods, thus limiting unreasonable access to variables; ③ Bundling data with the code that manipulates it to improve maintainability. Due to the strict limitations of encapsulation, the teaching strategy must clarify how C++ and Java adopt different strategies for allowing an object of one class to access the private data of another class, highlighting the different countermeasures they employ in response to the strict limitations of encapsulation.

1.1 C++ Course

Using everyday examples, a friend is akin to a “friend” in programming, as we typically do not disclose our confidential information to others, but friends might know many secrets about each other. Friends break the information hiding principle because a friend in programming similarly undermines information hiding within a class. In C++, friends are declared at the module level; for instance, declaring module X as a friend of module Y means that module Y treats module X as a friend, allowing module X to access hidden information within module Y. Students should note that friends violate the original intent of encapsulation; they are exceptions to the rule. A friendship is a binary relationship; similarly, friendship in programming is also binary. For example, if Zhang San considers Li Si a friend, does Li Si necessarily consider Zhang San a friend? Not necessarily, indicating that friendships are unilateral. If Zhang San is a friend of Li Si and Li Si is a friend of Wang Wu, does Zhang San have to be a friend of Wang Wu? Not necessarily, as Zhang San may not know Li Si. Thus, friendship is not transitive, showing that friendship in programming is also non-transitive. For example, does a father’s friend have to be a friend of his child? Not necessarily, as a father’s friends may not know us, showing that friendships are not inheritable, indicating that friendship in programming is also non-inheritable. The teaching challenge lies in understanding the three characteristics of friendship: unilateral, non-transitive, and non-inheritable. Through this analogy, students gain an intuitive understanding of friendship, clarifying this knowledge point; once an abstract concept is infused with real-world meaning, it becomes easier to comprehend.

The keyword for friends in C++ is friend, which includes friend functions and friend classes. A friend function is a non-member function of a class, declared within the class body and marked with friend, allowing it to access hidden members through object names. Friend functions enable quick access to hidden information within a class but violate encapsulation; thus, our programs must strike a balance between information hiding and quick access. If class A is declared a friend of class B, all member functions of class A are friends of class B, meaning they can access all data members and member functions of class B, even if they are private or protected.

1.2 Java Course

Java does not have a friend mechanism like C++, but it can achieve similar functionality through inner classes. In Java, a class definition can be placed inside another class definition, which is called an inner class. Depending on its location and form, inner classes can be member inner classes, static inner classes, local inner classes, or anonymous inner classes. In Java, inner classes can access the private members of the outer class, allowing one class object to access the private data of another class object. Therefore, although Java’s approach differs from C++, both achieve similar functionality. When learning C++ and Java, students can better grasp the different strategies in implementing object-oriented techniques by summarizing their experiences. Inner classes are a key focus in Java, and to facilitate student understanding, the following inner class example can be designed in the teaching plan:

Comparative Teaching of C++ and Java in Object-Oriented Programming
Comparative Teaching of C++ and Java in Object-Oriented Programming

In the above example program, even though the attributes of the outer class OutsideClass are marked as private, the inner class InsideClass can seamlessly access these attributes. The teaching focus should explain to students from two perspectives: one is to view the inner class as a member of the outer class, which can naturally access its other members; the other is to explain from the pointer perspective that when creating an inner class object of a certain outer class, this inner class object will capture a reference to the outer class object. Thus, when accessing members of the outer class, this reference will be used to select the members of the outer class. If students learn C++ before Java, they will better grasp inner classes, as the concept of pointers aids in understanding how inner class objects access private data of outer class objects.

2 Teaching Challenges and Strategies for Inheritance

Inheritance can be classified into single inheritance and multiple inheritance. A derived class with only one base class is called single inheritance, while a derived class with two or more base classes is called multiple inheritance. A derived class must accomplish three tasks: first, inherit the members of the base class, meaning the derived class effectively contains all member functions and data members of its base class. Second, override the base class members, necessitating a clear understanding of the differences and connections between overloading and overriding; overloading involves functions with the same name but different parameter lists, while overriding involves functions with the same name and parameter lists. If derived class A declares a function with the same name as a member of base class B, function FA in class A overrides function FA in class B. Third, add new data members and member functions; the derived class must enhance its functionality compared to the base class, or it has no reason to exist. The core of inheritance and derivation mechanisms is the addition of new members to the derived class. Both C++ and Java implement single inheritance, but they adopt different strategies for multiple inheritance; the teaching plan for inheritance must clarify the different implementations of multiple inheritance in C++ and Java, allowing students to understand their differing approaches through comparison.

2.1 Multiple Inheritance in C++ Courses

C++ supports multiple inheritance. When a derived class has members that are the same as those in its base class, accessing these members requires careful attention to avoid ambiguity. Ambiguity arises when a derived class inherits from multiple base classes that have the same data members and member functions. Generally, when directly using a class object, the data members and member functions of that class are used. If access to a similarly named data member or member function in the parent class is required, the base class name and scope resolution operator (::) must be used to qualify the access. Based on my teaching practice, to access data and functions in multiple inheritance, I first draw a derived relationship diagram, then create a member composition diagram based on that derived relationship diagram. However, even this approach can be difficult for students to understand how to eliminate ambiguity. My strategy is to explain computer directories, illustrating how to access files stored on a computer. By comparing access to files in a computer with the derived relationship diagram and member composition diagram, students can clearly see the unambiguous “access path” for each variable, which starts from the last derived class and traces back to its parent class until it can unambiguously access the corresponding variable or function. According to the teaching plan, I will discuss a classic inheritance in C++, known as diamond inheritance, where the root base class Base is inherited by two base classes Base1 and Base2, and a derived class Derived inherits from both Base1 and Base2, forming a diamond relationship. Diamond inheritance presents ambiguity and redundant data because the derived class Derived inherits the same members from both Base1 and Base2, leading to multiple instances of the same member in Derived. The teaching plan includes a program that uses scope resolution and tracing back to resolve ambiguity in diamond inheritance.

Comparative Teaching of C++ and Java in Object-Oriented Programming

Comparative Teaching of C++ and Java in Object-Oriented Programming

During class discussions, I encourage students to relate inheritance to everyday life, such as what can be inherited from one generation to the next in a family. Once the abstract concept of inheritance is infused with real-world meaning, it becomes easier to understand. When students grasp inheritance, they will find it much easier to understand derived relationship diagrams and member composition diagrams. Based on this understanding, it becomes easier to comprehend how to resolve ambiguity in C++ multiple inheritance through path tracing.

2.2 Multiple Inheritance in Java Courses

Java does not support multiple inheritance but can simulate its effects through interfaces. An interface is a collection of method definitions (without implementations) and constant values. While interfaces can achieve similar functionality to multiple inheritance, they are fundamentally different: Java interfaces can inherit from other interfaces, allowing for multiple inheritance, but classes can only implement interfaces and can implement multiple interfaces. Interfaces can abstract the same behavior of unrelated classes, so classes implementing the same interface do not need to be related in terms of class inheritance. In the teaching process, examples such as airplanes, birds, and superheroes, all of which can fly but do not belong to the same class, can be used to illustrate that interface inheritance is independent of class inheritance. Interfaces represent the interaction interface of objects, but through an interface, one cannot determine the class to which an object belongs. For example, a USB interface is a universal interface that includes methods to be implemented by multiple classes, supporting devices such as mice, keyboards, external hard drives, printers, scanners, cameras, chargers, and phones. The essence of an interface is a contract, standard, and specification; once established, everyone must adhere to it, and there is no inheritance relationship. Thus, it can be seen that Java implements simulated multiple inheritance, which is different from C++’s true multiple inheritance.

In object-oriented programming, all objects are defined through classes, but some classes cannot define objects. If a class contains abstract methods, it is called an abstract class, which cannot define objects; an abstract method is a method without a body. An abstract class can also contain constructors, destructors, member functions, and member variables. Since abstract classes cannot define objects, they must be inherited and all abstract functions implemented in their derived classes before objects can be defined. The concept of abstract classes can be challenging for students to grasp. At this point, I design the following teaching plan: for instance, humanity desires to fly out of the galaxy, but currently lacks the technological capability to realize this dream; thus, the aspiration to fly out of the galaxy serves as an abstract class, while the technologies that cannot currently be realized are the abstract methods of this class. However, with advancements in science and technology, if one day those previously unattainable technologies become achievable, the aspiration to fly out of the galaxy will become a reality, and the original abstract class’s subclass will no longer be abstract and can be instantiated. This analogy helps students gain an intuitive understanding of abstract classes. For Java inheritance, I also emphasize the differences and connections between interfaces and abstract classes in class.

3 Teaching Challenges and Strategies for Polymorphism

Polymorphism is the ability of the same behavior to exhibit multiple different forms or manifestations. It can be divided into compile-time polymorphism and runtime polymorphism. Object-oriented technology uses overloading to achieve compile-time polymorphism, which occurs when member functions in a class share the same name but have different parameters; the return type is not considered a condition for overloading. Therefore, the overloaded methods share the same function name but differ in their parameter lists, and this polymorphism is determined at compile time. When calling these overloaded methods, different functions are selected based on the parameters passed. Runtime polymorphism is achieved through dynamic binding, which determines the type of the referenced object at runtime and calls the corresponding member function based on that type. In other words, only when the program reaches that statement does it know which class’s member function is being called. This polymorphism is realized through inheritance, method overriding, and upcasting. If the method in the derived class shares the same name, parameter list, and return type as the method in the base class, then the derived class’s method overrides the base class’s method. Thus, subclasses can inherit methods from their parents but can also modify and improve the inherited methods as needed. Upcasting refers to creating a subclass object and using it as a parent class object. Runtime polymorphism treats different subclass objects as parent class objects, thereby shielding the differences between different subclass objects, allowing for the writing of generic code, creating universal programs, and establishing unified calling standards. The strategies for implementing compile-time polymorphism in C++ and Java are similar, but the teaching plan for polymorphism must clearly explain how C++ and Java adopt different strategies for implementing runtime polymorphism.

3.1 Runtime Polymorphism in C++ Course Teaching

C++ does not have native runtime polymorphism; its runtime polymorphism is achieved through virtual functions and virtual base classes.

3.1.1 Virtual Functions in C++

A virtual function is a member function of a class that becomes a virtual function when the keyword virtual is added before its declaration. Virtual functions are the foundation for achieving runtime polymorphism, allowing polymorphism to be expressed through common methods while employing different strategies due to individual differences. C++ introduces virtual functions for two main purposes: first, to allow derived classes to override the base class’s virtual functions; second, virtual functions are dynamic binding functions, which only occur when called via a base class pointer or reference. Virtual functions must be non-static member functions, as static member functions are shared by all objects of the class and are not specific to any individual object, hence cannot be treated as virtual functions. Through derivation, virtual functions can achieve runtime polymorphism.

The virtual keyword is only used in function declarations within class definitions, not in function bodies. When a member function FA of a class is defined as a virtual function, all derived classes of that class retain the characteristics of virtual functions. When overriding a base class’s virtual function, not only must the function name remain unchanged, but the number of parameters, parameter order, and return type must also match those of the base class’s virtual function; otherwise, the compiler will report an error. If the function has default parameter values, those values cannot be changed. A pure virtual function is a special type of virtual function that only has a declaration without a definition, meaning it has no function body, requiring each derived class to define its own function body as needed. As mentioned in section 2.2, Java’s abstract methods lack function bodies, and pure virtual functions also lack function bodies, so Java’s abstract functions are similar to C++’s pure virtual functions.

In C++, virtual functions are managed through a virtual table (VTable). The VTable is essentially a table of addresses of a class’s virtual functions, similar to function pointers. The construction of the VTable occurs in two steps: first, each object has a pointer (vptr) pointing to the current class’s VTable, which is assigned during the constructor; second, the VTable contains the entry addresses of the current class’s various virtual functions. Once the address of the current class’s VTable is assigned to the object, the object can traverse the function pointers in the VTable and call the corresponding functions, thereby achieving dynamic binding. Virtual functions are a challenging concept in C++, primarily due to two aspects: understanding the VTable and vptr, and grasping the concept of pure virtual functions. Since pointers are already a challenging topic in C, understanding VTable and vptr can be difficult without a clear grasp of pointers. My strategy is to draw the VTable for students, allowing them to visualize the process of calling virtual functions through vptr, providing them with an intuitive understanding.

C++ does not have an abstract keyword; instead, it refers to classes containing pure virtual functions as abstract classes. In C++, an abstract class is a class that cannot directly create object instances because it contains undefined pure virtual functions. Abstract classes can only be used as base classes and cannot define instances of abstract classes; however, pointers and references to abstract classes can be declared. As long as a derived class contains at least one pure virtual function that has not been implemented, it remains an abstract class. Only when the derived class implements all inherited pure virtual functions can instances of that derived class be created. Thus, C++’s abstract classes are equivalent to Java’s abstract classes, and I believe this point should be emphasized repeatedly in the teaching process. According to the teaching plan, I will continue to use the example of humanity’s desire to fly out of the galaxy, indicating that currently unattainable technologies represent pure virtual functions. If, in the future, those technologies become achievable, the original abstract class’s subclass will no longer be abstract and can be instantiated, thus no longer being an abstract class.

3.1.2 Virtual Base Classes in C++

While virtual functions solve issues of inheritance and overriding, ensuring that their content accurately reflects the actual functions, section 2.1’s code using scope resolution and tracing back resolves ambiguity in diamond inheritance but does not address the issue of redundant data. Therefore, simultaneously resolving both ambiguity and redundant data in diamond inheritance is a challenge in C++. According to the teaching plan, I will use virtual base classes to explain how to simultaneously resolve both ambiguity and redundant data in diamond inheritance. In C++, the root base class of a diamond inheritance can be designated as a virtual base class, ensuring that the same data members and member functions inherited from different direct base classes only have one instance in memory, thus eliminating redundant data issues. If a base class is marked with the virtual keyword in inheritance, that base class is designated as a virtual base class, and the virtual keyword only applies to the immediately following base class name. The program in this section provides a detailed description of virtual base classes. Based on the concept of virtual base classes, C++ introduces virtual inheritance, which declares a common base class with the virtual keyword. In derived classes, only one instance of the common base class is retained, thereby avoiding issues of redundant data and ambiguity. In conjunction with the definition of interfaces, I find that C++’s virtual base classes are equivalent to Java’s interfaces. According to the teaching plan, I will discuss a second solution to diamond inheritance, modifying the code from section 2.1 as illustrated in this section, which simultaneously resolves both redundant data and ambiguity issues in diamond inheritance. The program in this section demonstrates that in diamond inheritance, there is only one instance of both data and functions in memory, thus avoiding both redundant data and ambiguity issues. Emphasizing the examples related to virtual base classes will assist students in understanding the characteristics of runtime polymorphism in C++.

Comparative Teaching of C++ and Java in Object-Oriented Programming

3.2 Runtime Polymorphism in Java

Java does not have the concept of virtual functions; runtime polymorphism is a native feature of Java. Ordinary functions in Java can achieve dynamic binding, so Java’s ordinary functions possess the characteristics of C++’s virtual functions. In Java, if a function is preceded by the final keyword, that function cannot be inherited, thus it lacks the characteristics of a virtual function. When a Java object needs to call a certain method, the runtime system first determines the actual type of that reference object, then calls the corresponding member function based on that actual type. Generally, Java objects first call their own class’s member functions. Through years of teaching practice, I have found that students grasp Java’s runtime polymorphism quite well, primarily because Java does not support multiple inheritance, making issues of data redundancy and ambiguity easier to resolve.

4 Conclusion

In teaching the differences between C++ and Java in implementing object-oriented programming techniques, instructors must clearly explain the connections and differences between these basic concepts through specific examples. Only in this way can students achieve a more efficient learning outcome. Encapsulation, inheritance, and polymorphism are the core of object-oriented programming. Once these concepts are thoroughly understood, students will have mastered the core techniques of object-oriented programming. C++ and Java are merely two of the object-oriented programming languages, and with the advancement of science and technology, new object-oriented programming languages will emerge; for instance, the currently popular Python is also an object-oriented design language, making it easier for students to grasp its object-oriented features.

References

[1] Xu Xiaofei. An Agile Educational System for Sustainable Competitiveness and an Open Educational Ecology. Computer Education, 2023(4): 15-20.

[2] Leiserson C E, Thompson N C, Emer J S, et al. There’s Plenty of Room at the Top: What Will Drive Computer Performance After Moore’s Law? Science, 2020, 368(6495): 1-7.

[3] Xiao Fengxiang, Qin Lijun. The Formation, Content, and Internal Logic of MIT’s New Engineering Education Reform. Higher Engineering Education Research, 2018(2): 45-51.

[4] Stanford University. Open Loop University Stanford 2025. Palo Alto: Stanford University Press, 2013.

[5] ACM China Education Committee, Ministry of Education University Computer Course Teaching Guidance Committee. Computer Course System Specification 2020 CC2020. Beijing: Higher Education Press, 2021.

[6] Computer Education 20-Person Forum Report Writing Group. Computer Education and Sustainable Competitiveness. Beijing: Higher Education Press, 2019.

[7] Chen J, Impagliazzo J, Shen L. High-Performance Computing and Engineering Educational Development and Practice. 2020 IEEE Frontiers in Education Conference (FIE). Piscataway: IEEE, 2020: 1-8.

[8] Liu Qiang, Wang Ruijin, Zhao Wentao, et al. Construction of Gold Courses for Cybersecurity Practice Teaching Based on CC2020 Competency Model. Computer Education, 2021(5): 59-64.

(Intern Editor: Ouyang)

Funding Project: Renmin University of China Teaching Reform Project “Renmin University of China 123 Gold Course – ‘Programming II (Fundamentals of Big Data Technology)” (123 Gold Course).

Author Profile: Qin Biao, Male, Associate Professor at Renmin University of China, Research Interests: Knowledge Bases and Artificial Intelligence,[email protected]..

Citation Format: Qin Biao. Comparative Teaching of C++ and Java in Object-Oriented Programming Courses. Computer Education, 2024(04):144-148,153.

(End)

More Exciting Content:

Yan Shi | Review and Prospect of Computer System Capability Cultivation

Boao Releases Educational Strong Voice | CIE2023 Fifth China IT Education Boao Forum and the 20th Anniversary Conference of Computer Education Magazine Successfully Concluded

Call for Papers for the 2024 China Higher Education Computer Education Conference (CCEC2024)

President Interview | Promoting Interdisciplinary Integration to Cultivate Innovative Talents for the New Era – Interview with Professor Ni Mingxuan, Founding President of Hong Kong University of Science and Technology (Guangzhou)

New Year Message from the 7th Editorial Board

Guidelines for Teaching Ideology in Computer Science Courses

Academician Chen Guoliang | Cultural Construction of Virtual Teaching and Research Room for Ideology in Computer Courses

Professor Chen Daoxu from Nanjing University | Change and Constancy: Dialectics in the Learning Process

Yan Shi | Thoughts and Suggestions on the “Predicament” of Young Teachers in Higher Education

Xu Xiaofei et al. | Metaverse Education and Its Service Ecosystem

[Directory] Computer Education 2024 Issue 3

[Directory] Computer Education 2024 Issue 2

[Directory] Computer Education 2024 Issue 1

[Editorial Board Message] Professor Li Xiaoming from Peking University: Reflections on the “Year of Classroom Teaching Improvement”…

Professor Chen Daoxu from Nanjing University: Which is more important, teaching students to ask questions or teaching students to answer questions?

[Yan Shi Series]: Trends in Computer Discipline Development and Their Impact on Computer Education

Professor Li Xiaoming from Peking University: From Interesting Mathematics to Interesting Algorithms to Interesting Programming – A Path for Non-Majors to Experience Computational Thinking?

Reflections on Several Issues in Building a First-Class Computer Discipline

New Engineering and Big Data Major Construction

Learning from Others’ Strengths to Overcome Weaknesses – A Compilation of Research Articles on Computer Education at Home and Abroad

Comparative Teaching of C++ and Java in Object-Oriented Programming

Comparative Teaching of C++ and Java in Object-Oriented Programming

Leave a Comment