快速回忆
- 面向对象语言的概念及近代发展
- 面向对象语言的三大特征是什么
- 类与对象是什么
- 什么是方法的重写与重载
- 继承的定义关键字?
- 继承的好处?
- 重写的注意事项?
- super的含义?
- this的含义?
- this的用法?
知识讲解
4.1 面向对象思想
4.1.1 面向对象的语言
面向机械语言 → 面向过程语言 → 面向对象语言
面向机械语言
每种计算机(机械)都有自己独特的机械命令,面向机械语言是一种底层的、直接操作硬件的编程语言。机械语言是一组可以识别的由 0 和 1 序列构成的指令序列。使用机械语言编程也称为面向机械编程。
但后来人们发现可以使用一些便于记忆的符号标识大量的二进制序列来避免使用机械语言编程过于繁重和晦涩,于是演变出了面向过程的编程语言。
面向过程的语言
随着时代的发展,计算机语言也逐渐朝着人类所熟悉的语言发展,最明显的特征就是出现了便于阅读和理解的函数和语法。这种编程语言通过定义一系列的过程或函数来组织代码,使得更加接近于人们的自然语言,人们只许按照自己的意图来编写函数,习惯上将过程语言称为高级语言。
面向对象语言
确实,计算机语言发展过程中有着一直不变的量——“翻译”。通过翻译,人们将所期望的意图表达给计算机,从而实现预期的效果。
在编译中,“翻译”的质量与代码的数量成反比,与其表达的信息量成正比。这意味着,随着编程语言发展,为了代码更加易于维护、扩展和复用,越来越多的信息被抽象出来,以更加高级的方式表达——类与对象。
计算机语言的发展趋势是朝着更高层次的抽象和更丰富的信息表达能力发展的,这也使得面向对象的语言表达出三个特征:封装、继承、多态。封装产生独立的对象,继承扩展已有的类,产生差异化的对象,多态又统一了不同类型的对象的表达形式,比如所有的类都继承了Object,这些对象就像自然界的丛林树木,组成了一个大自然的生态圈,利用抽象的数据描绘了整个世界中一草一木的属性,习性和动作,万物皆对象,这也是面向对象语言的成功之处。
面向对象编程以对象为基本单元,将算法和数据封装其中,程序可以访问和修改对象关联的数据。
4.2 类与对象
4.2.1 类
从概念上说,类是是一组相关的属性和行为的集合,是属性和方法的组织形式,方法就是对象行为的抽象。类也是最重要的基本类型之一。
从计算机的角度来说,类是由java文件编译后得到的class字节码文件,是编译时最基本的代码组织。
class 类名{
类体的内容(属性和方法)
}
class Person{
//人的抽象类
//人名
String name;
//年龄
int age;
//身高
float height;
//人的方法行为
void sleep(){
//睡觉
}
void eat(){
//吃饭
}
}
class 饮料{
String 饮料名;
String 颜色;
Float 碳水能量;
void 被喝掉(String 小明){
//小明认为好喝 增加碳水能量
}
}
变量的声明:用来存储属性的值,体现对象的属性。
方法的定义:方法可以对类中的数据进行操作,或者对得到的参数做出响应,体现了对象所具有的的行为,也就是事物之间的相互影响。
4.2.2 成员变量与方法
类体的内容可分为两个部分:①变量的声明②方法的定义。
在类中声明的变量称为成员变量,成员变量的类型是前面提到的基本数据类型,一旦声明就可以在类中使用,变量名的命名规范详见标识符的命名。
class 类名{
数据类型 变量名;
}
在类中声明的方法称为成员方法,方法就是将一个功能抽取出来,把代码单独定义在一个大括号内,形成一个单独的功能。
一般情况下,方法的声明由修饰符,返回值及类型,方法名,参数列表,方法体组成,当返回值类型为void时,方法没有返回值。
class 类名{
修饰符 返回值类型 方法名(参数列表){
return 返回值;
}
修饰符 void 方法名(参数列表){
}
}
- 方法必须定义在一类中方法外
- 方法不能定义在另一个方法的里面
- 明确返回值类型,返回值的类型与方法体体中标志的数据类型一致
- 明确参数列表
- 修饰符可以使用public和static来进行修饰
静态成员指被static修饰的成员变量和成员方法
①静态成员只能访问静态成员,普通成员可以访问静态成员也可以访问普通成员
②静态成员随着类的加载而存在(静态成员在方法区中),可以通过"类名.静态成员"的方式访问
③静态成员优先于非静态成员初始化
public class Test {
//例一:使用三元运算符打印成绩
public static void main(String[] args) {
int s1 = 85,s2=89,s3=95;
method(s1,s1,s3)
}
public void method(int s1,int s2,int s3){
int max = s1>s2?s1>s3?s1:s3:s2>s3?s2:s3;
int min = s1<s2?s1<s3?s1:s3:s2<s3?s2:s3;
int avg = (s1+s2+s3)/3;
int cha = max-min;
System.out.println("最高分:"+max);
System.out.println("最低分:"+min);
System.out.println("平均分:"+avg);
System.out.println("分差:"+cha);
}
}
public class Test2{
public static void main(String[] args) {
//例二求和,调用方法Sum
//并接收方法计算后的结果,整数
int sum = Sum();
System.out.println(sum);
}
/*
定义计算1~100的求和方法
返回值类型,计算结果整数int
参数:没有不确定数据
*/
public static int Sum() {
//定义变量保存求和
int sum = 0;
//从1开始循环,到100结束
for (int i = 1; i <= 100; i++) {
sum = sum + i;
}
return sum;
}
}
4.2.3 构造方法
构造方法是一种特殊方法,用于类创建对象,主要完成对象数据的初始化。
无论你与否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个无参数构造方法,但一旦自己定义了构造方法,Java自动提供的默认无参数构造方法就会失效
class 类名{
修饰符 类名(){
//默认的无参构造方法
}
}
①构造方法的写法上,方法名与它所在的类名相同。
②没有返回值,也不需要返回值类型。
③构造方法是可以重载的,既可以定义参数,也可以不定义参数。
(重载指方法名相同但是参数列表个数或其类型的顺序不同)
4.2.4 对象的创建
从概念上说,对象是一类事物的具体体现。对象是类的一个实体,拥有着该类事物最基本的特征。如果说类是饮料,那么对象就是快乐水,前者偏向于抽象概念,后者偏向于它的实体实现。
从计算机的角度来说,对象就是类的实例化,就是根据类模板制作类对象,存入堆内存中。一个java源文件的运行离不开方法区,堆内存,栈内存。方法区存放概念性的,抽象性的类。堆内存存放运行时根据类模板制造的个性类对象。
接上例喻体,方法区中可能只有一个“饮料”的概念,但是堆内存中可以存放不同品牌的“快乐水”,这与你实例化的次数有关。
//引入的饮料外部类,尚不规范仅做教学参考
class 饮料{
String 饮料名;
String 颜色;
Float 碳水能量;
//无参构造方法
public 饮料() {
}
//有参构造方法
public 饮料(String 饮料名, String 颜色, Float 碳水能量) {
this.饮料名 = 饮料名;
this.颜色 = 颜色;
this.碳水能量 = 碳水能量;
}
void 被喝掉(String str){
//str 认为好喝 增加碳水能量
//System.out.println()方法接收参数并打印在控制台
System.out.println("str"+"认为好喝 增加碳水能量");
}
}
public class test {
//程序执行的入口
public static void main(String[] args) {
//使用无参构造函数创建“饮料”类的实例对象快乐水,内部属性值为空
饮料 快乐水 = new 饮料();
//給值
快乐水.饮料名 = "快乐水";
快乐水.颜色 = "蓝色";
快乐水.碳水能量 = 800f;
//使用构造方法创建对象,内部属性根据有参构造方法給值
饮料 快乐水2 = new 饮料("快乐水2", "绿色", 1000f);
//调用快乐水的方法“被喝掉”
//方法要求提供一个参数喝水的人
快乐水.被喝掉(“小明”);
//方法被调用 打印“小明认为好喝 增加碳水能量”
}
}
类是静态的概念是存放在硬盘上的文件,加载时放入方法区。对象是根据类,在程序运行过程中,在内存中申请的一块空间,放入堆内存
代码执行时,首先将程序入口main方法作为主线程压入栈内存中,按顺序执行代码。接着从方法区找到需要实例化的类对象,调用其无参构造方法并将其实例化到堆内存中。
此时栈内存的main方法中“快乐水”存放着一个地址,指向堆内存中对象实例的内存空间(引用类型作为参数,传递的是地址值),而被实例化的类仍在在方法区中,方便实例更多的对象,即方法信息保存一份,节省内存。
4.2.5 类与对象的关系
- 类是对一类事物的描述,是抽象的结果,用来描述一类事物的共同属性和行为。
- 对象是一类事物的实例,是类的具体化。代表着一类事物中的一个或一组个体。
- 类和对象是一种互相联系的关系, 类是对象的模板,对象是类的实例。
4.3 面向对象的三个特征
4.3.1 封装
封装就是在类中使用private关键字,并对需要访问的变量提供getter和setter方法。
public class 饮料 {
//成员变量
private String 饮料名;
private String 颜色;
private Float 碳水能量;
public 饮料() {
}
public 饮料(String 饮料名, String 颜色, Float 碳水能量) {
this.饮料名 = 饮料名;
this.颜色 = 颜色;
this.碳水能量 = 碳水能量;
}
public String get饮料名() {
return 饮料名;
}
public void set饮料名(String 饮料名) {
this.饮料名 = 饮料名;
}
public String get颜色() {
return 颜色;
}
public void set颜色(String 颜色) {
this.颜色 = 颜色;
}
public Float get碳水能量() {
return 碳水能量;
}
public void set碳水能量(Float 碳水能量) {
this.碳水能量 = 碳水能量;
}
//成员方法
void 被喝掉(String str) {
//str 认为好喝 增加碳水能量
//System.out.println()方法接收参数并打印在控制台
System.out.println(str + "认为好喝 增加碳水能量");
}
}
this关键字
this代表所在类的当前对象的引用(地址值),即对象自己的引用。
this.成员变量 ‐‐ 本类的
this.成员方法名() ‐‐ 本类的
当使用构造方法或成员方法时,如果接收参数名与内部成员变量名重名,则接收参数在方法体中就会覆盖该变量名的引用,此时如果想要调用 类中成员变量就要使用this关键字使用当前对象的引用来使用成员变量。
4.3.2 继承
当一个类继承另一个类时,我们称继承的类为子类,被继承的类叫做父类。
继承是java中创建类的一个机制,是子类继承父类的“属性”和“行为”(成员变量和成员方法),使得子类对象具有与父类相同的属性、相同的行为。下文中父类的特征指子类继承的成员变量和成员方法。
子类可以直接 访问父类中的“非私有”的属性和行为。类与类之间产生了关系,是多态的前提。
public class 碳酸饮料 extends 饮料{
}
继承的特性
①Java只支持单继承,不支持多继承。
②Java支持多层继承(继承体系),即继承具有传递性。
重载与重写
重载与重写描绘了java中方法与方法之间的关系。
重载:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。
重写:在字父类中,子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效 果,也称为重写或者复写。(声明不变,重新实现)
重载可以提供不同的参数列表的函数,增加函数的灵活性和复用性。重写可以使得子类在继承父类特征的同时,增强已有的函数的功能,增强类的扩展性和复用性。
//父类-饮料
public class 饮料 {
……(省略成员变量,构造方法,getter和setter方法,其它方法)
//成员方法
void 被喝掉(String str) {
//str 认为好喝 增加碳水能量
//System.out.println()方法接收参数并打印在控制台
System.out.println(str + "认为好喝 增加碳水能量");
}
}
//子类-碳酸饮料
public class 碳酸饮料 extends 饮料{
public void 被喝掉(String name, int i) {
//重载 类中方法
System.out.println(name + "认为好喝,喝了"+i+"L增加碳水能量");
}
public void 被喝掉(String str) {
//重写 父类方法
System.out.println(str + "认为碳酸饮料好喝 增加碳水能量");
}
}
super关键字
在使用类方法时会出现参数名与成员变量名重名而覆盖内部值的问题,在继承中也会出现子类成员变量名与父类变量名重名而导致父类变量的值取不到的问题,同this关键字一样,java中使用super关键字表示对父类对象的引用。
super.成员变量 ‐‐ 父类的
super.成员方法名() ‐‐ 父类的
super关键字和this关键字的三种用法
①super.成员变量,this.成员变量,表示对父类或当前类变量的引用
②super.成员方法名(),this.成员方法名,表示对父类或当前类方法发引用
③super(参数),this(参数),用在类的构造方法中,且调用必须是构造函数主体中的第一条语句,表示对父类或子类中构造方法的调用
4.3.3 多态
是指同一对象,在不同时刻下具有多个不同表现形式。
public static class test{
public static void main(String[] args){
//多态的表现形式
饮料 drink = new 碳酸饮料();
}
}
//父类-饮料
public class 饮料 {
……(省略成员变量,构造方法,getter和setter方法,其它方法)
//成员方法
void 被喝掉(String str) {……}
}
//子类-碳酸饮料
public class 碳酸饮料 extends 饮料{
public void 被喝掉(String name, int i) {……}
public void 被喝掉(String str) {……}
}
饮料 drink = new 碳酸饮料( )就是多态的表现形式,drink中保留着子类中重写的方法,但不保留重载的方法,此时的drink中的“被喝掉( )”方法就与饮料类中的“被喝掉( )”方法产生了差异,这就是“在不同时刻下具有多个不同表现形式”的含义。java中抽象类与接口便是多态的具体实现形式。
多态的前提
①有着继承/实现关系
②方法重写
③有父类引用指向子类对象
向上转型和向下转型
向上转型和向下转型是对子父类转换步骤的描述。
向上转型:父类 父类对象 = new 子类()
向下转型:子类 子类对象 = (子类)父类对象
转型的异常 instanceof 关键字
如上图所示,父类方法打印信息,子类继承全部重写打印方法。
向上转型:此时的转换呈现多对一,转换成各自重写打印方法的父类
向下转型:此时的转换呈现一对多,即当前父类对象不知道是由那个子类上转的,从而抛出类型转换异常问题。
instanceof关键字
变量名 instanceof 数据类型
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false。
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat();
// 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
可以根据instanceof 判断该变量是否是该数据类型再去进行强制转换。