一、面向对象
1.1 面向对象概述
1.1.1 什么是对象
在Java中,对象(Object)是指一个具体事物的实例,任何事物都可以使用对象(类)来描述,如猫、狗、计算机、杯子、云、水、空气、叶子、灰尘等看得见的、看不见的、宏观的、微观的、具体的、抽象的都是对象,总之"万物皆对象";
1.1.2 面向对象程序设计
我们前面就提到过,Java是一门面向对象的编程语言,面向对象是一种程序设计思想,与之对应的还有面向过程程序设计;面向对象是把一个对象的特征(属性)和行为单独封装到对象源代码中;这些属性和行为都被集中到一个地方,这样比把方法或者过程与数据分散开来更为方便和安全,含义更加明确;
1.1.3 面向对象和面向过程
举例:开车去上班
- 面向过程:去车库提车、拿钥匙、打火、踩离合、挂挡、踩油门、刹车、加油、到公司、找车库停车
- 面向对象:找个司机(对象)、到公司
可以看得出来,面向过程关注的是步骤,将所有步骤连在一起"我就能开车到公司上班",面向对象则关注的是"开车去上班"这个事物整体,完成这件事的步骤全部封装起来,交给指定的对象去做(司机);
面向过程适合简单、不需要协作的事务,重点关注如何执行;面向过程来处理事物时,我们首先思考的是"如何按步骤来实现?"一步一步,最终完成,适合简单任务,不需要过多协作的情况;
但是当我们思考较为复杂的设计任务时,比如将"开车"改为了"造车",就会发现我们列出步骤来一步步执行是不现实的,一个汽车厂不可能将造汽车的全部过程都执行一遍,从一个个螺丝的加工生产到发动机的生产、座椅的生产、汽车玻璃的提炼/生产、甚至车载芯片的研发/生产、发动机的研发、电器设备的研发等,相信国内汽车厂没有一家是这样造汽车的,因为自己要做的事物太多了。
这个时候,面向对象更加符合我们的思维方式。我们首先思考的是"车由什么组成?"开始思考问题,为了协作,我们找到轮胎厂采购轮胎(交给轮胎厂对象来生产轮胎),找到玻璃厂采购玻璃(玻璃厂对象来生产玻璃),找到发动机厂采购发动机等,最终进行产品的组装。而不是按步骤一个个造出来。这就是面向对象思维方式;
但是需要注意的是,具体到某个轮胎厂、玻璃厂的一个个流水线操作仍然是有步骤的,仍然离不开面向过程思维;面向对象可以帮助我们从整体上分析整个系统,但是具体到某一个操作上,任然需要面向过程的思路去处理;面向对象和面向过程是相辅相成的,面向对象离不开面向过程;
面向对象与面向过程都是解决问题的一种思维方式,也都是代码的组织方式:
- 面向过程是一种"执行者思维",主要用于解决一些简单问题的场景
- 面向对象是一种"设计者思维",主要解决复杂、需要协作的问题场景
1.1.4 面向对象的特点
面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。面向对象的语言中,包含了三大基本特征,即封装、继承和多态。
Tps:关于面向对象和面向过程大家不必太过纠结,我们现在是初学者,理解表面意思即可,随着后面的深入学习,我们会对面向对象有着更深刻的理解;
1.2 类和对象
1.2.1 类和对象的区分
在现实世界中,属于同一类的对象很多,类是抽象的,不是具体的,我们人习惯以对象的方式认识现实世界;
例如我说一辆汽车,那你脑海中立马就能够呈现出一辆汽车的模样吧,不管他是什么品牌、什么颜色、什么价格、什么参数等总而言之,都是一辆汽车,都是属于"汽车类"产品;
tips:类是抽象的,对象是具体的,对象是类的实例化;
类是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
- 属性:该事物的状态信息;
- 行为:该事物的功能信息;
1.2.2 类和对象的举例
- 类举例:
举例:猫类
属性:名字、体重、年龄、颜色; 行为:走、跑、叫;
- 实例化对象:
猫对象:
属性:旺财、8kg、6岁、棕色;行为:悄悄的走、飞快的跑、喵喵叫;
1.3 Java类的定义
1.3.1 类的定义格式
public class 类名 {
//成员变量
//成员方法
}
- 定义类:就是定义类的成员,包括成员变量和成员方法。
- 成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
- 成员方法:和以前定义方法几乎是一样的。只不过把static去掉,static的作用在面向对象后面课程中再详细讲解。
类的定义格式举例:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Student {
// 成员变量(属性)
String name; //姓名
int age; //年龄
// 成员方法(行为)
public void study(){
System.out.println("学习");
}
public void eat(){
System.out.println("吃饭");
}
}
1.3.2 类的实例化
前面我们说道过,类是抽象的,不是具体的,类只是负责把事物描述起来,提供模板;对象是类的实例化,是具体的;我们定义好一个Java类后需要通过对象将类进行实例化;
- 创建对象:
类名 对象名 = new 类名();
- 使用对象访问类中的成员:
对象名.成员变量;
对象名.成员方法();
- 练习:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01 {
public static void main(String[] args) {
//创建对象格式: 类名 对象名 = new 类名();
Student s = new Student();
System.out.println("s: " + s); //s: com.dfbz.demo.Student@1540e19d
//直接输出成员变量的值
System.out.println("姓名: " + s.name); //null
System.out.println("年龄" + s.age); //0
System.out.println("--------");
//给成员变量赋值
s.name = "刘德华";
s.age = 38;
//再次输出成员变量的值
System.out.println("姓名: " + s.name); //刘德华
System.out.println("年龄: " + s.age); //18
System.out.println("---------");
//调用成员方法
s.study(); //学习
s.eat(); //吃饭
}
}
1.3.3 成员变量的默认值
数据类型 | 默认值 | |
基本类型 | 整数(byte,short,int,long) | 0 |
浮点数(float,double) | 0.0 | |
字符(char) | '\u0000' | |
布尔(boolean) | false | |
引用数据类型 | 数组,对象,String | null |
- 定义手机类并使用:
- 属性:品牌,价格,颜色;
- 行为:打电话,发短信
1)定义Phone类:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Phone {
//成员变量
String brand;
int price;
String color;
//成员方法
//打电话
public void call(String name){
System.out.println("给"+name+"打电话");
}
//发短信
public void sendMessage(){
System.out.println("大家新年好!");
}
}
2)定义测试类:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo02 {
public static void main(String[] args) {
//创建对象
Phone p=new Phone();
System.out.println("品牌: "+p.brand); // null
System.out.println("价格: "+p.price); // 0
System.out.println("颜色: "+p.color); // null
System.out.println("-------");
p.brand="华为";
p.price=2999;
p.color="银白色";
System.out.println("品牌: "+p.brand); //华为
System.out.println("价格: "+p.price); //2999
System.out.println("颜色: "+p.color); //银白色
System.out.println("------------");
p.call("刘德华");
p.sendMessage();
}
}
1.4 对象内存图
回顾JVM内存,JVM总共分为5大内存区域,寄存器、本地方法栈、方法区、栈内存(虚拟机栈)、堆内存;和我们程序员有关的为方法区、栈内存、堆内存;
- 方法区:存储类的信息(有多少变量、方法、是什么修饰符修饰的等)、常量信息、静态变量等信息
- 栈内存(VM栈):方法调用时进栈内存执行,也就是方法运行时消耗的内存;
- 堆内存:存储类的实例信息,只要是new出来的信息都存在堆内存
new这个类的时候,Jvm去方法区找有没有这个class,没有就加载到方法区,属性方法这些都是在方法区class中的;Jvm加载完后,就根据这个模板在堆中创建对象给属性赋默认值,然后再执行赋值语句给对象赋值;
一个对象内存图:
两个对象内存图:
对象调用方法时,根据对象中方法标记(地址值),去类中寻找方法信息。这样哪怕是多个对象,方法信息只保存一份,节约内存空间。
1.5 局部变量和成员变量
变量根据定义位置的不同,我们给变量起了不同的名字,看下列测试类:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Person {
String name; // 成员变量
int age; // 成员变量
/**
* 跑步方法
*/
public void running() {
int stretch = 10; // 局部变量
System.out.println("跑了" + stretch + "公里");
}
/**
* 自我介绍方法
* @param name: 姓名(局部变量)
* @param age: 年龄(局部变量)
*/
public void intro(String name, int age) {
System.out.println("大家好,我叫" + name + ",今年" + age + "岁");
}
/**
* 计算方法(列出0~num之间的偶数)
* @param num: 局部变量
*/
public void calculate(int num) {
for (int i = 0; i < num; i++) {
if (num % 2 == 0) {
System.out.println(num);
}
}
}
}
- 局部变量与成员变量:
关于static关键字我们后面再解释,暂时把不用把实例变量与类变量区分开;
- 成员变量和局部变量的区别:
- 成员变量:
- 1)成员变量定义在类中,在整个类中都可以被访问。
- 2)成员变量分为类成员变量和实例成员变量(对象成员变量),实例变量存在于对象所在的堆内存中。
- 3)成员变量有默认初始化值。
- 4)成员变量的权限修饰符可以根据需要,选择任意一个
- 局部变量:
- 1)局部变量只定义在局部范围内,如:方法内,代码块内等。
- 2)局部变量存在于栈内存中,当方法弹栈(执行完毕)后,局部变量销毁;
- 3)局部变量没有默认初始化值,使用前必须手动赋值;
- 4)局部变量声明时不指定权限修饰符;
1.6 值传递和引用传递
1.6.1 思考
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo04 {
public static void main(String[] args) {
Phone p1=new Phone();
p1.price=2899;
test(p1);
System.out.println(p1.price); // 2899 or 3899?
int i=20;
test2(i);
System.out.println(i); // 20 or 50?
}
public static void test(Phone p){
p.price=3899;
}
public static void test2(int i){
i=50;
}
}
1.6.2 形参和实参
形参也叫形式参数,是一个方法的参数列表中的参数;实参也叫实际参数,是调用者在调用方法时实际传递的参数;
1.6.3 值传递和引用传递概念
- 值传递:(参数类型是基本数据类型):方法调用时,实参把它的值传递给对应的形参,形参只是用实参的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形参值的改变不影响实参的值。
- 引用传递:(参数类型是引用数据类型参数):也称为传地址。方法调用时,实参是对象(或数组),这时实参与形参指向同一个地址,在方法执行中,对形参的操作实际上就是对实参的操作,这个结果在方法结束后被保留了下来,所以方法执行中形参的改变将会影响实参。
在Java中,除了基本数据类型之外的数据类型都是引用数据类型,都是通过new在堆内存开辟空间;
- 引用传递对象内存图:
1.6.4 小练习
- 示例代码:
public class Demo {
public static void main(String[] args) {
String[] arr1 = {"1", "2", "3"};
String[] arr2 = {"100", "200", "300"};
test(arr1, arr2);
System.out.println("-------arr1-------");
for (int i = 0; i < arr1.length; i++) {
System.out.print(arr1[i] + ',');
}
System.out.println();
System.out.println("-------arr2-------");
for (int i = 0; i < arr2.length; i++) {
System.out.print(arr2[i] + ',');
}
}
public static void test(String[] arr1, String[] arr2) {
String[] temp;
temp = arr1;
arr1 = arr2;
arr2 = temp;
}
}
1.7 匿名对象
顾名思义,匿名就是没有名字的对象,在创建对象时,只通过new的动作在堆内存开辟空间,却没有把堆内存空间的地址值赋值给栈内存的某个变量用以存储;
使用场景:
- 1)如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
- 2)我们经常将匿名对象作为实参传递给一个方法调用。
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo05 {
public static void main(String[] args) {
Student student=new Student();
intro(student);
student.study();
// 使用匿名对象传递
intro(new Student());
// 使用匿名对象调用方法
new Student().study();
}
public static void intro(Student student) {
System.out.println("姓名: "+student.name+",年龄: "+student.age);
}
}