JAVA learning from CS61B

JAVA learning from CS61BIntroWhat is CS61B mainly aboutCompilationClasstestingAd Hoc Testing vs. JUnittip: sort:Steps to developing according to TDD:Sample Runner PseudocodePrimitive Types and Data structuresDeclaring a variable:Declaring a class:Declaration and Instantiation of ArraysIntListImprovements on IntList and Data safetyStep I:Rebranding and CullingStep II: BureaucracyStep III: Access ControlStep IV: Nested ClassesaddLast() and size()Step V: Fast size()Step VI: Representing the Empty ListDLList and Other TrialsStep VII: Modify addLast Step VIII: fancier sentinel nodesGeneric(泛型) ListArraysNaive AListResizing ArrayGeneric AlistsObscurantism in JavaInheritance, Implements & InterfaceOverload(重写) and Override(重写)Inheritance ImplementsInterfaceExtends, Casting, higher order functionsTips: Any subclasses construct must include the superclass constructer

Intro

What is CS61B mainly about

Compilation

path

Class

Arrays of Objects

To create an array of objects:

First use the new keyword to create the array.

Then use new again for each object that you want to put in the array.

testing

Ad Hoc Testing vs. JUnit

ad hoc

JUnit

tip: sort:

Without changing the signature of public static void sort(String[] a), how can we use recursion? What might the recursive call look like?

Design Process

Steps to developing according to TDD:

Sample Runner Pseudocode

Primitive Types and Data structures

Declaring a variable:

When you declare a variable of a certain type in Java: Your computer sets aside exactly enough bits to hold a thing of that type.

Example: Declaring an int sets aside a “box” of 32 bits.

Example: Declaring a double sets aside a box of 64 bits.

Java creates an internal table that maps each variable name to a location. Java does NOT write anything into the reserved boxes. For safety, Java will not let access a variable that is uninitialized.

The Golden Rule of Equals (GRoE)

Given variables y and x: y = x copies all the bits from x into y.

Reference Types

Declaring a class:

When we instantiate an Object (e.g. Dog, Walrus, Planet):

Java first allocates a box of bits for each instance variable of the class and fills them with a default value (e.g. 0, null).

The constructor then usually fills every such box with some other value.

Reference Types Obey the Golden Rule of Equals

Just as with primitive types, the equals sign copies the bits. In terms of our visual metaphor, we “copy” the arrow by making the arrow in the b box point at the same instance as a.

Declaration and Instantiation of Arrays

Arrays are also Objects. As we’ve seen, objects are (usually) instantiated using the new keyword.

Note: Instantiated objects can be lost! If we were to reassign a to something else, we’d never be able to get the original Object back!

IntList

we need the following member variables and methods

Improvements on IntList and Data safety

While functional, “naked” linked lists like the one above are hard to use. Users of this class are probably going to need to know references very well, and be able to think recursively. Let’s make our users’ lives easier.

Step I:Rebranding and Culling

Step II: Bureaucracy

The Final Trial

While functional, “naked” linked lists like the IntList class are hard to use.

the visual structure

Step III: Access Control

We can prevent programmers from making such mistakes with the private keyword.

Hide implementation details from users of your class.

Car analogy:

Step IV: Nested Classes

Nested Classes are useful when a class doesn’t stand on its own and is obviously subordinate to another class.

Make the nested class private if other classes should never use the nested class. In my opinion, probably makes sense to make IntNode a nested private class. Hard to imagine other classes having a need to manipulate IntNodes.

Static Nested Classes

If the class don't need to call any variables outside , you can make it static

Static classes cannot access outer class’s instance variables or methods. Results in a minor savings of memory. See book for more details / exercise.

addLast() and size()

Create a private recursive helper method.

Have the public method call the private recursive helper method.

Step V: Fast size()

Solution: Maintain a special size variable that caches the size of the list. Caching: putting aside data to speed up retrieval.

TANSTAAFL: There ain't no such thing as a free lunch. But spreading the work over each add call is a net win in almost any circumstance.

Step VI: Representing the Empty List

When the List is empty, we may find error when add last. So one solution is to add a judgement when addLast , though making the whole function look worse. So we come up with another idea:

Sentinel Nodes

Notes:

MethodsNon-Obvious Improvements
addFirst(int x)Rebranding: IntList → IntNode
getFirstBureaucracy: SLList
sizeAccess Control: public → private
addLast(int x)Nested Class: Bringing IntNode into SLList
 Caching: Saving size as an int
 Generalizing: Adding a sentinel node to allow representation of the empty list.

DLList and Other Trials

Step VII: Modify addLast

On first thought, we tend to only add a .last, so w e can get the last fast , but then it becomes hard to remove.

We added .last. What other changes might we make so that remove is also fast? Add backwards links from every node. This yields a “doubly linked list” or DLList, as opposed to our earlier “singly linked list” or SLList.

Step VIII: fancier sentinel nodes

While fast, adding .last and .prev introduces lots of special cases. To avoid these, either: Add an additional sentBack sentinel at the end of the list. Make your linked list circular (highly recommended for project 1), with a single sentinel in the middle.

Generic(泛型) List

We might notice that our list only support Integers while we need to rewrite the whole class while we need other data types.

We’ll spend a lot more time with generics later, but here are the rules of thumb you’ll need for project 1:

DLList<Double> s1 = new DLList<>(5.3); double x = 9.3 + 15.2;s1.insertFront(x);

Arrays

Arrays consist of:

A fixed integer length (cannot change!) A sequence of N memory boxes where N=length, such that: All of the boxes hold the same type of value (and have same # of bits). The boxes are numbered 0 through length-1.

Like instances of classes: You get one reference when its created. If you reassign all variables containing that reference, you can never get the array back.

Unlike classes:

Do not have methods.

Arrays and Classes can both be used to organize a bunch of memory boxes.

 

Naive AList

So how to remove Last?

When we removeLast(), which memory boxes need to change? To what?-

User’s mental model: {5, 3, 1, 7, 22, -1} → {5, 3, 1, 7, 22}

abstract process

Resizing Array

When the array gets too full, e.g. addLast(11), just make a new array:

the improving should be all-around and considering its influence

works

much better

but its to slow and occupy to many memory in total

Geometric resizing is much faster: Just how much better will have to wait.

like this:

An AList should not only be efficient in time, but also efficient in space. Define the “usage ratio” R = size / items.length;

Typical solution: Half array size when R < 0.25.

More details in a few weeks.

Generic Alists

null out unnecessary items

Unlike integer based ALists, we actually want to null out deleted items. Java only destroys unwanted objects when the last reference has been lost. Keeping references to unneeded objects is sometimes called loitering. Save memory. Don’t loiter.

Obscurantism in Java

We talk of “layers of abstraction” often in computer science. Related concept: obscurantism. The user of a class does not and should not know how it works. The Java language allows you to enforce this with ideas like private! A good programmer obscures details from themselves, even within a class. Example: addFirst and resize should be written totally independently. You should not be thinking about the details of one method while writing the other. Simply trust that the other works. Breaking programming tasks down into small pieces (especially functions) helps with this greatly! Through judicious use of testing, we can build confidence in these small pieces, as we’ll see in the next lecture.

Inheritance, Implements & Interface

Firstly, let's talk about override and override,

Overload(重写) and Override(重写)

In Java, both override and overload are used to define a new behavior for a method. However, they have different meanings and use cases.

Override: When a subclass provides its own implementation for a method that is already present in its superclass, it is called method overriding. The new implementation of the methOverrideod in the subclass is said to override the implementation in the superclass. The signature of the overridden method must be the same as the method in the superclass, including the method name, return type, and parameter list.

Example:

In the above example, the Dog class overrides the makeSound() method of the Animal class to provide its own implementation.

Overload: When a class has multiple methods with the same name but different parameters, it is called method overloading. The methods must have the same name but different parameter lists. The return type of the method can be different, but it is not considered when overloading a method.

Example:

In the above example, the Calculator class has two methods with the same name add(), but with different parameter lists. This is an example of method overloading.

n 61b, we’ll always mark every overriding method with the @Override annotation.

Example: Mark AList.java’s overriding methods with @Override. The only effect of this tag is that the code won’t compile if it is not actually an overriding method.

In Java, inheritance and implements are two essential object-oriented programming (OOP) concepts that allow you to create new classes based on existing ones. They promote reuse of code and help you design a more organized and maintainable application.

Inheritance

Inheritance is a mechanism that enables one class to inherit properties (fields) and methods from another class. The class that is being inherited is called the superclass or parent class, and the class that inherits the superclass is called the subclass or derived class.

In Java, you use the extends keyword to establish an inheritance relationship between two classes.

Syntax:

When a subclass inherits a superclass, it has access to:

The subclass can also add new fields and methods or override existing methods. However, it cannot inherit the superclass's private fields and methods directly.

Example:

Implements

Java does not support multiple inheritance among classes, but it does allow a class to implement multiple interfaces. An interface is a collection of abstract methods (methods without a body) that can be implemented by any class.

To implement an interface, a class must use the implements keyword and provide a body for all the abstract methods declared in the interface. A class can implement multiple interfaces by separating them with a comma.

Syntax:

Example:

In this example, the Circle class implements the Drawable interface and provides an implementation for the draw method.

In summary, inheritance in Java allows you to create new classes by extending existing ones, while the implements keyword enables a class to implement one or more interfaces. Both concepts promote code reusability and help you design more organized and maintainable applications.

Interface

In Java, an interface is a collection of abstract methods and constants, which are defined without an implementation. An interface can be thought of as a contract or a blueprint for a class to implement. It specifies a set of methods and properties that a class must implement in order to be considered an implementation of that interface.

An interface can be declared using the interface keyword, followed by the name of the interface and its members. For example:

In this example, MyInterface declares two abstract methods method1() and method2(), and a constant CONSTANT. Any class that implements MyInterface must provide an implementation of these two methods, and can access the constant CONSTANT.

To implement an interface in a class, the implements keyword can be used. For example:

In this example, MyClass implements MyInterface and provides an implementation for the method1() and method2() methods. Once a class implements an interface, it must provide an implementation for all of the abstract methods defined in the interface.

One of the key benefits of interfaces is that they allow for polymorphism. This means that an object of a class that implements an interface can be treated as an instance of that interface. For example:

In this example, obj is an instance of MyClass, but it is treated as an instance of MyInterface. This allows for greater flexibility in designing code, as different implementations of an interface can be used interchangeably.

In addition to abstract methods and constants, an interface can also contain default methods and static methods, which provide a default implementation and can be called without an instance of the interface, respectively. Default methods were introduced in Java 8, and static methods in Java 8 and later versions.

So in java , an interface give lots of abstract methods and constants , while needing a class to implement them

Extends, Casting, higher order functions

In Java, the extends keyword is used to create a subclass that inherits properties and methods from a superclass. The subclass can access all the non-private members (methods and variables) of the superclass, and it can also override inherited methods or define new methods.

For example, let's say we have a Vehicle class with properties like make, model, and year. We can create a Car class that extends Vehicle and adds properties like numDoors and driveType. The Car class can also inherit and override methods from Vehicle.

Here's an example code snippet:

In this example, Car is a subclass of Vehicle and inherits the properties make, model, and year. It also adds two new properties, numDoors and driveType. The Car class overrides the startEngine() method from Vehicle to provide its own implementation, and it also adds a new method drift().

In Java, the super keyword is used to refer to the superclass of a subclass. It can be used to call the constructor, methods, and properties of the superclass from the subclass.

Tips: Any subclasses construct must include the superclass constructer

so if you want to make a new constructor , be sure to use the superclass constructor first

 

In Java, all objects inherit certain default methods from the Object class. These default methods are:

  1. equals(Object obj): This method is used to compare two objects and check if they are equal. It returns a boolean value: true if the objects are equal, and false otherwise.
  2. hashCode(): This method returns an integer value that represents the hash code of the object. The hash code is used to identify the object and is used in hash-based data structures such as HashMap.
  3. toString(): This method returns a string representation of the object. By default, this method returns the class name followed by the object's hash code.
  4. getClass(): This method returns the class object of the current object.
  5. finalize(): This method is called by the garbage collector before an object is destroyed. By default, this method does nothing, but it can be overridden by a subclass to perform cleanup operations.

It's worth noting that these methods can be overridden by subclasses to provide their own implementation. In addition, the Object class also provides other methods such as wait(), notify(), and notifyAll() that are used for thread synchronization, but these methods are not considered default methods as they are not inherited by all objects.