一、面向对象的概述
例子“把大象装进冰箱里”。
面向过程:
对于面向过程思想的做法应该有以下三步:
1.打开冰箱。
2.装入大象。
3.关闭冰箱。
我们可以看出,面向过程思想,强调的就是过程,也就动作(打开,装入,关闭)。
面向过程:
现在我们看一下,对于上面的三个步骤,很明显都于冰箱这个实体有关系,那么我们可以这样理解:
1.冰箱打开。
2.冰箱装入。
3.冰箱关闭。
我们可以看出,与上面的面向过程不一样的是,这里强调的实体冰箱,而冰箱具有打开、装入和关闭这三个功能,这就是我们要学习的面向对象思想,是符合人的一般思维方式。
我们现在来说说面向对象的特点,大概可以概括为以下三点:
1.面向对象就是一种常见的的思想,符合人们的思考习惯。
2.面向对象的出现,将复杂问题简单化。
3.面向对象的出现,让曾经在过程中的执行者,变成了对象中的指挥者。
二、类与对象的关系
类:事物的描述。
对象:该类事物的实例.在java中通过new创建的。
三、对象的内存体现
我们在函数的内存分配分析过,当该代码运行时,首先会加载主函数在栈内存中为main函数分配一个空间;
然后运行函数中的第一行语句,Car myCar = new Car();
然后把局部变量myCar加载到栈内存,然后通过new在堆内存中分配空间,然后把这个地址赋给变量myCar;
class CarDemo
{
public static void main(String[] args)
{
//在计算中创建一个car的实例,通过new这个关键字.
Car myCar = new Car();//myCar就是一个类类型的引用变量,指向了该类的对象.
System.out.println(myCar);
}
}
当我们不赋值时:
我们看到了三个属性被默认初始化了,那么就是说我们没有给属性赋值,系统也会为我们打造一辆空皮汽车的.
当我们赋值时:
我们看到程序会先分别给num,color,brand这三个属性赋值,也就是说我们得到了一个完完整整的宝马汽车,而存放这辆车的地址没有发生变化,还是原来堆内存中的地址值.
同理如果我们创建多个car的实例,内存中的加载过程都是一样的,它们都有各自不同的空间和对应的地址.
四、成员变量与局部变量
1.从它们的定义环境来分析
成员变量定义在类中,整个类中都可以访问.
局部变量定义在函数,语句,局部代码块中,只在所属区域有效.
2,从它们在内在中存放形式来分析:
成员变量存在于堆内存的对象中.
局部变量存在于栈内存的方法中.
3,从它们的默认初始化来分析
成员变量都有默认初始化值.
局部变量没有默认初始化值.
4.从它们的生命周期来分析
成员变量随着对象的创建而存在,随着对象的消失而消失.
局部变量随着所属区域的执行而存在,随着所属区域的结束而释放.
注意
成员变量和局部变量的同名情况:
class People//定义一个People类
{
String name;//姓名属性
int age;//年龄属性
char sex;
void run()//跑的方法
{
System.out.println(name+"开始跑步了.");
}
void information()
{
String name = "JAMES";
int age = 29;
char sex = '女';
System.out.println("姓名:"+name+";\n性别:"+sex+";\n年龄:"+age+".");
}
}
我们看到在打印出了局部变量的值,这是由于局部变量在栈的方法中,程序会把值直接赋值给局部变量,如果当在栈中找不到,则到堆内存中寻找指定的成员变量.这一点在以后的操作中一定要小心.
五、类类型参数与匿名对象
类类型参数
class CarDemo
{
public static void main(String[] args)
{
//定义第一辆车
Car myCar1 = new Car();
assemble(myCar1);
System.out.println("myCar1是一辆"+myCar1.color+"颜色的"+myCar1.brand+"品牌的小汽车,它有"+myCar1.num+"个轮子");
//定义第二辆车
Car myCar2 = new Car();
assemble(myCar2);
System.out.println("myCar2是一辆"+myCar2.color+"颜色的"+myCar2.brand+"品牌的小汽车,它有"+myCar2.num+"个轮子");
//定义第三辆可以直接复用assemble(Car car)这个组装功能即可
}
//我们可以封闭一个汽车组装的功能,然后调用这个功能就可以
public static void assemble(Car car)//类类型的参数一定要指向对象,要么就是null
{
car.num = 4;
car.color = "red";
car.brand = "BWM";
}
}
从上面的代码我们很容易就实现了相同车辆的组装,提高了代码的复用性,同时我们注意到,封装的功能的参数是一个类类型的形式参数,它指向的是一个Car类型的对象.
匿名对象
匿名对象顾名思义就是没有名字的对象
new Car();//匿名对象,其实就是定义对象的简写格式
那么匿名对象一般怎么用呢?
我们先看一例子,比如我们要创建一辆车,并运行run方法,那么我们会这样做:
Car c = new Car();//这个是有名对象 c.run();
对于上面这种情况,我们可以简写为如下一句代码:
new Car().run;
那么我们就看到匿名对象的第一种用法:
当对象对方法仅进行一次调用的时候,就可以简化成匿名对象.
我们看一段代码:
class CarDemo
{
public static void main(String[] args)
{
new Car().num = 5;
new Car().color = "green";
new Car().run();
}
}
我们看一下结果:
对于这三句语句,每执行一名,在堆内存中都会创建新的空间,而不是只创建一个对象.当执行下面的一句语句时,前面的语句已经被释放了,所以上面的前两句代码没有意义.但是最后一句会被执行,因为该对象对方法只进行了一次调用.
匿名对象的第二种用法:
匿名对象可以作为实际参数进行传递.
比如前面我们的方法assemble(Car car),我们可以这样使用:
assemble(new Car());
六、基本数据类型参数与引用数据类型参数的传递过程
数据类型参数和引用参数我们在前面章节中都已涉及到了,那么我们来看看下面的两段代码:
//基本数据类型参数传递
class Demo
{
public static void main(String[] args)
{
int x = 3;
change(x);//调用方法
System.out.println("x = " + x);//
}
public static void change(int x)
{
x = 4;
}
}
//引用类型数据参数传递
class Demo
{
int x = 3;
public static void main(String[] args)
{
Demo d = new Demo();
d.x = 9;
change(d);
System.out.println("d.x = " + d.x);
}
public static void change(Demo d)
{
d.x = 4;
}
}
现在我们来分别对这两对代码的运行程分析一下。
一、对于基本数据类型参数传递代码的运行过程分析:
1.main方法进栈内存,main方法中有基本数据类型变量int x;
2.为main方法中的变量x赋值为3;
3.调用change(x)方法,则change方法进栈;
4.为change方法变量x赋值为4;
5.跳出change方法,同时change方法出栈,释放所有change方法和change方法中的x,即把x=4释放掉;
6.执行打印语句,些时的栈中只有main方法中的x,那么打印出的x=3;
7.跳出main方法,结束程序。
我们来看一下打印结果与我们的分析是不是一致的?
二、对于引用数据类型参数传递代码的运行过程分析:
1.main方法进栈内存,main方法中有一个类类型变量Demo d;
2.new创建Demo对象,在堆内存中开辟一个空间,并把空间地址传给d(我们这里假设为0x0078),并为该地址中的x初始化为0,然后把3赋给x;
3.把d所指堆内存(0x0078)中的x赋为9;
4.调用change(d)方法,change方法进栈,change方法中的对象d就是main方法中的d,指向之前的堆内存地址(0x0078);
5.把d所指堆内存(0x0078)中的x赋为4;
6.跳出change方法,同时change方法出栈,释放change方法和方法中的对象d;
7.执行打印语句,些时的栈中也是只有main方法,并且d指向堆内存(0x0078),该地址中的x就是步骤5中的值4;
8.跳出main方法,结束程序。
我们看看结果:
我们从两个代码打印出的结果可以看出结果与我们的分析是完全一致的。
那么,基本数据类型参数和引用数据类型参数的过程就是我们上面分析的过程。
七、 封装
封装:是指隐藏对象的发生和实现细节,仅对外提供公共访问方式.
class Person
{
private int age;//把该成员属性设置为私有
public void setAge(int a)
{
if(a>0 && a<130)
{
age = a;
speak();
}
else
System.out.println("错误的数据");
}
void speak()
{
System.out.println("age = "+age);
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
p.setAge(-20);//调用方法设置年龄,但是我们不知道该方法的内部实现
}
}
面我们再来说说封装的优点和原则:
优点:
1.提高代码安全性;
2.实现对内部属性的可控性;
3.提高代码的复用性;
4.隔离变化;
原则:
1.将不需要对外提供的内容都隐藏起来;
2.把属性都隐藏起来,提供公共方法供外部访问(当然不是所有方法都必须是公共方法).
最后我们再看一下刚才我们用到的一个关键字:private.
private关键字:指私有,是一个权限修饰符,用于修饰成员.私有的内容只在本类中有效.
它的常见用法:就是将成员变量私有化,对外提供对应的set,get方法对其进行访问.提高对数据访问的安全性.
注意:私有仅仅是封装的一种体现而已.
对于java语言来讲,最小的封装体就是函数.类也是封装体.框架本身也是封装体.这些都一个共性,就是我们可以不知道实现细节,直接拿来用就可以了.