- if you are seeing these notes, you can already see all the course info on the course page
- I wanna help you understand!!!!!!
- NO QUESTIONS ARE DUMB!!!!!
- NEVER BE AFRAID TO ASK THEM!!!!
- DON’T STRUGGLE IN SILENCE ON YOUR PROJECTS/LABS!!!!!!!
- what is a university setting good for today?
- focus, practice, and access to people who know stuff
- I trust you!
- I try to be accommodating re: extensions
- I don’t think most cheaters are being lazy
- but you’re an adult, and are responsible for your actions.
- To expand your problem-solving toolkit with important data structures
- These are ways of representing the data that your programs deal with
- To understand when/why to use each kind
- Be able to objectively evaluate them and be able to justify your decisions
- To deepen your programming ability
- New ways of expressing more complex ideas
- Everything’s an object… even if it doesn’t make sense for it to be
- hey they were still figuring this stuff out in the early 90s
- Classes define the “archetype” for objects
- specify what data and methods they have
- that is, what they know and what they do
- When you use
new Classname(), that instantiates (makes a new instance of) that class
- you can make as many instances as you want
- you can make constructors, special methods which are called as part of the
- When you write
inst.method(), that is a method call
- it runs the code of that method, with the thing to the left of the
- method calls are really what make OOP… OOP
- everything else is just icing on top
- Java’s instances are always accessed by reference
- like the keys to a car: you can carry and pass around the keys easily
- variables of type
Classname can be
null or refer to an instance
- When you write
a = b, it does not make a copy of the object, it makes a copy of the reference
- multiple different variables can refer to the same instance - this is aliasing
- this applies to function arguments too:
- if you have
int f(Classname a) and do
f(b), it is like
a = b
Getters and Setters
- Data fields should usually be private and then accessed with getters and setters
- this feels like a weird arbitrary rule and a way to triple the amount of code you write
- in some cases, it is.
- What you might not have learned is that it is almost always better to access a variable/field in exactly one place than all over the place
- in this way, you can change how that variable is accessed by only changing one piece of code
- this is a form of abstraction: hiding the implementation of details
- Abstraction is a big part of data structure design
Static class members
- they have nothing to do with classes and instances
- they just happen to come inside classes cause… uh……………………..
- these are really “outside” of Java’s OOP framework
- they are commonly known in other languages as global variables and functions
- static fields are global variables
- there is exactly one copy of it throughout your program
- mutable (non-final) static fields are not usually a great practice
- but private ones, accessed through getters/setters can have their uses
- static methods are functions
- they have no
this; all they can see are their arguments and other statics
main is the one you’re most familiar with
- you could, if you tried really hard, write an entire Java program with only static methods, but it would be very un-Java-ful
- not to mention interacting with most libraries requires using OOP
- Level of organization above classes
- Kinda like a directory in your file system
- you know about e.g.
- How to make one
- Make a directory named
- Put your
.java files in it, and put
package packagename; at the top of each
- From parent dir,
- How to access one
- From parent dir,
- to import,
import packagename.Whatever or
import packagename.* for all
Making more complex objects
- Stick together multiple simpler objects to build up a more complex one
- Every class you’ve written is composed (has fields)
- But of course you can have fields which are, themselves, objects
- Be careful!
- Try not to make “kitchen sink classes” out of simpler ones
- it’s easy to use a 2x4 lego piece in many situations; it’s much harder to use a front-of-a-boat piece
- you want to aim for composability: classes which can be put together with other objects in various flexible ways
- Existing classes don’t always fit your needs
- Poor/inconsistent names
- Don’t want all methods to be available
- Want to add better error reporting
- An adapter class is one which “wraps” an existing class to change or add functionality
- Java has a few mechanisms for subtyping: making a new type which is a more specialized version of an existing type
- there are two types involved: the original type, called the supertype, and the new type being created, called the subtype
- the supertype is more general than the subtype
- the subtype “is a” supertype – anything that expects a supertype, you can use a subtype of it instead
- Subtyping creates a hierarchical relationship between types
- in Java,
Object is the root of the class hierarchy
- all classes derive ultimately from
Object - if you don’t “extend” another class, it implicitly extends
- all in all, you end up with a big tree
- but interfaces let you make other trees - we’ll get to that
Class Inheritance (
- You can make a class extend another class
- Essentially, you copy and paste the contents of the superclass into the subclass
- Then you can add or override functionality
- the subclass is a subtype of the superclass
- But class inheritance is not flexible
- a type hierarchy is only sometimes the right model of the data/behavior of a program
- using a hierarchy when you instead need e.g. “categories” of things can make your code harder to write and less flexible
- Class inheritance can also create “kitchen-sink base classes”
- as a program grows in complexity, it is tempting to shove more and more features into the base classes to reduce the amount of copying-and-pasting needed
- until you end up with something like JFrame - look at those “methods inherited from” sections!!!
- Java has no mechanism for automatic code reuse besides class inheritance
- So this kind of kitchen-sink base class arises out of need
- Class inheritance also adds complexities to other class mechanisms
- constructors are “chained” up the class inheritance tree
- each subclass calls the superclass constructor at the beginning of its constructors
- subclasses can only access
protected superclass members, even though they do inherit the
- subclasses can override superclass methods… unless the superclass marked it as
- Finally there are abstract classes
- they cannot be instantiated - they must be extended
- they have abstract methods which have no code; the subclasses must implement them
- abstract classes tend to be used as “dumping grounds” for common code
- they can often be better expressed through interfaces and adapter classes
Virtual methods (which some instructors didn’t teach in 401???)
- Why do we care about extending classes with new subclasses?
- So that the subclasses can have specialized behavior!
- One way we can have specialized behavior is with a virtual method
- This is a method defined in the base class, which the subclasses then re-implement
- If I said “go home”, you would all go home in different ways… but I don’t need to know how/where you go home, you just know how to do it.
- Very common virtual method:
- Defined in
Object; this is what Java calls when you e.g. concatenate an object with a string or print it out on the console (
System.out.println("My object: " + someObject))
Object gives you a default implementation, so if you print out a
SomeClass, it’ll show something like
- But you can define your own
String toString() in your classes, and it’ll use your implementation instead.
- Consider a
Vehicle class, where we want to have an
- We could have a
Car class, a
Plane class, a
Bicycle class which all extend
- And each one could implement
- So if we had an array of
Vehicle, and called
accelerate() on each one…
- How does it know which implementation of
accelerate() to call?
- This is the magic of virtual methods: it decides which one to call while the program is running, by looking at the underlying type of the object.
- If I have
Vehicle v = new Car();, and then call
- It looks at the REAL type of
v at runtime, and sees that it’s really a
- Then it calls the
Car.accelerate() method with
- How does this happen?
- Every class has an associated virtual method table (“vtable” or “vtbl” for short).
- Every instance has a hidden field which is a reference to its class’s vtable.
- When you write a method call like
- It gets
v’s vtable, then gets the
accelerate() method from there, and calls that.
- This way, it doesn’t matter what type
v really is: it will always get the “right” version of
accelerate() for it!
- There is one vtable for each class, and all instances of that class refer to that vtable.