Java面向对象
0.面向对象程序设计
面向对象程序设计思想:
数据结构优先,算法在后,是方法的集合
传统结构程序设计思想:
算法优先,数据结构在后,数据结构服务于算法,是对象以及对象间的消息的集合
1.面向对象基本思想
1.1 类(class)
类是构造对象的模板或蓝图,由类构造对象的过程是创建类的实例,描述了对象属性和行为的共性。对象中的数据是实例字段,操作数据的过程是方法,特定对象的实例字段值就是对象的当前状态,只要在对象上调用一个方法,对象的当前状态就有可能发生变化。
封装给对象赋予了黑盒特性,程序只能通过对象的方法和对象数据进行交互。
1.2 类的定义
- 成员变量
指的是事物的属性,定义在类中方法体之外的变量 - 成员方法
指的是事物的行为,没有static关键字的方法
类的定义,本质是对数据类型的定义,是我们自己定义的数据类型。创建对象就是类的实例化,对象是类的实例
1.3 类在JVM中的内存映像
对于同一个JVM,一次运行最多只加载一次类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kCiuXtSQ-1641519509091)(/assets/MemoryOfClass.drawio.png)]
2.面向对象特殊语法
2.1 局部变量和成员变量的比较
- 在类中定义的位置不同
局部变量:定义在方法中(方法体中和方法形式参数)
成员变量:类中方法体之外 - 在内存中的位置不同
局部变量:栈空间栈帧中
成员变量:堆上的对象里 - 生命周期不同
局部变量:生命周期和方法运行有直接关系
成员变量:生命周期和方法运行没有直接关系 - 初始化值不同
局部变量:手动赋初值
成员变量:天生有初值
2.2 对象作为方法参数
时刻注意,实际的参数到底有没有改变,参数有两份,但是参数的实际值只有一份
2.3 构造方法
创建对象时对成员变量初始化
语法
- 构造方法方法名必须与类名相同
- 没有返回值声明,加了返回值就不是构造方法了
注意事项
- 方法重载同样适用
- 参数类型不同
- 参数个数不同
- 参数类型顺序不同
- 没有构造方法的时候,JVM会默认添加构造方法;定义了构造方法后,就不会默认添加构造方法了。
- 对象创建及初始化过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y2HBS7tt-1641519509093)(/assets/2022-01-03_15-19-57.png)] - 当方法中定义了和成员变量同名的局部变量,成员变量被隐藏,因此需要区分成员变量和局部变量(this)
2.4 this关键字
作用:
- 用于区分成员变量和局部变量
- 访问对象的成员(包含成员变量和成员方法)
public void eat(){
this.sleep();
System.out.println(name + " is eating");
}
- 访问对象的构造方法
public Student(){
this("Unknown", -1, -1);
}
该语句只能在构造方法的方法体中,并且只能位于构造方法的第一条语句位置
this代表对当前对象的引用,那么当前对象是哪个对象?
- 在构造方法中的对象
当构造方法执行时,JVM正在创造一个对象,所以this就指正在创建的那个对象 - 在普通成员方法中
访问的是哪个对象,this就指那个对象
2.5 static关键字
可以修饰成员和方法,被修饰的成员变量,该变量我们称之为静态成员变量(静态变量);被修饰的成员方法,称之为静态成员方法(静态方法)
特点:
- 被当前类的所有对象所共享
- 被修饰成员变量被类的所有对象共享
- 被修饰成员方法,从共享的角度看,和普通方法没有区别
- 可以通过类名访问静态变量或访问静态方法
- 静态变量,随着类加载被放进方法区(首次访问静态成员时会触发类加载)同时,静态方法也是随着类加载而被放进方法区,在对象还未创建时就可以直接访问
class StaticClasses{
static int staticField;
static void staticField(){
System.out.println("sao");
}
}
- 静态变量、静态方法优先于对象存在,不依赖于对象而存在
注意事项:
- 静态上下文中,无法访问非静态的成员变量(成员方法)
- 静态方法的方法体(只是静态上下文的一种,后面会补充)
- 无法访问普通成员变量(成员方法), 是因为有可能访问不到当前对象。
- 并非在静态方法体中,都不能访问静态变量和静态方法,我们可以在自己new的对象上访问
- 不能出现在方法体当中
使用场景:
- 针对成员变量,如果我们想让成员变量被当前类的所有对象共享,使用static修饰成员变量
- 针对成员方法,通常工具方法会被定义成静态方法。
2.6 代码块
2.6.1 局部代码块
出现在方法中的代码块,方法体中用花括号定义的代码,随着方法执行而执行
2.6.2 构造代码块
定义在类中方法体之外的代码块,创建对象时执行,每个对象创建都会执行
特点:
- 构造代码块优先于构造方法执行
- 成员变量初始化语句,和构造方法比构造代码块后执行
- 成员变量初始化语句和构造代码块比,谁先定义谁先执行
作用:
- 在构造代码块中,执行对对象成员变量值的初始化
- 如果在多个构造方法中,有需要执行的公共代码,公共代码可以放到构造代码块中
2.6.3 静态代码块(也是一种静态上下文)
被static修饰的定义在类中方法体之外的代码块
特点:
- 随类加载而加载,而在JVM的一次运行当中,一个类最多只会执行一次,那静态代码块最多也只会执行一次
作用
- 如果有些代码只需要执行一次,可以放在静态代码块中
2.7 package关键字
声明可以使文件中定义的类成为指定包的成员
没有package关键字并不意味着java文件不属于任何一个包,java语言里有一个默认包,类总是被包组织起来的。
2.8 import关键字
导入全限定类名,编译器默认我们导入的是当前包下的类,要用其它包的类需要使用import
注意事项:
- import语句在package后,class前
- java.lang包中的类被隐式导入,例:String类
- import <包名> .*
- 按需导入
3.访问权限修饰符
3.1 修饰类中成员
权限/权限关键字 | public | protected | default | private |
---|---|---|---|---|
同一个类中访问 | √ | √ | √ | √ |
同包其它类中访问 | √ | √ | √ | × |
跨包访问 | √ | × (跨包子类可以访问) | × | × |
普通成员方法和普通成员变量从访问权限的角度来说,是一样的。
private作用:
- 提示用户不用触碰他们不该触碰的代码
- 类库设计者可以安全地修改实现细节
3.2 修饰类
- 类只有两种访问权限default和public
- 一个Java文件中只能有一个类具有public访问权限
- 一旦类被public修饰,类名必须和Java文件名相同
public 其它任意类可见
default 同包类可见
4.面向对象三大特征
4.1 封装
- 将数据和基于数据的操作封装在一起
- 数据被保护在内部
private修饰成员变量,使用get和set方法获取和修改成员变量内容,同时又能保护内部数据,又能控制外部对成员变量控制的访问
如果把所有构造方法的访问权限定义为private,那么在其他类就无法新建对象了
4.2 继承
特点:
- Java只支持单重继承,但是一个类可以继承父类以及其所有祖先类中定义的成员
继承中的名词:
- 父类(基类,超类)指被继承的类
- 子类(派生类,导出类)指继承的类
优点:
- 代码复用
- 提高了代码的维护性
- 弱化Java的类型约束
可以让父类引用指向子类对象
缺点:
- 父类修改会被继承到所有子类中
继承的注意事项:
- 子类只能访问父类中的非私有成员
- 子类不能继承父类的构造方法
4.2.1 子类对象的初始化问题
先父后子
原因:
- 父得有东西,子才能继承
- 子类有可能要依赖父类成员变量值
如何实现?
子类构造方法体执行之前先去调用父类的构造方法
子类对象初始化方式:
- 隐式初始化方式
JVM自动保证父类构造方法先构造
条件:- 父类必须有默认构造方法(无参构造方法)
- 子类构造方法中没有显式调用父类的其它构造方法
- 显式初始化方式
结合super关键字,访问父类构造方法,程序员手动保证父类构造方法先执行
4.2.2 super关键字
super指向了子类对象存储空间里父类对象成员变量的存储空间
用法:
- 访问成员变量
- 访问构造方法(访问父类构造方法)
- 访问成员方法
注意事项:
- 保证先父后子的顺序,显式和隐式初始化都无所谓
- super只能用在子类的方法中使用,super调用父类无参构造方法只能在子类构造方法中的第一条语句
- super和this在构造方法中不能同时存在
- 为了保证先父后子,父类的成员变量初始化语句、构造代码块、构造方法都优先于子类中的初始化语句、构造代码块和构造方法
4.2.3 父类域field的隐藏
- 父类方法体中访问同名成员变量,访问的是父类成员变量的变量值;子类方法提中访问同名成员变量,访问的是子类成员变量的变量值
- 如果在子类对象上访问成员变量值,先在子类对象上搜索有没有该成员变量,有则访问,没有则去父类对象找
4.2.4 方法的覆盖和重写
JVM对待执行方法的搜索策略:
- 子类中搜索待执行的方法,找到就执行
- 去父类找,找到就执行
- 一路溯源,直到最后找到就执行
方法覆盖的条件:
- 方法的访问权限
子类方法的访问权限不必和父类方法完全相同,只需保证子类方法的访问权限大于等于父类即可 - 方法的返回值
- 如果返回值是基本数据类型,子类方法的返回值类型和父类方法相同
- 如果返回值是引用数据类型,子类方法的返回值类型不必和父类方法相同,但是在这种情况下,子类返回值类型是父类返回值类型的子类
- 方法的方法签名(方法名+参数列表)
方法签名必须完全一致
方法覆盖的使用场景
修改父类中的方法实现
方法覆盖的注意事项
- 父类中私有方法不能被覆盖
4.2.5 final关键字
final是最终的意思,可以修饰类、方法、变量。
- 被修饰的类不能继承
- 被修饰的方法不能被覆盖
- 被修饰的变量
修饰局部变量:变成了常量,只能被赋值一次
修饰成员变量:必须保证,在对象创建完毕之前给它赋值一次且仅一次(初始化语句、构造方法、构造代码块)
4.3 多态
多态是同一个行为具有多个不同表现形式或形态的能力(同一个行为在不同对象上的表现形式)
必要条件:
- 继承
- 方法覆盖
- 父类引用指向子类对象
多态成员的额访问特征
- 成员变量
编译看左边(我们所能访问到的成员是根据引用变量的类型决定的),运行看左边(成员变量有父类的外貌特征) - 成员方法
编译看左边(我们所能访问到的方法是根据引用变量的类型决定的),运行看右边
编译时,能够访问的成员变量是父类所具有的,最后访问的也是父类的变量;
而能够访问的成员方法是父类的方法,但是实际调用的是子类的重写方法
问题:
- 父类类型引用的引用变量无法访问到子类自己定义的方法
solution:引用变量类型转化- 子类类型–>父类类型:向上转型(一定安全,编译器默认允许)
- 父类类型–>子类类型:向下转型(编译器默认不允许,运行可能存在安全问题)当然,可以强制转化,可是如果向下转型时,当前被转化的实际指向的对象类型如果不是真正的子类,转型会失败
- 解决方案
instanceof:
引用变量 instanceof 目标类的类名
返回true:引用变量指向对象是目标类的对象
返回false:引用变量指向对象不是目标类的对象
需要注意的是, null instanceof 任意类 的结果都是false
- 解决方案
优点:
- 提高程序可维护性
- 提高代码可扩展性
5. 抽象类
抽象类不能实例化,因此其必须要被子类继承才能使用;子类中需要实现的方法,需要在抽象类中声明,即声明抽象方法,该方法不需要方法体。
抽象类的成员组成
- 成员变量:和普通类没有任何区别
- 构造方法:和普通类也没有任何区别
- 成员方法:
- 抽象方法:声明该类,有这种行为
- 非抽象方法:如果知道行为如何实现,可以定义非抽象方法实现
注意事项:
- abstract不能和private, final, static共存,因为被这三个关键字修饰的抽象方法是无法被覆盖的
6. 接口
接口打破了单重继承特性
接口表示数据类型,侧重于描述一组有特殊功能的行为。可以定义一个类,让类实现接口,这种关系叫做实现关系。
特征:
- 接口不能直接实例化,但是可以间接实例化
接口类型 引用变量 = 接口的子类对象 - 接口的子类,可以是抽象类也可以是具体类
如果子类是普通类,则必须重写接口里的所有方法
接口的组成:
- 成员变量:
接口中定义的成员变量,实际上被public static final修饰 - 构造方法:
接口不能定义构造方法,因为接口的方法都是抽象方法 - 成员方法
只能定义抽象方法,默认被public abstract修饰
接口定义其它方法(不建议,也很少使用到)
默认方法
我们原来说接口里只能定义抽象方法,实际上默认方法也是可以的,默认方法不强制所有子类覆盖它。
静态方法
作为工具方法
6.1 接口的多重继承
- 接口和接口之间可以多重继承
- 一个类可以实现多个接口《Java编程思想》
6.2 完整类定义语法
class 类名 extends 类名 implements 接口1,接口2...{
类定义
}
6.3 抽象类和接口的比较
成员区别
- 抽象类 变量,常量;有抽象方法; 非抽象方法
- 接口 常量;抽象方法
关系区别
- 类与类 继承,单继承
- 类与接口 实现,单实现,多实现
- 接口与接口 继承,单继承,多继承
设计理念区别
- 抽象类 被继承体现的是:”is a”的关系,是一种共性功能。
- 接口 被实现体现的是:”like a”的关系,每个接口只实现了接口的冰山一角,是一种扩展功能。
7.内部类
内部类指的是一个类里嵌套其它的类
7.1 非静态内部类
被嵌套的类拥有访问外部类的权限,由于嵌套在外部类中,因此必须先实例化外部类,然后创建内部类的对象实现。
class OuterClass{
int x = 10;
class InnerClass{
int y = 5;
}
}
7.2 私有内部类
内部类可以被private和protected修饰,这样内部类不能被外部类访问。
7.3 静态内部类
即不需要先进性外部实例化就可以直接访问内部类,但是不能访问外部类。
7.4 内部类访问外成员
class Outerclass{
int x = 10;
class InterClass{
public int myInnerMethod(){
return x;
}
}
}
这就是Java面向对象的基础知识,欢迎大家讨论交流😘!