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.