什么是面向对象
(object - oriented - programming ,OOP )面向对象程序设计
-
一切事物皆对象,在面向对象编程的过程中,我们可以把所有的事物抽象成一个个对象,根据人的认知,将他们分成一个个的类,通过继承,组合等等将所有的事物描述出来,在编程过程中,只需要对这个对象发送指令,对象自己就会完成指令所描述的行为
例如
calss Dog{ //添加属性 int age ; String name ; char gender ; //添加方法 public void fly(){ //.... } } //在我们创建一只特定的狗之后,我想让他飞 //这时只需 发送指令给这个狗(调用这个狗的行为方法) Dog dog = new Dog(); dog.fly();//直接起飞
-
通俗的说,我们可以把面向对象过程中人扮演的角色看作,指挥者
我们只需发送指令,让特定的人来完成指令内容
这个过程,省时省力并且结果比较理想
什么是面向过程
Procedure Oriented 面向过程
-
面向过程的基本流程
分析问题 ====》 分解问题 ====》 解决问题
在这个当中所有的问题都需要人来亲力亲为的解决
比如“做饭”
首先,确定吃什么,其次去购买材料,之后再做饭,再之后才能吃
而面向对象,以吃饭为例,类似于点外卖,我们们只需告诉商家,我们要吃什么,商家就会帮我做好。
-
在面向过程的编程中,人可以看作是 执行者,每个过程都要亲力亲为
费时费力,且结果不一定理想
处理一件事情,能不能完全面向对象
很显然,不可能完全面向对象
就例如点外卖,虽然点的人没有做饭,但商家是不是还要执行这个做饭过程,
所以处理一件事情最少需要一个执行者
- 因此面向对象和面向过程并不是互斥的,面向对象基于面向过程
类与对象
(calss) 类 ,(object) 对象
-
我们可以将类想象成一个摸具,将对象想象成模型。 由类构造对象的过程称之为类的实例
-
但凡存在于现实世界中的所有事物,都可以称之为对象
-
我们将具有相同属性,相同行为的对象进行统称和分类
例如,拉布拉多,和雪纳瑞,都可以称之为一个对象,他们可以分到Dog类中
-
我们来思考一个问题,先有类,还是先有对象?
现实生活中
人是通过观察已有的事物,并对其进行分类。故先有对象后又类
但在编程中
则是先有类,后有对象
因此,我们在编程中,一般情况下都是先创建类,再实例化对象
创建类&&创建对象
创建一个类
-
语法
/* 权限修饰符 类型 class 类名 构造代码块 静态代码块 成员变量/实例变量 静态变量 构造函数/构造器 成员函数/实例函数 静态函数 继承而来的函数 修改器 访问器 } */ // 例如 public class Dog{ String name ; int age ; char gender ; } //需要注意的是,在一个文件中最多有一个public 修饰的类 //当有public修饰的类时,类名必须与文件名一致
创建一个对象
语法
/*
类名 对象名 = new 类名() // 准确来说应该是 = new 构造方法;
*/
// 例如,以上方的Dog类为例
Dog dog = new Dog();
属性/方法
- 访问属性或方法
//我们可以通过 对象名.属性/方法
//例如 ,打印dog的属性
System.out.println(dog.name);
System.out.println(dog.age);
System.out.println(dog.gender);
/*
执行输出:
null
0
(char类型是一个空字符)
*/
/*
可以看到,即使我们并没有赋予属性一个准确的值
系统也会给变量生成一个默认值
这个过程称之为隐性赋值;当然,我们在编写类的过程中就可以直接对属性赋予初值
例如:
public class Dog{ int age = 1 ; String name = "小花"; char gender = '公' ; }
这种直观的可以看到的赋值
我们称之为 显性赋值
并且对于拥有显性赋值的属性
在创建对象时,并不是直接将值赋给了属性
而是先对属性隐性赋值,再进行显性赋值
- 修改属性
//修改属性值 可以通过 对象名.属性= ;
//例如:
dog.name = "黑豹”;
dog.age = 2;
dog.gender = '公' ;
//此时我们再打印
System.out.println(dog.name);
System.out.println(dog.age);
System.out.println(dog.gender);
/*
执行输出:
黑豹
2
公
*/
看起来是不是很简单,很方便?
但是这样简单的访问又存在很大的漏洞与缺陷
属性可以随意修改,访问。
就比如 :
一只狗的年龄
我通过 dog.age = -1 ; 使这只狗的年龄变为-1岁,
这可能吗?什么事物的年龄可以是负数?
显然,这在常识上是不合理的,具有很大的业务逻辑错误那具体要怎么解决呢? 接着往下看
封装
如何解决属性的安全性,避免一些业务逻辑上的错误
在这里就不得不谈谈封装了
- 两层含义:
- 将独立单元或者多个数据进行整体的封装,目的在于重复利用和操作,不需要过多在乎封装的内容,我们只需要调用即可,底层对于调用者而言是透明的
- 使用权限修饰符private来修饰属性,将属性私有化,保证数据的安全性
写到这里又产生了问题,我们将属性私有化之后,外部将无法访问,那如何进行相关属性的赋值呢?
访问器
顾名思义, 就是一个让外界通过其访问类的内部属性的方法
-
-
语法:
public 属性类型 方法名(){
return 属性名 ;
}
一般我们将方法名命名为 get属性
-
public class Dog{
private String name ;
private int age ;
private char gender ;
//属性全部私有化
//通过一个公开的方法,让外界获取属性
public String getName(){
return name ;
}
public int getAge (){
return age ;
}
public char getGender(){
return gender ;
}
}
// 拥有访问器之后,我们就可以通过 对象名.访问方法,来获取属性的值
Dog dog = new Dog();
System.out.println(dog.getName());
System.out.println(dog.getAge());
System,out.println(dog.getGender());
/*
执行且成功访问到属性
*/
解决类访问问题,那属性赋值怎么办呢? 同理,创建公开的修改方法
修改器
顾名思义, 就是一个让外界通过其改变内部属性的方法
-
-
语法
public void 方法名(变量类型 变量名){
this.属性 = 变量名 ;
}
一般我们将方法名命名为 set属性
-
public class Dog{
private String name ;
private int age ;
private char gender ;
//属性全部私有化
//通过一个公开的方法,让外界获取属性
//访问器
public String getName(){
return name ;
}
public int getAge (){
return age ;
}
public char getGender(){
return gender ;
}
//通过一个公开的修改方法,让外界修改属性
//修改器
public void setAge(int age) {
/*
修改器的好处在于我们可以判断传进的变量是否符合逻辑
并自己决定是否修改属性值
*/
if(age<0||age>30){
this.age = 0;
//加上判断如果狗的年龄不在这个范围则赋默认值
}
this.age = age;
}
public void setGender(char gender) {
this.gender = gender;
}
public void setName(String name) {
this.name = name;
}
}
// 拥有修改器之后,我们就可以通过 对象名.修改方法,来修改属性的值
Dog dog = new Dog();
dog.setName ("小黄");
dog.setGender('公');
dog.setAge(2);
System.out.println(dog.getName());
System.out.println(dog.getAge());
System,out.println(dog.getGender());
/*
执行输出
小黄
公
2
*/
//已成功修改
this 指针
- this指针指向当前对象的属性
可以看到,在修改器中使用到了 this.属性名。
//修改器
public void setGender(char gender) {
this.gender = gender;
}
这是因为修改器方法参数与属性名相同
而java变量查找符合就近原则
先在函数内部搜索变量,再到函数外部搜索
倘若写成下面这样,那两个gender都是传入参数
public void setGender(char gender) { gender = gender; }
当然你也可以使用别的变量名来定义传入参数,但不建议这么写,因为规范!!!
到这里其实我们已经基本掌握了创建一个类,并实例化一个对象的方法
但是,仔细想想创建一个对象并初始化属性,几个属性我就要写几行代码
倘若我要创建好几个对象呢,这是就会发现这样写其实非常臃肿
Dog dog = new Dog(); dog.setName ("小黄"); dog.setGender('公'); dog.setAge(2); System.out.println(dog.getName()); System.out.println(dog.getAge()); System,out.println(dog.getGender());
那有没有办法解决呢?
构造函数
- 语法:
修饰符 类名称(){
}
前面说到 创建一个对象的语法为:
类名 对象名 = new 类名()
准确来说应该是 = new 构造方法;
构造函数是一个函数名与类名一致的方法。
可是我明明没有写这个所谓的构造方法啊
这是因为:
在java类中,如果没有自己定义的构造函数时,JVM会自动生成一个无参的构造函数,方便我们构建一个属性都是默认值的对象
但是当我们定义了一个构造函数时,JVM价格不在自动生成这个无参的函数,如果需要这个无参的构造函数,需要自己定义,建议一定要写无参方法,因为规范!!!
public class Dog {
private String name ;
private int age ;
private char gender ;
//有参构造方法
public Dog(String name, int age, char gender) {
this.age=(age<0||age>30)?0:age;
this.name = name;
this.gender = gender;
}
//无参构造方法
public Dog(){
}
public void setAge(int age) {
this.age=(age<0||age>30)?0:age;
}
public void setGender(char gender) {
this.gender = gender;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public char getGender() {
return gender;
}
}
//这时当我们创建对象时只需要调用有参的构造方法即可
Dog dog1 = new Dog("小黄",2,'公');
Dog dog1 = new Dog("小黑",6,'母');
//通过访问器输出发现,已经成功在擦汗黄建对象的同时,对其属性进行了初始化
一个标准的java类
1、所有的成员变量都要使用private关键字修饰
2、为每一个成员变量编写一对儿Getter/Setter方法
3、编写一个无参数的构造方法
4、编写一个全参数的构造方法