蓝杰学习第二课
---------------类与对象
先讲一下我自己对类与对象的理解:类是一个比较广泛的、许多具有共性的事物的整体。比如说学生,是由许多学生组成的,而且每个学生都具有:学号、姓名、年龄、性别、成绩、等公有属性,同时还有:学习、运动、说话、走路、玩游戏等公有行为。
而相对于类,对象则更加具体,对象应是类的一个实例化,甚至可以这样理解,对象就是属于类。比如相对于学生类,一个具体的学生就是学生类的一个对象,是学生类的一个实例化。
public class Student
{ //添加学生类的属性
private int age;
private String name;
private int score;
……
}
因为age 、name、 number 都是学生类的私有成员数据,外部不能访问,所以要从外部改变这些值还要添加设置函数setXXX,同时,如果要获取学生的信息,还要设置一个getXXX的函数。
public class Student
{ //添加学生类的属性
private int age;
private String name;
private int score;
//设置函数与获取函数
public void setName(String n)
{
name=n;
}
public void setAge(int x)
{
age=x;
}
public void setScore(int x)
{
score=x;
}
public int getNumber()
{
return number;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
……
}
前面已经分析了,学生除了姓名、年龄、学号等属性以外,还具有学习、玩游戏等行为。
public class Student
{ //添加学生类的属性
private int age;
private String name;
private int scorer;
//设置函数与获取函数
public void setName(String n)
{
name=n;
}
public void setAge(int x)
{
age=x;
}
public void setScore(int x)
{
number=x;
}
public int getScore ()
{
return number;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
//定义学生的行为函数
public void play()
{
score--;
}
public void study()
{
score++;
}
}
同时,类似于学生类,我们还可以定义老师类。在老师类中,可以定义一个带参数的行为函数
public void teach(Student stu)
{
stu.setScore(stu.getScore+1); //可以看到,参数就是学生类的一个对象。(对
//的实例化方法为 Student stu=new Student();
//相当于是调用了系统的默认构造函数来初化
//对象)
}
课后练习:编写一个PK的回合制游戏,当一方血量为0时,输出谁获胜。
先讲一下我的思路:
1. 先定义一个Monster类和Warrior类。在其中定义HP(血量)的属性。还要加上Attack()(攻击)的行为,由于攻击必然有一个对象,所以Attack函数还应带有参数->如类Warrior里的Attack(Monster monster)。
2. 然后,就是要在主函数中将两个类各实例化一个对象:
Monster monster=new Monster();
Warrior warrior =new Warrior();
然后让两个对象按一定的规则来互相攻击对方(即依次调用Attack() 命令。)
3. 规则可以制定为:系统随机生成一个0~100的随机数a,
假如0<a<50则Monster攻击,
假如50<a<100 则Warrior攻击。
整段程序为:
PK主函数:
publicclass pk {
publicstaticvoid main(String args[])
{
//初始化血量
Monster monster=new Monster(100);
Warrior warrior=new Warrior(100);
java.util.Random random=new java.util.Random();
//判断两者都还存活着
while(monster.getHP()>0&&warrior.getHP()>0)
{
int r= random.nextInt(2); //每一轮经=进攻产生一个随机数。
if(r==1)
{
monster.Attack(warrior);
warrior.ShowHP();
monster.ShowHP();
}
else
{
warrior.Attack(monster);
warrior.ShowHP();
monster.ShowHP();
}
}
if (warrior.getHP()>0)
System.out.println("warrior win ");
else
System.out.println("monster win");
}
}
Monster类的函数:
publicclass Monster {
privateintHP;
publicvoid setHP(int x)
{
this.HP=x;
}
publicint getHP()
{
returnHP;
}
//显示血量
publicvoid ShowHP()
{
System.out.println("monster' HP is "+HP);
}
//攻击函数
publicvoid Attack(Warrior warrior)
{
HP++;
warrior.setHP(warrior.getHP()-1);
System.out.println("monster are attacking");
}
//构造函数
public Monster(int x)
{
HP=x;
}
}
Warrior类的函数:
publicclass Warrior {
privateintHP;
publicvoid setHP(int x)
{
this.HP=x;
}
publicint getHP()
{
returnHP;
}
publicvoid ShowHP()
{
System.out.println("warrior' HP is "+HP);
}
publicvoid Attack(Monster monster)
{
HP++;
monster.setHP(monster.getHP()-1);
System.out.println("warrior are attacking");
}
public Warrior(int x)
{
HP=x;
}
}
类与对象2
1.类的构造方法:其实之前已经涉及到了类的构造方法,例如初始化一个学生对象
Student stu=new Student(); 这之中就使用到了Student()的构造方法,只不过这个构造方法是系统默认的,当然,我们也可以自己来给一个类编写构造方法,可以给这个构造方法加上不同的参数。例如;
public Student(int x,String n)
{
age=x;
name=n;
}
//然后我们可以用这个构造方法来创建一个对象:
Student stu=new Student(19,”John”);
//这样我们就创建了一个学生对象,年龄为19岁,名字为John.
从这个例子中,我们可以很明确地知道构造方法的用途:就是在创建对象时,给对象初始化一些属性(即给年龄名字等属性赋给初值)。
但是,这时又出现了一个新的问题:就是一个对象的属性可以通过“设置函数”来设定,那么,为什么要使用构造方法来初始化对象,这样做有什么好处或者是特殊的目的?
对于这个问题,我首先想到的就是使用构造方法来初始化对象更加方便,可以在创建对象的时候就将设定好其属性。至于其他的好处,我猜想可能就是给对象添加标签。
例如人的国籍,一个人刚出生的时候他的国籍就定了,一个人不能随便到不同国籍的国家。
类比到对象,一个对象如果刚被创建时,利用构造方法给它初始化一些属性,就相当于给它加上了标签,这样可以把对象限制在某个范围内而不至于“乱窜”(不知道这样形容对不对,纯属个人臆想,毫无现实根据。)
2.方法(构造方法和普通方法)重载
说到重载,就是多个同名却用法不同的方法。举个例子,一个人吃饭,他可以只吃饭、只吃菜、先吃饭在吃菜、也可以先吃菜再吃饭、也可以饭菜一起吃。同样都是吃的动作,但对应的对象不同,所涉及到的动作方法也就不同,更具体说就是方法的参数类型、个数以及顺序不同,但是名称相同,这也就是方法的重载。
假设米饭类和菜类、人类已经定义过。以下是人类吃的“不同”的函数,
public void eat(Rice rice){System.out.println(name+” is eating “+rice.getName())}
public void eat(Dish dish){ System.out.println(name+” is eating “+dish.getName())}
public void eat(Rice rice,Dish dish){……}
public void eat(Dish dish,Rice rice){……}
这些便是方法的重载。
当然,还是同样的问题:方法重载到底有什么好,我个人认为,方法重载使得程序中对类的描述更加接近现实生活。
3.this关键字
this,顾名思义,就是“这”的意思。当this放在方法中时,this就表示调用该方法的对象。
比如在一个学生类中,有一个设置年龄的方法。
public void setName(String n)
{
this.Name=n;
}
之后,再给Student 创建一个对象:
Student stu1=new Student();
Student stu2=new Student();
stu1.setName(“Bob”); …….①
stu2.setName(“John”); …….②
此时,程序执行到①时,this就表示调用setName()方法的对象stu1.
程序执行到②时,this就表示调用setName()方法的对象stu2.
4引用
这个名词对于许多像我这样的初学者来说真的有点难理解,但是,在之前的编程中,我们却早已多次使用到了引用。
比如说,当我们要实例化一个对象时(比如说是一个Student类的对象)。
我们会写这样一段代码 Student stu=new Student();
其实,在这段码中,stu就是对某一个学生实体对象的引用。打个比方,在一群学生中,我们随机找了一个学生,我们先叫他学生甲,这样,学生甲就是对这个学生对象的引用。当然,这群学生中的任何一个人都可以担任学生甲这个角色,比如说我们可以叫小明学生甲,也可以叫小芳学生甲。所以学生甲就具有了不确定性,其实,更加确切地说:学生甲是学生类型的一个变量,但是他并不具体地表示一个学生,他只是指向(或是引用)了一个学生实体。(好吧,说到这里感觉有点糊涂了)。
我觉得要搞清楚这个问题归根究底还是要搞清楚变量究竟在内存中时如何存储的。
这就涉及了栈内存和堆内存。
栈内存 |
堆内存 |
stu(XXX) |
number,name……(各种属性) play();study()……(各种行为方法) Student(……)(各种构造方法) |
知道对象在内存中是如何存储的之后,我们注意,stu这个对像创建的过程。
我们是先用Student类来声明一个叫做stu 的变量(存储在栈内存中),然后用new关键字在堆内存中开辟了一块区域,用来存储这个对象的各种属性,行为方法以及构造方法。然后调用了类的构造方法来给这些属性写入一个初始值,最后构造方法会返回一个地址给stu变量,也就是说stu里面存储的是其所对应的对象实体所在的内存区域的首地址。并且,可以通过stu来访问(或者说是引用)对象实体里面的数据(比如说行为方法),也可以修改对象实体里面的一些属性所对应的的数值。
其实说了这么多,我感觉还是很难把这个问题说清楚,总而言之,记住一点:创建对象时所用的名称只是对象的一个代号,他并不能代表这个对象(代号是存储在栈空间,而对象实体是存储在堆空间),只是我们可以暂时先通过这个名称来引用这个对象实体的行为方法和属性。
这时,我又想到一个例子:假如你是一个新老师,你到了一个班级,里面的同学你都不认
识,但是这时候你得给每个学生编学号。假如是有5个同学,你不知道他们叫什么名字,于是你就先把他们叫做甲、乙、丙、丁、戊。然后分别把甲、乙、丙、丁、戊编号为1、2、3、4、5.这样,甲、乙、丙、丁、戊就相当于是对象类型的变量,然后你利用这个变量来修改了对象实体的属性值。 也就是说,甲乙丙丁只是代号,与真正的对象实体完全是两回事,但是你可以利用这个代号来引用对象实体的一些行为方法,例如stu.study();
就是用stu这个代号来引用了其对应的对象实体的学习方法。