Having It All-- Java in CS 1

David M. Arnow and Gerald Weiss

Dept. of Computer and Information Science

Brooklyn College of CUNY

Brooklyn, NY 11210

{arnow,weiss}@sci.brooklyn.cuny.edu

ABSTRACT: An experiment using Java in CS 1 for the purpose of introducing network computing at that level achieved its goal and turned out to have several other benefits as well: an accessible object-oriented approach, a usable class design technique, realistic programming problems, event-driven programming and the use of graphical user interfaces. In addition, the last three weeks of the term were spent on an introduction to C, in particular, procedural programming concepts.

Introduction

In the spring of 1997, Brooklyn College started an experiment using Java as the primary language in CS 1. Our reasons for doing so stem from an NSF curriculum grant to promote the study of distributed programming at all levels of the CIS curriculum. We thought that Java, with its network classes, and its unusual web capabilities would greatly facilitate this at the CS 1 level.

As so often happens in both life and computer science, the consequences of a decision reached far beyond its original rationale. Such was the case in our experiment, and we report these consequences here. To sum them up at the outset, besides meeting our expectations of introducing network computing at the CS 1 level, the use of Java allowed us to

When the dust had settled, it was quite evident that using Java significantly extended the scope of the CS 1 course-- we felt as if we were "having it all".

The Context: an NSF-Sponsored Curriculum Project

The impetus for the course was an NSF-funded project to develop institutionally realistic ways of integrating the advances in distributed programming into our undergraduate curriculum.

The proposal. Our proposal called for two approaches. First, we would design a set of modules on a variety of aspects of distributed and network computing, suitable for (ideally seamless) integration into many of our existing courses. They were to range in duration from 1-week to 1-month and, in most cases, augment rather than replace existing topics in their "host" courses, e.g. CS 1 and 2, and discrete math. To date, modules have been developed for our second semester programming course, data structures, operating systems and architecture.

Second, we were to create a cluster of advanced undergraduate electives that integrate the methods, problems and results of current research in distributed programming into junior and senior level curriculum. The primary focus of these courses would be distributed applications, with some attention paid as well to systems issues. This has now found fruition in courses on artificial intelligence, simulation, workstation programming, decision support software, distributed systems administration and as well as a specially designed capstone course in parallel distributed programming. The viability of these courses depends on a pool of students who are, well-acquainted with network and distributed programming. It is the purpose of the modules described above to create such a pool.

The proposal also had well-defined plan for dissemination: web pages with downloadable modules, papers in education conferences such as the SIGCSE Technical Symposium, and faculty development seminars.

The problem. Early on we developed, as planned, a module for introducing network computing issues in CS 1. Unfortunately, it did not seamlessly integrate with the already packed course nor was it institutionally realistic-- an "extra" module at this level is too easily discarded by the myriad instructors (graduate students, adjuncts, physicists, assorted CS faculty) teaching the many sections of the CS 1 course. As a result, we sought a different approach, one where the context of networks and distributed programming would be as natural and inevitable as arithmetic. We therefore turned to Java.

An Object-Oriented Approach

The viability of teaching the object-oriented paradigm in the CS1 course is fairly well-established (for example, see [1] and [2]). Different approaches to the introduction of this paradigm remain, however.

One can provisionally ignore the OOP character of C++ in a CS1 course and many have chosen to take this "better C" approach (for example, see [3] and [4]). The programs that result, though not OOP, do not necessarily do an injustice to C++. This is not the case in Java. Although one could conceivably teach Java in a purely procedural approach (using static methods in a single class), the resulting programs would bear little resemblance to real Java programs. Even texts that do take this initial procedural approach are obliged to introduce classes and objects at a much earlier point in the text than their C++ counterparts

Some sidestep this issue by starting with various forms of event-driven applet programming ([5] and [6]). Students start overriding various applet callback methods. It is true they are implementing methods in derived classes, but from a pedagogical standpoint this is procedural programming, with an event-driven spin.

Others react to this by pursuing both imperative and object-oriented programming concurrently, at the outset of CS 1 [7]. However, we felt that the introductory student has enough of a challenge learning one paradigm, without being forced to master two initially.

We chose, therefore, to work with only one approach-- the object-oriented one-- at first. (Here we mean "object-oriented" in the sense of [8], that is, not focused on system-wide design but rather on objects and classes.) This meant avoiding primitive data types and their operators as well as traditional imperative control structures. In their stead, the students learned to use objects by sending messages to them and to create class definitions.

One element of imperative programming that could not be excised was the notion of variable and assignment statement. (Even Smalltalk, a pure object-oriented language, requires an assignment imperative.) However, this was mitigated by the fact that the variables were all reference variables and they could be used only for sending messages.

Replacing the usual assortment of operations and control structures of elementary imperative programming is an introduction to the usual assortment of elementary OOP concepts and facilities:

Our approach is both structured and graduated. It limits the presentation to a single group of related concepts at a time while moving in a logical progression toward full class definitions. Thus, students

So our students use predefined objects before creating new objects, and then use predefined classes to create objects prior to defining new classes-- an approach advocated in [9].

We had some trepidation concerning this approach, because the topics looked "advanced". As it turned out, the students had almost no trouble with these. Rather, the biggest conceptual stumbling block early on was-- as usual-- the imperative character of the assignment statement. Perhaps the object-oriented world, despite its relative recency in computer science, is more intuitive and natural for students than the imperative world.

In any case, the concept of behavior-driven programming is emphasized by the initial absence of most imperative programming constructs. Without conditionals, loops, and arithmetic to worry about, the student is able (forced) to concentrate upon the notion of behavior rather than which construct is to be used.

We did not choose Java with the aim of an early training in OOP for our students, but that turned out to be a happy outcome.

Challenge: living without imperative programming-- initially. Learning programming requires a continuous stream of examples and assignments. Finding a sufficient supply of problems that did not involve arithmetic, conditionals, booleans, or iteration was somewhat of a challenge. Ironically, Java's basic i/o classes -- an aspect of Java that is often cited as a reason for not using it in an introductory course -- provided a perfect setting. In class and in exercises, students used Java's existing, rather unsatisfactory i/o classes to define classes that would

One of the main objections to using Java in a CS 1 class is the acrobatics required to read integer input from the keyboard. A cascade of intermediate objects of several classes must be created in order to produce an appropriate object from which a string must be read and then parsed into an integer. Rather than viewing this as a hindrance, we took advantage of this to illustrate the concepts of composition and cascaded messages. It also provided an opportunity for the student to incorporate this behavior into a new class which is often presented to the beginner as a functional "black box". This willingness to work with these classes allowed us to approximate the "pure OOP" approach advocated by [10], without using a Smalltalk system.

A student-accessible method for class design. Beginners are usually puzzled as to how to approach a programming problem. Early on, we present a simple approach to class design, one based on determining the classes/objects from the statement of the problem and then developing those classes. The first couple of examples do this informally so as not to detract from the syntax and mechanics of class definition. The technique is then presented in a self-conscious manner and thereafter rigorously adhered to throughout the course:

In general, we develop one class in its entirety before preceding to the next. This approach is viable this because of the small number of classes in CS 1 problems and their hierarchical relationship. Few of the problems involve more than one primary object from the statement of the problem. The majority of additional classes arise from implementing the methods of earlier classes. Where multiple primary objects arise, their interactions are sufficiently minimal to allow independent development.

Leveraging the message paradigm: a gentle introduction to arrays. By the time we get to arrays, midway in the course, the students are quite comfortable with message-sending. This comfort coupled with the availability of the predefined collection class, Vector, permits the graduated progression towards indexing and arrays:

The initial introduction of Vectors requires no new syntax as would be required with arrays. Further, there are no capacity issues and at the outset no indexing-- just the concept of a collection of objects. From there, indexing and the notion of relative position are introduced. Again, this requires no new syntax-- the indexes are just arguments in elementAt/setElementAt invocations. The earlier presentation of a methodical approach to loop design eases further the students use of indexes. Sorting, and sequential and binary searching are all presented in this subscript-less environment.

When arrays are finally introduced, the student has a complete understanding of the nature, purpose and use of indexing. All that is required is a new syntax and a discussion of the array's capacity limitations.

Loop Design

Although our first focus was the world of objects and classes, we were able to pay proper attention to imperative programming issues. One area in particular that we emphasized was loop design. We developed a simple, informal but nonetheless methodical approach:

We are able to use this method without resorting to explicit loop invariants because in CS 1 problems, most loop invariants turn out to be equivalent to the variable definitions themselves.

Recursion

We present recursion as just another means for implementing methods. It is ironic, but in procedural courses, recursion seems a bit more difficult than it did here. It may be because in a procedural approach, a decision to use recursion requires first and identification of what to put into a procedure and followed by the recursive implementation. In the object-oriented course, the methods are already dictated by the analysis of class behavior. Each method then can be considered as recursion-appropriate-- or not.

Testing

In traditional procedural languages like C, the module is realized through separately compiled source files. Such a program organization is often hard to motivate early in a traditional CS 1 class. As a result, students are not exposed to the notion and practice of unit testing.

By contrast, Java programs are of necessity modular (in the sense that each class is a distinct entity) and the need for certifying the behavior of the classes-- unit testing-- that are used to build other classes is apparent. The ability to declare a static test driver directly within a class allows us to demonstrate that every class can and should be provided its own test suite. This can be followed through the entire semester.

Event-driven and GUI Programming

Although the main development of the material involves non-GUI applications, we present examples of applets, introducing new AWT classes as we go along, and give small applet homeworks in each course unit. As the course progresses, many-- though not all (we don't cover threads)-- of the mysteries surrounding our original applet introduction disappear. This component of the course is more informal and encourages experiments and exploration on the part of the students. This is as an important part of learning to be a computer scientist as the more disciplined components of the course.

Network Computing in CS 1

Our original goal was to find a convenient platform for introducing CS 1 students to distributed programming via some concrete programming experiences in network computing. In particular, we wanted our students to have experience building network clients.

The original plan, in our NSF proposal, had been to create a "network-client module", one week in length, that could be inserted towards the end of a traditional C++ or C-based introductory course [11].

In the Java experiment, we were able to integrate network-client experience as "just another example" throughout the semester. Early on, the students were given the task to define a WebReader class-- simply reading from a web page. This could be used in programs such as one that monitored the current DJIA from various web sources.

Later, we worked through a WebSurveyor program-- one that recursively searched a portion of the Web for missing or otherwise erroneous web pages.

Applets. Another dimension of network computing in the course are the applet examples that students work with. When a student codes and prepares an applet on the network in the computer lab and shows it off to her friends in the library the presence of the network communication is stark. Interestingly, though Java applets are an example of client-side actions, from the student's point of view they are a server phenomenon-- after all they are designed and compiled on the server side. Thus, on the one hand the students write web client programs but on the other they develop data for the server.

The Web as a source of Realism

Using the World Wide Web as a repository for data to be processed by student programs provided an opportunity to draw a number of lessons for our students that in other circumstances have been difficult:

Real data can be complicated. Formal specification techniques were not feasible in our course, so we dealt with HTML and its vagaries in an ad hoc fashion, but the students are able to see the limitations of that approach and the desirability for a more methodical one.

The appeal to students of using "real" data from the web was noted by Fell and Proulx [12]. This appeal is greatly enhanced when, using Java and its network classes, the students' programs can directly access the data from the Internet.

Fast Introduction to C

This was an experimental section. While our students learned Java, their counterparts in other sections studied C, the primary language used in our intermediate level courses. To help integrate our students into the mainstream of the curriculum, we stopped teaching Java after only 10 weeks and spent the final 3 weeks of the term on C-- but would 3 weeks suffice?

We introduced C by treating it as "Java--". First they were told of the elements that were identical to Java. When the students were told that there were no classes and objects in C they themselves recognized the consequences: no reference variables, all methods are static (naturally they initially described C using Java terminology). After one day their knowledge of C included: basic primitive types and operators, all the control constructs, functions and recursion, call by value parameters, file I/O and single file scoping rules. In the procedural world of C, run-time libraries are repositories of behavior. Students coming out of an object-oriented course find it easier to learn and adopt new sets of library functions (like fopen/fclose/fscanf/fprintf) than new language constructs-- they're used to encountering new behavior in classes. They learned also that whereas in Java when one wants to get something done one searches for an existing class or defines such a class oneself, in C one searches for an existing function or defines such a function oneself-- the essence of procedural programming. As examples of this we introduced fopen, fclose, fscanf and fprintf-- all on day one. They could immediately start writing some non-trivial (by CS 1 standards) C programs.

The remaining two and a half weeks were spent introducing C arrays (superficially not so different from Java arrays), structures (explained rather effectively-- though certainly taking liberties-- as methodless classes), and pointers and dynamic storage allocation. Although the semantics of the latter were certainly unfamiliar to the students, their application was not and this helped a great deal.

Faculty often express fears that students introduced to OOP will somehow have difficulty adjusting to "mainstream" procedural programming. That was not the case here-- and all our students completing this course successfully have continued to be successful in subsequent "mainstream" courses.

Conclusion

Our experience is in accord with Hosch's preliminary evaluation of the language [8]: it seems that, though Java is a relatively small language, it allows one to cover quite a bit of ground in a CS1 course. Our success with OOP is in accord with [13]; in addition, looking back, it seems that Java meets many, though not yet all, of their requirements for an appropriate object-oriented language for CS1. Given its wide use, we find it preferable to using a specialized teaching language like Blue [14].

This was a pleasant surprise. We had proposed the experiment to further our network computing/distributed programming curriculum project; we had justified it to our curriculum committee on the grounds that the students would love the GUIs and that we would in any case teach them C at the end of the semester. We opted for an OOP approach with some trepidation and ended up with a workable course that gave as all that we wanted and more.

Our treatment of CS1 differs from our project's original design in that a new CS1 course, not just a new CS1 module, has been developed. However, we are hopeful that this early foundational experience will ease the inclusion of other modules in the curriculum and prepare the students for the advanced courses that our project has introduced.

References

1. Rick Decker and Stuart Hirshfield. The Top 10 Reasons Why Object-Oriented Programming Can't Be Taught in CS 1. 25th SIGCSE Technical Symposium on Computer Science Education, SIGCSE Bulletin 26,1 (March, 1994) Phoenix, AR.

2. Richard Reid. The Object-Oriented Paradigm in CS 1. 24th SIGCSE Technical Symposium on Computer Science Education, SIGCSE Bulletin 25,1 (March, 1993) Indianapolis, IN.

3. Walter Savitch. Problem Solving With C++-- The Object of Programming. Addison-Wesley-Longman (1996).

4. James P. Cohoon and Jack W. Davidson. C++ Program Design-- An Introduction to Programming and Object-Oriented Design. Irwin (1997).

5. H. M. Deitel and P. J. Deitel. Java-- How to Program. Prentice-Hall (1997).

6. Rick Decker and Stuart Hirshfield. programming.java. PWS Publishing (1998). We base our assessment on the beta edition of the associated lab manual and the PWS web site description of the text (www.pws.com).

7. John Lewis and William Loftus. Java Software Solutions: Foundations of Program Design, Addison-Wesley-Longman (1997).

8. Frederick Hosch. Java as a First Language: an Evaluation. SIGCSE Bulletin 28,3 (Sept. 1996).

9. Rick Decker and Stuart Hirshfield. Top Down Teaching: Object-Oriented Programming in CS 1. 24th SIGCSE Technical Symposium on Computer Science Education, SIGCSE Bulletin 25,1 (March, 1993) Indianapolis, IN.

10. Mark Woodman, Gordon Davies, and Simon Holland. The Joy of Software-- Starting with Objects, 27th SIGCSE Technical Symposium on Computer Science Education, SIGCSE Bulletin 28,1 (March, 1997) Philadelphia, PA.

11. David Arnow, Carol Tretkoff, and Paula Whitlock: Using Modules To Integrate Distributed Computing In The Undergraduate Cs Curriculum, CCSCNE96, April, 1996.

12. Harriet H. Fell and Viera K. Proulx. Exploring Martian Planetary Images-- C++ Exercises for CS1. 28th SIGCSE Technical Symposium on Computer Science Education, SIGCSE Bulletin 29,1 (March, 1997) San Jose, CA.

13. Michael Kolling, Bett Koch, and John Rosenberg. Requirements for a First-Year Object-Oriented Programming Language, 26th SIGCSE Technical Symposium on Computer Science Education, SIGCSE Bulletin 27,1 (March, 1995) Nashville, TN.

14. Michael Kolling, and John Rosenberg. An Object-Oriented Program Development Environment for the First Programming Course, 27th SIGCSE Technical Symposium on Computer Science Education, SIGCSE Bulletin 28,1 (March, 1997) Philadelphia, PA.