写在前面:
- Focus
- Keep your mind clean
- Beginners mind.
- No Ego.
- There is no career goal.
- Shut up.
- Mindfulness. Care. Awareness
- There is no Boss
- Do something else
- There is nothing special.
上一篇文章:JavaSE学习笔记2
8 类与对象
8.0 面向过程与面向对象思想
首先我们来举一个例子:
假如我们想吃饭,现在有两种方式吃到饭,一种是自己做饭吃,一种是点外卖。
我们先来看第一种方式——自己做:首先我们需要买菜,然后洗菜,再切菜,然后炒菜,最后我们就可以吃到我们自己做的菜了;
接着我们来看第二种方式——点外卖:首先我们需要点外餐,然后餐馆接收到信息后,就做菜然后让骑手送餐,最后我们就可以吃到外卖了。
我们对比下这两种方式,第一种我们每一步都需要亲自参与,所以我们扮演了一个执行者的角色;第二种我们只需要下命令,然后就会有其他人帮我们完成,我们就可以得到最后的结果,我们在其中扮演了一个指挥者的角色。
以上,执行者就是面向过程的方式,指挥者就是面向过程的方式。
面向过程
解决问题时,我们需要亲自去操作其中的每一步。
面向对象
解决问题时,我们可以借助一些工具或者事物来帮助我们,我们只关心它最后的结果。
但是回头来看,面向对象最后需要解决一个问题,也是需要有东西来解决这个问题,那最终解决这个问题的工具或事物也必然是面向过程的。
因此,面向过程与面向对象并不是独立的,而是相辅相成的。
8.1 类与对象的关系
8.1.0 什么是对象
我们常听到一句话说万物皆对象
,那什么是对象呢?
对象其实就是每一件事与每一个物,比如说,一个人是一个对象,一台电脑是一个对象,一个房子是一个对象,等等。
我们可以对一个对象进行描述,比如说一个人,他有着姓名,性别,年龄,身高,体重等等,这些就是一个人的属性,这个人他能够跑步,上课,写代码这些能力,这些就是一个人的行为。
这也对应这任何事物:
- 对象的属性:就是对象的各种特征
- 对象的行为:就是对象可以产生的动作
8.1.1 什么是类
比如说一个名叫张三的男人,一个名叫李四的女人,他们都可以统称为人,所以,这两个对象都来自于人这个类
。
所以说,类
就是那些具有相同属性特征和行为的对象
的统称,对象
就是该类下的具体实例
;类
就相当于一个蓝图
,而对象
就是照着蓝图实际出来的实体
。
8.1.2 一个例子
定义一个圆类
class Circle {
//数据域,也叫作类的属性,成员变量
double radius;
double type = 1;
//行为,成员方法
public double getArea() {
return Math.PI * radius * radius;
}
public double getPerimeter() {
return 2 * Math.PI * radius;
}
public void setRadius(double newRadius) {
radius = newRadius;
}
}
如何生成一个类的对象呢?
类名 变量名 = new 类名();
类名 变量名 = new 类名(参数);
//创建一个Circle对象c1
Circle c1 = new Circle();
//调用这个对象中的属性
c1.radius;
//调用这个对象中的方法
c1.getArea();
8.2 封装与private关键字
封装表面意思就是包装,将我们程序的细节包装起来,只给外界呈现它的使用方式,然后我们就可以通过它来得到结果,而不用在乎其中的细节。
封装的常见体现:
- 类
- 方法
封装的好处:
- 提高了安全性
- 向外界隐藏了一些不需要外界获知的内容
- 提高了代码复用性
封装并没有完全让外界无法访问,还是提供了相应访问内部的方法
在第6章方法中曾提及过权限修饰符,这里在来探讨下。
修饰词 | 本类 | 同一个包的类 | 继承类 | 其他类 |
---|---|---|---|---|
private | √ | × | × | × |
空 (默认) | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
他们不仅可以修饰方法还可以修饰类和属性,我们可以先看下private
修饰符,他只能在本类可访问,在其他地方都不可访问。
我们可以看下下面这个例子:
//Sample.java
public class Sample {
Person p1 = new Person();
p1.name = "小明"; //报错
}
class Person {
private String name;
private int age;
public void speak() {
System.out.println("我是" + name + ",今年" + age + "岁");
}
其报错原因就是无法通过,p1.name
来访问到name
变量,我们在设计类的时候,通常将我们变量使用private
来修饰,使其外部无法直接访问或修改其值,而这时我们将需要访问或修改的变量通过设置访问器(getXXX
)或修改器(setXXX
)的方法来实现。
8.3 局部变量与成员变量
我们按照的作用域可以将变量分为局部变量和成员变量,局部变量只能在声明它的方法中使用,而全局变量可以在整个类中去使用。
成员变量,在类被创建为对象时,在该对象存储空间中创建成员变量并默认初始化,在之后的该对象的任何地方都可以通过使用this
来操作该对象的成员方法。
局部变量,在方法被加载入栈的时候,并执行声明语句时创建变量与变量存储空间,并在之后的方法体中可以使用该变量,直至方法结束(return),弹出栈之后,变量存储空间被回收。
区别:
声明周期 | 存储位置 | 定义位置 | 初始化 | |
---|---|---|---|---|
成员变量 | 与对象的生存周期相同 | 堆中对象所属空间里 | 在类中,在方法外 | 默认被初始化 |
局部变量 | 从声明创建开始到方法结束为止 | 栈中方法所属空间里 | 在方法中 | 无默认初始化,必须先初始化后才能调用 |
8.4 构造器(方法)
我们在创建一个对象时是通过:类名 对象名 = new 类名();
的方式,或者通过带参的方式来创建的一个对象,其实,new
后面的是一个名叫构造器的方法,我们之前创建的类中可能没有给定构造器,但是也能使用无参的构造器,这是因为编译器会为无构造器的类中隐式的创建一个无参的构造器。
构造器的格式
权限修饰符 类名(参数列表) {
代码块...
}
注意:
- 构造器没有返回值,存在返回值会被当做一个成员方法
- 构造器的名称必须是类名
- 构造器参数列表可为空,即无参构造器
- 当类中没有定义显式构造器时,会为我们隐式创建无参构造器
- 构造器只能在对象创建时被调用,构造器可以调用与之重载的构造器,但是必须在第一行使用
this(..)
调用,且不能产生递归调用 - 构造器的作用主要用于对象的初始化工作
8.5 static关键字
static
的作用与其本意相同,静态,它可以用于修饰方法与全局变量,注意,该修饰符不是权限修饰符,也不能修饰局部变量。
当static
修饰方法时,该方法作为静态方法,该方法可以不用声明对象,直接通过类名.静态方法名
去调用,当然也可以通过对象名.静态方法名
去调用。当一个成员方法不访问成员时,可以定义为静态方法。注意,此时静态方法无法该类中的其他非静态成员,只能访问静态方法或静态变量。
当static
修饰成员变量时,该变量作为静态变量,该变量将不在属于某个对象独享,而是属于整个类,直接通过类名.静态变量名
去调用,当然也可以通过对象名.静态变量名
去调用,静态变量只会在类被装载时创建一次,之后不再创建,在程序结束时按序销毁。
8.6 静态变量与成员变量
存储位置 | 生命周期 | 归属 | 调用方式 | |
---|---|---|---|---|
静态变量 | 类装载的静态方法区中 | 类的装载而创建,程序的结束而销毁 | 整个类 | 通过类的方式来调用,也可以通过对象的方式来调用 |
成员变量 | 堆内存下对象所属空间里 | 对象的创建而创建,对象的回收而回收 | 单个对象 | 只能通过对象的方式调用 |
9 继承
9.0 概述
9.0.0 一个例子
class Dog {
double weight;
double height;
void say {
System.out.println("Wang wang.");
}
}
class Cat {
double weight;
double height;
void say {
System.out.println("Meow meow.");
}
}
这里可以看出,两个类一个表示狗,一个表示猫,他们都有相同的属性,体重和身高,他们都能发声,但是发声的内容不一样,多个事物之间有共同的属性或行为,我们可以对其进行抽取,然后单独成类。
总体看,狗类与猫类他们都可以属于一个更大的类——动物类,我们可以构建一个动物类,将他们共有的属性和行为包含在里面。
class Animal {
double weight;
double height;
}
我们这里并没有写say()
这个方法,原因是,他们在狗类与猫类中的表现是不一样的,并不能提取出来。
但是,现在这三个只是独立的类,并没有任何的联系,我们使用关键字extends
来表明他们的关系。
class Animal {
double weight;
double height;
}
class Dog extends Animal {
void say {
System.out.println("Wang wang.");
}
}
class Cat extends Animal {
void say {
System.out.println("Meow meow.");
}
}
这样就可以表示他们之间的关系,总的来说,我们把多个事物的重复性的内容提取出来,生成一个新的类,这个类就是其他类的父类,也叫作基类或超类,其他的类称为子类或派生类,他们之间用extends
关键字来标识。
9.0.1 单继承与多继承
父类与子类是一对多的关系,是一种 is a 的关系,因此,Java中类与类之间只能是单继承,但是接口与接口之间支持多继承。
class A {}
class B {}
class C extends A {} //正确写法
class D extends A, B {} //没有这种写法
interface E {}
interface F {}
interface G extends E {} //正确写法
interface G extends E, F {} //正确写法
9.0.2 继承中的特点
变量
- 当父类中的变量被
private
修饰后,子类将不能直接使用父类中的变量 - 子类中可以声明与父类相同名称的变量,两变量没有任何关系,在子类中直接访问变量名是子类中的变量,这时只有使用
super
关键字才能访问父类变量
this
可以表示当前对象,但是super
不表示父类的对象,因此在继承中,只实例化了子类并没有实例化一个父类的对象,super
只表示在子类实例化对象堆存储空间中一个表示父类的空间
方法
- 当父类中的方法被
private
修饰后,子类将不能直接使用父类中的方法 - 当子类声明与父类同名的方法,若父类方法没有被
private
修饰且子类中方法的权限修饰符大于或等于父类时,这里称之为重写(Override),这时也只有使用super
关键字才能访问父类方法。
在程序寻找一个变量或者方法时,按照局部 → \rightarrow →对象 → \rightarrow →静态 → \rightarrow →父类
9.0.3 继承中构造器的特点
- 子类继承父类,子类中每一个构造器第一句如果不是
this(..)
,那默认第一句是隐藏的super()
- 创建子类对象时,需要给父类中继承给子类的变量初始化
- 因为不能出现递归调用,所以每个构造器第一句不可能都是
this(..)
,至少有一个super(..)
- 如果父类中没有无参构造器,可能会引起子类默认的
super()
调用失败,引发错误。建议将父类中的无参构造器显式写出来。
静态与非静态的特点一致
类的初始化过程
- 初始化父类中的静态成员变量和静态初始化代码块;
- 初始化子类中的静态成员变量和静态初始化代码块;
- 初始化父类的普通成员变量和初始化代码块,再执行父类的构造方法;
- 初始化子类的普通成员变量和初始化代码块,再执行子类的构造方法.
9.1 final
final
关键字可以修饰变量,方法,类
9.1.0 修饰变量
表示该变量的值不可被修改
- 对于基本数据类型来说,其变量存储的常量值不能被修改
- 对于引用数据类型来说,其变量存储的对象引用不能被修改,但是可以修改对象中的数据
在类中表示一个常量用public static final 数据类型 变量名 = 常量数据;
来表示,常量名应符合 常量命名法
9.1.1 修饰方法
在父类中将某方法使用final修饰,表示子类不能够进行重写(Override),但是可以调用父类的该方法
9.1.2 修饰类
表示该类不能被继承
9.2 抽象类
9.2.0 抽象方法
将多个事物的共同行为进行抽取并封装到另一个类时,但是其具体实现方法可能有所区别,但是都是做相同的事,得到相同的结果,因此,我们可以只保留方法名称,而不需要方法体,这就是抽象方法,那么这个类必须为抽象类,他们都是用abstract
关键字修饰。抽象类不能创建对象,只有其实现子类能够创建对象。
9.2.1 抽象类的特点
当一个类中只要存在抽象方法,那么这个类一定是抽象类,抽象类中可以存在普通方法。一个抽象类,也可以没有抽象方法。
当一个类继承一个抽象类,如果没有实现父类中的抽象方法,就会报错。一个类如果也是抽象类,那就可以只继承另一个抽象类,且可选的去实现父类的方法。
9.2.2 abstract的非法组合
final
:修饰类时,final
表示不能被继承,但是abstract
就是表示将被继承的类private
:修饰方法表示方法私有,不能被重写,但是abstract
就是表示将被子类实现static
:修饰方法表示类的独占,和对象没有关系,但是abstract
需要创建一个对象时,所调用的方法
9.3 接口
9.3.0 概述
接口时抽象类的一种特殊形式,当一个抽象类中的方法都是抽象方法,那么这个类就可以用接口表示。
接口不属于类,不能和类做相同的操作了,接口没有成员变量,成员方法,构造器,静态变量,静态方法。
接口不能直接创建对象。
9.3.1 定义
接口将不在用class
修饰,而是使用interface
修饰一个接口
interface InterfaceA {
//这就是一个接口
}
接口中可以存在接口的“变量”与方法
- 在接口中声明的“变量”默认会被
public static final
修饰,本质上为公共的静态常量 - 在接口中声明的方法默认会被
public abstract
修饰,本质上是一个公共的抽象方法 - 在接口中不能修改上述修饰符,它有且只有一种形式,只是可以省略
9.3.2 接口与类的特点
- 接口不属于类的
- 在类之间是单继承关系
- 在接口之间是多继承关系
- 在类与接口之间是实现关系,使用
implements
关键字表示 - 当类为普通类时必须实现接口中的所有方法,且方法修饰符只能为
public
- 当类为抽象类时可选实现接口中的方法,且方法修饰符只能为
public abstract
- 类可以同时继承一个父类和实现多个接口
接口与抽象类区别在是否存在需要提前实现的方法
更多内容: JavaSE学习笔记1 | JavaSE学习笔记2