面向对象
1. 概念
- 面向对象是一种的程序设计方法,其基本思想是使用对象和类等基本概念来进行程序设计。
- 类是对现实世界事物的抽象定义
- 类是现实世界中,同一类事物的共同的特征的抽象
- 类的成员包括属性和方法
- 对象是类的实例,与现实世界的一个具体的事物相对应
- 对象是类的实例
- 类是对现实世界事物的抽象定义
2. 类与对象之间关系
针对一类对象进行抽取,把共有的特征抽取成了属性,把共有的行为抽象成了方法,把这一类对象抽取成了类。类就是对对象概况,对象就是类具体实现。
- 从一个类可以创建多个不同的对象
- 每个对象有自己不同的属性值
3. 对象属性的初始化
当执行new语句创建一个对象时,JVM执行以下基本步骤:
1. 为新对象分配一块适当大小的内存,以用来保存该对象的属性值,属性值也称为对象状态
2. 在该内存中,将对象的所有属性进行缺省初始化赋值
3. 对有显式初始化赋值的属性执行显式初始化赋值
-
对象的属性,也称为字段,又称为实例变量。
-
对象属性的初始化,即初始赋值,有以下同两种方式:
- 缺省初始化赋值:对象被创建时,不同类型的属性首先被赋予不同的缺省值。
- 显式初始化赋值:如果在类中声明属性时指定了初始值,那么在对象被创建时,相应的属性就被显式地赋予此初始值。
-
-
垃圾对象 — 内存中不再被使用的对象
- 垃圾回收(GC) — JVM自动释放垃圾对象所占内存的机制
- 如果对象再没有被引用变量引用时,便称之为垃圾,其所占用的内存将很快被JVM回收。
4. 构造方法
- 与类同名,没有方法返回值类型
- 用于构建对象
- 有参构造,给对象属性进行初始化赋值
- 支持重载
- 当类中没有定义任何形式构造方法时,底层JVM就会默认添加无参构造。当类中定义构造方法底层就不再提供无参构造
- 类中至少含有一个构造
- 注:大部分的企业要求,声明类的时候,必须显式声明无参构造器
package cn.tedu.object;
public class ObjectDemo1{
public static void main(String[]args){
//**创建Person类对象p
//**调用构造方法用于创建对象
Personp=newPerson("莉莉");
//**给对象属性赋值
//.**代表“的”的意思
p.name="**豆豆";
p.age=20; p.gender='男';
//**对象调用方法
p.eat();
}
}
//**定义类---代表人的类
class Person{
//**特征---属性
//**不加static属性就是非静态属性
//**成员变量
String name;
char gender;
int age;
//**当类中没有定义任何形式构造方法,底层会自动添加无参构造
//**当类中已经定义构造方法,底层不再自动添加无参构造
//1.**与类同名2.没有方法返回值类型
public Person(){}
//**普通方法也能与类同名
public void Person(){}
//**有参构造
//**属性初始化
//**构造方法支持重载
public Person(String name){
//**方法就近原则
//**对象才能调用到非晶态属性---name
//this---**代表当前类的对象
this.name=name;
}
//**行为---方法
//**成员方法---描述类的信息
public void eat(){
//**局部变量
inta=1;
System.out.println(name+"在吃饭...");
}
public void sleep(){
System.out.println(name+"在睡觉...");
}
}
5. this
-
作为关键字,代表当前类的对象
- this可以代表当前类还没有创建对象,this也可以代表当前类刚创建出来的对象,还可以代表当前类正在使用的对象。this只是虚拟指代可以指代成当前类正在活动的对象
this.name=name;
-
注意:在方法中,变量遵循就近原则,因此,一般使用this来区别方法的局部变量和实例的属性
-
this语句
- 用于在同一个类中的构造方法里调用别的构造方法,必须在构造器的首行使用
this(); // 调用本类无参构造器
this(name,age,....) // 调用本类带参构造器
-
应用场景:
- 本类创建对象时,需要执行一些复杂的初始化操作,这些代码可以放在无参构造器中,在其他的带参构造器中,添加
this();
语句,保证无参构造器中的初始化代码被执行 - 本类的多个不同参数个数的构造器,一般是使用
this(...)
调用参数最全的那个带参构造器,这样未来如果初始化的逻辑发生变化,仅需要修改参数最全的那个构造器的代码即可
- 本类创建对象时,需要执行一些复杂的初始化操作,这些代码可以放在无参构造器中,在其他的带参构造器中,添加
package cn.tedu.object;
public class ThisDemo{
public static void main(String[]args){
//**创建对象
Students1=newStudent();
//this**可以代表当前类刚创建对象
System.out.println("s1:"+s1);
//**创建对象
Students2=newStudent("莉莉");
//this**是一个灵活的指代
System.out.println("s2:"+s2);
//**调用方法
//this**可以指代成正在使用的对象
s1.study();
}
}
//**代表学生的类
class Student{
//**属性
String name;
int age;
char gender;
//**无参构造
public Student(){
//System.out.println("this:"+this);
}
//**有参构造
public Student(String name){
this.name=name;
//System.out.println("this:"+this);
}
public Student(String name,int age){
//this.name=name;
//**调用单个参数有参构造
//Student(name);
//this**语句---在构造方法里调用别的构造方法
//**在首行使用 this(name);
this.age=age;
}
//**方法
public void study(){
//this**可以代表当前类还没有产生的对象
System.out.println(this.name+"在学习...");
//System.out.println("this:"+this);
}
}
6. 构造代码块
- 方法外类内定义{}
- 属性初始化
- 优先于任何形式构造方法先执行
- 作用: 保证不论调用什么构造器,构造代码块中的代码都会被执行
package cn.tedu.object;
public class ObjectDemo2{
public static void main(String[] args){
//
Babyb=newBaby();
System.out.println(b.name+","+b.age);
Babyb1=newBaby("tom");
System.out.println(b1.name+","+b1.age);
}
}
//**代表婴儿的类
//**类产生对象就是具体的婴儿
//**保证孩子出生之后名字叫莉莉,年龄是1岁
class Baby{
//**属性
String name;
char gender;
int age;
//**构造代码块(属性初始化)
//**优先于任意形式构造方法先执行
{
this.name="莉莉";
this.age=1;
}
//**无参构造---出生之后才起的名字,年龄是0岁
public Baby(){
//**选中代码区域。Ctrl+Shift+?进行注释
//**取消注释就是再按一遍
this.name="**莉莉";
this.age=1;
}
//**有参构造---出生之前名字起好了
public Baby(String name){
//
this.name=name;
this.name="**莉莉";
this.age=1;
}
//**出生之前名字和年龄给值了
public Baby(String name,int age){
/*this.name=name; this.age=age;*/
/*this.name="**莉莉"; this.age=1;*/
}
//**方法
public void cry(){
System.out.println(name+"在嗷嗷的哭...");
}
}
题目:
public class Cat(){
String type;
int age;
{
System.out.println("构造代码块被执行")
this.age=5;
}
public Cat(){
this.age=3;
System.out.println("无参被调用");
}
public Cat(String type){
this();
this.type=type;
}
public Cat(String type,int age){
this();
this.type=type;
this.age=age;
}
}
public class Test{
public static void main(String[] args){
Cat cat1=new Cat("中华田园猫",6);
System.out.println("age="+cat1.age);
}
}
7.局部代码块
- 方法内定义{}
- 可以控制变量声明周期,提高内存的利用率
package cn.tedu.object;
public class ObjectDemo3{
public static void main(String[]args){
int j=2;
//**局部(方法)代码块
//**控制变量生命周期
{
int i=1;
System.out.println(i+j);
}
//1000**行代码和前面的变量没有关系
}
}
8. 成员变量与局部变量的区别
-
位置
- 成员变量:方法外类内
- 局部变量:
- 方法内/代码块内
- 方法参数列表中的变量,看作方法的局部变量
-
使用范围
- 成员变量:整个类
- 局部变量:整个方法/代码块
-
内存
- 成员变量:堆
- 局部变量:栈
-
生命周期
- 成员变量:随着类的对象的创建而产生,随着对象的回收而消失
- 局部变量:随着方法的调用/代码块的执行而出现,随着方法调用完毕/代码块执行完毕而消失
总结:
-
方法相关
- 值传递:调用方法时,传入的是值的拷贝还是内存地址
- 基本数据类型:值的拷贝
- 引用类型:内存地址
- String:值的拷贝
- 递归:在方法中调用了自己
- 必须有递归的出口
- 什么时候用?常规的思路解决不了的时候,用递归的想法试试
- 值传递:调用方法时,传入的是值的拷贝还是内存地址
-
面向对象编程相关
- 什么是面向对象编程?
-
语法上:
- 类的声明
- 属性的声明
- 方法的声明
- 构造方法
- 构造代码块
- this关键字
- this语句
- 对象的创建
- 对象创建的执行过程
- 对构造方法的调用
- 局部代码块
- 类的声明
拓展阅读
面向过程
- 面向过程和面向对象都属于编程范式(思考方式),即分析这个世界,并用计算机模拟世界的方式
- 面向过程方法认为:
- 我们的世界是由一个个相互关联的小系统组成
- 每个小系统都有着明确的开始和明确的结束,开始和结束间有着严谨的因果关系
- 只要将小系统中的每一个步骤和影响这个小系统走向的所有因素都分析出来,我们就能完全定义这个系统的行为
- 基于面向过程分析这个世界并用计算机来模拟它,需要:
- 将现实世界的过程秒回出来,把它们的英国关系都定义出来
- 通过结构化的设计方法,将这些过程进行细化,形成可以控制的、范围较小的部分
- 即找到过程的起点,顺藤摸瓜,分析每一个部分,直至达到过程的终点
面向过程的困难
- 面向过程蕴涵着一个前提假设,即这个过程是稳定的,这样才有分析的基础,所有的工作成果都依赖于对这个过程的步步分析
- 并且,过程中的每一步都是预设好的,有着严谨的因果关系
- 随着时代的发展,现实世界无时无刻不在发生着变化,系统所依赖的因果关系变得越来越脆弱
- 并非面向过程的方法不正确,只是因为构成一个系统的因素太多,要把所有可能的因素都考虑到,把所有因素的因果关系都分析清楚,再把这个过程模拟出来实在太困难,可能分析还没完成,现实已经发生了变化
面向过程与面向对象
面向对象大师Grady Booch在2004年IBM Developer Works Live!大会的访谈中讲过的一段流传甚广的话:
我对面向对象编程的目标从来就不是复用。相反,对我来说,对象提供了一种处理复杂性问题的方式。这个问题可以追溯到亚里士多德:您把这个世界视为过程还是对象?在面向对象兴起运动之前,编程以过程为中心,例如结构化设计方法。然而,系统已经到达了超越其处理能力的复杂性极点。有了对象,我们能够通过提升抽象级别来构建更大的,更复杂的系统——我认为,这才是面向对象编程运动的真正胜利。
- 面向对象:
- 将世界看做一个个相互独立的对象,相互之间并无因果关系
- 只有在某个外部力量的驱动下,对象之间才会依据某种规律相互传递信息
- 这些交互构成了现实世界的一个“过程”
- 面向对象与面向过程的根本不同,在于不再将世界看过一个紧密关联的系统,而是看成一些相互独立的离散的小零件,这些零件依据某种规律组织起来,完成一个特定的功能。
- 面向对象可以帮助我们构造更为复杂的系统来解释越来越复杂的现实世界
- 独立对象依据某个规律结合在一起,具备了特有性质和功能,然后又构成更为复杂的更大的对象,这正是面向对象的基本原理