第三章 面向对象
3.1 面向对象概念
-
面向过程与面向对象
举例:
大象装进冰箱。面向过程:
打开冰箱。
存储大象。
关上冰箱。对于面向过程思想,强调的是过程(动作)
例:C语言
面向对象:
冰箱打开。
冰箱存储。
冰箱关闭。对于面向对象思想,强调的是对象(实体)
例:C++, Java, C#
-
特点:
-
面向对象就是一种常见的思想。符合人们的思考习惯。
-
面向对象的出现,将复杂的问题简单化。
-
面向对象的出现,让曾经在过程中的执行者,变成了对象中的指挥者。例:买电脑找电脑专家一起去商城
-
面向对象语言的三个特征:
1,封装
2,继承
3,多态
3.2 类与对象的关系
- 类是用Java语言对现实生活中的事物进行的描述
通过类的形式来体现 - 怎么描述呢?
对于事物描述通常只关注两方面。一个是属性,一个是行为。 - 只要明确该事物的属性和行为并定义在类中即可。
对象:其实就是该类事物实实在在存在的个体。 - 类与对象之间的关系?
类:事物的描述。
对象:该类事物的实例。在Java中通过new来创建的。 - 定义类其实就是在定义类中的成员。
成员:成员变量<–>属性,成员函数<–>行为。 - 成员变量和局部变量的区别:
1,成员变量定义在类中,整个类中都可以访问。
局部变量定义在函数,语句,局部代码块中,只在所属的区域有效。
2,成员变量存在于堆内存的对象中,
局部变量存在于栈内存的方法中。
3,成员变量随着对象的创建而存在,随着对象的消失而消失。
局部变量随着所属区域的执行而存在,随着所属区域的结束而释放。
4,成员变量都有默认初始化值。
局部变量没有默认初始化值。
例:
class Car
{
int num;
String color;
void run()
{
System.out.println(num+"..."+color);
}
}
class CarDemo
{
public static void main(String[] args)
{
//在计算机中创建一个car的实例。通过new关键字。
Car c = new Car();// c就是一个类Car类型的引用变量,指向了该类的对象。
c.num = 4;
c.color = "red";
c.run();// 要使用对象中的内容可以通过 对象.成员的形式来完成调用。
}
}
对象的内存体现:
- 基本数据类型参数传递和引用数据类型参数传递
3.3 封装(Encapsulation)
- 封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。函数、类、框架都是封装体。
- 封装的好处
- 将变化隔离
- 便于使用
- 提高复用性
- 提高安全性
- 封装原则
- 将不需要对外提供的内容都隐藏起来
- 把属性都隐藏,提供公共方法对其访问
- private关键字
- 是一个权限修饰符
- 用于修饰成员(成员变量和成员函数)
- 用private私有的变量只在本类中有效
- 常用之一:
- 将成员变量私有化,对外提供对应的set, get方法对其进行访问。提高对数据访问的安全性
3.4 构造函数
- 构造函数:构建创造对象时调用的函数
- 特点
- 函数名与类名相同
- 不用定义返回值类型
- 没有具体的返回值
- 作用
- 给对象进行初始化
- 注意
- 一个类中如果没有定义过构造函数,那么该类中会有一个默认的空参数构造函数
- 如果在类中定义了指定的构造函数,那么类中的默认构造函数就没有了
- 默认构造函数的特点
- 多个构造函数是以重载的形式存在的
- 将一个指定类的构造函数私有化private,可以保证不让其他程序创建该类对象
- 构造函数与一般函数的区别
- 构造函数:对象创建时,就会调用与之对应的构造函数,对对象进行初始化
- 一般函数:对象创建后,需要函数功能时才调用
- 构造函数:对象创建时,调用且只调用一次
- 一般函数:对象创建后,可以被调用多次
- 一般函数可以调用构造函数,构造函数不能调用一般函数
- 构造函数首字母大写,与类名相同;一般函数首字母小写,因此可以通过名称区分
- 细节:构造函数中可以通过写return的方式来控制输入,但这种情况很少。因为当你new对象时的参数往往来源于客户通过网页形式的输入,如用户名密码等参数。这种情况下通常会有校验,所以不用再在构造函数中进行控制。
- 对象必须要进行初始化后才能使用
- 什么时候定义构造函数
- 在描述事物时,该事物一存在就具备的一些内容,这些内容都定义在构造函数中
- 构造函数的重载(overload)
- 一个类中出现多个构造函数时,必须以重载的形式来体现,它可以给不同的对象定义不同的初始化
- 重载是方法名相同,参数不同。
- 参数类型顺序不相同的话是重载,相同的话不是
f(int x, double y),f(int y, double x)//不是重载
f(int x, double y),f(double x, int y)//是重载
f(int x, double y),f(double y, int x)//是重载
- 构造函数内存图解(左栈右堆)
3.5 this关键字
- 特点:this代表其所在函数所属对象的引用。换言之,this代本类对象的引用。
简单来说,哪个对象调用了this所在的函数,this就代表哪个对象 - 什么时候使用this关键字?
- 当在函数内需要用到调用该函数的对象时,就用this。例如当成员变量与局部变量重名时,可以用关键字this来区分 this.name = name;
- this也可以用于在构造函数中调用其他构造函数。
- 注意:
1,一般函数无法调用构造函数。
2,this只能定义在构造函数的第一行。因为初始化动作要先执行。
3.6 static(静态)关键字
- static关键字
- 同时也是修饰符,用于修饰成员(成员变量和成员函数)
- 被修饰后的成员具备以下特点:
- static修饰的成员随着类的加载而加载
- static修饰的成员优先于对象存在
- static修饰的成员被所有对象所共享
- static修饰的是共享数据,对象中存储的是特有数据
- static修饰的成员多了一种调用方式,可以直接被类名调用。格式:
Person.country//类名.静态成员
-
使用注意
-
静态方法只能访问静态成员(非静态既可以访问静态,又可以访问非静态)
-
静态方法中不可以写this,super关键字
-
主函数是静态的
-
成员变量与静态变量
-
成员变量和静态变量区别:
1,两个变量的生命周期不同。
成员变量随着对象的创建而存在,随着对象的被回收而释放。
静态变量随着类的加载而存在,随着类的消失而消失。
2,调用方式不同。
成员变量只能被对象调用。
静态变量可以被对象调用,还可以被类名调用。(建议用类名调用,阅读性好)
3,别名不同。
成员变量也称为实例变量。
静态变量也称为类变量。
4,数据的存储位置不同。
成员变量数据存储在堆内存的对象中,所以也叫对象的特有数据。
静态变量数据存储在方法区(共享数据区)的静态区中,所以也叫对象的共享数据。 -
注:JVM内存划分为五个区域:
寄存器:cpu
本地方法区:调用系统底层内容
方法区(数据区/共享区/共享数据区):方法存储
栈(stack):方法调用和局部变量
堆(heap):存储对象 -
主函数的解释
public static void main(String[] args) -
public:因为权限必须是最大的。
-
static:虚拟机调用主函数时是不需要对象的,直接用主函数所属类名调用即可。
-
void:主函数没有具体的返回值。
-
main:函数名,不是关键字,只是一个JVM识别的固定名字。
-
String[] args:这是主函数的参数列表,是一个数组类型的参数,而且元素都是字符串类型。
-
System.out.println(args);//[Ljava.lang.String;@61064425 @右边是哈希值,@左边[表示是数组,L表示long,包名java.lang,存储的是String类型的元素
-
System.out.println(args.length); //0 表示主函数创建了一个字符串类型的数组,元素个数为0,即new String[0]
-
静态什么时候使用
1,静态变量。 -
当分析对象中所具备的成员变量的值都是相同的。
-
这时这个成员就可以被静态修饰。
-
只要数据在对象中都是不同的,就是对象的特有数据,必须存储在对象中,是非静态的。
-
如果是相同的数据,对象不需要做修改,只需要使用即可,不需要存储在对象中,定义成静态的。
2,静态变量。 -
函数是否用静态修饰,就参考该函数功能是否有访问到对象中的特有数据。
-
简单点说,从源代码看,该功能是否需要访问非静态的成员变量。
-
如果需要,该功能就是非静态的。
-
如果不需要,就可以将该功能定义成静态的。当然,也可以定义成非静态,但是非静态需要被对象调用,而仅创建对象调用非静态的没有访问特有数据的方法,该对象的创建时没有意义的。
-
静态代码块
-
随着类的加载而执行。而且只执行一次。
-
作用:用于给类进行初始化。
-
构造代码块:可以给所有对象进行初始化。
-
执行顺序(如果有对象):静态代码块-构造代码块-构造函数
3.7 单例设计模式
- 设计模式:对问题行之有效的解决方式。其实是一种思想。
- 单例设计模式(Singleton Pattern)。
- 解决的问题:就是可以保证一个类在内存中的对象唯一性。
比如对于多个程序使用同一个配置信息对象时,就需要保证该对象的唯一性。 - 如何保证对象唯一性呢?
1,不允许其他程序用new创建该类对象。
2,在该类中创建一个本类实例。
3,对外提供一个方法,让其他程序可以获取该对象。 - 步骤:
1,私有化该类的构造函数。private
2,通过new在本类中创建一个本类对象。
3,定义一个公有的方法getInstance(获取实例),将创建的对象返回。 - 示例:
//饿汉式(实际开发中使用较多)
class Single//类一加载,对象就已经存在了。
{
private static Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
//懒汉式(被多线程并发访问时有可能保证不了对象的唯一性)
class Single2//类加载进来,没对象,只有调用了getInstance方法时,才会创建对象。
//延迟加载形式。
{
private static Single2 s = null;
private Single2(){}
public static Single2 getInstance()
{
if (s==null)
{
s = new Single2();
}
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1==s2);
}
}
单例设计模式-内存图解