面向对象基础
核心基础
一、面向对象概述
1、官方概述
面向对象(Object Oriented)是软件开发方法。面向对象的概念和应用已超越了程序设计和软件开发,是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。
面向对象是相对于面向过程来讲的,指的是把相关的数据和方法组织为一个整体来看待。
面向过程到面向对象思想层面的转变:面向过程到面向对象,是程序员思想上 从执行者到指挥者的转变
编写程序通常是为了解决生活中的一些问题,程序来源于生活。
2、解释
下面举个例子便于理解面向对象思想:
- 例1:
问:把大象装进冰箱,需要分几步? 回答: 面向过程回答:3步:1把冰箱门打开, 2把大象装进去 ,3把冰箱门关闭 面向对象回答:2步:1招一个能操作冰箱的工人(对象),2指挥工人装大象 思考: 如果问题改成: 把100只大象依次关进冰箱,共分为几步? 面向过程回答: 每个操作都要自己执行 面向对象回答:还是2步:1招一个能操作冰箱的工人(对象),2指挥工人把大象依次装进去。
- 结论:们发现面向过程很死板,很难适应变化。而面向对象更灵活,可复用性更高。
- 缺点:找工人比直接执行慢,但是在实际程序中这个缺点可以忽略不计。
再举一个大型开发场景的例子,理解面向对象思想的好处:
-
例2:实际生活场景
场景:当我们独自生活时,我们经常纠结一日三餐怎么吃。
面向过程:每天亲力亲为:买菜 - 做饭 - 吃饭 - 洗碗 的过程。
面向对象:雇一个保姆,帮你做这些事。场景升级:假设你是一个富豪,拥有一座占地3000亩地的庄园,不再是只关注吃饭问题,还有花草树木修剪,泳池维 护清洗,卫生打扫,洗衣做饭…
面向过程:所有事都自己做。
面向对象:招聘一个管家, 然后让管家招聘园丁、泳池维护工、保姆等等。 -
结论:我们发现面向过程需要关注很繁琐的过程,而面向对象不用关注具体的细节,更关注的是统筹架构的问题。
我们进行大型应用开发时,如果写程序只关注过程的话,代码量达到一定层次以后,就很难再编写下去了。
3、三大思想
面向对象思想从概念上讲分为以下三种:OOA、OOD、OOP (具体后面学习)
- OOA:面向对象分析(Object Oriented Analysis)
- OOD:面向对象设计(Object Oriented Design)
- OOP:面向对象程序(Object Oriented Programming)
4、三大特性(重点)
- 封装性:所有的内容对外部不可见
- 继承性:将其他的功能继承下来继续发展
- 多态性:方法的重载本身就是一个多态性的体现
二、类与对象的关系
生活中需要招聘工人,在程序中是创造具备某个功能的对象。
将类比作图纸,对象比作实物:
1、区别
图纸规定类有共性的属性,实物的属性有各自具体的值。(区别)
-
类表示一个共性的产物,是一个综合的特征。
-
对象是根据图纸造出来的个体。
2、联系
类必须通过对象才可以使用,对象的所有操作都在类中定义。(联系) 类由属性和方法组成:
类里面有成员,包含类的组成部分(成员属性、成员方法)。可把属性理解为类里面定义的一个描述共性的变量。 当根据类创建对象后,对象中就具有可以存储数据的执行变量。
可把方法理解为行为。
- 属性:就相当于人的一个个的特征
- 方法:就相当于人的一个个的行为,例如:说话、吃饭、唱歌、睡觉
三、类与对象创建
类的定义格式:
class 类名称 {
成员属性
成员方法
}
类必须编写在.java文件中,一个.Java文件中可以存在n个类,但是只能存在一个public修饰的类。(public是权限修饰符的一种,叫做公有的,表示所有位置都可以访问,后面会学习)
- 为什么一个.java文件中只能存在一个public修饰的类?
这个类作为整个文件存在的公共类存在,一个文件只能有一个主类存在。
注意:.Java的文件名称必须与public修饰的类名完全一致
1、如何定义一个类?
//类就像是图纸
class Person {
//属性:特征
String name;
int age;
char gender;
//方法:行为
/**
* void:没有返回值
* 定义格式:
* 返回值类型 方法名称(形式参数列表){
* 方法体
* return 返回值;
* }
*
* 调用格式:
* 对象名.方法名称(实际参数列表);
*/
void say() {
System.out.println("自我介绍:我是"+name+",我的年龄:"+age+",我的性别:"+gender);
}
}
2、如何创建对象?
public class Demo {
public static void main(String[] args) {
//创建对象的格式:
//类名 对象名 = new 类名();
Person p = new Person();
//给对象属性赋值的格式:
//对象名.属性名 = 值;
p.name = "张三";
p.age = 18;
p.gender = '男';
p.say();
}
}
- 运行结果:
- 再加一个方法(行为):计算算术和
通过//定义形式参数列表x和y,表示未来要执行这个方法必须传两个int型的参数 int sum(int x, int y) { //方法体中的参数可以当作两个变量来使用 int z = x + y; //表示传入形式参数x和y,x和y进行求和,赋值给z,最终把z返回给调用者 return z; }
p.sum(100,200);
调用。(100和200为实际参数)结果就是return返回值。int s = p.sum(100,200); System.out.println(s);
- 运行结果:
- 运行结果:
系统中有一种特殊的方法写法:
void xxx() {
if(ture) {
//没有返回值还写return
//表示如果某个结果为true,直接结束方法,不再继续执行
return;
}
System.out.println();
}
return:表示方法已经结束,结果已经产生,后续已经不能再写代码了。
四、对象创建和使用
-
同一个包中类名不能重名,在上述基础上我们再创建一个Book类:
/** * @author Elvira * @date 2020/9/23 14:57 * @description */ public class Demo2 { public static void main(String[] args) { //创建第一本书 Book b1 = new Book(); //创建第二本书 Book b2 = new Book(); b1.name = "红宝书"; b1.info = "讲述了一本红色的书深入人家成为宝贝的历程。"; b1.say(); } } class Book { String name; String info; void say() { System.out.println("书名:"+name+",简介:"+info); } }
- 运行结果:
-
此时使用
b2.say();
就会出现:
因为没给b2各属性进行赋值,引用数据类型默认值为null。 -
给b2属性赋值并调用say()方法:
b2.name = "黄宝书"; b2.info = "讲述了一本黄色的书潜入人们家中成为宝贝的故事。"; b2.say();
- 运行结果:
-
b1、b2同时调用say()方法:
b1.say(); b2.say();
- 运行结果:
五、对象创建内存
对象创建内存的细节
public class Demo3 {
public static void main(String[] args) {
Book b1 = new Book();
b1.name = "红宝书";
b1.info = "讲述了一本红色的书深入人家成为宝贝的历程。";
b1.say();
Book b2 = b1;//是复制吗?
b2.name = "嘎嘎嘎";
b1.say();
}
}
class Book {
String name;
String info;
void say() {
System.out.println("书名:" + name + ",简介:" + info);
}
}
-
运行结果:
书名改变了,Book b2 = b1;
并不是复制。 -
为什么b1.say()是上面的运行结果?
b1和b2叫做对象名称,指向同一个对象,通过b2改变name的值,另一个也发生了变化。
通过上述示例我们发现,名称就是名称,一个对象可以有多个名称。 -
内存主要划分为:栈、堆、方法区、PC寄存器、本地方法栈,学习过程中能使用到的只有栈和堆,其他简单了解下。
-
方法区:存储类信息、静态变量、常量、成员方法
-
PC寄存器:保存正在执行的指令
-
本地方法栈:保存本地(native)方法的地址
-
六、栈
Java中一个线程就会有一个栈。(线程后面学习)栈中存储数据的方式是先进后出。
1、栈的存储特点
先进后出:比作一个瓶子里放糖块,先放的糖块在底部,只能后取出,后放的糖块在上面,可以先取出。
2、栈的存储方式
确定存储范围,有一个指针,通过 ‘栈指针’ 来创建空间与释放空间,速度特别快。
- 假设栈中存放的数据如下:
- 当指针移动,栈的范围就发生了改变,超出范围的就是垃圾,释放掉。
- 通过移动指针的方式创建空间和释放空间,速度快。
- 缺点:栈内存这种移动方式必须每次确定移动的大小和范围,所存储的每一个数据都有自己的大小,限制了数据存储的灵活性,导致在存储某些数据时可能无法存储。
此时有更大的数据,建议存放在堆中。
七、堆
堆内存中存储的是(引用数据类型)对象。空间占用大小不固定。
真正存储时我们无法改变Java对象的存储位置,这是Java定好的,对象在堆中存储。
方法:存储在方法区中
class Book {
String name;
String info;
void say() {
System.out.println("书名:" + name + ",简介:" + info);
}
}
栈中的变量名指向堆中的内存地址:
Book b1 = new Book();
b1.name = "红宝书";
b1.info = "讲述了一本红色的书深入人家成为宝贝的历程。";
b1.say();
Book b2 = b1;
b2.name = "嘎嘎嘎";
b1.say();
- 对于上述代码详解:
Book b1 = new Book();
这句话的意思是:将Book b1存储在栈内存底部,默认值为null;在堆内存中开辟了一块新的名为Book的内存空间,b1指向Book的地址。b1.name = "红宝书";b1.info = "讲述了一本红色的书深入人家成为宝贝的历程。";
这句话的意思为:给Book中的属性name和info赋值。通过b1指向Book地址查找属性,找到属性后赋值b1.say();
调用方法区中的方法Book b2 = b1;
相当于在栈内存中又创建了一个名为b2的区域b2.name = "嘎嘎嘎";
通过b2找到Book中的name,将其值修改为“嘎嘎嘎”b1.say();
通过b1调用方法,指向同一地址,内容已经修改为“嘎嘎嘎”
程序执行完毕,b1和b2会被释放,先释放b2,再释放b1。没有任何栈内存指向堆内存中的地址(某一对象没有引用指向它),此时满足GC垃圾回收,程序会自动回收堆内存中没有任何引用的对象。
- 一个特殊情况:
Book b1 = new Book();//创建第一本书 b1.name = "红宝书"; b1.info = "讲述了一本红色的书深入人家成为宝贝的历程。"; Book b2 = new Book();//创建第二本书 b2.name = "黄宝书"; b1.info = "讲述了一本黄色的书深入人家成为宝贝的历程。"; b2 = b1;//将b1赋值给b2,指向同一堆内存地址 b2.name = "绿宝书"; b1.say();
八、构造方法
又称构造器,是一种构建对象,创造对象的方法。
所有Java类都存在一个或一个以上的构造方法,如果我们没有编写,则代码在编译的时候会自动生成一个没有任何参数的构造器,如果写了就不会生成。
- 语法格式:与普通方法基本相同
区别:方法名称与类名相同,没有返回值类型的声明
1、无参构造方法
Person1() {
System.out.println("创建对象时调用构造方法");
}
作用:用于对象初始化。比如游戏loading界面,把某些资源加载进来,进行初始化。
之后会经常使用的场景:连接数据库,往里存储数据
我们将存储数据的操作流程封装成一个对象,使用它,进行存取操作。这个对象初始化操作:测试能否正常连接数据库,要准备数据库的账号和密码进行连接。
2、有参构造方法
调用时需要传参
Person2(String n) {
name = n;
}
此时new Person2("张三");
时,name被设置为了张三。
不建议不写无参构造方法,最好每次都自己写上,提供好无参和全参的构造方法,后续使用更加灵活。
九、方法的重载
一个类中定义的方法允许重载(相同方法名)
- 方法名相同
- 参数列表长度/参数列表类型/参数类型顺序不同
- 与返回值类型无关
根据传递参数类型调用符合的方法。
class Math{
int sum(int x,int y) {
int z = x+y;
return z;
}
double sum(double x,double y) {
double z = x+y;
return z;
}
double sum(int x,double y) {
double z = x+y;
return z;
}
double sum(double y,int x) {
double z = x+y;
return z;
}
}
十、构造方法重载
与方法重载方式基本一样。
class Person3{
Person3(String name2,int age2){
name = name2;
age = age2;
}
Person3(String name2){
name = name2;
}
String name;
int age;
void say() {
System.out.println("姓名:"+name+", 年龄:"+age);
}
}
十一、匿名对象
public class Demo {
public static void main(String[] args) {
//m只使用一次
//Math2 n = new Math2();
//int num = m.sum(100, 200);
int num = new Math2().sum(100, 200);
System.out.println(num);
}
}
class Math2{
int sum(int x,int y) {
return x+y;
}
没有对象名称的对象,就是匿名对象。栈内存中没有任何名称,只在堆内存中创建内存。匿名对象只能使用一次,因为没有任何的对象引用,所以将称为垃圾,等待被GC回收。
如果一个对象我们要使用两次或以上,一定要给对象创建对象名称。
public class Demo {
public static void main(String[] args) {
//在堆内存中开辟了三个内存空间
new Person5().name = "张三";
new Person5().age = 18;
new Person5().say();
}
}
class Person5{
String name;
int age;
void say() {
System.out.println("姓名"+name+"年龄"+age);
}
}
- 运行结果为:
原因:在堆内存中开辟了三个内存空间,如下图所示,未给第三个内存空间设置name和age。
十二、阿里巴巴编程规范
可以去网上搜一版《阿里巴巴Java开发手册》,看看大厂是怎么怎么写代码的。此处摘取一段基础规范。重点关注命名风格和代码格式:
命名风格
- 【强制】表示一定不能违反
- 千万不要使用拼音英文混合,这样会导致阅读起来十分困难
-
类名使用大写驼峰命名法;方法名、参数名、成员变量、局部变量都统一使用小写驼峰命名法
-
常量单词之间用下划线_连接,力求语义表达清楚,不要嫌名字长
代码格式
总结
学习了面向对象思想之后,发现Java开发非常聪明,只需要操作对象即可完成想要进行的行为,对类和对象的关系更加清晰。还学习了栈和堆,明白了它们之间的联系。最后对方法的重载给程序员提供了极大便利,Java是一门强大的编程语言。