SPEAKER 1: Hello, everybody.
Welcome to the CS50 Seminar.
Today, we'll be talking about
Java SE 8 programming basics.
Java is one of the most
popular and well-known coding
languages in the world.
And whether you've never
touched Java before,
or you've coded
extensively in it, I hope
that you'll find this
lecture helpful because we'll
be going over a review
of syntax, but also some
of the more foundational
and important concepts
that Java has to contribute
to us as programmers.
So let's get started.
Java, as a background, was founded
by Sun Microsystems in 1995.
That means today it is 22
years old, older than I
am, probably older than some of you.
It is an object-oriented
programming language.
And we'll cover a little bit more
in-depth what this means in a moment.
But it suffices to say that Java is
composed of objects that you create,
classes that you define,
and everything revolves
around this idea of us making these
objects and manipulating them.
Java runs on the Java
Virtual Machine, which
is something you have to
download on your computer
if you want to run Java code.
Unfortunately, this does mean
it's a little bit of a hassle
to get off the ground.
But this will have some benefits to it,
as well, that we'll cover in a moment.
Today, we'll be talking about SE 8--
that's Standard Edition 8 of
Java-- that was released in 2014.
Java is one of those languages
that's continuously updated, whenever
you're resolving a bug or you're
adding new features to the language.
In fact, there was actually a Java SE
9 released in September of this year.
But it's OK, because all the
ideas and the concepts that we're
going to talk about today have not
changed much at all between the two
updates.
So moving forward, what
are the benefits of Java?
Why should we learn about Java?
Why is it relevant today?
Well, first and foremost,
it is object-oriented,
which is a new way of-- or at
least, when it was founded--
a new way of looking at coding.
Beforehand, there was
procedural coding language,
such as C, that we learned in
class, and afterwards there
were things that came along called
functional programming languages,
like Python.
But being in the middle of these two,
object-oriented programming language
is both complex in that you
have a lot of creative room
to manipulate your objects and to add
your own creativity into your program,
but at the same time, it has
some built-in functionality,
like in Python, how we have certain
functions we can call that simplify
our task, that's the
same thing with Java,
because there is some built-in methods,
built-in classes, that help us.
Another very important benefit of
Java is the theory of encapsulation.
Encapsulation is this
idea that you create data
and you format it in a way such that
only certain people and certain users
can access it.
That way, if you're trading
sensitive information,
or you're building programs
for important causes,
there will not be any access to it
by people who are not authorized.
This is very important because
it is the key to secure coding,
and it is very important
for programmers if we
want to make our code
have a real world impact.
Java's also platform independent,
because when you compile Java code,
it compiles into bytecode,
which you can run
on any operating system, whether
it's iOS, or if it's Windows,
or if it's Linux.
And that means that it reduces the
hassle of converting, or basically
porting, between these
different platforms,
because you don't have
to recompile your code
and you don't have to
change your code in any way.
Because it stays inside of its
virtual machine, it will run anywhere.
Also, Java is very robust.
This is an upgrade that it got from C.
Java has no memory leaks, because Java
handles its memory, and it handles
garbage collection on its own.
We don't have to free memory.
We don't have to allocate memory.
And that reduces the hassle on the
coder and makes it a lot easier
to do what is relevant for our projects.
Java is also simple.
One of the other major
upgrades it got from C
is the fact that it no
longer has pointers.
You no longer have to worry about
where your addresses and memory are
because Java allocates them
for you, and therefore, it
is easier, as I said before, to
focus on the relevant tasks at hand.
And finally, Java is very secure.
I mentioned the Java Virtual
Machine a moment ago.
The Java Virtual Machine
is basically a sandbox.
And all Java programs run
only inside of this sandbox.
And they cannot affect your computer
in any other way outside of it.
This is very important because that way,
you cannot have any malicious attacks
on your hardware or on your memory or
anything like that from a Java program,
because it can only stay inside
a certain area of your computer.
And finally, but most
importantly, Java is everywhere.
It is almost impossible for you to
pursue a career in software engineering
or programming and not come across
some sort of Java files or projects.
In addition, if you think about
getting one of those jobs,
you most likely will have to learn
Java at some point in the future.
So I hope that today's
lecture will help give you
an introduction, a refresher, or
some motivation to go and explore
this fascinating language.
But before we dive into
the specifics, we also
have to talk a little bit
about the downsides of Java.
Yes, we have to
reintroduce the semi-colon.
I know in Python we are
able to eliminate this,
but in C, as in Java, we have
semi-colons as well as more complicated
syntax, because we are dealing with
a lower level programming language
than in Python, unfortunately.
So for those of you who
didn't exactly like C,
or thought that it was too
much hassle, potentially Java
could also be this way to you.
But I hope that I will be able
to convince you otherwise today.
So let's go with a roadmap of
what we're going to talk about.
As I said before, we'll start
off with some basic Java syntax
so that those of you who
are new to the language,
or who have not coded in
it for a while, can come up
to speed with the rest of us.
And then, we'll go into good class
design, because like David Malan talks
about in CS50, there are
good ways and bad ways
to implement the same project in code.
So how do we get the most
optimal, most efficient,
most elegant solution to our problem?
That comes through good class design.
And finally, we'll talk about the very
important concept of encapsulation
and how we can implement
that into our programs,
and why it's relevant in real life.
And hopefully, coming out of
this presentation, whenever
you look at a Java program or you
work with others on a Java project,
you'll be able to relate a
little bit less to this meme
that I'll share with you.
All right, now, moving forward, let's
start with object-oriented programming.
So I talked about how Java relates to
objects and classes that we define.
Well, what exactly is that?
What is the most basic
structure of a Java program?
Well, it's simply this.
Public class Animal--
notice here that we
have a class, which is
what we have defined,
and a name for our
class, which is animal.
By convention, the first letter
of our name is capitalized.
And notice here that we
have two curly braces, which
are what will encase our code.
Notice that there is no code inside,
but this program will still run,
and because this is the most basic
structure that you can have in Java.
So now, let's put something
inside of our program.
Namely, we have now created an object.
What is this object?
It is called name.
And what type is it?
It is a string object.
So just like we had strings in Python
and C, we now have strings in Java,
as well.
Not a big surprise, but
you'll see later that there
will be different functionalities
with this program.
Also notice the semi-colon,
because we'll be using that a lot.
OK, so in Java, there's
also a distinction
that we make between fields and methods.
So here, we have a class.
Class is Animal, and the difference
between fields and methods
is fields is basically
all of our objects,
and methods are all the things
that our objects can do.
So for instance, in this class called
Animal, we have an object called name.
And then we have a method
over here called getName.
And we distinguish methods by the
parentheses that we put afterwards.
And notice here in our getName we have
a word called public, which is a keyword
that we'll introduce in a moment.
We have a return type that we've
talked about in C and Python before.
It's called string.
We have the name of the method, and
then we have the body of the method,
too, which is return name.
So return name tells us exactly
what this method is going to do.
In this case, it just gives us back
this object that we have defined above.
So now, let's look at a
little bit more over here.
As another example of a method, we have
public void, setName, string, newName.
So in this case, we have a
different return type, namely void.
And we might have
touched on this before,
but void just means that there
is no return to the user.
When the user calls this method,
they don't expect anything back.
And then, we also have the
setName, name of the method.
But now, we have something inside.
This is called a parameter.
And the parameter has a type specified--
in this case, it's a string--
as well as a placeholder,
which is newName.
newName is not going to be an
actual variable or anything.
But whenever we call this
method, we'll provide an object
that newName will take on.
And newName is what is
accessed inside our code.
And we set our name variable
equal to this newName.
All right, so now, let's
move up a little bit
and talk about classes and files.
So every single Java file starts
with this header, public class
Animal, every single one of them.
And specifically, this word
right here, this title,
has to match the name of the Java file.
So if we were to look
into our computer, we
would see a file called animal
dot Java when we save this.
As you can see, if we open up
terminal or command prompt,
and we want to compile
and run this program,
we have to use a command called javac.
And then, we type in
the name of our program.
So javac is the compiler of Java.
And notice that, unlike C, where
we can have, like, c leng or make,
and we can customize our
compilers, Java has only one.
And this also reduces the
hassle that programmers
have to go through when you're trying
to construct and then execute code.
After we have compiled
Java, Animal dot Java,
we can now run it by using
this command, java Animal.
Notice that we have taken
off the Java at the end.
Why is that so?
It's because actually, when
we compile our program,
we have converted it into
bytecode, like I said before.
But it becomes a new file inside of
our folder that ends with dot class.
So in fact, we're not
calling Animal Java anymore.
We're calling Animal dot
class when we're executing it.
And what do you think this will print
after I've executed these two program
statements inside my computer?
If you thought nothing,
well, you're correct,
because this program has
nothing inside of it.
But in a while, we'll
have some programs that
actually have something that
will print out on your computer,
or that will make something happen.
All right, so now, let's
talk about the main method.
This is something that we had in Python
that was a little bit syntactically
challenging, mainly because
we had certain things we
had to type out that we weren't
exactly sure what it meant.
But the same thing kind
of happens in Java.
And this is how we get a lot
of our functionality with Java,
is this main method.
So let's take a look.
We have a program here
called public class Zoo.
And inside of it, there
is this very fancy line
that says public static void
main, string, bracket, args.
Some of these words
might be familiar to you.
Some of them might be very foreign.
But let's go over them.
So first is a static keyword.
Static is very important inside
of Java because it basically
means that you can call this
method without creating an object.
Usually you think, oh, if I have a
method or some action that I can do,
I need an object to do that action.
But in this case, we want to
make sure that we can run main
without having to create
instances of the class Zoo
because, when we run
this in our computer,
are we going to be able
to make a Zoo object when
we're executing a main method?
The answer is no, and
therefore, we have to specify
static so that it will run anyways.
Void is obviously the return type
because when you call the main method,
you don't expect to get
anything back, right?
You only get the print methods that
happen inside of the main method,
and therefore, the
return type would be void
because you're not getting
any objects or any output.
Finally, inside, we have our parameter.
And this parameter is very unique.
This is an array inside of Java.
And although we've talked about
arrays before in Python and in C,
obviously in Java, the array
syntax and the array structure
will be a little bit
different than before.
Notice that this is called args.
And why is it called args?
Well, it's because we're
accepting command line
arguments with this parameter.
And we've touched on those
before with C and Python.
But let's take a look at Java's specific
command line argument structure.
So two things that you'll notice with
this expanded program-- first of all,
we have something called
system dot out dot println.
So this is kind of what printf is
in C and what print is in Java.
This is the way you type out a
specification for the program
to print something on
your computer screen.
In this case, what is it printing?
It is printing the first element
inside of this array of arguments.
In Java, unlike in other
programming languages,
the first element of
your arguments is not
going to actually be
the file you're running.
It's going to be what comes
immediately afterwards.
So if I were to just execute, say,
Java Zoo with nothing afterwards,
no command line arguments
afterwards, this first element
would actually be null.
There would be nothing there.
And then, we have our
second element inside
of the string array of arguments.
And this is also going
to be the second thing
that we put after our program name.
So if I were to say Java Zoo,
and then I say, like, city,
well, this is a second
element of the array.
And in this case, we have
not set anything there.
So it would be null, as well.
Well, let's take a look at us trying to
implement this program in our terminal
or in our command prompt.
So first, obviously, we would have
to compile it, javac Zoo dot Java.
And then afterwards, we assume
that it compiles without error.
Let us try to run it.
And in this case, we run it
with the Java keyword Zoo
And then we put in two command line
arguments, Bronx Zoo specifically.
So what will be printed out afterwards?
Exactly what we expected it
to, Bronx, new line, Zoo.
Notice here that our
command line arguments,
like we did in Python and in
C, were separated by spaces.
So you might ask yourself, well, what
if I wanted to give it a parameter,
that is, two words?
How do I separate those two
words without using a space?
How do I make it look like one
command line argument instead of two?
Well, the answer is you simply
have to put quotation marks around.
And in this case, we would treat
San Diego as one command line
argument instead of two.
And therefore, when we print it out,
we will get San Diego on one line
and then Zoo on the next.
All right, now let's
take a look at something
that we might try to do
that is not going to work.
So if I compile Zoo, and then I try to
run Zoo with one command line argument,
what do you think will happen?
Probably an error, right?
And that is actually
exactly what happens.
You'll get this exception
called ArrayIndexOutOfBounds.
And why is that the case?
It's because Java recognizes
that we created a code,
we created a program,
that has a reference
to the second element of an array.
But in this case, we have only
added one element to that array.
And if we're trying to access an index
of the array that does not exist,
it will create this exception
and throw it back at us.
Exceptions are a fancy word
that basically means an error.
But in Java, errors and exceptions
are a little bit different.
And this is something
that you'll definitely
learn about as you move on in Java, but
we will not be covering today in class.
All right, so moving forward,
let's make a distinction
between primitives and objects.
So before, we already
talked about things
like a name, which is a string object.
String is a class inside of Java.
It's granted a class that Java
has already created for us,
and therefore, it is a special class.
But nonetheless, it is an
object instead of a primitive.
And notice here that when
we are creating this object,
we are telling it exactly what
goes inside of it, namely,
in this case, my name.
In this case, there is also what is
called a new parameter, a new keyword,
which is what you will
use when you're creating
an object for the first time,
when you're referencing it
for the first time.
Afterwards, you will have to repeat
the variable type of the object,
in this case, string.
It should match what is on the
left hand side of the equal sign.
And then, you have what is
inside, which is my name.
Now let's take a look at a primitive.
So in this case, integer is specified
by Java as a primitive class,
meaning that Java sees that people
use this class so often that it should
build in some functionalities and
make it easier for people to create
and manipulate this variable
type, so that they don't
have to go through as much hassle.
So notice here that with this, we have
int as our specifier at the beginning,
but then, we do not have
any of this fancy new--
or we don't have to type out int again.
We just simply specify the name,
and then specify the value.
And that cuts down on
a lot of the hassle
that programmers have to go through
when they're making these objects.
So here is just, very briefly,
a rundown of all the primitives
that Java supports.
We have boolean, which we've seen
before, obviously, true or false.
We have, then, four of these
variables, byte, short, int, long,
that serve kind of the
same purpose as what we
saw in C with different sized integers.
They basically store integers, but they
have different spaces allocated them,
8 bits, 16 bits, 32 bits, and 64 bits.
Afterwards, we have two more
primitive types, float and double,
which both store decimals.
And again, double has twice the space
that float has to store decimals.
And finally, we have char,
which is a 16-bit unicode value
to store letters of the alphabet,
apostrophes, punctuation,
et cetera, et cetera.
All right, so going forward, we're
going to focus a little bit more
on constructors.
Just to give you a
brief rundown of these,
we have a program here to look at.
This is public class Chick.
These are baby chickens.
And obviously, we want to
create our own chick object.
So how do we go about doing that?
Well, first thing to notice
is that we, up here, have
created an object with the name, name.
And what do we put inside of it?
We put "Fluffy."
Notice here that this is a little
different from the argument
that we saw on the previous slide, which
had this new string inside of the line.
Why don't we have a new
string inside of this line?
Well, that's because Java recognizes
that string as a special class.
People are going to
be using string a lot.
And even though Java doesn't
make it a primitive type,
it does simplify it
so that we don't have
to put the new operator
every single time we
create an instance of this object.
That's just to cut down on the hassle
that programmers have to go through.
Next, notice this block of code.
This block of code is
very important because it
is what is called a constructor.
When we create this class and
we're typing all this out,
we need to have a constructor in order
to create instances of this class,
to create chick objects, right?
Because this fundamentally is
like a blueprint for a chick.
But in order to create the actual chick
itself, we need this block of code.
It starts out with the word public,
which is, again, a specifier that we'll
talk about in a moment.
And then it has a name,
Chick, and then parentheses.
So this looks similar to the
format that we had for methods,
except in this case,
the name of the method
is the same as the name of the class.
And this is very
important because we have
to have this specified here in order for
Java to know that it is a constructor,
instead of a regular method.
The names have to match, and they have
to be identical, case and everything.
Inside notice that we can run
arguments that change variables
or create new variables.
In this case, what we're
doing is we're taking the name
object that we created before and giving
it a new value, in this case, the word
"tiny."
And then, below, when we use our main
method that we talked about earlier,
we can create an object
that is a chick object.
And in this case, we have
to use a new parameter.
We have to say, Chick, over here.
And notice that this way, we have
a new object successfully created.
And then, if we go on to
the very bottom and we
try to print out the chick's name,
what do you think we would get?
If we were to go over here and
compile Chick, and then run Chick,
what do you think will be printed?
If you said "tiny," you are correct.
And notice that this is because,
even though we started out
with "Fluffy" at the top for
the value of our parameter,
inside of our constructor, we
changed its value to "tiny."
And therefore, when we create
every object of the Chick class,
it will always have "tiny" stored
inside of it instead of "Fluffy."
All right, so going forward, let's look
at a slightly more complicated program.
The first thing you'll
notice is that we have
a lot of the same features,
Fluffy, tiny, constructor.
But there is another constructor here.
Why do we have two of these, and
why are they called the same thing?
This is a very important concept
in Java called overloading.
Overloading in Java is where you
have constructors or methods that
have the same name.
They could potentially have
the same return type, as well.
But the difference is in
what parameters they accept.
The first constructor
accepts no parameters.
But the second constructor
accepts a parameter
called name, which is a string object.
And what specifically does it
do with this string object?
Well, it calls itself--
this dot name is referencing the
name that is inside of the file,
and it's setting its value equal
to the name that the user inputs.
Notice here that because we have
two objects with the title name,
we have to distinguish between
them by using the this operator.
The this operator is
optional, obviously,
when we're just talking about
one variable of that name.
But when we have two,
it's useful to have this
here so we know which one is
which and we don't get confused.
And notice here that because we have
this additional constructor, when
we create the object below, we can
choose to specify a name inside of it.
And with the specified name,
when we print out the chick name,
we will get something of our choosing.
So what will this print, in
this case, given that before, we
had the chick "Cutie" as our constructor
instead of just a blank constructor?
That is correct.
We will print "Cutie"
out instead of "Fluffy"
and instead of "tiny,"
because, in this case,
we've accessed a different
constructor than before.
All right, now let's go ahead and talk
about declarations and initializations.
And here's where I want to make a
specific reference to all the objects
and all the changes in values
that we've made in this program,
highlighted in yellow.
So at the very top, we have our
first declaration of this object.
The distinction between
declaration and initialization
is the declaration is the left
hand side of this equal sign.
That is where we just say
that this object exists.
That is our declaration of the object.
But do we know what is inside of it yet?
Not exactly.
After the equal sign is
where we specify the data
that goes inside of this object.
And that is when we have initialized it.
Is a very important distinction
because you can create the object
without putting anything inside.
Therefore, you are just declaring
but not instantiating it.
Afterwards, when we
run name equals tiny,
we're not declaring or
initializing any objects,
but we're just rather
changing the value inside.
And if we look below at our
final example in this program,
when we create our new
chick object, we're
again declaring that a
chick exists, and then we're
telling it what exactly it is
that this chick object has inside.
So you have to make sure you understand
the distinction between this,
because sometimes you will
see only the left side
of the equal sign in your programs.
And you have to make sure you
understand that the program will still
run and still work, because
you are declaring the object,
not yet initializing it.
All right, now, I will go through
very quickly some Java statements.
These statements are
just logical statements
that we have also in C and in Python.
Namely, we have the if-then statement--
we've definitely talked
about this before--
if-then-else statements, also that we've
referenced before, while statements,
do-while statements, and for statements.
For the sake of time, and so we
can go into more complex and cooler
concepts in Java, I'm going to skip
over these because they follow basically
the same logic as in other languages.
The only difference might be the syntax.
And so I'll cover that very
quickly here with two examples.
Notice that in our if statement,
we have, once again, parentheses.
Sorry.
In Python, you might have gotten
used to not using parentheses,
but we're back to using parentheses.
Notice that we have our logical
operator, equals equals.
And notice that NULL, when we
specify it, NULL is a keyword
and it's in all caps.
Down below, we have a for statement.
And notice that this looks very similar
to what we have in C. We have the three
parts of our parentheses separated
by semi-colons, starts the int at 0,
increases it each time using
the plus plus operator,
and also uses the less-than operator
to specify when this for loop will end.
And every time, it just
prints out i, the integer.
Cool.
So now we're ready to get into
the fun part of the lecture, which
talks about class design.
Like I mentioned before,
there are good and bad ways
to implement the same project
to code the same thing in Java.
But how do we make sure
that we're coding it
in the most elegant, most
efficient way possible?
So let's take a look
at two files in Java.
The first one is public class Animal.
This is an animal that we're creating.
Inside of it there is an age,
as well as a method for us
to get the age of this animal.
On the right hand side, we have a tiger.
We're creating a tiger.
And it also has an age,
obviously, because tigers
are animals and tigers have ages.
And there is a way to get the
age, too, using our age method.
Beneath, we also have
another method called roar,
because this tiger can roar.
Not all animals can
roar, but a tiger can.
But do you notice something
about this code that
makes it not exactly the most efficient,
not actually the cleanest solution?
Yes, exactly.
You have two repeated
redundant sections up here.
These say the same exact thing.
So if you're a programmer
looking to cut down
on the number of lines
of code you're using,
or just to make your
solution cleaner, how
would you think is a good way to combat
this, combat this repetitiveness,
and to make this more compressed,
more precise, more concise?
This is how we came about with
the idea of class inheritance.
And this is a concept that's been
around in Java for a long time.
It basically says that if
you have a certain class,
and you create child
classes of it, we should
let the child classes share all the
features inside of the parent class,
so to speak.
That way, we can cut down
on the length of our code
and also cut down on how many
of the repetitive features
we have to implement.
Inside of Java, we have only
what's called single inheritance,
meaning that each child class
can only have one parent class.
The reason why it can't have
more than one parent class
is because of the complexities
that arise when you have, like,
a certain value inside of one
parent class that does not
match the value inside a different one.
And if you get the child class to
inherit both of them, which one should
it use?
That creates an issue,
and that's something
that Java doesn't want to be a part of.
And therefore, it says you
can only have one parent.
So now, let's take a look logically
at what this would entail.
I know, animals, beautiful.
Animal is our parent class.
It is the one that is not
referencing anything higher than it.
But notice that all these other
classes are children of it.
Mammals are children of Animal,
Bird are children of Animals.
And then below it, we also have
bats that the children of mammals,
and tigers that are children
and mammals, et cetera,
et cetera, et cetera.
And this is kind of
the logical structure
that you want to have
in your mind as you
are designing these
classes because that way,
if Animal has something like
a name, we can just specify it
and then say that the children
classes are inheriting
from it so that we don't have to specify
a name inside of all of our children.
So let's take a look
again at our two classes.
But now, notice that we have
done something different.
Over here on the right,
we have Tiger, and then we
have a very important
keyword, extends Animal.
This is how we specify that
Tiger is a child class,
and Animal is the parent class of child.
We use the extends keyword,
and then we specify
the name of that class afterwards.
And because we are
extending it that means
that we have all of the information
inside of it at our disposal, namely,
the age and the getAge method
that are inside of Animal before.
We are now able to use inside of
Tiger without typing it a second time.
And this definitely cuts
down on all of the hassle
that a programmer has to go
through when they're coding Tiger,
or when they're coding other
potential classes like Bird,
or Crocodile, or something like that.
They don't have that keep specifying
age and typing the same code over,
and over, and over.
They can just say it inherits
it, and therefore, they're
done with this part of the code.
Now, I have gone ahead and added a
second method to the Tiger class.
As a child class, obviously, we want
to do something with this information.
Are we are able to reference it?
The answer is yes.
Notice here that I have created an age
method that uses the getAge method.
The getAge method is not
inside of the Tiger class,
but rather inside the Animal class.
But this is still valid because
Tiger is a child of Animal.
And then afterwards, we're able
to print out the age of the tiger
by simply referencing it from above.
So now, let's take a look at a
more complicated pair of classes.
Notice here on the left, we
have a class called Animal.
Animal, as we have before, has an age.
But now, we have also given it a name.
So notice that we have
two constructors again.
We're overloading our constructors
because the first one has
two parameters, an age and a
name, and our second one only
has one parameter, just an age.
But notice that we're
now going to highlight
a very important key word, super.
What does super mean?
Super is a keyword introducing
Java to reference a parent class.
So if you're inside of
your child class and you
want to reference something
that isn't your parent class,
you can use a super
operator in order to do so.
In this case, the super
parentheses references
the constructor of the parent class.
This is very useful
because that way, we can
cut down on the implementation
inside of our constructors, as well.
We can make it easier
by just saying, hey,
our parent class did
this in its constructor.
Let's just do that again here, and
specify it with the super keyword
so we don't have to do it again.
Now, the question may
arise in your minds,
what's the parent class of Animal?
We haven't specified anything there.
Well Java has hidden this fact from you.
And it is because usually
it's not very useful at all.
But the reality is, in
Java, every single class
that we create is inherently
a child class of a class
that Java has created
beforehand called object.
Object dot Java is built
inside of Java, and it
acts as the parent class for
everything else inside of Java.
It is also very important because it
builds all the functionality of, like,
string classes and primitives
and stuff like that so we
don't have to worry about it.
But this means that we
can also reference it
by using the super keyword.
So what this means is, inside
of this Animal constructor,
we are referencing an
object constructor.
Well, in this case, object
constructors don't really do anything.
So this line of code
is pretty much useless.
But it's good for
demonstration purposes.
And afterwards, we are
creating and manipulating
the variables inside of this constructor
using age, name, et cetera, et cetera.
Notice how this super
operator is also optional
whenever we're talking
about the Animal class,
because we do not include it here.
Like I said before, this was
pretty much useless and therefore,
why would you have to
put it in your code?
But now, we're going to talk about
something more interesting, which
is over there in the Gorilla subclass.
The Gorilla subclass
extends the Animal class.
And in this case, what happens is
we have two constructors inside
of the Gorilla class.
Notice that we're overloading them
once again, because one of them
has one parameter, the other has none.
But notice that, in both of them now, we
are calling the super keyword in order
to reference all the features
inside of our Animal constructors.
So over here in our first Gorilla
constructor, we input an age.
What does a Gorilla constructor do?
It goes in and says,
hey, Animal class, can I
borrow your constructor
that takes two inputs?
And then Animal says, all right, this
is my constructor that takes two inputs.
And Gorilla says, all
right, put in an age for me,
and put in the word gorilla.
And what is going to come
back is these actions.
So by going over to Animal and
referencing its constructor,
we now take the age and store it inside
of the age variable, We take Gorilla,
and we store it inside the name
variable, and voila, it's done.
We could have specified this in a
ton of lines of code by repeating it.
But instead, we just
used the super keyword
to get rid of all the redundancies.
And notice below that we
also have a second reference
of super in our second constructor.
In this case, we just
say, hey, Animal, can you
give me a constructor that
has only one parameter?
And Animal says, sure.
Give me an integer.
And then Gorilla gives it the integer 5.
It stores it inside of the age.
It gives a name as
NULL, and it returns it.
And therefore, you're done
constructing your second gorilla.
So notice how this entire program
takes basically no lines at all.
And it could have taken
a lot more if we did not
have this concept of inheritance and
the super keyword to help us out.
So now let's move on to something a
little bit more fun, specifically what
are called abstract classes.
Abstract classes are
very, very important
because these are classes
that maybe you do not
know exactly what you want
to be inside of them yet,
but you know that this
should exist anyways.
It's kind of like a
blueprint for a house.
You're not building a house yet.
But you kind of have an idea of
what you want inside of it, like you
want a bathroom, a kitchen, a bedroom.
And based off of this, you can create
actual house classes or actual house
objects.
You're just not ready at
this point yet in your code.
And that's why abstract
classes are important.
Let's take a look.
Here, we have an abstract
class called Animal.
Notice here that we have a
child class that extends Animal.
So Animal is the parent class.
Swan is the child class.
And up here, very important,
we have an abstract keyword
that we have not introduced.
And specifically, inside of
Animal, we have an abstract method.
Public abstract string getName
has nothing inside of it.
It doesn't even have the braces anymore.
It just has a semi-colon
to signify that there
is nothing to be implemented here.
The reason being, when you
have an abstract method,
it means that you're leaving it blank
for children classes to take care of.
You're not going to
implement it in your class
because you don't know
what it's going to do yet.
Why don't we know what it's going to do?
Maybe we don't know the
name of the animal yet.
Therefore, we cannot get the name.
But we know that at some
point in the future, when
we create different objects,
they are going to have names.
And we want them to be
able to get the names.
So we specify in our blueprint that
there will be a getName method.
But we don't create it just yet.
And now when we go below to Swan, we
are extending the Animal parent class,
in this case, the abstract class.
And then we are implementing the
abstract method that was in Animal.
Public string, getName-- we
drop the abstract keyword,
and now we have braces.
And we implement it inside.
So notice that in this case, we
have to make everything identical.
And then by implementing this
method, we are successfully taking
our blueprint and building
something concrete out of it.
We're taking our getName
method and putting a swan
here because we now know exactly what
information we want to put inside.
Now, I have a question for you.
What if we have the public
class Swan extending the Animal,
but we do not try to
implement the getName method?
What if, instead, we just have a random
method here that says, like, getAge,
and it takes the age and returns it?
What would happen here?
Will this compile?
The answer is no,
unfortunately, because Java
notices that you had an abstract
class with an abstract method.
When you create your child
class and you inherit everything
from the parent class, you
inherited an abstract method.
But you did not implement it.
You did not take your blueprint
and do anything with it.
So what is happening here?
Java is saying that, unfortunately,
your class cannot be concrete because it
still has something
abstract inside of it.
How do we fix this?
Well, the only way to fix it is to
make the Swan class abstract, as well.
Because Swan class is a
child of Animal, if Animal
has an abstract method the Swan
does not implement concretely,
Swan also has to be abstract.
The other solution to this is obviously
what we saw before, where it just
implements the getName method.
But if, for some reason,
we didn't want to,
we would have to specify
Swan as an abstract class.
Even though it might have these
concrete methods inside of it,
as it has more than
zero abstract methods,
as long as it has at
least one abstract method,
it has to be specified as abstract.
And one of the downsides of abstract
methods and abstract classes is that
you cannot create instances of it.
That means we can't
create an Animal object.
And in this case, we can't
create a Swan object, either,
because we have an
abstract keyword inside,
All right, so now,
let's go ahead and talk
about something a little
bit more complicated,
a little bit less
concrete, too, interfaces.
So in Java, sometimes you want to have
multiple parents for a single class.
But obviously, with abstract
classes and inheritance in general,
we can't do that.
But we can do that with interfaces.
And interfaces thus bring along the
concept of multiple inheritance.
One class can inherit
multiple interfaces
because interfaces, as
we'll define in a moment,
are kind of like what they sound like.
They're very, very flexible,
very, very elastic programs
that do exactly what
you want them to do,
and don't have much
already set inside of them.
They're kind of like a touch screen.
They're kind of like
a holograph, something
that you can control to your own
will and manipulate it like you wish.
So let's take a look at
an interface example.
So public interface has Tail.
Notice the inside of this,
we have an abstract method.
But do we have anything else?
The answer is no, because
inside of an interface,
you cannot have anything concrete.
All you can have are abstract methods.
You can't even have variables.
So inside of this abstract class,
we have specified something
that we will potentially
implement, a getTailLength method.
But do we have any idea what our
tail length is, or how we get it?
Not right now.
We have to wait until later when
we implement the Mouse class.
Notice here that the Mouse
uses an important keyword
called implement hasTail.
This means that we are taking interface
and putting it inside of our class,
and we're going to specify concretely
all of the abstract methods
that the interface had before.
In this case, the only abstract
method we had was getTailLength.
And so we did that below with
our call of getTailLength,
and then our returning
of the tail length,
as we now have a tail length to return,
because we've specified a Mouse,
and mice have tail lengths
of 5, for some reason.
So let's take a look at how interfaces
are different from abstract classes,
specifically because now, we are
going to create two interfaces.
One has the name hasTail.
The other has the name hasWhiskers.
And in this case, when
we create a Mouse class,
we can implement both of the interfaces.
This is something we could not do with
the extend keyword and we cannot do
with our inheritance alone.
In this case, we can inherit
both of these interfaces,
and then we have to
implement both of them.
Notice that here, I have
accidentally missed something.
I did not have a getNumberOfWhiskers
method at the end.
What do you think will
happen to this code?
That is correct.
There will be a compiler error
specifically for this line
because here, we have specified
a method that is abstract,
but we have not implemented it below.
We have not concretely implemented
it below, and therefore,
Mouse cannot be a concrete class.
Now wait a minute.
You might be telling me
to back up because you
see that in this line of code, we
don't have an abstract keyword.
Well, the reason is in
interfaces, Java will
assume that everything is abstract.
We don't even have to put
the word abstract in it.
It will automatically
assume that it is in there,
and the reason being that it knows
that if we put a semi-colon after
the parentheses, there cannot be
anything inside of this method.
Because it's in an interface, that
means that it has to be abstract.
So this is another way that
Java simplifies the hassle
that programmers have to go through.
But in this case, how
do we solve the problem?
Well, if we're trying to just
solve the compiler error,
something we could obviously
do is just put the abstract
in front of the Mouse class, and
now, it is an abstract class.
Because it has this abstract method
inside that it has not implemented,
we will just specify it is abstract.
But now, we cannot
create any Mouse objects.
Obviously, there are such
thing as mice objects,
and therefore we want to create them.
So how do we make a
better solution to this?
A better solution is here.
Notice they have the
same two interfaces,
but now, we have implemented the method.
We have said, getNumberOfWhiskers
you return the number of whiskers,
and we specify the number of
whiskers above, as 10 for mice.
But you have to be careful here.
Notice how we have also
created a constructor inside
of our class that
specifies a tail length.
Well, why is this not good class design?
Yes, it's because we have no
control over the number of whiskers
the mouse might have.
Good class design would
dictate that we also
create a second constructor that
specifies both the tail length
and the number of whiskers
so that even though we're
overloading the constructors, we can
now change the number of whiskers
that the mouse might have to our desire.
And that way, when we
get our tail length
and we get out number of whiskers, we
don't just get 10 every single time
we call this method.
We can get something different
based on how we create our mouse.
And this is part of the concept of
good class design, because this way,
you give the user more
functionality with your program.
The user can do more with it, and
therefore, it has more potential
to help you in other projects.
All right, so going forward, we have
just covered some good class design
by talking about inheritance,
interfaces, abstract classes.
And now, we're going to turn it over to
the final section of the seminar, which
will talk about methods and
the concept of encapsulation.
To understand encapsulation, we first
have to talk about what in the world
are packages.
Packages are kind of, in Java, what
you think of in real life as folders.
These are just little boxes that
you put relevant classes inside of.
And it groups it together so
they have some common things
and some common functions that you can
call once instead of multiple times.
So let's take a look at a package.
So this, at the very top, is what is
inside of your computer's directory
when you are accessing this class.
It is inside of a
folder called packagea.
It is called ClassA dot Java.
Notice here that when we
look at our actual program,
we have what is specified at
the top as package, packagea.
This is the name of the package.
This is the package-specifying keyword.
And down below, we start
implementing our actual class,
which has a name as we have
specified above in the file.
So this just means that ClassA is
inside of this package called packagea.
Well, that's not that much fun.
But what about if we
created another package?
So now, we are inside of a
different folder, packageb.
And we have a ClassB file inside of it.
Notice that ClassB belongs
to the package packageb,
and it imports packagea dot class.
We've talked about the
import method before.
And importing is very
important because now, we
can access things that are
inside of ClassA within ClassB.
Specifically, we now
can create an object
of type ClassA inside of our ClassB
And this might get
confusing very quickly.
But for now, it suffices to
say that packages are important
just so we can group things together.
And that will be relevant when we
start talking about encapsulation.
All right, so going back to a slide
that we talked about previously
with making methods, I want us to take a
moment to look at these words over here
that I said we would talk about leader.
Public is what is called
an access modifier.
An access modifier in Java specifies
who exactly can see our information.
When I talked earlier about
the concept of encapsulation,
it is so that only specific users, only
specific programmers, that we want to,
can have access to our code,
can be able to change our code.
Well, how do we specify who
gets to look at our code?
It is with our access modifiers.
Here, everything is public, which
sounds basically like what it means.
It means that it's public to anybody.
Anybody can use this.
Anybody can change it.
Anybody can reference these
methods and reference this class.
But what if we wanted a different
degree of accessibility for our files,
or for our variables,
or for our methods?
How do we specify that without
just the public keyword?
Well, Java actually has
four different levels
of specificity for
different levels of access.
Let us start with the most
restrictive access modifier.
It is called private.
Private means exactly what it is.
It is very private
because when you specify
a private variable or private
method, only the class that is inside
can use it.
Nothing outside of that class can
ever reference this private method
or this private object.
Afterwards, we have default, also
known as package private access.
Default is not a keyword
we'll ever see in a program,
because default is just
what happens if you
don't put an access modifier in front of
your object or in front of your method.
Default means that only other
classes inside of your package,
inside of your folder, will
be able to access and change
the parameters that are specified
as default. Nothing outside of that,
no other folders in other directories,
and not even your child classes,
can touch your objects and your methods.
The second to least most
restrictive is protected.
The protected access modifier
says that any classes that
are children class or inside the
same package can access your methods
and access your variables
that you specify as protected
as a keyword in front.
And finally, we have
obviously public, which
says that anybody who references this
class can reference the variables
in the methods inside of it.
So why does Java have all these
different differing levels
of accessibility?
Why didn't we ever talk about
this in C, and why did we never
talk about this in Python?
The reason being that it is
especially important for Java
as a real world coding language,
because for a lot of Java projects
that handle sensitive information, like
potentially bank accounts or deposits
and transactions, you
don't want certain people,
like customers potentially, or clients,
to be able to see, like, the reserves
that the bank has, or the deposits
that other users are making.
Therefore, making certain information
private or default or protected
to the differing levels that you want
gives you customization, and also
gives you security with your programs.
So let us talk about a duck study, a
case study in which we will highlight
all the different features
of accessibility and access
modifiers using a family of ducks
that we are going to illustrate below.
Notice here that we have one big
package called pond dot duck.
Inside, we have four classes,
FatherDuck, MotherDuck, BadDuckling,
and GoodDuckling.
And then we create another
package called pond dot swan.
And inside of it, we have what
is called a BadCygnet class.
So what are we going to
do with this case study?
Let's start by looking at
the file for FatherDuck.
Notice the inside of it, we have
the private keyword for everything.
FatherDuck is very private
about all of his actions.
He doesn't want others to be
able to see or imitate him.
So at the top, he obviously
quacks because he is a duck.
But he is private with it.
Well, we can also call it to
quack, or for it to make noise,
by using the quack method
that is also private.
Notice here that we are referencing
noise, which is a private parameter.
But because we are in the same class,
it is valid for us to reference it.
Now, notice below that we are also
specifying a makeNoise method.
And makeNoise is also going to reference
the quack method inside of FatherDuck.
But because it is inside the same
class, even though quack is private,
you can still reference it validly.
So having set FatherDuck
in the water, we now
go back and talk about BadDuckling.
BadDuckling really wants
to be like FatherDuck.
So inside of its makeNoise method,
it will create a FatherDuck object.
First of all, how can you
create a FatherDuck object
inside of the BadDuckling class?
It's because they're
in the same package.
They're both part of pond dot duck,
and therefore, like we saw before,
you can create instances
of the other class
because they all start
with the public keyword.
However, after you've created
your FatherDuck object,
you try to make it quack.
But can BadDuckling really
quack like FatherDuck does?
The answer is no,
because quack is private,
and therefore, only FatherDuck
inside of its own class can quack.
The FatherDuck inside of this
class cannot quack because it is
in a different class, even though it
is referencing the same type of object.
The same thing will
happen when you go below
and you try to make
the duck create noise,
and you access the noise variable.
The noise variable was also
private inside of FatherDuck,
and therefore, it is
not accessible here.
Now let's take a look at MotherDuck.
MotherDuck is a little bit more
forgiving with what she does.
Instead of making
everything private, she just
makes everything default, default
meaning that when she quacks,
there is nothing in the front.
When she creates a quack method,
there is nothing in the front.
And when she makes noise,
there's nothing in the front.
Notice that these are
all valid references
to the noise variable
and the quack method,
because as the default specifier,
not only can MotherDuck access
its own methods, but its children--
and other classes inside of its package,
not its children-- other classes inside
of its package can access its
methods and as variables, as well.
So let's take a look at GoodDuckling.
GoodDuckling, as a class, is
inside of the same package,
and therefore, it really
wants to imitate MotherDuck.
And how does it do it?
It creates a MotherDuck object, which
we can before because it was public,
and it tries to quack and make
duck noises with MotherDuck.
The reason that this works is
because MotherDuck was default,
and therefore, if it's
in the same package,
you can validly implement
these two lines of code.
You can reference the
quack method, and you
can access the parameters and the
variables inside of the MotherDuck
class.
Ah, sorry, sorry, yes.
I unfortunately made a typo here
that says private makeNoise, which
means that if we specify it over
here with a makeNoise Method that
would mean that we would have an error.
Thankfully, we're not
referencing the makeNoise method.
So we're not going to
generate a compiler error.
But that is a very accurate
observation by you, yes.
All right, moving
forward, we will go ahead
and talk about the BadCygnet class.
BadCygnet class is inside
of a different package.
It is no longer inside
of the duck package.
It is now a swan package.
And even though we are successfully
importing the MotherDuck class
from the duck package, when we
try to imitate the duck, when
we try to imitate the MotherDuck,
we are successful in creating
an instance of the class
because MotherDuck was public.
But when we try to quack, and when we
try to print the noise of the duck,
we will get a compiler error
because this is unfortunately not
in the same package, and therefore,
MotherDuck's default access
does not reach BadCygnet.
And for those of you who don't
know, cygnet is a young swan.
You learn new things every day.
So now, we're going to move on to
protected access, which is probably
one of the more complicated features
of access modifiers inside of Java.
So we're put a stop
sign here to say, stop,
both if you're not allowed
inside of this protected access,
and also so that we
can gather ourselves.
Going forward, we have
another second case study
that has to do with another
package called pond dot shore.
Inside this package, we
have a class called Bird.
And Bird has a lot of protected
methods and variables.
So we have a protected
variable called text
that says that this Bird
is floating on the shore.
And then we have a protected
method, floatInWater,
that prints out the fact
that it is floating.
So remember what we said protected was.
Protected means that both other
classes inside of its package,
as well as its child classes,
can access these two parameters.
So let's look at a second
class called Gosling.
Gosling extends Bird, because
it is a child class of Bird.
Goslings are all birds,
and therefore, even
though it is in a different package,
it is still a child class of Bird.
And it imports the functionality of
Bird through the import statement.
Now below, Gosling has a
method where it tries to swim.
Notice here that we're able to
call the floatInWater method
and also print out the text without
even creating a Bird object.
That is because we are a child class,
and because it is protected access.
We can access it as a child class
even if we're not in the same package.
Now let's look at someone different.
In this case, we have a BirdWatcher.
This is obviously not a Bird,
but it is still in same package,
and is still along
the shore of the pond.
And therefore, inside of
the BirdWatcher class,
when we try to call watchBird
method, then we create a new Bird,
and we say that the
Bird floats in water,
and the Bird prints out
its text of floating
this will also work because of
the fact that they're inside
of the same package.
And therefore, protected access
extends to the BirdWatcher class.
But finally, obviously, we need to
have something that does not work.
So let's create a package called inland.
And we try to import the Bird
class from the shore package.
And we have a BirdWatcher
from afar because this
is a BirdWatcher that is farther
inland, and is not along the shore.
Well if it tries to create a
method of watching the birds,
and it creates a new Bird, and it
says the Bird floats in the water,
and it tries to print out the bird's
floating text, is this going to work?
The answer is no, because
there will be a compiler
error, since this is
neither in the same package,
nor is it a child class of Bird.
Therefore, it does not have
access to the protected
modifiers and the protected variables
and methods inside of the Bird class.
All right, so now, we are arriving
at the end of our time together.
But I want to leave you
all with a quick quiz
to see if you've been paying attention.
So here, remember that
at the beginning, we
talked about an Animal
class and a Tiger subclass,
or child class that extended Animal.
Can anybody tell me if this will
compile, and if it does not,
what the problem is with our code here?
SPEAKER 2: The Tiger is trying to
call age, but it's [INAUDIBLE]..
SPEAKER 1: Exactly.
The Tiger is trying to call the
age variable inside of Animal.
But notice how, when we talked about
this example before, it was private.
Therefore, if I just straight up tried
to call the age from the Animal class,
it will not print.
There will be a compiler error.
When we talked about this
example on a previous slide,
I actually went in a roundabout way.
And when I created this method,
I said to get the age first.
Because getAge is a public
method, I can successfully
call inside of the
Tiger class, and then I
can print out the
Tiger's age afterwards.
So notice that there is
this redundancy in this case
because we specified it as private.
But this could also be very
beneficial because maybe we
do not want tigers to be able
to change the ages of themselves
or of other animals.
All right, so now we arrive at the
conclusion of our time together.
And the big question
that we've been asking,
and that I have tried to
help you answer, is why.
Why is Java important to
us, and why is Java worth
learning today, in our day and age?
The answer is very, very simple.
It's that Java is a secure, very, very
portable, and simple language that
uses these ideas of
encapsulation, of inheritance,
in order to make writing code
much easier for programmers.
Although these methods and these
ideas can be seen in other programming
languages, potentially like Python, no
programming language does it as much
and emphasizes it as much as Java does.
In the real world, you most likely
see Java applied in applets, in games.
The reason why it is seen
in all these scenarios
is because Java's inheritance
structure makes it easy
for you to create players, to create
obstacles, to create bosses and levels,
and so on, without spending
a huge amount of code.
And that is where Java
usually comes into light,
and where we will usually
deal with it in real life.
Obviously, Java has many, many
other potential applications
in storing data, in creating objects
and projects, and stuff like that.
But in those areas, it is also
potentially viable for other languages
to replace it.
So at the end of the day.
I hope that if you got
anything out of this seminar,
it is an increased understanding and
appreciation for Java, specifically
the nuances that come with
encapsulation and inheritance,
so you can relate a little bit less
to the meme I'll depart you all with.
Best of luck with all of
your coding adventures.
This has been a CS 50 seminar.
Thank you very much for watching.
