The authors are faculty members in the Computer Science Department of the State University of New York at Oswego. Craig, Doug, and Rameen can be contacted at [email protected], [email protected], and [email protected] cs.oswego.edu, respectively.
The computer science department of the State University of New York at Oswego was, to the best of our knowledge, the first to use Java in core computer-science courses. We started using Java in 1995, back when just about everything involving Java (including our courses) carried an "alpha" disclaimer. Now, two years later, both Java and our courses have matured to the point where we can hardly even contemplate teaching or programming without Java. Not that we are complacent-teaching computer science requires nearly constant experimentation and innovation.
Like nearly all computer-science programs, our curriculum starts off with two courses-typically called CS1 and CS2-that cover those aspects of programming that every computer-science student needs to know to continue in the major. We also require core courses in discrete math and formal methods, computer architecture, advanced data structures, software engineering, and comparative programming languages, followed by sets of electives. Many of these courses use Java in some way, but the main impact has been in CS1 and CS2, where students are introduced to Java in the course of learning about computing.
We didn't adopt Java just to be hip. In fact, our decision to try it experimentally was made well before Java became a cultural phenomenon. But some of the reasons for the decision parallel the reasons Java has become so popular:
- Java is fully buzzword compliant-safe, portable, object oriented, component based, and so on. It is among the few good choices for a wide-spectrum language that can be used across most of the courses we teach. (But not all of them; for example, artificial-intelligence courses use Lisp and Prolog, and project-based electives usually leave the choice to students.)
- Java is relatively simple to learn, at least compared to most other plausible language choices. Despite having a 700-page language specification, essential matters of Java syntax and usage take up only a small percentage of class time. This is aided by teaching techniques, such as using rigid templates, that help students focus attention on the language features they need to use for a given lab exercise or assignment.
- Java can be used to program a wide range of intrinsically "neat" software (for example, applets and GUI-based applications) that motivate students to learn and experiment. Many of our students truly enjoy working on their programming assignments.
There are lots of ways to divide up the field of computer science, but an increasingly central distinction separates algorithmic from interactive aspects of programming (see, for example, Peter Wegner's article "Why Interaction Is More Powerful Than Algorithms," Communications of the ACM, May 1997). The algorithmic aspects include classic topics such as procedures, functions, data structures, and algorithms. The interactive aspects are not at all restricted to user interaction, but encompass the broad areas of message passing among objects, software architecture, concurrency, distribution, modeling, and simulation. Neither aspect is necessarily more central, yet traditional CS1 and CS2 courses typically focus only on algorithmic aspects. Over the past few years, most introductory computer science courses have introduced some coverage of object-oriented design concepts (sometimes even using Java), but normally present them within the algorithmic programming tradition, so students learn about the software design and engineering concepts associated with interaction only in advanced courses, if ever.
The adoption of Java allows us to provide a more balanced introduction to these two broad categories of computing, by weaving together traditional and not-so-traditional topics. Our curriculum, as a whole, increasingly reflects the entire range of engineering and theoretical issues in both algorithmic and interactive computing.
So, it was not so much Java as it was the ideas that surround it that led Oswego and some other colleges, universities, and even high schools to make seemingly revolutionary curriculum changes. In retrospect, we see that these changes have mostly occurred in incremental steps, reflected in the progression of languages used in CS1 at Oswego-from Fortran in the 1970s, to Pascal, to Modula-2, to C++, to Java. But language changes are only one aspect of the changes in content and delivery. A curriculum suitable for today's students-who will later go out and build, for example, mobile agent systems-bears scarce resemblance to degree programs for previous students-who later built, for example, multitape sorting systems. Most of the traditional topics in our curriculum have not gone away (a few have), but they are now presented in conjunction with equally important design and engineering concepts and skills.
These grandiose-sounding ideas translate into some rather simple features and teaching techniques in our CS1 and CS2 courses, as described in the remainder of this article.
The CS1 Course
A CS1 course must be handled with great care. Students may know very little (or worse, know useless or unproductive things) about programming upon entering the course, but must be in a fair position to succeed in the demanding subsequent courses in the major.
The clerical aspects of our CS1 course are not unusual. It is taught in a lecture hall to roughly 100 students per semester. A structured weekly laboratory experience (with a maximum of 10 students per lab section) supports the course. It is not presumed that the students have previous programming experience. However, it is expected that they have an enthusiasm for learning and that they will work very hard. Programming challenges that engage students in meaningful computational activities are posed. The course does not follow a textbook, so students must learn to take good notes and rely upon handouts and online resources.
The nature of our CS1 course is decidedly object oriented. In addition to the object-oriented, component-based design and the programming notions that dominate our course, explicit instruction in problem solving is provided early in the course. Attention is paid to algorithmic concepts later in the semester.
The initial weeks of the course are centered on computing with (reusing, not writing) a host of interesting components including simple geometric shapes, coins and dice, and musical notes. In the process of scripting programs that perform computations with such objects, students are also exposed to numbers, characters, and strings. The next weeks of the course feature discussions of different sorts of "application architectures." In the context of these discussions, students learn to write their own methods. They also learn the basics of selection and repetition. Students are taught to appreciate various forms of abstraction-procedural abstraction, functional abstraction, and objective abstraction. Only at this point do students learn to craft their own classes. Throughout these weeks, students are taught to program incrementally, and debugging strategies are provided. Roughly two weeks are then devoted to a fairly intensive treatment of arrays and character strings. Searching, sorting, and merging are among the algorithms discussed. The last part of the course focuses on data modeling. The concept of inheritance is discussed generally and its realization in Java is detailed. Then, an overview of the Java API is presented.
Students are required to attend weekly supervised small-group laboratory sessions in which they engage in carefully crafted, extremely explicit activities designed to pave the way for programming assignments, which they are expected to do on their own time. This laboratory component appears to be very important to the success of the students. Good machines, editors, operating systems, and other software support notwithstanding, exacting technical prerequisites to successful programming are a reality. These "survival skills" are best dealt with directly. Here are two examples of the lab exercises:
- Students build a simple program by generating a special Java application template and filling in slots with a text editor. They compile and run their programs. The template generation and slot filling is done with just a few keystrokes. This slot-filling approach is used throughout the semester. The use of templates eliminates the need for novice programmers to deal with the relatively high declarative overhead of Java, such as that involved in defining and using packages.
- Scripted exercises that are explicit (down to the level of individual keystrokes) help students get acquainted with the text editor, operating system, and file system. Even with good tools, Java programming requires serious attention to pragmatic matters such as setting CLASSPATHs, placing files in the proper directories, and so on.
We have written about a dozen labs. Objectives are made explicit so that the role of labs in directly supporting the course is obvious. Directions are explicit so that students, for the most part, are able to complete the labs on their own. This approach frees lab instructors to engage in conceptual and philosophical discussions with students (this is otherwise a fairly rare occurrence at the CS1 level). More importantly, it facilitates operational independence for the student.
We are pleased with the way the course has been evolving. A great deal of work has been put into crafting packages expressly for use in this course. The use of special packages helps avoid problems and frustrations encountered when novices attempt to use native Java packages that were not constructed with novices in mind (for example, java.io). As the course continues to evolve, we intend to introduce at least a few elements of interaction by including a limited set of components derived from the standard API components. This transformation will have to be accomplished principally by changing examples, not by introducing a host of elaborate concepts. The course really can't accommodate many more of these! The objects of interaction will be simple to create and use (they will be quite limited), but we feel that their value could be great in terms of stimulating student interest and excitement.
The CS2 Course
Traditional CS2 courses typically cover standard data structures such as lists, stacks, queues, trees, and hash tables; implementation techniques based on arrays and linked structures for those data structures; recursive algorithms such as quicksort; and Big-O notation for analyzing time complexity. Our CS2 course covers these topics as well, but presents them as algorithmic techniques needed in the support of interactive computation.
The construction of applets serves as an entry point for most of the interactive topics. Java applets provide a solid (yet fun) introduction to topics like event handling, component frameworks, object-oriented design patterns, reactive computation, and user interfaces. Also, students are exposed to a few issues in concurrency and distribution surrounding threads and HTTP protocols, though these types are not treated in depth in our CS2 course. Coverage of other Java features, especially exceptions and interfaces, further contributes to the design and engineering focus of the course.
We are still experimenting with exact presentation and coverage details. (We do not follow a textbook, but do recommend students buy background texts on Java and data structures.) For example, the first part of one CS2 course proceeded via presentations, labs, and assignments surrounding:
- Modeling simple objects such as light bulbs and water tanks using Java classes, including those that provide visual appearance.
- Building applets that enable users to manipulate such objects, along with general discussions of event handling, subclassing, and object-oriented frameworks such as the AWT.
- Modeling objects that refer to others of their own kind; for example, a series of connected water tanks, each maintaining a reference to its successor, sending messages to that successor using recursive programming idioms.
- Generalizing such classes to the abstract notion of a sequential list, defined via interfaces, exception classes, and annotated pre- and post-conditions.
- Alternative implementation techniques for list interfaces; for example, those based on arrays and double linking.
- Asymptotic time complexity analysis (Big-O notation), along with empirical timing measurements.
- Using the Observer (also known as Model-View-Controller) design pattern to separate classes defining models from those supporting graphical presentation and manipulation, and designing several aspects of the resulting framework using the abstraction.
Later in the course, an assignment was given to create an online dictionary applet that supported searching for words either by exact or partial match, as well as operations to add and remove entries. The applet was to be based on an OrderedCollection interface that had been discussed earlier in class. Such a Collection keeps Keyed objects that identify themselves uniquely through a key method. Students first built a DictionaryElement class implementing the Keyed interface, and maintaining a word (the key) and a meaning. A Binary Search Tree implementation of OrderedCollection was analytically shown in class to be a good basis for the assignment, since it could efficiently support partial matching and ordered traversal algorithms. But, since the provided BinarySearchTree class didn't accommodate these directly, students needed to extend it to form a Dictionary class, mainly by adding a method that performed a partial match and returned the matching DictionaryElements. AWT components were used to control and display queries and other operations in the driver applet.
Several features of Java allow us to cover more material in CS2 than is typical. For example, because Java is intrinsically reference based, there is no separate notion of a pointer. Java garbage collection obviates the need to write error-prone storage management code (as in C++ "destructors"). And while students in our CS2 probably have just as many frustrating debugging sessions diagnosing Null reference exceptions in linked structures as those in any other version of CS2, the lack of sudden core dumps and machine crashes usually makes debugging easier than in most other languages-even without sophisticated tools.
Students learn to exploit standard Java packages to avoid reinventing common components. Motivated software reuse is a constant theme across our curriculum. This occasionally causes instructors to provide slightly artificial requirements for assigned programs-just to make clear that students should write original code in those cases where doing so is a worthwhile learning experience, and conversely, to teach students how to find, understand, and compose or subclass existing components when doing so is a more worthwhile learning experience.
During lab times, instructors provide help with the many and varied logistic matters involved in making Java applets run from web pages. While the templates from CS1 are not used, most early exercises and assignments provide students with a skeletal running version of the applet to which they must add classes and methods. This again helps avoid the paralysis that can occur when students make mistakes in some of the incantations needed to make applets work on the Web. Most semesters, students in the course establish a mailing list that they use to help each other out with such snags. They are also encouraged to check out web-based supplementary material (including many sample applets from which students are encouraged to extract ideas), as well as other web resources (like http://www.gamelan.com/) for guidance.
As the course proceeds, students become more proficient at mechanics, and very often add GUI and/or algorithmic niceties to their programming assignments that go far beyond stated requirements. A surprising number of students continue to polish their applets and present them on their home pages even after the course is over, and continue experimenting with Java on their own.
Java is by no means a perfect language. Nor does using Java in computer-science courses make the courses, instructors, or students perfect. For most colleges and universities, switching to Java provides a timely impetus to reexamine notions of what and how to teach, especially in the critical introductory courses that shape students' visions of what computing is all about. We do not pretend to have gotten all this right ourselves. Other promising approaches include Lynn Andrea Stein's experimental Java-based CS1 course at MIT, which focuses entirely on interaction. It's hard to predict where such efforts will lead, but that's part of the lure of teaching in the constantly and rapidly changing discipline of computer science.